syncthingtray/model/syncthingdevicemodel.cpp

408 lines
14 KiB
C++
Raw Normal View History

2016-08-25 00:45:32 +02:00
#include "./syncthingdevicemodel.h"
2016-11-08 19:42:50 +01:00
#include "./colors.h"
2017-05-01 03:34:43 +02:00
#include "./syncthingicons.h"
#include <syncthingconnector/syncthingconnection.h>
#include <syncthingconnector/utils.h>
2016-09-01 16:34:30 +02:00
2018-08-04 20:34:24 +02:00
#include <c++utilities/conversion/stringconversion.h>
2019-04-10 18:25:41 +02:00
#include <QStringBuilder>
using namespace std;
2019-06-10 22:48:26 +02:00
using namespace CppUtilities;
2016-08-25 00:45:32 +02:00
namespace Data {
2017-05-01 03:34:43 +02:00
SyncthingDeviceModel::SyncthingDeviceModel(SyncthingConnection &connection, QObject *parent)
: SyncthingModel(connection, parent)
, m_devs(connection.devInfo())
2016-08-25 00:45:32 +02:00
{
2016-08-26 16:43:53 +02:00
connect(&m_connection, &SyncthingConnection::devStatusChanged, this, &SyncthingDeviceModel::devStatusChanged);
2016-08-25 00:45:32 +02:00
}
QHash<int, QByteArray> SyncthingDeviceModel::roleNames() const
{
2018-11-03 21:47:30 +01:00
const static QHash<int, QByteArray> roles{
{ Qt::DisplayRole, "name" },
{ DeviceStatus, "status" },
{ Qt::DecorationRole, "statusIcon" },
{ DevicePaused, "paused" },
{ DeviceStatusString, "statusString" },
{ DeviceStatusColor, "statusColor" },
{ DeviceId, "devId" },
{ DeviceDetail, "detail" },
{ DeviceDetailIcon, "detailIcon" },
2018-11-03 21:47:30 +01:00
};
return roles;
}
2017-09-11 23:44:19 +02:00
const QVector<int> &SyncthingDeviceModel::colorRoles() const
{
static const QVector<int> colorRoles({ Qt::DecorationRole, Qt::ForegroundRole, DeviceStatusColor, DeviceDetailIcon });
2017-09-11 23:44:19 +02:00
return colorRoles;
}
2016-08-25 00:45:32 +02:00
/*!
* \brief Returns the device info for the spcified \a index. The returned object is not persistent.
*/
const SyncthingDev *SyncthingDeviceModel::devInfo(const QModelIndex &index) const
{
2017-05-01 03:34:43 +02:00
return (index.parent().isValid() ? devInfo(index.parent())
: (static_cast<size_t>(index.row()) < m_devs.size() ? &m_devs[static_cast<size_t>(index.row())] : nullptr));
2016-08-25 00:45:32 +02:00
}
QModelIndex SyncthingDeviceModel::index(int row, int column, const QModelIndex &parent) const
{
2017-05-01 03:34:43 +02:00
if (!parent.isValid()) {
2016-08-25 00:45:32 +02:00
// top-level: all dev IDs
2017-05-01 03:34:43 +02:00
if (row < rowCount(parent)) {
return createIndex(row, column, static_cast<quintptr>(-1));
2016-08-25 00:45:32 +02:00
}
2017-05-01 03:34:43 +02:00
} else if (!parent.parent().isValid()) {
2016-08-25 00:45:32 +02:00
// dev-level: dev attributes
2017-05-01 03:34:43 +02:00
if (row < rowCount(parent)) {
return createIndex(row, column, static_cast<quintptr>(parent.row()));
2016-08-25 00:45:32 +02:00
}
}
return QModelIndex();
}
QModelIndex SyncthingDeviceModel::parent(const QModelIndex &child) const
{
return child.internalId() != static_cast<quintptr>(-1) ? index(static_cast<int>(child.internalId()), 0, QModelIndex()) : QModelIndex();
2016-08-25 00:45:32 +02:00
}
QVariant SyncthingDeviceModel::headerData(int section, Qt::Orientation orientation, int role) const
{
2017-05-01 03:34:43 +02:00
switch (orientation) {
2016-08-25 00:45:32 +02:00
case Qt::Horizontal:
2017-05-01 03:34:43 +02:00
switch (role) {
2016-08-25 00:45:32 +02:00
case Qt::DisplayRole:
2017-05-01 03:34:43 +02:00
switch (section) {
case 0:
return tr("ID");
case 1:
return tr("Status");
2016-08-25 00:45:32 +02:00
}
break;
2017-05-01 03:34:43 +02:00
default:;
2016-08-25 00:45:32 +02:00
}
break;
2017-05-01 03:34:43 +02:00
default:;
2016-08-25 00:45:32 +02:00
}
return QVariant();
}
QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const
{
2019-02-16 01:06:08 +01:00
if (!index.isValid()) {
return QVariant();
}
if (index.parent().isValid()) {
// dir attributes
if (static_cast<size_t>(index.parent().row()) >= m_devs.size()) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
if (index.column() == 0) {
// attribute names
switch (index.row()) {
case 0:
return tr("ID");
case 1:
2019-04-10 18:25:41 +02:00
return tr("Address");
2019-02-16 01:06:08 +01:00
case 2:
return tr("Last seen");
case 3:
return tr("Compression");
case 4:
return tr("Certificate");
case 5:
return tr("Introducer");
case 6:
return tr("Incoming traffic");
case 7:
return tr("Outgoing traffic");
case 8:
return tr("Version");
2016-08-25 00:45:32 +02:00
}
2019-02-16 01:06:08 +01:00
break;
2016-08-25 00:45:32 +02:00
}
2019-06-12 21:00:49 +02:00
[[fallthrough]];
2019-02-16 01:06:08 +01:00
case DeviceDetail:
if (index.column() == 1 || role == DeviceDetail) {
// attribute values
const SyncthingDev &dev = m_devs[static_cast<size_t>(index.parent().row())];
switch (index.row()) {
2017-05-01 03:34:43 +02:00
case 0:
2019-02-16 01:06:08 +01:00
return dev.id;
2016-08-25 00:45:32 +02:00
case 1:
2019-04-10 18:25:41 +02:00
if (dev.connectionAddress.isEmpty()) {
return dev.addresses.join(QStringLiteral(", "));
} else {
return QVariant(
dev.connectionAddress % QStringLiteral(" (") % dev.addresses.join(QStringLiteral(", ")) % QStringLiteral(")"));
}
2019-02-16 01:06:08 +01:00
case 2:
return dev.lastSeen.isNull() ? tr("unknown or own device")
: QString::fromLatin1(dev.lastSeen.toString(DateTimeOutputFormat::DateAndTime, true).data());
case 3:
return dev.compression;
case 4:
return dev.certName.isEmpty() ? tr("none") : dev.certName;
case 5:
return dev.introducer ? tr("yes") : tr("no");
case 6:
return QString::fromStdString(dataSizeToString(dev.totalIncomingTraffic));
case 7:
return QString::fromStdString(dataSizeToString(dev.totalOutgoingTraffic));
case 8:
return dev.clientVersion;
2016-08-25 00:45:32 +02:00
}
2019-02-16 01:06:08 +01:00
}
break;
case Qt::DecorationRole:
case DeviceDetailIcon:
if (index.column() == 0) {
// attribute icons
const auto &icons = m_brightColors ? fontAwesomeIconsForDarkTheme() : fontAwesomeIconsForLightTheme();
switch (index.row()) {
case 0:
return icons.hashtag;
case 1:
return icons.link;
case 2:
return icons.eye;
case 3:
return icons.fileArchive;
case 4:
return icons.certificate;
case 5:
return icons.networkWired;
case 6:
return icons.cloudDownloadAlt;
case 7:
return icons.cloudUploadAlt;
case 8:
return icons.tag;
}
}
break;
2019-02-16 01:06:08 +01:00
case Qt::ForegroundRole:
switch (index.column()) {
case 1:
const SyncthingDev &dev = m_devs[static_cast<size_t>(index.parent().row())];
switch (index.row()) {
case 2:
if (dev.lastSeen.isNull()) {
return Colors::gray(m_brightColors);
2016-08-25 00:45:32 +02:00
}
break;
2019-02-16 01:06:08 +01:00
case 4:
if (dev.certName.isEmpty()) {
return Colors::gray(m_brightColors);
}
2017-05-01 03:34:43 +02:00
break;
2016-08-25 00:45:32 +02:00
}
2019-02-16 01:06:08 +01:00
}
break;
case Qt::ToolTipRole:
switch (index.column()) {
case 1:
2019-04-10 18:25:41 +02:00
const SyncthingDev &dev = m_devs[static_cast<size_t>(index.parent().row())];
2019-02-16 01:06:08 +01:00
switch (index.row()) {
2019-04-10 18:25:41 +02:00
case 1:
if (!dev.connectionType.isEmpty()) {
return dev.connectionType;
}
break;
2019-02-16 01:06:08 +01:00
case 2:
if (!dev.lastSeen.isNull()) {
return agoString(dev.lastSeen);
}
2017-05-01 03:34:43 +02:00
break;
2016-08-25 00:45:32 +02:00
}
}
2019-02-16 01:06:08 +01:00
break;
default:;
}
return QVariant();
}
if (static_cast<size_t>(index.row()) >= m_devs.size()) {
return QVariant();
}
// dir IDs and status
const SyncthingDev &dev = m_devs[static_cast<size_t>(index.row())];
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch (index.column()) {
case 0:
return dev.name.isEmpty() ? dev.id : dev.name;
case 1:
return devStatusString(dev);
2016-08-25 00:45:32 +02:00
}
2019-02-16 01:06:08 +01:00
case Qt::DecorationRole:
switch (index.column()) {
case 0:
if (dev.paused) {
return statusIcons().pause;
} else {
switch (dev.status) {
case SyncthingDevStatus::Unknown:
case SyncthingDevStatus::Disconnected:
return statusIcons().disconnected;
case SyncthingDevStatus::OwnDevice:
case SyncthingDevStatus::Idle:
return statusIcons().idling;
case SyncthingDevStatus::Synchronizing:
return statusIcons().sync;
case SyncthingDevStatus::OutOfSync:
case SyncthingDevStatus::Rejected:
return statusIcons().error;
}
}
break;
}
break;
case Qt::TextAlignmentRole:
switch (index.column()) {
case 0:
break;
case 1:
return static_cast<int>(Qt::AlignRight | Qt::AlignVCenter);
}
break;
case Qt::ForegroundRole:
switch (index.column()) {
case 0:
break;
case 1:
return devStatusColor(dev);
}
break;
case DeviceStatus:
return static_cast<int>(dev.status);
case DevicePaused:
return dev.paused;
case IsOwnDevice:
return dev.status == SyncthingDevStatus::OwnDevice;
case DeviceStatusString:
return devStatusString(dev);
case DeviceStatusColor:
return devStatusColor(dev);
case DeviceId:
return dev.id;
default:;
2016-08-25 00:45:32 +02:00
}
return QVariant();
}
bool SyncthingDeviceModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
2019-05-04 22:10:37 +02:00
Q_UNUSED(index)
Q_UNUSED(value)
Q_UNUSED(role)
return false;
2016-08-25 00:45:32 +02:00
}
int SyncthingDeviceModel::rowCount(const QModelIndex &parent) const
{
2017-05-01 03:34:43 +02:00
if (!parent.isValid()) {
return static_cast<int>(m_devs.size());
2017-05-01 03:34:43 +02:00
} else if (!parent.parent().isValid()) {
2018-08-04 20:34:24 +02:00
// hide everything after introducer (eg. traffic) unless connected
const auto *const dev(devInfo(parent));
return dev && dev->isConnected() ? 9 : 6;
2016-08-25 00:45:32 +02:00
} else {
return 0;
}
}
int SyncthingDeviceModel::columnCount(const QModelIndex &parent) const
{
2017-05-01 03:34:43 +02:00
if (!parent.isValid()) {
2016-08-25 00:45:32 +02:00
return 2; // name/id, status
2017-05-01 03:34:43 +02:00
} else if (!parent.parent().isValid()) {
2016-08-25 00:45:32 +02:00
return 2; // field name and value
} else {
return 0;
}
}
2016-08-26 16:43:53 +02:00
void SyncthingDeviceModel::devStatusChanged(const SyncthingDev &, int index)
{
const QModelIndex modelIndex1(this->index(index, 0, QModelIndex()));
2018-08-04 15:47:43 +02:00
static const QVector<int> modelRoles1(
{ Qt::DisplayRole, Qt::EditRole, Qt::DecorationRole, DevicePaused, DeviceStatus, DeviceStatusString, DeviceStatusColor, DeviceId });
emit dataChanged(modelIndex1, modelIndex1, modelRoles1);
2016-08-26 16:43:53 +02:00
const QModelIndex modelIndex2(this->index(index, 1, QModelIndex()));
static const QVector<int> modelRoles2({ Qt::DisplayRole, Qt::EditRole, Qt::ForegroundRole });
emit dataChanged(modelIndex2, modelIndex2, modelRoles2);
static const QVector<int> modelRoles3({ Qt::DisplayRole, Qt::EditRole, Qt::ToolTipRole });
emit dataChanged(this->index(0, 1, modelIndex1), this->index(5, 1, modelIndex1), modelRoles3);
static const QVector<int> modelRoles4({ Qt::DisplayRole, Qt::EditRole, DeviceDetail });
emit dataChanged(this->index(0, 0, modelIndex1), this->index(5, 0, modelIndex1), modelRoles4);
}
void SyncthingDeviceModel::handleStatusIconsChanged()
{
emit dataChanged(index(0, 0), index(static_cast<int>(m_devs.size()) - 1, 0), QVector<int>({ Qt::DecorationRole }));
}
QString SyncthingDeviceModel::devStatusString(const SyncthingDev &dev)
{
if (dev.paused) {
return tr("Paused");
}
switch (dev.status) {
case SyncthingDevStatus::Unknown:
return tr("Unknown status");
case SyncthingDevStatus::OwnDevice:
return tr("Own device");
case SyncthingDevStatus::Idle:
return tr("Idle");
case SyncthingDevStatus::Disconnected:
return tr("Disconnected");
case SyncthingDevStatus::Synchronizing:
2020-03-01 22:04:49 +01:00
return dev.overallCompletion.needed.bytes > 0 ? tr("Synchronizing (%1 %, %2)")
.arg(static_cast<int>(dev.overallCompletion.percentage))
2020-03-01 22:04:49 +01:00
.arg(QString::fromStdString(dataSizeToString(dev.overallCompletion.needed.bytes)))
: tr("Synchronizing");
case SyncthingDevStatus::OutOfSync:
return tr("Out of sync");
case SyncthingDevStatus::Rejected:
return tr("Rejected");
}
return QString();
}
2017-09-01 17:13:01 +02:00
QVariant SyncthingDeviceModel::devStatusColor(const SyncthingDev &dev) const
{
if (dev.paused) {
2017-09-01 17:13:01 +02:00
return QVariant();
}
switch (dev.status) {
case SyncthingDevStatus::Unknown:
break;
case SyncthingDevStatus::Disconnected:
break;
case SyncthingDevStatus::OwnDevice:
case SyncthingDevStatus::Idle:
return Colors::green(m_brightColors);
case SyncthingDevStatus::Synchronizing:
return Colors::blue(m_brightColors);
case SyncthingDevStatus::OutOfSync:
case SyncthingDevStatus::Rejected:
return Colors::red(m_brightColors);
}
2017-09-01 17:13:01 +02:00
return QVariant();
2016-08-26 16:43:53 +02:00
}
2016-08-25 00:45:32 +02:00
} // namespace Data