moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kstars / kstars / tools / observinglist.cpp
blobf03ba86eb76970df92762a73ec90e747e5b00665
1 /***************************************************************************
2 observinglist.cpp - K Desktop Planetarium
3 -------------------
4 begin : 29 Nov 2004
5 copyright : (C) 2004 by Jeff Woods, Jason Harris
6 email : jcwoods@bellsouth.net, jharris@30doradus.org
7 ***************************************************************************/
9 /***************************************************************************
10 * *
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. *
15 * *
16 ***************************************************************************/
18 #include <stdio.h>
19 #include <qfile.h>
20 #include <qdir.h>
21 #include <qlabel.h>
22 #include <qlayout.h>
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"
39 #include "kstars.h"
40 #include "kstarsdata.h"
41 #include "skyobject.h"
42 #include "skymap.h"
43 #include "detaildialog.h"
44 #include "tools/altvstime.h"
46 #include "indimenu.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 );
63 //Connections
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;
107 return false;
111 //SLOTS
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
120 QString smag("--");
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(),
125 smag,
126 obj->typeName() );
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 ) {
133 if ( !o )
134 o = ks->map()->clickedObject();
136 obsList.remove(o);
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() ) {
152 //DEBUG
153 kdDebug() << "removing anonymous star from list." << endl;
154 kdDebug() << item->text(1) << "::" << o->ra()->toHMSString() << endl;
156 delete item;
157 objectFound = true;
158 break;
160 } else if ( item->text( 0 ) == o->translatedName() ) {
161 //DEBUG
162 kdDebug() << "removing " << o->translatedName() << " from list." << endl;
164 delete item;
165 objectFound = true;
166 break;
168 ++it;
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 );
182 slotNewSelection();
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);
195 break;
197 } else if ( o->translatedName() == it.current()->text(0) ) {
198 SelectedObjects.append( o );
199 break;
202 it++;
205 //Enable widgets when one object selected
206 if ( SelectedObjects.count() == 1 ) {
207 QString newName( SelectedObjects.first()->translatedName() );
208 QString oldName( obsList.current()->translatedName() );
210 //Enable buttons
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)
220 bool found( false );
221 for ( SkyObject* o = obsList.first(); o; o = obsList.next() ) {
222 if ( o->translatedName() == newName ) {
223 found = true;
224 break;
228 if ( ! found ) {
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() ) );
244 } else {
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 ) {
255 //Disable buttons
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()
291 INDI_D *indidev;
292 INDI_P *eqCoord, *onSet;
293 INDI_E *RAEle, *DecEle, *ConnectEle;
294 bool useJ2000( false);
295 SkyPoint sp;
297 if (obsList.current() == NULL)
298 return;
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");
311 if (eqCoord == NULL)
313 eqCoord = indidev->findProp("EQUATORIAL_COORD");
314 useJ2000 = true;
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));
325 return;
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());
340 if (useJ2000)
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()));
347 eqCoord->newText();
349 return;
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 );
363 dd.exec();
367 void ObservingList::slotAVT() {
368 if ( SelectedObjects.count() ) {
369 AltVsTime avt( ks );
370 for ( SkyObject *o = SelectedObjects.first(); o; o = SelectedObjects.next() ) {
371 avt.processObject( o );
374 avt.exec();
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() );
386 hide();
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:" ) );
397 LogObject = NULL;
401 void ObservingList::slotOpenList() {
402 KURL fileURL = KFileDialog::getOpenURL( QDir::homeDirPath(), "*.obslist|KStars Observing List (*.obslist)" );
403 QFile f;
405 if ( fileURL.isValid() ) {
406 if ( ! fileURL.isLocalFile() ) {
407 //Save remote list to a temporary local file
408 KTempFile tmpfile;
409 tmpfile.setAutoDelete(true);
410 FileName = tmpfile.name();
411 if( KIO::NetAccess::download( fileURL, FileName, this ) )
412 f.setName( FileName );
414 } else {
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" ) );
422 return;
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() ) {
428 if ( isModified ) {
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 )
432 slotSaveList();
435 //If we ever allow merging the loaded list with
436 //the existing one, that code would go here
437 obsList.clear();
438 ui->table->clear();
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);
444 QString line;
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
454 isModified = false;
456 f.close();
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 ) {
461 slotOpenList();
466 void ObservingList::slotSaveListAs() {
467 bool ok(false);
468 ListName = KInputDialog::getText( i18n( "Enter list name" ),
469 i18n( "List Name:" ), "", &ok );
471 if ( ok ) {
472 KURL fileURL = KFileDialog::getSaveURL( QDir::homeDirPath(), "*.obslist|KStars Observing List (*.obslist)" );
474 if ( fileURL.isValid() )
475 FileName = fileURL.path();
477 slotSaveList();
481 void ObservingList::slotSaveList() {
482 if ( ListName.isEmpty() || FileName.isEmpty() ) {
483 slotSaveListAs();
484 return;
487 QFile f( FileName );
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 ) {
492 FileName == "";
493 slotSaveList();
495 return;
498 QTextStream ostream(&f);
499 ostream << ListName << endl;
500 for ( SkyObject* o = obsList.first(); o; o = obsList.next() ) {
501 ostream << o->name() << endl;
504 f.close();
505 isModified = false;
508 #include "observinglist.moc"