syncthingtray/model/syncthingdownloadmodel.cpp
Martchus 975e86c895 Allow backend libraries to be used from other projects
So far the backend libraries' include paths were relative within this
repository. This means the header files could not be used at their
installed location.

This change replaces them with "<>" includes to fix that problem and adds
a new include directory so building everything at once still works.

With this change it should be easier to actually split some parts into
another repository if this one would become too big.
2021-01-25 19:48:11 +01:00

308 lines
11 KiB
C++

#include "./syncthingdownloadmodel.h"
#include <syncthingconnector/syncthingconnection.h>
#include <syncthingconnector/utils.h>
#include <QStringBuilder>
using namespace std;
using namespace CppUtilities;
namespace Data {
SyncthingDownloadModel::PendingDir::PendingDir(const SyncthingDir *syncthingDir, unsigned int pendingItems)
: syncthingDir(syncthingDir)
, pendingItems(pendingItems)
{
}
bool SyncthingDownloadModel::PendingDir::operator==(const SyncthingDir *dir) const
{
return syncthingDir == dir;
}
SyncthingDownloadModel::SyncthingDownloadModel(SyncthingConnection &connection, QObject *parent)
: SyncthingModel(connection, parent)
, m_dirs(connection.dirInfo())
, m_unknownIcon(
QIcon::fromTheme(QStringLiteral("text-x-generic"), QIcon(QStringLiteral(":/icons/hicolor/scalable/mimetypes/text-x-generic.svg"))))
, m_pendingDownloads(0)
, m_singleColumnMode(true)
{
connect(&m_connection, &SyncthingConnection::downloadProgressChanged, this, &SyncthingDownloadModel::downloadProgressChanged);
}
QHash<int, QByteArray> SyncthingDownloadModel::roleNames() const
{
const static QHash<int, QByteArray> roles{
{ Qt::DisplayRole, "name" },
{ Qt::DecorationRole, "fileIcon" },
{ ItemPercentage, "percentage" },
{ ItemProgressLabel, "progressLabel" },
{ ItemPath, "path" },
};
return roles;
}
/*!
* \brief Returns the directory info for the spcified \a index. The returned object is not persistent.
*/
const SyncthingDir *SyncthingDownloadModel::dirInfo(const QModelIndex &index) const
{
return (index.parent().isValid()
? dirInfo(index.parent())
: (static_cast<size_t>(index.row()) < m_pendingDirs.size() ? m_pendingDirs[static_cast<size_t>(index.row())].syncthingDir : nullptr));
}
const SyncthingItemDownloadProgress *SyncthingDownloadModel::progressInfo(const QModelIndex &index) const
{
if (index.parent().isValid() && static_cast<size_t>(index.parent().row()) < m_pendingDirs.size()
&& static_cast<size_t>(index.row()) < m_pendingDirs[static_cast<size_t>(index.parent().row())].syncthingDir->downloadingItems.size()) {
return &(m_pendingDirs[static_cast<size_t>(index.parent().row())].syncthingDir->downloadingItems[static_cast<size_t>(index.row())]);
} else {
return nullptr;
}
}
QModelIndex SyncthingDownloadModel::index(int row, int column, const QModelIndex &parent) const
{
if (!parent.isValid()) {
// top-level: all pending dir labels/IDs
if (row < rowCount(parent)) {
return createIndex(row, column, static_cast<quintptr>(-1));
}
} else if (!parent.parent().isValid()) {
// dir-level: pending downloads
if (row < rowCount(parent)) {
return createIndex(row, column, static_cast<quintptr>(parent.row()));
}
}
return QModelIndex();
}
QModelIndex SyncthingDownloadModel::parent(const QModelIndex &child) const
{
return child.internalId() != static_cast<quintptr>(-1) ? index(static_cast<int>(child.internalId()), 0, QModelIndex()) : QModelIndex();
}
QVariant SyncthingDownloadModel::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("Dir/item");
case 1:
return tr("Progress");
}
break;
default:;
}
break;
default:;
}
return QVariant();
}
QVariant SyncthingDownloadModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (index.parent().isValid()) {
// downloading items (of dir)
if (static_cast<size_t>(index.parent().row()) >= m_pendingDirs.size()) {
return QVariant();
}
const SyncthingDir &dir = *m_pendingDirs[static_cast<size_t>(index.parent().row())].syncthingDir;
if (static_cast<size_t>(index.row()) < dir.downloadingItems.size()) {
const SyncthingItemDownloadProgress &progress = dir.downloadingItems[static_cast<size_t>(index.row())];
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch (index.column()) {
case 0: // file names
return progress.relativePath;
case 1: // progress information
return progress.label;
}
break;
case Qt::ToolTipRole:
break;
case Qt::DecorationRole:
switch (index.column()) {
case 0: // file icon
return progress.fileInfo.exists() ? m_fileIconProvider.icon(progress.fileInfo) : m_unknownIcon;
default:;
}
break;
case ItemPercentage:
return progress.downloadPercentage;
case ItemProgressLabel:
return progress.label;
case ItemPath:
return dir.path + progress.relativePath;
default:;
}
}
return QVariant();
}
if (static_cast<size_t>(index.row()) >= m_pendingDirs.size()) {
return QVariant();
}
// dir IDs and overall dir progress
const SyncthingDir &dir = *m_pendingDirs[static_cast<size_t>(index.row())].syncthingDir;
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch (index.column()) {
case 0:
return QVariant(
(dir.label.isEmpty() ? dir.id : dir.label) % QChar(' ') % QChar('(') % QString::number(dir.downloadingItems.size()) % QChar(')'));
case 1:
return dir.downloadLabel;
}
break;
case Qt::TextAlignmentRole:
switch (index.column()) {
case 0:
break;
case 1:
return static_cast<int>(Qt::AlignRight | Qt::AlignVCenter);
}
break;
case ItemPercentage:
return dir.downloadPercentage;
case ItemProgressLabel:
return dir.downloadLabel;
case ItemPath:
return dir.path;
default:;
}
return QVariant();
}
bool SyncthingDownloadModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
Q_UNUSED(index)
Q_UNUSED(value)
Q_UNUSED(role)
return false;
}
int SyncthingDownloadModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return static_cast<int>(m_pendingDirs.size());
} else if (!parent.parent().isValid() && parent.row() >= 0 && static_cast<size_t>(parent.row()) < m_pendingDirs.size()) {
return static_cast<int>(m_pendingDirs[static_cast<size_t>(parent.row())].pendingItems);
} else {
return 0;
}
}
int SyncthingDownloadModel::columnCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return singleColumnMode() ? 1 : 2; // label/ID, status/progress
} else if (!parent.parent().isValid()) {
return singleColumnMode() ? 1 : 2; // file, progress
} else {
return 0;
}
}
void SyncthingDownloadModel::handleConfigInvalidated()
{
beginResetModel();
m_pendingDirs.clear();
endResetModel();
}
void SyncthingDownloadModel::handleNewConfigAvailable()
{
m_pendingDirs.reserve(m_connection.dirInfo().size());
}
void SyncthingDownloadModel::downloadProgressChanged()
{
int row = 0;
// iterate through all directories ...
for (const SyncthingDir &dirInfo : m_connection.dirInfo()) {
// ... and check whether the directory has been pending before
const auto pendingIterator = find(m_pendingDirs.begin(), m_pendingDirs.end(), &dirInfo);
// check whether the directory has downloading items ("is pending")
if (dirInfo.downloadingItems.empty()) {
// check whether the non-pending directory was pending before
if (pendingIterator != m_pendingDirs.end()) {
// remove the row for the previously pending directory
beginRemoveRows(QModelIndex(), row, row);
m_pendingDirs.erase(pendingIterator);
endRemoveRows();
}
} else {
// check whether the pending directory was pending before
if (pendingIterator != m_pendingDirs.end()) {
// get parent index and number of previous/new pending items
const auto parentIndex(index(row, 0));
const auto pendingItemCount(pendingIterator->pendingItems);
const auto newPendingItemCount(dirInfo.downloadingItems.size());
// insert/remove rows missing/surplus rows
if (pendingItemCount < newPendingItemCount) {
beginInsertRows(parentIndex, static_cast<int>(pendingItemCount), static_cast<int>(newPendingItemCount - 1));
pendingIterator->pendingItems = newPendingItemCount;
endInsertRows();
} else if (pendingItemCount > newPendingItemCount) {
beginRemoveRows(parentIndex, static_cast<int>(newPendingItemCount), static_cast<int>(pendingItemCount - 1));
pendingIterator->pendingItems = newPendingItemCount;
endRemoveRows();
}
// update the present items
if (newPendingItemCount) {
static const QVector<int> roles({ Qt::DisplayRole, Qt::EditRole, Qt::DecorationRole, Qt::ForegroundRole, Qt::ToolTipRole,
ItemPercentage, ItemProgressLabel, ItemPath });
emit dataChanged(parentIndex, index(row, 1), roles);
emit dataChanged(index(0, 0, parentIndex), index(static_cast<int>(newPendingItemCount - 1), 1, parentIndex), roles);
}
} else {
// add the new directory
beginInsertRows(QModelIndex(), row, row);
m_pendingDirs.emplace_back(&dirInfo, 0);
endInsertRows();
// add new pending items
beginInsertRows(index(row, row), 0, static_cast<int>(dirInfo.downloadingItems.size() - 1));
m_pendingDirs.back().pendingItems = dirInfo.downloadingItems.size();
endInsertRows();
}
++row;
}
}
}
void SyncthingDownloadModel::setSingleColumnMode(bool singleColumnModeEnabled)
{
if (m_singleColumnMode != singleColumnModeEnabled) {
if (m_singleColumnMode) {
beginInsertColumns(QModelIndex(), 1, 1);
m_singleColumnMode = true;
endInsertColumns();
} else {
beginRemoveColumns(QModelIndex(), 1, 1);
m_singleColumnMode = false;
endRemoveColumns();
}
}
}
} // namespace Data