Marble

GeoDataTreeModel.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2010 Thibaut Gridel <[email protected]>
4 // SPDX-FileCopyrightText: 2013 Levente Kurusa <[email protected]>
5 //
6 
7 
8 // Own
9 #include "GeoDataTreeModel.h"
10 
11 // Qt
12 #include <QBrush>
13 #include <QModelIndex>
14 #include <QList>
15 #include <QItemSelectionModel>
16 
17 // Marble
18 #include "GeoDataObject.h"
19 #include "GeoDataDocument.h"
20 #include "GeoDataContainer.h"
21 #include "GeoDataExtendedData.h"
22 #include "GeoDataFolder.h"
23 #include "GeoDataPlacemark.h"
24 #include "GeoDataPoint.h"
25 #include "GeoDataPolygon.h"
26 #include "GeoDataLinearRing.h"
27 #include "GeoDataLookAt.h"
28 #include "GeoDataMultiGeometry.h"
29 #include "GeoDataPlaylist.h"
30 #include "GeoDataTour.h"
31 #include "GeoDataWait.h"
32 #include "GeoDataFlyTo.h"
33 #include "GeoDataCamera.h"
34 #include "GeoDataStyle.h"
35 #include "GeoDataIconStyle.h"
36 #include "GeoDataListStyle.h"
37 #include "FileManager.h"
38 #include "MarbleDebug.h"
39 #include "MarblePlacemarkModel.h"
40 
41 using namespace Marble;
42 
43 class Q_DECL_HIDDEN GeoDataTreeModel::Private {
44  public:
45  Private( QAbstractItemModel* model );
46  ~Private();
47 
48  static void checkParenting( GeoDataObject *object );
49 
50  GeoDataDocument* m_rootDocument;
51  bool m_ownsRootDocument;
52  QItemSelectionModel m_selectionModel;
53  QHash<int, QByteArray> m_roleNames;
54 };
55 
56 GeoDataTreeModel::Private::Private( QAbstractItemModel *model ) :
57  m_rootDocument( new GeoDataDocument ),
58  m_ownsRootDocument( true ),
59  m_selectionModel( model )
60 {
61  m_roleNames[MarblePlacemarkModel::DescriptionRole] = "description";
62  m_roleNames[MarblePlacemarkModel::IconPathRole] = "iconPath";
63  m_roleNames[MarblePlacemarkModel::PopularityIndexRole] = "zoomLevel";
64  m_roleNames[MarblePlacemarkModel::VisualCategoryRole] = "visualCategory";
65  m_roleNames[MarblePlacemarkModel::AreaRole] = "area";
66  m_roleNames[MarblePlacemarkModel::PopulationRole] = "population";
67  m_roleNames[MarblePlacemarkModel::CountryCodeRole] = "countryCode";
68  m_roleNames[MarblePlacemarkModel::StateRole] = "state";
69  m_roleNames[MarblePlacemarkModel::PopularityRole] = "popularity";
70  m_roleNames[MarblePlacemarkModel::GeoTypeRole] = "role";
71  m_roleNames[MarblePlacemarkModel::CoordinateRole] = "coordinate";
72  m_roleNames[MarblePlacemarkModel::StyleRole] = "style";
73  m_roleNames[MarblePlacemarkModel::GmtRole] = "gmt";
74  m_roleNames[MarblePlacemarkModel::DstRole] = "dst";
75  m_roleNames[MarblePlacemarkModel::GeometryRole] = "geometry";
76  m_roleNames[MarblePlacemarkModel::ObjectPointerRole] = "objectPointer";
77  m_roleNames[MarblePlacemarkModel::LongitudeRole] = "longitude";
78  m_roleNames[MarblePlacemarkModel::LatitudeRole] = "latitude";
79 }
80 
81 GeoDataTreeModel::Private::~Private()
82 {
83  if ( m_ownsRootDocument ) {
84  delete m_rootDocument;
85  }
86 }
87 
88 void GeoDataTreeModel::Private::checkParenting( GeoDataObject *object )
89 {
90  if (const auto container = dynamic_cast<const GeoDataContainer *>(object)) {
91  for( GeoDataFeature *child: container->featureList() ) {
92  if ( child->parent() != container ) {
93  qWarning() << "Parenting mismatch for " << child->name();
94  Q_ASSERT( 0 );
95  }
96  }
97  }
98 }
99 
101  : QAbstractItemModel( parent ),
102  d( new Private( this ) )
103 {
104  auto const roleNames = QAbstractItemModel::roleNames();
105  for(auto iter = roleNames.constBegin(); iter != roleNames.constEnd(); ++iter) {
106  d->m_roleNames[iter.key()] = iter.value();
107  }
108 }
109 
111 {
112  delete d;
113 }
114 
115 int GeoDataTreeModel::rowCount( const QModelIndex &parent ) const
116 {
117 // mDebug() << "rowCount";
118  const GeoDataObject *parentItem;
119  if ( parent.column() > 0 ) {
120 // mDebug() << "rowCount bad column";
121  return 0;
122  }
123 
124  if ( !parent.isValid() ) {
125 // mDebug() << "rowCount root parent";
126  parentItem = d->m_rootDocument;
127  } else {
128  parentItem = static_cast<const GeoDataObject *>(parent.internalPointer());
129  }
130 
131  if ( !parentItem ) {
132 // mDebug() << "rowCount bad parent";
133  return 0;
134  }
135 
136  if (const GeoDataContainer *container = dynamic_cast<const GeoDataContainer *>(parentItem)) {
137 // mDebug() << "rowCount " << type << "(" << parentItem << ") =" << container->size();
138  return container->size();
139 // } else {
140 // mDebug() << "rowCount bad container " << container;
141  }
142 
143  if (const auto placemark = geodata_cast<GeoDataPlacemark>(parentItem)) {
144  if (geodata_cast<GeoDataMultiGeometry>(placemark->geometry())) {
145 // mDebug() << "rowCount " << type << "(" << parentItem << ") = 1";
146  return 1;
147  }
148  }
149 
150  if (const auto geometry = geodata_cast<GeoDataMultiGeometry>(parentItem)) {
151 // mDebug() << "rowCount " << parent << " " << type << " " << geometry->size();
152  return geometry->size();
153 // } else {
154 // mDebug() << "rowCount bad geometry " << geometry;
155  }
156 
157  if (const auto tour = geodata_cast<GeoDataTour>(parentItem)) {
158  const GeoDataPlaylist *playlist = tour->playlist();
159  if ( playlist ) {
160 // mDebug() << "rowCount " << parent << " Playlist " << 1;
161  return 1;
162  }
163  }
164 
165  if (const auto playlist = geodata_cast<GeoDataPlaylist>(parentItem)) {
166 // mDebug() << "rowCount " << parent << " Playlist " << playlist->size();
167  return playlist->size();
168  }
169 
170 // mDebug() << "rowcount end";
171  return 0;//parentItem->childCount();
172 }
173 
174 QVariant GeoDataTreeModel::headerData(int section, Qt::Orientation orientation,
175  int role) const
176 {
177  if ( role == Qt::DisplayRole && orientation == Qt::Horizontal )
178  {
179  switch ( section ) {
180  case 0:
181  return tr("Name");
182  case 1:
183  return tr("Type");
184  case 2:
185  return tr("Popularity");
186  case 3:
187  return tr("PopIndex", "Popularity index");
188  }
189  }
190  return QVariant();
191 }
192 
193 QHash<int, QByteArray> GeoDataTreeModel::roleNames() const
194 {
195  return d->m_roleNames;
196 }
197 
198 QVariant GeoDataTreeModel::data( const QModelIndex &index, int role ) const
199 {
200 // mDebug() << "data";
201  if ( !index.isValid() )
202  return QVariant();
203 
204  GeoDataObject *object = static_cast<GeoDataObject*>( index.internalPointer() );
205  if ( role == Qt::DisplayRole ) {
206 
207  if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) {
208  if ( index.column() == 0 ){
209  if ( placemark->countryCode().isEmpty() ) {
210  return QVariant( placemark->name() );
211  } else {
212  return QVariant(placemark->name() + QLatin1String(" (") + placemark->countryCode() + QLatin1Char(')'));
213  }
214 
215  }
216  else if ( index.column() == 1 ){
217  return QVariant( placemark->nodeType() );
218  }
219  else if ( index.column() == 2 ){
220  return QVariant( placemark->popularity() );
221  }
222  else if ( index.column() == 3 ){
223  return QVariant( placemark->zoomLevel() );
224  }
225  }
226 
227  if (const auto feature = dynamic_cast<const GeoDataFeature *>(object)) {
228  if ( index.column() == 0 ){
229  return QVariant( feature->name() );
230  }
231  else if ( index.column() == 1 ){
232  return QVariant( feature->nodeType() );
233  }
234  }
235 
236  GeoDataGeometry *geometry = dynamic_cast<GeoDataGeometry*>( object );
237  if ( geometry && index.column() == 1 ){
238  return QVariant( geometry->nodeType() );
239  }
240 
241  GeoDataPlaylist *playlist = geodata_cast<GeoDataPlaylist>(object);
242  if ( playlist && index.column() == 0 ) {
243  return tr( "Playlist" );
244  }
245 
246  if (object && index.column() == 1) {
247  return QVariant(object->nodeType());
248  }
249 
250  }
251  else if ( role == Qt::CheckStateRole
252  && index.column() == 0 ) {
253  if (const auto feature = geodata_cast<GeoDataPlacemark>(object)) {
254  if (const auto folder = geodata_cast<GeoDataFolder>(feature->parent())) {
255  if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder
256  || folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckOffOnly) {
257  if ( feature->isVisible() ) {
258  return QVariant ( Qt::Checked );
259  } else {
260  return QVariant ( Qt::Unchecked );
261  }
262  }
263  }
264 
265  if (feature->isGloballyVisible()) {
266  return QVariant(Qt::Checked);
267  }
268 
269  if (feature->isVisible()) {
271  }
272 
273  return QVariant(Qt::Unchecked);
274  } else if (auto feature = dynamic_cast<GeoDataContainer *>(object)) {
275  if (auto folder = geodata_cast<GeoDataFolder>(object)) {
276  if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder) {
277  bool anyVisible = false;
279  for (; i < folder->end(); ++i) {
280  if ((*i)->isVisible()) {
281  anyVisible = true;
282  break;
283  }
284  }
285  if (anyVisible) {
286  return QVariant( Qt::PartiallyChecked );
287  } else {
288  return QVariant( Qt::Unchecked );
289  }
290  } else if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckOffOnly) {
292  bool anyVisible = false;
293  bool allVisible = true;
294  for (; i < folder->end(); ++i) {
295  if ((*i)->isVisible()) {
296  anyVisible = true;
297  } else {
298  allVisible = false;
299  }
300  }
301  if (allVisible) {
302  return QVariant( Qt::Checked );
303  } else if (anyVisible) {
304  return QVariant( Qt::PartiallyChecked );
305  } else {
306  return QVariant( Qt::Unchecked );
307  }
308  }
309  }
310  if ( feature->isGloballyVisible() ) {
311  return QVariant( Qt::Checked );
312  } else if ( feature->isVisible() ) {
313  return QVariant( Qt::PartiallyChecked );
314  } else {
315  return QVariant( Qt::Unchecked );
316  }
317  }
318  }
319  else if ( role == Qt::DecorationRole
320  && index.column() == 0 ) {
321  if (const auto feature = dynamic_cast<const GeoDataFeature *>(object)) {
322  if (feature->style()->iconStyle().icon().isNull()) {
323  return QImage();
324  }
325 
326  return QVariant(feature->style()->iconStyle().icon().scaled( QSize(16,16), Qt::KeepAspectRatio, Qt::SmoothTransformation ));
327  }
328  } else if ( role == Qt::ToolTipRole
329  && index.column() == 0 ) {
330  if (const auto feature = dynamic_cast<const GeoDataFeature *>(object)) {
331  return QVariant( feature->description() );
332  }
333  } else if ( role == MarblePlacemarkModel::ObjectPointerRole ) {
334  return QVariant::fromValue( object );
335  } else if ( role == MarblePlacemarkModel::PopularityIndexRole ) {
336  if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) {
337  return QVariant( placemark->zoomLevel() );
338  }
339  } else if ( role == MarblePlacemarkModel::PopularityRole ) {
340  if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) {
341  return QVariant( placemark->popularity() );
342  }
343  } else if ( role == MarblePlacemarkModel::CoordinateRole ) {
344  if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) {
345  return QVariant::fromValue( placemark->coordinate() );
346  } else if (const auto flyTo = geodata_cast<GeoDataFlyTo>(object)) {
347  if (const auto camera = geodata_cast<GeoDataCamera>(flyTo->view())) {
348  return QVariant::fromValue<GeoDataCoordinates>( camera->coordinates() );
349  } else if (const auto lookAt = (flyTo->view() ? geodata_cast<GeoDataLookAt>(flyTo->view()) : nullptr)) {
350  return QVariant::fromValue<GeoDataCoordinates>( lookAt->coordinates() );
351  }
352  }
353  } else if ( role == Qt::BackgroundRole ) {
354  if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) {
355  if (const GeoDataContainer *container = dynamic_cast<const GeoDataContainer *>(placemark->parent())) {
356  return container->customStyle() ? QVariant( QBrush( container->customStyle()->listStyle().backgroundColor() )) : QVariant();
357  }
358  }
359  } else if (role == MarblePlacemarkModel::IconPathRole) {
360  if (const auto placemark = geodata_cast<GeoDataPlacemark>(object)) {
361  return placemark->style()->iconStyle().iconPath();
362  }
363  }
364 
365  return QVariant();
366 }
367 
368 QModelIndex GeoDataTreeModel::index( int row, int column, const QModelIndex &parent ) const
369 {
370 // mDebug() << "index";
371  if ( !hasIndex( row, column, parent ) ) {
372 // mDebug() << "index bad index";
373  return QModelIndex();
374  }
375 
376  GeoDataObject *parentItem;
377 
378  if ( !parent.isValid() )
379  parentItem = d->m_rootDocument;
380  else
381  parentItem = static_cast<GeoDataObject*>( parent.internalPointer() );
382 
383  if ( !parentItem ) {
384 // mDebug() << "index bad parent";
385  return QModelIndex();
386  }
387 
388  GeoDataObject *childItem = nullptr;
389 
390 
391  if (auto container = dynamic_cast<GeoDataContainer *>(parentItem)) {
392  childItem = container->child( row );
393  return createIndex( row, column, childItem );
394  }
395 
396  if (const auto placemark = geodata_cast<GeoDataPlacemark>(parentItem)) {
397  childItem = placemark->geometry();
398  if (geodata_cast<GeoDataMultiGeometry>(childItem)) {
399  return createIndex( row, column, childItem );
400  }
401  }
402 
403  if (const auto geometry = geodata_cast<GeoDataMultiGeometry>(parentItem)) {
404  childItem = geometry->child( row );
405  return createIndex( row, column, childItem );
406  }
407 
408  if (const auto tour = geodata_cast<GeoDataTour>(parentItem)) {
409  childItem = tour->playlist();
410  return createIndex( row, column, childItem );
411  }
412 
413  if (const auto playlist = geodata_cast<GeoDataPlaylist>(parentItem)) {
414  childItem = playlist->primitive( row );
415  return createIndex(row, column, childItem);
416  }
417 
418  return QModelIndex();
419 }
420 
422 {
423 // mDebug() << "parent";
424  if ( !index.isValid() ) {
425 // mDebug() << "parent bad index";
426  return QModelIndex();
427  }
428 
429 
430  GeoDataObject *childObject = static_cast<GeoDataObject*>( index.internalPointer() );
431  if ( childObject ) {
432 
433  /// parentObject can be a container, placemark, multigeometry or playlist
434  GeoDataObject *parentObject = childObject->parent();
435  if ( parentObject == d->m_rootDocument )
436  {
437  return QModelIndex();
438  }
439 
440  GeoDataObject *greatParentObject = parentObject->parent();
441 
442  // Avoid crashing when there is no grandparent
443  if ( greatParentObject == nullptr )
444  {
445  return QModelIndex();
446  }
447 
448  // greatParent can be a container
449  if (auto greatparentContainer = dynamic_cast<GeoDataContainer *>(greatParentObject)) {
450  GeoDataFeature *parentFeature = static_cast<GeoDataFeature*>( parentObject );
451 // mDebug() << "parent " << childObject->nodeType() << "(" << childObject << ") = "
452 // << parentObject->nodeType() << "[" << greatparentContainer->childPosition( parentFeature ) << "](" << parentObject << ")";
453  return createIndex( greatparentContainer->childPosition( parentFeature ), 0, parentObject );
454  }
455 
456  // greatParent can be a placemark
457  if (geodata_cast<GeoDataPlacemark>(greatParentObject)) {
458 // GeoDataPlacemark *greatparentPlacemark = static_cast<GeoDataPlacemark*>( greatParentObject );
459 // mDebug() << "parent " << childObject->nodeType() << "(" << childObject << ") = "
460 // << parentObject->nodeType() << "[0](" << parentObject << ")";
461  return createIndex( 0, 0, parentObject );
462  }
463 
464  // greatParent can be a multigeometry
465  if (GeoDataMultiGeometry *greatparentMultiGeo = geodata_cast<GeoDataMultiGeometry>(greatParentObject)) {
466  GeoDataGeometry *parentGeometry = static_cast<GeoDataGeometry*>( parentObject );
467 // mDebug() << "parent " << childObject->nodeType() << "(" << childObject << ") = "
468 // << parentObject->nodeType() << "[" << greatParentItem->childPosition( parentGeometry ) << "](" << parentObject << ")";
469  return createIndex( greatparentMultiGeo->childPosition( parentGeometry ), 0, parentObject );
470  }
471 
472  if (GeoDataTour *tour = geodata_cast<GeoDataTour>(greatParentObject)) {
473  return createIndex( 0, 0, tour->playlist() );
474  }
475 
476  }
477 
478 // mDebug() << "parent unknown index";
479  return QModelIndex();
480 }
481 
482 int GeoDataTreeModel::columnCount( const QModelIndex & ) const
483 {
484  return 4;
485 }
486 
487 bool GeoDataTreeModel::setData ( const QModelIndex & index, const QVariant & value, int role )
488 {
489  if ( !index.isValid() )
490  return false;
491 
492  GeoDataObject *object = static_cast<GeoDataObject*>( index.internalPointer() );
493  if ( role == Qt::CheckStateRole ) {
494  if (auto feature = dynamic_cast<GeoDataFeature *>(object)) {
495  bool bValue = value.toBool();
496  if (auto pfolder = geodata_cast<GeoDataFolder>(feature->parent())) {
497  if ( pfolder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder) {
498  if ( bValue ) {
500  for(; i < pfolder->end(); ++i) {
501  (*i)->setVisible( false );
502  }
503  }
504  }
505  }
506  if (auto folder = geodata_cast<GeoDataFolder>(object)) {
507  if ( bValue ) {
508  } else {
509  if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder
510  || folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckOffOnly ) {
512  for(; i < folder->end(); ++i) {
513  (*i)->setVisible( false );
514  }
515  folder->setVisible( false );
516  }
517  }
518  }
519  feature->setVisible( bValue );
520  mDebug() << "setData " << feature->name();
521  updateFeature( feature );
522  return true;
523  }
524  } else if ( role == Qt::EditRole ) {
525  if (auto feature = dynamic_cast<GeoDataFeature *>(object)) {
526  feature->setName( value.toString() );
527  mDebug() << "setData " << feature->name() << " " << value.toString();
528  updateFeature( feature );
529  return true;
530  }
531  }
532 
533  return false;
534 }
535 
536 Qt::ItemFlags GeoDataTreeModel::flags ( const QModelIndex & index ) const
537 {
538  if ( !index.isValid() )
539  return Qt::NoItemFlags;
540 
541  const GeoDataObject *object = static_cast<const GeoDataObject *>(index.internalPointer());
542 
543  if (const auto feature = geodata_cast<GeoDataPlacemark>(object)) {
544  const GeoDataObject *parent = feature->parent();
545 
546  if (const auto parentfolder = geodata_cast<GeoDataFolder>(parent)) {
547  if ( parentfolder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder ) {
549  } else if ( parentfolder->style()->listStyle().listItemType() == GeoDataListStyle::CheckHideChildren ) {
550  return Qt::NoItemFlags;
551  }
552  }
553  }
554 
555  if (const auto folder = geodata_cast<GeoDataFolder>(object)) {
556  if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::RadioFolder) {
558  } else if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckOffOnly ) {
560  bool allVisible = true;
561  for (; i < folder->constEnd(); ++i) {
562  if( ! (*i)->isVisible() ) {
563  allVisible = false;
564  break;
565  }
566  }
567  if ( allVisible ) {
569  } else {
571  }
572  } else if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckHideChildren) {
574  }
575  }
576 
577  if (geodata_cast<GeoDataTour>(object)) {
579  }
580 
581  if (dynamic_cast<const GeoDataFeature *>(object)) {
582  const GeoDataObject *parent = object;
583  while (!geodata_cast<GeoDataDocument>(parent)) {
584  parent = parent->parent();
585  }
586  const GeoDataDocument *document = static_cast<const GeoDataDocument *>(parent);
587  if( document->documentRole() == UserDocument ) {
589  }
590 
592  }
593 
594  if (geodata_cast<GeoDataWait>(object)
595  || geodata_cast<GeoDataFlyTo>(object)
596  || geodata_cast<GeoDataPlaylist>(object)) {
598  }
599 
601 }
602 
603 
604 QModelIndex GeoDataTreeModel::index(const GeoDataObject *object) const
605 {
606  if ( object == nullptr )
607  return QModelIndex();
608 
609  //It first runs bottom-top, storing every ancestor of the object, and
610  //then goes top-down retrieving the QModelIndex of every ancestor until reaching the
611  //index of the requested object.
612  //The TreeModel contains: Documents, Folders, Placemarks, MultiGeometries
613  //and Geometries that are children of MultiGeometries
614  //You can not call this function with an element that does not belong to the tree
615 
616  Q_ASSERT(geodata_cast<GeoDataFolder>(object)
617  || geodata_cast<GeoDataDocument>(object)
618  || geodata_cast<GeoDataPlacemark>(object)
619  || geodata_cast<GeoDataTour>(object)
620  || ( geodata_cast<GeoDataPlaylist>(object)
621  && geodata_cast<GeoDataTour>(object->parent()))
622  || (geodata_cast<GeoDataWait>(object)
623  && geodata_cast<GeoDataPlaylist>(object->parent()))
624  || ( geodata_cast<GeoDataFlyTo>(object)
625  && geodata_cast<GeoDataPlaylist>(object->parent()))
626  || (geodata_cast<GeoDataLineString>(object)
627  && geodata_cast<GeoDataMultiGeometry>(object->parent()))
628  || (geodata_cast<GeoDataLinearRing>(object)
629  && geodata_cast<GeoDataMultiGeometry>(object->parent()))
630  || (geodata_cast<GeoDataPoint>(object)
631  && geodata_cast<GeoDataMultiGeometry>(object->parent()))
632  || (geodata_cast<GeoDataPolygon>(object)
633  && geodata_cast<GeoDataMultiGeometry>(object->parent()))
634  || geodata_cast<GeoDataMultiGeometry>(object));
635 
636 
638 
639  const GeoDataObject *itup = object; //Iterator to reach the top of the GeoDataDocument (bottom-up)
640 
641  while ( itup && ( itup != d->m_rootDocument ) ) {//We reach up to the rootDocument
642 
643  ancestors.append( itup );
644  itup = itup->parent() ;
645  }
646 
647  QModelIndex itdown;
648  if ( !ancestors.isEmpty() ) {
649 
650  itdown = index(d->m_rootDocument->childPosition(static_cast<const GeoDataFeature *>(ancestors.last())), 0, QModelIndex());//Iterator to go top down
651 
652  while ( ( ancestors.size() > 1 ) ) {
653 
654  const GeoDataObject *parent = static_cast<const GeoDataObject*>(ancestors.last());
655 
656  if (const auto container = dynamic_cast<const GeoDataContainer *>(parent)) {
657 
658  ancestors.removeLast();
659  itdown = index(container->childPosition(static_cast<const GeoDataFeature *>(ancestors.last())), 0, itdown);
660  } else if (geodata_cast<GeoDataPlacemark>(parent)) {
661  //The only child of the model is a Geometry or MultiGeometry object
662  //If it is a geometry object, we should be on the bottom of the list
663  ancestors.removeLast();
664  if (geodata_cast<GeoDataMultiGeometry>(ancestors.last()))
665  itdown = index( 0 , 0, itdown );
666  else
667  itdown = QModelIndex();
668 
669  } else if (auto multiGeometry = geodata_cast<GeoDataMultiGeometry>(parent)) {
670  //The child is one of the geometry children of MultiGeometry
671  ancestors.removeLast();
672  itdown = index(multiGeometry->childPosition(static_cast<const GeoDataGeometry *>(ancestors.last())), 0, itdown);
673  } else if (geodata_cast<GeoDataTour>(parent)) {
674  ancestors.removeLast();
675  itdown = index( 0, 0, itdown );
676  } else if (auto playlist = geodata_cast<GeoDataPlaylist>(parent)) {
677  for ( int i=0; i< playlist->size(); i++ )
678  {
679  if ( playlist->primitive(i) == ancestors.last() )
680  {
681  ancestors.removeLast();
682  itdown = index( i, 0, itdown );
683  break;
684  }
685  }
686  }
687  else { //If the element is not found on the tree, it will be added under m_rootDocument
688  itdown = QModelIndex();
689  break;
690  }
691  }
692  }
693  return itdown;
694 }
695 
696 QItemSelectionModel *GeoDataTreeModel::selectionModel()
697 {
698  return &d->m_selectionModel;
699 }
700 
701 int GeoDataTreeModel::addFeature( GeoDataContainer *parent, GeoDataFeature *feature, int row )
702 {
703  if ( parent && feature ) {
704 
705  QModelIndex modelindex = index( parent );
706  //index(GeoDataObject*) returns QModelIndex() if parent == m_rootDocument
707  //or if parent is not found on the tree.
708  //We must check that we are in top of the tree (then QModelIndex() is
709  //the right parent to insert the child object) or that we have a valid QModelIndex
710 
711  if( ( parent == d->m_rootDocument ) || modelindex.isValid() )
712  {
713  if( row < 0 || row > parent->size()) {
714  row = parent->size();
715  }
716  beginInsertRows( modelindex , row , row );
717  parent->insert( row, feature );
718  d->checkParenting( parent );
719  endInsertRows();
720  emit added(feature);
721  }
722  else
723  qWarning() << "GeoDataTreeModel::addFeature (parent " << parent << " - feature" << feature << ") : parent not found on the TreeModel";
724  }
725  else
726  qWarning() << "Null pointer in call to GeoDataTreeModel::addFeature (parent " << parent << " - feature" << feature << ")";
727  return row; //-1 if it failed, the relative index otherwise.
728 }
729 
730 int GeoDataTreeModel::addDocument( GeoDataDocument *document )
731 {
732  return addFeature( d->m_rootDocument, document );
733 }
734 
735 bool GeoDataTreeModel::removeFeature( GeoDataContainer *parent, int row )
736 {
737  if ( row<parent->size() ) {
738  beginRemoveRows( index( parent ), row , row );
739  GeoDataFeature *feature = parent->child( row );
740  parent->remove( row );
741  emit removed(feature);
742  endRemoveRows();
743  return true;
744  }
745  return false; //Tried to remove a row that is not contained in the parent.
746 }
747 
748 int GeoDataTreeModel::removeFeature(GeoDataFeature *feature)
749 {
750  if ( feature && ( feature!=d->m_rootDocument ) ) {
751 
752  if (!feature->parent()) {
753  return -1;
754  }
755 
756  //We check to see we are not removing the
757  //top level element m_rootDocument
758 
759  GeoDataObject *parent = static_cast< GeoDataObject* >( feature->parent() );
760 
761  if (dynamic_cast<const GeoDataContainer *>(parent)) {
762 
763  int row = static_cast< GeoDataContainer* >( feature->parent() )->childPosition( feature );
764  if ( row != -1 ) {
765  bool removed = removeFeature( static_cast< GeoDataContainer* >( feature->parent() ) , row );
766  if( removed ) {
767  return row;
768  }
769  }
770  //The feature is not contained in the parent it points to
771  }
772  }
773  return -1; //We can not remove the rootDocument
774 }
775 
776 void GeoDataTreeModel::updateFeature( GeoDataFeature *feature )
777 {
778  GeoDataContainer *container = static_cast<GeoDataContainer*>( feature->parent() );
779  int index = removeFeature( feature );
780  Q_ASSERT( index != -1 );
781  addFeature( container, feature, index );
782 }
783 
784 void GeoDataTreeModel::removeDocument( int index )
785 {
786  removeFeature( d->m_rootDocument, index );
787 }
788 
789 void GeoDataTreeModel::removeDocument( GeoDataDocument *document )
790 {
791  removeFeature( document );
792 }
793 
795 {
796  beginResetModel();
797  if ( d->m_ownsRootDocument ) {
798  delete d->m_rootDocument;
799  }
800 
801  d->m_ownsRootDocument = ( document == nullptr );
802  d->m_rootDocument = document ? document : new GeoDataDocument;
803  endResetModel();
804 }
805 
806 GeoDataDocument * GeoDataTreeModel::rootDocument()
807 {
808  return d->m_rootDocument;
809 }
810 
811 int GeoDataTreeModel::addTourPrimitive( const QModelIndex &parent, GeoDataTourPrimitive *primitive, int row )
812 {
813  GeoDataObject *parentObject = static_cast<GeoDataObject*>( parent.internalPointer() );
814  if (auto playlist = geodata_cast<GeoDataPlaylist>(parentObject)) {
815  if( row == -1 ) {
816  row = playlist->size();
817  }
818  beginInsertRows( parent, row, row );
819  playlist->insertPrimitive( row, primitive );
820  endInsertRows();
821  return row;
822  }
823  return -1;
824 }
825 
826 bool GeoDataTreeModel::removeTourPrimitive( const QModelIndex &parent , int index)
827 {
828  GeoDataObject *parentObject = static_cast<GeoDataObject*>( parent.internalPointer() );
829  if (auto playlist = (parent.isValid() ? geodata_cast<GeoDataPlaylist>(parentObject) : nullptr)) {
830  if( playlist->size() > index ) {
831  beginRemoveRows( parent, index, index );
832  playlist->removePrimitiveAt( index );
833  endRemoveRows();
834  return true;
835  }
836  }
837  return false;
838 }
839 
840 bool GeoDataTreeModel::swapTourPrimitives( const QModelIndex &parent, int indexA, int indexB )
841 {
842  GeoDataObject *parentObject = static_cast<GeoDataObject*>( parent.internalPointer() );
843  if (auto playlist = (parent.isValid() ? geodata_cast<GeoDataPlaylist>(parentObject) : nullptr)) {
844  if( indexA > indexB ) {
845  qSwap(indexA, indexB);
846  }
847  if ( indexB - indexA == 1 ) {
848  beginMoveRows( parent, indexA, indexA, parent, indexB+1 );
849  } else {
850  beginMoveRows( parent, indexA, indexA, parent, indexB );
851  beginMoveRows( parent, indexB, indexB, parent, indexA );
852  }
853  playlist->swapPrimitives( indexA, indexB );
854  if( indexB - indexA != 1 ) {
855  endMoveRows();
856  }
857  endMoveRows();
858  return true;
859  }
860  return false;
861 }
862 
863 #include "moc_GeoDataTreeModel.cpp"
void append(const T &value)
@ GeoTypeRole
The geo type (e.g. city or mountain)
@ GeometryRole
The GeoDataGeometry geometry.
DisplayRole
~GeoDataTreeModel() override
Destroys the GeoDataModel.
Q_SCRIPTABLE QString camera()
void beginRemoveRows(const QModelIndex &parent, int first, int last)
virtual const char * nodeType() const =0
Provides type information for downcasting a GeoNode.
@ LatitudeRole
The latitude in degree (for use in QML)
QVariant fromValue(const T &value)
void * internalPointer() const const
QVector::iterator begin()
@ PopularityIndexRole
The popularity index.
int column() const const
QVector::const_iterator constEnd() const const
A base class for all geodata features.
GeoDataTreeModel(QObject *parent=nullptr)
Creates a new GeoDataTreeModel.
bool hasIndex(int row, int column, const QModelIndex &parent) const const
void beginInsertRows(const QModelIndex &parent, int first, int last)
void removeLast()
bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild)
@ GmtRole
The Greenwich Mean Time.
@ ObjectPointerRole
The pointer to a specific object.
int size() const const
virtual QHash< int, QByteArray > roleNames() const const
QModelIndex createIndex(int row, int column, void *ptr) const const
KeepAspectRatio
typedef ItemFlags
QHash::const_iterator constBegin() const const
QHash::const_iterator constEnd() const const
A base class for all geodata features.
A base class for all geodata objects.
Definition: GeoDataObject.h:43
Orientation
A container for Features, Styles and in the future Schemas.
The representation of GeoData in a model This class represents all available data given by kml-data f...
@ IconPathRole
Path to the image, if known.
bool isEmpty() const const
@ CoordinateRole
The GeoDataCoordinates coordinate.
Binds a QML item to a specific geodetic location in screen coordinates.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Return the number of Items in the Model.
const GeoDataObject * parent() const
Provides the parent of the object in GeoDataContainers.
bool isValid() const const
void setRootDocument(GeoDataDocument *document)
Sets the root document to use.
bool toBool() const const
T & last()
static ScriptableExtension * childObject(QObject *obj)
QVector::iterator end()
A base class that can hold GeoDataFeatures.
int size() const const
void removed(GeoDataObject *object)
insert and remove row don't trigger any signal that proxies forward this signal will refresh geometry...
@ DstRole
The Daylight Saving Time.
QVector::const_iterator constBegin() const const
@ LongitudeRole
The longitude in degree (for use in QML)
SmoothTransformation
QString tr(const char *sourceText, const char *disambiguation, int n)
A class that can contain other GeoDataGeometry objects.
QObject * parent() const const
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Oct 2 2023 03:52:08 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.