7#include "AbstractDataPluginModel.h"
14#include <QtAlgorithms>
16#include <QAbstractListModel>
17#include <QMetaProperty>
20#include "MarbleDebug.h"
21#include "AbstractDataPluginItem.h"
22#include "CacheStoragePolicy.h"
23#include "GeoDataCoordinates.h"
24#include "GeoDataLatLonAltBox.h"
25#include "HttpDownloadManager.h"
27#include "MarbleDirs.h"
35const QString descriptionPrefix(
"description_" );
38const int timeBetweenTriedDownloads = 500;
40const int timeBetweenDownloads = 1500;
44const qreal boxComparisonFactor = 16.0;
51class AbstractDataPluginModelPrivate
54 AbstractDataPluginModelPrivate(
const QString& name,
55 const MarbleModel *marbleModel,
56 AbstractDataPluginModel * parent );
58 ~AbstractDataPluginModelPrivate();
63 void updateFavoriteItems();
65 AbstractDataPluginModel *m_parent;
67 const MarbleModel *
const m_marbleModel;
68 GeoDataLatLonAltBox m_lastBox;
69 GeoDataLatLonAltBox m_downloadedBox;
71 qint32 m_downloadedNumber;
77 quint32 m_descriptionFileNumber;
80 bool m_favoriteItemsOnly;
82 CacheStoragePolicy m_storagePolicy;
83 HttpDownloadManager m_downloadManager;
84 FavoritesModel* m_favoritesModel;
93 AbstractDataPluginModelPrivate* d;
95 explicit FavoritesModel( AbstractDataPluginModelPrivate* d,
QObject*
parent =
nullptr );
109AbstractDataPluginModelPrivate::AbstractDataPluginModelPrivate(
const QString& name,
110 const MarbleModel *marbleModel,
111 AbstractDataPluginModel * parent )
112 : m_parent( parent ),
114 m_marbleModel( marbleModel ),
118 m_downloadedNumber( 0 ),
119 m_currentPlanetId( marbleModel->planetId() ),
120 m_downloadTimer( m_parent ),
121 m_descriptionFileNumber( 0 ),
123 m_favoriteItemsOnly( false ),
125 m_downloadManager( &m_storagePolicy ),
126 m_favoritesModel( nullptr ),
127 m_hasMetaObject( false ),
128 m_needsSorting( false )
132AbstractDataPluginModelPrivate::~AbstractDataPluginModelPrivate() {
135 for (; lIt != lItEnd; ++lIt ) {
136 (*lIt)->deleteLater();
141 for (; hIt != hItEnd; ++hIt ) {
142 (*hIt)->deleteLater();
145 m_storagePolicy.clearCache();
148void AbstractDataPluginModelPrivate::updateFavoriteItems()
150 if ( m_favoriteItemsOnly ) {
151 for(
const QString &
id: m_favoriteItems ) {
152 if ( !m_parent->findItem(
id ) ) {
153 m_parent->getItem(
id );
159void AbstractDataPluginModel::themeChanged()
161 if ( d->m_currentPlanetId != d->m_marbleModel->planetId() ) {
163 d->m_currentPlanetId = d->m_marbleModel->planetId();
167static bool lessThanByPointer(
const AbstractDataPluginItem *item1,
168 const AbstractDataPluginItem *item2 )
170 if( item1 && item2 ) {
172 bool const sticky1 = item1->isSticky();
173 bool const favorite1 = item1->isFavorite();
174 if ( sticky1 != item2->isSticky() ) {
176 }
else if ( favorite1 != item2->isFavorite() ) {
179 return item1->operator<( item2 );
187FavoritesModel::FavoritesModel( AbstractDataPluginModelPrivate *_d,
QObject* parent ) :
191 int const size = d->m_hasMetaObject ? d->m_metaObject.propertyCount() : 0;
192 for (
int i=0; i<size; ++i ) {
201int FavoritesModel::rowCount (
const QModelIndex &parent )
const
208 for( AbstractDataPluginItem* item: d->m_itemSet ) {
209 if ( item->initialized() && item->isFavorite() ) {
219 int const row = index.
row();
220 if ( row >= 0 && row < rowCount() ) {
222 for( AbstractDataPluginItem* item: d->m_itemSet ) {
223 if ( item->initialized() && item->isFavorite() ) {
224 if ( count == row ) {
225 QString const roleName = roleNames().value( role );
236void FavoritesModel::reset()
247AbstractDataPluginModel::AbstractDataPluginModel(
const QString &name,
const MarbleModel *marbleModel,
QObject *parent )
249 d( new AbstractDataPluginModelPrivate(
name, marbleModel, this ) )
251 Q_ASSERT( marbleModel !=
nullptr );
258 this, SLOT(themeChanged()) );
261 connect( &d->m_downloadTimer, SIGNAL(timeout()),
262 this, SLOT(handleChangedViewport()),
264 d->m_downloadTimer.start( timeBetweenDownloads );
267AbstractDataPluginModel::~AbstractDataPluginModel()
272const MarbleModel *AbstractDataPluginModel::marbleModel()
const
274 return d->m_marbleModel;
283 Q_ASSERT( !d->m_displayedItems.contains( 0 ) &&
"Null item in m_displayedItems. Please report a bug to marble-devel@kde.org" );
284 Q_ASSERT( !d->m_itemSet.contains( 0 ) &&
"Null item in m_itemSet. Please report a bug to marble-devel@kde.org" );
288 if ( d->m_needsSorting ) {
290 std::sort( candidates.
begin(), candidates.
end(), lessThanByPointer );
291 std::sort( d->m_itemSet.begin(), d->m_itemSet.end(), lessThanByPointer );
292 d->m_needsSorting =
false;
299 for (; i != end && list.
size() < number; ++i ) {
301 if( !(*i)->initialized() ) {
306 if( d->m_favoriteItemsOnly && !(*i)->isFavorite() ) {
310 (*i)->setProjection( viewport );
311 if( (*i)->positions().isEmpty() ) {
321 bool const alreadyDisplayed = d->m_displayedItems.contains( *i );
322 if ( !alreadyDisplayed || (*i)->addedAngularResolution() >= viewport->angularResolution() || (*i)->isSticky() ) {
323 bool collides =
false;
324 int const length = list.
length();
325 for (
int j=0; !collides && j<length; ++j ) {
326 for(
const QRectF &rect: list[j]->boundingRects() ) {
327 for(
const QRectF &itemRect: (*i)->boundingRects() ) {
328 if ( rect.intersects( itemRect ) )
336 (*i)->setSettings( d->m_itemSettings );
339 if( !alreadyDisplayed ) {
340 (*i)->setAddedAngularResolution( viewport->angularResolution() );
347 d->m_lastBox = currentBox;
348 d->m_lastNumber = number;
349 d->m_displayedItems = list;
358 for( AbstractDataPluginItem* item: d->m_displayedItems ) {
359 if (item && item->contains(curposF)) {
367void AbstractDataPluginModel::parseFile(
const QByteArray& file )
372void AbstractDataPluginModel::downloadItem(
const QUrl& url,
374 AbstractDataPluginItem *item )
380 QString id = d->generateFilename( item->id(), type );
383 d->m_downloadingItems.insert(
id, item );
386void AbstractDataPluginModel::downloadDescriptionFile(
const QUrl& url )
389 QString name( descriptionPrefix );
393 d->m_descriptionFileNumber++;
397void AbstractDataPluginModel::addItemToList( AbstractDataPluginItem *item )
404 bool needsUpdate =
false;
405 bool favoriteChanged =
false;
406 for( AbstractDataPluginItem *item: items ) {
412 if ( d->m_itemSet.contains( item ) ) {
416 if( itemExists( item->id() ) ) {
421 mDebug() <<
"New item " << item->id();
429 d->m_itemSet.insert( i, item );
431 connect( item, SIGNAL(stickyChanged()),
this, SLOT(scheduleItemSort()) );
432 connect( item, SIGNAL(destroyed(
QObject*)),
this, SLOT(removeItem(
QObject*)) );
433 connect( item, SIGNAL(updated()),
this, SIGNAL(itemsUpdated()) );
434 connect( item, SIGNAL(favoriteChanged(
QString,
bool)),
this,
435 SLOT(favoriteItemChanged(
QString,
bool)) );
437 if ( !needsUpdate && item->initialized() ) {
441 if ( !favoriteChanged && item->initialized() && item->isFavorite() ) {
442 favoriteChanged =
true;
446 if ( favoriteChanged && d->m_favoritesModel ) {
447 d->m_favoritesModel->reset();
455void AbstractDataPluginModel::getItem(
const QString & )
457 qWarning() <<
"Retrieving items by identifier is not implemented by this plugin";
460void AbstractDataPluginModel::setFavoriteItems(
const QStringList& list )
462 if ( d->m_favoriteItems != list) {
463 d->m_favoriteItems = list;
464 d->updateFavoriteItems();
465 if ( d->m_favoritesModel ) {
466 d->m_favoritesModel->reset();
468 emit favoriteItemsChanged( d->m_favoriteItems );
472QStringList AbstractDataPluginModel::favoriteItems()
const
474 return d->m_favoriteItems;
477void AbstractDataPluginModel::setFavoriteItemsOnly(
bool favoriteOnly )
479 if ( isFavoriteItemsOnly() != favoriteOnly ) {
480 d->m_favoriteItemsOnly = favoriteOnly;
481 d->updateFavoriteItems();
482 emit favoriteItemsOnlyChanged();
486bool AbstractDataPluginModel::isFavoriteItemsOnly()
const
488 return d->m_favoriteItemsOnly;
491QObject *AbstractDataPluginModel::favoritesModel()
493 if ( !d->m_favoritesModel ) {
494 d->m_favoritesModel =
new FavoritesModel( d,
this );
495 d->updateFavoriteItems();
498 return d->m_favoritesModel;
501void AbstractDataPluginModel::favoriteItemChanged(
const QString&
id,
bool isFavorite )
512 setFavoriteItems( favorites );
516void AbstractDataPluginModel::scheduleItemSort()
518 d->m_needsSorting =
true;
525 name += fileIdSeparator;
531QString AbstractDataPluginModelPrivate::generateFilepath(
const QString&
id,
const QString& type )
const
536AbstractDataPluginItem *AbstractDataPluginModel::findItem(
const QString&
id )
const
538 for ( AbstractDataPluginItem *item: d->m_itemSet ) {
539 if( item->id() == id ) {
547bool AbstractDataPluginModel::itemExists(
const QString&
id )
const
549 return findItem(
id );
554 d->m_itemSettings = itemSettings;
557void AbstractDataPluginModel::handleChangedViewport()
559 if( d->m_favoriteItemsOnly ) {
564 if( d->m_lastNumber != 0
566 && ( !( d->m_downloadedBox == d->m_lastBox )
567 || d->m_downloadedNumber != d->m_lastNumber )
569 && ( fabs( d->m_downloadedBox.east() - d->m_lastBox.east() ) * boxComparisonFactor
570 > d->m_lastBox.width()
571 || fabs( d->m_downloadedBox.south() - d->m_lastBox.south() ) * boxComparisonFactor
572 > d->m_lastBox.height()
573 || fabs( d->m_downloadedBox.north() - d->m_lastBox.north() ) * boxComparisonFactor
574 > d->m_lastBox.height()
575 || fabs( d->m_downloadedBox.west() - d->m_lastBox.west() ) * boxComparisonFactor
576 > d->m_lastBox.width() ) )
580 d->m_downloadTimer.setInterval( timeBetweenDownloads );
583 d->m_downloadedBox = d->m_lastBox;
584 d->m_downloadedNumber = d->m_lastNumber;
587 getAdditionalItems( d->m_lastBox, d->m_lastNumber );
592 d->m_downloadTimer.setInterval( timeBetweenTriedDownloads );
596void AbstractDataPluginModel::processFinishedJob(
const QString& relativeUrlString,
599 Q_UNUSED( relativeUrlString );
601 if(
id.startsWith( descriptionPrefix ) ) {
602 parseFile( d->m_storagePolicy.data(
id ) );
608 QStringList fileInformation =
id.split( fileIdSeparator );
610 if( fileInformation.
size() < 2) {
611 mDebug() <<
"Strange file information " << id;
614 QString itemId = fileInformation.
at( 0 );
620 if( i != d->m_downloadingItems.end() ) {
621 if( itemId != (*i)->id() ) {
625 (*i)->addDownloadedFile( d->generateFilepath( itemId, fileType ),
628 d->m_downloadingItems.erase( i );
633void AbstractDataPluginModel::removeItem(
QObject *item )
635 AbstractDataPluginItem * pluginItem = qobject_cast<AbstractDataPluginItem*>( item );
636 d->m_itemSet.removeAll( pluginItem );
638 for( i = d->m_downloadingItems.begin(); i != d->m_downloadingItems.end(); ++i ) {
639 if( *i == pluginItem ) {
640 i = d->m_downloadingItems.erase( i );
645void AbstractDataPluginModel::clear()
647 d->m_displayedItems.clear();
650 for (; iter != end; ++iter ) {
651 (*iter)->deleteLater();
653 d->m_itemSet.clear();
656 d->m_downloadedNumber = 0;
660void AbstractDataPluginModel::registerItemProperties(
const QMetaObject &item )
662 d->m_metaObject = item;
663 d->m_hasMetaObject =
true;
668#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.
Type type(const QSqlDatabase &db)
QString name(StandardShortcut id)
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
QObject * parent() const const
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