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"