Merge pull request #17 from guh/improve-connect-dialog
improve discovery and connecttion
This commit is contained in:
commit
bd86599c16
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
|
||||
* Copyright (C) 2018 Michael Zanetti <michael.zanetti@guh.io> *
|
||||
* *
|
||||
* This file is part of mea. *
|
||||
* *
|
||||
@ -22,38 +22,19 @@
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
DiscoveryDevice::DiscoveryDevice()
|
||||
DiscoveryDevice::DiscoveryDevice(QObject *parent): QObject(parent)
|
||||
{
|
||||
m_portConfigs = new PortConfigs(this);
|
||||
}
|
||||
|
||||
QUrl DiscoveryDevice::location() const
|
||||
QUuid DiscoveryDevice::uuid() const
|
||||
{
|
||||
return m_location;
|
||||
return m_uuid;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setLocation(const QUrl &location)
|
||||
void DiscoveryDevice::setUuid(const QUuid &uuid)
|
||||
{
|
||||
m_location = location;
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::webSocketUrl() const
|
||||
{
|
||||
return m_webSocketUrl;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setWebSocketUrl(const QString &webSocketUrl)
|
||||
{
|
||||
m_webSocketUrl = webSocketUrl;
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::nymeaRpcUrl() const
|
||||
{
|
||||
return m_nymeaRpcUrl;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setNymeaRpcUrl(const QString &nymeaRpcUrl)
|
||||
{
|
||||
m_nymeaRpcUrl = nymeaRpcUrl;
|
||||
m_uuid = uuid;
|
||||
}
|
||||
|
||||
QHostAddress DiscoveryDevice::hostAddress() const
|
||||
@ -61,116 +42,156 @@ QHostAddress DiscoveryDevice::hostAddress() const
|
||||
return m_hostAddress;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setHostAddress(const QHostAddress &hostAddress)
|
||||
QString DiscoveryDevice::hostAddressString() const
|
||||
{
|
||||
m_hostAddress = hostAddress;
|
||||
return m_hostAddress.toString();
|
||||
}
|
||||
|
||||
int DiscoveryDevice::port() const
|
||||
void DiscoveryDevice::setHostAddress(const QHostAddress &hostAddress)
|
||||
{
|
||||
if (m_hostAddress != hostAddress) {
|
||||
m_hostAddress = hostAddress;
|
||||
emit hostAddressChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setName(const QString &name)
|
||||
{
|
||||
if (m_name != name) {
|
||||
m_name = name;
|
||||
emit nameChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::version() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setVersion(const QString &version)
|
||||
{
|
||||
if (m_version != version) {
|
||||
m_version = version;
|
||||
emit versionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
PortConfigs* DiscoveryDevice::portConfigs() const
|
||||
{
|
||||
return m_portConfigs;
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::toUrl(int portConfigIndex)
|
||||
{
|
||||
PortConfig *pc = m_portConfigs->get(portConfigIndex);
|
||||
if (!pc) {
|
||||
qWarning() << "No portconfig for index" << portConfigIndex;
|
||||
}
|
||||
QString ret = pc->protocol() == PortConfig::ProtocolNymeaRpc ? "nymea" : "ws";
|
||||
ret += pc->sslEnabled() ? "s" : "";
|
||||
ret += "://";
|
||||
ret += m_hostAddress.toString();
|
||||
ret += ":";
|
||||
ret += QString::number(pc->port());
|
||||
return ret;
|
||||
}
|
||||
|
||||
PortConfigs::PortConfigs(QObject *parent): QAbstractListModel(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int PortConfigs::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_portConfigs.count();
|
||||
}
|
||||
|
||||
QVariant PortConfigs::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case RolePort:
|
||||
return m_portConfigs.at(index.row())->port();
|
||||
case RoleProtocol:
|
||||
return m_portConfigs.at(index.row())->protocol();
|
||||
case RoleSSLEnabled:
|
||||
return m_portConfigs.at(index.row())->sslEnabled();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
PortConfig *PortConfigs::find(int port)
|
||||
{
|
||||
foreach (PortConfig* pc, m_portConfigs) {
|
||||
if (pc->port() == port) {
|
||||
return pc;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PortConfigs::insert(PortConfig *portConfig)
|
||||
{
|
||||
portConfig->setParent(this);
|
||||
beginInsertRows(QModelIndex(), m_portConfigs.count(), m_portConfigs.count());
|
||||
m_portConfigs.append(portConfig);
|
||||
endInsertRows();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
PortConfig* PortConfigs::get(int index) const
|
||||
{
|
||||
return m_portConfigs.at(index);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> PortConfigs::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles.insert(RolePort, "port");
|
||||
roles.insert(RoleProtocol, "protocol");
|
||||
roles.insert(RoleSSLEnabled, "sslEnabled");
|
||||
return roles;
|
||||
}
|
||||
|
||||
PortConfig::PortConfig(int port, QObject *parent):
|
||||
QObject(parent),
|
||||
m_port(port)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int PortConfig::port() const
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setPort(const int &port)
|
||||
PortConfig::Protocol PortConfig::protocol() const
|
||||
{
|
||||
m_port = port;
|
||||
return m_protocol;
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::friendlyName() const
|
||||
void PortConfig::setProtocol(PortConfig::Protocol protocol)
|
||||
{
|
||||
return m_friendlyName;
|
||||
if (m_protocol != protocol) {
|
||||
m_protocol = protocol;
|
||||
emit protocolChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setFriendlyName(const QString &friendlyName)
|
||||
bool PortConfig::sslEnabled() const
|
||||
{
|
||||
m_friendlyName = friendlyName;
|
||||
return m_sslEnabled;
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::manufacturer() const
|
||||
void PortConfig::setSslEnabled(bool sslEnabled)
|
||||
{
|
||||
return m_manufacturer;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setManufacturer(const QString &manufacturer)
|
||||
{
|
||||
m_manufacturer = manufacturer;
|
||||
}
|
||||
|
||||
QUrl DiscoveryDevice::manufacturerURL() const
|
||||
{
|
||||
return m_manufacturerURL;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setManufacturerURL(const QUrl &manufacturerURL)
|
||||
{
|
||||
m_manufacturerURL = manufacturerURL;
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::modelDescription() const
|
||||
{
|
||||
return m_modelDescription;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setModelDescription(const QString &modelDescription)
|
||||
{
|
||||
m_modelDescription = modelDescription;
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::modelName() const
|
||||
{
|
||||
return m_modelName;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setModelName(const QString &modelName)
|
||||
{
|
||||
m_modelName = modelName;
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::modelNumber() const
|
||||
{
|
||||
return m_modelNumber;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setModelNumber(const QString &modelNumber)
|
||||
{
|
||||
m_modelNumber = modelNumber;
|
||||
}
|
||||
|
||||
QUrl DiscoveryDevice::modelURL() const
|
||||
{
|
||||
return m_modelURL;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setModelURL(const QUrl &modelURL)
|
||||
{
|
||||
m_modelURL = modelURL;
|
||||
}
|
||||
|
||||
QString DiscoveryDevice::uuid() const
|
||||
{
|
||||
return m_uuid;
|
||||
}
|
||||
|
||||
void DiscoveryDevice::setUuid(const QString &uuid)
|
||||
{
|
||||
m_uuid = uuid;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const DiscoveryDevice &DiscoveryDevice)
|
||||
{
|
||||
debug << "----------------------------------------------\n";
|
||||
debug << "UPnP device on " << QString("%1:%2").arg(DiscoveryDevice.hostAddress().toString()).arg(DiscoveryDevice.port()) << "\n";
|
||||
debug << "location | " << DiscoveryDevice.location().toString() << "\n";
|
||||
debug << "websocket | " << DiscoveryDevice.webSocketUrl() << "\n";
|
||||
debug << "nymearpc | " << DiscoveryDevice.nymeaRpcUrl() << "\n";
|
||||
debug << "friendly name | " << DiscoveryDevice.friendlyName() << "\n";
|
||||
debug << "manufacturer | " << DiscoveryDevice.manufacturer() << "\n";
|
||||
debug << "manufacturer URL | " << DiscoveryDevice.manufacturerURL().toString() << "\n";
|
||||
debug << "model name | " << DiscoveryDevice.modelName() << "\n";
|
||||
debug << "model number | " << DiscoveryDevice.modelNumber() << "\n";
|
||||
debug << "model description | " << DiscoveryDevice.modelDescription() << "\n";
|
||||
debug << "model URL | " << DiscoveryDevice.modelURL().toString() << "\n";
|
||||
debug << "UUID | " << DiscoveryDevice.uuid() << "\n";
|
||||
|
||||
return debug;
|
||||
if (m_sslEnabled != sslEnabled) {
|
||||
m_sslEnabled = sslEnabled;
|
||||
emit sslEnabledChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stuerz <stuerz.simon@gmail.com> *
|
||||
* Copyright (C) 2018 Michael Zanetti <michael.zanetti@guh.io> *
|
||||
* *
|
||||
* This file is part of mea. *
|
||||
* This file is part of mea. *
|
||||
* *
|
||||
* mea is free software: you can redistribute it and/or modify *
|
||||
* mea 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, version 3 of the License. *
|
||||
* *
|
||||
* mea is distributed in the hope that it will be useful, *
|
||||
* mea 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 for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with mea. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* along with mea. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
@ -22,72 +22,115 @@
|
||||
#define DISCOVERYDEVICE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUuid>
|
||||
#include <QUrl>
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class DiscoveryDevice
|
||||
class PortConfig: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int port READ port CONSTANT)
|
||||
Q_PROPERTY(Protocol protocol READ protocol NOTIFY protocolChanged)
|
||||
Q_PROPERTY(bool sslEnabled READ sslEnabled NOTIFY sslEnabledChanged)
|
||||
public:
|
||||
explicit DiscoveryDevice();
|
||||
|
||||
QUrl location() const;
|
||||
void setLocation(const QUrl &location);
|
||||
|
||||
QString webSocketUrl() const;
|
||||
void setWebSocketUrl(const QString &webSocketUrl);
|
||||
|
||||
QString nymeaRpcUrl() const;
|
||||
void setNymeaRpcUrl(const QString &nymeaRpcUrl);
|
||||
|
||||
QHostAddress hostAddress() const;
|
||||
void setHostAddress(const QHostAddress &hostAddress);
|
||||
enum Protocol {
|
||||
ProtocolNymeaRpc,
|
||||
ProtocolWebSocket
|
||||
};
|
||||
Q_ENUM(Protocol)
|
||||
PortConfig(int port, QObject *parent = nullptr);
|
||||
|
||||
int port() const;
|
||||
void setPort(const int &port);
|
||||
|
||||
QString deviceType() const;
|
||||
void setDeviceType(const QString & deviceType);
|
||||
Protocol protocol() const;
|
||||
void setProtocol(Protocol protocol);
|
||||
|
||||
QString friendlyName() const;
|
||||
void setFriendlyName(const QString &friendlyName);
|
||||
bool sslEnabled() const;
|
||||
void setSslEnabled(bool sslEnabled);
|
||||
|
||||
QString manufacturer() const;
|
||||
void setManufacturer(const QString &manufacturer);
|
||||
|
||||
QUrl manufacturerURL() const;
|
||||
void setManufacturerURL(const QUrl & manufacturerURL);
|
||||
|
||||
QString modelDescription() const;
|
||||
void setModelDescription(const QString & modelDescription);
|
||||
|
||||
QString modelName() const;
|
||||
void setModelName(const QString & modelName);
|
||||
|
||||
QString modelNumber() const;
|
||||
void setModelNumber(const QString &modelNumber);
|
||||
|
||||
QUrl modelURL() const;
|
||||
void setModelURL(const QUrl &modelURL);
|
||||
|
||||
QString uuid() const;
|
||||
void setUuid(const QString &uuid);
|
||||
signals:
|
||||
void protocolChanged();
|
||||
void sslEnabledChanged();
|
||||
|
||||
private:
|
||||
QUrl m_location;
|
||||
QString m_webSocketUrl;
|
||||
QString m_nymeaRpcUrl;
|
||||
QHostAddress m_hostAddress;
|
||||
int m_port;
|
||||
QString m_friendlyName;
|
||||
QString m_manufacturer;
|
||||
QUrl m_manufacturerURL;
|
||||
QString m_modelDescription;
|
||||
QString m_modelName;
|
||||
QString m_modelNumber;
|
||||
QUrl m_modelURL;
|
||||
QString m_uuid;
|
||||
int m_port = -1;
|
||||
Protocol m_protocol = ProtocolNymeaRpc;
|
||||
bool m_sslEnabled = false;
|
||||
};
|
||||
|
||||
QDebug operator<< (QDebug debug, const DiscoveryDevice &discoveryDevice);
|
||||
class PortConfigs: public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
public:
|
||||
enum Roles {
|
||||
RolePort,
|
||||
RoleProtocol,
|
||||
RoleSSLEnabled
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
PortConfigs(QObject* parent = nullptr);
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
PortConfig* find(int port);
|
||||
void insert(PortConfig* portConfig);
|
||||
|
||||
Q_INVOKABLE PortConfig *get(int index) const;
|
||||
|
||||
signals:
|
||||
void countChanged();
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
QList<PortConfig*> m_portConfigs;
|
||||
|
||||
};
|
||||
|
||||
class DiscoveryDevice: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QUuid uuid READ uuid CONSTANT)
|
||||
Q_PROPERTY(QString hostAddress READ hostAddressString NOTIFY hostAddressChanged)
|
||||
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
||||
Q_PROPERTY(QString version READ version NOTIFY versionChanged)
|
||||
Q_PROPERTY(PortConfigs* portConfigs READ portConfigs CONSTANT)
|
||||
|
||||
public:
|
||||
explicit DiscoveryDevice(QObject *parent = nullptr);
|
||||
|
||||
QUuid uuid() const;
|
||||
void setUuid(const QUuid &uuid);
|
||||
|
||||
QHostAddress hostAddress() const;
|
||||
QString hostAddressString() const;
|
||||
void setHostAddress(const QHostAddress &hostAddress);
|
||||
|
||||
QString name() const;
|
||||
void setName(const QString &name);
|
||||
|
||||
QString version() const;
|
||||
void setVersion(const QString &version);
|
||||
|
||||
PortConfigs *portConfigs() const;
|
||||
|
||||
Q_INVOKABLE QString toUrl(int portConfigIndex);
|
||||
|
||||
signals:
|
||||
void nameChanged();
|
||||
void hostAddressChanged();
|
||||
void versionChanged();
|
||||
|
||||
private:
|
||||
QUuid m_uuid;
|
||||
QHostAddress m_hostAddress;
|
||||
QString m_name;
|
||||
QString m_version;
|
||||
PortConfigs *m_portConfigs = nullptr;
|
||||
};
|
||||
|
||||
#endif // DISCOVERYDEVICE_H
|
||||
|
||||
@ -25,11 +25,6 @@ DiscoveryModel::DiscoveryModel(QObject *parent) :
|
||||
{
|
||||
}
|
||||
|
||||
QList<DiscoveryDevice> DiscoveryModel::devices()
|
||||
{
|
||||
return m_devices;
|
||||
}
|
||||
|
||||
int DiscoveryModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
@ -41,61 +36,54 @@ QVariant DiscoveryModel::data(const QModelIndex &index, int role) const
|
||||
if (index.row() < 0 || index.row() >= m_devices.count())
|
||||
return QVariant();
|
||||
|
||||
DiscoveryDevice device = m_devices.at(index.row());
|
||||
if (role == NameRole) {
|
||||
return device.friendlyName();
|
||||
} else if (role == HostAddressRole) {
|
||||
return device.hostAddress().toString();
|
||||
} else if (role == WebSocketUrlRole) {
|
||||
return device.webSocketUrl();
|
||||
} else if (role == PortRole) {
|
||||
return device.port();
|
||||
} else if (role == VersionRole) {
|
||||
return device.modelNumber();
|
||||
} else if (role == NymeaRpcUrlRole) {
|
||||
return device.nymeaRpcUrl();
|
||||
DiscoveryDevice *device = m_devices.at(index.row());
|
||||
switch (role) {
|
||||
case UuidRole:
|
||||
return device->uuid();
|
||||
case NameRole:
|
||||
return device->name();
|
||||
case HostAddressRole:
|
||||
return device->hostAddress().toString();
|
||||
// case WebSocketUrlRole:
|
||||
// return device.webSocketUrl();
|
||||
// case PortRole:
|
||||
// return device.port();
|
||||
case VersionRole:
|
||||
return device->version();
|
||||
// case NymeaRpcUrlRole:
|
||||
// return device.nymeaRpcUrl();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void DiscoveryModel::addDevice(const DiscoveryDevice &device)
|
||||
void DiscoveryModel::addDevice(DiscoveryDevice *device)
|
||||
{
|
||||
for (int i = 0; i < m_devices.count(); i++) {
|
||||
if (m_devices.at(i).uuid() == device.uuid()) {
|
||||
m_devices[i] = device;
|
||||
emit dataChanged(index(i), index(i));
|
||||
if (m_devices.at(i)->uuid() == device->uuid()) {
|
||||
qWarning() << "Device already added. Update existing device instead.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
device->setParent(this);
|
||||
beginInsertRows(QModelIndex(), m_devices.count(), m_devices.count());
|
||||
m_devices.append(device);
|
||||
endInsertRows();
|
||||
emit countChanged();
|
||||
}
|
||||
|
||||
QString DiscoveryModel::get(int index, DeviceRole role) const
|
||||
DiscoveryDevice *DiscoveryModel::get(int index) const
|
||||
{
|
||||
return data(this->index(index), role).toString();
|
||||
return m_devices.at(index);
|
||||
}
|
||||
|
||||
bool DiscoveryModel::contains(const QString &uuid) const
|
||||
DiscoveryDevice *DiscoveryModel::find(const QUuid &uuid)
|
||||
{
|
||||
foreach (const DiscoveryDevice &dev, m_devices) {
|
||||
if (dev.uuid() == uuid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DiscoveryDevice DiscoveryModel::find(const QHostAddress &address) const
|
||||
{
|
||||
foreach (const DiscoveryDevice &dev, m_devices) {
|
||||
if (dev.hostAddress() == address) {
|
||||
foreach (DiscoveryDevice *dev, m_devices) {
|
||||
if (dev->uuid() == uuid) {
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
return DiscoveryDevice();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DiscoveryModel::clearModel()
|
||||
@ -109,11 +97,9 @@ void DiscoveryModel::clearModel()
|
||||
QHash<int, QByteArray> DiscoveryModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[UuidRole] = "uuid";
|
||||
roles[NameRole] = "name";
|
||||
roles[HostAddressRole] = "hostAddress";
|
||||
roles[WebSocketUrlRole] = "webSocketUrl";
|
||||
roles[NymeaRpcUrlRole] = "nymeaRpcUrl";
|
||||
roles[PortRole] = "port";
|
||||
roles[VersionRole] = "version";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@ -32,27 +32,22 @@ class DiscoveryModel : public QAbstractListModel
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
public:
|
||||
enum DeviceRole {
|
||||
UuidRole,
|
||||
NameRole,
|
||||
HostAddressRole,
|
||||
WebSocketUrlRole,
|
||||
NymeaRpcUrlRole,
|
||||
PortRole,
|
||||
VersionRole
|
||||
};
|
||||
Q_ENUM(DeviceRole)
|
||||
|
||||
explicit DiscoveryModel(QObject *parent = 0);
|
||||
|
||||
QList<DiscoveryDevice> devices();
|
||||
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const;
|
||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
|
||||
|
||||
void addDevice(const DiscoveryDevice &device);
|
||||
void addDevice(DiscoveryDevice *device);
|
||||
|
||||
Q_INVOKABLE QString get(int index, DiscoveryModel::DeviceRole role) const;
|
||||
bool contains(const QString &uuid) const;
|
||||
DiscoveryDevice find(const QHostAddress &address) const;
|
||||
Q_INVOKABLE DiscoveryDevice* get(int index) const;
|
||||
Q_INVOKABLE DiscoveryDevice* find(const QUuid &uuid);
|
||||
|
||||
void clearModel();
|
||||
|
||||
@ -63,7 +58,7 @@ protected:
|
||||
QHash<int, QByteArray> roleNames() const;
|
||||
|
||||
private:
|
||||
QList<DiscoveryDevice> m_devices;
|
||||
QList<DiscoveryDevice*> m_devices;
|
||||
};
|
||||
|
||||
#endif // DISCOVERYMODEL_H
|
||||
|
||||
@ -114,7 +114,7 @@ void UpnpDiscovery::writeDiscoveryPacket()
|
||||
"MX:2\r\n"
|
||||
"ST: ssdp:all\r\n\r\n");
|
||||
|
||||
qDebug() << "sending discovery packet";
|
||||
// qDebug() << "sending discovery packet";
|
||||
writeDatagram(ssdpSearchMessage, m_host, m_port);
|
||||
}
|
||||
|
||||
@ -170,97 +170,104 @@ void UpnpDiscovery::readData()
|
||||
|
||||
if (!m_foundDevices.contains(location) && isNymea) {
|
||||
m_foundDevices.append(location);
|
||||
DiscoveryDevice discoveryDevice;
|
||||
discoveryDevice.setHostAddress(hostAddress);
|
||||
discoveryDevice.setPort(port);
|
||||
discoveryDevice.setLocation(location.toString());
|
||||
|
||||
qDebug() << "Getting server data from:" << location;
|
||||
QNetworkReply *reply = m_networkAccessManager->get(QNetworkRequest(location));
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, reply](const QList<QSslError> &errors){
|
||||
reply->ignoreSslErrors(errors);
|
||||
});
|
||||
m_runningReplies.insert(reply, discoveryDevice);
|
||||
m_runningReplies.insert(reply, hostAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpnpDiscovery::networkReplyFinished(QNetworkReply *reply)
|
||||
{
|
||||
reply->deleteLater();
|
||||
QHostAddress discoveredAddress = m_runningReplies.take(reply);
|
||||
|
||||
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (reply->error() != QNetworkReply::NoError || status != 200) {
|
||||
qWarning() << "Error fetching UPnP discovery data:" << status << reply->error() << reply->errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
DiscoveryDevice discoveryDevice = m_runningReplies.take(reply);
|
||||
|
||||
switch (status) {
|
||||
case(200):{
|
||||
// parse XML data
|
||||
QXmlStreamReader xml(data);
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
xml.readNext();
|
||||
QString name;
|
||||
QString version;
|
||||
QUuid uuid;
|
||||
QList<PortConfig*> portConfigList;
|
||||
|
||||
if (xml.isStartDocument())
|
||||
continue;
|
||||
// parse XML data
|
||||
QXmlStreamReader xml(data);
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
xml.readNext();
|
||||
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.name().toString() == "websocketURL") {
|
||||
discoveryDevice.setWebSocketUrl(xml.readElementText());
|
||||
}
|
||||
if (xml.isStartDocument())
|
||||
continue;
|
||||
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.name().toString() == "websocketURL") {
|
||||
QUrl u(xml.readElementText());
|
||||
PortConfig *pc = new PortConfig(u.port());
|
||||
pc->setProtocol(PortConfig::ProtocolWebSocket);
|
||||
pc->setSslEnabled(u.scheme().endsWith('s'));
|
||||
portConfigList.append(pc);
|
||||
}
|
||||
}
|
||||
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.name().toString() == "nymeaRpcURL") {
|
||||
discoveryDevice.setNymeaRpcUrl(xml.readElementText());
|
||||
}
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.name().toString() == "nymeaRpcURL") {
|
||||
QUrl u(xml.readElementText());
|
||||
qDebug() << "have url" << u << u.scheme();
|
||||
PortConfig *pc = new PortConfig(u.port());
|
||||
pc->setProtocol(PortConfig::ProtocolNymeaRpc);
|
||||
pc->setSslEnabled(u.scheme().endsWith('s'));
|
||||
portConfigList.append(pc);
|
||||
}
|
||||
}
|
||||
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.name().toString() == "device") {
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.name() == "friendlyName" && xml.isStartElement()) {
|
||||
discoveryDevice.setFriendlyName(xml.readElementText());
|
||||
}
|
||||
if (xml.name() == "manufacturer" && xml.isStartElement()) {
|
||||
discoveryDevice.setManufacturer(xml.readElementText());
|
||||
}
|
||||
if (xml.name() == "manufacturerURL" && xml.isStartElement()) {
|
||||
discoveryDevice.setManufacturerURL(QUrl(xml.readElementText()));
|
||||
}
|
||||
if (xml.name() == "modelDescription" && xml.isStartElement()) {
|
||||
discoveryDevice.setModelDescription(xml.readElementText());
|
||||
}
|
||||
if (xml.name() == "modelName" && xml.isStartElement()) {
|
||||
discoveryDevice.setModelName(xml.readElementText());
|
||||
}
|
||||
if (xml.name() == "modelNumber" && xml.isStartElement()) {
|
||||
discoveryDevice.setModelNumber(xml.readElementText());
|
||||
}
|
||||
if (xml.name() == "modelURL" && xml.isStartElement()) {
|
||||
discoveryDevice.setModelURL(QUrl(xml.readElementText()));
|
||||
}
|
||||
if (xml.name() == "UDN" && xml.isStartElement()) {
|
||||
discoveryDevice.setUuid(xml.readElementText());
|
||||
}
|
||||
xml.readNext();
|
||||
if (xml.isStartElement()) {
|
||||
if (xml.name().toString() == "device") {
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.name() == "friendlyName" && xml.isStartElement()) {
|
||||
name = xml.readElementText();
|
||||
}
|
||||
if (xml.name() == "modelNumber" && xml.isStartElement()) {
|
||||
version = xml.readElementText();
|
||||
}
|
||||
if (xml.name() == "UDN" && xml.isStartElement()) {
|
||||
uuid = xml.readElementText().split(':').last();
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "discovered device" << discoveryDevice.friendlyName() << discoveryDevice.hostAddress();
|
||||
qDebug() << "discovered device" << uuid << name << discoveredAddress << version;
|
||||
|
||||
if (discoveryDevice.manufacturer().contains("guh")) {
|
||||
if (!m_discoveryModel->contains(discoveryDevice.uuid())) {
|
||||
m_discoveryModel->addDevice(discoveryDevice);
|
||||
}
|
||||
DiscoveryDevice* device = m_discoveryModel->find(uuid);
|
||||
if (!device) {
|
||||
device = new DiscoveryDevice(m_discoveryModel);
|
||||
device->setUuid(uuid);
|
||||
qDebug() << "Adding new host to model";
|
||||
m_discoveryModel->addDevice(device);
|
||||
}
|
||||
device->setHostAddress(discoveredAddress);
|
||||
device->setName(name);
|
||||
device->setVersion(version);
|
||||
foreach (PortConfig *pc, portConfigList) {
|
||||
PortConfig *portConfig = device->portConfigs()->find(pc->port());
|
||||
if (portConfig) {
|
||||
qDebug() << "Updating port config" << portConfig->port() << portConfig->sslEnabled() << portConfig->protocol();
|
||||
portConfig->setProtocol(pc->protocol());
|
||||
portConfig->setSslEnabled(pc->sslEnabled());
|
||||
pc->deleteLater();
|
||||
} else {
|
||||
qDebug() << "adding new port config" << pc->port() << pc->sslEnabled() << pc->protocol();
|
||||
device->portConfigs()->insert(pc);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qWarning() << "HTTP request error " << status;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ private:
|
||||
|
||||
bool m_discovering;
|
||||
bool m_available;
|
||||
QHash<QNetworkReply *, DiscoveryDevice> m_runningReplies;
|
||||
QHash<QNetworkReply *, QHostAddress> m_runningReplies;
|
||||
QList<QUrl> m_foundDevices;
|
||||
|
||||
void setDiscovering(const bool &discovering);
|
||||
|
||||
@ -49,6 +49,7 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
|
||||
QString uuid;
|
||||
bool sslEnabled = false;
|
||||
QString serverName;
|
||||
QString version;
|
||||
foreach (const QByteArray &key, entry.txt().keys()) {
|
||||
QPair<QString, QString> txtRecord = qMakePair<QString, QString>(key, entry.txt().value(key));
|
||||
if (!sslEnabled && txtRecord.first == "sslEnabled") {
|
||||
@ -60,34 +61,30 @@ void ZeroconfDiscovery::serviceEntryAdded(const QZeroConfService &entry)
|
||||
if (txtRecord.first == "name") {
|
||||
serverName = txtRecord.second;
|
||||
}
|
||||
if (txtRecord.first == "serverVersion") {
|
||||
version = txtRecord.second;
|
||||
}
|
||||
}
|
||||
qDebug() << "avahi service entry added" << serverName << uuid << sslEnabled;
|
||||
|
||||
DiscoveryDevice dev = m_discoveryModel->find(entry.ip());
|
||||
if (dev.uuid() == uuid && dev.nymeaRpcUrl().startsWith("nymeas") && !sslEnabled) {
|
||||
// We already have this host and with a more secure configuration... skip this one...
|
||||
return;
|
||||
}
|
||||
qDebug() << "Adding new found entry:" << entry.name() << entry.ip();
|
||||
dev.setUuid(uuid);
|
||||
dev.setHostAddress(entry.ip());
|
||||
dev.setPort(entry.port());
|
||||
dev.setFriendlyName(serverName + " on " + entry.ip().toString());
|
||||
QHostAddress address = entry.ip();
|
||||
QString addressString;
|
||||
if (address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
addressString = "[" + address.toString() + "]";
|
||||
} else {
|
||||
addressString = address.toString();
|
||||
}
|
||||
if (entry.type() == "_ws._tcp") {
|
||||
dev.setWebSocketUrl(QString("%1://%2:%3").arg(sslEnabled ? "wss" : "ws").arg(addressString).arg(entry.port()));
|
||||
} else {
|
||||
dev.setNymeaRpcUrl(QString("%1://%2:%3").arg(sslEnabled ? "nymeas" : "nymea").arg(addressString).arg(entry.port()));
|
||||
}
|
||||
m_discoveryModel->addDevice(dev);
|
||||
|
||||
// DiscoveryDevice *dev = new DiscoveryDevice();
|
||||
// dev->setFriendlyName(entry.hostName());
|
||||
DiscoveryDevice* device = m_discoveryModel->find(uuid);
|
||||
if (!device) {
|
||||
device = new DiscoveryDevice(m_discoveryModel);
|
||||
device->setUuid(uuid);
|
||||
qDebug() << "Adding new host to model";
|
||||
m_discoveryModel->addDevice(device);
|
||||
}
|
||||
device->setHostAddress(entry.ip());
|
||||
device->setName(serverName);
|
||||
device->setVersion(version);
|
||||
PortConfig *portConfig = device->portConfigs()->find(entry.port());
|
||||
if (!portConfig) {
|
||||
qDebug() << "Adding new port config";
|
||||
portConfig = new PortConfig(entry.port());
|
||||
device->portConfigs()->insert(portConfig);
|
||||
}
|
||||
portConfig->setProtocol(entry.type() == "_ws._tcp" ? PortConfig::ProtocolWebSocket : PortConfig::ProtocolNymeaRpc);
|
||||
portConfig->setSslEnabled(sslEnabled);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -224,7 +224,7 @@ void JsonRpcClient::sendRequest(const QVariantMap &request)
|
||||
{
|
||||
QVariantMap newRequest = request;
|
||||
newRequest.insert("token", m_token);
|
||||
qDebug() << "Sending request" << qUtf8Printable(QJsonDocument::fromVariant(newRequest).toJson());
|
||||
// qDebug() << "Sending request" << qUtf8Printable(QJsonDocument::fromVariant(newRequest).toJson());
|
||||
m_connection->sendData(QJsonDocument::fromVariant(newRequest).toJson());
|
||||
}
|
||||
|
||||
@ -259,7 +259,7 @@ void JsonRpcClient::dataReceived(const QByteArray &data)
|
||||
// qWarning() << "Could not parse json data from mea" << data << error.errorString();
|
||||
return;
|
||||
}
|
||||
qDebug() << "received response" << m_receiveBuffer.left(splitIndex);
|
||||
// qDebug() << "received response" << m_receiveBuffer.left(splitIndex);
|
||||
m_receiveBuffer = m_receiveBuffer.right(m_receiveBuffer.length() - splitIndex - 1);
|
||||
if (!m_receiveBuffer.isEmpty()) {
|
||||
staticMetaObject.invokeMethod(this, "dataReceived", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray()));
|
||||
|
||||
@ -115,6 +115,9 @@ void registerQmlTypes() {
|
||||
|
||||
qmlRegisterType<NymeaDiscovery>(uri, 1, 0, "NymeaDiscovery");
|
||||
qmlRegisterUncreatableType<DiscoveryModel>(uri, 1, 0, "DiscoveryModel", "Get it from NymeaDiscovery");
|
||||
qmlRegisterUncreatableType<DiscoveryDevice>(uri, 1, 0, "DiscoveryDevice", "Get it from DiscoveryModel");
|
||||
qmlRegisterUncreatableType<PortConfigs>(uri, 1, 0, "PortConfigs", "Get it from DiscoveryDevice");
|
||||
qmlRegisterUncreatableType<PortConfig>(uri, 1, 0, "PortConfig", "Get it from DiscoveryDevice");
|
||||
|
||||
qmlRegisterType<EventDescriptorParamsFilterModel>(uri, 1, 0, "EventDescriptorParamsFilterModel");
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ win32:Debug:LIBS += -L$$top_builddir/libmea-core/debug \
|
||||
win32:Release:LIBS += -L$$top_builddir/libmea-core/release \
|
||||
-L$$top_builddir/libnymea-common/release
|
||||
linux:!android:LIBS += -lavahi-client -lavahi-common
|
||||
|
||||
PRE_TARGETDEPS += ../libmea-core
|
||||
HEADERS += \
|
||||
stylecontroller.h
|
||||
|
||||
|
||||
@ -164,5 +164,7 @@
|
||||
<file>ui/AppSettingsPage.qml</file>
|
||||
<file>ui/images/stock_application.svg</file>
|
||||
<file>ui/delegates/ThingDelegate.qml</file>
|
||||
<file>ui/images/network-secure.svg</file>
|
||||
<file>ui/images/lock-broken.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Controls.Material 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import Mea 1.0
|
||||
import "components"
|
||||
|
||||
@ -24,9 +24,8 @@ Page {
|
||||
target: Engine.connection
|
||||
onVerifyConnectionCertificate: {
|
||||
print("verify cert!")
|
||||
certDialog.issuerInfo = issuerInfo
|
||||
certDialog.fingerprint = fingerprint
|
||||
certDialog.open();
|
||||
var popup = certDialogComponent.createObject(app, {issuerInfo: issuerInfo, fingerprint: fingerprint});
|
||||
popup.open();
|
||||
}
|
||||
onConnectionError: {
|
||||
pageStack.pop(root)
|
||||
@ -118,34 +117,81 @@ Page {
|
||||
model: discovery.discoveryModel
|
||||
clip: true
|
||||
|
||||
delegate: ItemDelegate {
|
||||
delegate: SwipeDelegate {
|
||||
width: parent.width
|
||||
height: app.delegateHeight
|
||||
objectName: "discoveryDelegate" + index
|
||||
property string hostAddress: model.hostAddress
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
Label {
|
||||
text: model.name
|
||||
contentItem: RowLayout {
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
text: model.name
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
Label {
|
||||
text: model.hostAddress
|
||||
font.pixelSize: app.smallFont
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: model.hostAddress
|
||||
font.pixelSize: app.smallFont
|
||||
ColorIcon {
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: height
|
||||
name: "../images/network-secure.svg"
|
||||
visible: {
|
||||
var discoveryDevice = discovery.discoveryModel.get(index);
|
||||
for (var i = 0; i < discoveryDevice.portConfigs.count; i++) {
|
||||
if (discoveryDevice.portConfigs.get(i).sslEnabled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
var url;
|
||||
if (model.nymeaRpcUrl) {
|
||||
url = model.nymeaRpcUrl;
|
||||
} else {
|
||||
url = model.webSocketUrl;
|
||||
}
|
||||
print("Should connect to", url)
|
||||
Engine.connection.connect(url)
|
||||
|
||||
onClicked: {
|
||||
var discoveryDevice = discovery.discoveryModel.get(index);
|
||||
print("discoveryDevice:", discoveryDevice.name, discoveryDevice.uuid, discoveryDevice.hostAddress)
|
||||
var usedConfigIndex = 0;
|
||||
for (var i = 1; i < discoveryDevice.portConfigs.count; i++) {
|
||||
var oldConfig = discoveryDevice.portConfigs.get(usedConfigIndex);
|
||||
var newConfig = discoveryDevice.portConfigs.get(i);
|
||||
|
||||
// prefer secure over insecure
|
||||
if (!oldConfig.sslEnabled && newConfig.sslEnabled) {
|
||||
usedConfigIndex = i;
|
||||
continue;
|
||||
}
|
||||
if (oldConfig.sslEnabled && !newConfig.sslEnabled) {
|
||||
continue; // discard new one as the one we already have is more secure
|
||||
}
|
||||
|
||||
// both options are new either secure or insecure, prefer nymearpc over websocket for less overhead
|
||||
if (oldConfig.protocol === PortConfig.ProtocolWebSocket && newConfig.protocol === PortConfig.ProtocolNymeaRpc) {
|
||||
usedConfigIndex = i;
|
||||
}
|
||||
}
|
||||
Engine.connection.connect(discoveryDevice.toUrl(usedConfigIndex))
|
||||
pageStack.push(connectingPage)
|
||||
}
|
||||
|
||||
swipe.right: MouseArea {
|
||||
height: parent.height
|
||||
width: height
|
||||
anchors.right: parent.right
|
||||
ColorIcon {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins
|
||||
name: "../images/info.svg"
|
||||
}
|
||||
onClicked: {
|
||||
swipe.close()
|
||||
var popup = infoDialog.createObject(app,{discoveryDevice: discovery.discoveryModel.get(index)})
|
||||
popup.open()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Column {
|
||||
@ -317,82 +363,231 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: certDialog
|
||||
width: Math.min(parent.width * .9, 400)
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
standardButtons: Dialog.Yes | Dialog.No
|
||||
Component {
|
||||
id: certDialogComponent
|
||||
|
||||
property var fingerprint
|
||||
property var issuerInfo
|
||||
Dialog {
|
||||
id: certDialog
|
||||
width: Math.min(parent.width * .9, 400)
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
standardButtons: Dialog.Yes | Dialog.No
|
||||
|
||||
ColumnLayout {
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||
spacing: app.margins
|
||||
property var fingerprint
|
||||
property var issuerInfo
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
name: "../images/dialog-warning-symbolic.svg"
|
||||
color: app.guhAccent
|
||||
ColumnLayout {
|
||||
id: certLayout
|
||||
anchors.fill: parent
|
||||
// spacing: app.margins
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: app.margins
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
name: "../images/dialog-warning-symbolic.svg"
|
||||
color: app.guhAccent
|
||||
}
|
||||
|
||||
Label {
|
||||
id: titleLabel
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Warning")
|
||||
color: app.guhAccent
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: titleLabel
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Warning")
|
||||
color: app.guhAccent
|
||||
font.pixelSize: app.largeFont
|
||||
text: qsTr("The authenticity of this %1 box cannot be verified.").arg(app.systemName)
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("If this is the first time you connect to this box, this is expected. Once you trust a box, you should never see this message again for that one. If you see this message multiple times for the same box, something suspicious is going on!")
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitHeight: certGridLayout.implicitHeight
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: certGridLayout.implicitHeight
|
||||
clip: true
|
||||
|
||||
GridLayout {
|
||||
id: certGridLayout
|
||||
columns: 2
|
||||
width: parent.width
|
||||
|
||||
Repeater {
|
||||
model: certDialog.issuerInfo
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: modelData
|
||||
}
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: 2
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
text: qsTr("Fingerprint: ") + certDialog.fingerprint
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThinDivider {}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Do you want to trust this device?")
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("The authenticity of this %1 box cannot be verified.").arg(app.systemName)
|
||||
|
||||
onAccepted: {
|
||||
Engine.connection.acceptCertificate(certDialog.fingerprint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("If this is the first time you connect to this box, this is expected. Once you trust a box, you should never see this message again for that one. If you see this message multiple times for the same box, something suspicious is going on!")
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
columns: 2
|
||||
Component {
|
||||
id: infoDialog
|
||||
Dialog {
|
||||
id: dialog
|
||||
width: Math.min(parent.width, contentGrid.implicitWidth)
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
modal: true
|
||||
title: qsTr("Box information")
|
||||
|
||||
Repeater {
|
||||
model: certDialog.issuerInfo
|
||||
standardButtons: Dialog.Ok
|
||||
|
||||
property var discoveryDevice: null
|
||||
|
||||
header: Item {
|
||||
implicitHeight: headerRow.height + app.margins * 2
|
||||
implicitWidth: parent.width
|
||||
RowLayout {
|
||||
id: headerRow
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top; margins: app.margins }
|
||||
spacing: app.margins
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize * 2
|
||||
Layout.preferredWidth: height
|
||||
name: "../images/info.svg"
|
||||
color: app.guhAccent
|
||||
}
|
||||
|
||||
Label {
|
||||
id: titleLabel
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: modelData
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
text: dialog.title
|
||||
color: app.guhAccent
|
||||
font.pixelSize: app.largeFont
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
text: qsTr("Fingerprint: ") + certDialog.fingerprint
|
||||
}
|
||||
GridLayout {
|
||||
id: contentGrid
|
||||
anchors.fill: parent
|
||||
rowSpacing: app.margins
|
||||
columns: 2
|
||||
Label {
|
||||
text: "Name:"
|
||||
}
|
||||
Label {
|
||||
text: dialog.discoveryDevice.name
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
Label {
|
||||
text: "UUID:"
|
||||
}
|
||||
Label {
|
||||
text: dialog.discoveryDevice.uuid
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
Label {
|
||||
text: "Version:"
|
||||
}
|
||||
Label {
|
||||
text: dialog.discoveryDevice.version
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
Label {
|
||||
text: "IP Address:"
|
||||
}
|
||||
Label {
|
||||
text: dialog.discoveryDevice.hostAddress
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
ThinDivider { Layout.columnSpan: 2 }
|
||||
Label {
|
||||
Layout.columnSpan: 2
|
||||
text: qsTr("Available connections")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Do you want to trust this device?")
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
Flickable {
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 200
|
||||
contentHeight: contentColumn.implicitHeight
|
||||
clip: true
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
width: parent.width
|
||||
Repeater {
|
||||
model: dialog.discoveryDevice.portConfigs
|
||||
ItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
contentItem: RowLayout {
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
text: qsTr("Port: %1").arg(model.port)
|
||||
}
|
||||
Label {
|
||||
text: model.protocol === PortConfig.ProtocolNymeaRpc ? "nymea-rpc" : "websocket"
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: app.smallFont
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
Engine.connection.acceptCertificate(certDialog.fingerprint)
|
||||
ColorIcon {
|
||||
Layout.preferredHeight: app.iconSize
|
||||
Layout.preferredWidth: height
|
||||
name: model.sslEnabled ? "../images/network-secure.svg" : "../images/lock-broken.svg"
|
||||
color: model.sslEnabled ? app.guhAccent : "red"
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
Engine.connection.connect(dialog.discoveryDevice.toUrl(index))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
173
mea/ui/images/lock-broken.svg
Normal file
173
mea/ui/images/lock-broken.svg
Normal file
@ -0,0 +1,173 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg4874"
|
||||
version="1.1"
|
||||
inkscape:version="0.91+devel r"
|
||||
viewBox="0 0 96 96.000001"
|
||||
sodipodi:docname="lock-broken.svg">
|
||||
<defs
|
||||
id="defs4876" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6199993"
|
||||
inkscape:cx="-18.558726"
|
||||
inkscape:cy="57.553367"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g4780"
|
||||
showgrid="true"
|
||||
showborder="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-center="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5451"
|
||||
empspacing="8" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="8,-8.0000001"
|
||||
id="guide4063" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="4,-8.0000001"
|
||||
id="guide4065" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,88.000001"
|
||||
id="guide4067" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,92.000001"
|
||||
id="guide4069" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="104,4"
|
||||
id="guide4071" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,8.0000001"
|
||||
id="guide4073" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="92,-8.0000001"
|
||||
id="guide4075" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="88,-8.0000001"
|
||||
id="guide4077" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,84.000001"
|
||||
id="guide4074" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="12,-8.0000001"
|
||||
id="guide4076" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,12"
|
||||
id="guide4078" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="84,-9.0000001"
|
||||
id="guide4080" />
|
||||
<sodipodi:guide
|
||||
position="48,-8.0000001"
|
||||
orientation="1,0"
|
||||
id="guide4170" />
|
||||
<sodipodi:guide
|
||||
position="-8,48"
|
||||
orientation="0,1"
|
||||
id="guide4172" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4879">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(67.857146,-78.50504)">
|
||||
<g
|
||||
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
|
||||
id="g4845"
|
||||
style="display:inline">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="next01.png"
|
||||
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
|
||||
id="g4778"
|
||||
inkscape:label="Layer 1">
|
||||
<g
|
||||
transform="matrix(-1,0,0,1,575.99999,611)"
|
||||
id="g4780"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
|
||||
id="rect4782"
|
||||
width="96.037987"
|
||||
height="96"
|
||||
x="-438.00244"
|
||||
y="345.36221"
|
||||
transform="scale(-1,1)" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 393.98438,425.36133 -2,0 -46.01758,0 0,-64 48.01758,0 0,64 z m -4,-4 0,-55.99805 -40.01758,0 0,55.99805 40.01758,0 z"
|
||||
id="rect4269"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00000048;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="M 48 4 C 34.18438 4 22.998047 15.180068 22.998047 28.990234 L 22.998047 45 L 27 45 L 27 28.990234 C 27 17.326469 36.33162 7.9980469 48 7.9980469 C 56.909544 7.9980469 64.43796 13.446423 67.507812 21.210938 L 71.169922 19.642578 C 67.468388 10.473439 58.504779 4 48 4 z M 68.998047 34 L 68.998047 45 L 73 45 L 73 34 L 68.998047 34 z "
|
||||
transform="matrix(0,-1,-1.0003957,0,438.00245,441.36222)"
|
||||
id="rect4271" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079107;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.5999999;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 364.03516,387.09375 -16.96289,8.47852 1.78906,3.58007 15.04883,-7.52148 14.06836,8.03516 14.99804,-8.56641 -1.98437,-3.47461 -13.01367,7.43359 -13.94336,-7.96484 z"
|
||||
id="path4179"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.7 KiB |
175
mea/ui/images/network-secure.svg
Normal file
175
mea/ui/images/network-secure.svg
Normal file
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg4874"
|
||||
version="1.1"
|
||||
inkscape:version="0.91+devel r"
|
||||
viewBox="0 0 96 96.000001"
|
||||
sodipodi:docname="lock.svg">
|
||||
<defs
|
||||
id="defs4876" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6199992"
|
||||
inkscape:cx="5.4626288"
|
||||
inkscape:cy="25.355855"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g4780"
|
||||
showgrid="true"
|
||||
showborder="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-center="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5451"
|
||||
empspacing="8" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="8,-8.0000001"
|
||||
id="guide4063" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="4,-8.0000001"
|
||||
id="guide4065" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,88.000001"
|
||||
id="guide4067" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,92.000001"
|
||||
id="guide4069" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="104,4"
|
||||
id="guide4071" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,8.0000001"
|
||||
id="guide4073" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="92,-8.0000001"
|
||||
id="guide4075" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="88,-8.0000001"
|
||||
id="guide4077" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-8,84.000001"
|
||||
id="guide4074" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="12,-8.0000001"
|
||||
id="guide4076" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="-5,12"
|
||||
id="guide4078" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="84,-9.0000001"
|
||||
id="guide4080" />
|
||||
<sodipodi:guide
|
||||
position="48,-8.0000001"
|
||||
orientation="1,0"
|
||||
id="guide4170" />
|
||||
<sodipodi:guide
|
||||
position="-8,48"
|
||||
orientation="0,1"
|
||||
id="guide4172" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4879">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(67.857146,-78.50504)">
|
||||
<g
|
||||
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
|
||||
id="g4845"
|
||||
style="display:inline">
|
||||
<g
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="next01.png"
|
||||
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
|
||||
id="g4778"
|
||||
inkscape:label="Layer 1">
|
||||
<g
|
||||
transform="matrix(-1,0,0,1,575.99999,611)"
|
||||
id="g4780"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
|
||||
id="rect4782"
|
||||
width="96.037987"
|
||||
height="96"
|
||||
x="-438.00244"
|
||||
y="345.36221"
|
||||
transform="scale(-1,1)" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 393.98438,425.36133 -2,0 -46.01758,0 0,-64 48.01758,0 0,64 z m -4,-4 0,-55.99805 -40.01758,0 0,55.99805 40.01758,0 z"
|
||||
id="rect4269"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079155;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 434.00087,393.36222 c 0,13.81562 -11.1845,25.00195 -25.00013,25.00195 l -16.0161,0 0,-4.00195 16.0161,0 c 11.66838,0 21.0005,-9.33162 21.0005,-21 0,-11.66838 -9.33212,-20.99805 -21.0005,-20.99805 l -16.0161,0 0,-4.00195 16.0161,0 c 13.81563,0 25.00013,11.18438 25.00013,25 z"
|
||||
id="rect4271"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
transform="matrix(0,-1,-1,0,0,0)"
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
d="m -397.36221,-377.9787 8,-2.00079 0,20.00794 -8,0 z"
|
||||
id="rect4280"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.7 KiB |
Reference in New Issue
Block a user