/////////////////////////////////////////////////////////////////////////////////// // 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 . // /////////////////////////////////////////////////////////////////////////////////// #ifndef INCLUDE_FEATURE_MAPGUI_H_ #define INCLUDE_FEATURE_MAPGUI_H_ #include #include #include #include "feature/featuregui.h" #include "util/messagequeue.h" #include "pipes/pipeendpoint.h" #include "mapsettings.h" #include "SWGMapItem.h" class PluginAPI; class FeatureUISet; class Map; namespace Ui { class MapGUI; } class MapGUI; class MapModel; // Information required about each item displayed on the map class MapItem { public: MapItem(const PipeEndPoint *source, SWGSDRangel::SWGMapItem *mapItem) { m_source = source; m_name = *mapItem->getName(); m_latitude = mapItem->getLatitude(); m_longitude = mapItem->getLongitude(); m_image = *mapItem->getImage(); m_imageRotation = mapItem->getImageRotation(); m_imageFixedSize = mapItem->getImageFixedSize() == 1; m_text = *mapItem->getText(); } void update(SWGSDRangel::SWGMapItem *mapItem) { m_latitude = mapItem->getLatitude(); m_longitude = mapItem->getLongitude(); m_image = *mapItem->getImage(); m_imageRotation = mapItem->getImageRotation(); m_imageFixedSize = mapItem->getImageFixedSize() == 1; m_text = *mapItem->getText(); } QGeoCoordinate getCoordinates() { QGeoCoordinate coords; coords.setLatitude(m_latitude); coords.setLongitude(m_longitude); return coords; } private: friend MapModel; const PipeEndPoint *m_source; // Channel/feature that created the item QString m_name; float m_latitude; float m_longitude; QString m_image; int m_imageRotation; bool m_imageFixedSize; // Keep image same size when map is zoomed QString m_text; }; // 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, mapImageRole = Qt::UserRole + 4, mapImageRotationRole = Qt::UserRole + 5, mapImageFixedSizeRole = Qt::UserRole + 6, bubbleColourRole = Qt::UserRole + 7, selectedRole = Qt::UserRole + 8 }; MapModel(MapGUI *gui) : m_gui(gui) { } 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) { QString name = *swgMapItem->getName(); // Add, update or delete and item MapItem *item = findMapItem(source, name); if (item != nullptr) { QString image = *swgMapItem->getImage(); if (image.isEmpty()) { // Delete the item remove(item); } else { // Update the item item->update(swgMapItem); update(item); } } else { // Make sure not a duplicate request to delete QString image = *swgMapItem->getImage(); if (!image.isEmpty()) { // Add new item add(new MapItem(source, swgMapItem)); } } } void update(MapItem *item) { int row = m_items.indexOf(item); if (row >= 0) { QModelIndex idx = index(row); emit dataChanged(idx, idx); } } 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); endRemoveRows(); } } MapItem *findMapItem(const PipeEndPoint *source, const QString& name) { // FIXME: Should consider adding a QHash for this QListIterator i(m_items); while (i.hasNext()) { MapItem *item = i.next(); if ((item->m_name == name) && (item->m_source == source)) return item; } return nullptr; } MapItem *findMapItem(const QString& name) { QListIterator 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(); } QHash roleNames() const { QHash roles; roles[positionRole] = "position"; roles[mapTextRole] = "mapText"; roles[mapTextVisibleRole] = "mapTextVisible"; roles[mapImageRole] = "mapImage"; roles[mapImageRotationRole] = "mapImageRotation"; roles[mapImageFixedSizeRole] = "mapImageFixedSize"; roles[bubbleColourRole] = "bubbleColour"; roles[selectedRole] = "selected"; return roles; } private: MapGUI *m_gui; QList m_items; QList m_selected; bool m_displayNames; }; 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; } private: Ui::MapGUI* ui; PluginAPI* m_pluginAPI; FeatureUISet* m_featureUISet; MapSettings m_settings; bool m_doApplySettings; QList m_availablePipes; Map* m_map; MessageQueue m_inputMessageQueue; MapModel m_mapModel; explicit MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); virtual ~MapGUI(); void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); void updatePipeList(); bool handleMessage(const Message& message); void find(const QString& target); void leaveEvent(QEvent*); void enterEvent(QEvent*); 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_deleteAll_clicked(); }; #endif // INCLUDE_FEATURE_MAPGUI_H_