#include "./syncthingdirectorymodel.h" #include "../connector/syncthingconnection.h" #include "../connector/utils.h" using namespace ChronoUtilities; namespace Data { SyncthingDirectoryModel::SyncthingDirectoryModel(SyncthingConnection &connection, QObject *parent) : QAbstractItemModel(parent), m_connection(connection), m_dirs(connection.dirInfo()), m_unknownIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-disconnected.svg"))), m_idleIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-ok.svg"))), m_syncIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-sync.svg"))), m_errorIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-error.svg"))), m_pausedIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-pause.svg"))), m_otherIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-default.svg"))) { connect(&m_connection, &SyncthingConnection::newConfig, this, &SyncthingDirectoryModel::newConfig); connect(&m_connection, &SyncthingConnection::newDirs, this, &SyncthingDirectoryModel::newDirs); connect(&m_connection, &SyncthingConnection::dirStatusChanged, this, &SyncthingDirectoryModel::dirStatusChanged); } /*! * \brief Returns the directory info for the spcified \a index. The returned object is not persistent. */ const SyncthingDir *SyncthingDirectoryModel::dirInfo(const QModelIndex &index) const { return (index.parent().isValid() ? dirInfo(index.parent()) : (static_cast(index.row()) < m_dirs.size() ? &m_dirs[static_cast(index.row())] : nullptr)); } QModelIndex SyncthingDirectoryModel::index(int row, int column, const QModelIndex &parent) const { if(!parent.isValid()) { // top-level: all dir labels/IDs if(row < rowCount(parent)) { return createIndex(row, column, static_cast(-1)); } } else if(!parent.parent().isValid()) { // dir-level: dir attributes if(row < rowCount(parent)) { return createIndex(row, column, static_cast(parent.row())); } } return QModelIndex(); } QModelIndex SyncthingDirectoryModel::parent(const QModelIndex &child) const { return child.internalId() != static_cast(-1) ? index(static_cast(child.internalId()), 0, QModelIndex()) : QModelIndex(); } QVariant SyncthingDirectoryModel::headerData(int section, Qt::Orientation orientation, int role) const { switch(orientation) { case Qt::Horizontal: switch(role) { case Qt::DisplayRole: switch(section) { case 0: return tr("ID"); case 1: return tr("Status"); } break; default: ; } break; default: ; } return QVariant(); } QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const { if(index.isValid()) { if(index.parent().isValid()) { // dir attributes if(static_cast(index.parent().row()) < m_dirs.size()) { switch(role) { case Qt::DisplayRole: case Qt::EditRole: switch(index.column()) { case 0: // attribute names switch(index.row()) { case 0: return tr("ID"); case 1: return tr("Path"); case 2: return tr("Devices"); case 3: return tr("Read-only"); case 4: return tr("Rescan interval"); case 5: return tr("Last scan"); case 6: return tr("Last file"); case 7: return tr("Errors"); } break; case 1: // attribute values const SyncthingDir &dir = m_dirs[static_cast(index.parent().row())]; switch(index.row()) { case 0: return dir.id; case 1: return dir.path; case 2: return dir.devices.join(QStringLiteral(", ")); case 3: return dir.readOnly ? tr("yes") : tr("no"); case 4: return QString::fromLatin1(TimeSpan::fromSeconds(dir.rescanInterval).toString(TimeSpanOutputFormat::WithMeasures, true).data()); case 5: return dir.lastScanTime.isNull() ? tr("unknown") : QString::fromLatin1(dir.lastScanTime.toString(DateTimeOutputFormat::DateAndTime, true).data()); case 6: return dir.lastFileName.isEmpty() ? tr("unknown") : dir.lastFileName; case 7: return dir.errors.empty() ? tr("none") : tr("%1 item(s) out of sync", nullptr, static_cast(dir.errors.size())).arg(dir.errors.size()); } break; } break; case Qt::ForegroundRole: switch(index.column()) { case 1: const SyncthingDir &dir = m_dirs[static_cast(index.parent().row())]; switch(index.row()) { case 5: if(dir.lastScanTime.isNull()) { return QColor(Qt::gray); } break; case 6: if(dir.lastFileName.isEmpty()) { return QColor(Qt::gray); } else if(dir.lastFileDeleted) { return QColor(Qt::red); } break; case 7: return dir.errors.empty() ? QColor(Qt::gray) : QColor(Qt::red); } } break; case Qt::ToolTipRole: switch(index.column()) { case 1: const SyncthingDir &dir = m_dirs[static_cast(index.parent().row())]; switch(index.row()) { case 5: if(!dir.lastScanTime.isNull()) { return agoString(dir.lastScanTime); } break; case 6: if(!dir.lastFileTime.isNull()) { if(dir.lastFileDeleted) { return tr("Deleted at %1").arg(QString::fromLatin1(dir.lastFileTime.toString(DateTimeOutputFormat::DateAndTime, true).data())); } else { return tr("Updated at %1").arg(QString::fromLatin1(dir.lastFileTime.toString(DateTimeOutputFormat::DateAndTime, true).data())); } } break; } } default: ; } } } else if(static_cast(index.row()) < m_dirs.size()) { // dir IDs and status const SyncthingDir &dir = m_dirs[static_cast(index.row())]; switch(role) { case Qt::DisplayRole: case Qt::EditRole: switch(index.column()) { case 0: return dir.label.isEmpty() ? dir.id : dir.label; case 1: switch(dir.status) { case SyncthingDirStatus::Unknown: return tr("Unknown status"); case SyncthingDirStatus::Idle: return tr("Idle"); case SyncthingDirStatus::Scanning: return dir.progressPercentage > 0 ? tr("Scanning (%1 %)").arg(dir.progressPercentage) : tr("Scanning"); case SyncthingDirStatus::Synchronizing: return dir.progressPercentage > 0 ? tr("Synchronizing (%1 %)").arg(dir.progressPercentage) : tr("Synchronizing"); case SyncthingDirStatus::Paused: return tr("Paused"); case SyncthingDirStatus::OutOfSync: return tr("Out of sync"); } break; } break; case Qt::DecorationRole: switch(index.column()) { case 0: switch(dir.status) { case SyncthingDirStatus::Unknown: return m_unknownIcon; case SyncthingDirStatus::Idle: return m_idleIcon; case SyncthingDirStatus::Scanning: return m_otherIcon; case SyncthingDirStatus::Synchronizing: return m_syncIcon; case SyncthingDirStatus::Paused: return m_pausedIcon; case SyncthingDirStatus::OutOfSync: return m_errorIcon; } break; } break; case Qt::TextAlignmentRole: switch(index.column()) { case 0: break; case 1: return static_cast(Qt::AlignRight | Qt::AlignVCenter); } break; case Qt::ForegroundRole: switch(index.column()) { case 0: break; case 1: switch(dir.status) { case SyncthingDirStatus::Unknown: break; case SyncthingDirStatus::Idle: return QColor(Qt::darkGreen); case SyncthingDirStatus::Scanning: return QColor(Qt::blue); case SyncthingDirStatus::Synchronizing: return QColor(Qt::blue); case SyncthingDirStatus::Paused: break; case SyncthingDirStatus::OutOfSync: return QColor(Qt::red); } break; } break; default: ; } } } return QVariant(); } bool SyncthingDirectoryModel::setData(const QModelIndex &index, const QVariant &value, int role) { Q_UNUSED(index) Q_UNUSED(value) Q_UNUSED(role) return false; } int SyncthingDirectoryModel::rowCount(const QModelIndex &parent) const { if(!parent.isValid()) { return static_cast(m_dirs.size()); } else if(!parent.parent().isValid()) { return 8; } else { return 0; } } int SyncthingDirectoryModel::columnCount(const QModelIndex &parent) const { if(!parent.isValid()) { return 2; // label/ID, status/buttons } else if(!parent.parent().isValid()) { return 2; // field name and value } else { return 0; } } void SyncthingDirectoryModel::newConfig() { beginResetModel(); } void SyncthingDirectoryModel::newDirs() { endResetModel(); } void SyncthingDirectoryModel::dirStatusChanged(const SyncthingDir &, int index) { const QModelIndex modelIndex1(this->index(index, 0, QModelIndex())); emit dataChanged(modelIndex1, modelIndex1, QVector() << Qt::DecorationRole); const QModelIndex modelIndex2(this->index(index, 1, QModelIndex())); emit dataChanged(modelIndex2, modelIndex2, QVector() << Qt::DisplayRole << Qt::ForegroundRole); emit dataChanged(this->index(0, 1, modelIndex1), this->index(7, 1, modelIndex1), QVector() << Qt::DisplayRole); } } // namespace Data