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