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"