/*************************************************************************** skymapevents.cpp - Trinity Desktop Planetarium ------------------- begin : Sat Feb 10 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ //This file contains Event handlers for the SkyMap class. #include #include //using fabs() #include #include #include #include #include #include //KKey class #include "skymap.h" #include "Options.h" #include "kstars.h" #include "kstarsdata.h" #include "ksutils.h" #include "simclock.h" #include "infoboxes.h" #include "ksfilereader.h" #include "kspopupmenu.h" #include "ksmoon.h" void SkyMap::resizeEvent( TQResizeEvent * ) { computeSkymap = true; // skymap must be new computed if ( testWState(WState_AutoMask) ) updateMask(); // avoid phantom positions of infoboxes if ( ksw && ( isVisible() || width() == ksw->width() || height() == ksw->height() ) ) { infoBoxes()->resize( width(), height() ); } sky->resize( width(), height() ); sky2->resize( width(), height() ); } void SkyMap::keyPressEvent( TQKeyEvent *e ) { TQString s; bool arrowKeyPressed( false ); bool shiftPressed( false ); float step = 1.0; if ( e->state() & ShiftButton ) { step = 10.0; shiftPressed = true; } //If the DCOP resume key was pressed, we process it here if ( ! data->resumeKey.isNull() && e->key() == data->resumeKey.keyCodeQt() ) { //kdDebug() << "resumeKey pressed; resuming DCOP." << endl; ksw->resumeDCOP(); return; } switch ( e->key() ) { case Key_Left : if ( Options::useAltAz() ) { focus()->setAz( dms( focus()->az()->Degrees() - step * MINZOOM/Options::zoomFactor() ).reduce() ); focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() ); } else { focus()->setRA( focus()->ra()->Hours() + 0.05*step * MINZOOM/Options::zoomFactor() ); focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() ); } arrowKeyPressed = true; slewing = true; ++scrollCount; break; case Key_Right : if ( Options::useAltAz() ) { focus()->setAz( dms( focus()->az()->Degrees() + step * MINZOOM/Options::zoomFactor() ).reduce() ); focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() ); } else { focus()->setRA( focus()->ra()->Hours() - 0.05*step * MINZOOM/Options::zoomFactor() ); focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() ); } arrowKeyPressed = true; slewing = true; ++scrollCount; break; case Key_Up : if ( Options::useAltAz() ) { focus()->setAlt( focus()->alt()->Degrees() + step * MINZOOM/Options::zoomFactor() ); if ( focus()->alt()->Degrees() > 90.0 ) focus()->setAlt( 90.0 ); focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() ); } else { focus()->setDec( focus()->dec()->Degrees() + step * MINZOOM/Options::zoomFactor() ); if (focus()->dec()->Degrees() > 90.0) focus()->setDec( 90.0 ); focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() ); } arrowKeyPressed = true; slewing = true; ++scrollCount; break; case Key_Down: if ( Options::useAltAz() ) { focus()->setAlt( focus()->alt()->Degrees() - step * MINZOOM/Options::zoomFactor() ); if ( focus()->alt()->Degrees() < -90.0 ) focus()->setAlt( -90.0 ); focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() ); } else { focus()->setDec( focus()->dec()->Degrees() - step * MINZOOM/Options::zoomFactor() ); if (focus()->dec()->Degrees() < -90.0) focus()->setDec( -90.0 ); focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() ); } arrowKeyPressed = true; slewing = true; ++scrollCount; break; case Key_Plus: //Zoom in case Key_Equal: if ( ksw ) ksw->slotZoomIn(); break; case Key_Minus: //Zoom out case Key_Underscore: if ( ksw ) ksw->slotZoomOut(); break; //In the following cases, we set slewing=true in order to disengage tracking case Key_N: //center on north horizon stopTracking(); setDestinationAltAz( 15.0, 0.0001 ); break; case Key_E: //center on east horizon stopTracking(); setDestinationAltAz( 15.0, 90.0 ); break; case Key_S: //center on south horizon stopTracking(); setDestinationAltAz( 15.0, 180.0 ); break; case Key_W: //center on west horizon stopTracking(); setDestinationAltAz( 15.0, 270.0 ); break; case Key_Z: //center on Zenith stopTracking(); setDestinationAltAz( 90.0, focus()->az()->Degrees() ); break; case Key_0: //center on Sun setClickedObject( data->PCat->findByName("Sun") ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_1: //center on Mercury setClickedObject( data->PCat->findByName("Mercury") ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_2: //center on Venus setClickedObject( data->PCat->findByName("Venus") ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_3: //center on Moon setClickedObject( data->Moon ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_4: //center on Mars setClickedObject( data->PCat->findByName("Mars") ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_5: //center on Jupiter setClickedObject( data->PCat->findByName("Jupiter") ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_6: //center on Saturn setClickedObject( data->PCat->findByName("Saturn") ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_7: //center on Uranus setClickedObject( data->PCat->findByName("Uranus") ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_8: //center on Neptune setClickedObject( data->PCat->findByName("Neptune") ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_9: //center on Pluto setClickedObject( data->PCat->findByName("Pluto") ); setClickedPoint( clickedObject() ); slotCenter(); break; case Key_BracketLeft: // Begin measuring angular distance if ( !isAngleMode() ) { slotBeginAngularDistance(); } break; case Key_BracketRight: // End measuring angular distance if ( isAngleMode() ) { slotEndAngularDistance(); } break; case Key_Escape: // Cancel angular distance measurement if (isAngleMode() ) { slotCancelAngularDistance(); } break; case Key_Comma: //advance one step backward in time case Key_Less: if ( data->clock()->isActive() ) data->clock()->stop(); data->clock()->setScale( -1.0 * data->clock()->scale() ); //temporarily need negative time step data->clock()->manualTick( true ); data->clock()->setScale( -1.0 * data->clock()->scale() ); //reset original sign of time step update(); kapp->processEvents(); break; case Key_Period: //advance one step forward in time case Key_Greater: if ( data->clock()->isActive() ) data->clock()->stop(); data->clock()->manualTick( true ); update(); kapp->processEvents(); break; //FIXME: Uncomment after feature thaw! // case Key_C: //Center clicked object object // if ( clickedObject() ) slotCenter(); // break; case Key_D: //Details window for Clicked/Centered object if ( shiftPressed ) setClickedObject( focusObject() ); if ( clickedObject() ) slotDetail(); break; case Key_P: //Show Popup menu for Clicked/Centered object if ( shiftPressed ) setClickedObject( focusObject() ); if ( clickedObject() ) clickedObject()->showPopupMenu( pmenu, TQCursor::pos() ); break; case Key_O: //Add object to Observing List if ( shiftPressed ) setClickedObject( focusObject() ); if ( clickedObject() ) ksw->observingList()->slotAddObject(); break; case Key_L: //Toggle User label on Clicked/Centered object if ( shiftPressed ) setClickedObject( focusObject() ); if ( clickedObject() ) { if ( isObjectLabeled( clickedObject() ) ) slotRemoveObjectLabel(); else slotAddObjectLabel(); } break; case Key_T: //Toggle planet trail on Clicked/Centered object (if solsys) if ( shiftPressed ) setClickedObject( focusObject() ); if ( clickedObject() && clickedObject()->isSolarSystem() ) { if ( ((KSPlanetBase*)clickedObject())->hasTrail() ) slotRemovePlanetTrail(); else slotAddPlanetTrail(); } break; //TIMING /* case Key_G: //loop through all cities { TQFile file; if ( KSUtils::openDataFile( file, "Cities.dat" ) ) { KSFileReader fileReader( file ); int nCount = 0; while (fileReader.hasMoreLines()) { TQString line = fileReader.readLine(); nCount++; kdDebug() << "Line " << nCount << " : " << line; } } TQTime t1; t1.start(); for (int i=0;i<10;i++) { if ( KSUtils::openDataFile( file, "Cities.dat" ) ) { TQString sAll( file.readAll() ); TQStringList lines = TQStringList::split( "\n", sAll ); int nSize = lines.size(); for ( int i=0; isetAz( focus()->az()->Degrees() ); oldfocus()->setAlt( focus()->alt()->Degrees() ); double dHA = data->LST->Hours() - focus()->ra()->Hours(); while ( dHA < 0.0 ) dHA += 24.0; data->HourAngle->setH( dHA ); if ( arrowKeyPressed ) { infoBoxes()->focusObjChanged( i18n( "nothing" ) ); stopTracking(); if ( scrollCount > 10 ) { setDestination( focus() ); scrollCount = 0; } } forceUpdate(); //need a total update, or slewing with the arrow keys doesn't work. } void SkyMap::stopTracking() { if ( ksw && Options::isTracking() ) ksw->slotTrack(); } void SkyMap::keyReleaseEvent( TQKeyEvent *e ) { switch ( e->key() ) { case Key_Left : //no break; continue to Key_Down case Key_Right : //no break; continue to Key_Down case Key_Up : //no break; continue to Key_Down case Key_Down : slewing = false; scrollCount = 0; if ( Options::useAltAz() ) setDestinationAltAz( focus()->alt()->Degrees(), focus()->az()->Degrees() ); else setDestination( focus() ); showFocusCoords( true ); forceUpdate(); // Need a full update to draw faint objects that are not drawn while slewing. break; } } void SkyMap::mouseMoveEvent( TQMouseEvent *e ) { if ( Options::useHoverLabel() ) { //First of all, if the transientObject() pointer is not NULL, then //we just moved off of a hovered object. Begin fading the label. if ( transientObject() && ! TransientTimer.isActive() ) { fadeTransientLabel(); } //Start a single-shot timer to monitor whether we are currently hovering. //The idea is that whenever a moveEvent occurs, the timer is reset. It //will only timeout if there are no move events for HOVER_INTERVAL ms HoverTimer.start( HOVER_INTERVAL, true ); } //Are we dragging an infoBox? if ( infoBoxes()->dragBox( e ) ) { update(); return; } //Are we defining a ZoomRect? if ( ZoomRect.center().x() > 0 && ZoomRect.center().y() > 0 ) { //cancel operation if the user let go of CTRL if ( !( e->state() & ControlButton ) ) { ZoomRect = TQRect(); //invalidate ZoomRect update(); } else { //Resize the rectangle so that it passes through the cursor position TQPoint pcenter = ZoomRect.center(); int dx = abs(e->x() - pcenter.x()); int dy = abs(e->y() - pcenter.y()); if ( dx == 0 || float(dy)/float(dx) > float(height())/float(width()) ) { //Size rect by height ZoomRect.setHeight( 2*dy ); ZoomRect.setWidth( 2*dy*width()/height() ); } else { //Size rect by height ZoomRect.setWidth( 2*dx ); ZoomRect.setHeight( 2*dx*height()/width() ); } ZoomRect.moveCenter( pcenter ); //reset center update(); return; } } double dx = ( 0.5*width() - e->x() )/Options::zoomFactor(); double dy = ( 0.5*height() - e->y() )/Options::zoomFactor(); double dyPix = 0.5*height() - e->y(); if (unusablePoint (dx, dy)) return; // break if point is unusable //determine RA, Dec of mouse pointer setMousePoint( dXdYToRaDec( dx, dy, Options::useAltAz(), data->LST, data->geo()->lat(), Options::useRefraction() ) ); mousePoint()->EquatorialToHorizontal( data->LST, data->geo()->lat() ); if ( midMouseButtonDown ) { //zoom according to y-offset float yoff = dyPix - y0; if (yoff > 10 ) { y0 = dyPix; if ( ksw ) ksw->slotZoomIn(); } if (yoff < -10 ) { y0 = dyPix; if ( ksw ) ksw->slotZoomOut(); } } if ( mouseButtonDown ) { // set the mouseMoveCursor and set slewing=true, if they are not set yet if (!mouseMoveCursor) setMouseMoveCursor(); if (!slewing) { slewing = true; infoBoxes()->focusObjChanged( i18n( "nothing" ) ); stopTracking(); //toggle tracking off } //Update focus such that the sky coords at mouse cursor remain approximately constant if ( Options::useAltAz() ) { mousePoint()->EquatorialToHorizontal( data->LST, data->geo()->lat() ); clickedPoint()->EquatorialToHorizontal( data->LST, data->geo()->lat() ); dms dAz = mousePoint()->az()->Degrees() - clickedPoint()->az()->Degrees(); dms dAlt = mousePoint()->alt()->Degrees() - clickedPoint()->alt()->Degrees(); focus()->setAz( focus()->az()->Degrees() - dAz.Degrees() ); //move focus in opposite direction focus()->setAlt( focus()->alt()->Degrees() - dAlt.Degrees() ); if ( focus()->alt()->Degrees() >90.0 ) focus()->setAlt( 90.0 ); if ( focus()->alt()->Degrees() <-90.0 ) focus()->setAlt( -90.0 ); focus()->setAz( focus()->az()->reduce() ); focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() ); } else { dms dRA = mousePoint()->ra()->Degrees() - clickedPoint()->ra()->Degrees(); dms dDec = mousePoint()->dec()->Degrees() - clickedPoint()->dec()->Degrees(); focus()->setRA( focus()->ra()->Hours() - dRA.Hours() ); //move focus in opposite direction focus()->setDec( focus()->dec()->Degrees() - dDec.Degrees() ); if ( focus()->dec()->Degrees() >90.0 ) focus()->setDec( 90.0 ); if ( focus()->dec()->Degrees() <-90.0 ) focus()->setDec( -90.0 ); focus()->setRA( focus()->ra()->reduce() ); focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() ); } ++scrollCount; if ( scrollCount > 4 ) { showFocusCoords( true ); scrollCount = 0; } setOldFocus( focus() ); double dHA = data->LST->Hours() - focus()->ra()->Hours(); while ( dHA < 0.0 ) dHA += 24.0; data->HourAngle->setH( dHA ); //redetermine RA, Dec of mouse pointer, using new focus setMousePoint( dXdYToRaDec( dx, dy, Options::useAltAz(), data->LST, data->geo()->lat(), Options::useRefraction() ) ); setClickedPoint( mousePoint() ); forceUpdate(); // must be new computed } else { if ( ksw ) { TQString sX, sY, s; sX = mousePoint()->az()->toDMSString(true); //true = force +/- symbol sY = mousePoint()->alt()->toDMSString(true); //true=force +/- symbol if ( Options::useAltAz() && Options::useRefraction() ) sY = refract( mousePoint()->alt(), true ).toDMSString(true); s = sX + ", " + sY; ksw->statusBar()->changeItem( s, 1 ); sX = mousePoint()->ra()->toHMSString(); sY = mousePoint()->dec()->toDMSString(true); //true = force +/- symbol s = sX + ", " + sY; ksw->statusBar()->changeItem( s, 2 ); } } } void SkyMap::wheelEvent( TQWheelEvent *e ) { if ( ksw && e->delta() > 0 ) ksw->slotZoomIn(); else if ( ksw ) ksw->slotZoomOut(); } void SkyMap::mouseReleaseEvent( TQMouseEvent * ) { if ( infoBoxes()->unGrabBox() ) { update(); return; } if ( ZoomRect.isValid() ) { //Zoom in on center of Zoom Circle, by a factor equal to the ratio //of the sky pixmap's width to the Zoom Circle's diameter float factor = float(width()) / float(ZoomRect.width()); double dx = ( 0.5*width() - ZoomRect.center().x() )/Options::zoomFactor(); double dy = ( 0.5*height() - ZoomRect.center().y() )/Options::zoomFactor(); infoBoxes()->focusObjChanged( i18n( "nothing" ) ); stopTracking(); SkyPoint newcenter = dXdYToRaDec( dx, dy, Options::useAltAz(), data->LST, data->geo()->lat(), Options::useRefraction() ); setFocus( &newcenter ); setDestination( &newcenter ); ksw->zoom( Options::zoomFactor() * factor ); setDefaultMouseCursor(); ZoomRect = TQRect(); //invalidate ZoomRect forceUpdate(); } else { setDefaultMouseCursor(); ZoomRect = TQRect(); //just in case user Ctrl+clicked + released w/o dragging... } if (mouseMoveCursor) setDefaultMouseCursor(); // set default cursor if (mouseButtonDown) { //false if double-clicked, because it's unset there. mouseButtonDown = false; if ( slewing ) { slewing = false; if ( Options::useAltAz() ) setDestinationAltAz( focus()->alt()->Degrees(), focus()->az()->Degrees() ); else setDestination( focus() ); } setOldFocus( focus() ); forceUpdate(); // is needed because after moving the sky not all stars are shown } if ( midMouseButtonDown ) midMouseButtonDown = false; // if middle button was pressed unset here scrollCount = 0; } void SkyMap::mousePressEvent( TQMouseEvent *e ) { //did we Grab an infoBox? if ( e->button() == Qt::LeftButton && infoBoxes()->grabBox( e ) ) { update(); //refresh without redrawing skymap return; } if ( (e->state() & ControlButton) && (e->button() == Qt::LeftButton) ) { ZoomRect.moveCenter( e->pos() ); setZoomMouseCursor(); update(); //refresh without redrawing skymap return; } // if button is down and cursor is not moved set the move cursor after 500 ms TQTimer t; t.singleShot (500, this, TQT_SLOT (setMouseMoveCursor())); double dx = ( 0.5*width() - e->x() )/Options::zoomFactor(); double dy = ( 0.5*height() - e->y() )/Options::zoomFactor(); if (unusablePoint (dx, dy)) return; // break if point is unusable if ( !midMouseButtonDown && e->button() == Qt::MidButton ) { y0 = 0.5*height() - e->y(); //record y pixel coordinate for middle-button zooming midMouseButtonDown = true; } if ( !mouseButtonDown ) { if ( e->button()==Qt::LeftButton ) { mouseButtonDown = true; scrollCount = 0; } //determine RA, Dec of mouse pointer setMousePoint( dXdYToRaDec( dx, dy, Options::useAltAz(), data->LST, data->geo()->lat(), Options::useRefraction() ) ); setClickedPoint( mousePoint() ); //Find object nearest to clickedPoint() setClickedObject( objectNearest( clickedPoint() ) ); if ( clickedObject() ) { setClickedPoint( clickedObject() ); if ( e->button() == Qt::RightButton ) { clickedObject()->showPopupMenu( pmenu, TQCursor::pos() ); } if ( ksw && e->button() == Qt::LeftButton ) { ksw->statusBar()->changeItem( clickedObject()->translatedLongName(), 0 ); } } else { //Empty sky selected. If left-click, display "nothing" in the status bar. //If right-click, open "empty" popup menu. setClickedObject( NULL ); switch (e->button()) { case Qt::LeftButton: if ( ksw ) ksw->statusBar()->changeItem( i18n( "Empty sky" ), 0 ); break; case Qt::RightButton: { SkyObject *nullObj = new SkyObject( SkyObject::TYPE_UNKNOWN, clickedPoint()->ra()->Hours(), clickedPoint()->dec()->Degrees() ); pmenu->createEmptyMenu( nullObj ); delete nullObj; pmenu->popup( TQCursor::pos() ); break; } default: break; } } } } void SkyMap::mouseDoubleClickEvent( TQMouseEvent *e ) { //Was the event inside an infoBox? If so, shade the box. if ( e->button() == Qt::LeftButton ) { if ( infoBoxes()->shadeBox( e ) ) { update(); return; } double dx = ( 0.5*width() - e->x() )/Options::zoomFactor(); double dy = ( 0.5*height() - e->y() )/Options::zoomFactor(); if (unusablePoint (dx, dy)) return; // break if point is unusable if (mouseButtonDown ) mouseButtonDown = false; if ( dx != 0.0 || dy != 0.0 ) slotCenter(); } } void SkyMap::paintEvent( TQPaintEvent * ) { //If computeSkymap is false, then we just refresh the window using the stored sky pixmap //and draw the "overlays" on top. This lets us update the overlay information rapidly //without needing to recompute the entire skymap. //use update() to trigger this "short" paint event; to force a full "recompute" //of the skymap, use forceUpdate(). if (!computeSkymap) { *sky2 = *sky; drawOverlays( sky2 ); bitBlt( this, 0, 0, sky2 ); return ; // exit because the pixmap is repainted and that's all what we want } TQPainter psky; setMapGeometry(); //checkSlewing combines the slewing flag (which is true when the display is actually in motion), //the hideOnSlew option (which is true if slewing should hide objects), //and clockSlewing (which is true if the timescale exceeds Options::slewTimeScale) bool checkSlewing = ( ( slewing || ( clockSlewing && data->clock()->isActive() ) ) && Options::hideOnSlew() ); //shortcuts to inform wheter to draw different objects bool drawPlanets( Options::showPlanets() && !(checkSlewing && Options::hidePlanets() ) ); bool drawMW( Options::showMilkyWay() && !(checkSlewing && Options::hideMilkyWay() ) ); bool drawCNames( Options::showCNames() && !(checkSlewing && Options::hideCNames() ) ); bool drawCLines( Options::showCLines() &&!(checkSlewing && Options::hideCLines() ) ); bool drawCBounds( Options::showCBounds() &&!(checkSlewing && Options::hideCBounds() ) ); bool drawGrid( Options::showGrid() && !(checkSlewing && Options::hideGrid() ) ); psky.begin( sky ); psky.fillRect( 0, 0, width(), height(), TQBrush( TQColor(data->colorScheme()->colorNamed( "SkyColor" ) )) ); if ( drawMW ) drawMilkyWay( psky ); if ( drawGrid ) drawCoordinateGrid( psky ); if ( drawCBounds ) drawConstellationBoundaries( psky ); if ( drawCLines ) drawConstellationLines( psky ); if ( drawCNames ) drawConstellationNames( psky ); if ( Options::showEquator() ) drawEquator( psky ); if ( Options::showEcliptic() ) drawEcliptic( psky ); //drawing to screen, so leave scale parameter at its default value of 1.0 drawStars( psky ); drawDeepSkyObjects( psky ); drawSolarSystem( psky, drawPlanets ); drawAttachedLabels( psky ); drawHorizon( psky ); //Finish up psky.end(); *sky2 = *sky; drawOverlays( sky2 ); bitBlt( this, 0, 0, sky2 ); computeSkymap = false; // use forceUpdate() to compute new skymap else old pixmap will be shown }