| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | // SPDX-License-Identifier: GPL-2.0
 | 
					
						
							|  |  |  | #include "maplocationmodel.h"
 | 
					
						
							| 
									
										
										
										
											2019-05-09 21:33:01 +02:00
										 |  |  | #include "divelocationmodel.h"
 | 
					
						
							| 
									
										
										
										
											2018-10-26 17:03:54 +02:00
										 |  |  | #include "core/divesite.h"
 | 
					
						
							| 
									
										
										
										
											2019-11-17 18:13:55 +01:00
										 |  |  | #include "core/divefilter.h"
 | 
					
						
							| 
									
										
										
										
											2020-11-14 19:21:16 -08:00
										 |  |  | #if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER)
 | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | #include "qt-models/filtermodels.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-30 15:25:59 +02:00
										 |  |  | #include "desktop-widgets/mapwidget.h"
 | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | #define MIN_DISTANCE_BETWEEN_DIVE_SITES_M 50.0
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | MapLocation::MapLocation(struct dive_site *dsIn, QGeoCoordinate coordIn, QString nameIn, bool selectedIn) : | 
					
						
							|  |  |  |     divesite(dsIn), coordinate(coordIn), name(nameIn), selected(selectedIn) | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 15:25:59 +02:00
										 |  |  | // Check whether we are in divesite-edit mode. This doesn't
 | 
					
						
							|  |  |  | // exist on mobile. And on desktop we have to access the MapWidget.
 | 
					
						
							|  |  |  | // Simplify this!
 | 
					
						
							|  |  |  | static bool inEditMode() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-14 19:21:16 -08:00
										 |  |  | #if defined(SUBSURFACE_MOBILE) || defined(SUBSURFACE_DOWNLOADER)
 | 
					
						
							| 
									
										
										
										
											2019-08-30 15:25:59 +02:00
										 |  |  | 	return false; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 	return MapWidget::instance()->editMode(); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | QVariant MapLocation::getRole(int role) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (role) { | 
					
						
							| 
									
										
										
										
											2019-09-01 16:38:48 +02:00
										 |  |  | 	case RoleDivesite: | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		return QVariant::fromValue(divesite); | 
					
						
							| 
									
										
										
										
											2019-09-01 16:38:48 +02:00
										 |  |  | 	case RoleCoordinate: | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		return QVariant::fromValue(coordinate); | 
					
						
							| 
									
										
										
										
											2019-09-01 16:38:48 +02:00
										 |  |  | 	case RoleName: | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		return QVariant::fromValue(name); | 
					
						
							| 
									
										
										
										
											2019-09-01 16:38:48 +02:00
										 |  |  | 	case RolePixmap: | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		return selected ? QString("qrc:///dive-location-marker-selected-icon") : | 
					
						
							| 
									
										
										
										
											2019-08-30 15:25:59 +02:00
										 |  |  | 		       inEditMode() ? QString("qrc:///dive-location-marker-inactive-icon") : | 
					
						
							|  |  |  | 				    QString("qrc:///dive-location-marker-icon"); | 
					
						
							| 
									
										
										
										
											2019-09-01 16:38:48 +02:00
										 |  |  | 	case RoleZ: | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		return selected ? 1 : 0; | 
					
						
							| 
									
										
										
										
											2019-09-01 16:38:48 +02:00
										 |  |  | 	case RoleIsSelected: | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		return QVariant::fromValue(selected); | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		return QVariant(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-02 00:09:59 +02:00
										 |  |  | MapLocationModel::MapLocationModel(QObject *parent) : QAbstractListModel(parent) | 
					
						
							| 
									
										
										
										
											2018-10-26 17:03:54 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-09 21:33:01 +02:00
										 |  |  | 	connect(&diveListNotifier, &DiveListNotifier::diveSiteChanged, this, &MapLocationModel::diveSiteChanged); | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MapLocationModel::~MapLocationModel() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 	qDeleteAll(m_mapLocations); | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QVariant MapLocationModel::data(const QModelIndex & index, int role) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (index.row() < 0 || index.row() >= m_mapLocations.size()) | 
					
						
							|  |  |  | 		return QVariant(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return m_mapLocations.at(index.row())->getRole(role); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QHash<int, QByteArray> MapLocationModel::roleNames() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-08-31 23:17:04 +02:00
										 |  |  | 	QHash<int, QByteArray> roles; | 
					
						
							| 
									
										
										
										
											2019-09-01 16:38:48 +02:00
										 |  |  | 	roles[MapLocation::RoleDivesite] = "divesite"; | 
					
						
							|  |  |  | 	roles[MapLocation::RoleCoordinate] = "coordinate"; | 
					
						
							|  |  |  | 	roles[MapLocation::RoleName] = "name"; | 
					
						
							|  |  |  | 	roles[MapLocation::RolePixmap] = "pixmap"; | 
					
						
							|  |  |  | 	roles[MapLocation::RoleZ] = "z"; | 
					
						
							|  |  |  | 	roles[MapLocation::RoleIsSelected] = "isSelected"; | 
					
						
							| 
									
										
										
										
											2019-08-31 23:17:04 +02:00
										 |  |  | 	return roles; | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 17:53:42 +02:00
										 |  |  | int MapLocationModel::rowCount(const QModelIndex&) const | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	return m_mapLocations.size(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void MapLocationModel::add(MapLocation *location) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	beginInsertRows(QModelIndex(), m_mapLocations.size(), m_mapLocations.size()); | 
					
						
							|  |  |  | 	m_mapLocations.append(location); | 
					
						
							|  |  |  | 	endInsertRows(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-03 23:16:40 +02:00
										 |  |  | const QVector<dive_site *> &MapLocationModel::selectedDs() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return m_selectedDs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-08 21:35:49 +02:00
										 |  |  | static bool hasVisibleDive(const dive_site *ds) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return std::any_of(&ds->dives.dives[0], &ds->dives.dives[ds->dives.nr], | 
					
						
							|  |  |  | 			   [] (const dive *d) { return !d->hidden_by_filter; }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool hasSelectedDive(const dive_site *ds) | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-08 21:35:49 +02:00
										 |  |  | 	return std::any_of(&ds->dives.dives[0], &ds->dives.dives[ds->dives.nr], | 
					
						
							|  |  |  | 			   [] (const dive *d) { return d->selected; }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 16:51:59 +02:00
										 |  |  | void MapLocationModel::selectionChanged() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (m_mapLocations.isEmpty()) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	for(MapLocation *m: m_mapLocations) | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		m->selected = m_selectedDs.contains(m->divesite); | 
					
						
							| 
									
										
										
										
											2019-08-30 16:51:59 +02:00
										 |  |  | 	emit dataChanged(createIndex(0, 0), createIndex(m_mapLocations.size() - 1, 0)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-08 22:15:01 +02:00
										 |  |  | void MapLocationModel::reload(QObject *map) | 
					
						
							| 
									
										
										
										
											2019-05-08 21:35:49 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 	beginResetModel(); | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	qDeleteAll(m_mapLocations); | 
					
						
							|  |  |  | 	m_mapLocations.clear(); | 
					
						
							| 
									
										
										
										
											2019-05-02 00:09:59 +02:00
										 |  |  | 	m_selectedDs.clear(); | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	QMap<QString, MapLocation *> locationNameMap; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-14 19:21:16 -08:00
										 |  |  | #if defined(SUBSURFACE_MOBILE) || defined(SUBSURFACE_DOWNLOADER)
 | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 	bool diveSiteMode = false; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 	// In dive site mode (that is when either editing a dive site or on
 | 
					
						
							|  |  |  | 	// the dive site tab), we want to show all dive sites, not only those
 | 
					
						
							| 
									
										
										
										
											2019-05-03 23:37:42 +02:00
										 |  |  | 	// of the non-hidden dives. Moreover, the selected dive sites are those
 | 
					
						
							|  |  |  | 	// that we filter for.
 | 
					
						
							| 
									
										
										
										
											2019-11-17 18:13:55 +01:00
										 |  |  | 	bool diveSiteMode = DiveFilter::instance()->diveSiteMode(); | 
					
						
							| 
									
										
										
										
											2019-05-03 23:37:42 +02:00
										 |  |  | 	if (diveSiteMode) | 
					
						
							| 
									
										
										
										
											2019-11-17 18:13:55 +01:00
										 |  |  | 		m_selectedDs = DiveFilter::instance()->filteredDiveSites(); | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-05-08 21:35:49 +02:00
										 |  |  | 	for (int i = 0; i < dive_site_table.nr; ++i) { | 
					
						
							|  |  |  | 		struct dive_site *ds = dive_site_table.dive_sites[i]; | 
					
						
							| 
									
										
										
										
											2019-05-08 22:15:01 +02:00
										 |  |  | 		QGeoCoordinate dsCoord; | 
					
						
							| 
									
										
										
										
											2019-05-08 21:35:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Don't show dive sites of hidden dives, unless we're in dive site edit mode.
 | 
					
						
							|  |  |  | 		if (!diveSiteMode && !hasVisibleDive(ds)) | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2019-05-08 22:15:01 +02:00
										 |  |  | 		if (!dive_site_has_gps_location(ds)) { | 
					
						
							|  |  |  | 			// Dive sites that do not have a gps location are not shown in normal mode.
 | 
					
						
							|  |  |  | 			// In dive-edit mode, selected sites are placed at the center of the map,
 | 
					
						
							|  |  |  | 			// so that the user can drag them somewhere without having to enter coordinates.
 | 
					
						
							|  |  |  | 			if (!diveSiteMode || !m_selectedDs.contains(ds) || !map) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			dsCoord = map->property("center").value<QGeoCoordinate>(); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			qreal latitude = ds->location.lat.udeg * 0.000001; | 
					
						
							|  |  |  | 			qreal longitude = ds->location.lon.udeg * 0.000001; | 
					
						
							|  |  |  | 			dsCoord = QGeoCoordinate(latitude, longitude); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-08 21:35:49 +02:00
										 |  |  | 		if (!diveSiteMode && hasSelectedDive(ds) && !m_selectedDs.contains(ds)) | 
					
						
							| 
									
										
										
										
											2019-05-02 00:09:59 +02:00
										 |  |  | 			m_selectedDs.append(ds); | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 		QString name(ds->name); | 
					
						
							| 
									
										
										
										
											2019-05-08 21:35:49 +02:00
										 |  |  | 		if (!diveSiteMode) { | 
					
						
							|  |  |  | 			// don't add dive locations with the same name, unless they are
 | 
					
						
							|  |  |  | 			// at least MIN_DISTANCE_BETWEEN_DIVE_SITES_M apart
 | 
					
						
							|  |  |  | 			if (locationNameMap.contains(name)) { | 
					
						
							|  |  |  | 				MapLocation *existingLocation = locationNameMap[name]; | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 				QGeoCoordinate coord = existingLocation->coordinate; | 
					
						
							| 
									
										
										
										
											2019-05-08 21:35:49 +02:00
										 |  |  | 				if (dsCoord.distanceTo(coord) < MIN_DISTANCE_BETWEEN_DIVE_SITES_M) | 
					
						
							|  |  |  | 					continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-30 15:25:59 +02:00
										 |  |  | 		bool selected = m_selectedDs.contains(ds); | 
					
						
							|  |  |  | 		MapLocation *location = new MapLocation(ds, dsCoord, name, selected); | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 		m_mapLocations.append(location); | 
					
						
							| 
									
										
										
										
											2019-07-27 22:30:09 +02:00
										 |  |  | 		if (!diveSiteMode) | 
					
						
							|  |  |  | 			locationNameMap[name] = location; | 
					
						
							| 
									
										
										
										
											2019-05-01 23:33:45 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	endResetModel(); | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 12:38:25 +02:00
										 |  |  | void MapLocationModel::setSelected(struct dive_site *ds) | 
					
						
							| 
									
										
										
										
											2018-10-26 17:03:54 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-02 00:09:59 +02:00
										 |  |  | 	m_selectedDs.clear(); | 
					
						
							| 
									
										
										
										
											2019-08-30 12:38:25 +02:00
										 |  |  | 	if (ds) | 
					
						
							|  |  |  | 		m_selectedDs.append(ds); | 
					
						
							| 
									
										
										
										
											2018-10-26 17:03:54 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 17:38:54 +02:00
										 |  |  | void MapLocationModel::setSelected(const QVector<dive_site *> &divesites) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	m_selectedDs = divesites; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-26 17:03:54 +02:00
										 |  |  | MapLocation *MapLocationModel::getMapLocation(const struct dive_site *ds) | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	MapLocation *location; | 
					
						
							|  |  |  | 	foreach(location, m_mapLocations) { | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		if (ds == location->divesite) | 
					
						
							| 
									
										
										
										
											2017-07-28 22:01:33 -07:00
										 |  |  | 			return location; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-11-09 18:43:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-09 21:33:01 +02:00
										 |  |  | void MapLocationModel::diveSiteChanged(struct dive_site *ds, int field) | 
					
						
							| 
									
										
										
										
											2017-11-09 18:43:21 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-09 21:33:01 +02:00
										 |  |  | 	// Find dive site
 | 
					
						
							|  |  |  | 	int row; | 
					
						
							|  |  |  | 	for (row = 0; row < m_mapLocations.size(); ++row) { | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		if (m_mapLocations[row]->divesite == ds) | 
					
						
							| 
									
										
										
										
											2019-05-09 21:33:01 +02:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 	if (row == m_mapLocations.size()) | 
					
						
							| 
									
										
										
										
											2019-05-09 21:33:01 +02:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (field) { | 
					
						
							|  |  |  | 	case LocationInformationModel::LOCATION: | 
					
						
							|  |  |  | 		if (has_location(&ds->location)) { | 
					
						
							|  |  |  | 			const qreal latitude_r = ds->location.lat.udeg * 0.000001; | 
					
						
							|  |  |  | 			const qreal longitude_r = ds->location.lon.udeg * 0.000001; | 
					
						
							|  |  |  | 			QGeoCoordinate coord(latitude_r, longitude_r); | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 			m_mapLocations[row]->coordinate = coord; | 
					
						
							| 
									
										
										
										
											2017-11-09 18:43:21 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-09 21:33:01 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case LocationInformationModel::NAME: | 
					
						
							| 
									
										
										
										
											2019-09-01 00:18:15 +02:00
										 |  |  | 		m_mapLocations[row]->name = ds->name; | 
					
						
							| 
									
										
										
										
											2019-05-09 21:33:01 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2017-11-09 18:43:21 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-09 21:33:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	emit dataChanged(createIndex(row, 0), createIndex(row, 0)); | 
					
						
							| 
									
										
										
										
											2017-11-09 18:43:21 +02:00
										 |  |  | } |