1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-17 13:51:47 -05:00
sdrangel/plugins/feature/map/mapgui.h
2021-01-22 15:22:47 +00:00

377 lines
12 KiB
C++

///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE //
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_MAPGUI_H_
#define INCLUDE_FEATURE_MAPGUI_H_
#include <QTimer>
#include <QAbstractListModel>
#include <QGeoCoordinate>
#include "feature/featuregui.h"
#include "util/messagequeue.h"
#include "util/azel.h"
#include "pipes/pipeendpoint.h"
#include "mapsettings.h"
#include "SWGMapItem.h"
#include "mapbeacondialog.h"
class PluginAPI;
class FeatureUISet;
class Map;
namespace Ui {
class MapGUI;
}
class MapGUI;
class MapModel;
struct Beacon;
// Information required about each item displayed on the map
class MapItem {
public:
MapItem(const PipeEndPoint *sourcePipe, quint32 sourceMask, SWGSDRangel::SWGMapItem *mapItem)
{
m_sourcePipe = sourcePipe;
m_sourceMask = sourceMask;
m_name = *mapItem->getName();
m_latitude = mapItem->getLatitude();
m_longitude = mapItem->getLongitude();
m_altitude = mapItem->getAltitude();
m_image = *mapItem->getImage();
m_imageRotation = mapItem->getImageRotation();
m_imageMinZoom = mapItem->getImageMinZoom();
QString *text = mapItem->getText();
if (text != nullptr)
m_text = *text;
findFrequency();
}
void update(SWGSDRangel::SWGMapItem *mapItem)
{
m_latitude = mapItem->getLatitude();
m_longitude = mapItem->getLongitude();
m_altitude = mapItem->getAltitude();
m_image = *mapItem->getImage();
m_imageRotation = mapItem->getImageRotation();
m_imageMinZoom = mapItem->getImageMinZoom();
QString *text = mapItem->getText();
if (text != nullptr)
m_text = *text;
findFrequency();
}
QGeoCoordinate getCoordinates()
{
QGeoCoordinate coords;
coords.setLatitude(m_latitude);
coords.setLongitude(m_longitude);
return coords;
}
private:
void findFrequency();
friend MapModel;
const PipeEndPoint *m_sourcePipe; // Channel/feature that created the item
quint32 m_sourceMask; // Source bitmask as per MapSettings::SOURCE_* constants
QString m_name;
float m_latitude;
float m_longitude;
float m_altitude; // In metres
QString m_image;
int m_imageRotation;
int m_imageMinZoom;
QString m_text;
double m_frequency; // Frequency to set
QString m_frequencyString;
};
// Model used for each item on the map
class MapModel : public QAbstractListModel {
Q_OBJECT
public:
using QAbstractListModel::QAbstractListModel;
enum MarkerRoles {
positionRole = Qt::UserRole + 1,
mapTextRole = Qt::UserRole + 2,
mapTextVisibleRole = Qt::UserRole + 3,
mapImageVisibleRole = Qt::UserRole + 4,
mapImageRole = Qt::UserRole + 5,
mapImageRotationRole = Qt::UserRole + 6,
mapImageMinZoomRole = Qt::UserRole + 7,
bubbleColourRole = Qt::UserRole + 8,
selectedRole = Qt::UserRole + 9,
targetRole = Qt::UserRole + 10,
frequencyRole = Qt::UserRole + 11,
frequencyStringRole = Qt::UserRole + 12
};
MapModel(MapGUI *gui) :
m_gui(gui),
m_target(-1),
m_sources(-1)
{
}
Q_INVOKABLE void add(MapItem *item)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_items.append(item);
m_selected.append(false);
endInsertRows();
}
void update(const PipeEndPoint *source, SWGSDRangel::SWGMapItem *swgMapItem, quint32 sourceMask=0);
void updateTarget();
void update(MapItem *item)
{
int row = m_items.indexOf(item);
if (row >= 0)
{
QModelIndex idx = index(row);
emit dataChanged(idx, idx);
if (row == m_target)
updateTarget();
}
}
void remove(MapItem *item)
{
int row = m_items.indexOf(item);
if (row >= 0)
{
beginRemoveRows(QModelIndex(), row, row);
m_items.removeAt(row);
m_selected.removeAt(row);
if (row == m_target)
m_target = -1;
endRemoveRows();
}
}
Q_INVOKABLE void moveToFront(int oldRow)
{
// Last item in list is drawn on top, so remove than add to end of list
if (oldRow < m_items.size() - 1)
{
bool wasTarget = m_target == oldRow;
MapItem *item = m_items[oldRow];
bool wasSelected = m_selected[oldRow];
remove(item);
add(item);
int newRow = m_items.size() - 1;
if (wasTarget)
m_target = newRow;
m_selected[newRow] = wasSelected;
QModelIndex idx = index(newRow);
emit dataChanged(idx, idx);
}
}
Q_INVOKABLE void moveToBack(int oldRow)
{
// First item in list is drawn first, so remove item then add to front of list
if ((oldRow < m_items.size()) && (oldRow > 0))
{
bool wasTarget = m_target == oldRow;
int newRow = 0;
// See: https://forum.qt.io/topic/122991/changing-the-order-mapquickitems-are-drawn-on-a-map
//QModelIndex parent;
//beginMoveRows(parent, oldRow, oldRow, parent, newRow);
beginResetModel();
m_items.move(oldRow, newRow);
m_selected.move(oldRow, newRow);
if (wasTarget)
m_target = newRow;
//endMoveRows();
endResetModel();
//emit dataChanged(index(oldRow), index(newRow));
}
}
MapItem *findMapItem(const PipeEndPoint *source, const QString& name)
{
// FIXME: Should consider adding a QHash for this
QListIterator<MapItem *> i(m_items);
while (i.hasNext())
{
MapItem *item = i.next();
if ((item->m_name == name) && (item->m_sourcePipe == source))
return item;
}
return nullptr;
}
MapItem *findMapItem(const QString& name)
{
QListIterator<MapItem *> i(m_items);
while (i.hasNext())
{
MapItem *item = i.next();
if (item->m_name == name)
return item;
}
return nullptr;
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
Q_UNUSED(parent)
return m_items.count();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant& value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override
{
(void) index;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
void allUpdated()
{
for (int i = 0; i < m_items.count(); i++)
{
QModelIndex idx = index(i);
emit dataChanged(idx, idx);
}
}
void removeAll()
{
beginRemoveRows(QModelIndex(), 0, m_items.count());
m_items.clear();
m_selected.clear();
endRemoveRows();
}
void setDisplayNames(bool displayNames)
{
m_displayNames = displayNames;
allUpdated();
}
Q_INVOKABLE void setFrequency(double frequency);
QHash<int, QByteArray> roleNames() const
{
QHash<int, QByteArray> roles;
roles[positionRole] = "position";
roles[mapTextRole] = "mapText";
roles[mapTextVisibleRole] = "mapTextVisible";
roles[mapImageVisibleRole] = "mapImageVisible";
roles[mapImageRole] = "mapImage";
roles[mapImageRotationRole] = "mapImageRotation";
roles[mapImageMinZoomRole] = "mapImageMinZoom";
roles[bubbleColourRole] = "bubbleColour";
roles[selectedRole] = "selected";
roles[targetRole] = "target";
roles[frequencyRole] = "frequency";
roles[frequencyStringRole] = "frequencyString";
return roles;
}
// Set the sources of data we should display
void setSources(quint32 sources)
{
m_sources = sources;
allUpdated();
}
private:
MapGUI *m_gui;
QList<MapItem *> m_items;
QList<bool> m_selected;
int m_target; // Row number of current target, or -1 for none
bool m_displayNames;
quint32 m_sources;
};
class MapGUI : public FeatureGUI {
Q_OBJECT
public:
static MapGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
AzEl *getAzEl() { return &m_azEl; }
Map *getMap() { return m_map; }
quint32 getSourceMask(const PipeEndPoint *sourcePipe);
static QString getBeaconFilename();
QList<Beacon *> *getBeacons() { return m_beacons; }
void setBeacons(QList<Beacon *> *beacons);
void find(const QString& target);
private:
Ui::MapGUI* ui;
PluginAPI* m_pluginAPI;
FeatureUISet* m_featureUISet;
MapSettings m_settings;
bool m_doApplySettings;
QList<PipeEndPoint::AvailablePipeSource> m_availablePipes;
Map* m_map;
MessageQueue m_inputMessageQueue;
MapModel m_mapModel;
AzEl m_azEl; // Position of station
QList<Beacon *> *m_beacons;
MapBeaconDialog m_beaconDialog;
explicit MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
virtual ~MapGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void applyMapSettings();
void displaySettings();
bool handleMessage(const Message& message);
void geoReply();
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
static QString getDataDir();
private slots:
void onMenuDialogCalled(const QPoint &p);
void onWidgetRolled(QWidget* widget, bool rollDown);
void handleInputMessages();
void on_displayNames_clicked(bool checked=false);
void on_find_returnPressed();
void on_maidenhead_clicked();
void on_deleteAll_clicked();
void on_displaySettings_clicked();
void on_mapTypes_currentIndexChanged(int index);
void on_beacons_clicked();
};
#endif // INCLUDE_FEATURE_MAPGUI_H_