File indexing completed on 2025-01-05 03:59:20
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2010 Thibaut Gridel <tgridel@free.fr> 0004 // SPDX-FileCopyrightText: 2013 Levente Kurusa <levex@linux.com> 0005 // 0006 0007 0008 // Own 0009 #include "GeoDataTreeModel.h" 0010 0011 // Qt 0012 #include <QBrush> 0013 #include <QModelIndex> 0014 #include <QList> 0015 #include <QItemSelectionModel> 0016 0017 #include <klocalizedstring.h> 0018 0019 // Marble 0020 #include "GeoDataObject.h" 0021 #include "GeoDataDocument.h" 0022 #include "GeoDataContainer.h" 0023 #include "GeoDataExtendedData.h" 0024 #include "GeoDataFolder.h" 0025 #include "GeoDataPlacemark.h" 0026 #include "GeoDataPoint.h" 0027 #include "GeoDataPolygon.h" 0028 #include "GeoDataLinearRing.h" 0029 #include "GeoDataLookAt.h" 0030 #include "GeoDataMultiGeometry.h" 0031 #include "GeoDataPlaylist.h" 0032 #include "GeoDataTour.h" 0033 #include "GeoDataWait.h" 0034 #include "GeoDataFlyTo.h" 0035 #include "GeoDataCamera.h" 0036 #include "GeoDataStyle.h" 0037 #include "GeoDataIconStyle.h" 0038 #include "GeoDataListStyle.h" 0039 #include "FileManager.h" 0040 #include "MarblePlacemarkModel.h" 0041 0042 #include "digikam_debug.h" 0043 0044 using namespace Marble; 0045 0046 class Q_DECL_HIDDEN GeoDataTreeModel::Private { 0047 public: 0048 Private( QAbstractItemModel* model ); 0049 ~Private(); 0050 0051 static void checkParenting( GeoDataObject *object ); 0052 0053 GeoDataDocument* m_rootDocument; 0054 bool m_ownsRootDocument; 0055 QItemSelectionModel m_selectionModel; 0056 QHash<int, QByteArray> m_roleNames; 0057 }; 0058 0059 GeoDataTreeModel::Private::Private( QAbstractItemModel *model ) : 0060 m_rootDocument( new GeoDataDocument ), 0061 m_ownsRootDocument( true ), 0062 m_selectionModel( model ) 0063 { 0064 m_roleNames[MarblePlacemarkModel::DescriptionRole] = "description"; 0065 m_roleNames[MarblePlacemarkModel::IconPathRole] = "iconPath"; 0066 m_roleNames[MarblePlacemarkModel::PopularityIndexRole] = "zoomLevel"; 0067 m_roleNames[MarblePlacemarkModel::VisualCategoryRole] = "visualCategory"; 0068 m_roleNames[MarblePlacemarkModel::AreaRole] = "area"; 0069 m_roleNames[MarblePlacemarkModel::PopulationRole] = "population"; 0070 m_roleNames[MarblePlacemarkModel::CountryCodeRole] = "countryCode"; 0071 m_roleNames[MarblePlacemarkModel::StateRole] = "state"; 0072 m_roleNames[MarblePlacemarkModel::PopularityRole] = "popularity"; 0073 m_roleNames[MarblePlacemarkModel::GeoTypeRole] = "role"; 0074 m_roleNames[MarblePlacemarkModel::CoordinateRole] = "coordinate"; 0075 m_roleNames[MarblePlacemarkModel::StyleRole] = "style"; 0076 m_roleNames[MarblePlacemarkModel::GmtRole] = "gmt"; 0077 m_roleNames[MarblePlacemarkModel::DstRole] = "dst"; 0078 m_roleNames[MarblePlacemarkModel::GeometryRole] = "geometry"; 0079 m_roleNames[MarblePlacemarkModel::ObjectPointerRole] = "objectPointer"; 0080 m_roleNames[MarblePlacemarkModel::LongitudeRole] = "longitude"; 0081 m_roleNames[MarblePlacemarkModel::LatitudeRole] = "latitude"; 0082 } 0083 0084 GeoDataTreeModel::Private::~Private() 0085 { 0086 if ( m_ownsRootDocument ) { 0087 delete m_rootDocument; 0088 } 0089 } 0090 0091 void GeoDataTreeModel::Private::checkParenting( GeoDataObject *object ) 0092 { 0093 if (const auto container = dynamic_cast<const GeoDataContainer *>(object)) { 0094 for( GeoDataFeature *child: container->featureList() ) { 0095 if ( child->parent() != container ) { 0096 qCWarning(DIGIKAM_MARBLE_LOG) << "Parenting mismatch for " << child->name(); 0097 Q_ASSERT( 0 ); 0098 } 0099 } 0100 } 0101 } 0102 0103 GeoDataTreeModel::GeoDataTreeModel( QObject *parent ) 0104 : QAbstractItemModel( parent ), 0105 d( new Private( this ) ) 0106 { 0107 auto const roleNames = QAbstractItemModel::roleNames(); 0108 for(auto iter = roleNames.constBegin(); iter != roleNames.constEnd(); ++iter) { 0109 d->m_roleNames[iter.key()] = iter.value(); 0110 } 0111 } 0112 0113 GeoDataTreeModel::~GeoDataTreeModel() 0114 { 0115 delete d; 0116 } 0117 0118 int GeoDataTreeModel::rowCount( const QModelIndex &parent ) const 0119 { 0120 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount"; 0121 const GeoDataObject *parentItem; 0122 if ( parent.column() > 0 ) { 0123 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount bad column"; 0124 return 0; 0125 } 0126 0127 if ( !parent.isValid() ) { 0128 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount root parent"; 0129 parentItem = d->m_rootDocument; 0130 } else { 0131 parentItem = static_cast<const GeoDataObject *>(parent.internalPointer()); 0132 } 0133 0134 if ( !parentItem ) { 0135 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount bad parent"; 0136 return 0; 0137 } 0138 0139 if (const GeoDataContainer *container = dynamic_cast<const GeoDataContainer *>(parentItem)) { 0140 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount " << type << "(" << parentItem << ") =" << container->size(); 0141 return container->size(); 0142 // } else { 0143 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount bad container " << container; 0144 } 0145 0146 if (const auto placemark = geodata_cast<GeoDataPlacemark>(parentItem)) { 0147 if (geodata_cast<GeoDataMultiGeometry>(placemark->geometry())) { 0148 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount " << type << "(" << parentItem << ") = 1"; 0149 return 1; 0150 } 0151 } 0152 0153 if (const auto geometry = geodata_cast<GeoDataMultiGeometry>(parentItem)) { 0154 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount " << parent << " " << type << " " << geometry->size(); 0155 return geometry->size(); 0156 // } else { 0157 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount bad geometry " << geometry; 0158 } 0159 0160 if (const auto tour = geodata_cast<GeoDataTour>(parentItem)) { 0161 const GeoDataPlaylist *playlist = tour->playlist(); 0162 if ( playlist ) { 0163 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount " << parent << " Playlist " << 1; 0164 return 1; 0165 } 0166 } 0167 0168 if (const auto playlist = geodata_cast<GeoDataPlaylist>(parentItem)) { 0169 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowCount " << parent << " Playlist " << playlist->size(); 0170 return playlist->size(); 0171 } 0172 0173 // qCDebug(DIGIKAM_MARBLE_LOG) << "rowcount end"; 0174 return 0;//parentItem->childCount(); 0175 } 0176 0177 QVariant GeoDataTreeModel::headerData(int section, Qt::Orientation orientation, 0178 int role) const 0179 { 0180 if ( role == Qt::DisplayRole && orientation == Qt::Horizontal ) 0181 { 0182 switch ( section ) { 0183 case 0: 0184 return i18n("Name"); 0185 case 1: 0186 return i18n("Type"); 0187 case 2: 0188 return i18n("Popularity"); 0189 case 3: 0190 return i18nc("Popularity index", "PopIndex"); 0191 } 0192 } 0193 return QVariant(); 0194 } 0195 0196 QHash<int, QByteArray> GeoDataTreeModel::roleNames() const 0197 { 0198 return d->m_roleNames; 0199 } 0200 0201 QVariant GeoDataTreeModel::data( const QModelIndex &index, int role ) const 0202 { 0203 // qCDebug(DIGIKAM_MARBLE_LOG) << "data"; 0204 if ( !index.isValid() ) 0205 return QVariant(); 0206 0207 GeoDataObject *object = static_cast<GeoDataObject*>( index.internalPointer() ); 0208 if ( role == Qt::DisplayRole ) { 0209 0210 if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) { 0211 if ( index.column() == 0 ){ 0212 if ( placemark->countryCode().isEmpty() ) { 0213 return QVariant( placemark->name() ); 0214 } else { 0215 return QVariant(placemark->name() + QLatin1String(" (") + placemark->countryCode() + QLatin1Char(')')); 0216 } 0217 0218 } 0219 else if ( index.column() == 1 ){ 0220 return QVariant( QString::fromUtf8(placemark->nodeType()) ); 0221 } 0222 else if ( index.column() == 2 ){ 0223 return QVariant( placemark->popularity() ); 0224 } 0225 else if ( index.column() == 3 ){ 0226 return QVariant( placemark->zoomLevel() ); 0227 } 0228 } 0229 0230 if (const auto feature = dynamic_cast<const GeoDataFeature *>(object)) { 0231 if ( index.column() == 0 ){ 0232 return QVariant( feature->name() ); 0233 } 0234 else if ( index.column() == 1 ){ 0235 return QVariant( QString::fromUtf8(feature->nodeType()) ); 0236 } 0237 } 0238 0239 GeoDataGeometry *geometry = dynamic_cast<GeoDataGeometry*>( object ); 0240 if ( geometry && index.column() == 1 ){ 0241 return QVariant( QString::fromUtf8(geometry->nodeType()) ); 0242 } 0243 0244 GeoDataPlaylist *playlist = geodata_cast<GeoDataPlaylist>(object); 0245 if ( playlist && index.column() == 0 ) { 0246 return i18n( "Playlist" ); 0247 } 0248 0249 if (object && index.column() == 1) { 0250 return QVariant(QString::fromUtf8(object->nodeType())); 0251 } 0252 0253 } 0254 else if ( role == Qt::CheckStateRole 0255 && index.column() == 0 ) { 0256 if (const auto feature = geodata_cast<GeoDataPlacemark>(object)) { 0257 if (const auto folder = geodata_cast<GeoDataFolder>(feature->parent())) { 0258 if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder 0259 || folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckOffOnly) { 0260 if ( feature->isVisible() ) { 0261 return QVariant ( Qt::Checked ); 0262 } else { 0263 return QVariant ( Qt::Unchecked ); 0264 } 0265 } 0266 } 0267 0268 if (feature->isGloballyVisible()) { 0269 return QVariant(Qt::Checked); 0270 } 0271 0272 if (feature->isVisible()) { 0273 return QVariant(Qt::PartiallyChecked); 0274 } 0275 0276 return QVariant(Qt::Unchecked); 0277 } else if (auto feature = dynamic_cast<GeoDataContainer *>(object)) { 0278 if (auto folder = geodata_cast<GeoDataFolder>(object)) { 0279 if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder) { 0280 bool anyVisible = false; 0281 QVector<GeoDataFeature *>::Iterator i = folder->begin(); 0282 for (; i < folder->end(); ++i) { 0283 if ((*i)->isVisible()) { 0284 anyVisible = true; 0285 break; 0286 } 0287 } 0288 if (anyVisible) { 0289 return QVariant( Qt::PartiallyChecked ); 0290 } else { 0291 return QVariant( Qt::Unchecked ); 0292 } 0293 } else if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckOffOnly) { 0294 QVector<GeoDataFeature *>::Iterator i = folder->begin(); 0295 bool anyVisible = false; 0296 bool allVisible = true; 0297 for (; i < folder->end(); ++i) { 0298 if ((*i)->isVisible()) { 0299 anyVisible = true; 0300 } else { 0301 allVisible = false; 0302 } 0303 } 0304 if (allVisible) { 0305 return QVariant( Qt::Checked ); 0306 } else if (anyVisible) { 0307 return QVariant( Qt::PartiallyChecked ); 0308 } else { 0309 return QVariant( Qt::Unchecked ); 0310 } 0311 } 0312 } 0313 if ( feature->isGloballyVisible() ) { 0314 return QVariant( Qt::Checked ); 0315 } else if ( feature->isVisible() ) { 0316 return QVariant( Qt::PartiallyChecked ); 0317 } else { 0318 return QVariant( Qt::Unchecked ); 0319 } 0320 } 0321 } 0322 else if ( role == Qt::DecorationRole 0323 && index.column() == 0 ) { 0324 if (const auto feature = dynamic_cast<const GeoDataFeature *>(object)) { 0325 if (feature->style()->iconStyle().icon().isNull()) { 0326 return QImage(); 0327 } 0328 0329 return QVariant(feature->style()->iconStyle().icon().scaled( QSize(16,16), Qt::KeepAspectRatio, Qt::SmoothTransformation )); 0330 } 0331 } else if ( role == Qt::ToolTipRole 0332 && index.column() == 0 ) { 0333 if (const auto feature = dynamic_cast<const GeoDataFeature *>(object)) { 0334 return QVariant( feature->description() ); 0335 } 0336 } else if ( role == MarblePlacemarkModel::ObjectPointerRole ) { 0337 return QVariant::fromValue( object ); 0338 } else if ( role == MarblePlacemarkModel::PopularityIndexRole ) { 0339 if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) { 0340 return QVariant( placemark->zoomLevel() ); 0341 } 0342 } else if ( role == MarblePlacemarkModel::PopularityRole ) { 0343 if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) { 0344 return QVariant( placemark->popularity() ); 0345 } 0346 } else if ( role == MarblePlacemarkModel::CoordinateRole ) { 0347 if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) { 0348 return QVariant::fromValue( placemark->coordinate() ); 0349 } else if (const auto flyTo = geodata_cast<GeoDataFlyTo>(object)) { 0350 if (const auto camera = geodata_cast<GeoDataCamera>(flyTo->view())) { 0351 return QVariant::fromValue<GeoDataCoordinates>( camera->coordinates() ); 0352 } else if (const auto lookAt = (flyTo->view() ? geodata_cast<GeoDataLookAt>(flyTo->view()) : nullptr)) { 0353 return QVariant::fromValue<GeoDataCoordinates>( lookAt->coordinates() ); 0354 } 0355 } 0356 } else if ( role == Qt::BackgroundRole ) { 0357 if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) { 0358 if (const GeoDataContainer *container = dynamic_cast<const GeoDataContainer *>(placemark->parent())) { 0359 return container->customStyle() ? QVariant( QBrush( container->customStyle()->listStyle().backgroundColor() )) : QVariant(); 0360 } 0361 } 0362 } else if (role == MarblePlacemarkModel::IconPathRole) { 0363 if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) { 0364 return placemark->style()->iconStyle().iconPath(); 0365 } 0366 } 0367 0368 return QVariant(); 0369 } 0370 0371 QModelIndex GeoDataTreeModel::index( int row, int column, const QModelIndex &parent ) const 0372 { 0373 // qCDebug(DIGIKAM_MARBLE_LOG) << "index"; 0374 if ( !hasIndex( row, column, parent ) ) { 0375 // qCDebug(DIGIKAM_MARBLE_LOG) << "index bad index"; 0376 return QModelIndex(); 0377 } 0378 0379 GeoDataObject *parentItem; 0380 0381 if ( !parent.isValid() ) 0382 parentItem = d->m_rootDocument; 0383 else 0384 parentItem = static_cast<GeoDataObject*>( parent.internalPointer() ); 0385 0386 if ( !parentItem ) { 0387 // qCDebug(DIGIKAM_MARBLE_LOG) << "index bad parent"; 0388 return QModelIndex(); 0389 } 0390 0391 GeoDataObject *childItem = nullptr; 0392 0393 0394 if (auto container = dynamic_cast<GeoDataContainer *>(parentItem)) { 0395 childItem = container->child( row ); 0396 return createIndex( row, column, childItem ); 0397 } 0398 0399 if (const auto placemark = geodata_cast<GeoDataPlacemark>(parentItem)) { 0400 childItem = placemark->geometry(); 0401 if (geodata_cast<GeoDataMultiGeometry>(childItem)) { 0402 return createIndex( row, column, childItem ); 0403 } 0404 } 0405 0406 if (const auto geometry = geodata_cast<GeoDataMultiGeometry>(parentItem)) { 0407 childItem = geometry->child( row ); 0408 return createIndex( row, column, childItem ); 0409 } 0410 0411 if (const auto tour = geodata_cast<GeoDataTour>(parentItem)) { 0412 childItem = tour->playlist(); 0413 return createIndex( row, column, childItem ); 0414 } 0415 0416 if (const auto playlist = geodata_cast<GeoDataPlaylist>(parentItem)) { 0417 childItem = playlist->primitive( row ); 0418 return createIndex(row, column, childItem); 0419 } 0420 0421 return QModelIndex(); 0422 } 0423 0424 QModelIndex GeoDataTreeModel::parent( const QModelIndex &index ) const 0425 { 0426 // qCDebug(DIGIKAM_MARBLE_LOG) << "parent"; 0427 if ( !index.isValid() ) { 0428 // qCDebug(DIGIKAM_MARBLE_LOG) << "parent bad index"; 0429 return QModelIndex(); 0430 } 0431 0432 0433 GeoDataObject *childObject = static_cast<GeoDataObject*>( index.internalPointer() ); 0434 if ( childObject ) { 0435 0436 /// parentObject can be a container, placemark, multigeometry or playlist 0437 GeoDataObject *parentObject = childObject->parent(); 0438 if ( parentObject == d->m_rootDocument ) 0439 { 0440 return QModelIndex(); 0441 } 0442 0443 GeoDataObject *greatParentObject = parentObject->parent(); 0444 0445 // Avoid crashing when there is no grandparent 0446 if ( greatParentObject == nullptr ) 0447 { 0448 return QModelIndex(); 0449 } 0450 0451 // greatParent can be a container 0452 if (auto greatparentContainer = dynamic_cast<GeoDataContainer *>(greatParentObject)) { 0453 GeoDataFeature *parentFeature = static_cast<GeoDataFeature*>( parentObject ); 0454 // qCDebug(DIGIKAM_MARBLE_LOG) << "parent " << childObject->nodeType() << "(" << childObject << ") = " 0455 // << parentObject->nodeType() << "[" << greatparentContainer->childPosition( parentFeature ) << "](" << parentObject << ")"; 0456 return createIndex( greatparentContainer->childPosition( parentFeature ), 0, parentObject ); 0457 } 0458 0459 // greatParent can be a placemark 0460 if (geodata_cast<GeoDataPlacemark>(greatParentObject)) { 0461 // GeoDataPlacemark *greatparentPlacemark = static_cast<GeoDataPlacemark*>( greatParentObject ); 0462 // qCDebug(DIGIKAM_MARBLE_LOG) << "parent " << childObject->nodeType() << "(" << childObject << ") = " 0463 // << parentObject->nodeType() << "[0](" << parentObject << ")"; 0464 return createIndex( 0, 0, parentObject ); 0465 } 0466 0467 // greatParent can be a multigeometry 0468 if (GeoDataMultiGeometry *greatparentMultiGeo = geodata_cast<GeoDataMultiGeometry>(greatParentObject)) { 0469 GeoDataGeometry *parentGeometry = static_cast<GeoDataGeometry*>( parentObject ); 0470 // qCDebug(DIGIKAM_MARBLE_LOG) << "parent " << childObject->nodeType() << "(" << childObject << ") = " 0471 // << parentObject->nodeType() << "[" << greatParentItem->childPosition( parentGeometry ) << "](" << parentObject << ")"; 0472 return createIndex( greatparentMultiGeo->childPosition( parentGeometry ), 0, parentObject ); 0473 } 0474 0475 if (GeoDataTour *tour = geodata_cast<GeoDataTour>(greatParentObject)) { 0476 return createIndex( 0, 0, tour->playlist() ); 0477 } 0478 0479 } 0480 0481 // qCDebug(DIGIKAM_MARBLE_LOG) << "parent unknown index"; 0482 return QModelIndex(); 0483 } 0484 0485 int GeoDataTreeModel::columnCount( const QModelIndex & ) const 0486 { 0487 return 4; 0488 } 0489 0490 bool GeoDataTreeModel::setData ( const QModelIndex & index, const QVariant & value, int role ) 0491 { 0492 if ( !index.isValid() ) 0493 return false; 0494 0495 GeoDataObject *object = static_cast<GeoDataObject*>( index.internalPointer() ); 0496 if ( role == Qt::CheckStateRole ) { 0497 if (auto feature = dynamic_cast<GeoDataFeature *>(object)) { 0498 bool bValue = value.toBool(); 0499 if (auto pfolder = geodata_cast<GeoDataFolder>(feature->parent())) { 0500 if ( pfolder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder) { 0501 if ( bValue ) { 0502 QVector< GeoDataFeature * >::Iterator i = pfolder->begin(); 0503 for(; i < pfolder->end(); ++i) { 0504 (*i)->setVisible( false ); 0505 } 0506 } 0507 } 0508 } 0509 if (auto folder = geodata_cast<GeoDataFolder>(object)) { 0510 if ( bValue ) { 0511 } else { 0512 if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder 0513 || folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckOffOnly ) { 0514 QVector< GeoDataFeature * >::Iterator i = folder->begin(); 0515 for(; i < folder->end(); ++i) { 0516 (*i)->setVisible( false ); 0517 } 0518 folder->setVisible( false ); 0519 } 0520 } 0521 } 0522 feature->setVisible( bValue ); 0523 qCDebug(DIGIKAM_MARBLE_LOG) << "setData " << feature->name(); 0524 updateFeature( feature ); 0525 return true; 0526 } 0527 } else if ( role == Qt::EditRole ) { 0528 if (auto feature = dynamic_cast<GeoDataFeature *>(object)) { 0529 feature->setName( value.toString() ); 0530 qCDebug(DIGIKAM_MARBLE_LOG) << "setData " << feature->name() << " " << value.toString(); 0531 updateFeature( feature ); 0532 return true; 0533 } 0534 } 0535 0536 return false; 0537 } 0538 0539 Qt::ItemFlags GeoDataTreeModel::flags ( const QModelIndex & index ) const 0540 { 0541 if ( !index.isValid() ) 0542 return Qt::NoItemFlags; 0543 0544 const GeoDataObject *object = static_cast<const GeoDataObject *>(index.internalPointer()); 0545 0546 if (const auto feature = geodata_cast<GeoDataPlacemark>(object)) { 0547 const GeoDataObject *parent = feature->parent(); 0548 0549 if (const auto parentfolder = geodata_cast<GeoDataFolder>(parent)) { 0550 if ( parentfolder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder ) { 0551 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable; 0552 } else if ( parentfolder->style()->listStyle().listItemType() == GeoDataListStyle::CheckHideChildren ) { 0553 return Qt::NoItemFlags; 0554 } 0555 } 0556 } 0557 0558 if (const auto folder = geodata_cast<GeoDataFolder>(object)) { 0559 if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder) { 0560 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable; 0561 } else if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckOffOnly ) { 0562 QVector<GeoDataFeature *>::ConstIterator i = folder->constBegin(); 0563 bool allVisible = true; 0564 for (; i < folder->constEnd(); ++i) { 0565 if( ! (*i)->isVisible() ) { 0566 allVisible = false; 0567 break; 0568 } 0569 } 0570 if ( allVisible ) { 0571 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable; 0572 } else { 0573 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; 0574 } 0575 } else if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckHideChildren) { 0576 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable; 0577 } 0578 } 0579 0580 if (geodata_cast<GeoDataTour>(object)) { 0581 return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; 0582 } 0583 0584 if (dynamic_cast<const GeoDataFeature *>(object)) { 0585 const GeoDataObject *parent = object; 0586 while (!geodata_cast<GeoDataDocument>(parent)) { 0587 parent = parent->parent(); 0588 } 0589 const GeoDataDocument *document = static_cast<const GeoDataDocument *>(parent); 0590 if( document->documentRole() == UserDocument ) { 0591 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable; 0592 } 0593 0594 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; 0595 } 0596 0597 if (geodata_cast<GeoDataWait>(object) 0598 || geodata_cast<GeoDataFlyTo>(object) 0599 || geodata_cast<GeoDataPlaylist>(object)) { 0600 return Qt::ItemIsSelectable | Qt::ItemIsEnabled; 0601 } 0602 0603 return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 0604 } 0605 0606 0607 QModelIndex GeoDataTreeModel::index(const GeoDataObject *object) const 0608 { 0609 if ( object == nullptr ) 0610 return QModelIndex(); 0611 0612 //It first runs bottom-top, storing every ancestor of the object, and 0613 //then goes top-down retrieving the QModelIndex of every ancestor until reaching the 0614 //index of the requested object. 0615 //The TreeModel contains: Documents, Folders, Placemarks, MultiGeometries 0616 //and Geometries that are children of MultiGeometries 0617 //You can not call this function with an element that does not belong to the tree 0618 0619 Q_ASSERT(geodata_cast<GeoDataFolder>(object) 0620 || geodata_cast<GeoDataDocument>(object) 0621 || geodata_cast<GeoDataPlacemark>(object) 0622 || geodata_cast<GeoDataTour>(object) 0623 || ( geodata_cast<GeoDataPlaylist>(object) 0624 && geodata_cast<GeoDataTour>(object->parent())) 0625 || (geodata_cast<GeoDataWait>(object) 0626 && geodata_cast<GeoDataPlaylist>(object->parent())) 0627 || ( geodata_cast<GeoDataFlyTo>(object) 0628 && geodata_cast<GeoDataPlaylist>(object->parent())) 0629 || (geodata_cast<GeoDataLineString>(object) 0630 && geodata_cast<GeoDataMultiGeometry>(object->parent())) 0631 || (geodata_cast<GeoDataLinearRing>(object) 0632 && geodata_cast<GeoDataMultiGeometry>(object->parent())) 0633 || (geodata_cast<GeoDataPoint>(object) 0634 && geodata_cast<GeoDataMultiGeometry>(object->parent())) 0635 || (geodata_cast<GeoDataPolygon>(object) 0636 && geodata_cast<GeoDataMultiGeometry>(object->parent())) 0637 || geodata_cast<GeoDataMultiGeometry>(object)); 0638 0639 0640 QList<const GeoDataObject *> ancestors; 0641 0642 const GeoDataObject *itup = object; //Iterator to reach the top of the GeoDataDocument (bottom-up) 0643 0644 while ( itup && ( itup != d->m_rootDocument ) ) {//We reach up to the rootDocument 0645 0646 ancestors.append( itup ); 0647 itup = itup->parent() ; 0648 } 0649 0650 QModelIndex itdown; 0651 if ( !ancestors.isEmpty() ) { 0652 0653 itdown = index(d->m_rootDocument->childPosition(static_cast<const GeoDataFeature *>(ancestors.last())), 0, QModelIndex());//Iterator to go top down 0654 0655 while ( ( ancestors.size() > 1 ) ) { 0656 0657 const GeoDataObject *parent = static_cast<const GeoDataObject*>(ancestors.last()); 0658 0659 if (const auto container = dynamic_cast<const GeoDataContainer *>(parent)) { 0660 0661 ancestors.removeLast(); 0662 itdown = index(container->childPosition(static_cast<const GeoDataFeature *>(ancestors.last())), 0, itdown); 0663 } else if (geodata_cast<GeoDataPlacemark>(parent)) { 0664 //The only child of the model is a Geometry or MultiGeometry object 0665 //If it is a geometry object, we should be on the bottom of the list 0666 ancestors.removeLast(); 0667 if (geodata_cast<GeoDataMultiGeometry>(ancestors.last())) 0668 itdown = index( 0 , 0, itdown ); 0669 else 0670 itdown = QModelIndex(); 0671 0672 } else if (auto multiGeometry = geodata_cast<GeoDataMultiGeometry>(parent)) { 0673 //The child is one of the geometry children of MultiGeometry 0674 ancestors.removeLast(); 0675 itdown = index(multiGeometry->childPosition(static_cast<const GeoDataGeometry *>(ancestors.last())), 0, itdown); 0676 } else if (geodata_cast<GeoDataTour>(parent)) { 0677 ancestors.removeLast(); 0678 itdown = index( 0, 0, itdown ); 0679 } else if (auto playlist = geodata_cast<GeoDataPlaylist>(parent)) { 0680 for ( int i=0; i< playlist->size(); i++ ) 0681 { 0682 if ( playlist->primitive(i) == ancestors.last() ) 0683 { 0684 ancestors.removeLast(); 0685 itdown = index( i, 0, itdown ); 0686 break; 0687 } 0688 } 0689 } 0690 else { //If the element is not found on the tree, it will be added under m_rootDocument 0691 itdown = QModelIndex(); 0692 break; 0693 } 0694 } 0695 } 0696 return itdown; 0697 } 0698 0699 QItemSelectionModel *GeoDataTreeModel::selectionModel() 0700 { 0701 return &d->m_selectionModel; 0702 } 0703 0704 int GeoDataTreeModel::addFeature( GeoDataContainer *parent, GeoDataFeature *feature, int row ) 0705 { 0706 if ( parent && feature ) { 0707 0708 QModelIndex modelindex = index( parent ); 0709 //index(GeoDataObject*) returns QModelIndex() if parent == m_rootDocument 0710 //or if parent is not found on the tree. 0711 //We must check that we are in top of the tree (then QModelIndex() is 0712 //the right parent to insert the child object) or that we have a valid QModelIndex 0713 0714 if( ( parent == d->m_rootDocument ) || modelindex.isValid() ) 0715 { 0716 if( row < 0 || row > parent->size()) { 0717 row = parent->size(); 0718 } 0719 beginInsertRows( modelindex , row , row ); 0720 parent->insert( row, feature ); 0721 d->checkParenting( parent ); 0722 endInsertRows(); 0723 Q_EMIT added(feature); 0724 } 0725 else 0726 qCWarning(DIGIKAM_MARBLE_LOG) << "GeoDataTreeModel::addFeature (parent " << parent << " - feature" << feature << ") : parent not found on the TreeModel"; 0727 } 0728 else 0729 qCWarning(DIGIKAM_MARBLE_LOG) << "Null pointer in call to GeoDataTreeModel::addFeature (parent " << parent << " - feature" << feature << ")"; 0730 return row; //-1 if it failed, the relative index otherwise. 0731 } 0732 0733 int GeoDataTreeModel::addDocument( GeoDataDocument *document ) 0734 { 0735 return addFeature( d->m_rootDocument, document ); 0736 } 0737 0738 bool GeoDataTreeModel::removeFeature( GeoDataContainer *parent, int row ) 0739 { 0740 if ( row<parent->size() ) { 0741 beginRemoveRows( index( parent ), row , row ); 0742 GeoDataFeature *feature = parent->child( row ); 0743 parent->remove( row ); 0744 Q_EMIT removed(feature); 0745 endRemoveRows(); 0746 return true; 0747 } 0748 return false; //Tried to remove a row that is not contained in the parent. 0749 } 0750 0751 int GeoDataTreeModel::removeFeature(GeoDataFeature *feature) 0752 { 0753 if ( feature && ( feature!=d->m_rootDocument ) ) { 0754 0755 if (!feature->parent()) { 0756 return -1; 0757 } 0758 0759 //We check to see we are not removing the 0760 //top level element m_rootDocument 0761 0762 GeoDataObject *parent = static_cast< GeoDataObject* >( feature->parent() ); 0763 0764 if (dynamic_cast<const GeoDataContainer *>(parent)) { 0765 0766 int row = static_cast< GeoDataContainer* >( feature->parent() )->childPosition( feature ); 0767 if ( row != -1 ) { 0768 bool removed = removeFeature( static_cast< GeoDataContainer* >( feature->parent() ) , row ); 0769 if( removed ) { 0770 return row; 0771 } 0772 } 0773 //The feature is not contained in the parent it points to 0774 } 0775 } 0776 return -1; //We can not remove the rootDocument 0777 } 0778 0779 void GeoDataTreeModel::updateFeature( GeoDataFeature *feature ) 0780 { 0781 GeoDataContainer *container = static_cast<GeoDataContainer*>( feature->parent() ); 0782 int index = removeFeature( feature ); 0783 Q_ASSERT( index != -1 ); 0784 addFeature( container, feature, index ); 0785 } 0786 0787 void GeoDataTreeModel::removeDocument( int index ) 0788 { 0789 removeFeature( d->m_rootDocument, index ); 0790 } 0791 0792 void GeoDataTreeModel::removeDocument( GeoDataDocument *document ) 0793 { 0794 removeFeature( document ); 0795 } 0796 0797 void GeoDataTreeModel::setRootDocument( GeoDataDocument* document ) 0798 { 0799 beginResetModel(); 0800 if ( d->m_ownsRootDocument ) { 0801 delete d->m_rootDocument; 0802 } 0803 0804 d->m_ownsRootDocument = ( document == nullptr ); 0805 d->m_rootDocument = document ? document : new GeoDataDocument; 0806 endResetModel(); 0807 } 0808 0809 GeoDataDocument * GeoDataTreeModel::rootDocument() 0810 { 0811 return d->m_rootDocument; 0812 } 0813 0814 int GeoDataTreeModel::addTourPrimitive( const QModelIndex &parent, GeoDataTourPrimitive *primitive, int row ) 0815 { 0816 GeoDataObject *parentObject = static_cast<GeoDataObject*>( parent.internalPointer() ); 0817 if (auto playlist = geodata_cast<GeoDataPlaylist>(parentObject)) { 0818 if( row == -1 ) { 0819 row = playlist->size(); 0820 } 0821 beginInsertRows( parent, row, row ); 0822 playlist->insertPrimitive( row, primitive ); 0823 endInsertRows(); 0824 return row; 0825 } 0826 return -1; 0827 } 0828 0829 bool GeoDataTreeModel::removeTourPrimitive( const QModelIndex &parent , int index) 0830 { 0831 GeoDataObject *parentObject = static_cast<GeoDataObject*>( parent.internalPointer() ); 0832 if (auto playlist = (parent.isValid() ? geodata_cast<GeoDataPlaylist>(parentObject) : nullptr)) { 0833 if( playlist->size() > index ) { 0834 beginRemoveRows( parent, index, index ); 0835 playlist->removePrimitiveAt( index ); 0836 endRemoveRows(); 0837 return true; 0838 } 0839 } 0840 return false; 0841 } 0842 0843 bool GeoDataTreeModel::swapTourPrimitives( const QModelIndex &parent, int indexA, int indexB ) 0844 { 0845 GeoDataObject *parentObject = static_cast<GeoDataObject*>( parent.internalPointer() ); 0846 if (auto playlist = (parent.isValid() ? geodata_cast<GeoDataPlaylist>(parentObject) : nullptr)) { 0847 if( indexA > indexB ) { 0848 qSwap(indexA, indexB); 0849 } 0850 if ( indexB - indexA == 1 ) { 0851 beginMoveRows( parent, indexA, indexA, parent, indexB+1 ); 0852 } else { 0853 beginMoveRows( parent, indexA, indexA, parent, indexB ); 0854 beginMoveRows( parent, indexB, indexB, parent, indexA ); 0855 } 0856 playlist->swapPrimitives( indexA, indexB ); 0857 if( indexB - indexA != 1 ) { 0858 endMoveRows(); 0859 } 0860 endMoveRows(); 0861 return true; 0862 } 0863 return false; 0864 } 0865 0866 #include "moc_GeoDataTreeModel.cpp"