Kstars

observinglist.cpp
1 /*
2  SPDX-FileCopyrightText: 2004-2020 Jeff Woods <[email protected]>
3  SPDX-FileCopyrightText: 2004-2020 Jason Harris <[email protected]>
4  SPDX-FileCopyrightText: Prakash Mohan <[email protected]>
5  SPDX-FileCopyrightText: Akarsh Simha <[email protected]>
6 
7  SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include "observinglist.h"
11 
12 #include "config-kstars.h"
13 
14 #include "constellationboundarylines.h"
15 #include "fov.h"
16 #include "imageviewer.h"
17 #include "ksalmanac.h"
18 #include "ksnotification.h"
19 #include "ksdssdownloader.h"
20 #include "kspaths.h"
21 #include "kstars.h"
22 #include "kstarsdata.h"
23 #include "ksutils.h"
24 #include "obslistpopupmenu.h"
25 #include "obslistwizard.h"
26 #include "Options.h"
27 #include "sessionsortfilterproxymodel.h"
28 #include "skymap.h"
29 #include "thumbnailpicker.h"
30 #include "dialogs/detaildialog.h"
31 #include "dialogs/finddialog.h"
32 #include "dialogs/locationdialog.h"
33 #include "oal/execute.h"
34 #include "skycomponents/skymapcomposite.h"
35 #include "skyobjects/skyobject.h"
36 #include "skyobjects/starobject.h"
37 #include "tools/altvstime.h"
38 #include "tools/eyepiecefield.h"
39 #include "tools/wutdialog.h"
40 
41 #ifdef HAVE_INDI
42 #include <basedevice.h>
43 #include "indi/indilistener.h"
44 #include "indi/drivermanager.h"
45 #include "indi/driverinfo.h"
46 #include "ekos/manager.h"
47 #endif
48 
49 #include <KPlotting/KPlotAxis>
50 #include <KPlotting/KPlotObject>
51 #include <KMessageBox>
52 #include <QMessageBox>
53 
54 #include <kstars_debug.h>
55 
56 //
57 // ObservingListUI
58 // ---------------------------------
59 ObservingListUI::ObservingListUI(QWidget *p) : QFrame(p)
60 {
61  setupUi(this);
62 }
63 
64 //
65 // ObservingList
66 // ---------------------------------
67 ObservingList::ObservingList()
68  : QDialog((QWidget *)KStars::Instance()), LogObject(nullptr), m_CurrentObject(nullptr), isModified(false), m_dl(nullptr),
69  m_manager{ CatalogsDB::dso_db_path() }
70 {
71 #ifdef Q_OS_OSX
72  setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
73 #endif
74  ui = new ObservingListUI(this);
75  QVBoxLayout *mainLayout = new QVBoxLayout;
76  mainLayout->addWidget(ui);
77  setWindowTitle(i18nc("@title:window", "Observation Planner"));
78 
79  setLayout(mainLayout);
80 
82  setFocusPolicy(Qt::StrongFocus);
83  geo = KStarsData::Instance()->geo();
84  sessionView = false;
85  m_listFileName = QString();
86  pmenu.reset(new ObsListPopupMenu());
87  //Set up the Table Views
88  m_WishListModel.reset(new QStandardItemModel(0, 5, this));
89  m_SessionModel.reset(new QStandardItemModel(0, 5));
90 
91  m_WishListModel->setHorizontalHeaderLabels(
92  QStringList() << i18n("Name") << i18n("Alternate Name") << i18nc("Right Ascension", "RA (J2000)")
93  << i18nc("Declination", "Dec (J2000)") << i18nc("Magnitude", "Mag") << i18n("Type")
94  << i18n("Current Altitude"));
95  m_SessionModel->setHorizontalHeaderLabels(
96  QStringList() << i18n("Name") << i18n("Alternate Name") << i18nc("Right Ascension", "RA (J2000)")
97  << i18nc("Declination", "Dec (J2000)") << i18nc("Magnitude", "Mag") << i18n("Type")
98  << i18nc("Constellation", "Constell.") << i18n("Time") << i18nc("Altitude", "Alt")
99  << i18nc("Azimuth", "Az"));
100 
101  m_WishListSortModel.reset(new QSortFilterProxyModel(this));
102  m_WishListSortModel->setSourceModel(m_WishListModel.get());
103  m_WishListSortModel->setDynamicSortFilter(true);
104  m_WishListSortModel->setSortRole(Qt::UserRole);
105  ui->WishListView->setModel(m_WishListSortModel.get());
106  ui->WishListView->horizontalHeader()->setStretchLastSection(true);
107 
108  ui->WishListView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
109  m_SessionSortModel.reset(new SessionSortFilterProxyModel());
110  m_SessionSortModel->setSourceModel(m_SessionModel.get());
111  m_SessionSortModel->setDynamicSortFilter(true);
112  ui->SessionView->setModel(m_SessionSortModel.get());
113  ui->SessionView->horizontalHeader()->setStretchLastSection(true);
114  ui->SessionView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
115  ksal.reset(new KSAlmanac);
116  ksal->setLocation(geo);
117  ui->avt->setGeoLocation(geo);
118  ui->avt->setSunRiseSetTimes(ksal->getSunRise(), ksal->getSunSet());
119  ui->avt->setLimits(-12.0, 12.0, -90.0, 90.0);
120  ui->avt->axis(KPlotWidget::BottomAxis)->setTickLabelFormat('t');
121  ui->avt->axis(KPlotWidget::BottomAxis)->setLabel(i18n("Local Time"));
122  ui->avt->axis(KPlotWidget::TopAxis)->setTickLabelFormat('t');
123  ui->avt->axis(KPlotWidget::TopAxis)->setTickLabelsShown(true);
124  ui->DateEdit->setDate(dt.date());
125  ui->SetLocation->setText(geo->fullName());
126  ui->ImagePreview->installEventFilter(this);
127  ui->WishListView->viewport()->installEventFilter(this);
128  ui->WishListView->installEventFilter(this);
129  ui->SessionView->viewport()->installEventFilter(this);
130  ui->SessionView->installEventFilter(this);
131  // setDefaultImage();
132  //Connections
133  connect(ui->WishListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotCenterObject()));
134  connect(ui->WishListView->selectionModel(),
135  SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(slotNewSelection()));
136  connect(ui->SessionView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
137  this, SLOT(slotNewSelection()));
138  connect(ui->WUTButton, SIGNAL(clicked()), this, SLOT(slotWUT()));
139  connect(ui->FindButton, SIGNAL(clicked()), this, SLOT(slotFind()));
140  connect(ui->OpenButton, SIGNAL(clicked()), this, SLOT(slotOpenList()));
141  connect(ui->SaveButton, SIGNAL(clicked()), this, SLOT(slotSaveSession()));
142  connect(ui->SaveAsButton, SIGNAL(clicked()), this, SLOT(slotSaveSessionAs()));
143  connect(ui->WizardButton, SIGNAL(clicked()), this, SLOT(slotWizard()));
144  connect(ui->batchAddButton, SIGNAL(clicked()), this, SLOT(slotBatchAdd()));
145  connect(ui->SetLocation, SIGNAL(clicked()), this, SLOT(slotLocation()));
146  connect(ui->Update, SIGNAL(clicked()), this, SLOT(slotUpdate()));
147  connect(ui->DeleteImage, SIGNAL(clicked()), this, SLOT(slotDeleteCurrentImage()));
148  connect(ui->SearchImage, SIGNAL(clicked()), this, SLOT(slotSearchImage()));
149  connect(ui->SetTime, SIGNAL(clicked()), this, SLOT(slotSetTime()));
150  connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slotChangeTab(int)));
151  connect(ui->saveImages, SIGNAL(clicked()), this, SLOT(slotSaveAllImages()));
152  connect(ui->DeleteAllImages, SIGNAL(clicked()), this, SLOT(slotDeleteAllImages()));
153  connect(ui->OALExport, SIGNAL(clicked()), this, SLOT(slotOALExport()));
154  connect(ui->clearListB, SIGNAL(clicked()), this, SLOT(slotClearList()));
155  //Add icons to Push Buttons
156  ui->OpenButton->setIcon(QIcon::fromTheme("document-open"));
157  ui->OpenButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
158  ui->SaveButton->setIcon(QIcon::fromTheme("document-save"));
159  ui->SaveButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
160  ui->SaveAsButton->setIcon(
161  QIcon::fromTheme("document-save-as"));
162  ui->SaveAsButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
163  ui->WizardButton->setIcon(QIcon::fromTheme("tools-wizard"));
164  ui->WizardButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
165  noSelection = true;
166  showScope = false;
167  ui->NotesEdit->setEnabled(false);
168  ui->SetTime->setEnabled(false);
169  ui->TimeEdit->setEnabled(false);
170  ui->SearchImage->setEnabled(false);
171  ui->saveImages->setEnabled(false);
172  ui->DeleteImage->setEnabled(false);
173  ui->OALExport->setEnabled(false);
174 
175  m_NoImagePixmap =
176  QPixmap(":/images/noimage.png")
177  .scaled(ui->ImagePreview->width(), ui->ImagePreview->height(), Qt::KeepAspectRatio, Qt::FastTransformation);
178  m_altCostHelper = [this](const SkyPoint & p) -> QStandardItem *
179  {
180  const double inf = std::numeric_limits<double>::infinity();
181  double altCost = 0.;
182  QString itemText;
183  double maxAlt = p.maxAlt(*(geo->lat()));
184  if (Options::obsListDemoteHole() && maxAlt > 90. - Options::obsListHoleSize())
185  maxAlt = 90. - Options::obsListHoleSize();
186  if (maxAlt <= 0.)
187  {
188  altCost = -inf;
189  itemText = i18n("Never rises");
190  }
191  else
192  {
193  altCost = (p.alt().Degrees() / maxAlt) * 100.;
194  if (altCost < 0)
195  itemText = i18nc("Short text to describe that object has not risen yet", "Not risen");
196  else
197  {
198  if (altCost > 100.)
199  {
200  altCost = -inf;
201  itemText = i18nc("Object is in the Dobsonian hole", "In hole");
202  }
203  else
204  itemText = QString::number(altCost, 'f', 0) + '%';
205  }
206  }
207 
208  QStandardItem *altItem = new QStandardItem(itemText);
209  altItem->setData(altCost, Qt::UserRole);
210  // qCDebug(KSTARS) << "Updating altitude for " << p.ra().toHMSString() << " " << p.dec().toDMSString() << " alt = " << p.alt().toDMSString() << " info to " << itemText;
211  return altItem;
212  };
213 
214  // Needed to fix weird bug on Windows that started with Qt 5.9 that makes the title bar
215  // not visible and therefore dialog not movable.
216 #ifdef Q_OS_WIN
217  move(100, 100);
218 #endif
219 }
220 
221 void ObservingList::showEvent(QShowEvent *)
222 {
223  // ONLY run for first ever load
224 
225  if (m_initialWishlistLoad == false)
226  {
227  m_initialWishlistLoad = true;
228 
229  slotLoadWishList(); //Load the wishlist from disk if present
230  m_CurrentObject = nullptr;
231  setSaveImagesButton();
232 
233  slotUpdateAltitudes();
234  m_altitudeUpdater = new QTimer(this);
235  connect(m_altitudeUpdater, SIGNAL(timeout()), this, SLOT(slotUpdateAltitudes()));
236  m_altitudeUpdater->start(120000); // update altitudes every 2 minutes
237  }
238 }
239 
240 //SLOTS
241 
242 void ObservingList::slotAddObject(const SkyObject *_obj, bool session, bool update)
243 {
244  if (!m_initialWishlistLoad)
245  {
246  showEvent(nullptr); // Initialize the observing wishlist
247  }
248  bool addToWishList = true;
249  if (!_obj)
250  _obj = SkyMap::Instance()->clickedObject(); // Eh? Why? Weird default behavior.
251 
252  if (!_obj)
253  {
254  qCWarning(KSTARS) << "Trying to add null object to observing list! Ignoring.";
255  return;
256  }
257 
258  QString finalObjectName = getObjectName(_obj);
259 
260  if (finalObjectName.isEmpty())
261  {
262  KSNotification::sorry(i18n("Stars and objects whose names KStars does not know are not supported in the observing lists"));
263  return;
264  }
265 
266  //First, make sure object is not already in the list
267  QSharedPointer<SkyObject> obj = findObject(_obj);
268  if (obj)
269  {
270  addToWishList = false;
271  if (!session)
272  {
274  i18n("%1 is already in your wishlist.", finalObjectName),
275  0); // FIXME: This message is too inconspicuous if using the Find dialog to add
276  return;
277  }
278  }
279  else
280  {
281  assert(!findObject(_obj, session));
282  qCDebug(KSTARS) << "Cloned object " << finalObjectName << " to add to observing list.";
284  _obj->clone()); // Use a clone in case the original SkyObject is deleted due to change in catalog configuration.
285  }
286 
287  if (session && sessionList().contains(obj))
288  {
289  KStars::Instance()->statusBar()->showMessage(i18n("%1 is already in the session plan.", finalObjectName), 0);
290  return;
291  }
292 
293  // JM: If we are loading observing list from disk, solar system objects magnitudes are not calculated until later
294  // Therefore, we manual invoke updateCoords to force computation of magnitude.
295  if ((obj->type() == SkyObject::COMET || obj->type() == SkyObject::ASTEROID || obj->type() == SkyObject::MOON ||
296  obj->type() == SkyObject::PLANET) &&
297  obj->mag() == 0)
298  {
299  KSNumbers num(dt.djd());
300  CachingDms LST = geo->GSTtoLST(dt.gst());
301  obj->updateCoords(&num, true, geo->lat(), &LST, true);
302  }
303 
304  QString smag = "--";
305  if (-30.0 < obj->mag() && obj->mag() < 90.0)
306  smag = QString::number(obj->mag(), 'f', 2); // The lower limit to avoid display of unrealistic comet magnitudes
307 
308  SkyPoint p = obj->recomputeHorizontalCoords(dt, geo);
309 
310  QList<QStandardItem *> itemList;
311 
312  auto getItemWithUserRole = [](const QString & itemText) -> QStandardItem *
313  {
314  QStandardItem *ret = new QStandardItem(itemText);
315  ret->setData(itemText, Qt::UserRole);
316  return ret;
317  };
318 
319  // Fill itemlist with items that are common to both wishlist additions and session plan additions
320  auto populateItemList = [&getItemWithUserRole, &itemList, &finalObjectName, obj, &p, &smag]()
321  {
322  itemList.clear();
323  QStandardItem *keyItem = getItemWithUserRole(finalObjectName);
324  keyItem->setData(QVariant::fromValue<void *>(static_cast<void *>(obj.data())), Qt::UserRole + 1);
325  itemList
326  << keyItem // NOTE: The rest of the methods assume that the SkyObject pointer is available in the first column!
327  << getItemWithUserRole(obj->translatedLongName()) << getItemWithUserRole(p.ra0().toHMSString())
328  << getItemWithUserRole(p.dec0().toDMSString()) << getItemWithUserRole(smag)
329  << getItemWithUserRole(obj->typeName());
330  };
331 
332  //Insert object in the Wish List
333  if (addToWishList)
334  {
335  m_WishList.append(obj);
336  m_CurrentObject = obj.data();
337 
338  //QString ra, dec;
339  //ra = "";//p.ra().toHMSString();
340  //dec = p.dec().toDMSString();
341 
342  populateItemList();
343  // FIXME: Instead sort by a "clever" observability score, calculated as follows:
344  // - First sort by (max altitude) - (current altitude) rounded off to the nearest
345  // - Weight by declination - latitude (in the northern hemisphere, southern objects get higher precedence)
346  // - Demote objects in the hole
347  SkyPoint p = obj->recomputeHorizontalCoords(KStarsDateTime::currentDateTimeUtc(), geo); // Current => now
348  itemList << m_altCostHelper(p);
349  m_WishListModel->appendRow(itemList);
350 
351  //Note addition in statusbar
352  KStars::Instance()->statusBar()->showMessage(i18n("Added %1 to observing list.", finalObjectName), 0);
353  ui->WishListView->resizeColumnsToContents();
354  if (!update)
355  slotSaveList();
356  }
357  //Insert object in the Session List
358  if (session)
359  {
360  m_SessionList.append(obj);
361  dt.setTime(TimeHash.value(finalObjectName, obj->transitTime(dt, geo)));
362  dms lst(geo->GSTtoLST(dt.gst()));
363  p.EquatorialToHorizontal(&lst, geo->lat());
364 
365  QString alt = "--", az = "--";
366 
367  QStandardItem *BestTime = new QStandardItem();
368  /* QString ra, dec;
369  if(obj->name() == "star" ) {
370  ra = obj->ra0().toHMSString();
371  dec = obj->dec0().toDMSString();
372  BestTime->setData( QString( "--" ), Qt::DisplayRole );
373  }
374  else {*/
375  BestTime->setData(TimeHash.value(finalObjectName, obj->transitTime(dt, geo)), Qt::DisplayRole);
376  alt = p.alt().toDMSString();
377  az = p.az().toDMSString();
378  //}
379  // TODO: Change the rest of the parameters to their appropriate datatypes.
380  populateItemList();
381  itemList << getItemWithUserRole(KSUtils::constNameToAbbrev(
382  KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(obj.data())))
383  << BestTime << getItemWithUserRole(alt) << getItemWithUserRole(az);
384 
385  m_SessionModel->appendRow(itemList);
386  //Adding an object should trigger the modified flag
387  isModified = true;
388  ui->SessionView->resizeColumnsToContents();
389  //Note addition in statusbar
390  KStars::Instance()->statusBar()->showMessage(i18n("Added %1 to session list.", finalObjectName), 0);
391  SkyMap::Instance()->forceUpdate();
392  }
393  setSaveImagesButton();
394 }
395 
396 void ObservingList::slotRemoveObject(const SkyObject *_o, bool session, bool update)
397 {
398  if (!update) // EH?!
399  {
400  if (!_o)
401  _o = SkyMap::Instance()->clickedObject();
402  else if (sessionView) //else if is needed as clickedObject should not be removed from the session list.
403  session = true;
404  }
405 
406  // Is the pointer supplied in our own lists?
407  const QList<QSharedPointer<SkyObject>> &list = (session ? sessionList() : obsList());
408  QStandardItemModel *currentModel = (session ? m_SessionModel.get() : m_WishListModel.get());
409 
410  QSharedPointer<SkyObject> o = findObject(_o, session);
411  if (!o)
412  {
413  qWarning() << "Object (name: " << getObjectName(o.data())
414  << ") supplied to ObservingList::slotRemoveObject() was not found in the "
415  << QString(session ? "session" : "observing") << " list!";
416  return;
417  }
418 
419  int k = list.indexOf(o);
420  assert(k >= 0);
421 
422  // Remove from hash
423  ImagePreviewHash.remove(o.data());
424 
425  if (o.data() == LogObject)
426  saveCurrentUserLog();
427 
428  //Remove row from the TableView model
429  // FIXME: Is there no faster way?
430  for (int irow = 0; irow < currentModel->rowCount(); ++irow)
431  {
432  QString name = currentModel->item(irow, 0)->text();
433  if (getObjectName(o.data()) == name)
434  {
435  currentModel->removeRow(irow);
436  break;
437  }
438  }
439 
440  if (!session)
441  {
442  obsList().removeAt(k);
443  ui->avt->removeAllPlotObjects();
444  ui->WishListView->resizeColumnsToContents();
445  if (!update)
446  slotSaveList();
447  }
448  else
449  {
450  if (!update)
451  TimeHash.remove(o->name());
452  sessionList().removeAt(k); //Remove from the session list
453  isModified = true; //Removing an object should trigger the modified flag
454  ui->avt->removeAllPlotObjects();
455  ui->SessionView->resizeColumnsToContents();
456  SkyMap::Instance()->forceUpdate();
457  }
458 }
459 
461 {
462  //Find each object by name in the session list, and remove it
463  //Go backwards so item alignment doesn't get screwed up as rows are removed.
464  for (int irow = getActiveModel()->rowCount() - 1; irow >= 0; --irow)
465  {
466  bool rowSelected;
467  if (sessionView)
468  rowSelected = ui->SessionView->selectionModel()->isRowSelected(irow, QModelIndex());
469  else
470  rowSelected = ui->WishListView->selectionModel()->isRowSelected(irow, QModelIndex());
471 
472  if (rowSelected)
473  {
474  QModelIndex sortIndex, index;
475  sortIndex = getActiveSortModel()->index(irow, 0);
476  index = getActiveSortModel()->mapToSource(sortIndex);
477  SkyObject *o = static_cast<SkyObject *>(index.data(Qt::UserRole + 1).value<void *>());
478  Q_ASSERT(o);
479  slotRemoveObject(o, sessionView);
480  }
481  }
482 
483  if (sessionView)
484  {
485  //we've removed all selected objects, so clear the selection
486  ui->SessionView->selectionModel()->clear();
487  //Update the lists in the Execute window as well
488  KStarsData::Instance()->executeSession()->init();
489  }
490 
491  setSaveImagesButton();
492  ui->ImagePreview->setCursor(Qt::ArrowCursor);
493 }
494 
496 {
497  bool found = false;
498  singleSelection = false;
499  noSelection = false;
500  showScope = false;
501  //ui->ImagePreview->clearPreview();
502  //ui->ImagePreview->setPixmap(QPixmap());
503  ui->ImagePreview->setCursor(Qt::ArrowCursor);
504  QModelIndexList selectedItems;
505  QString newName;
507  QString labelText;
508  ui->DeleteImage->setEnabled(false);
509 
510  selectedItems =
511  getActiveSortModel()->mapSelectionToSource(getActiveView()->selectionModel()->selection()).indexes();
512 
513  if (selectedItems.size() == getActiveModel()->columnCount())
514  {
515  newName = selectedItems[0].data().toString();
516  singleSelection = true;
517  //Find the selected object in the SessionList,
518  //then break the loop. Now SessionList.current()
519  //points to the new selected object (until now it was the previous object)
520  for (auto &o_temp : getActiveList())
521  {
522  if (getObjectName(o_temp.data()) == newName)
523  {
524  o = o_temp;
525  found = true;
526  break;
527  }
528  }
529  }
530 
531  if (singleSelection)
532  {
533  //Enable buttons
534  ui->ImagePreview->setCursor(Qt::PointingHandCursor);
535 #ifdef HAVE_INDI
536  showScope = true;
537 #endif
538  if (found)
539  {
540  m_CurrentObject = o.data();
541  //QPoint pos(0,0);
542  plot(o.data());
543  //Change the m_currentImageFileName, DSS/SDSS Url to correspond to the new object
544  setCurrentImage(o.data());
545  ui->SearchImage->setEnabled(true);
546  if (currentObject()->hasName())
547  {
548  //Display the current object's user notes in the NotesEdit
549  //First, save the last object's user log to disk, if necessary
550  saveCurrentUserLog(); //uses LogObject, which is still the previous obj.
551  //set LogObject to the new selected object
552  LogObject = currentObject();
553  ui->NotesEdit->setEnabled(true);
554 
555  const auto &userLog =
556  KStarsData::Instance()->getUserData(LogObject->name()).userLog;
557 
558  if (userLog.isEmpty())
559  {
560  ui->NotesEdit->setPlainText(
561  i18n("Record here observation logs and/or data on %1.", getObjectName(LogObject)));
562  }
563  else
564  {
565  ui->NotesEdit->setPlainText(userLog);
566  }
567  if (sessionView)
568  {
569  ui->TimeEdit->setEnabled(true);
570  ui->SetTime->setEnabled(true);
571  ui->TimeEdit->setTime(TimeHash.value(o->name(), o->transitTime(dt, geo)));
572  }
573  }
574  else //selected object is named "star"
575  {
576  //clear the log text box
577  saveCurrentUserLog();
578  ui->NotesEdit->clear();
579  ui->NotesEdit->setEnabled(false);
580  ui->SearchImage->setEnabled(false);
581  }
582  QString ImagePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, m_currentImageFileName);
583  if (!ImagePath.isEmpty())
584  {
585  //If the image is present, show it!
586  KSDssImage ksdi(ImagePath);
587  KSDssImage::Metadata md = ksdi.getMetadata();
588  //ui->ImagePreview->showPreview( QUrl::fromLocalFile( ksdi.getFileName() ) );
589  if (ImagePreviewHash.contains(o.data()) == false)
590  ImagePreviewHash[o.data()] = QPixmap(ksdi.getFileName()).scaledToHeight(ui->ImagePreview->width());
591 
592  //ui->ImagePreview->setPixmap(QPixmap(ksdi.getFileName()).scaledToHeight(ui->ImagePreview->width()));
593  ui->ImagePreview->setPixmap(ImagePreviewHash[o.data()]);
594  if (md.isValid())
595  {
596  ui->dssMetadataLabel->setText(
597  i18n("DSS Image metadata: \n Size: %1\' x %2\' \n Photometric band: %3 \n Version: %4",
599  }
600  else
601  ui->dssMetadataLabel->setText(i18n("No image info available."));
602  ui->ImagePreview->show();
603  ui->DeleteImage->setEnabled(true);
604  }
605  else
606  {
607  setDefaultImage();
608  ui->dssMetadataLabel->setText(
609  i18n("No image available. Click on the placeholder image to download one."));
610  }
611  QString cname =
612  KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(o.data());
613  if (o->type() != SkyObject::CONSTELLATION)
614  {
615  labelText = "<b>";
616  if (o->type() == SkyObject::PLANET)
617  labelText += o->translatedName();
618  else
619  labelText += o->name();
620  if (std::isfinite(o->mag()) && o->mag() <= 30.)
621  labelText += ":</b> " + i18nc("%1 magnitude of object, %2 type of sky object (planet, asteroid "
622  "etc), %3 name of a constellation",
623  "%1 mag %2 in %3", o->mag(), o->typeName().toLower(), cname);
624  else
625  labelText +=
626  ":</b> " + i18nc("%1 type of sky object (planet, asteroid etc), %2 name of a constellation",
627  "%1 in %2", o->typeName(), cname);
628  }
629  }
630  else
631  {
632  setDefaultImage();
633  qCWarning(KSTARS) << "Object " << newName << " not found in list.";
634  }
635  ui->quickInfoLabel->setText(labelText);
636  }
637  else
638  {
639  if (selectedItems.isEmpty()) //Nothing selected
640  {
641  //Disable buttons
642  noSelection = true;
643  ui->NotesEdit->setEnabled(false);
644  m_CurrentObject = nullptr;
645  ui->TimeEdit->setEnabled(false);
646  ui->SetTime->setEnabled(false);
647  ui->SearchImage->setEnabled(false);
648  //Clear the user log text box.
649  saveCurrentUserLog();
650  ui->NotesEdit->setPlainText("");
651  //Clear the plot in the AVTPlotwidget
652  ui->avt->removeAllPlotObjects();
653  }
654  else //more than one object selected.
655  {
656  ui->NotesEdit->setEnabled(false);
657  ui->TimeEdit->setEnabled(false);
658  ui->SetTime->setEnabled(false);
659  ui->SearchImage->setEnabled(false);
660  m_CurrentObject = nullptr;
661  //Clear the plot in the AVTPlotwidget
662  ui->avt->removeAllPlotObjects();
663  //Clear the user log text box.
664  saveCurrentUserLog();
665  ui->NotesEdit->setPlainText("");
666  ui->quickInfoLabel->setText(QString());
667  }
668  }
669 }
670 
672 {
673  if (getSelectedItems().size() == 1)
674  {
675  SkyMap::Instance()->setClickedObject(currentObject());
676  SkyMap::Instance()->setClickedPoint(currentObject());
677  SkyMap::Instance()->slotCenter();
678  }
679 }
680 
682 {
683 #ifdef HAVE_INDI
684 
685  if (INDIListener::Instance()->size() == 0)
686  {
687  KSNotification::sorry(i18n("No connected mounts found."));
688  return;
689  }
690 
691  for (auto &oneDevice : INDIListener::Instance()->getDevices())
692  {
693  if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
694  continue;
695 
696  if (oneDevice->isConnected() == false)
697  {
698  KSNotification::error(i18n("Mount %1 is offline. Please connect and retry again.", oneDevice->getDeviceName()));
699  return;
700  }
701 
702  auto mount = dynamic_cast<ISD::Mount *>(oneDevice->getConcreteDevice(INDI::BaseDevice::TELESCOPE_INTERFACE));
703  if (!mount)
704  continue;
705  mount->Slew(currentObject());
706  return;
707  }
708 
709  KSNotification::sorry(i18n("No connected mounts found."));
710 
711 #endif
712 }
713 
715 {
716 #ifdef HAVE_INDI
717  Ekos::Manager::Instance()->addObjectToScheduler(currentObject());
718 #endif
719 }
720 
721 //FIXME: This will open multiple Detail windows for each object;
722 //Should have one window whose target object changes with selection
724 {
725  if (currentObject())
726  {
728  new DetailDialog(currentObject(), KStarsData::Instance()->ut(), geo, KStars::Instance());
729  dd->exec();
730  delete dd;
731  }
732 }
733 
735 {
736  KStarsDateTime lt = dt;
737  lt.setTime(QTime(8, 0, 0));
738  QPointer<WUTDialog> w = new WUTDialog(KStars::Instance(), sessionView, geo, lt);
739  w->exec();
740  delete w;
741 }
742 
744 {
745  Q_ASSERT(!sessionView);
746  if (getSelectedItems().size())
747  {
748  foreach (const QModelIndex &i, getSelectedItems())
749  {
750  foreach (QSharedPointer<SkyObject> o, obsList())
751  if (getObjectName(o.data()) == i.data().toString())
752  slotAddObject(
753  o.data(),
754  true); // FIXME: Would be good to have a wrapper that accepts QSharedPointer<SkyObject>
755  }
756  }
757 }
758 
760 {
761  if (FindDialog::Instance()->exec() == QDialog::Accepted)
762  {
763  SkyObject *o = FindDialog::Instance()->targetObject();
764  if (o != nullptr)
765  {
766  slotAddObject(o, sessionView);
767  }
768  }
769 }
770 
772 {
773  bool accepted = false;
775  sessionView ? i18n("Batch add to observing session") : i18n("Batch add to observing wishlist"),
776  i18n("Specify a list of objects with one object on each line to add. The names must be understood to KStars, or if the internet resolver is enabled in settings, to the CDS Sesame resolver. Objects that are internet resolved will be added to the database."),
777  QString(),
778  &accepted);
779  bool resolve = Options::resolveNamesOnline();
780 
781  if (accepted && !items.isEmpty())
782  {
783  QStringList failedObjects;
784  QStringList objectNames = items.split("\n");
785  for (QString objectName : objectNames)
786  {
787  objectName = FindDialog::processSearchText(objectName);
788  SkyObject *object = KStarsData::Instance()->objectNamed(objectName);
789  if (!object && resolve)
790  {
791  object = FindDialog::resolveAndAdd(m_manager, objectName);
792  }
793  if (!object)
794  {
795  failedObjects.append(objectName);
796  }
797  else
798  {
799  slotAddObject(object, sessionView);
800  }
801  }
802 
803  if (!failedObjects.isEmpty())
804  {
805  QMessageBox msgBox =
806  {
807  QMessageBox::Icon::Warning,
808  i18np("Batch add: %1 object not found", "Batch add: %1 objects not found", failedObjects.size()),
809  i18np("%1 object could not be found in the database or resolved, and hence could not be added. See the details for more.",
810  "%1 objects could not be found in the database or resolved, and hence could not be added. See the details for more.",
811  failedObjects.size()),
813  this
814  };
815  msgBox.setDetailedText(failedObjects.join("\n"));
816  msgBox.exec();
817  }
818  }
819  Q_ASSERT(false); // Not implemented
820 }
821 
823 {
824  KStars::Instance()->slotEyepieceView(currentObject(), getCurrentImagePath());
825 }
826 
828 {
829  QModelIndexList selectedItems;
830  // TODO: Think and see if there's a more efficient way to do this. I can't seem to think of any, but this code looks like it could be improved. - Akarsh
831  selectedItems =
832  (sessionView ?
833  m_SessionSortModel->mapSelectionToSource(ui->SessionView->selectionModel()->selection()).indexes() :
834  m_WishListSortModel->mapSelectionToSource(ui->WishListView->selectionModel()->selection()).indexes());
835 
836  if (selectedItems.size())
837  {
838  QPointer<AltVsTime> avt = new AltVsTime(KStars::Instance());
839  foreach (const QModelIndex &i, selectedItems)
840  {
841  if (i.column() == 0)
842  {
843  SkyObject *o = static_cast<SkyObject *>(i.data(Qt::UserRole + 1).value<void *>());
844  Q_ASSERT(o);
845  avt->processObject(o);
846  }
847  }
848  avt->exec();
849  delete avt;
850  }
851 }
852 
853 //FIXME: On close, we will need to close any open Details/AVT windows
854 void ObservingList::slotClose()
855 {
856  //Save the current User log text
857  saveCurrentUserLog();
858  ui->avt->removeAllPlotObjects();
859  slotNewSelection();
860  saveCurrentList();
861  hide();
862 }
863 
865 {
866  if (LogObject && !ui->NotesEdit->toPlainText().isEmpty() &&
867  ui->NotesEdit->toPlainText() !=
868  i18n("Record here observation logs and/or data on %1.", getObjectName(LogObject)))
869  {
870  const auto &success = KStarsData::Instance()->updateUserLog(
871  LogObject->name(), ui->NotesEdit->toPlainText());
872 
873  if (!success.first)
874  KSNotification::sorry(success.second, i18n("Could not update the user log."));
875 
876  ui->NotesEdit->clear();
877  LogObject = nullptr;
878  }
879 }
880 
882 {
883  QUrl fileURL = QFileDialog::getOpenFileUrl(KStars::Instance(), i18nc("@title:window", "Open Observing List"), QUrl(),
884  "KStars Observing List (*.obslist)");
885  QFile f;
886 
887  if (fileURL.isValid())
888  {
889  f.setFileName(fileURL.toLocalFile());
890  //FIXME do we still need to do this?
891  /*
892  if ( ! fileURL.isLocalFile() ) {
893  //Save remote list to a temporary local file
894  QTemporaryFile tmpfile;
895  tmpfile.setAutoRemove(false);
896  tmpfile.open();
897  m_listFileName = tmpfile.fileName();
898  if( KIO::NetAccess::download( fileURL, m_listFileName, this ) )
899  f.setFileName( m_listFileName );
900 
901  } else {
902  m_listFileName = fileURL.toLocalFile();
903  f.setFileName( m_listFileName );
904  }
905  */
906 
907  if (!f.open(QIODevice::ReadOnly))
908  {
909  QString message = i18n("Could not open file %1", f.fileName());
910  KSNotification::sorry(message, i18n("Could Not Open File"));
911  return;
912  }
913  saveCurrentList(); //See if the current list needs to be saved before opening the new one
914  ui->tabWidget->setCurrentIndex(1); // FIXME: This is not robust -- asimha
915  slotChangeTab(1);
916 
917  sessionList().clear();
918  TimeHash.clear();
919  m_CurrentObject = nullptr;
920  m_SessionModel->removeRows(0, m_SessionModel->rowCount());
921  SkyMap::Instance()->forceUpdate();
922  //First line is the name of the list. The rest of the file is
923  //object names, one per line. With the TimeHash value if present
924  QTextStream istream(&f);
925  QString input;
926  input = istream.readAll();
927  OAL::Log logObject;
928  logObject.readBegin(input);
929  //Set the New TimeHash
930  TimeHash = logObject.timeHash();
931  GeoLocation *geo_new = logObject.geoLocation();
932  if (!geo_new)
933  {
934  // FIXME: This is a very hackish solution -- if we
935  // encounter an invalid XML file, we know we won't read a
936  // GeoLocation successfully. It does not detect partially
937  // corrupt files. -- asimha
938  KSNotification::sorry(i18n("The specified file is invalid. We expect an XML file based on the OpenAstronomyLog schema."));
939  f.close();
940  return;
941  }
942  dt = logObject.dateTime();
943  //foreach (SkyObject *o, *(logObject.targetList()))
944  for (auto &o : logObject.targetList())
945  slotAddObject(o.data(), true);
946  //Update the location and user set times from file
947  slotUpdate();
948  //Newly-opened list should not trigger isModified flag
949  isModified = false;
950  f.close();
951  }
952  else if (!fileURL.toLocalFile().isEmpty())
953  {
954  KSNotification::sorry(i18n("The specified file is invalid"));
955  }
956 }
957 
959 {
960  if ((ui->tabWidget->currentIndex() == 0 && obsList().isEmpty()) ||
961  (ui->tabWidget->currentIndex() == 1 && sessionList().isEmpty()))
962  return;
963 
964  QString message = i18n("Are you sure you want to clear all objects?");
965  if (KMessageBox::questionYesNo(this, message, i18n("Clear all?")) == KMessageBox::Yes)
966  {
967  // Did I forget anything else to remove?
968  ui->avt->removeAllPlotObjects();
969  m_CurrentObject = LogObject = nullptr;
970 
971  if (ui->tabWidget->currentIndex() == 0)
972  {
973  // IMPORTANT: Is this enough or we will have dangling pointers in memory?
974  ImagePreviewHash.clear();
975  obsList().clear();
976  m_WishListModel->setRowCount(0);
977  }
978  else
979  {
980  // IMPORTANT: Is this enough or we will have dangling pointers in memory?
981  sessionList().clear();
982  TimeHash.clear();
983  isModified = true; //Removing an object should trigger the modified flag
984  m_SessionModel->setRowCount(0);
985  SkyMap::Instance()->forceUpdate();
986  }
987  }
988 }
989 
991 {
992  //Before loading a new list, do we need to save the current one?
993  //Assume that if the list is empty, then there's no need to save
994  if (sessionList().size())
995  {
996  if (isModified)
997  {
998  QString message = i18n("Do you want to save the current session?");
999  if (KMessageBox::questionYesNo(this, message, i18n("Save Current session?"), KStandardGuiItem::save(),
1000  KStandardGuiItem::discard()) == KMessageBox::Yes)
1001  slotSaveSession();
1002  }
1003  }
1004 }
1005 
1007 {
1008  if (sessionList().isEmpty())
1009  return;
1010 
1011  QUrl fileURL = QFileDialog::getSaveFileUrl(KStars::Instance(), i18nc("@title:window", "Save Observing List"), QUrl(),
1012  "KStars Observing List (*.obslist)");
1013  if (fileURL.isValid())
1014  {
1015  m_listFileName = fileURL.toLocalFile();
1016  slotSaveSession(nativeSave);
1017  }
1018 }
1019 
1021 {
1022  QFile f;
1023  // FIXME: Move wishlist into a database.
1024  // TODO: Support multiple wishlists.
1025 
1026  QString fileContents;
1027  QTextStream ostream(
1028  &fileContents); // We first write to a QString to prevent truncating the file in case there is a crash.
1029  foreach (const QSharedPointer<SkyObject> o, obsList())
1030  {
1031  if (!o)
1032  {
1033  qWarning() << "Null entry in observing wishlist! Skipping!";
1034  continue;
1035  }
1036  if (o->name() == "star")
1037  {
1038  //ostream << o->name() << " " << o->ra0().Hours() << " " << o->dec0().Degrees() << Qt::endl;
1039  ostream << getObjectName(o.data(), false) << '\n';
1040  }
1041  else if (o->type() == SkyObject::STAR)
1042  {
1043  Q_ASSERT(dynamic_cast<const StarObject *>(o.data()));
1044  const QSharedPointer<StarObject> s = qSharedPointerCast<StarObject>(o);
1045  if (s->name() == s->gname())
1046  ostream << s->name2() << '\n';
1047  else
1048  ostream << s->name() << '\n';
1049  }
1050  else
1051  {
1052  ostream << o->name() << '\n';
1053  }
1054  }
1055  f.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("wishlist.obslist"));
1056  if (!f.open(QIODevice::WriteOnly))
1057  {
1058  qWarning() << "Cannot save wish list to file!"; // TODO: This should be presented as a message box to the user
1059  KMessageBox::error(this,
1060  i18n("Could not open the observing wishlist file %1 for writing. Your wishlist changes will not be saved. Check if the location is writable and not full.",
1061  f.fileName()), i18n("Could not save observing wishlist"));
1062  return;
1063  }
1064  QTextStream writeemall(&f);
1065  writeemall << fileContents;
1066  f.close();
1067 }
1068 
1070 {
1071  QFile f;
1072  f.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("wishlist.obslist"));
1073  if (!f.open(QIODevice::ReadOnly))
1074  {
1075  qWarning(KSTARS) << "No WishList Saved yet";
1076  return;
1077  }
1078  QTextStream istream(&f);
1079  QString line;
1080 
1081  QPointer<QProgressDialog> addingObjectsProgress = new QProgressDialog();
1082  addingObjectsProgress->setWindowTitle(i18nc("@title:window", "Observing List Wizard"));
1083  addingObjectsProgress->setLabelText(i18n("Please wait while loading observing wishlist..."));
1084 
1085 
1086  // Read the entire file in one pass so we can show better progress indication
1087  QStringList objects;
1088  while (!istream.atEnd())
1089  {
1090  objects.append(istream.readLine());
1091  }
1092  addingObjectsProgress->setMaximum(objects.size());
1093  addingObjectsProgress->setMinimum(0);
1094  addingObjectsProgress->show();
1095 
1096  QStringList failedObjects;
1097  for (std::size_t idx = 0; idx < objects.size(); ++idx)
1098  {
1099  const auto &objectName = objects[idx];
1100 
1101  if (addingObjectsProgress->wasCanceled())
1102  {
1103  QMessageBox msgBox =
1104  {
1105  QMessageBox::Icon::Warning,
1106  i18n("Canceling this will truncate your wishlist"),
1107  i18n("If you cancel this operation, your wishlist will be truncated and the following objects will be removed from the wishlist when you exit KStars. Are you sure this is okay?"),
1109  this
1110  };
1112  msgBox.setDetailedText(objects.mid(idx).join("\n") + "\n");
1113  if (msgBox.exec() == QMessageBox::Yes)
1114  break;
1115  else
1116  {
1117  addingObjectsProgress->reset();
1118  addingObjectsProgress->setValue(idx);
1119  addingObjectsProgress->show();
1120  }
1121 
1122  }
1123 
1124  SkyObject *o = KStarsData::Instance()->objectNamed(objectName);
1125 
1126  //If we haven't identified the object, try interpreting the
1127  //name as a star's genetive name (with ascii letters)
1128  if (!o)
1129  o = KStarsData::Instance()->skyComposite()->findStarByGenetiveName(line);
1130 
1131  if (o)
1132  {
1133  slotAddObject(o, false, true);
1134  }
1135  else
1136  {
1137  failedObjects.append(line);
1138  }
1139 
1140  addingObjectsProgress->setValue(idx + 1);
1141  qApp->processEvents();
1142  }
1143  delete (addingObjectsProgress);
1144  f.close();
1145 
1146  if (!failedObjects.isEmpty())
1147  {
1148  QMessageBox msgBox = {QMessageBox::Icon::Warning,
1149  i18np("Observing wishlist truncated: %1 object not found", "Observing wishlist truncated: %1 objects not found", failedObjects.size()),
1150  i18np("%1 object could not be found in the database, and will be removed from the observing wish list. We recommend that you copy its name as a backup so you can add it later.", "%1 objects could not be found in the database, and will be removed from the observing wish list. We recommend that you copy the detailed list as a backup, whereby you can later use the Batch Add feature in the Observation Planner to add them back using internet search.", failedObjects.size()),
1152  this
1153  };
1154  msgBox.setDetailedText(failedObjects.join("\n") + "\n");
1155  msgBox.exec();
1156  }
1157 }
1158 
1159 void ObservingList::slotSaveSession(bool nativeSave)
1160 {
1161  if (sessionList().isEmpty())
1162  {
1163  KSNotification::error(i18n("Cannot save an empty session list."));
1164  return;
1165  }
1166 
1167  if (m_listFileName.isEmpty())
1168  {
1169  slotSaveSessionAs(nativeSave);
1170  return;
1171  }
1172  QFile f(m_listFileName);
1173  if (!f.open(QIODevice::WriteOnly))
1174  {
1175  QString message = i18n("Could not open file %1. Try a different filename?", f.fileName());
1176  if (KMessageBox::warningYesNo(nullptr, message, i18n("Could Not Open File"), KGuiItem(i18n("Try Different")),
1177  KGuiItem(i18n("Do Not Try"))) == KMessageBox::Yes)
1178  {
1179  m_listFileName.clear();
1180  slotSaveSessionAs(nativeSave);
1181  }
1182  return;
1183  }
1184  QTextStream ostream(&f);
1185  OAL::Log log;
1186  ostream << log.writeLog(nativeSave);
1187  f.close();
1188  isModified = false; //We've saved the session, so reset the modified flag.
1189 }
1190 
1192 {
1193  QPointer<ObsListWizard> wizard = new ObsListWizard(KStars::Instance());
1194  if (wizard->exec() == QDialog::Accepted)
1195  {
1196  QPointer<QProgressDialog> addingObjectsProgress = new QProgressDialog();
1197  addingObjectsProgress->setWindowTitle(i18nc("@title:window", "Observing List Wizard"));
1198  addingObjectsProgress->setLabelText(i18n("Please wait while adding objects..."));
1199  addingObjectsProgress->setMaximum(wizard->obsList().size());
1200  addingObjectsProgress->setMinimum(0);
1201  addingObjectsProgress->setValue(0);
1202  addingObjectsProgress->show();
1203  int counter = 1;
1204  foreach (SkyObject *o, wizard->obsList())
1205  {
1206  slotAddObject(o);
1207  addingObjectsProgress->setValue(counter++);
1208  if (addingObjectsProgress->wasCanceled())
1209  break;
1210  qApp->processEvents();
1211  }
1212  delete addingObjectsProgress;
1213  }
1214 
1215  delete wizard;
1216 }
1217 
1219 {
1220  if (!o)
1221  return;
1222  float DayOffset = 0;
1223  if (TimeHash.value(o->name(), o->transitTime(dt, geo)).hour() > 12)
1224  DayOffset = 1;
1225 
1226  QDateTime midnight = QDateTime(dt.date(), QTime());
1227  KStarsDateTime ut = geo->LTtoUT(KStarsDateTime(midnight));
1228  double h1 = geo->GSTtoLST(ut.gst()).Hours();
1229  if (h1 > 12.0)
1230  h1 -= 24.0;
1231 
1232  ui->avt->setSecondaryLimits(h1, h1 + 24.0, -90.0, 90.0);
1233  ksal->setLocation(geo);
1234  ksal->setDate(ut);
1235  ui->avt->setGeoLocation(geo);
1236  ui->avt->setSunRiseSetTimes(ksal->getSunRise(), ksal->getSunSet());
1237  ui->avt->setDawnDuskTimes(ksal->getDawnAstronomicalTwilight(), ksal->getDuskAstronomicalTwilight());
1238  ui->avt->setMinMaxSunAlt(ksal->getSunMinAlt(), ksal->getSunMaxAlt());
1239  ui->avt->setMoonRiseSetTimes(ksal->getMoonRise(), ksal->getMoonSet());
1240  ui->avt->setMoonIllum(ksal->getMoonIllum());
1241  ui->avt->update();
1243  for (double h = -12.0; h <= 12.0; h += 0.5)
1244  {
1245  po->addPoint(h, findAltitude(o, (h + DayOffset * 24.0)));
1246  }
1247  ui->avt->removeAllPlotObjects();
1248  ui->avt->addPlotObject(po);
1249 }
1250 
1251 double ObservingList::findAltitude(SkyPoint *p, double hour)
1252 {
1253  // Jasem 2015-09-05 Using correct procedure to find altitude
1254  SkyPoint sp = *p; // make a copy
1255  QDateTime midnight = QDateTime(dt.date(), QTime());
1256  KStarsDateTime ut = geo->LTtoUT(KStarsDateTime(midnight));
1257  KStarsDateTime targetDateTime = ut.addSecs(hour * 3600.0);
1258  dms LST = geo->GSTtoLST(targetDateTime.gst());
1259  sp.EquatorialToHorizontal(&LST, geo->lat());
1260  return sp.alt().Degrees();
1261 }
1262 
1264 {
1265  noSelection = true;
1266  saveCurrentUserLog();
1267  ui->NotesEdit->setEnabled(false);
1268  ui->TimeEdit->setEnabled(false);
1269  ui->SetTime->setEnabled(false);
1270  ui->SearchImage->setEnabled(false);
1271  ui->DeleteImage->setEnabled(false);
1272  m_CurrentObject = nullptr;
1273  sessionView = index != 0;
1274  setSaveImagesButton();
1275  ui->WizardButton->setEnabled(!sessionView); //wizard adds only to the Wish List
1276  ui->OALExport->setEnabled(sessionView);
1277  //Clear the selection in the Tables
1278  ui->WishListView->clearSelection();
1279  ui->SessionView->clearSelection();
1280  //Clear the user log text box.
1281  saveCurrentUserLog();
1282  ui->NotesEdit->setPlainText("");
1283  ui->avt->removeAllPlotObjects();
1284 }
1285 
1287 {
1288  QPointer<LocationDialog> ld = new LocationDialog(this);
1289  if (ld->exec() == QDialog::Accepted)
1290  {
1291  geo = ld->selectedCity();
1292  ui->SetLocation->setText(geo->fullName());
1293  }
1294  delete ld;
1295 }
1296 
1298 {
1299  dt.setDate(ui->DateEdit->date());
1300  ui->avt->removeAllPlotObjects();
1301  //Creating a copy of the lists, we can't use the original lists as they'll keep getting modified as the loop iterates
1302  QList<QSharedPointer<SkyObject>> _obsList = m_WishList, _SessionList = m_SessionList;
1303 
1304  for (QSharedPointer<SkyObject> &o : _obsList)
1305  {
1306  if (o->name() != "star")
1307  {
1308  slotRemoveObject(o.data(), false, true);
1309  slotAddObject(o.data(), false, true);
1310  }
1311  }
1312  for (QSharedPointer<SkyObject> &obj : _SessionList)
1313  {
1314  if (obj->name() != "star")
1315  {
1316  slotRemoveObject(obj.data(), true, true);
1317  slotAddObject(obj.data(), true, true);
1318  }
1319  }
1320  SkyMap::Instance()->forceUpdate();
1321 }
1322 
1324 {
1325  SkyObject *o = currentObject();
1326  slotRemoveObject(o, true);
1327  TimeHash[o->name()] = ui->TimeEdit->time();
1328  slotAddObject(o, true, true);
1329 }
1330 
1332 {
1333  ui->SearchImage->setEnabled(false);
1334  //ui->ImagePreview->clearPreview();
1335  ui->ImagePreview->setPixmap(QPixmap());
1336 
1338  bool ok = true;
1339 
1340  int width = QInputDialog::getInt(this, i18n("Customized DSS Download"), i18n("Specify image width (arcminutes): "),
1341  15, 15, 75, 1, &ok);
1342  int height = QInputDialog::getInt(this, i18n("Customized DSS Download"),
1343  i18n("Specify image height (arcminutes): "), 15, 15, 75, 1, &ok);
1344  QStringList strList = (QStringList() << "poss2ukstu_blue"
1345  << "poss2ukstu_red"
1346  << "poss2ukstu_ir"
1347  << "poss1_blue"
1348  << "poss1_red"
1349  << "quickv"
1350  << "all");
1351  QString version =
1352  QInputDialog::getItem(this, i18n("Customized DSS Download"), i18n("Specify version: "), strList, 0, false, &ok);
1353 
1354  QUrl srcUrl(KSDssDownloader::getDSSURL(currentObject()->ra0(), currentObject()->dec0(), width, height, "gif",
1355  version, &md));
1356 
1357  delete m_dl;
1358  m_dl = new KSDssDownloader();
1359  connect(m_dl, SIGNAL(downloadComplete(bool)), SLOT(downloadReady(bool)));
1360  m_dl->startSingleDownload(srcUrl, getCurrentImagePath(), md);
1361 }
1362 
1363 void ObservingList::slotGetImage(bool _dss, const SkyObject *o)
1364 {
1365  dss = _dss;
1366  Q_ASSERT(
1367  !o ||
1368  o == currentObject()); // FIXME: Meaningless to operate on m_currentImageFileName unless o == currentObject()!
1369  if (!o)
1370  o = currentObject();
1371  ui->SearchImage->setEnabled(false);
1372  //ui->ImagePreview->clearPreview();
1373  //ui->ImagePreview->setPixmap(QPixmap());
1374  setCurrentImage(o);
1375  QString currentImagePath = getCurrentImagePath();
1376  if (QFile::exists(currentImagePath))
1377  QFile::remove(currentImagePath);
1378  //QUrl url;
1379  dss = true;
1380  qWarning() << "FIXME: Removed support for SDSS. Until reintroduction, we will supply a DSS image";
1381  std::function<void(bool)> slot = std::bind(&ObservingList::downloadReady, this, std::placeholders::_1);
1382  new KSDssDownloader(o, currentImagePath, slot, this);
1383 }
1384 
1385 void ObservingList::downloadReady(bool success)
1386 {
1387  // set downloadJob to 0, but don't delete it - the job will be deleted automatically
1388  // downloadJob = 0;
1389 
1390  delete m_dl;
1391  m_dl = nullptr; // required if we came from slotCustomDSS; does nothing otherwise
1392 
1393  if (!success)
1394  {
1395  KSNotification::sorry(i18n("Failed to download DSS/SDSS image."));
1396  }
1397  else
1398  {
1399  /*
1400  if( QFile( QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(m_currentImageFileName) ).size() > 13000)
1401  //The default image is around 8689 bytes
1402  */
1403  //ui->ImagePreview->showPreview( QUrl::fromLocalFile( getCurrentImagePath() ) );
1404  ui->ImagePreview->setPixmap(QPixmap(getCurrentImagePath()).scaledToHeight(ui->ImagePreview->width()));
1405  saveThumbImage();
1406  ui->ImagePreview->show();
1407  ui->ImagePreview->setCursor(Qt::PointingHandCursor);
1408  ui->DeleteImage->setEnabled(true);
1409  }
1410  /*
1411  // FIXME: Implement a priority order SDSS > DSS in the DSS downloader
1412  else if( ! dss )
1413  slotGetImage( true );
1414  */
1415 }
1416 
1418 {
1419  QString sanitizedName = o->name().remove(' ').remove('\'').remove('\"').toLower();
1420 
1421  // JM: Always use .png across all platforms. No JPGs at all?
1422  m_currentImageFileName = "image-" + sanitizedName + ".png";
1423 
1424  m_currentThumbImageFileName = "thumb-" + sanitizedName + ".png";
1425 
1426  // Does full image exists in the path?
1427  QString currentImagePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, m_currentImageFileName);
1428 
1429  // Let's try to fallback to thumb-* images if they exist
1430  if (currentImagePath.isEmpty())
1431  {
1432  currentImagePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, m_currentThumbImageFileName);
1433 
1434  // If thumb image exists, let's use it
1435  if (currentImagePath.isEmpty() == false)
1436  m_currentImageFileName = m_currentThumbImageFileName;
1437  }
1438 
1439  // 2017-04-14: Unnamed stars already unsupported in observing list
1440  /*
1441  if( o->name() == "star" )
1442  {
1443  QString RAString( o->ra0().toHMSString() );
1444  QString DecString( o->dec0().toDMSString() );
1445  m_currentImageFileName = "Image_J" + RAString.remove(' ').remove( ':' ) + DecString.remove(' ').remove( ':' ); // Note: Changed naming convention to standard 2016-08-25 asimha; old images shall have to be re-downloaded.
1446  // Unnecessary complication below:
1447  // QChar decsgn = ( (o->dec0().Degrees() < 0.0 ) ? '-' : '+' );
1448  // m_currentImageFileName = m_currentImageFileName.remove('+').remove('-') + decsgn;
1449  }
1450  */
1451 
1452  // 2017-04-14 JM: If we use .png always, let us use it across all platforms.
1453  /*
1454  QString imagePath = getCurrentImagePath();
1455  if ( QFile::exists( imagePath)) // New convention -- append filename extension so file is usable on Windows etc.
1456  {
1457  QFile::rename( imagePath, imagePath + ".png" );
1458  }
1459  m_currentImageFileName += ".png";
1460  */
1461 }
1462 
1464 {
1465  QString currentImagePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, m_currentImageFileName);
1466  if (QFile::exists(currentImagePath))
1467  {
1468  return currentImagePath;
1469  }
1470  else
1471  return QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(m_currentImageFileName);
1472 }
1473 
1475 {
1476  ui->SearchImage->setEnabled(false);
1477  ui->DeleteImage->setEnabled(false);
1478  m_CurrentObject = nullptr;
1479  //Clear the selection in the Tables
1480  ui->WishListView->clearSelection();
1481  ui->SessionView->clearSelection();
1482 
1483  foreach (QSharedPointer<SkyObject> o, getActiveList())
1484  {
1485  if (!o)
1486  continue; // FIXME: Why would we have null objects? But appears that we do.
1487  setCurrentImage(o.data());
1488  QString img(getCurrentImagePath());
1489  // QUrl url( ( Options::obsListPreferDSS() ) ? DSSUrl : SDSSUrl ); // FIXME: We have removed SDSS support!
1491  if (!o->isSolarSystem()) //TODO find a way for adding support for solar system images
1492  saveImage(url, img, o.data());
1493  }
1494 }
1495 
1496 void ObservingList::saveImage(QUrl /*url*/, QString /*filename*/, const SkyObject *o)
1497 {
1498  if (!o)
1499  o = currentObject();
1500  Q_ASSERT(o);
1501  if (!QFile::exists(getCurrentImagePath()))
1502  {
1503  // Call the DSS downloader
1504  slotGetImage(true, o);
1505  }
1506 }
1507 
1509 {
1511  QString currentImagePath = getCurrentImagePath();
1512  if (QFile::exists(currentImagePath))
1513  {
1514  QUrl url = QUrl::fromLocalFile(currentImagePath);
1515  iv = new ImageViewer(url);
1516  }
1517 
1518  if (iv)
1519  iv->show();
1520 }
1521 
1523 {
1524  if (KMessageBox::warningYesNo(nullptr, i18n("This will delete all saved images. Are you sure you want to do this?"),
1525  i18n("Delete All Images")) == KMessageBox::No)
1526  return;
1527  ui->ImagePreview->setCursor(Qt::ArrowCursor);
1528  ui->SearchImage->setEnabled(false);
1529  ui->DeleteImage->setEnabled(false);
1530  m_CurrentObject = nullptr;
1531  //Clear the selection in the Tables
1532  ui->WishListView->clearSelection();
1533  ui->SessionView->clearSelection();
1534  //ui->ImagePreview->clearPreview();
1535  ui->ImagePreview->setPixmap(QPixmap());
1536  QDirIterator iterator(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
1537  while (iterator.hasNext())
1538  {
1539  // TODO: Probably, there should be a different directory for cached images in the observing list.
1540  if (iterator.fileName().contains("Image") && (!iterator.fileName().contains("dat")) &&
1541  (!iterator.fileName().contains("obslist")))
1542  {
1543  QFile file(iterator.filePath());
1544  file.remove();
1545  }
1546  iterator.next();
1547  }
1548 }
1549 
1551 {
1552  ui->saveImages->setEnabled(!getActiveList().isEmpty());
1553 }
1554 
1555 // FIXME: Is there a reason to implement these as an event filter,
1556 // instead of as a signal-slot connection? Shouldn't we just use slots
1557 // to subscribe to various events from the Table / Session view?
1558 //
1559 // NOTE: ui->ImagePreview is a QLabel, which has no clicked() event or
1560 // public mouseReleaseEvent(), so eventFilter makes sense.
1562 {
1563  if (obj == ui->ImagePreview)
1564  {
1565  if (event->type() == QEvent::MouseButtonRelease)
1566  {
1567  if (currentObject())
1568  {
1569  if (!QFile::exists(getCurrentImagePath()))
1570  {
1571  if (!currentObject()->isSolarSystem())
1572  slotGetImage(Options::obsListPreferDSS());
1573  else
1574  slotSearchImage();
1575  }
1576  else
1577  slotImageViewer();
1578  }
1579  return true;
1580  }
1581  }
1582  if (obj == ui->WishListView->viewport() || obj == ui->SessionView->viewport())
1583  {
1584  bool sessionViewEvent = (obj == ui->SessionView->viewport());
1585 
1586  if (event->type() == QEvent::MouseButtonRelease) // Mouse button release event
1587  {
1588  QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
1589  QPoint pos(mouseEvent->globalX(), mouseEvent->globalY());
1590 
1591  if (mouseEvent->button() == Qt::RightButton)
1592  {
1593  if (!noSelection)
1594  {
1595  pmenu->initPopupMenu(sessionViewEvent, !singleSelection, showScope);
1596  pmenu->popup(pos);
1597  }
1598  return true;
1599  }
1600  }
1601  }
1602 
1603  if (obj == ui->WishListView || obj == ui->SessionView)
1604  {
1605  if (event->type() == QEvent::KeyPress)
1606  {
1607  QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
1608  if (keyEvent->key() == Qt::Key_Delete)
1609  {
1610  slotRemoveSelectedObjects();
1611  return true;
1612  }
1613  }
1614  }
1615 
1616  return false;
1617 }
1618 
1619 void ObservingList::slotSearchImage()
1620 {
1621  QPixmap *pm = new QPixmap(":/images/noimage.png");
1622  QPointer<ThumbnailPicker> tp = new ThumbnailPicker(currentObject(), *pm, this, 200, 200, i18n("Image Chooser"));
1623  if (tp->exec() == QDialog::Accepted)
1624  {
1625  QString currentImagePath = getCurrentImagePath();
1626  QFile f(currentImagePath);
1627 
1628  //If a real image was set, save it.
1629  if (tp->imageFound())
1630  {
1631  const auto image = *tp->image();
1632  image.save(f.fileName(), "PNG");
1633  //ui->ImagePreview->showPreview( QUrl::fromLocalFile( f.fileName() ) );
1634  saveThumbImage();
1635  slotNewSelection();
1636  ui->ImagePreview->setPixmap(image.scaledToHeight(ui->ImagePreview->width()));
1637  ui->ImagePreview->repaint();
1638  }
1639  }
1640  delete pm;
1641  delete tp;
1642 }
1643 
1645 {
1646  QFile::remove(getCurrentImagePath());
1647  ImagePreviewHash.remove(m_CurrentObject);
1648  slotNewSelection();
1649 }
1650 
1652 {
1653  QFileInfo const f(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(
1654  m_currentThumbImageFileName));
1655  if (!f.exists())
1656  {
1657  QImage img(getCurrentImagePath());
1659  img.save(f.filePath());
1660  }
1661 }
1662 
1663 QString ObservingList::getTime(const SkyObject *o) const
1664 {
1665  return TimeHash.value(o->name(), QTime(30, 0, 0)).toString("h:mm:ss AP");
1666 }
1667 
1668 QTime ObservingList::scheduledTime(SkyObject *o) const
1669 {
1670  return TimeHash.value(o->name(), o->transitTime(dt, geo));
1671 }
1672 
1673 void ObservingList::setTime(const SkyObject *o, QTime t)
1674 {
1675  TimeHash.insert(o->name(), t);
1676 }
1677 
1679 {
1680  slotSaveSessionAs(false);
1681 }
1682 
1683 void ObservingList::slotAddVisibleObj()
1684 {
1685  KStarsDateTime lt = dt;
1686  lt.setTime(QTime(8, 0, 0));
1687  QPointer<WUTDialog> w = new WUTDialog(KStars::Instance(), sessionView, geo, lt);
1688  w->init();
1689  QModelIndexList selectedItems;
1690  selectedItems =
1691  m_WishListSortModel->mapSelectionToSource(ui->WishListView->selectionModel()->selection()).indexes();
1692  if (selectedItems.size())
1693  {
1694  foreach (const QModelIndex &i, selectedItems)
1695  {
1696  foreach (QSharedPointer<SkyObject> o, obsList())
1697  if (getObjectName(o.data()) == i.data().toString() && w->checkVisibility(o.data()))
1698  slotAddObject(
1699  o.data(),
1700  true); // FIXME: Better if there is a QSharedPointer override for this, although the check will ensure that we don't duplicate.
1701  }
1702  }
1703  delete w;
1704 }
1705 
1707 {
1708  foreach (QSharedPointer<SkyObject> o, sessionList())
1709  {
1710  if (getObjectName(o.data(), false) == name)
1711  return o.data();
1712  }
1713  return nullptr;
1714 }
1715 
1717 {
1718  ui->tabWidget->setCurrentIndex(1);
1719  ui->SessionView->selectionModel()->clear();
1720  for (int irow = m_SessionModel->rowCount() - 1; irow >= 0; --irow)
1721  {
1722  QModelIndex mSortIndex = m_SessionSortModel->index(irow, 0);
1723  QModelIndex mIndex = m_SessionSortModel->mapToSource(mSortIndex);
1724  int idxrow = mIndex.row();
1725  if (m_SessionModel->item(idxrow, 0)->text() == getObjectName(o))
1726  ui->SessionView->selectRow(idxrow);
1727  slotNewSelection();
1728  }
1729 }
1730 
1732 {
1733  ui->ImagePreview->setPixmap(m_NoImagePixmap);
1734  ui->ImagePreview->update();
1735 }
1736 
1738 {
1739  QString finalObjectName;
1740  if (o->name() == "star")
1741  {
1742  const StarObject *s = dynamic_cast<const StarObject *>(o);
1743 
1744  // JM: Enable HD Index stars to be added to the observing list.
1745  if (s != nullptr && s->getHDIndex() != 0)
1746  finalObjectName = QString("HD %1").arg(QString::number(s->getHDIndex()));
1747  }
1748  else
1749  finalObjectName = translated ? o->translatedName() : o->name();
1750 
1751  return finalObjectName;
1752 }
1753 
1755 {
1756  // FIXME: Update upon gaining visibility, do not update when not visible
1758  // qCDebug(KSTARS) << "Updating altitudes in observation planner @ JD - J2000 = " << double( now.djd() - J2000 );
1759  for (int irow = m_WishListModel->rowCount() - 1; irow >= 0; --irow)
1760  {
1761  QModelIndex idx = m_WishListSortModel->mapToSource(m_WishListSortModel->index(irow, 0));
1762  SkyObject *o = static_cast<SkyObject *>(idx.data(Qt::UserRole + 1).value<void *>());
1763  Q_ASSERT(o);
1764  SkyPoint p = o->recomputeHorizontalCoords(now, geo);
1765  idx =
1766  m_WishListSortModel->mapToSource(m_WishListSortModel->index(irow, m_WishListSortModel->columnCount() - 1));
1767  QStandardItem *replacement = m_altCostHelper(p);
1768  m_WishListModel->setData(idx, replacement->data(Qt::DisplayRole), Qt::DisplayRole);
1769  m_WishListModel->setData(idx, replacement->data(Qt::UserRole), Qt::UserRole);
1770  delete replacement;
1771  }
1772  emit m_WishListModel->dataChanged(
1773  m_WishListModel->index(0, m_WishListModel->columnCount() - 1),
1774  m_WishListModel->index(m_WishListModel->rowCount() - 1, m_WishListModel->columnCount() - 1));
1775 }
1776 
1777 QSharedPointer<SkyObject> ObservingList::findObject(const SkyObject *o, bool session)
1778 {
1779  const QList<QSharedPointer<SkyObject>> &list = (session ? sessionList() : obsList());
1780  const QString &target = getObjectName(o);
1781  foreach (QSharedPointer<SkyObject> obj, list)
1782  {
1783  if (getObjectName(obj.data()) == target)
1784  return obj;
1785  }
1786  return QSharedPointer<SkyObject>(); // null pointer
1787 }
int getInt(QWidget *parent, const QString &title, const QString &label, int value, int min, int max, int step, bool *ok, Qt::WindowFlags flags)
bool removeRow(int row, const QModelIndex &parent)
QString filePath() const const
void append(const T &value)
const dms & alt() const
Definition: skypoint.h:281
static KStarsDateTime currentDateTime()
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options, const QStringList &supportedSchemes)
QString readAll()
QString version
Used for DSS – Indicates which version of scans to pull.
Definition: ksdssimage.h:65
void slotClearList()
slotClearList Remove all objects from current list
T * data() const const
void setDefaultButton(QPushButton *button)
void slotSaveAllImages()
Downloads the images of all the objects in the session list Note: This downloads the SDSS image,...
void selectObject(const SkyObject *o)
make a selection in the session view
SkyPoint recomputeHorizontalCoords(const KStarsDateTime &dt, const GeoLocation *geo) const
Like recomputeCoords, but also calls EquatorialToHorizontal before returning.
Definition: skyobject.cpp:329
UserRole
MouseButtonRelease
void slotAddToSession()
Add the object to the Session List.
void slotWUT()
Open the WUT dialog.
QString number(int n, int base)
void slotGetImage(bool _dss=false, const SkyObject *o=nullptr)
Downloads the corresponding DSS or SDSS image from the web and displays it.
bool remove()
QString text() const const
QString getItem(QWidget *parent, const QString &title, const QString &label, const QStringList &items, int current, bool editable, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
Qt::MouseButton button() const const
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
a dms subclass that caches its sine and cosine values every time the angle is changed.
Definition: cachingdms.h:18
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
int column() const const
T value() const const
void slotRemoveObject(const SkyObject *o=nullptr, bool session=false, bool update=false)
Remove skyobject from the observing list.
Helps download a DSS image.
bool hasNext() const const
void setClickedPoint(const SkyPoint *f)
Set the ClickedPoint to the skypoint given as an argument.
Definition: skymap.cpp:996
QIcon fromTheme(const QString &name)
SkyObject * objectNamed(const QString &name)
Find object by name.
Definition: kstarsdata.cpp:429
virtual QString name(void) const
Definition: skyobject.h:145
SkyObject * clickedObject() const
Retrieve the object nearest to a mouse click event.
Definition: skymap.h:243
Structure to hold some DSS image metadata.
Definition: ksdssimage.h:35
QString getObjectName(const SkyObject *o, bool translated=true)
get object name.
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QString translatedName() const
Definition: skyobject.h:148
static KStarsDateTime currentDateTimeUtc()
void saveCurrentUserLog()
Save the user log text to a file.
static CatalogObject * resolveAndAdd(CatalogsDB::DBManager &db_manager, const QString &query)
Resolves an object using the internet and adds it to the database.
Definition: finddialog.cpp:460
QString next()
const SkyObjectUserdata::Data & getUserData(const QString &name)
Get a reference to the user data of an object with the name name.
RightButton
bool exists() const const
double findAltitude(SkyPoint *p, double hour=0)
Return the altitude of the given SkyObject for the given hour.
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
Definition: skypoint.cpp:77
KGuiItem save()
void setDetailedText(const QString &text)
ButtonCode warningYesNo(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
KStarsDateTime addSecs(double s) const
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
int getHDIndex() const
Definition: starobject.h:248
virtual SkyObject * clone() const
Create copy of object.
Definition: skyobject.cpp:50
static KStars * Instance()
Definition: kstars.h:125
void showMessage(const QString &message, int timeout)
void setTime(const QTime &t)
Assign the Time according to a QTime object.
bool isValid() const const
void slotWizard()
construct a new observing list using the wizard.
QVariant data(int role) const const
int size() const const
void slotSaveSessionAs(bool nativeSave=true)
save the current observing session plan to disk, specify filename.
void slotCenterObject()
center the selected object in the display
KeepAspectRatio
QString i18n(const char *text, const TYPE &arg...)
Store several time-dependent astronomical quantities.
Definition: ksnumbers.h:42
void slotSlewToObject()
slew the telescope to the selected object
bool atEnd() const const
void slotCustomDSS()
Present the user with options to get the right DSS image for the job.
void slotSaveSession(bool nativeSave=true)
save the current session
Provides a class to hold a DSS Image along with its metadata.
Definition: ksdssimage.h:20
void slotRemoveSelectedObjects()
Remove skyobjects which are highlighted in the observing list tool from the observing list.
void slotAddObject(const SkyObject *o=nullptr, bool session=false, bool update=false)
add a new object to list o pointer to the object to add to the list session flag toggle adding the ob...
bool eventFilter(QObject *obj, QEvent *event) override
This is the declaration of the event filter function which is installed on the KImageFilePreview and ...
void slotLoadWishList()
Load the Wish list from disk.
void slotAVT()
Show the Altitude vs Time for selecteld objects.
subclass of SkyObject specialized for stars.
Definition: starobject.h:32
bool isEmpty() const const
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options, const QStringList &supportedSchemes)
QUrl fromLocalFile(const QString &localFile)
GeoLocation * geo()
Definition: kstarsdata.h:229
ArrowCursor
QString readLine(qint64 maxlen)
void saveThumbImage()
saves a thumbnail image for the details dialog from the downloaded image
void saveImage(QUrl url, QString filename, const SkyObject *o=nullptr)
saves the image synchronously from a given URL into a given file url the url from which the image has...
void setClickedObject(SkyObject *o)
Set the ClickedObject pointer to the argument.
Definition: skymap.cpp:331
float width
Width in arcminutes.
Definition: ksdssimage.h:79
bool isEmpty() const const
QString toLocalFile() const const
int globalX() const const
int globalY() const const
QString getCurrentImagePath()
Returns a path to the current image, or a writable image.
std::pair< bool, QString > updateUserLog(const QString &name, const QString &newLog)
Update the user log of the object with the name to contain newLog (find and replace).
void slotDeleteAllImages()
Removes all the save DSS/SDSS images from the disk.
QString getMultiLineText(QWidget *parent, const QString &title, const QString &label, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
GeoCoordinates geo(const QVariant &location)
QString join(const QString &separator) const const
This is the main window for KStars. In addition to the GUI elements, the class contains the program c...
Definition: kstars.h:92
void slotFind()
Open the Find Dialog.
Key_Delete
void setupUi(QWidget *widget)
int indexOf(QStringView str, int from) const const
void setCurrentImage(const SkyObject *o)
Sets the image parameters for the current object o The passed object for setting the parameters.
float height
Height in arcminutes.
Definition: ksdssimage.h:77
void plot(SkyObject *o)
Plot the SkyObject's Altitude vs Time in the AVTPlotWidget.
void slotDetails()
Show the details window for the selected object.
QPixmap scaledToHeight(int height, Qt::TransformationMode mode) const const
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
void slotCenter()
Center the display at the point ClickedPoint.
Definition: skymap.cpp:345
QString & remove(int position, int n)
QStatusBar * statusBar() const const
int row() const const
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
SkyMapComposite * skyComposite()
Definition: kstarsdata.h:165
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
QString fileName() const const
void slotDeleteCurrentImage()
Remove the current image.
QString toLower() const const
void addPoint(const QPointF &p, const QString &label=QString(), double barWidth=0.0)
virtual int exec() override
QPixmap scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
SkyObject * findObjectByName(QString name)
return the object with the name as the passed QString from the Session List, return null otherwise
void slotImageViewer()
Shows the image in a ImageViewer window.
void slotSetTime()
Takes the time from the QTimeEdit box and sets it as the time parameter in the tableview of the Sessi...
Image viewer window for KStars.
Definition: imageviewer.h:56
KIOCORE_EXPORT CopyJob * move(const QList< QUrl > &src, const QUrl &dest, JobFlags flags=DefaultFlags)
Implement methods to find important times in a day.
Definition: ksalmanac.h:26
void slotUpdateAltitudes()
Recalculate and update the values of the altitude in the wishlist for the current time.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void slotSaveList()
save the current observing list to disk.
void setDefaultImage()
set the default image in the image preview.
const double & Degrees() const
Definition: dms.h:141
QString filePath(const QString &fileName) const const
virtual QVariant data(int role) const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void saveCurrentList()
If the current list has unsaved changes, ask the user about saving it.
void slotBatchAdd()
Batch add from a list of objects.
void slotOALExport()
Export a target list to the oal compliant format.
QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const
The same iteration technique described in riseSetTime() is used here.
Definition: skyobject.cpp:239
QChar * data()
virtual int rowCount(const QModelIndex &parent) const const override
void forceUpdate(bool now=false)
Recalculates the positions of objects in the sky, and then repaints the sky map.
Definition: skymap.cpp:1186
void slotLocation()
Opens the Location dialog to set the GeoLocation for the sessionlist.
virtual void setData(const QVariant &value, int role)
void slotEyepieceView()
Show the eyepiece field view.
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
void slotAddToEkosScheduler()
slotAddToEkosScheduler Add object to Ekos scheduler
StrongFocus
QStandardItem * item(int row, int column) const const
FastTransformation
ButtonCode questionYesNo(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Notify)
void slotChangeTab(int index)
toggle the setEnabled flags according to current view set the m_currentItem to nullptr and clear sele...
static QString getDSSURL(const SkyPoint *const p, const QString &version="all", struct KSDssImage::Metadata *md=nullptr)
High-level method to create a URL to obtain a DSS image for a given SkyPoint.
Information about an object in the sky.
Definition: skyobject.h:41
bool save(const QString &fileName, const char *format, int quality) const const
QString message
void slotEyepieceView(SkyPoint *sp, const QString &imagePath=QString())
Show the eyepiece view tool.
void slotNewSelection()
Tasks needed when changing the selected object Save the user log of the previous selected object,...
WA_LayoutUsesWidgetRect
KGuiItem discard()
char band
Photometric band (UBVRI...) Use "?" for unknown.
Definition: ksdssimage.h:81
Sort best observation times by reimplementing lessThan() to work on the transit times of objects.
void setSaveImagesButton()
decides on whether to enable the SaveImages button or not
void slotOpenList()
load an observing list from disk.
Relevant data about an observing location on Earth.
Definition: geolocation.h:27
void slotUpdate()
Updates the tableviews for the new geolocation and date.
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 12 2022 04:00:56 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.