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

KDE's Doxygen guidelines are available online.