Kstars

observinglist.cpp
1/*
2 SPDX-FileCopyrightText: 2004-2020 Jeff Woods <jcwoods@bellsouth.net>
3 SPDX-FileCopyrightText: 2004-2020 Jason Harris <jharris@30doradus.org>
4 SPDX-FileCopyrightText: Prakash Mohan <prakash.mohan@kdemail.net>
5 SPDX-FileCopyrightText: Akarsh Simha <akarsh@kde.org>
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// ---------------------------------
59ObservingListUI::ObservingListUI(QWidget *p) : QFrame(p)
60{
61 setupUi(this);
62}
63
64//
65// ObservingList
66// ---------------------------------
67ObservingList::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
221void 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;
232
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
242void 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
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 {
273 KStars::Instance()->statusBar()->showMessage(
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();
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
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
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.
381 itemList << getItemWithUserRole(KSUtils::constNameToAbbrev(
382 KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(obj.data())))
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 }
394}
395
396void 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)
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
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;
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
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!
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",
598 QString::number(md.width), QString::number(md.height), QString() + md.band, md.version));
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 {
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 {
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.
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.
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::devices())
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 = oneDevice->getMount();
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 {
751 if (getObjectName(o.data()) == i.data().toString())
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 {
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 {
796 }
797 else
798 {
799 slotAddObject(object, sessionView);
800 }
801 }
802
803 if (!failedObjects.isEmpty())
804 {
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
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 {
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
854void ObservingList::slotClose()
855{
856 //Save the current User log text
858 ui->avt->removeAllPlotObjects();
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
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)
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();
1017 }
1018}
1019
1021{
1022 QFile f;
1023 // FIXME: Move wishlist into a database.
1024 // TODO: Support multiple wishlists.
1025
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()));
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 }
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
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
1097 for (std::size_t idx = 0; idx < objects.size(); ++idx)
1098 {
1099 const auto &objectName = objects[idx];
1100
1101 if (addingObjectsProgress->wasCanceled())
1102 {
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 };
1111 msgBox.setDefaultButton(QMessageBox::No);
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
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 {
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();
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{
1194 if (wizard->exec() == QDialog::Accepted)
1195 {
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
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
1252{
1253 // Jasem 2015-09-05 Using correct procedure to find altitude
1254 SkyPoint sp = *p; // make a copy
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;
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;
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.
1282 ui->NotesEdit->setPlainText("");
1283 ui->avt->removeAllPlotObjects();
1284}
1285
1287{
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
1305 {
1306 if (o->name() != "star")
1307 {
1308 slotRemoveObject(o.data(), false, true);
1309 slotAddObject(o.data(), false, true);
1310 }
1311 }
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
1364{
1365 dss = _dss;
1366 if (!o)
1367 o = currentObject();
1368 ui->SearchImage->setEnabled(false);
1369 setCurrentImage(o);
1373 //QUrl url;
1374 dss = true;
1375 std::function<void(bool)> slot = std::bind(&ObservingList::downloadReady, this, std::placeholders::_1);
1376 new KSDssDownloader(o, currentImagePath, slot, this);
1377}
1378
1379void ObservingList::downloadReady(bool success)
1380{
1381 // set downloadJob to 0, but don't delete it - the job will be deleted automatically
1382 // downloadJob = 0;
1383
1384 delete m_dl;
1385 m_dl = nullptr; // required if we came from slotCustomDSS; does nothing otherwise
1386
1387 if (!success)
1388 {
1389 KSNotification::sorry(i18n("Failed to download DSS/SDSS image."));
1390 }
1391 else
1392 {
1393 /*
1394 if( QFile( QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(m_currentImageFileName) ).size() > 13000)
1395 //The default image is around 8689 bytes
1396 */
1397 //ui->ImagePreview->showPreview( QUrl::fromLocalFile( getCurrentImagePath() ) );
1398 ui->ImagePreview->setPixmap(QPixmap(getCurrentImagePath()).scaledToHeight(ui->ImagePreview->width()));
1400 ui->ImagePreview->show();
1401 ui->ImagePreview->setCursor(Qt::PointingHandCursor);
1402 ui->DeleteImage->setEnabled(true);
1403 }
1404 /*
1405 // FIXME: Implement a priority order SDSS > DSS in the DSS downloader
1406 else if( ! dss )
1407 slotGetImage( true );
1408 */
1409}
1410
1412{
1413 QString sanitizedName = o->name().remove(' ').remove('\'').remove('\"').toLower();
1414
1415 // JM: Always use .png across all platforms. No JPGs at all?
1416 m_currentImageFileName = "image-" + sanitizedName + ".png";
1417
1418 m_currentThumbImageFileName = "thumb-" + sanitizedName + ".png";
1419
1420 // Does full image exists in the path?
1421 QString currentImagePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, m_currentImageFileName);
1422
1423 // Let's try to fallback to thumb-* images if they exist
1424 if (currentImagePath.isEmpty())
1425 {
1426 currentImagePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, m_currentThumbImageFileName);
1427
1428 // If thumb image exists, let's use it
1429 if (currentImagePath.isEmpty() == false)
1430 m_currentImageFileName = m_currentThumbImageFileName;
1431 }
1432
1433 // 2017-04-14: Unnamed stars already unsupported in observing list
1434 /*
1435 if( o->name() == "star" )
1436 {
1437 QString RAString( o->ra0().toHMSString() );
1438 QString DecString( o->dec0().toDMSString() );
1439 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.
1440 // Unnecessary complication below:
1441 // QChar decsgn = ( (o->dec0().Degrees() < 0.0 ) ? '-' : '+' );
1442 // m_currentImageFileName = m_currentImageFileName.remove('+').remove('-') + decsgn;
1443 }
1444 */
1445
1446 // 2017-04-14 JM: If we use .png always, let us use it across all platforms.
1447 /*
1448 QString imagePath = getCurrentImagePath();
1449 if ( QFile::exists( imagePath)) // New convention -- append filename extension so file is usable on Windows etc.
1450 {
1451 QFile::rename( imagePath, imagePath + ".png" );
1452 }
1453 m_currentImageFileName += ".png";
1454 */
1455}
1456
1458{
1459 QString currentImagePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, m_currentImageFileName);
1461 {
1462 return currentImagePath;
1463 }
1464 else
1465 return QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(m_currentImageFileName);
1466}
1467
1469{
1470 ui->SearchImage->setEnabled(false);
1471 ui->DeleteImage->setEnabled(false);
1472 m_CurrentObject = nullptr;
1473 //Clear the selection in the Tables
1474 ui->WishListView->clearSelection();
1475 ui->SessionView->clearSelection();
1476
1477 foreach (QSharedPointer<SkyObject> o, getActiveList())
1478 {
1479 if (!o)
1480 continue; // FIXME: Why would we have null objects? But appears that we do.
1481 setCurrentImage(o.data());
1483 // QUrl url( ( Options::obsListPreferDSS() ) ? DSSUrl : SDSSUrl ); // FIXME: We have removed SDSS support!
1484 QUrl url(KSDssDownloader::getDSSURL(o.data()));
1485 if (!o->isSolarSystem()) //TODO find a way for adding support for solar system images
1486 saveImage(url, img, o.data());
1487 }
1488}
1489
1490void ObservingList::saveImage(QUrl /*url*/, QString /*filename*/, const SkyObject *o)
1491{
1492 if (!o)
1493 o = currentObject();
1494 Q_ASSERT(o);
1496 {
1497 // Call the DSS downloader
1498 slotGetImage(true, o);
1499 }
1500}
1501
1503{
1507 {
1509 iv = new ImageViewer(url);
1510 }
1511
1512 if (iv)
1513 iv->show();
1514}
1515
1517{
1518 if (KMessageBox::warningYesNo(nullptr, i18n("This will delete all saved images. Are you sure you want to do this?"),
1519 i18n("Delete All Images")) == KMessageBox::No)
1520 return;
1521 ui->ImagePreview->setCursor(Qt::ArrowCursor);
1522 ui->SearchImage->setEnabled(false);
1523 ui->DeleteImage->setEnabled(false);
1524 m_CurrentObject = nullptr;
1525 //Clear the selection in the Tables
1526 ui->WishListView->clearSelection();
1527 ui->SessionView->clearSelection();
1528 //ui->ImagePreview->clearPreview();
1529 ui->ImagePreview->setPixmap(QPixmap());
1530 QDirIterator iterator(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
1531 while (iterator.hasNext())
1532 {
1533 // TODO: Probably, there should be a different directory for cached images in the observing list.
1534 if (iterator.fileName().contains("Image") && (!iterator.fileName().contains("dat")) &&
1535 (!iterator.fileName().contains("obslist")))
1536 {
1537 QFile file(iterator.filePath());
1538 file.remove();
1539 }
1540 iterator.next();
1541 }
1542}
1543
1545{
1546 ui->saveImages->setEnabled(!getActiveList().isEmpty());
1547}
1548
1549// FIXME: Is there a reason to implement these as an event filter,
1550// instead of as a signal-slot connection? Shouldn't we just use slots
1551// to subscribe to various events from the Table / Session view?
1552//
1553// NOTE: ui->ImagePreview is a QLabel, which has no clicked() event or
1554// public mouseReleaseEvent(), so eventFilter makes sense.
1556{
1557 if (obj == ui->ImagePreview)
1558 {
1559 if (event->type() == QEvent::MouseButtonRelease)
1560 {
1561 if (currentObject())
1562 {
1564 {
1565 if (!currentObject()->isSolarSystem())
1566 slotGetImage(Options::obsListPreferDSS());
1567 else
1568 slotSearchImage();
1569 }
1570 else
1572 }
1573 return true;
1574 }
1575 }
1576 if (obj == ui->WishListView->viewport() || obj == ui->SessionView->viewport())
1577 {
1578 bool sessionViewEvent = (obj == ui->SessionView->viewport());
1579
1580 if (event->type() == QEvent::MouseButtonRelease) // Mouse button release event
1581 {
1582 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
1583 QPoint pos(mouseEvent->globalX(), mouseEvent->globalY());
1584
1585 if (mouseEvent->button() == Qt::RightButton)
1586 {
1587 if (!noSelection)
1588 {
1589 pmenu->initPopupMenu(sessionViewEvent, !singleSelection, showScope);
1590 pmenu->popup(pos);
1591 }
1592 return true;
1593 }
1594 }
1595 }
1596
1597 if (obj == ui->WishListView || obj == ui->SessionView)
1598 {
1599 if (event->type() == QEvent::KeyPress)
1600 {
1601 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
1602 if (keyEvent->key() == Qt::Key_Delete)
1603 {
1605 return true;
1606 }
1607 }
1608 }
1609
1610 return false;
1611}
1612
1613void ObservingList::slotSearchImage()
1614{
1615 QPixmap *pm = new QPixmap(":/images/noimage.png");
1616 QPointer<ThumbnailPicker> tp = new ThumbnailPicker(currentObject(), *pm, this, 200, 200, i18n("Image Chooser"));
1617 if (tp->exec() == QDialog::Accepted)
1618 {
1621
1622 //If a real image was set, save it.
1623 if (tp->imageFound())
1624 {
1625 const auto image = *tp->image();
1626 image.save(f.fileName(), "PNG");
1627 //ui->ImagePreview->showPreview( QUrl::fromLocalFile( f.fileName() ) );
1630 ui->ImagePreview->setPixmap(image.scaledToHeight(ui->ImagePreview->width()));
1631 ui->ImagePreview->repaint();
1632 }
1633 }
1634 delete pm;
1635 delete tp;
1636}
1637
1639{
1641 ImagePreviewHash.remove(m_CurrentObject);
1643}
1644
1646{
1647 QFileInfo const f(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(
1648 m_currentThumbImageFileName));
1649 if (!f.exists())
1650 {
1653 img.save(f.filePath());
1654 }
1655}
1656
1657QString ObservingList::getTime(const SkyObject *o) const
1658{
1659 return TimeHash.value(o->name(), QTime(30, 0, 0)).toString("h:mm:ss AP");
1660}
1661
1662QTime ObservingList::scheduledTime(SkyObject *o) const
1663{
1664 return TimeHash.value(o->name(), o->transitTime(dt, geo));
1665}
1666
1667void ObservingList::setTime(const SkyObject *o, QTime t)
1668{
1669 TimeHash.insert(o->name(), t);
1670}
1671
1673{
1674 slotSaveSessionAs(false);
1675}
1676
1677void ObservingList::slotAddVisibleObj()
1678{
1679 KStarsDateTime lt = dt;
1680 lt.setTime(QTime(8, 0, 0));
1681 QPointer<WUTDialog> w = new WUTDialog(KStars::Instance(), sessionView, geo, lt);
1682 w->init();
1683 QModelIndexList selectedItems;
1684 selectedItems =
1685 m_WishListSortModel->mapSelectionToSource(ui->WishListView->selectionModel()->selection()).indexes();
1686 if (selectedItems.size())
1687 {
1688 foreach (const QModelIndex &i, selectedItems)
1689 {
1690 foreach (QSharedPointer<SkyObject> o, obsList())
1691 if (getObjectName(o.data()) == i.data().toString() && w->checkVisibility(o.data()))
1693 o.data(),
1694 true); // FIXME: Better if there is a QSharedPointer override for this, although the check will ensure that we don't duplicate.
1695 }
1696 }
1697 delete w;
1698}
1699
1701{
1703 {
1704 if (getObjectName(o.data(), false) == name)
1705 return o.data();
1706 }
1707 return nullptr;
1708}
1709
1711{
1712 ui->tabWidget->setCurrentIndex(1);
1713 ui->SessionView->selectionModel()->clear();
1714 for (int irow = m_SessionModel->rowCount() - 1; irow >= 0; --irow)
1715 {
1716 QModelIndex mSortIndex = m_SessionSortModel->index(irow, 0);
1717 QModelIndex mIndex = m_SessionSortModel->mapToSource(mSortIndex);
1718 int idxrow = mIndex.row();
1719 if (m_SessionModel->item(idxrow, 0)->text() == getObjectName(o))
1720 ui->SessionView->selectRow(idxrow);
1722 }
1723}
1724
1726{
1727 ui->ImagePreview->setPixmap(m_NoImagePixmap);
1728 ui->ImagePreview->update();
1729}
1730
1732{
1734 if (o->name() == "star")
1735 {
1736 const StarObject *s = dynamic_cast<const StarObject *>(o);
1737
1738 // JM: Enable HD Index stars to be added to the observing list.
1739 if (s != nullptr && s->getHDIndex() != 0)
1741 }
1742 else
1743 finalObjectName = translated ? o->translatedName() : o->name();
1744
1745 return finalObjectName;
1746}
1747
1749{
1750 // FIXME: Update upon gaining visibility, do not update when not visible
1752 // qCDebug(KSTARS) << "Updating altitudes in observation planner @ JD - J2000 = " << double( now.djd() - J2000 );
1753 for (int irow = m_WishListModel->rowCount() - 1; irow >= 0; --irow)
1754 {
1755 QModelIndex idx = m_WishListSortModel->mapToSource(m_WishListSortModel->index(irow, 0));
1756 SkyObject *o = static_cast<SkyObject *>(idx.data(Qt::UserRole + 1).value<void *>());
1757 Q_ASSERT(o);
1759 idx =
1760 m_WishListSortModel->mapToSource(m_WishListSortModel->index(irow, m_WishListSortModel->columnCount() - 1));
1761 QStandardItem *replacement = m_altCostHelper(p);
1762 m_WishListModel->setData(idx, replacement->data(Qt::DisplayRole), Qt::DisplayRole);
1763 m_WishListModel->setData(idx, replacement->data(Qt::UserRole), Qt::UserRole);
1764 delete replacement;
1765 }
1766 emit m_WishListModel->dataChanged(
1767 m_WishListModel->index(0, m_WishListModel->columnCount() - 1),
1768 m_WishListModel->index(m_WishListModel->rowCount() - 1, m_WishListModel->columnCount() - 1));
1769}
1770
1771QSharedPointer<SkyObject> ObservingList::findObject(const SkyObject *o, bool session)
1772{
1773 const QList<QSharedPointer<SkyObject>> &list = (session ? sessionList() : obsList());
1774 const QString &target = getObjectName(o);
1775 foreach (QSharedPointer<SkyObject> obj, list)
1776 {
1777 if (getObjectName(obj.data()) == target)
1778 return obj;
1779 }
1780 return QSharedPointer<SkyObject>(); // null pointer
1781}
the Altitude vs.
Definition altvstime.h:41
a dms subclass that caches its sine and cosine values every time the angle is changed.
Definition cachingdms.h:19
DetailDialog is a window showing detailed information for a selected object.
static CatalogObject * resolveAndAdd(CatalogsDB::DBManager &db_manager, const QString &query)
Resolves an object using the internet and adds it to the database.
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
Definition geolocation.h:28
QString fullName() const
const CachingDms * lat() const
Definition geolocation.h:70
A class that implements methods to find sun rise, sun set, twilight begin / end times,...
Definition ksalmanac.h:27
Helps download a DSS image.
void startSingleDownload(const QUrl srcUrl, const QString &destFileName, KSDssImage::Metadata &md)
Stateful single-download of a supplied URL.
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.
Provides a class to hold a DSS Image along with its metadata.
Definition ksdssimage.h:21
There are several time-dependent values used in position calculations, that are not specific to an ob...
Definition ksnumbers.h:43
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
KStarsDateTime addSecs(double s) const
void setDate(const QDate &d)
Assign the Date according to a QDate object.
static KStarsDateTime currentDateTimeUtc()
static KStarsDateTime currentDateTime()
long double djd() const
void setTime(const QTime &t)
Assign the Time according to a QTime object.
This is the main window for KStars.
Definition kstars.h:91
static KStars * Instance()
Definition kstars.h:123
Dialog for changing the geographic location of the observer.
The Popup Menu for the observing list in KStars.
Wizard for constructing observing lists.
void slotUpdateAltitudes()
Recalculate and update the values of the altitude in the wishlist for the current time.
void saveThumbImage()
saves a thumbnail image for the details dialog from the downloaded image
void slotLoadWishList()
Load the Wish list from disk.
void saveCurrentList()
If the current list has unsaved changes, ask the user about saving it.
void slotSaveSessionAs(bool nativeSave=true)
save the current observing session plan to disk, specify filename.
void slotOALExport()
Export a target list to the oal compliant format.
QString getCurrentImagePath()
Returns a path to the current image, or a writable image.
QList< QSharedPointer< SkyObject > > & sessionList()
void slotFind()
Open the Find Dialog.
void setDefaultImage()
set the default image in the image preview.
void setCurrentImage(const SkyObject *o)
Sets the image parameters for the current object o The passed object for setting the parameters.
void slotUpdate()
Updates the tableviews for the new geolocation and date.
QString getObjectName(const SkyObject *o, bool translated=true)
get object name.
void slotImageViewer()
Shows the image in a ImageViewer window.
QList< QSharedPointer< SkyObject > > & obsList()
void slotSaveAllImages()
Downloads the images of all the objects in the session list Note: This downloads the SDSS image,...
void slotAddToSession()
Add the object to the Session List.
void slotAVT()
Show the Altitude vs Time for selecteld objects.
void slotWizard()
construct a new observing list using the wizard.
void setSaveImagesButton()
decides on whether to enable the SaveImages button or not
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...
void slotOpenList()
load an observing list from disk.
void slotGetImage(bool _dss=false, const SkyObject *o=nullptr)
Downloads the corresponding DSS or SDSS image from the web and displays it.
void slotLocation()
Opens the Location dialog to set the GeoLocation for the sessionlist.
void slotRemoveSelectedObjects()
Remove skyobjects which are highlighted in the observing list tool from the observing list.
bool contains(const SkyObject *o, bool session=false)
void slotAddToEkosScheduler()
slotAddToEkosScheduler Add object to Ekos scheduler
void slotClearList()
slotClearList Remove all objects from current list
void slotDeleteAllImages()
Removes all the save DSS/SDSS images from the disk.
void slotBatchAdd()
Batch add from a list of objects.
SkyObject * currentObject() const
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 slotWUT()
Open the WUT dialog.
void slotEyepieceView()
Show the eyepiece field view.
void slotCustomDSS()
Present the user with options to get the right DSS image for the job.
void selectObject(const SkyObject *o)
make a selection in the session view
void saveCurrentUserLog()
Save the user log text to a file.
SkyObject * findObjectByName(QString name)
return the object with the name as the passed QString from the Session List, return null otherwise
void slotCenterObject()
center the selected object in the display
void slotDeleteCurrentImage()
Remove the current image.
void slotSlewToObject()
slew the telescope to the selected object
double findAltitude(SkyPoint *p, double hour=0)
Return the altitude of the given SkyObject for the given hour.
void slotSetTime()
Takes the time from the QTimeEdit box and sets it as the time parameter in the tableview of the Sessi...
void slotDetails()
Show the details window for the selected object.
void slotNewSelection()
Tasks needed when changing the selected object Save the user log of the previous selected object,...
void plot(SkyObject *o)
Plot the SkyObject's Altitude vs Time in the AVTPlotWidget.
void slotChangeTab(int index)
toggle the setEnabled flags according to current view set the m_currentItem to nullptr and clear sele...
bool eventFilter(QObject *obj, QEvent *event) override
This is the declaration of the event filter function which is installed on the KImageFilePreview and ...
void slotSaveList()
save the current observing list to disk.
void slotSaveSession(bool nativeSave=true)
save the current session
void slotRemoveObject(const SkyObject *o=nullptr, bool session=false, bool update=false)
Remove skyobject from the observing list.
Sort best observation times by reimplementing lessThan() to work on the transit times of objects.
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
SkyPoint recomputeHorizontalCoords(const KStarsDateTime &dt, const GeoLocation *geo) const
Like recomputeCoords, but also calls EquatorialToHorizontal before returning.
QString translatedName() const
Definition skyobject.h:148
virtual QString name(void) const
Definition skyobject.h:145
QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const
The same iteration technique described in riseSetTime() is used here.
The sky coordinates of a point in the sky.
Definition skypoint.h:45
const CachingDms & ra0() const
Definition skypoint.h:251
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
const dms & az() const
Definition skypoint.h:275
const dms & alt() const
Definition skypoint.h:281
const CachingDms & dec0() const
Definition skypoint.h:257
This is a subclass of SkyObject.
Definition starobject.h:33
int getHDIndex() const
Definition starobject.h:248
Dialog for modifying an object's thumbnail image.
What's up tonight dialog is a window which lists all sky objects that will be visible during the next...
Definition wutdialog.h:40
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
double Hours() const
Definition dms.h:168
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT CopyJob * move(const QList< QUrl > &src, const QUrl &dest, JobFlags flags=DefaultFlags)
GeoCoordinates geo(const QVariant &location)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
KGuiItem save()
KGuiItem discard()
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QDate date() const const
void accepted()
virtual int exec()
QString filePath(const QString &fileName) const const
QString fileName() const const
QString filePath() const const
bool hasNext() const const
QString next()
MouseButtonRelease
bool exists() const const
bool remove()
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
void clear()
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
bool remove(const Key &key)
T value(const Key &key) const const
QIcon fromTheme(const QString &name)
int getInt(QWidget *parent, const QString &title, const QString &label, int value, int min, int max, int step, bool *ok, Qt::WindowFlags flags)
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)
QString getMultiLineText(QWidget *parent, const QString &title, const QString &label, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
void append(QList< T > &&value)
void clear()
void removeAt(qsizetype i)
int column() const const
QVariant data(int role) const const
int row() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QPixmap scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QPixmap scaledToHeight(int height, Qt::TransformationMode mode) const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
virtual QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const const override
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const override
virtual QVariant data(int role) const const
QString arg(Args &&... args) const const
void clear()
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
KeepAspectRatio
ArrowCursor
StrongFocus
UserRole
Key_Delete
RightButton
FastTransformation
WA_LayoutUsesWidgetRect
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void start()
QUrl fromLocalFile(const QString &localFile)
QString toString() const const
T value() const const
void setCursor(const QCursor &)
void setEnabled(bool)
virtual bool event(QEvent *event) override
void hide()
void repaint()
void show()
void update()
Structure to hold some DSS image metadata.
Definition ksdssimage.h:36
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:04 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.