1 /***************************************************************************
2 observinglist.cpp - K Desktop Planetarium
5 copyright : (C) 2004 by Jeff Woods, Jason Harris
6 email : jcwoods@bellsouth.net, 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 ***************************************************************************/
23 #include <qstringlist.h>
24 #include <klistview.h>
25 #include <kpushbutton.h>
26 #include <kstatusbar.h>
27 #include <ktextedit.h>
28 #include <kinputdialog.h>
29 #include <kicontheme.h>
30 #include <kiconloader.h>
31 #include <kio/netaccess.h>
32 #include <kmessagebox.h>
33 #include <kfiledialog.h>
34 #include <ktempfile.h>
35 #include <klineedit.h>
37 #include "observinglist.h"
38 #include "observinglistui.h"
40 #include "kstarsdata.h"
41 #include "skyobject.h"
43 #include "detaildialog.h"
44 #include "tools/altvstime.h"
47 #include "indielement.h"
48 #include "indiproperty.h"
49 #include "indidevice.h"
50 #include "devicemanager.h"
52 ObservingList::ObservingList( KStars
*_ks
, QWidget
* parent
)
53 : KDialogBase( KDialogBase::Plain
, i18n( "Observing List" ),
54 Close
, Close
, parent
, "observinglist", false ), ks( _ks
), LogObject(0), noNameStars(0), isModified(false)
56 QFrame
*page
= plainPage();
57 setMainWidget( page
);
58 QVBoxLayout
*vlay
= new QVBoxLayout( page
, 0, spacingHint() );
59 ui
= new ObservingListUI( page
);
60 vlay
->addWidget( ui
);
61 vlay
->addSpacing( 12 );
64 connect( this, SIGNAL( closeClicked() ), this, SLOT( slotClose() ) );
65 connect( ui
->table
, SIGNAL( selectionChanged() ),
66 this, SLOT( slotNewSelection() ) );
67 connect( ui
->RemoveButton
, SIGNAL( clicked() ),
68 this, SLOT( slotRemoveObjects() ) );
69 connect( ui
->CenterButton
, SIGNAL( clicked() ),
70 this, SLOT( slotCenterObject() ) );
71 connect( ui
->ScopeButton
, SIGNAL( clicked() ),
72 this, SLOT( slotSlewToObject() ) );
73 connect( ui
->DetailsButton
, SIGNAL( clicked() ),
74 this, SLOT( slotDetails() ) );
75 connect( ui
->AVTButton
, SIGNAL( clicked() ),
76 this, SLOT( slotAVT() ) );
78 connect( ui
->OpenButton
, SIGNAL( clicked() ),
79 this, SLOT( slotOpenList() ) );
80 connect( ui
->SaveButton
, SIGNAL( clicked() ),
81 this, SLOT( slotSaveList() ) );
82 connect( ui
->SaveAsButton
, SIGNAL( clicked() ),
83 this, SLOT( slotSaveListAs() ) );
85 obsList
.setAutoDelete( false ); //do NOT delete removed pointers!
87 //Add icons to Push Buttons
88 KIconLoader
*icons
= KGlobal::iconLoader();
89 ui
->OpenButton
->setPixmap( icons
->loadIcon( "fileopen", KIcon::Toolbar
) );
90 ui
->SaveButton
->setPixmap( icons
->loadIcon( "filesave", KIcon::Toolbar
) );
91 ui
->SaveAsButton
->setPixmap( icons
->loadIcon( "filesaveas", KIcon::Toolbar
) );
93 ui
->CenterButton
->setEnabled( false );
94 ui
->ScopeButton
->setEnabled( false );
95 ui
->DetailsButton
->setEnabled( false );
96 ui
->AVTButton
->setEnabled( false );
97 ui
->RemoveButton
->setEnabled( false );
98 ui
->NotesLabel
->setEnabled( false );
99 ui
->NotesEdit
->setEnabled( false );
102 bool ObservingList::contains( const SkyObject
*q
) {
103 for ( SkyObject
* o
= obsList
.first(); o
; o
= obsList
.next() ) {
104 if ( o
== q
) return true;
112 void ObservingList::slotAddObject( SkyObject
*obj
) {
113 if ( ! obj
) obj
= ks
->map()->clickedObject();
115 //Insert object in obsList
116 obsList
.append( obj
);
117 if ( ! isModified
) isModified
= true;
119 //Insert object entry in table
121 if ( obj
->mag() < 90.0 ) smag
= QString::number( obj
->mag(), 'g', 2 );
122 new KListViewItem( ui
->table
, obj
->translatedName(),
123 obj
->ra()->toHMSString(),
124 obj
->dec()->toDMSString(),
128 //Note addition in statusbar
129 ks
->statusBar()->changeItem( i18n( "Added %1 to observing list." ).arg( obj
->name() ), 0 );
132 void ObservingList::slotRemoveObject( SkyObject
*o
) {
134 o
= ks
->map()->clickedObject();
137 if ( ! isModified
) isModified
= true;
139 if ( o
== LogObject
)
140 saveCurrentUserLog();
142 //Remove entry from table
143 bool objectFound
= false;
144 QListViewItemIterator
it( ui
->table
);
145 while ( it
.current() ) {
146 QListViewItem
*item
= it
.current();
148 //If the object is named "star" then match coordinates instead of name
149 if ( o
->translatedName() == i18n( "star" ) ) {
150 if ( item
->text(1) == o
->ra()->toHMSString()
151 && item
->text(2) == o
->dec()->toDMSString() ) {
153 kdDebug() << "removing anonymous star from list." << endl
;
154 kdDebug() << item
->text(1) << "::" << o
->ra()->toHMSString() << endl
;
160 } else if ( item
->text( 0 ) == o
->translatedName() ) {
162 kdDebug() << "removing " << o
->translatedName() << " from list." << endl
;
171 if ( ! objectFound
) {
172 kdDebug() << i18n( "Cannot remove Object %1; not found in table." ).arg(o
->translatedName()) << endl
;
176 void ObservingList::slotRemoveObjects() {
177 if ( SelectedObjects
.count() == 0) return;
179 for ( SkyObject
*o
= SelectedObjects
.first(); o
; o
= SelectedObjects
.next() )
180 slotRemoveObject( o
);
185 void ObservingList::slotNewSelection() {
186 //Construct list of selected objects
187 SelectedObjects
.clear();
188 QListViewItemIterator
it( ui
->table
, QListViewItemIterator::Selected
); //loop over selected items
189 while ( it
.current() ) {
190 for ( SkyObject
*o
= obsList
.first(); o
; o
= obsList
.next() ) {
191 if ( it
.current()->text(0) == i18n("star") ) {
192 if ( it
.current()->text(1) == o
->ra()->toHMSString()
193 && it
.current()->text(2) == o
->dec()->toDMSString() ) {
194 SelectedObjects
.append(o
);
197 } else if ( o
->translatedName() == it
.current()->text(0) ) {
198 SelectedObjects
.append( o
);
205 //Enable widgets when one object selected
206 if ( SelectedObjects
.count() == 1 ) {
207 QString
newName( SelectedObjects
.first()->translatedName() );
208 QString
oldName( obsList
.current()->translatedName() );
211 ui
->CenterButton
->setEnabled( true );
212 ui
->ScopeButton
->setEnabled( true );
213 ui
->DetailsButton
->setEnabled( true );
214 ui
->AVTButton
->setEnabled( true );
215 ui
->RemoveButton
->setEnabled( true );
217 //Find the selected object in the obsList,
218 //then break the loop. Now obsList.current()
219 //points to the new selected object (until now it was the previous object)
221 for ( SkyObject
* o
= obsList
.first(); o
; o
= obsList
.next() ) {
222 if ( o
->translatedName() == newName
) {
229 kdDebug() << i18n( "Object %1 not found in obsList." ).arg( newName
) << endl
;
230 } else if ( newName
!= i18n( "star" ) ) {
231 //Display the object's current user notes in the NotesEdit
232 //First, save the last object's user log to disk, if necessary
233 saveCurrentUserLog();
235 //set LogObject to the new selected object
236 LogObject
= obsList
.current();
238 ui
->NotesLabel
->setEnabled( true );
239 ui
->NotesEdit
->setEnabled( true );
241 ui
->NotesLabel
->setText( i18n( "observing notes for %1:" ).arg( LogObject
->translatedName() ) );
242 if ( LogObject
->userLog
.isEmpty() ) {
243 ui
->NotesEdit
->setText( i18n("Record here observation logs and/or data on %1.").arg( LogObject
->translatedName() ) );
245 ui
->NotesEdit
->setText( LogObject
->userLog
);
247 } else { //selected object is named "star"
248 //clear the log text box
249 saveCurrentUserLog();
250 ui
->NotesLabel
->setEnabled( false );
251 ui
->NotesEdit
->setEnabled( false );
254 } else if ( SelectedObjects
.count() == 0 ) {
256 ui
->CenterButton
->setEnabled( false );
257 ui
->ScopeButton
->setEnabled( false );
258 ui
->DetailsButton
->setEnabled( false );
259 ui
->AVTButton
->setEnabled( false );
260 ui
->RemoveButton
->setEnabled( false );
261 ui
->NotesLabel
->setEnabled( false );
262 ui
->NotesEdit
->setEnabled( false );
264 //Clear the user log text box.
265 saveCurrentUserLog();
266 } else { //more than one object selected.
267 ui
->CenterButton
->setEnabled( false );
268 ui
->ScopeButton
->setEnabled( false );
269 ui
->DetailsButton
->setEnabled( false );
270 ui
->AVTButton
->setEnabled( true );
271 ui
->RemoveButton
->setEnabled( true );
272 ui
->NotesLabel
->setEnabled( false );
273 ui
->NotesEdit
->setEnabled( false );
275 //Clear the user log text box.
276 saveCurrentUserLog();
280 void ObservingList::slotCenterObject() {
281 if ( obsList
.current() ) {
282 ks
->map()->setClickedObject( obsList
.current() );
283 ks
->map()->setClickedPoint( obsList
.current() );
284 ks
->map()->slotCenter();
288 void ObservingList::slotSlewToObject()
292 INDI_P
*eqCoord
, *onSet
;
293 INDI_E
*RAEle
, *DecEle
, *ConnectEle
;
294 bool useJ2000( false);
297 if (obsList
.current() == NULL
)
300 // Find the first device with EQUATORIAL_EOD_COORD or EQUATORIAL_COORD and with SLEW element
301 // i.e. the first telescope we find!
303 INDIMenu
*imenu
= ks
->getINDIMenu();
305 for (unsigned int i
=0; i
< imenu
->mgr
.count() ; i
++)
307 for (unsigned int j
=0; j
< imenu
->mgr
.at(i
)->indi_dev
.count(); j
++)
309 indidev
= imenu
->mgr
.at(i
)->indi_dev
.at(j
);
310 eqCoord
= indidev
->findProp("EQUATORIAL_EOD_COORD");
313 eqCoord
= indidev
->findProp("EQUATORIAL_COORD");
317 if (eqCoord
== NULL
) continue;
319 ConnectEle
= indidev
->findElem("CONNECT");
320 if (!ConnectEle
) continue;
322 if (ConnectEle
->state
== PS_OFF
)
324 KMessageBox::error(0, i18n("Telescope %1 is offline. Please connect and retry again.").arg(indidev
->label
));
328 RAEle
= eqCoord
->findElement("RA");
329 if (!RAEle
) continue;
330 DecEle
= eqCoord
->findElement("DEC");
331 if (!DecEle
) continue;
333 onSet
= indidev
->findProp("ON_COORD_SET");
334 if (!onSet
) continue;
336 onSet
->activateSwitch("SLEW");
338 sp
.set (obsList
.current()->ra(), obsList
.current()->dec());
341 sp
.apparentCoord(ks
->data()->ut().djd(), (long double) J2000
);
343 // Use JNow coordinate as required by INDI
344 RAEle
->write_w
->setText(QString("%1:%2:%3").arg(sp
.ra()->hour()).arg(sp
.ra()->minute()).arg(sp
.ra()->second()));
345 DecEle
->write_w
->setText(QString("%1:%2:%3").arg(sp
.dec()->degree()).arg(sp
.dec()->arcmin()).arg(sp
.dec()->arcsec()));
353 // We didn't find any telescopes
354 KMessageBox::sorry(0, i18n("KStars did not find any active telescopes."));
358 //FIXME: This will open multiple Detail windows for each object;
359 //Should have one window whose target object changes with selection
360 void ObservingList::slotDetails() {
361 if ( obsList
.current() ) {
362 DetailDialog
dd( obsList
.current(), ks
->data()->lt(), ks
->geo(), ks
);
367 void ObservingList::slotAVT() {
368 if ( SelectedObjects
.count() ) {
370 for ( SkyObject
*o
= SelectedObjects
.first(); o
; o
= SelectedObjects
.next() ) {
371 avt
.processObject( o
);
378 //FIXME: On close, we will need to close any open Details/AVT windows
379 void ObservingList::slotClose() {
380 //Save the current User log text
381 if ( ! ui
->NotesEdit
->text().isEmpty() && ui
->NotesEdit
->text()
382 != i18n("Record here observation logs and/or data on %1.").arg( obsList
.current()->name()) ) {
383 obsList
.current()->saveUserLog( ui
->NotesEdit
->text() );
389 void ObservingList::saveCurrentUserLog() {
390 if ( ! ui
->NotesEdit
->text().isEmpty() &&
391 ui
->NotesEdit
->text() !=
392 i18n("Record here observation logs and/or data on %1.").arg( LogObject
->translatedName() ) ) {
393 LogObject
->saveUserLog( ui
->NotesEdit
->text() );
395 ui
->NotesEdit
->clear();
396 ui
->NotesLabel
->setText( i18n( "Observing notes for object:" ) );
401 void ObservingList::slotOpenList() {
402 KURL fileURL
= KFileDialog::getOpenURL( QDir::homeDirPath(), "*.obslist|KStars Observing List (*.obslist)" );
405 if ( fileURL
.isValid() ) {
406 if ( ! fileURL
.isLocalFile() ) {
407 //Save remote list to a temporary local file
409 tmpfile
.setAutoDelete(true);
410 FileName
= tmpfile
.name();
411 if( KIO::NetAccess::download( fileURL
, FileName
, this ) )
412 f
.setName( FileName
);
415 FileName
= fileURL
.path();
416 f
.setName( FileName
);
419 if ( !f
.open( IO_ReadOnly
) ) {
420 QString message
= i18n( "Could not open file %1" ).arg( f
.name() );
421 KMessageBox::sorry( 0, message
, i18n( "Could Not Open File" ) );
425 //Before loading a new list, do we need to save the current one?
426 //Assume that if the list is empty, then there's no need to save
427 if ( obsList
.count() ) {
429 QString message
= i18n( "Do you want to save the current list before opening a new list?" );
430 if ( KMessageBox::questionYesNo( this, message
,
431 i18n( "Save current list?" ) ) == KMessageBox::Yes
)
435 //If we ever allow merging the loaded list with
436 //the existing one, that code would go here
441 //First line is the name of the list. The rest of the file should
442 //be object names, one per line.
443 QTextStream
istream(&f
);
445 ListName
= istream
.readLine();
447 while ( ! istream
.eof() ) {
448 line
= istream
.readLine();
449 SkyObject
*o
= ks
->data()->objectNamed( line
);
450 if ( o
) slotAddObject( o
);
453 //Newly-opened list should not trigger isModified flag
458 } else if ( fileURL
.path() != "" ) {
459 QString message
= i18n( "The specified file is invalid. Try another file?" );
460 if ( KMessageBox::warningYesNo( this, message
, i18n("Invalid file") ) == KMessageBox::Yes
) {
466 void ObservingList::slotSaveListAs() {
468 ListName
= KInputDialog::getText( i18n( "Enter list name" ),
469 i18n( "List Name:" ), "", &ok
);
472 KURL fileURL
= KFileDialog::getSaveURL( QDir::homeDirPath(), "*.obslist|KStars Observing List (*.obslist)" );
474 if ( fileURL
.isValid() )
475 FileName
= fileURL
.path();
481 void ObservingList::slotSaveList() {
482 if ( ListName
.isEmpty() || FileName
.isEmpty() ) {
488 if ( !f
.open( IO_WriteOnly
) ) {
489 QString message
= i18n( "Could not open file %1. Try a different filename?" ).arg( f
.name() );
491 if ( KMessageBox::warningYesNo( 0, message
, i18n( "Could Not Open File" ) ) == KMessageBox::Yes
) {
498 QTextStream
ostream(&f
);
499 ostream
<< ListName
<< endl
;
500 for ( SkyObject
* o
= obsList
.first(); o
; o
= obsList
.next() ) {
501 ostream
<< o
->name() << endl
;
508 #include "observinglist.moc"