1 /***************************************************************************
2 skymap.cpp - K Desktop Planetarium
4 begin : Sat Feb 10 2001
5 copyright : (C) 2001 by Jason Harris
6 email : jharris@30doradus.org
7 ***************************************************************************/
9 /***************************************************************************
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 ***************************************************************************/
18 #include <kapplication.h>
20 #include <kiconloader.h>
21 #include <kstatusbar.h>
22 #include <kmessagebox.h>
24 #include <kstandarddirs.h>
26 #include <qmemarray.h>
27 #include <qpointarray.h>
39 #include "kstarsdata.h"
40 #include "imageviewer.h"
41 #include "infoboxes.h"
42 #include "detaildialog.h"
43 #include "addlinkdialog.h"
44 #include "kspopupmenu.h"
46 #include "skyobject.h"
47 #include "deepskyobject.h"
49 #include "ksasteroid.h"
51 #include "starobject.h"
53 SkyMap::SkyMap(KStarsData
*d
, QWidget
*parent
, const char *name
)
54 : QWidget (parent
,name
), computeSkymap(true), angularDistanceMode(false),
55 ksw(0), data(d
), pmenu(0), sky(0), IBoxes(0),
56 ClickedObject(0), FocusObject(0), TransientObject(0),
57 starpix(0), pts(0), sp(0)
59 if ( parent
) ksw
= (KStars
*) parent
->parent();
62 pts
= new QPointArray( 2000 ); // points for milkyway and horizon
63 sp
= new SkyPoint(); // needed by coordinate grid
67 setDefaultMouseCursor(); // set the cross cursor
69 // load the pixmaps of stars
70 starpix
= new StarPixmap( data
->colorScheme()->starColorMode(), data
->colorScheme()->starColorIntensity() );
72 setBackgroundColor( QColor( data
->colorScheme()->colorNamed( "SkyColor" ) ) );
73 setBackgroundMode( QWidget::NoBackground
);
74 setFocusPolicy( QWidget::StrongFocus
);
75 setMinimumSize( 380, 250 );
76 setSizePolicy( QSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Expanding
) );
78 setMouseTracking (true); //Generate MouseMove events!
79 midMouseButtonDown
= false;
80 mouseButtonDown
= false;
88 pmenu
= new KSPopupMenu( ksw
);
90 //Initialize Transient label stuff
91 TransientTimeout
= 100; //fade label color every 0.2 sec
92 connect( &HoverTimer
, SIGNAL( timeout() ), this, SLOT( slotTransientLabel() ) );
93 connect( &TransientTimer
, SIGNAL( timeout() ), this, SLOT( slotTransientTimeout() ) );
95 IBoxes
= new InfoBoxes( Options::windowWidth(), Options::windowHeight(),
96 Options::positionTimeBox(), Options::shadeTimeBox(),
97 Options::positionGeoBox(), Options::shadeGeoBox(),
98 Options::positionFocusBox(), Options::shadeFocusBox(),
99 data
->colorScheme()->colorNamed( "BoxTextColor" ),
100 data
->colorScheme()->colorNamed( "BoxGrabColor" ),
101 data
->colorScheme()->colorNamed( "BoxBGColor" ) );
103 IBoxes
->showTimeBox( Options::showTimeBox() );
104 IBoxes
->showFocusBox( Options::showFocusBox() );
105 IBoxes
->showGeoBox( Options::showGeoBox() );
106 IBoxes
->timeBox()->setAnchorFlag( Options::stickyTimeBox() );
107 IBoxes
->geoBox()->setAnchorFlag( Options::stickyGeoBox() );
108 IBoxes
->focusBox()->setAnchorFlag( Options::stickyFocusBox() );
110 IBoxes
->geoChanged( data
->geo() );
112 connect( IBoxes
->timeBox(), SIGNAL( shaded(bool) ), data
, SLOT( saveTimeBoxShaded(bool) ) );
113 connect( IBoxes
->geoBox(), SIGNAL( shaded(bool) ), data
, SLOT( saveGeoBoxShaded(bool) ) );
114 connect( IBoxes
->focusBox(), SIGNAL( shaded(bool) ), data
, SLOT( saveFocusBoxShaded(bool) ) );
115 connect( IBoxes
->timeBox(), SIGNAL( moved(QPoint
) ), data
, SLOT( saveTimeBoxPos(QPoint
) ) );
116 connect( IBoxes
->geoBox(), SIGNAL( moved(QPoint
) ), data
, SLOT( saveGeoBoxPos(QPoint
) ) );
117 connect( IBoxes
->focusBox(), SIGNAL( moved(QPoint
) ), data
, SLOT( saveFocusBoxPos(QPoint
) ) );
119 connect( this, SIGNAL( destinationChanged() ), this, SLOT( slewFocus() ) );
121 //Initialize Refraction correction lookup table arrays. RefractCorr1 is for calculating
122 //the apparent altitude from the true altitude, and RefractCorr2 is for the reverse.
123 for ( unsigned int index
= 0; index
<184; ++index
) {
124 double alt
= -1.75 + index
*0.5; //start at -1.75 degrees to get midpoint value for each interval.
126 RefractCorr1
[index
] = 1.02 / tan( dms::PI
*( alt
+ 10.3/(alt
+ 5.11) )/180.0 ) / 60.0; //correction in degrees.
127 RefractCorr2
[index
] = -1.0 / tan( dms::PI
*( alt
+ 7.31/(alt
+ 4.4) )/180.0 ) / 60.0;
139 //Deprecated...DeepSkyObject dtor now handles this itself.
140 /*//delete any remaining object Image pointers
141 for ( DeepSkyObject *obj = data->deepSkyListMessier.first(); obj; obj = data->deepSkyListMessier.next() ) {
142 if ( obj->image() ) obj->deleteImage();
144 for ( DeepSkyObject *obj = data->deepSkyListNGC.first(); obj; obj = data->deepSkyListNGC.next() ) {
145 if ( obj->image() ) obj->deleteImage();
147 for ( DeepSkyObject *obj = data->deepSkyListIC.first(); obj; obj = data->deepSkyListIC.next() ) {
148 if ( obj->image() ) obj->deleteImage();
150 for ( DeepSkyObject *obj = data->deepSkyListOther.first(); obj; obj = data->deepSkyListOther.next() ) {
151 if ( obj->image() ) obj->deleteImage();
155 void SkyMap::setGeometry( int x
, int y
, int w
, int h
) {
156 QWidget::setGeometry( x
, y
, w
, h
);
160 void SkyMap::setGeometry( const QRect
&r
) {
161 QWidget::setGeometry( r
);
162 sky
->resize( r
.width(), r
.height() );
166 void SkyMap::showFocusCoords( void ) {
167 //display object info in infoBoxes
169 oname
= i18n( "nothing" );
170 if ( focusObject() != NULL
&& Options::isTracking() )
171 oname
= focusObject()->translatedLongName();
173 infoBoxes()->focusObjChanged(oname
);
175 if ( Options::useAltAz() && Options::useRefraction() ) {
176 SkyPoint
corrFocus( *(focus()) );
177 corrFocus
.setAlt( refract( focus()->alt(), false ) );
178 corrFocus
.HorizontalToEquatorial( data
->LST
, data
->geo()->lat() );
179 infoBoxes()->focusCoordChanged( &corrFocus
);
181 infoBoxes()->focusCoordChanged( focus() );
185 SkyObject
* SkyMap::objectNearest( SkyPoint
*p
) {
186 double r0
= 200.0/Options::zoomFactor(); //the maximum search radius
189 //Search stars database for nearby object.
190 double rstar_min
= r0
;
191 double starmag_min
= 20.0; //absurd initial value
194 if ( Options::showStars() ) { //Can only click on a star if it's being drawn!
196 //test RA and dec to see if this star is roughly nearby
198 for ( register unsigned int i
=0; i
<data
->starList
.count(); ++i
) {
199 SkyObject
*test
= (SkyObject
*)data
->starList
.at(i
);
201 double dRA
= test
->ra()->Hours() - p
->ra()->Hours();
202 double dDec
= test
->dec()->Degrees() - p
->dec()->Degrees();
203 //determine angular distance between this object and mouse cursor
204 double f
= 15.0*cos( test
->dec()->radians() );
205 double r
= f
*f
*dRA
*dRA
+ dDec
*dDec
; //no need to take sqrt, we just want to ID smallest value.
206 if (r
< r0
&& test
->mag() < starmag_min
) {
209 starmag_min
= test
->mag();
214 //Next, find the nearest solar system body within r0
216 double rsolar_min
= r0
;
217 SkyObject
*solarminobj
= NULL
;
219 if ( Options::showPlanets() )
220 solarminobj
= data
->PCat
->findClosest( p
, r
);
229 if ( Options::showMoon() ) {
230 double dRA
= data
->Moon
->ra()->Hours() - p
->ra()->Hours();
231 double dDec
= data
->Moon
->dec()->Degrees() - p
->dec()->Degrees();
232 double f
= 15.0*cos( data
->Moon
->dec()->radians() );
233 r
= f
*f
*dRA
*dRA
+ dDec
*dDec
; //no need to take sqrt, we just want to ID smallest value.
234 if (r
< rsolar_min
) {
235 solarminobj
= data
->Moon
;
241 if ( Options::showAsteroids() ) {
242 for ( KSAsteroid
*ast
= data
->asteroidList
.first(); ast
; ast
= data
->asteroidList
.next() ) {
243 //test RA and dec to see if this object is roughly nearby
244 double dRA
= ast
->ra()->Hours() - p
->ra()->Hours();
245 double dDec
= ast
->dec()->Degrees() - p
->dec()->Degrees();
246 double f
= 15.0*cos( ast
->dec()->radians() );
247 double r
= f
*f
*dRA
*dRA
+ dDec
*dDec
; //no need to take sqrt, we just want to ID smallest value.
248 if ( r
< rsolar_min
&& ast
->mag() < Options::magLimitAsteroid() ) {
256 if ( Options::showComets() ) {
257 for ( KSComet
*com
= data
->cometList
.first(); com
; com
= data
->cometList
.next() ) {
258 //test RA and dec to see if this object is roughly nearby
259 double dRA
= com
->ra()->Hours() - p
->ra()->Hours();
260 double dDec
= com
->dec()->Degrees() - p
->dec()->Degrees();
261 double f
= 15.0*cos( com
->dec()->radians() );
262 double r
= f
*f
*dRA
*dRA
+ dDec
*dDec
; //no need to take sqrt, we just want to ID smallest value.
263 if ( r
< rsolar_min
) {
270 //Next, search for nearest deep-sky object within r0
271 double rmess_min
= r0
;
272 double rngc_min
= r0
;
274 double rother_min
= r0
;
280 for ( DeepSkyObject
*o
= data
->deepSkyList
.first(); o
; o
= data
->deepSkyList
.next() ) {
281 bool checkObject
= false;
282 if ( o
->isCatalogM() &&
283 ( Options::showMessier() || Options::showMessierImages() ) ) checkObject
= true;
284 if ( o
->isCatalogNGC() && Options::showNGC() ) checkObject
= true;
285 if ( o
->isCatalogIC() && Options::showIC() ) checkObject
= true;
286 if ( o
->catalog().isEmpty() && Options::showOther() ) checkObject
= true;
289 //test RA and dec to see if this object is roughly nearby
290 double dRA
= o
->ra()->Hours() - p
->ra()->Hours();
291 double dDec
= o
->dec()->Degrees() - p
->dec()->Degrees();
292 double f
= 15.0*cos( o
->dec()->radians() );
293 double r
= f
*f
*dRA
*dRA
+ dDec
*dDec
; //no need to take sqrt, we just want to ID smallest value.
294 if ( o
->isCatalogM() && r
< rmess_min
) {
295 imess_min
= data
->deepSkyList
.at();
298 if ( o
->isCatalogNGC() && r
< rngc_min
) {
299 ingc_min
= data
->deepSkyList
.at();
302 if ( o
->isCatalogIC() && r
< ric_min
) {
303 iic_min
= data
->deepSkyList
.at();
306 if ( o
->catalog().isEmpty() && r
< rother_min
) {
307 iother_min
= data
->deepSkyList
.at();
313 //Next, search for nearest object within r0 among the custom catalogs
314 double rcust_min
= r0
;
318 for ( register unsigned int j
=0; j
<Options::catalogCount(); ++j
) {
319 if ( Options::showCatalog()[j
] ) {
320 QPtrList
<DeepSkyObject
> cat
= data
->CustomCatalogs
[ Options::catalogName()[j
] ];
322 for ( register unsigned int i
=0; i
<cat
.count(); ++i
) {
323 //test RA and dec to see if this object is roughly nearby
324 SkyObject
*test
= (SkyObject
*)cat
.at(i
);
325 double dRA
= test
->ra()->Hours()-p
->ra()->Hours();
326 double dDec
= test
->dec()->Degrees()-p
->dec()->Degrees();
327 double f
= 15.0*cos( test
->dec()->radians() );
328 double r
= f
*f
*dRA
*dRA
+ dDec
*dDec
; //no need to take sqrt, we just want to ID smallest value.
341 //Among the objects selected within r0, prioritize the selection by catalog:
342 //Planets, Messier, NGC, IC, stars
343 if ( istar_min
>= 0 && rstar_min
< r0
) {
345 icat
= 0; //set catalog to star
348 //IC object overrides star, unless star is twice as close as IC object
349 if ( iic_min
>= 0 && ric_min
< r0
&& rmin
> 0.5*ric_min
) {
351 icat
= 1; //set catalog to Deep Sky
355 //NGC object overrides previous selection, unless previous is twice as close
356 if ( ingc_min
>= 0 && rngc_min
< r0
&& rmin
> 0.5*rngc_min
) {
358 icat
= 1; //set catalog to Deep Sky
362 //"other" object overrides previous selection, unless previous is twice as close
363 if ( iother_min
>= 0 && rother_min
< r0
&& rmin
> 0.5*rother_min
) {
365 icat
= 1; //set catalog to Deep Sky
369 //Messier object overrides previous selection, unless previous is twice as close
370 if ( imess_min
>= 0 && rmess_min
< r0
&& rmin
> 0.5*rmess_min
) {
372 icat
= 1; //set catalog to Deep Sky
376 //Custom object overrides previous selection, unless previous is twice as close
377 if ( icust_min
>= 0 && rcust_min
< r0
&& rmin
> 0.5*rcust_min
) {
379 icat
= 2; //set catalog to Custom
382 //Solar system body overrides previous selection, unless previous selection is twice as close
383 if ( solarminobj
!= NULL
&& rmin
> 0.5*rsolar_min
) {
385 icat
= 3; //set catalog to solar system
388 QPtrList
<DeepSkyObject
> cat
;
392 return data
->starList
.at(istar_min
);
395 case 1: //Deep-Sky Objects
396 return data
->deepSkyList
.at(jmin
);
399 case 2: //Custom Catalog Object
400 cat
= data
->CustomCatalogs
[ Options::catalogName()[icust_cat
] ];
401 return cat
.at(icust_min
);
404 case 3: //solar system object
408 default: //no object found
414 void SkyMap::slotTransientLabel( void ) {
415 //This function is only called if the HoverTimer manages to timeout.
416 //(HoverTimer is restarted with every mouseMoveEvent; so if it times
417 //out, that means there was no mouse movement for HOVER_INTERVAL msec.)
418 //Identify the object nearest to the mouse cursor as the
419 //TransientObject. The TransientObject is automatically labeled
420 //in SkyMap::paintEvent().
421 //Note that when the TransientObject pointer is not NULL, the next
422 //mouseMoveEvent calls fadeTransientLabel(), which will fade out the
423 //TransientLabel and then set TransientObject to NULL.
425 //Do not show a transient label if the map is in motion, or if the mouse
426 //pointer is below the opaque horizon, or if the object has a permanent label
427 if ( ! slewing
&& ! ( Options::useAltAz() && Options::showGround() &&
428 mousePoint()->alt()->Degrees() < 0.0 ) ) {
429 SkyObject
*so
= objectNearest( mousePoint() );
431 if ( so
&& ! isObjectLabeled( so
) ) {
432 setTransientObject( so
);
434 TransientColor
= data
->colorScheme()->colorNamed( "UserLabelColor" );
435 if ( TransientTimer
.isActive() ) TransientTimer
.stop();
444 void SkyMap::slotTransientTimeout( void ) {
445 //to fade the labels, we will need to smoothly transition from UserLabelColor to SkyColor.
446 QColor c1
= data
->colorScheme()->colorNamed( "UserLabelColor" );
447 QColor c2
= data
->colorScheme()->colorNamed( "SkyColor" );
449 int dRed
= ( c2
.red() - c1
.red() )/20;
450 int dGreen
= ( c2
.green() - c1
.green() )/20;
451 int dBlue
= ( c2
.blue() - c1
.blue() )/20;
452 int newRed
= TransientColor
.red() + dRed
;
453 int newGreen
= TransientColor
.green() + dGreen
;
454 int newBlue
= TransientColor
.blue() + dBlue
;
456 //Check to see if we have arrived at the target color (SkyColor).
457 //If so, point TransientObject to NULL.
458 if ( abs(newRed
-c2
.red()) < abs(dRed
) || abs(newGreen
-c2
.green()) < abs(dGreen
) || abs(newBlue
-c2
.blue()) < abs(dBlue
) ) {
459 setTransientObject( NULL
);
460 TransientTimer
.stop();
462 TransientColor
.setRgb( newRed
, newGreen
, newBlue
);
468 void SkyMap::setFocusObject( SkyObject
*o
) {
472 Options::setFocusObject( FocusObject
->name() );
474 Options::setFocusObject( i18n( "nothing" ) );
477 void SkyMap::slotCenter( void ) {
478 setFocusPoint( clickedPoint() );
479 if ( Options::useAltAz() )
480 focusPoint()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
482 //clear the planet trail of old focusObject, if it was temporary
483 if ( focusObject() && focusObject()->isSolarSystem() && data
->temporaryTrail
) {
484 ((KSPlanetBase
*)focusObject())->clearTrail();
485 data
->temporaryTrail
= false;
488 //If the requested object is below the opaque horizon, issue a warning message
489 //(unless user is already pointed below the horizon)
490 if ( Options::useAltAz() && Options::showGround() &&
491 focus()->alt()->Degrees() > -1.0 && focusPoint()->alt()->Degrees() < -1.0 ) {
493 QString caption
= i18n( "Requested Position Below Horizon" );
494 QString message
= i18n( "The requested position is below the horizon.\nWould you like to go there anyway?" );
495 if ( KMessageBox::warningYesNo( this, message
, caption
,
496 KStdGuiItem::yes(), KStdGuiItem::no(), "dag_focus_below_horiz" )==KMessageBox::No
) {
497 setClickedObject( NULL
);
498 setFocusObject( NULL
);
499 Options::setIsTracking( false );
505 //set FocusObject before slewing. Otherwise, KStarsData::updateTime() can reset
506 //destination to previous object...
507 setFocusObject( ClickedObject
);
508 Options::setIsTracking( true );
510 ksw
->actionCollection()->action("track_object")->setIconSet( BarIcon( "encrypted" ) );
511 ksw
->toolBar( "mainToolBar" )->setButtonIconSet( 4, BarIcon( "encrypted" ) );
512 ksw
->actionCollection()->action("track_object")->setText( i18n( "Stop &Tracking" ) );
515 //If focusObject is a SS body and doesn't already have a trail, set the temporaryTrail
516 if ( focusObject() && focusObject()->isSolarSystem()
517 && Options::useAutoTrail()
518 && ! ((KSPlanetBase
*)focusObject())->hasTrail() ) {
519 ((KSPlanetBase
*)focusObject())->addToTrail();
520 data
->temporaryTrail
= true;
523 //update the destination to the selected coordinates
524 if ( Options::useAltAz() ) {
525 if ( Options::useRefraction() )
526 setDestinationAltAz( refract( focusPoint()->alt(), true ).Degrees(), focusPoint()->az()->Degrees() );
528 setDestinationAltAz( focusPoint()->alt()->Degrees(), focusPoint()->az()->Degrees() );
530 setDestination( focusPoint() );
533 //display coordinates in statusBar
535 QString s
= focusPoint()->az()->toDMSString() + ", " + focusPoint()->alt()->toDMSString(true);
536 ksw
->statusBar()->changeItem( s
, 1 );
537 s
= focusPoint()->ra()->toHMSString() + ", " + focusPoint()->dec()->toDMSString(true);
538 ksw
->statusBar()->changeItem( s
, 2 );
541 showFocusCoords(); //update FocusBox
544 void SkyMap::slotDSS( void ) {
545 QString
URLprefix( "http://archive.stsci.edu/cgi-bin/dss_search?v=1" );
546 QString
URLsuffix( "&e=J2000&h=15.0&w=15.0&f=gif&c=none&fov=NONE" );
547 dms
ra(0.0), dec(0.0);
548 QString RAString
, DecString
;
551 //ra and dec must be the coordinates at J2000. If we clicked on an object, just use the object's ra0, dec0 coords
552 //if we clicked on empty sky, we need to precess to J2000.
553 if ( clickedObject() ) {
554 ra
.setH( clickedObject()->ra0()->Hours() );
555 dec
.setD( clickedObject()->dec0()->Degrees() );
557 //move present coords temporarily to ra0,dec0 (needed for precessToAnyEpoch)
558 clickedPoint()->setRA0( clickedPoint()->ra()->Hours() );
559 clickedPoint()->setDec0( clickedPoint()->dec()->Degrees() );
560 clickedPoint()->precessFromAnyEpoch( data
->ut().djd(), J2000
);
561 ra
.setH( clickedPoint()->ra()->Hours() );
562 dec
.setD( clickedPoint()->dec()->Degrees() );
564 //restore coords from present epoch
565 clickedPoint()->setRA( clickedPoint()->ra0()->Hours() );
566 clickedPoint()->setDec( clickedPoint()->dec0()->Degrees() );
569 RAString
= RAString
.sprintf( "&r=%02d+%02d+%02d", ra
.hour(), ra
.minute(), ra
.second() );
572 if ( dec
.Degrees() < 0.0 ) decsgn
= '-';
573 int dd
= abs( dec
.degree() );
574 int dm
= abs( dec
.arcmin() );
575 int ds
= abs( dec
.arcsec() );
576 DecString
= DecString
.sprintf( "&d=%c%02d+%02d+%02d", decsgn
, dd
, dm
, ds
);
578 //concat all the segments into the kview command line:
579 KURL
url (URLprefix
+ RAString
+ DecString
+ URLsuffix
);
581 QString message
= i18n( "Digitized Sky Survey image provided by the Space Telescope Science Institute." );
582 new ImageViewer (&url
, message
, this);
585 void SkyMap::slotDSS2( void ) {
586 QString
URLprefix( "http://archive.stsci.edu/cgi-bin/dss_search?v=2r" );
587 QString
URLsuffix( "&e=J2000&h=15.0&w=15.0&f=gif&c=none&fov=NONE" );
588 dms
ra(0.0), dec(0.0);
589 QString RAString
, DecString
;
592 //ra and dec must be the coordinates at J2000. If we clicked on an object, just use the object's ra0, dec0 coords
593 //if we clicked on empty sky, we need to precess to J2000.
594 if ( clickedObject() ) {
595 ra
.setH( clickedObject()->ra0()->Hours() );
596 dec
.setD( clickedObject()->dec0()->Degrees() );
598 //move present coords temporarily to ra0,dec0 (needed for precessToAnyEpoch)
599 clickedPoint()->setRA0( clickedPoint()->ra()->Hours() );
600 clickedPoint()->setDec0( clickedPoint()->dec()->Degrees() );
601 clickedPoint()->precessFromAnyEpoch( data
->ut().djd(), J2000
);
602 ra
.setH( clickedPoint()->ra()->Hours() );
603 dec
.setD( clickedPoint()->dec()->Degrees() );
605 //restore coords from present epoch
606 clickedPoint()->setRA( clickedPoint()->ra0()->Hours() );
607 clickedPoint()->setDec( clickedPoint()->dec0()->Degrees() );
610 RAString
= RAString
.sprintf( "&r=%02d+%02d+%02d", ra
.hour(), ra
.minute(), ra
.second() );
613 if ( dec
.Degrees() < 0.0 ) decsgn
= '-';
614 int dd
= abs( dec
.degree() );
615 int dm
= abs( dec
.arcmin() );
616 int ds
= abs( dec
.arcsec() );
618 DecString
= DecString
.sprintf( "&d=%c%02d+%02d+%02d", decsgn
, dd
, dm
, ds
);
620 //concat all the segments into the kview command line:
621 KURL
url (URLprefix
+ RAString
+ DecString
+ URLsuffix
);
623 QString message
= i18n( "Digitized Sky Survey image provided by the Space Telescope Science Institute." );
624 new ImageViewer (&url
, message
, this);
627 void SkyMap::slotInfo( int id
) {
628 QStringList::Iterator it
= clickedObject()->InfoList
.at(id
-200);
629 QString sURL
= (*it
);
632 kapp
->invokeBrowser(sURL
);
635 void SkyMap::slotBeginAngularDistance(void) {
636 setPreviousClickedPoint( mousePoint() );
637 angularDistanceMode
= true;
638 beginRulerPoint
= getXY( previousClickedPoint(), Options::useAltAz(), Options::useRefraction() );
639 endRulerPoint
= QPoint( beginRulerPoint
.x(),beginRulerPoint
.y() );
642 void SkyMap::slotEndAngularDistance(void) {
644 if(angularDistanceMode
) {
645 if ( SkyObject
*so
= objectNearest( mousePoint() ) ) {
646 angularDistance
= so
->angularDistanceTo( previousClickedPoint() );
647 ksw
->statusBar()->changeItem( so
->translatedLongName() +
649 i18n("Angular distance: " ) +
650 angularDistance
.toDMSString(), 0 );
652 angularDistance
= mousePoint()->angularDistanceTo( previousClickedPoint() );
653 ksw
->statusBar()->changeItem( i18n("Angular distance: " ) +
654 angularDistance
.toDMSString(), 0 );
656 angularDistanceMode
=false;
660 void SkyMap::slotCancelAngularDistance(void) {
661 angularDistanceMode
=false;
664 void SkyMap::slotImage( int id
) {
665 QStringList::Iterator it
= clickedObject()->ImageList
.at(id
-100);
666 QStringList::Iterator it2
= clickedObject()->ImageTitle
.at(id
-100);
667 QString sURL
= (*it
);
668 QString message
= (*it2
);
671 new ImageViewer (&url
, clickedObject()->messageFromTitle(message
), this);
674 bool SkyMap::isObjectLabeled( SkyObject
*object
) {
675 for ( SkyObject
*o
= data
->ObjLabelList
.first(); o
; o
= data
->ObjLabelList
.next() ) {
676 if ( o
== object
) return true;
682 void SkyMap::slotRemoveObjectLabel( void ) {
683 for ( SkyObject
*o
= data
->ObjLabelList
.first(); o
; o
= data
->ObjLabelList
.next() ) {
684 if ( o
== clickedObject() ) {
685 //remove object from list
686 data
->ObjLabelList
.remove();
694 void SkyMap::slotAddObjectLabel( void ) {
695 data
->ObjLabelList
.append( clickedObject() );
696 //Since we just added a permanent label, we don't want it to fade away!
697 if ( transientObject() == clickedObject() ) setTransientObject( NULL
);
701 void SkyMap::slotRemovePlanetTrail( void ) {
702 //probably don't need this if-statement, but just to be sure...
703 if ( clickedObject() && clickedObject()->isSolarSystem() ) {
704 ((KSPlanetBase
*)clickedObject())->clearTrail();
709 void SkyMap::slotAddPlanetTrail( void ) {
710 //probably don't need this if-statement, but just to be sure...
711 if ( clickedObject() && clickedObject()->isSolarSystem() ) {
712 ((KSPlanetBase
*)clickedObject())->addToTrail();
717 void SkyMap::slotDetail( void ) {
718 // check if object is selected
719 if ( !clickedObject() ) {
720 KMessageBox::sorry( this, i18n("No object selected."), i18n("Object Details") );
723 DetailDialog
detail( clickedObject(), data
->ut(), data
->geo(), ksw
);
727 void SkyMap::slotClockSlewing() {
728 //If the current timescale exceeds slewTimeScale, set clockSlewing=true, and stop the clock.
729 if ( fabs( data
->clock()->scale() ) > Options::slewTimeScale() ) {
730 if ( ! clockSlewing
) {
732 data
->clock()->setManualMode( true );
734 // don't change automatically the DST status
735 if ( ksw
) ksw
->updateTime( false );
738 if ( clockSlewing
) {
739 clockSlewing
= false;
740 data
->clock()->setManualMode( false );
742 // don't change automatically the DST status
743 if ( ksw
) ksw
->updateTime( false );
748 void SkyMap::setFocus( SkyPoint
*p
) {
749 setFocus( p
->ra()->Hours(), p
->dec()->Degrees() );
752 void SkyMap::setFocus( const dms
&ra
, const dms
&dec
) {
753 setFocus( ra
.Hours(), dec
.Degrees() );
756 void SkyMap::setFocus( double ra
, double dec
) {
757 Focus
.set( ra
, dec
);
758 focus()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
761 void SkyMap::setFocusAltAz( const dms
&alt
, const dms
&az
) {
762 setFocusAltAz( alt
.Degrees(), az
.Degrees() );
765 void SkyMap::setFocusAltAz(double alt
, double az
) {
766 focus()->setAlt(alt
);
768 focus()->HorizontalToEquatorial( data
->LST
, data
->geo()->lat() );
771 oldfocus()->set( focus()->ra(), focus()->dec() );
772 oldfocus()->setAz( focus()->az()->Degrees() );
773 oldfocus()->setAlt( focus()->alt()->Degrees() );
775 double dHA
= data
->LST
->Hours() - focus()->ra()->Hours();
776 while ( dHA
< 0.0 ) dHA
+= 24.0;
777 data
->HourAngle
->setH( dHA
);
779 forceUpdate(); //need a total update, or slewing with the arrow keys doesn't work.
782 void SkyMap::setDestination( SkyPoint
*p
) {
783 Destination
.set( p
->ra(), p
->dec() );
784 destination()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
785 emit
destinationChanged();
788 void SkyMap::setDestination( const dms
&ra
, const dms
&dec
) {
789 Destination
.set( ra
, dec
);
790 destination()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
791 emit
destinationChanged();
794 void SkyMap::setDestination( double ra
, double dec
) {
795 Destination
.set( ra
, dec
);
796 destination()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
797 emit
destinationChanged();
800 void SkyMap::setDestinationAltAz( const dms
&alt
, const dms
&az
) {
801 destination()->setAlt(alt
);
802 destination()->setAz(az
);
803 destination()->HorizontalToEquatorial( data
->LST
, data
->geo()->lat() );
804 emit
destinationChanged();
807 void SkyMap::setDestinationAltAz(double alt
, double az
) {
808 destination()->setAlt(alt
);
809 destination()->setAz(az
);
810 destination()->HorizontalToEquatorial( data
->LST
, data
->geo()->lat() );
811 emit
destinationChanged();
814 void SkyMap::updateFocus() {
815 if ( Options::isTracking() && focusObject() != NULL
) {
816 if ( Options::useAltAz() ) {
817 //Tracking any object in Alt/Az mode requires focus updates
818 double dAlt
= focusObject()->alt()->Degrees();
819 if ( Options::useRefraction() )
820 dAlt
= refract( focusObject()->alt(), true ).Degrees();
821 setFocusAltAz( dAlt
, focusObject()->az()->Degrees() );
822 focus()->HorizontalToEquatorial( data
->LST
, data
->geo()->lat() );
823 setDestination( focus() );
825 //Tracking in equatorial coords
826 setFocus( focusObject() );
827 focus()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
828 setDestination( focus() );
830 } else if ( Options::isTracking() && focusPoint() != NULL
) {
831 if ( Options::useAltAz() ) {
832 //Tracking on empty sky in Alt/Az mode
833 setFocus( focusPoint() );
834 focus()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
835 setDestination( focus() );
837 } else if ( ! slewing
) {
838 //Not tracking and not slewing, let sky drift by
839 if ( Options::useAltAz() ) {
840 focus()->setAlt( destination()->alt()->Degrees() );
841 focus()->setAz( destination()->az()->Degrees() );
842 focus()->HorizontalToEquatorial( data
->LST
, data
->geo()->lat() );
843 //destination()->HorizontalToEquatorial( data->LST, data->geo()->lat() );
845 focus()->setRA( data
->LST
->Hours() - data
->HourAngle
->Hours() );
846 setDestination( focus() );
847 focus()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
848 destination()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
852 //Update the Hour Angle
853 data
->setHourAngle( data
->LST
->Hours() - focus()->ra()->Hours() );
855 setOldFocus( focus() );
856 oldfocus()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
859 void SkyMap::slewFocus( void ) {
860 double dX
, dY
, fX
, fY
, r
;
864 //Don't slew if the mouse button is pressed
865 //Also, no animated slews if the Manual Clock is active
866 //08/2002: added possibility for one-time skipping of slew with snapNextFocus
867 if ( !mouseButtonDown
) {
868 bool goSlew
= ( Options::useAnimatedSlewing() &&
869 ! data
->snapNextFocus() ) &&
870 !( data
->clock()->isManualMode() && data
->clock()->isActive() );
872 if ( Options::useAltAz() ) {
873 dX
= destination()->az()->Degrees() - focus()->az()->Degrees();
874 dY
= destination()->alt()->Degrees() - focus()->alt()->Degrees();
876 dX
= destination()->ra()->Degrees() - focus()->ra()->Degrees();
877 dY
= destination()->dec()->Degrees() - focus()->dec()->Degrees();
880 //switch directions to go the short way around the celestial sphere, if necessary.
881 if ( dX
< -180.0 ) dX
= 360.0 + dX
;
882 else if ( dX
> 180.0 ) dX
= -360.0 + dX
;
884 r
= sqrt( dX
*dX
+ dY
*dY
);
890 if ( Options::useAltAz() ) {
891 focus()->setAlt( focus()->alt()->Degrees() + fY
*step
);
892 focus()->setAz( dms( focus()->az()->Degrees() + fX
*step
).reduce() );
893 focus()->HorizontalToEquatorial( data
->LST
, data
->geo()->lat() );
895 fX
= fX
/15.; //convert RA degrees to hours
896 newFocus
.set( focus()->ra()->Hours() + fX
*step
, focus()->dec()->Degrees() + fY
*step
);
897 setFocus( &newFocus
);
898 focus()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
902 //since we are slewing, fade out the transient label
903 if ( transientObject() && ! TransientTimer
.isActive() )
904 fadeTransientLabel();
907 kapp
->processEvents(10); //keep up with other stuff
909 if ( Options::useAltAz() ) {
910 dX
= destination()->az()->Degrees() - focus()->az()->Degrees();
911 dY
= destination()->alt()->Degrees() - focus()->alt()->Degrees();
913 dX
= destination()->ra()->Degrees() - focus()->ra()->Degrees();
914 dY
= destination()->dec()->Degrees() - focus()->dec()->Degrees();
917 //switch directions to go the short way around the celestial sphere, if necessary.
918 if ( dX
< -180.0 ) dX
= 360.0 + dX
;
919 else if ( dX
> 180.0 ) dX
= -360.0 + dX
;
921 r
= sqrt( dX
*dX
+ dY
*dY
);
925 //Either useAnimatedSlewing==false, or we have slewed, and are within one step of destination
926 //set focus=destination.
927 if ( Options::useAltAz() ) {
928 setFocusAltAz( destination()->alt()->Degrees(), destination()->az()->Degrees() );
929 focus()->HorizontalToEquatorial( data
->LST
, data
->geo()->lat() );
931 setFocus( destination() );
932 focus()->EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
935 data
->HourAngle
->setH( data
->LST
->Hours() - focus()->ra()->Hours() );
938 //Turn off snapNextFocus, we only want it to happen once
939 if ( data
->snapNextFocus() ) {
940 data
->setSnapNextFocus(false);
943 //Start the HoverTimer. if the user leaves the mouse in place after a slew,
944 //we want to attach a label to the nearest object.
945 if ( Options::useHoverLabel() )
946 HoverTimer
.start( HOVER_INTERVAL
, true );
952 void SkyMap::invokeKey( int key
) {
953 QKeyEvent
*e
= new QKeyEvent( QEvent::KeyPress
, key
, 0, 0 );
958 double SkyMap::findPA( SkyObject
*o
, int x
, int y
, double scale
) {
959 //Find position angle of North using a test point displaced to the north
960 //displace by 100/zoomFactor radians (so distance is always 100 pixels)
961 //this is 5730/zoomFactor degrees
962 double newDec
= o
->dec()->Degrees() + 5730.0/Options::zoomFactor();
963 if ( newDec
> 90.0 ) newDec
= 90.0;
964 SkyPoint
test( o
->ra()->Hours(), newDec
);
965 if ( Options::useAltAz() ) test
.EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
966 QPoint t
= getXY( &test
, Options::useAltAz(), Options::useRefraction(), scale
);
967 double dx
= double( t
.x() - x
);
968 double dy
= double( y
- t
.y() ); //backwards because QWidget Y-axis increases to the bottom
971 north
= atan( dx
/dy
)*180.0/dms::PI
;
972 //resolve atan ambiguity:
973 if ( dy
< 0.0 ) north
+= 180.0;
974 if ( north
>= 360.0 ) north
-= 360.;
977 if ( dx
> 0 ) north
= -90.0;
980 return ( north
+ o
->pa() );
983 QPoint
SkyMap::getXY( SkyPoint
*o
, bool Horiz
, bool doRefraction
, double scale
) {
987 double sindX
, cosdX
, sinY
, cosY
, sinY0
, cosY0
;
989 int Width
= int( width() * scale
);
990 int Height
= int( height() * scale
);
992 double pscale
= Options::zoomFactor() * scale
;
995 if ( doRefraction
) Y
= refract( o
->alt(), true ).radians(); //account for atmospheric refraction
996 else Y
= o
->alt()->radians();
998 if ( focus()->az()->Degrees() > 270.0 && o
->az()->Degrees() < 90.0 ) {
999 dX
= 2*dms::PI
+ focus()->az()->radians() - o
->az()->radians();
1001 dX
= focus()->az()->radians() - o
->az()->radians();
1004 focus()->alt()->SinCos( sinY0
, cosY0
);
1007 if (focus()->ra()->Hours() > 18.0 && o
->ra()->Hours() < 6.0) {
1008 dX
= 2*dms::PI
+ o
->ra()->radians() - focus()->ra()->radians();
1010 dX
= o
->ra()->radians() - focus()->ra()->radians();
1012 Y
= o
->dec()->radians();
1013 focus()->dec()->SinCos( sinY0
, cosY0
);
1016 //Convert dX, Y coords to screen pixel coords.
1017 #if ( __GLIBC__ >= 2 && __GLIBC_MINOR__ >=1 )
1019 sincos( dX
, &sindX
, &cosdX
);
1020 sincos( Y
, &sinY
, &cosY
);
1029 double c
= sinY0
*sinY
+ cosY0
*cosY
*cosdX
;
1031 if ( c
< 0.0 ) { //Object is on "back side" of the celestial sphere; don't plot it.
1032 p
.setX( -10000000 );
1033 p
.setY( -10000000 );
1037 double k
= sqrt( 2.0/( 1 + c
) );
1039 p
.setX( int( 0.5*Width
- pscale
*k
*cosY
*sindX
) );
1040 p
.setY( int( 0.5*Height
- pscale
*k
*( cosY0
*sinY
- sinY0
*cosY
*cosdX
) ) );
1045 SkyPoint
SkyMap::dXdYToRaDec( double dx
, double dy
, bool useAltAz
, dms
*LST
, const dms
*lat
, bool doRefract
) {
1046 //Determine RA and Dec of a point, given (dx, dy): it's pixel
1047 //coordinates in the SkyMap with the center of the map as the origin.
1050 double sinDec
, cosDec
, sinDec0
, cosDec0
, sinc
, cosc
, sinlat
, coslat
;
1053 double r
= sqrt( dx
*dx
+ dy
*dy
);
1055 centerAngle
.setRadians( 2.0*asin(0.5*r
) );
1057 focus()->dec()->SinCos( sinDec0
, cosDec0
);
1058 centerAngle
.SinCos( sinc
, cosc
);
1062 dms Dec
, alt
, az
, alt0
, az0
;
1064 double sinAlt
, cosAlt
, sinAlt0
, cosAlt0
, sinAz
, cosAz
;
1065 // double HA0 = LST - focus.ra();
1066 az0
= focus()->az()->Degrees();
1067 alt0
= focus()->alt()->Degrees();
1068 alt0
.SinCos( sinAlt0
, cosAlt0
);
1070 dx
= -dx
; //Flip East-west (Az goes in opposite direction of RA)
1072 xx
= r
*cosAlt0
*cosc
- dy
*sinAlt0
*sinc
;
1075 //resolve ambiguity of atan():
1076 if ( xx
<0 ) A
= A
+ dms::PI
;
1077 // if ( xx>0 && yy<0 ) A = A + 2.0*dms::PI;
1080 deltaAz
.setRadians( A
);
1081 az
= focus()->az()->Degrees() + deltaAz
.Degrees();
1082 alt
.setRadians( asin( cosc
*sinAlt0
+ ( dy
*sinc
*cosAlt0
)/r
) );
1084 if ( doRefract
) alt
.setD( refract( &alt
, false ).Degrees() ); //find true altitude from apparent altitude
1086 az
.SinCos( sinAz
, cosAz
);
1087 alt
.SinCos( sinAlt
, cosAlt
);
1088 lat
->SinCos( sinlat
, coslat
);
1090 Dec
.setRadians( asin( sinAlt
*sinlat
+ cosAlt
*coslat
*cosAz
) );
1091 Dec
.SinCos( sinDec
, cosDec
);
1093 HA
.setRadians( acos( ( sinAlt
- sinlat
*sinDec
)/( coslat
*cosDec
) ) );
1094 if ( sinAz
> 0.0 ) HA
.setH( 24.0 - HA
.Hours() );
1096 result
.setRA( LST
->Hours() - HA
.Hours() );
1097 result
.setRA( result
.ra()->reduce() );
1098 result
.setDec( Dec
.Degrees() );
1104 xx
= r
*cosDec0
*cosc
- dy
*sinDec0
*sinc
;
1106 double RARad
= ( atan( yy
/ xx
) );
1107 //resolve ambiguity of atan():
1108 if ( xx
<0 ) RARad
= RARad
+ dms::PI
;
1109 // if ( xx>0 && yy<0 ) RARad = RARad + 2.0*dms::PI;
1112 deltaRA
.setRadians( RARad
);
1113 Dec
.setRadians( asin( cosc
*sinDec0
+ (dy
*sinc
*cosDec0
)/r
) );
1115 result
.setRA( focus()->ra()->Hours() + deltaRA
.Hours() );
1116 result
.setRA( result
.ra()->reduce() );
1117 result
.setDec( Dec
.Degrees() );
1123 dms
SkyMap::refract( const dms
*alt
, bool findApparent
) {
1124 if ( alt
->Degrees() <= -2.000 ) return dms( alt
->Degrees() );
1126 int index
= int( ( alt
->Degrees() + 2.0 )*2. ); //RefractCorr arrays start at alt=-2.0 degrees.
1129 if ( findApparent
) {
1130 result
.setD( alt
->Degrees() + RefractCorr1
[index
] );
1132 result
.setD( alt
->Degrees() + RefractCorr2
[index
] );
1138 //---------------------------------------------------------------------------
1141 // force a new calculation of the skymap (used instead of update(), which may skip the redraw)
1142 // if now=true, SkyMap::paintEvent() is run immediately, rather than being added to the event queue
1143 // also, determine new coordinates of mouse cursor.
1144 void SkyMap::forceUpdate( bool now
)
1146 QPoint
mp( mapFromGlobal( QCursor::pos() ) );
1147 double dx
= ( 0.5*width() - mp
.x() )/Options::zoomFactor();
1148 double dy
= ( 0.5*height() - mp
.y() )/Options::zoomFactor();
1150 if (! unusablePoint (dx
, dy
)) {
1151 //determine RA, Dec of mouse pointer
1152 setMousePoint( dXdYToRaDec( dx
, dy
, Options::useAltAz(), data
->LST
, data
->geo()->lat(), Options::useRefraction() ) );
1155 computeSkymap
= true;
1156 if ( now
) repaint();
1160 float SkyMap::fov() {
1161 if ( width() >= height() )
1162 return 28.65*width()/Options::zoomFactor();
1164 return 28.65*height()/Options::zoomFactor();
1167 bool SkyMap::checkVisibility( SkyPoint
*p
, float FOV
, double XMax
) {
1169 bool useAltAz
= Options::useAltAz();
1171 //Skip objects below the horizon if:
1172 // + using Horizontal coords,
1173 // + the ground is drawn,
1174 // + and either of the following is true:
1175 // - focus is above the horizon
1176 // - field of view is larger than 50 degrees
1177 if ( useAltAz
&& Options::showGround() && p
->alt()->Degrees() < -2.0
1178 && ( focus()->alt()->Degrees() > 0. || FOV
> 50. ) ) return false;
1181 dY
= fabs( p
->alt()->Degrees() - focus()->alt()->Degrees() );
1183 dY
= fabs( p
->dec()->Degrees() - focus()->dec()->Degrees() );
1185 if ( isPoleVisible
) dY
*= 0.75; //increase effective FOV when pole visible.
1186 if ( dY
> FOV
) return false;
1187 if ( isPoleVisible
) return true;
1190 dX
= fabs( p
->az()->Degrees() - focus()->az()->Degrees() );
1192 dX
= fabs( p
->ra()->Degrees() - focus()->ra()->Degrees() );
1194 if ( dX
> 180.0 ) dX
= 360.0 - dX
; // take shorter distance around sky
1203 bool SkyMap::unusablePoint (double dx
, double dy
)
1205 if (dx
>= 1.41 || dx
<= -1.41 || dy
>= 1.41 || dy
<= -1.41)
1211 void SkyMap::setZoomMouseCursor()
1213 mouseMoveCursor
= false; // no mousemove cursor
1216 QPixmap
cursorPix (32, 32); // size 32x32 (this size is compatible to all systems)
1217 // the center of the pixmap
1218 int mx
= cursorPix
. width() / 2;
1219 int my
= cursorPix
. height() / 2;
1221 cursorPix
.fill (white
); // white background
1222 p
.begin (&cursorPix
);
1223 p
.setPen (QPen (black
, 2)); // black lines
1225 p
.drawEllipse( mx
- 7, my
- 7, 14, 14 );
1226 p
.drawLine( mx
+ 5, my
+ 5, mx
+ 11, my
+ 11 );
1229 // create a mask to make parts of the pixmap invisible
1230 QBitmap
mask (32, 32);
1231 mask
.fill (color0
); // all is invisible
1234 // paint over the parts which should be visible
1235 p
.setPen (QPen (color1
, 3));
1236 p
.drawEllipse( mx
- 7, my
- 7, 14, 14 );
1237 p
.drawLine( mx
+ 5, my
+ 5, mx
+ 12, my
+ 12 );
1240 cursorPix
.setMask (mask
); // set the mask
1241 QCursor
cursor (cursorPix
);
1245 void SkyMap::setDefaultMouseCursor()
1247 mouseMoveCursor
= false; // no mousemove cursor
1250 QPixmap
cursorPix (32, 32); // size 32x32 (this size is compatible to all systems)
1251 // the center of the pixmap
1252 int mx
= cursorPix
. width() / 2;
1253 int my
= cursorPix
. height() / 2;
1255 cursorPix
.fill (white
); // white background
1256 p
.begin (&cursorPix
);
1257 p
.setPen (QPen (black
, 2)); // black lines
1259 p
.drawLine (mx
- 2, my
- 2, mx
- 8, mx
- 8);
1260 p
.drawLine (mx
+ 2, my
+ 2, mx
+ 8, mx
+ 8);
1262 p
.drawLine (mx
- 2, my
+ 2, mx
- 8, mx
+ 8);
1263 p
.drawLine (mx
+ 2, my
- 2, mx
+ 8, mx
- 8);
1266 // create a mask to make parts of the pixmap invisible
1267 QBitmap
mask (32, 32);
1268 mask
.fill (color0
); // all is invisible
1271 // paint over the parts which should be visible
1272 p
.setPen (QPen (color1
, 3));
1274 p
.drawLine (mx
- 2, my
- 2, mx
- 8, mx
- 8);
1275 p
.drawLine (mx
+ 2, my
+ 2, mx
+ 8, mx
+ 8);
1277 p
.drawLine (mx
- 2, my
+ 2, mx
- 8, mx
+ 8);
1278 p
.drawLine (mx
+ 2, my
- 2, mx
+ 8, mx
- 8);
1281 cursorPix
.setMask (mask
); // set the mask
1282 QCursor
cursor (cursorPix
);
1286 void SkyMap::setMouseMoveCursor()
1288 if (mouseButtonDown
)
1290 setCursor (9); // cursor shape defined in qt
1291 mouseMoveCursor
= true;
1295 void SkyMap::addLink( void ) {
1296 AddLinkDialog
adialog( this, clickedObject()->name() );
1300 if ( adialog
.exec()==QDialog::Accepted
) {
1301 if ( adialog
.isImageLink() ) {
1302 //Add link to object's ImageList, and descriptive text to its ImageTitle list
1303 clickedObject()->ImageList
.append( adialog
.url() );
1304 clickedObject()->ImageTitle
.append( adialog
.desc() );
1306 //Also, update the user's custom image links database
1307 //check for user's image-links database. If it doesn't exist, create it.
1308 file
.setName( locateLocal( "appdata", "image_url.dat" ) ); //determine filename in local user KDE directory tree.
1310 if ( !file
.open( IO_ReadWrite
| IO_Append
) ) {
1311 QString message
= i18n( "Custom image-links file could not be opened.\nLink cannot be recorded for future sessions." );
1312 KMessageBox::sorry( 0, message
, i18n( "Could Not Open File" ) );
1315 entry
= clickedObject()->name() + ":" + adialog
.desc() + ":" + adialog
.url();
1316 QTextStream
stream( &file
);
1317 stream
<< entry
<< endl
;
1322 clickedObject()->InfoList
.append( adialog
.url() );
1323 clickedObject()->InfoTitle
.append( adialog
.desc() );
1325 //check for user's image-links database. If it doesn't exist, create it.
1326 file
.setName( locateLocal( "appdata", "info_url.dat" ) ); //determine filename in local user KDE directory tree.
1328 if ( !file
.open( IO_ReadWrite
| IO_Append
) ) {
1329 QString message
= i18n( "Custom information-links file could not be opened.\nLink cannot be recorded for future sessions." ); KMessageBox::sorry( 0, message
, i18n( "Could not Open File" ) );
1332 entry
= clickedObject()->name() + ":" + adialog
.desc() + ":" + adialog
.url();
1333 QTextStream
stream( &file
);
1334 stream
<< entry
<< endl
;
1342 void SkyMap::updateAngleRuler() {
1343 if ( Options::useAltAz() ) PreviousClickedPoint
.EquatorialToHorizontal( data
->LST
, data
->geo()->lat() );
1344 beginRulerPoint
= getXY( previousClickedPoint(), Options::useAltAz(), Options::useRefraction() );
1346 // endRulerPoint = QPoint(e->x(), e->y());
1347 endRulerPoint
= mapFromGlobal( QCursor::pos() );
1350 #include "skymap.moc"