Marble

GeoDataTreeModel.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2010 Thibaut Gridel <tgridel@free.fr>
4// SPDX-FileCopyrightText: 2013 Levente Kurusa <levex@linux.com>
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
41using namespace Marble;
42
43class 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
56GeoDataTreeModel::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
81GeoDataTreeModel::Private::~Private()
82{
83 if ( m_ownsRootDocument ) {
84 delete m_rootDocument;
85 }
86}
87
88void 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
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
115int 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
174QVariant 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
193QHash<int, QByteArray> GeoDataTreeModel::roleNames() const
194{
195 return d->m_roleNames;
196}
197
198QVariant 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;
278 QVector<GeoDataFeature *>::Iterator i = folder->begin();
279 for (; i < folder->end(); ++i) {
280 if ((*i)->isVisible()) {
281 anyVisible = true;
282 break;
283 }
284 }
285 if (anyVisible) {
287 } else {
288 return QVariant( Qt::Unchecked );
289 }
290 } else if ( folder->style()->listStyle().listItemType() == GeoDataListStyle::CheckOffOnly) {
291 QVector<GeoDataFeature *>::Iterator i = folder->begin();
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) {
305 } else {
306 return QVariant( Qt::Unchecked );
307 }
308 }
309 }
310 if ( feature->isGloballyVisible() ) {
311 return QVariant( Qt::Checked );
312 } else if ( feature->isVisible() ) {
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
368QModelIndex 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
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
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
473 return createIndex( 0, 0, tour->playlist() );
474 }
475
476 }
477
478// mDebug() << "parent unknown index";
479 return QModelIndex();
480}
481
482int GeoDataTreeModel::columnCount( const QModelIndex & ) const
483{
484 return 4;
485}
486
487bool 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
536Qt::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
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 ) {
559 QVector<GeoDataFeature *>::ConstIterator i = folder->constBegin();
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;
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)
598 }
599
601}
602
603
604QModelIndex 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
621 && geodata_cast<GeoDataTour>(object->parent()))
622 || (geodata_cast<GeoDataWait>(object)
624 || ( geodata_cast<GeoDataFlyTo>(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
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);
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();
665 itdown = index( 0 , 0, itdown );
666 else
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
689 break;
690 }
691 }
692 }
693 return itdown;
694}
695
696QItemSelectionModel *GeoDataTreeModel::selectionModel()
697{
698 return &d->m_selectionModel;
699}
700
701int 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 );
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
730int GeoDataTreeModel::addDocument( GeoDataDocument *document )
731{
732 return addFeature( d->m_rootDocument, document );
733}
734
735bool 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);
743 return true;
744 }
745 return false; //Tried to remove a row that is not contained in the parent.
746}
747
748int 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
776void 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
784void GeoDataTreeModel::removeDocument( int index )
785{
786 removeFeature( d->m_rootDocument, index );
787}
788
789void GeoDataTreeModel::removeDocument( GeoDataDocument *document )
790{
791 removeFeature( document );
792}
793
795{
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;
804}
805
806GeoDataDocument * GeoDataTreeModel::rootDocument()
807{
808 return d->m_rootDocument;
809}
810
811int 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 );
821 return row;
822 }
823 return -1;
824}
825
826bool 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 );
834 return true;
835 }
836 }
837 return false;
838}
839
840bool 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 ) {
846 }
847 if ( indexB - indexA == 1 ) {
849 } else {
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"
A base class that can hold GeoDataFeatures.
int childPosition(const GeoDataFeature *child) const
returns the position of an item in the list
A container for Features, Styles and in the future Schemas.
A base class for all geodata features.
A base class for all geodata features.
A class that can contain other GeoDataGeometry objects.
A base class for all geodata objects.
const GeoDataObject * parent() const
Provides the parent of the object in GeoDataContainers.
The representation of GeoData in a model This class represents all available data given by kml-data f...
GeoDataTreeModel(QObject *parent=nullptr)
Creates a new GeoDataTreeModel.
void removed(GeoDataObject *object)
insert and remove row don't trigger any signal that proxies forward this signal will refresh geometry...
void setRootDocument(GeoDataDocument *document)
Sets the root document to use.
~GeoDataTreeModel() override
Destroys the GeoDataModel.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Return the number of Items in the Model.
virtual const char * nodeType() const =0
Provides type information for downcasting a GeoNode.
@ GmtRole
The Greenwich Mean Time.
@ LongitudeRole
The longitude in degree (for use in QML)
@ PopularityIndexRole
The popularity index.
@ DstRole
The Daylight Saving Time.
@ LatitudeRole
The latitude in degree (for use in QML)
@ ObjectPointerRole
The pointer to a specific object.
@ GeoTypeRole
The geo type (e.g. city or mountain)
@ GeometryRole
The GeoDataGeometry geometry.
@ CoordinateRole
The GeoDataCoordinates coordinate.
@ IconPathRole
Path to the image, if known.
Q_SCRIPTABLE QString camera()
Binds a QML item to a specific geodetic location in screen coordinates.
void beginInsertRows(const QModelIndex &parent, int first, int last)
bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild)
void beginRemoveRows(const QModelIndex &parent, int first, int last)
QModelIndex createIndex(int row, int column, const void *ptr) const const
bool hasIndex(int row, int column, const QModelIndex &parent) const const
virtual QHash< int, QByteArray > roleNames() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
int column() const const
void * internalPointer() const const
bool isValid() const const
QObject * parent() const const
T qobject_cast(QObject *object)
QString tr(const char *sourceText, const char *disambiguation, int n)
KeepAspectRatio
DisplayRole
typedef ItemFlags
Orientation
SmoothTransformation
QVariant fromValue(T &&value)
bool toBool() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.