7#include "AbstractDataPluginModel.h"
10#include <QAbstractListModel>
11#include <QMetaProperty>
16#include <QtAlgorithms>
19#include "AbstractDataPluginItem.h"
20#include "CacheStoragePolicy.h"
21#include "GeoDataCoordinates.h"
22#include "GeoDataLatLonAltBox.h"
23#include "HttpDownloadManager.h"
24#include "MarbleDebug.h"
25#include "MarbleDirs.h"
34const QString descriptionPrefix(QStringLiteral(
"description_"));
37const int timeBetweenTriedDownloads = 500;
39const int timeBetweenDownloads = 1500;
43const qreal boxComparisonFactor = 16.0;
50class AbstractDataPluginModelPrivate
53 AbstractDataPluginModelPrivate(
const QString &name,
const MarbleModel *marbleModel, AbstractDataPluginModel *parent);
55 ~AbstractDataPluginModelPrivate();
60 void updateFavoriteItems();
62 AbstractDataPluginModel *m_parent =
nullptr;
64 const MarbleModel *
const m_marbleModel;
65 GeoDataLatLonAltBox m_lastBox;
66 GeoDataLatLonAltBox m_downloadedBox;
68 qint32 m_downloadedNumber;
74 quint32 m_descriptionFileNumber;
77 bool m_favoriteItemsOnly;
79 CacheStoragePolicy m_storagePolicy;
80 HttpDownloadManager m_downloadManager;
81 FavoritesModel *m_favoritesModel;
90 AbstractDataPluginModelPrivate *
const d;
92 explicit FavoritesModel(AbstractDataPluginModelPrivate *d,
QObject *
parent =
nullptr);
106AbstractDataPluginModelPrivate::AbstractDataPluginModelPrivate(
const QString &name,
const MarbleModel *marbleModel, AbstractDataPluginModel *parent)
109 , m_marbleModel(marbleModel)
113 , m_downloadedNumber(0)
114 , m_currentPlanetId(marbleModel->planetId())
115 , m_downloadTimer(m_parent)
116 , m_descriptionFileNumber(0)
118 , m_favoriteItemsOnly(false)
120 , m_downloadManager(&m_storagePolicy)
121 , m_favoritesModel(nullptr)
122 , m_hasMetaObject(false)
123 , m_needsSorting(false)
127AbstractDataPluginModelPrivate::~AbstractDataPluginModelPrivate()
131 for (; lIt != lItEnd; ++lIt) {
132 (*lIt)->deleteLater();
137 for (; hIt != hItEnd; ++hIt) {
138 (*hIt)->deleteLater();
141 m_storagePolicy.clearCache();
144void AbstractDataPluginModelPrivate::updateFavoriteItems()
146 if (m_favoriteItemsOnly) {
147 for (
const QString &
id : std::as_const(m_favoriteItems)) {
148 if (!m_parent->findItem(
id)) {
149 m_parent->getItem(
id);
155void AbstractDataPluginModel::themeChanged()
157 if (d->m_currentPlanetId != d->m_marbleModel->planetId()) {
159 d->m_currentPlanetId = d->m_marbleModel->planetId();
163static bool lessThanByPointer(
const AbstractDataPluginItem *item1,
const AbstractDataPluginItem *item2)
165 if (item1 && item2) {
167 bool const sticky1 = item1->isSticky();
168 bool const favorite1 = item1->isFavorite();
169 if (sticky1 != item2->isSticky()) {
171 }
else if (favorite1 != item2->isFavorite()) {
174 return item1->operator<(item2);
181FavoritesModel::FavoritesModel(AbstractDataPluginModelPrivate *_d,
QObject *parent)
186 int const size = d->m_hasMetaObject ? d->m_metaObject.propertyCount() : 0;
187 for (
int i = 0; i < size; ++i) {
196int FavoritesModel::rowCount(
const QModelIndex &parent)
const
203 for (AbstractDataPluginItem *item : std::as_const(d->m_itemSet)) {
204 if (item->initialized() && item->isFavorite()) {
214 int const row = index.
row();
215 if (row >= 0 && row < rowCount()) {
217 for (AbstractDataPluginItem *item : std::as_const(d->m_itemSet)) {
218 if (item->initialized() && item->isFavorite()) {
231void FavoritesModel::reset()
242AbstractDataPluginModel::AbstractDataPluginModel(
const QString &name,
const MarbleModel *marbleModel,
QObject *parent)
244 , d(new AbstractDataPluginModelPrivate(
name, marbleModel, this))
246 Q_ASSERT(marbleModel !=
nullptr);
251 connect(marbleModel, SIGNAL(themeChanged(
QString)),
this, SLOT(themeChanged()));
255 d->m_downloadTimer.start(timeBetweenDownloads);
258AbstractDataPluginModel::~AbstractDataPluginModel()
263const MarbleModel *AbstractDataPluginModel::marbleModel()
const
265 return d->m_marbleModel;
273 Q_ASSERT(!d->m_displayedItems.contains(
nullptr) &&
"Null item in m_displayedItems. Please report a bug to marble-devel@kde.org");
274 Q_ASSERT(!d->m_itemSet.contains(
nullptr) &&
"Null item in m_itemSet. Please report a bug to marble-devel@kde.org");
278 if (d->m_needsSorting) {
280 std::sort(candidates.
begin(), candidates.
end(), lessThanByPointer);
281 std::sort(d->m_itemSet.begin(), d->m_itemSet.end(), lessThanByPointer);
282 d->m_needsSorting =
false;
289 for (; i != end && list.
size() < number; ++i) {
291 if (!(*i)->initialized()) {
296 if (d->m_favoriteItemsOnly && !(*i)->isFavorite()) {
300 (*i)->setProjection(viewport);
301 if ((*i)->positions().isEmpty()) {
311 bool const alreadyDisplayed = d->m_displayedItems.contains(*i);
312 if (!alreadyDisplayed || (*i)->addedAngularResolution() >= viewport->angularResolution() || (*i)->isSticky()) {
313 bool collides =
false;
314 int const length = list.
length();
315 for (
int j = 0; !collides && j < length; ++j) {
316 for (
const QRectF &rect : list[j]->boundingRects()) {
317 for (
const QRectF &itemRect : (*i)->boundingRects()) {
318 if (rect.intersects(itemRect))
326 (*i)->setSettings(d->m_itemSettings);
329 if (!alreadyDisplayed) {
330 (*i)->setAddedAngularResolution(viewport->angularResolution());
337 d->m_lastBox = currentBox;
338 d->m_lastNumber = number;
339 d->m_displayedItems = list;
348 for (AbstractDataPluginItem *item : std::as_const(d->m_displayedItems)) {
349 if (item && item->contains(curposF)) {
357void AbstractDataPluginModel::parseFile(
const QByteArray &file)
362void AbstractDataPluginModel::downloadItem(
const QUrl &url,
const QString &type, AbstractDataPluginItem *item)
368 QString id = d->generateFilename(item->id(), type);
371 d->m_downloadingItems.insert(
id, item);
374void AbstractDataPluginModel::downloadDescriptionFile(
const QUrl &url)
377 QString name(descriptionPrefix);
381 d->m_descriptionFileNumber++;
385void AbstractDataPluginModel::addItemToList(AbstractDataPluginItem *item)
392 bool needsUpdate =
false;
393 bool favoriteChanged =
false;
394 for (AbstractDataPluginItem *item : items) {
400 if (d->m_itemSet.contains(item)) {
404 if (itemExists(item->id())) {
409 mDebug() <<
"New item " << item->id();
414 d->m_itemSet.insert(i, item);
416 connect(item, &AbstractDataPluginItem::stickyChanged,
this, &AbstractDataPluginModel::scheduleItemSort);
418 connect(item, &AbstractDataPluginItem::updated,
this, &AbstractDataPluginModel::itemsUpdated);
419 connect(item, &AbstractDataPluginItem::favoriteChanged,
this, &AbstractDataPluginModel::favoriteItemChanged);
421 if (!needsUpdate && item->initialized()) {
425 if (!favoriteChanged && item->initialized() && item->isFavorite()) {
426 favoriteChanged =
true;
430 if (favoriteChanged && d->m_favoritesModel) {
431 d->m_favoritesModel->reset();
435 Q_EMIT itemsUpdated();
439void AbstractDataPluginModel::getItem(
const QString &)
441 qWarning() <<
"Retrieving items by identifier is not implemented by this plugin";
444void AbstractDataPluginModel::setFavoriteItems(
const QStringList &list)
446 if (d->m_favoriteItems != list) {
447 d->m_favoriteItems = list;
448 d->updateFavoriteItems();
449 if (d->m_favoritesModel) {
450 d->m_favoritesModel->reset();
452 Q_EMIT favoriteItemsChanged(d->m_favoriteItems);
456QStringList AbstractDataPluginModel::favoriteItems()
const
458 return d->m_favoriteItems;
461void AbstractDataPluginModel::setFavoriteItemsOnly(
bool favoriteOnly)
463 if (isFavoriteItemsOnly() != favoriteOnly) {
464 d->m_favoriteItemsOnly = favoriteOnly;
465 d->updateFavoriteItems();
466 Q_EMIT favoriteItemsOnlyChanged();
470bool AbstractDataPluginModel::isFavoriteItemsOnly()
const
472 return d->m_favoriteItemsOnly;
475QObject *AbstractDataPluginModel::favoritesModel()
477 if (!d->m_favoritesModel) {
478 d->m_favoritesModel =
new FavoritesModel(d,
this);
479 d->updateFavoriteItems();
482 return d->m_favoritesModel;
485void AbstractDataPluginModel::favoriteItemChanged(
const QString &
id,
bool isFavorite)
496 setFavoriteItems(favorites);
500void AbstractDataPluginModel::scheduleItemSort()
502 d->m_needsSorting =
true;
509 name += fileIdSeparator;
515QString AbstractDataPluginModelPrivate::generateFilepath(
const QString &
id,
const QString &type)
const
520AbstractDataPluginItem *AbstractDataPluginModel::findItem(
const QString &
id)
const
522 for (AbstractDataPluginItem *item : std::as_const(d->m_itemSet)) {
523 if (item->id() ==
id) {
531bool AbstractDataPluginModel::itemExists(
const QString &
id)
const
538 d->m_itemSettings = itemSettings;
541void AbstractDataPluginModel::handleChangedViewport()
543 if (d->m_favoriteItemsOnly) {
548 if (d->m_lastNumber != 0
550 && (!(d->m_downloadedBox == d->m_lastBox) || d->m_downloadedNumber != d->m_lastNumber)
552 && (fabs(d->m_downloadedBox.east() - d->m_lastBox.east()) * boxComparisonFactor > d->m_lastBox.width()
553 || fabs(d->m_downloadedBox.south() - d->m_lastBox.south()) * boxComparisonFactor > d->m_lastBox.height()
554 || fabs(d->m_downloadedBox.north() - d->m_lastBox.north()) * boxComparisonFactor > d->m_lastBox.height()
555 || fabs(d->m_downloadedBox.west() - d->m_lastBox.west()) * boxComparisonFactor > d->m_lastBox.width())) {
558 d->m_downloadTimer.setInterval(timeBetweenDownloads);
561 d->m_downloadedBox = d->m_lastBox;
562 d->m_downloadedNumber = d->m_lastNumber;
565 getAdditionalItems(d->m_lastBox, d->m_lastNumber);
569 d->m_downloadTimer.setInterval(timeBetweenTriedDownloads);
573void AbstractDataPluginModel::processFinishedJob(
const QString &relativeUrlString,
const QString &
id)
575 Q_UNUSED(relativeUrlString);
577 if (
id.startsWith(descriptionPrefix)) {
578 parseFile(d->m_storagePolicy.data(
id));
583 QStringList fileInformation =
id.split(fileIdSeparator);
585 if (fileInformation.
size() < 2) {
586 mDebug() <<
"Strange file information " << id;
595 if (i != d->m_downloadingItems.end()) {
596 if (itemId != (*i)->id()) {
600 (*i)->addDownloadedFile(d->generateFilepath(itemId, fileType), fileType);
602 d->m_downloadingItems.erase(i);
607void AbstractDataPluginModel::removeItem(
QObject *item)
609 auto pluginItem = qobject_cast<AbstractDataPluginItem *>(item);
610 d->m_itemSet.removeAll(pluginItem);
612 for (i = d->m_downloadingItems.begin(); i != d->m_downloadingItems.end(); ++i) {
613 if (*i == pluginItem) {
614 i = d->m_downloadingItems.erase(i);
619void AbstractDataPluginModel::clear()
621 d->m_displayedItems.clear();
624 for (; iter != end; ++iter) {
625 (*iter)->deleteLater();
627 d->m_itemSet.clear();
630 d->m_downloadedNumber = 0;
631 Q_EMIT itemsUpdated();
634void AbstractDataPluginModel::registerItemProperties(
const QMetaObject &item)
636 d->m_metaObject = item;
637 d->m_hasMetaObject =
true;
642#include "moc_AbstractDataPluginModel.cpp"
This file contains the headers for MarbleModel.
This file contains the headers for ViewportParams.
A class that defines a 3D bounding box for geographic data.
A public class that controls what is visible in the viewport of a Marble map.
QString name(GameStandardAction id)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
Binds a QML item to a specific geodetic location in screen coordinates.
@ DownloadBrowse
Browsing mode, normal operation of Marble, like a web browser.
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
const char * constData() const const
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype length() const const
void removeAt(qsizetype i)
bool removeOne(const AT &t)
qsizetype size() const const
bool isValid() const const
void destroyed(QObject *obj)
QObject * parent() const const
QString fromUtf8(QByteArrayView str)
QString number(double n, char format, int precision)
QByteArray toLatin1() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isEmpty() const const