File indexing completed on 2025-01-05 03:59:31
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2009 Bastian Holst <bastianholst@gmx.de> 0004 // 0005 0006 // Self 0007 #include "AbstractDataPluginModel.h" 0008 0009 #include <cmath> 0010 0011 // Qt 0012 #include <QTimer> 0013 #include <QPointF> 0014 #include <QRectF> 0015 #include <QtAlgorithms> 0016 #include <QVariant> 0017 #include <QAbstractListModel> 0018 #include <QMetaProperty> 0019 0020 // Marble 0021 #include "AbstractDataPluginItem.h" 0022 #include "CacheStoragePolicy.h" 0023 #include "GeoDataCoordinates.h" 0024 #include "GeoDataLatLonAltBox.h" 0025 #include "HttpDownloadManager.h" 0026 #include "MarbleModel.h" 0027 #include "MarbleDirs.h" 0028 #include "ViewportParams.h" 0029 0030 #include "digikam_debug.h" 0031 0032 namespace Marble 0033 { 0034 0035 const QString descriptionPrefix( QString::fromUtf8("description_") ); 0036 0037 // Time between two tried description file downloads (we decided not to download anything) in ms 0038 const int timeBetweenTriedDownloads = 500; 0039 // Time between two real description file downloads in ms 0040 const int timeBetweenDownloads = 1500; 0041 0042 // The factor describing how much the box has to be changed to download a new description file. 0043 // A higher factor means more downloads. 0044 const qreal boxComparisonFactor = 16.0; 0045 0046 // Separator to separate the id of the item from the file type 0047 const QChar fileIdSeparator = QLatin1Char('_'); 0048 0049 class FavoritesModel; 0050 0051 class AbstractDataPluginModelPrivate 0052 { 0053 public: 0054 AbstractDataPluginModelPrivate( const QString& name, 0055 const MarbleModel *marbleModel, 0056 AbstractDataPluginModel * parent ); 0057 0058 ~AbstractDataPluginModelPrivate(); 0059 0060 static QString generateFilename(const QString &id, const QString &type); 0061 QString generateFilepath( const QString& id, const QString& type ) const; 0062 0063 void updateFavoriteItems(); 0064 0065 AbstractDataPluginModel *m_parent; 0066 const QString m_name; 0067 const MarbleModel *const m_marbleModel; 0068 GeoDataLatLonAltBox m_lastBox; 0069 GeoDataLatLonAltBox m_downloadedBox; 0070 qint32 m_lastNumber; 0071 qint32 m_downloadedNumber; 0072 QString m_currentPlanetId; 0073 QList<AbstractDataPluginItem*> m_itemSet; 0074 QHash<QString, AbstractDataPluginItem*> m_downloadingItems; 0075 QList<AbstractDataPluginItem*> m_displayedItems; 0076 QTimer m_downloadTimer; 0077 quint32 m_descriptionFileNumber; 0078 QHash<QString, QVariant> m_itemSettings; 0079 QStringList m_favoriteItems; 0080 bool m_favoriteItemsOnly; 0081 0082 CacheStoragePolicy m_storagePolicy; 0083 HttpDownloadManager m_downloadManager; 0084 FavoritesModel* m_favoritesModel; 0085 QMetaObject m_metaObject; 0086 bool m_hasMetaObject; 0087 bool m_needsSorting; 0088 }; 0089 0090 class FavoritesModel : public QAbstractListModel 0091 { 0092 public: 0093 AbstractDataPluginModelPrivate* d; 0094 0095 explicit FavoritesModel( AbstractDataPluginModelPrivate* d, QObject* parent = nullptr ); 0096 0097 int rowCount ( const QModelIndex & parent = QModelIndex() ) const override; 0098 0099 QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const override; 0100 0101 void reset(); 0102 0103 QHash<int, QByteArray> roleNames() const override; 0104 0105 private: 0106 QHash<int, QByteArray> m_roleNames; 0107 }; 0108 0109 AbstractDataPluginModelPrivate::AbstractDataPluginModelPrivate( const QString& name, 0110 const MarbleModel *marbleModel, 0111 AbstractDataPluginModel * parent ) 0112 : m_parent( parent ), 0113 m_name( name ), 0114 m_marbleModel( marbleModel ), 0115 m_lastBox(), 0116 m_downloadedBox(), 0117 m_lastNumber( 0 ), 0118 m_downloadedNumber( 0 ), 0119 m_currentPlanetId( marbleModel->planetId() ), 0120 m_downloadTimer( m_parent ), 0121 m_descriptionFileNumber( 0 ), 0122 m_itemSettings(), 0123 m_favoriteItemsOnly( false ), 0124 m_storagePolicy(MarbleDirs::localPath() + QLatin1String("/cache/") + m_name + QLatin1Char('/')), 0125 m_downloadManager( &m_storagePolicy ), 0126 m_favoritesModel( nullptr ), 0127 m_hasMetaObject( false ), 0128 m_needsSorting( false ) 0129 { 0130 } 0131 0132 AbstractDataPluginModelPrivate::~AbstractDataPluginModelPrivate() { 0133 QList<AbstractDataPluginItem*>::iterator lIt = m_itemSet.begin(); 0134 QList<AbstractDataPluginItem*>::iterator const lItEnd = m_itemSet.end(); 0135 for (; lIt != lItEnd; ++lIt ) { 0136 (*lIt)->deleteLater(); 0137 } 0138 0139 QHash<QString,AbstractDataPluginItem*>::iterator hIt = m_downloadingItems.begin(); 0140 QHash<QString,AbstractDataPluginItem*>::iterator const hItEnd = m_downloadingItems.end(); 0141 for (; hIt != hItEnd; ++hIt ) { 0142 (*hIt)->deleteLater(); 0143 } 0144 0145 m_storagePolicy.clearCache(); 0146 } 0147 0148 void AbstractDataPluginModelPrivate::updateFavoriteItems() 0149 { 0150 if ( m_favoriteItemsOnly ) { 0151 for( const QString &id: m_favoriteItems ) { 0152 if ( !m_parent->findItem( id ) ) { 0153 m_parent->getItem( id ); 0154 } 0155 } 0156 } 0157 } 0158 0159 void AbstractDataPluginModel::themeChanged() 0160 { 0161 if ( d->m_currentPlanetId != d->m_marbleModel->planetId() ) { 0162 clear(); 0163 d->m_currentPlanetId = d->m_marbleModel->planetId(); 0164 } 0165 } 0166 0167 static bool lessThanByPointer( const AbstractDataPluginItem *item1, 0168 const AbstractDataPluginItem *item2 ) 0169 { 0170 if( item1 && item2 ) { 0171 // Compare by sticky and favorite status (sticky first, then favorites), last by operator< 0172 bool const sticky1 = item1->isSticky(); 0173 bool const favorite1 = item1->isFavorite(); 0174 if ( sticky1 != item2->isSticky() ) { 0175 return sticky1; 0176 } else if ( favorite1 != item2->isFavorite() ) { 0177 return favorite1; 0178 } else { 0179 return item1->operator<( item2 ); 0180 } 0181 } 0182 else { 0183 return false; 0184 } 0185 } 0186 0187 FavoritesModel::FavoritesModel( AbstractDataPluginModelPrivate *_d, QObject* parent ) : 0188 QAbstractListModel( parent ), d(_d) 0189 { 0190 QHash<int,QByteArray> roles; 0191 int const size = d->m_hasMetaObject ? d->m_metaObject.propertyCount() : 0; 0192 for ( int i=0; i<size; ++i ) { 0193 QMetaProperty property = d->m_metaObject.property( i ); 0194 roles[Qt::UserRole+i] = property.name(); 0195 } 0196 roles[Qt::DisplayRole] = "display"; 0197 roles[Qt::DecorationRole] = "decoration"; 0198 m_roleNames = roles; 0199 } 0200 0201 int FavoritesModel::rowCount ( const QModelIndex &parent ) const 0202 { 0203 if ( parent.isValid() ) { 0204 return 0; 0205 } 0206 0207 int count = 0; 0208 for( AbstractDataPluginItem* item: d->m_itemSet ) { 0209 if ( item->initialized() && item->isFavorite() ) { 0210 ++count; 0211 } 0212 } 0213 0214 return count; 0215 } 0216 0217 QVariant FavoritesModel::data( const QModelIndex &index, int role ) const 0218 { 0219 int const row = index.row(); 0220 if ( row >= 0 && row < rowCount() ) { 0221 int count = 0; 0222 for( AbstractDataPluginItem* item: d->m_itemSet ) { 0223 if ( item->initialized() && item->isFavorite() ) { 0224 if ( count == row ) { 0225 QString const roleName = QString::fromUtf8(roleNames().value( role )); 0226 return item->property(roleName.toLatin1().constData()); 0227 } 0228 ++count; 0229 } 0230 } 0231 } 0232 0233 return QVariant(); 0234 } 0235 0236 void FavoritesModel::reset() 0237 { 0238 beginResetModel(); 0239 endResetModel(); 0240 } 0241 0242 QHash<int, QByteArray> FavoritesModel::roleNames() const 0243 { 0244 return m_roleNames; 0245 } 0246 0247 AbstractDataPluginModel::AbstractDataPluginModel( const QString &name, const MarbleModel *marbleModel, QObject *parent ) 0248 : QObject( parent ), 0249 d( new AbstractDataPluginModelPrivate( name, marbleModel, this ) ) 0250 { 0251 Q_ASSERT( marbleModel != nullptr ); 0252 0253 // Initializing file and download System 0254 connect( &d->m_downloadManager, SIGNAL(downloadComplete(QString,QString)), 0255 this , SLOT(processFinishedJob(QString,QString)) ); 0256 0257 connect( marbleModel, SIGNAL(themeChanged(QString)), 0258 this, SLOT(themeChanged()) ); 0259 0260 // We want to download a new description file every timeBetweenDownloads ms 0261 connect( &d->m_downloadTimer, SIGNAL(timeout()), 0262 this, SLOT(handleChangedViewport()), 0263 Qt::QueuedConnection ); 0264 d->m_downloadTimer.start( timeBetweenDownloads ); 0265 } 0266 0267 AbstractDataPluginModel::~AbstractDataPluginModel() 0268 { 0269 delete d; 0270 } 0271 0272 const MarbleModel *AbstractDataPluginModel::marbleModel() const 0273 { 0274 return d->m_marbleModel; 0275 } 0276 0277 QList<AbstractDataPluginItem*> AbstractDataPluginModel::items( const ViewportParams *viewport, 0278 qint32 number ) 0279 { 0280 GeoDataLatLonAltBox currentBox = viewport->viewLatLonAltBox(); 0281 QList<AbstractDataPluginItem*> list; 0282 0283 Q_ASSERT( !d->m_displayedItems.contains( nullptr ) && "Null item in m_displayedItems. Please report a bug to marble-devel@kde.org" ); 0284 Q_ASSERT( !d->m_itemSet.contains( nullptr ) && "Null item in m_itemSet. Please report a bug to marble-devel@kde.org" ); 0285 0286 QList<AbstractDataPluginItem*> candidates = d->m_displayedItems + d->m_itemSet; 0287 0288 if ( d->m_needsSorting ) { 0289 // Both the candidates list and the list of all items need to be sorted 0290 std::sort( candidates.begin(), candidates.end(), lessThanByPointer ); 0291 std::sort( d->m_itemSet.begin(), d->m_itemSet.end(), lessThanByPointer ); 0292 d->m_needsSorting = false; 0293 } 0294 0295 QList<AbstractDataPluginItem*>::const_iterator i = candidates.constBegin(); 0296 QList<AbstractDataPluginItem*>::const_iterator end = candidates.constEnd(); 0297 0298 // Items that are already shown have the highest priority 0299 for (; i != end && list.size() < number; ++i ) { 0300 // Only show items that are initialized 0301 if( !(*i)->initialized() ) { 0302 continue; 0303 } 0304 0305 // Hide non-favorite items if necessary 0306 if( d->m_favoriteItemsOnly && !(*i)->isFavorite() ) { 0307 continue; 0308 } 0309 0310 (*i)->setProjection( viewport ); 0311 if( (*i)->positions().isEmpty() ) { 0312 continue; 0313 } 0314 0315 if ( list.contains( *i ) ) { 0316 continue; 0317 } 0318 0319 // If the item was added initially at a nearer position, they don't have priority, 0320 // because we zoomed out since then. 0321 bool const alreadyDisplayed = d->m_displayedItems.contains( *i ); 0322 if ( !alreadyDisplayed || (*i)->addedAngularResolution() >= viewport->angularResolution() || (*i)->isSticky() ) { 0323 bool collides = false; 0324 int const length = list.length(); 0325 for ( int j=0; !collides && j<length; ++j ) { 0326 for( const QRectF &rect: list[j]->boundingRects() ) { 0327 for( const QRectF &itemRect: (*i)->boundingRects() ) { 0328 if ( rect.intersects( itemRect ) ) 0329 collides = true; 0330 } 0331 } 0332 } 0333 0334 if ( !collides ) { 0335 list.append( *i ); 0336 (*i)->setSettings( d->m_itemSettings ); 0337 0338 // We want to save the angular resolution of the first time the item got added. 0339 if( !alreadyDisplayed ) { 0340 (*i)->setAddedAngularResolution( viewport->angularResolution() ); 0341 } 0342 } 0343 } 0344 // TODO: Do we have to cleanup at some point? The list of all items keeps growing 0345 } 0346 0347 d->m_lastBox = currentBox; 0348 d->m_lastNumber = number; 0349 d->m_displayedItems = list; 0350 return list; 0351 } 0352 0353 QList<AbstractDataPluginItem *> AbstractDataPluginModel::whichItemAt( const QPoint& curpos ) 0354 { 0355 QList<AbstractDataPluginItem *> itemsAt; 0356 0357 const QPointF curposF(curpos); 0358 for( AbstractDataPluginItem* item: d->m_displayedItems ) { 0359 if (item && item->contains(curposF)) { 0360 itemsAt.append( item ); 0361 } 0362 } 0363 0364 return itemsAt; 0365 } 0366 0367 void AbstractDataPluginModel::parseFile( const QByteArray& file ) 0368 { 0369 Q_UNUSED( file ); 0370 } 0371 0372 void AbstractDataPluginModel::downloadItem( const QUrl& url, 0373 const QString& type, 0374 AbstractDataPluginItem *item ) 0375 { 0376 if( !item ) { 0377 return; 0378 } 0379 0380 QString id = d->generateFilename( item->id(), type ); 0381 0382 d->m_downloadManager.addJob( url, id, id, DownloadBrowse ); 0383 d->m_downloadingItems.insert( id, item ); 0384 } 0385 0386 void AbstractDataPluginModel::downloadDescriptionFile( const QUrl& url ) 0387 { 0388 if( !url.isEmpty() ) { 0389 QString name( descriptionPrefix ); 0390 name += QString::number( d->m_descriptionFileNumber ); 0391 0392 d->m_downloadManager.addJob( url, name, name, DownloadBrowse ); 0393 d->m_descriptionFileNumber++; 0394 } 0395 } 0396 0397 void AbstractDataPluginModel::addItemToList( AbstractDataPluginItem *item ) 0398 { 0399 addItemsToList( QList<AbstractDataPluginItem*>() << item ); 0400 } 0401 0402 void AbstractDataPluginModel::addItemsToList( const QList<AbstractDataPluginItem *> &items ) 0403 { 0404 bool needsUpdate = false; 0405 bool favoriteChanged = false; 0406 for( AbstractDataPluginItem *item: items ) { 0407 if( !item ) { 0408 continue; 0409 } 0410 0411 // If the item is already in our list, don't add it. 0412 if ( d->m_itemSet.contains( item ) ) { 0413 continue; 0414 } 0415 0416 if( itemExists( item->id() ) ) { 0417 item->deleteLater(); 0418 continue; 0419 } 0420 0421 qCDebug(DIGIKAM_MARBLE_LOG) << "New item " << item->id(); 0422 0423 // This find the right position in the sorted to insert the new item 0424 QList<AbstractDataPluginItem*>::iterator i = std::lower_bound( d->m_itemSet.begin(), 0425 d->m_itemSet.end(), 0426 item, 0427 lessThanByPointer ); 0428 // Insert the item on the right position in the list 0429 d->m_itemSet.insert( i, item ); 0430 0431 connect( item, SIGNAL(stickyChanged()), this, SLOT(scheduleItemSort()) ); 0432 connect( item, SIGNAL(destroyed(QObject*)), this, SLOT(removeItem(QObject*)) ); 0433 connect( item, SIGNAL(updated()), this, SIGNAL(itemsUpdated()) ); 0434 connect( item, SIGNAL(favoriteChanged(QString,bool)), this, 0435 SLOT(favoriteItemChanged(QString,bool)) ); 0436 0437 if ( !needsUpdate && item->initialized() ) { 0438 needsUpdate = true; 0439 } 0440 0441 if ( !favoriteChanged && item->initialized() && item->isFavorite() ) { 0442 favoriteChanged = true; 0443 } 0444 } 0445 0446 if ( favoriteChanged && d->m_favoritesModel ) { 0447 d->m_favoritesModel->reset(); 0448 } 0449 0450 if ( needsUpdate ) { 0451 Q_EMIT itemsUpdated(); 0452 } 0453 } 0454 0455 void AbstractDataPluginModel::getItem( const QString & ) 0456 { 0457 qCWarning(DIGIKAM_MARBLE_LOG) << "Retrieving items by identifier is not implemented by this plugin"; 0458 } 0459 0460 void AbstractDataPluginModel::setFavoriteItems( const QStringList& list ) 0461 { 0462 if ( d->m_favoriteItems != list) { 0463 d->m_favoriteItems = list; 0464 d->updateFavoriteItems(); 0465 if ( d->m_favoritesModel ) { 0466 d->m_favoritesModel->reset(); 0467 } 0468 Q_EMIT favoriteItemsChanged( d->m_favoriteItems ); 0469 } 0470 } 0471 0472 QStringList AbstractDataPluginModel::favoriteItems() const 0473 { 0474 return d->m_favoriteItems; 0475 } 0476 0477 void AbstractDataPluginModel::setFavoriteItemsOnly( bool favoriteOnly ) 0478 { 0479 if ( isFavoriteItemsOnly() != favoriteOnly ) { 0480 d->m_favoriteItemsOnly = favoriteOnly; 0481 d->updateFavoriteItems(); 0482 Q_EMIT favoriteItemsOnlyChanged(); 0483 } 0484 } 0485 0486 bool AbstractDataPluginModel::isFavoriteItemsOnly() const 0487 { 0488 return d->m_favoriteItemsOnly; 0489 } 0490 0491 QObject *AbstractDataPluginModel::favoritesModel() 0492 { 0493 if ( !d->m_favoritesModel ) { 0494 d->m_favoritesModel = new FavoritesModel( d, this ); 0495 d->updateFavoriteItems(); 0496 } 0497 0498 return d->m_favoritesModel; 0499 } 0500 0501 void AbstractDataPluginModel::favoriteItemChanged( const QString& id, bool isFavorite ) 0502 { 0503 QStringList favorites = d->m_favoriteItems; 0504 0505 if ( isFavorite ) { 0506 if ( !favorites.contains(id) ) 0507 favorites.append( id ); 0508 } else { 0509 favorites.removeOne( id ); 0510 } 0511 0512 setFavoriteItems( favorites ); 0513 scheduleItemSort(); 0514 } 0515 0516 void AbstractDataPluginModel::scheduleItemSort() 0517 { 0518 d->m_needsSorting = true; 0519 } 0520 0521 QString AbstractDataPluginModelPrivate::generateFilename(const QString &id, const QString &type) 0522 { 0523 QString name; 0524 name += id; 0525 name += fileIdSeparator; 0526 name += type; 0527 0528 return name; 0529 } 0530 0531 QString AbstractDataPluginModelPrivate::generateFilepath( const QString& id, const QString& type ) const 0532 { 0533 return MarbleDirs::localPath() + QLatin1String("/cache/") + m_name + QLatin1Char('/') + generateFilename(id, type); 0534 } 0535 0536 AbstractDataPluginItem *AbstractDataPluginModel::findItem( const QString& id ) const 0537 { 0538 for ( AbstractDataPluginItem *item: d->m_itemSet ) { 0539 if( item->id() == id ) { 0540 return item; 0541 } 0542 } 0543 0544 return nullptr; 0545 } 0546 0547 bool AbstractDataPluginModel::itemExists( const QString& id ) const 0548 { 0549 return findItem( id ); 0550 } 0551 0552 void AbstractDataPluginModel::setItemSettings(const QHash<QString, QVariant> &itemSettings) 0553 { 0554 d->m_itemSettings = itemSettings; 0555 } 0556 0557 void AbstractDataPluginModel::handleChangedViewport() 0558 { 0559 if( d->m_favoriteItemsOnly ) { 0560 return; 0561 } 0562 0563 // All this is to prevent to often downloads 0564 if( d->m_lastNumber != 0 0565 // We don't need to download if nothing changed 0566 && ( !( d->m_downloadedBox == d->m_lastBox ) 0567 || d->m_downloadedNumber != d->m_lastNumber ) 0568 // We try to filter little changes of the bounding box 0569 && ( fabs( d->m_downloadedBox.east() - d->m_lastBox.east() ) * boxComparisonFactor 0570 > d->m_lastBox.width() 0571 || fabs( d->m_downloadedBox.south() - d->m_lastBox.south() ) * boxComparisonFactor 0572 > d->m_lastBox.height() 0573 || fabs( d->m_downloadedBox.north() - d->m_lastBox.north() ) * boxComparisonFactor 0574 > d->m_lastBox.height() 0575 || fabs( d->m_downloadedBox.west() - d->m_lastBox.west() ) * boxComparisonFactor 0576 > d->m_lastBox.width() ) ) 0577 { 0578 // We will wait a little bit longer to start the 0579 // next download as we will really download something now. 0580 d->m_downloadTimer.setInterval( timeBetweenDownloads ); 0581 0582 // Save the download parameter 0583 d->m_downloadedBox = d->m_lastBox; 0584 d->m_downloadedNumber = d->m_lastNumber; 0585 0586 // Get items 0587 getAdditionalItems( d->m_lastBox, d->m_lastNumber ); 0588 } 0589 else { 0590 // Don't wait to long to start the next download as we decided not to download anything. 0591 // This will enhance response. 0592 d->m_downloadTimer.setInterval( timeBetweenTriedDownloads ); 0593 } 0594 } 0595 0596 void AbstractDataPluginModel::processFinishedJob( const QString& relativeUrlString, 0597 const QString& id ) 0598 { 0599 Q_UNUSED( relativeUrlString ); 0600 0601 if( id.startsWith( descriptionPrefix ) ) { 0602 parseFile( d->m_storagePolicy.data( id ) ); 0603 } 0604 else { 0605 // The downloaded file contains item data. 0606 0607 // Splitting the id in itemId and fileType 0608 QStringList fileInformation = id.split( fileIdSeparator ); 0609 0610 if( fileInformation.size() < 2) { 0611 qCDebug(DIGIKAM_MARBLE_LOG) << "Strange file information " << id; 0612 return; 0613 } 0614 QString itemId = fileInformation.at( 0 ); 0615 fileInformation.removeAt( 0 ); 0616 QString fileType = fileInformation.join( QString( fileIdSeparator ) ); 0617 0618 // Searching for the right item in m_downloadingItems 0619 QHash<QString, AbstractDataPluginItem *>::iterator i = d->m_downloadingItems.find( id ); 0620 if( i != d->m_downloadingItems.end() ) { 0621 if( itemId != (*i)->id() ) { 0622 return; 0623 } 0624 0625 (*i)->addDownloadedFile( d->generateFilepath( itemId, fileType ), 0626 fileType ); 0627 0628 d->m_downloadingItems.erase( i ); 0629 } 0630 } 0631 } 0632 0633 void AbstractDataPluginModel::removeItem( QObject *item ) 0634 { 0635 AbstractDataPluginItem * pluginItem = qobject_cast<AbstractDataPluginItem*>( item ); 0636 d->m_itemSet.removeAll( pluginItem ); 0637 QHash<QString, AbstractDataPluginItem *>::iterator i; 0638 for( i = d->m_downloadingItems.begin(); i != d->m_downloadingItems.end(); ++i ) { 0639 if( *i == pluginItem ) { 0640 i = d->m_downloadingItems.erase( i ); 0641 } 0642 } 0643 } 0644 0645 void AbstractDataPluginModel::clear() 0646 { 0647 d->m_displayedItems.clear(); 0648 QList<AbstractDataPluginItem*>::iterator iter = d->m_itemSet.begin(); 0649 QList<AbstractDataPluginItem*>::iterator const end = d->m_itemSet.end(); 0650 for (; iter != end; ++iter ) { 0651 (*iter)->deleteLater(); 0652 } 0653 d->m_itemSet.clear(); 0654 d->m_lastBox = GeoDataLatLonAltBox(); 0655 d->m_downloadedBox = GeoDataLatLonAltBox(); 0656 d->m_downloadedNumber = 0; 0657 Q_EMIT itemsUpdated(); 0658 } 0659 0660 void AbstractDataPluginModel::registerItemProperties( const QMetaObject &item ) 0661 { 0662 d->m_metaObject = item; 0663 d->m_hasMetaObject = true; 0664 } 0665 0666 } // namespace Marble 0667 0668 #include "moc_AbstractDataPluginModel.cpp"