2023-08-31 09:09:52 +02:00
|
|
|
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
2023-08-31 13:57:26 +02:00
|
|
|
#include "artmodel.h"
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-08-31 13:34:34 +02:00
|
|
|
#include <KLocalizedString>
|
2023-03-27 12:49:53 -04:00
|
|
|
#include <QDirIterator>
|
|
|
|
#include <QFile>
|
|
|
|
#include <QFileInfo>
|
|
|
|
#include <QIcon>
|
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QPixmap>
|
2023-06-15 13:54:18 -04:00
|
|
|
#include <QtConcurrent>
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-08-31 13:55:17 +02:00
|
|
|
ArtModel::ArtModel(const QDir &definitionDirectory, const QDir &assetDirectory, QObject *parent)
|
|
|
|
: QAbstractTableModel(parent)
|
2023-08-31 13:39:00 +02:00
|
|
|
{
|
|
|
|
piecesFuture = new QFutureWatcher<ArtPiece>(this);
|
2023-08-31 13:39:59 +02:00
|
|
|
connect(piecesFuture, &QFutureWatcher<ArtPiece>::resultReadyAt, this, &ArtModel::pieceFinished);
|
|
|
|
connect(piecesFuture, &QFutureWatcher<ArtPiece>::finished, this, &ArtModel::finished);
|
|
|
|
|
|
|
|
struct PieceInformation {
|
|
|
|
QString definition;
|
|
|
|
QString asset;
|
|
|
|
};
|
|
|
|
QVector<PieceInformation> pieceList;
|
2023-08-31 13:55:17 +02:00
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
QDirIterator it(definitionDirectory);
|
|
|
|
while (it.hasNext()) {
|
|
|
|
QFileInfo info(it.next());
|
|
|
|
if (!info.isFile()) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-08-31 13:39:00 +02:00
|
|
|
pieceList.push_back(PieceInformation{definitionDirectory.absoluteFilePath(info.baseName()), assetDirectory.absoluteFilePath(info.baseName())});
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-08-31 13:39:00 +02:00
|
|
|
beginInsertRows(QModelIndex(), m_artPieces.size(), m_artPieces.size() + 1);
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-08-31 15:14:25 +02:00
|
|
|
m_artPieces.push_back({});
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
endInsertRows();
|
|
|
|
}
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-09-02 09:47:58 -04:00
|
|
|
const std::function<ArtPiece(const PieceInformation &info)> loadPiece = [](const PieceInformation &info) -> ArtPiece {
|
2023-08-31 14:08:43 +02:00
|
|
|
ArtPiece p(info.definition, info.asset);
|
2023-06-15 13:54:18 -04:00
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
p.image.load(p.filename);
|
|
|
|
p.thumbnail = QPixmap::fromImage(p.image).scaled(100, 100, Qt::AspectRatioMode::KeepAspectRatio).toImage();
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
return p;
|
|
|
|
};
|
2023-06-15 13:54:18 -04:00
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
piecesFuture->setFuture(QtConcurrent::mapped(pieceList, loadPiece));
|
2023-03-27 12:49:53 -04:00
|
|
|
}
|
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
int ArtModel::rowCount(const QModelIndex &parent) const
|
|
|
|
{
|
2023-08-31 13:34:34 +02:00
|
|
|
Q_UNUSED(parent);
|
|
|
|
return m_artPieces.size();
|
2023-03-27 12:49:53 -04:00
|
|
|
}
|
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
int ArtModel::columnCount(const QModelIndex &parent) const
|
|
|
|
{
|
2023-08-31 13:34:34 +02:00
|
|
|
Q_UNUSED(parent);
|
|
|
|
return 4;
|
2023-03-27 12:49:53 -04:00
|
|
|
}
|
|
|
|
|
2023-08-31 13:55:17 +02:00
|
|
|
QVariant ArtModel::data(const QModelIndex &index, const int role) const
|
2023-08-31 13:39:59 +02:00
|
|
|
{
|
2023-08-31 13:55:17 +02:00
|
|
|
if (!index.isValid()) {
|
2023-08-31 13:39:59 +02:00
|
|
|
return {};
|
2023-08-31 13:55:17 +02:00
|
|
|
}
|
2023-08-31 13:39:59 +02:00
|
|
|
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
|
|
switch (index.column()) {
|
2023-12-01 15:51:12 -05:00
|
|
|
case 0: {
|
|
|
|
const QString filename = m_artPieces[index.row()].filename;
|
|
|
|
return filename.split('/').last();
|
|
|
|
}
|
2023-08-31 13:39:59 +02:00
|
|
|
case 1:
|
|
|
|
return {};
|
|
|
|
case 2:
|
|
|
|
return m_artPieces[index.row()].title;
|
|
|
|
case 3:
|
|
|
|
return m_artPieces[index.row()].hasAltText;
|
2023-08-31 13:34:34 +02:00
|
|
|
}
|
2023-08-31 13:39:59 +02:00
|
|
|
} else if (role == Qt::UserRole) {
|
|
|
|
return m_artPieces[index.row()].object;
|
|
|
|
} else if (role == Qt::DecorationRole) {
|
|
|
|
switch (index.column()) {
|
|
|
|
case 1:
|
|
|
|
return m_artPieces[index.row()].thumbnail;
|
|
|
|
case 3:
|
|
|
|
return m_artPieces[index.row()].hasAltText ? QIcon::fromTheme(QStringLiteral("emblem-checked")) : QIcon::fromTheme(QStringLiteral("emblem-error"));
|
|
|
|
}
|
|
|
|
} else if (role == Qt::UserRole + 1) {
|
|
|
|
return m_artPieces[index.row()].jsonFilename;
|
|
|
|
}
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
return {};
|
2023-03-27 12:49:53 -04:00
|
|
|
}
|
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
QVariant ArtModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
{
|
|
|
|
if (orientation == Qt::Orientation::Horizontal && role == Qt::DisplayRole) {
|
|
|
|
switch (section) {
|
|
|
|
case 0:
|
|
|
|
return i18nc("@title:column", "Filename");
|
|
|
|
case 1:
|
|
|
|
return i18nc("@title:column", "Image");
|
|
|
|
case 2:
|
|
|
|
return i18nc("@title:column", "Title");
|
|
|
|
case 3:
|
|
|
|
return i18nc("@title:column", "Has Alt Text");
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
2023-03-27 12:49:53 -04:00
|
|
|
|
2023-08-31 13:39:59 +02:00
|
|
|
return QAbstractTableModel::headerData(section, orientation, role);
|
|
|
|
}
|
2023-06-15 13:54:18 -04:00
|
|
|
|
2023-08-31 14:08:43 +02:00
|
|
|
void ArtModel::pieceFinished(const int row)
|
|
|
|
{
|
|
|
|
m_artPieces[row] = piecesFuture->resultAt(row);
|
|
|
|
|
|
|
|
Q_EMIT dataChanged(index(row, 0), index(row + 1, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArtModel::finished()
|
|
|
|
{
|
|
|
|
std::sort(m_artPieces.begin(), m_artPieces.end(), [](const ArtPiece &a, const ArtPiece &b) {
|
|
|
|
return a.date > b.date;
|
|
|
|
});
|
|
|
|
|
2023-11-29 22:51:37 -05:00
|
|
|
Q_EMIT dataChanged(index(0, 0), index(m_artPieces.size(), 3));
|
2023-09-02 09:47:58 -04:00
|
|
|
|
|
|
|
Q_EMIT loadingFinished();
|
2023-08-31 14:08:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ArtPiece::ArtPiece(const QString &filename, const QString &assetFilename)
|
2023-08-31 13:39:59 +02:00
|
|
|
{
|
2023-08-31 14:08:43 +02:00
|
|
|
jsonFilename = filename + ".json";
|
|
|
|
this->filename = assetFilename;
|
2023-08-31 13:39:59 +02:00
|
|
|
|
2023-08-31 14:08:43 +02:00
|
|
|
QFile artFile(jsonFilename);
|
2023-08-31 13:39:59 +02:00
|
|
|
artFile.open(QFile::ReadOnly);
|
2023-08-31 13:55:17 +02:00
|
|
|
|
|
|
|
const QJsonDocument artJson = QJsonDocument::fromJson(artFile.readAll());
|
2023-08-31 13:39:59 +02:00
|
|
|
|
|
|
|
if (artJson[QStringLiteral("date")].toString().contains(QStringLiteral("-"))) {
|
2023-08-31 14:08:43 +02:00
|
|
|
date = QDate::fromString(artJson[QStringLiteral("date")].toString(), QStringLiteral("yyyy-MM-dd"));
|
2023-08-31 13:39:59 +02:00
|
|
|
} else {
|
2023-08-31 14:08:43 +02:00
|
|
|
date = QDate::fromString(artJson[QStringLiteral("date")].toString(), QStringLiteral("yyyy"));
|
2023-08-31 13:39:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (artJson.object().contains(QStringLiteral("title"))) {
|
2023-08-31 14:08:43 +02:00
|
|
|
title = artJson.object()[QStringLiteral("title")].toString();
|
2023-08-31 13:39:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (artJson.object().contains(QStringLiteral("alt_text"))) {
|
2023-08-31 14:08:43 +02:00
|
|
|
hasAltText = true;
|
2023-08-31 13:39:59 +02:00
|
|
|
}
|
2023-06-15 13:54:18 -04:00
|
|
|
}
|