38 changes: 29 additions & 9 deletions kstars/dialogs/finddialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ FindDialog *FindDialog::Instance()
}

FindDialog::FindDialog(QWidget *parent)
: QDialog(parent), timer(nullptr),
m_targetObject(nullptr), m_manager{ CatalogsDB::dso_db_path() }

: QDialog(parent)
, timer(nullptr)
, m_targetObject(nullptr)
, m_asyncDBManager(new CatalogsDB::AsyncDBManager(CatalogsDB::dso_db_path()))
, m_dbManager(CatalogsDB::dso_db_path())
{
#ifdef Q_OS_OSX
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
Expand Down Expand Up @@ -148,7 +150,7 @@ FindDialog::FindDialog(QWidget *parent)

void FindDialog::init()
{
const auto &objs = m_manager.get_objects(Options::magLimitDrawDeepSky(), 100);
const auto &objs = m_dbManager.get_objects(Options::magLimitDrawDeepSky(), 100);
for (const auto &obj : objs)
{
KStarsData::Instance()->skyComposite()->catalogsComponent()->insertStaticObject(
Expand Down Expand Up @@ -293,9 +295,24 @@ void FindDialog::filterByType()
void FindDialog::filterList()
{
QString SearchText = processSearchText();
bool exactMatchExists = !(m_manager.find_objects_by_name(SearchText, 1, true).empty());
const auto &objs = m_manager.find_objects_by_name(SearchText, 10);
for (const auto &obj : objs)
const std::size_t searchId = m_currentSearchSequence;

QEventLoop loop;
QMutexLocker {&dbCallMutex}; // To prevent re-entrant calls into this
connect(m_asyncDBManager.get(), &CatalogsDB::AsyncDBManager::resultReady, &loop, &QEventLoop::quit);
QMetaObject::invokeMethod(m_asyncDBManager.get(), [&](){
m_asyncDBManager->find_objects_by_name(SearchText, 10); });
loop.exec();
std::unique_ptr<CatalogsDB::CatalogObjectList> objs = m_asyncDBManager->result();
if (m_currentSearchSequence != searchId) {
return; // Ignore this search since the search text has changed
}

Q_ASSERT(bool(objs));

bool exactMatchExists = objs->size() > 0 ? QString::compare(objs->front().name(), SearchText, Qt::CaseInsensitive) : false;

for (const auto &obj : *objs)
{
KStarsData::Instance()->skyComposite()->catalogsComponent()->insertStaticObject(
obj);
Expand Down Expand Up @@ -371,7 +388,10 @@ void FindDialog::enqueueSearch()
{
timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, SIGNAL(timeout()), this, SLOT(filterList()));
connect(timer, &QTimer::timeout, [&]() {
this->m_currentSearchSequence++;
this->filterList();
});
}
timer->start(500);
}
Expand Down Expand Up @@ -454,7 +474,7 @@ void FindDialog::finishProcessing(SkyObject *selObj, bool resolve)
{
if (!selObj && resolve)
{
selObj = resolveAndAdd(m_manager, processSearchText());
selObj = resolveAndAdd(m_dbManager, processSearchText());
}
m_targetObject = selObj;
if (selObj == nullptr)
Expand Down
6 changes: 4 additions & 2 deletions kstars/dialogs/finddialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ class FindDialog : public QDialog
QSortFilterProxyModel *sortModel { nullptr };
QTimer *timer { nullptr };
bool listFiltered { false };
std::size_t m_currentSearchSequence { 0 };
QPushButton *okB { nullptr };
SkyObject *m_targetObject { nullptr };

Expand All @@ -157,6 +158,7 @@ class FindDialog : public QDialog
QList<SkyObject *> m_HistoryList;

// DSO Database
CatalogsDB::DBManager m_manager;
std::unique_ptr<CatalogsDB::AsyncDBManager> m_asyncDBManager; // runs in another thread
CatalogsDB::DBManager m_dbManager; // runs in this thread
QMutex dbCallMutex;
};

5 changes: 5 additions & 0 deletions kstars/kstars.kcfg
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,11 @@
<whatsthis>If true, long names (common names) for deep-sky objects are shown in the labels.</whatsthis>
<default>false</default>
</entry>
<entry name="LabelFontScaling" type="Int">
<label>Label font size</label>
<whatsthis>Set this to adjust the font-size of labels placed on the sky map</whatsthis>
<default>0</default>
</entry>
<entry name="MaxRadCometName" type="Double">
<label>Maximum distance from Sun for labeling comets, in AU</label>
<whatsthis>The maximum solar distance for drawing comets.</whatsthis>
Expand Down
2 changes: 1 addition & 1 deletion kstars/kstarsdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ SkyObject *KStarsData::objectNamed(const QString &name)
{
if ((name == "star") || (name == "nothing") || name.isEmpty())
return nullptr;
return skyComposite()->findByName(name);
return skyComposite()->findByName(name, true); // objectNamed has to do an exact match
}

bool KStarsData::readCityData()
Expand Down
183 changes: 103 additions & 80 deletions kstars/options/opsadvanced.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>634</width>
<height>373</height>
<width>642</width>
<height>395</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
Expand Down Expand Up @@ -425,6 +425,41 @@
<property name="spacing">
<number>3</number>
</property>
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="toolTip">
<string>Adjust speed of zooming when scrolling in and out with the mouse wheel</string>
</property>
<property name="text">
<string>Zoom scroll speed:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSlider" name="zoomScrollSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QCheckBox" name="kcfg_UseAutoLabel">
<property name="toolTip">
Expand All @@ -438,23 +473,45 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="toolTip">
<string>Adjust speed of zooming when scrolling in and out with the mouse wheel</string>
<item row="5" column="3">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="text">
<string>Zoom scroll speed:</string>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</widget>
</spacer>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="kcfg_UseAntialias">
<property name="toolTip">
<string>Select this for smoother (but slower) graphics</string>
<item row="3" column="3">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="text">
<string>Use antialiased drawing</string>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="2">
<widget class="QDoubleSpinBox" name="kcfg_ZoomScrollFactor">
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.200000000000000</double>
</property>
</widget>
</item>
Expand All @@ -471,6 +528,19 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="kcfg_ShowInlineImages">
<property name="toolTip">
<string>Show inline images on the sky?</string>
</property>
<property name="whatsThis">
<string>If checked, inline images will be shown on the skymap.</string>
</property>
<property name="text">
<string>Show inline images</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QComboBox" name="kcfg_DefaultCursor">
<item>
Expand All @@ -490,6 +560,16 @@
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="kcfg_UseAntialias">
<property name="toolTip">
<string>Select this for smoother (but slower) graphics</string>
</property>
<property name="text">
<string>Use antialiased drawing</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="kcfg_LeftClickSelectsObject">
<property name="text">
Expand Down Expand Up @@ -517,86 +597,29 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="kcfg_ShowInlineImages">
<property name="toolTip">
<string>Show inline images on the sky?</string>
</property>
<property name="whatsThis">
<string>If checked, inline images will be shown on the skymap.</string>
</property>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Show inline images</string>
<string>Font size of sky map labels:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSlider" name="zoomScrollSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="6" column="1">
<widget class="QSlider" name="kcfg_LabelFontScaling">
<property name="minimum">
<number>1</number>
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
<number>0</number>
</property>
</widget>
</item>
<item row="3" column="3">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="2">
<widget class="QDoubleSpinBox" name="kcfg_ZoomScrollFactor">
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item row="5" column="3">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
Expand Down Expand Up @@ -1028,7 +1051,7 @@
<resources/>
<connections/>
<buttongroups>
<buttongroup name="logbuttonGroup"/>
<buttongroup name="verbosityButtonGroup"/>
<buttongroup name="logbuttonGroup"/>
</buttongroups>
</ui>
217 changes: 168 additions & 49 deletions kstars/skycomponents/catalogscomponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/

#include <cmath>
#include "catalogscomponent.h"
#include "skypainter.h"
#include "skymap.h"
Expand All @@ -20,11 +19,20 @@
#include "kspaths.h"
#include "import_skycomp.h"

#include <QtConcurrent>

#include <cmath>

constexpr std::size_t expectedKnownMagObjectsPerTrixel = 500;
constexpr std::size_t expectedUnknownMagObjectsPerTrixel = 1500;

CatalogsComponent::CatalogsComponent(SkyComposite *parent, const QString &db_filename,
bool load_default)
: SkyComponent(parent), m_db_manager(db_filename), m_skyMesh{ SkyMesh::Create(
m_db_manager.htmesh_level()) },
m_cache(m_skyMesh->size(), calculateCacheSize(Options::dSOCachePercentage()))
: SkyComponent(parent)
, m_db_manager(db_filename)
, m_skyMesh{ SkyMesh::Create(m_db_manager.htmesh_level()) }
, m_mainCache(m_skyMesh->size(), calculateCacheSize(Options::dSOCachePercentage()))
, m_unknownMagCache(m_skyMesh->size(), calculateCacheSize(Options::dSOCachePercentage()))
{
if (load_default)
{
Expand Down Expand Up @@ -85,27 +93,59 @@ void CatalogsComponent::draw(SkyPainter *skyp)

updateSkyMesh(map);

MeshIterator region(m_skyMesh, DRAW_BUF);

size_t num_trixels{ 0 };

while (region.hasNext())
{
Trixel trixel = region.next();
num_trixels++;

auto &objects = m_cache[trixel];
if (!objects.is_set())
const auto zoomFactor = Options::zoomFactor();
const double sizeScale = dms::PI * zoomFactor / 10800.0; // FIXME: magic number 10800

// Note: This function handles objects of known and unknown
// magnitudes differently. This is mostly because objects in the
// PGC catalog do not have magnitude information, which leads to
// hundreds of thousands of objects of unknown magnitude. This
// means we must find some way to:
// (a) Prevent PGC objects from flooding the screen at low zoom
// levels
// (b) Prevent PGC labels from crowding out more common NGC/M
// labels
// (c) Process the large number of PGC objects efficiently at low
// zoom levels (even when they are not actually displayed)

// The problem is that normally, we have relied on magnitude as a
// proxy to prioritize object labels, as well as to short-circuit
// filtering decisions by having our objects sorted by
// magnitude. We can no longer do that.

// Therefore, we first handle objects of known magnitude, given
// them priority in SkyLabeler label-space, solving (b). Then, we
// place aggressive filters on objects of unknown magnitude and
// size, by explicitly special-casing galaxies: generally
// speaking, large nebulae and small galaxies don't have known
// magnitudes; so we allow objects of unknown size and unknown
// magnitudes to be displayed at lower zoom levels provided they
// are not galaxies. With these series of tricks, lest we say
// hacks, we solve (a). Finally, we make the unknown mag loop
// concurrent since we are unable to bail-out when its time like
// we can with the objects of known magnitude, addressing
// (c). Thus, the user experience with the PGC flood of 1 million
// galaxies of unknown magnitude, and many of them also of unknown
// size, remains smooth.

// Helper lambda to fill the appropriate cache for a given trixel
auto fillCache = [&](
TrixelCache<ObjectList>::element& cacheElement,
ObjectList (CatalogsDB::DBManager::*fillFunction)(const int),
Trixel trixel
) -> void {
if (!cacheElement.is_set())
{
try
{
objects = m_db_manager.get_objects_in_trixel(trixel);
cacheElement = (m_db_manager.*fillFunction)(trixel);
}
catch (const CatalogsDB::DatabaseError &e)
{
qCCritical(KSTARS)
<< "Could not load catalog objects in trixel: " << trixel << ", "
<< e.what();
<< "Could not load catalog objects in trixel: " << trixel << ", "
<< e.what();

KMessageBox::detailedError(
nullptr, i18n("Could not load catalog objects in trixel: %1", trixel),
Expand All @@ -114,55 +154,134 @@ void CatalogsComponent::draw(SkyPainter *skyp)
throw; // do not silently fail
}
}
};

// Helper lambda to JIT update and draw
auto drawObjects = [&](std::vector<CatalogObject*>& objects) {
// TODO: If we are sure that JITupdate has no side effects
// that may cause races etc, it will be worth parallelizing

for (CatalogObject *object : objects) {
object->JITupdate();
auto &color = m_catalog_colors[object->catalogId()][color_scheme];
if (!color.isValid())
{
color = m_catalog_colors[object->catalogId()]["default"];

if (!color.isValid())
{
color = default_color;
}
}

skyp->setPen(color);

if (Options::showInlineImages())
object->load_image();

if (skyp->drawCatalogObject(*object) && !hideLabels)
{
labeler.drawNameLabel(object, proj.toScreen(object), label_padding);
}
}
};

std::vector<CatalogObject*> drawListKnownMag;
drawListKnownMag.reserve(expectedKnownMagObjectsPerTrixel);

for (auto &object : objects.data())
// Handle the objects of known magnitude
MeshIterator region(m_skyMesh, DRAW_BUF);
while (region.hasNext())
{
Trixel trixel = region.next();
num_trixels++;

// Fill the cache for this trixel
auto &objectsKnownMag = m_mainCache[trixel];
fillCache(objectsKnownMag, &CatalogsDB::DBManager::get_objects_in_trixel_no_nulls, trixel);
drawListKnownMag.clear();

// Filter based on magnitude and size
for (const auto &object : objectsKnownMag.data())
{
auto mag = object.mag();
bool mag_unknown = std::isnan(mag) || (mag > FAINTEST_MAGNITUDE);
bool magCriterion = (mag_unknown && showUnknownMagObjects) || (mag < maglim);
const auto mag = object.mag();
const auto a = object.a(); // major axis
const double size = a * sizeScale;
const bool magCriterion = (mag < maglim);

if (!magCriterion && !mag_unknown)
break; // the objects are strictly sorted by magnitude
// unknown magnitude first
if (!magCriterion)
continue;

// FIXME: Remove magic number 10800
double size = object.a() * dms::PI * Options::zoomFactor() / 10800.0;
{
break; // the known-mag objects are strictly sorted by
// magnitude, unknown magnitude first
}

bool sizeCriterion =
(size > 1.0 || size == 0 || Options::zoomFactor() > 2000.);
(size > 1.0 || size == 0 || zoomFactor > 2000.);

if (sizeCriterion)
{
object.JITupdate();
auto &color = m_catalog_colors[object.catalogId()][color_scheme];
if (!color.isValid())
{
color = m_catalog_colors[object.catalogId()]["default"];
if (!sizeCriterion)
break;

if (!color.isValid())
{
color = default_color;
}
}
drawListKnownMag.push_back(const_cast<CatalogObject*>(&object));
}

skyp->setPen(color);
// JIT update and draw
drawObjects(drawListKnownMag);
}

// Handle the objects of unknown magnitude
if (showUnknownMagObjects)
{
std::vector<CatalogObject*> drawListUnknownMag;
drawListUnknownMag.reserve(expectedUnknownMagObjectsPerTrixel);
QMutex drawListUnknownMagLock;

if (Options::showInlineImages())
object.load_image();
MeshIterator region(m_skyMesh, DRAW_BUF);
while (region.hasNext())
{
Trixel trixel = region.next();
drawListUnknownMag.clear();

if (skyp->drawCatalogObject(object) && !hideLabels)
// Fill cache
auto &objectsUnknownMag = m_unknownMagCache[trixel];
fillCache(objectsUnknownMag, &CatalogsDB::DBManager::get_objects_in_trixel_null_mag, trixel);

// Filter
QtConcurrent::blockingMap(
objectsUnknownMag.data(),
[&](const auto &object)
{
labeler.drawNameLabel(&object, proj.toScreen(&object), label_padding);
}
}
auto a = object.a(); // major axis
double size = a * sizeScale;

// For objects of unknown mag but known size, adjust
// display behavior as if it were 22 mags/arcsec² =
// 13.1 mag/arcmin² surface brightness, comparing it
// to the magnitude limit.
bool magCriterion = (a <= 0.0 || (13.1 - 5*log10(a)) < maglim);

if (!magCriterion)
return;

bool sizeCriterion =
(size > 1.0 || (size == 0 && object.type() != SkyObject::GALAXY) || zoomFactor > 10000.);

if (!sizeCriterion)
return;

QMutexLocker _{&drawListUnknownMagLock};
drawListUnknownMag.push_back(const_cast<CatalogObject*>(&object));
});

// JIT update and draw
drawObjects(drawListUnknownMag);
}

}

// prune only if the to-be-pruned trixels are likely not visible
// and we are not zooming
m_cache.prune(num_trixels * 1.2);
m_mainCache.prune(num_trixels * 1.2);
m_unknownMagCache.prune(num_trixels * 1.2);
};

void CatalogsComponent::updateSkyMesh(SkyMap &map, MeshBufNum_t buf)
Expand Down
17 changes: 12 additions & 5 deletions kstars/skycomponents/catalogscomponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ class CatalogsComponent : public SkyComponent
*/
void resizeCache(const int percentage)
{
m_cache.set_size(calculateCacheSize(percentage));
m_mainCache.set_size(calculateCacheSize(percentage));
m_unknownMagCache.set_size(calculateCacheSize(percentage));
};

/**
Expand Down Expand Up @@ -109,7 +110,8 @@ class CatalogsComponent : public SkyComponent
*/
void dropCache()
{
m_cache.clear();
m_mainCache.clear();
m_unknownMagCache.clear();
m_catalog_colors = m_db_manager.get_catalog_colors();
};

Expand Down Expand Up @@ -141,9 +143,14 @@ class CatalogsComponent : public SkyComponent
ObjectList m_objects;

/**
* The cache holding the DSOs
* The cache holding the DSOs of known magnitude
*/
TrixelCache<ObjectList> m_cache;
TrixelCache<ObjectList> m_mainCache;

/**
* The cache holding the DSOs of unknown magnitude
*/
TrixelCache<ObjectList> m_unknownMagCache;

/**
* A trixel indexed map of lists containing manually loaded
Expand Down Expand Up @@ -176,7 +183,7 @@ class CatalogsComponent : public SkyComponent
void updateSkyMesh(SkyMap &map, MeshBufNum_t buf = DRAW_BUF);
size_t calculateCacheSize(const unsigned int percentage)
{
return m_skyMesh->size() * percentage / 100;
return m_skyMesh->size() * percentage / 100.f;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions kstars/skycomponents/skylabeler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ bool SkyLabeler::drawNameLabel(SkyObject *obj, const QPointF &_p,
else
{
double factor = log(Options::zoomFactor() / 750.0);
double newPointSize = qBound(12.0, factor * m_stdFont.pointSizeF(), 18.0);
double newPointSize = qBound(12.0, factor * m_stdFont.pointSizeF(), 18.0) * (1.0 + 0.7 * Options::labelFontScaling()/100.0);
QFont zoomFont(m_p.font());
zoomFont.setPointSizeF(newPointSize);
m_p.setFont(zoomFont);
Expand Down Expand Up @@ -407,7 +407,7 @@ void SkyLabeler::draw(QPainter &p)

bool SkyLabeler::markText(const QPointF &p, const QString &text, qreal padding_factor)
{
static const auto ramp_zoom = log10(MINZOOM) + log10(MAXZOOM) * .3;
static const auto ramp_zoom = log10(MAXZOOM) + log10(0.3);

if (padding_factor != 1)
{
Expand Down
4 changes: 2 additions & 2 deletions kstars/skycomponents/skymapcomposite.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,12 @@ class SkyMapComposite : public QObject, public SkyComposite
* @note Overloaded from SkyComposite. In this version, we search
* the most likely object classes first to be more efficient.
* @p name the name to be matched
* @p exact If true, it will return an exact match, otherwise (default) it can return
* @p exact If true, it will return an exact match (default), otherwise it can return
* a partial match.
* @return a pointer to the SkyObject whose name matches
* the argument, or a nullptr pointer if no match was found.
*/
SkyObject *findByName(const QString &name, bool exact = false) override;
SkyObject *findByName(const QString &name, bool exact = true) override;

/**
* @return the list of objects in the region defined by skypoints
Expand Down
64 changes: 33 additions & 31 deletions kstars/tools/observinglist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1082,38 +1082,23 @@ void ObservingList::slotLoadWishList()
QPointer<QProgressDialog> addingObjectsProgress = new QProgressDialog();
addingObjectsProgress->setWindowTitle(i18nc("@title:window", "Observing List Wizard"));
addingObjectsProgress->setLabelText(i18n("Please wait while loading observing wishlist..."));
addingObjectsProgress->setMaximum(0);
addingObjectsProgress->setMinimum(0);
addingObjectsProgress->show();

QStringList failedObjects;

// Read the entire file in one pass so we can show better progress indication
QStringList objects;
while (!istream.atEnd())
{
line = istream.readLine();
//If the object is named "star", add it by coordinates
SkyObject *o;
/*if ( line.startsWith( QLatin1String( "star" ) ) ) {
QStringList fields = line.split( ' ', QString::SkipEmptyParts );
dms ra = dms::fromString( fields[1], false ); //false = hours
dms dc = dms::fromString( fields[2], true ); //true = degrees
SkyPoint p( ra, dc );
double maxrad = 1000.0/Options::zoomFactor();
o = ks->data()->skyComposite()->starNearest( &p, maxrad );
}
else {*/
o = KStarsData::Instance()->objectNamed(line);
//}
//If we haven't identified the object, try interpreting the
//name as a star's genetive name (with ascii letters)
if (!o)
o = KStarsData::Instance()->skyComposite()->findStarByGenetiveName(line);
if (o)
slotAddObject(o, false, true);
else
failedObjects.append(line);
objects.append(istream.readLine());
}
addingObjectsProgress->setMaximum(objects.size());
addingObjectsProgress->setMinimum(0);
addingObjectsProgress->show();

if (addingObjectsProgress->wasCanceled() && !istream.atEnd())
QStringList failedObjects;
for (std::size_t idx = 0; idx < objects.size(); ++idx) {
const auto &objectName = objects[idx];

if (addingObjectsProgress->wasCanceled())
{
QMessageBox msgBox =
{
Expand All @@ -1124,18 +1109,35 @@ void ObservingList::slotLoadWishList()
this
};
msgBox.setDefaultButton(QMessageBox::No);
auto pos = istream.pos();
msgBox.setDetailedText(istream.readAll());
istream.seek(pos);
msgBox.setDetailedText(objects.mid(idx).join("\n") + "\n");
if (msgBox.exec() == QMessageBox::Yes)
break;
else
{
addingObjectsProgress->reset();
addingObjectsProgress->setValue(idx);
addingObjectsProgress->show();
}

}

SkyObject *o = KStarsData::Instance()->objectNamed(objectName);

//If we haven't identified the object, try interpreting the
//name as a star's genetive name (with ascii letters)
if (!o)
o = KStarsData::Instance()->skyComposite()->findStarByGenetiveName(line);

if (o)
{
slotAddObject(o, false, true);
}
else
{
failedObjects.append(line);
}

addingObjectsProgress->setValue(idx + 1);
qApp->processEvents();
}
delete (addingObjectsProgress);
Expand All @@ -1149,7 +1151,7 @@ void ObservingList::slotLoadWishList()
QMessageBox::Ok,
this
};
msgBox.setDetailedText(failedObjects.join("\n"));
msgBox.setDetailedText(failedObjects.join("\n") + "\n");
msgBox.exec();
}
}
Expand Down