This commit is contained in:
Trial97
2023-05-04 20:29:33 +03:00
65 changed files with 3390 additions and 2148 deletions

View File

@ -39,18 +39,23 @@
#include <FileSystem.h>
#include <QDebug>
#include <QFileSystemWatcher>
#include <QIcon>
#include <QMimeData>
#include <QString>
#include <QStyle>
#include <QThreadPool>
#include <QUrl>
#include <QUuid>
#include <algorithm>
#include "Application.h"
#include "minecraft/mod/tasks/LocalModParseTask.h"
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
#include "modplatform/ModIndex.h"
ModFolderModel::ModFolderModel(const QString &dir, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), nullptr, create_dir), m_is_indexed(is_indexed)
ModFolderModel::ModFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance, bool is_indexed, bool create_dir)
: ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
}
@ -97,8 +102,25 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
}
case Qt::ToolTipRole:
if (column == NAME_COLUMN) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
"\nCanonical Path: %1")
.arg(at(row)->fileinfo().canonicalFilePath());
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
return m_resources[row]->internal_id();
case Qt::DecorationRole: {
if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
return APPLICATION->getThemedIcon("status-yellow");
return {};
}
case Qt::CheckStateRole:
switch (column)
{

View File

@ -75,7 +75,7 @@ public:
Enable,
Toggle
};
ModFolderModel(const QString &dir, bool is_indexed = false, bool create_dir = true);
ModFolderModel(const QString &dir, std::shared_ptr<const BaseInstance> instance, bool is_indexed = false, bool create_dir = true);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

View File

@ -1,6 +1,8 @@
#include "Resource.h"
#include <QRegularExpression>
#include <QFileInfo>
#include "FileSystem.h"
@ -152,3 +154,21 @@ bool Resource::destroy()
return FS::deletePath(m_file_info.filePath());
}
bool Resource::isSymLinkUnder(const QString& instPath) const
{
if (isSymLink())
return true;
auto instDir = QDir(instPath);
auto relAbsPath = instDir.relativeFilePath(m_file_info.absoluteFilePath());
auto relCanonPath = instDir.relativeFilePath(m_file_info.canonicalFilePath());
return relAbsPath != relCanonPath;
}
bool Resource::isMoreThanOneHardLink() const
{
return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1;
}

View File

@ -94,6 +94,19 @@ class Resource : public QObject {
// Delete all files of this resource.
bool destroy();
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
/**
* @brief Take a instance path, checks if the file pointed to by the resource is a symlink or under a symlink in that instance
*
* @param instPath path to an instance directory
* @return true
* @return false
*/
[[nodiscard]] bool isSymLinkUnder(const QString& instPath) const;
[[nodiscard]] bool isMoreThanOneHardLink() const;
protected:
/* The file corresponding to this resource. */
QFileInfo m_file_info;

View File

@ -2,17 +2,22 @@
#include <QCoreApplication>
#include <QDebug>
#include <QFileInfo>
#include <QIcon>
#include <QMimeData>
#include <QStyle>
#include <QThreadPool>
#include <QUrl>
#include "Application.h"
#include "FileSystem.h"
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "tasks/Task.h"
ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent, bool create_dir) : QAbstractListModel(parent), m_dir(dir), m_watcher(this)
ResourceFolderModel::ResourceFolderModel(QDir dir, std::shared_ptr<const BaseInstance> instance, QObject* parent, bool create_dir)
: QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this)
{
if (create_dir) {
FS::ensureFolderPathExists(m_dir.absolutePath());
@ -22,7 +27,7 @@ ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent, bool create_
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged);
connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this]{ m_helper_thread_task.clear(); });
connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this] { m_helper_thread_task.clear(); });
}
ResourceFolderModel::~ResourceFolderModel()
@ -417,7 +422,26 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
return {};
}
case Qt::ToolTipRole:
if (column == NAME_COLUMN) {
if (at(row).isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
"\nCanonical Path: %1")
.arg(at(row).fileinfo().canonicalFilePath());;
}
if (at(row).isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
return m_resources[row]->internal_id();
case Qt::DecorationRole: {
if (column == NAME_COLUMN && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()))
return APPLICATION->getThemedIcon("status-yellow");
return {};
}
case Qt::CheckStateRole:
switch (column) {
case ACTIVE_COLUMN:
@ -531,3 +555,7 @@ void ResourceFolderModel::enableInteraction(bool enabled)
return (compare_result.first < 0);
return (compare_result.first > 0);
}
QString ResourceFolderModel::instDirPath() const {
return QFileInfo(m_instance->instanceRoot()).absoluteFilePath();
}

View File

@ -9,6 +9,8 @@
#include "Resource.h"
#include "BaseInstance.h"
#include "tasks/Task.h"
#include "tasks/ConcurrentTask.h"
@ -24,7 +26,7 @@ class QSortFilterProxyModel;
class ResourceFolderModel : public QAbstractListModel {
Q_OBJECT
public:
ResourceFolderModel(QDir, QObject* parent = nullptr, bool create_dir = true);
ResourceFolderModel(QDir, std::shared_ptr<const BaseInstance>, QObject* parent = nullptr, bool create_dir = true);
~ResourceFolderModel() override;
/** Starts watching the paths for changes.
@ -125,6 +127,8 @@ class ResourceFolderModel : public QAbstractListModel {
[[nodiscard]] bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override;
};
QString instDirPath() const;
public slots:
void enableInteraction(bool enabled);
void disableInteraction(bool disabled) { enableInteraction(!disabled); }
@ -187,6 +191,7 @@ class ResourceFolderModel : public QAbstractListModel {
bool m_can_interact = true;
QDir m_dir;
std::shared_ptr<const BaseInstance> m_instance;
QFileSystemWatcher m_watcher;
bool m_is_watching = false;

View File

@ -36,12 +36,17 @@
#include "ResourcePackFolderModel.h"
#include <QIcon>
#include <QStyle>
#include "Application.h"
#include "Version.h"
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir) : ResourceFolderModel(QDir(dir))
ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance)
: ResourceFolderModel(QDir(dir), instance)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
}
@ -78,12 +83,29 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
default:
return {};
}
case Qt::DecorationRole: {
if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink()))
return APPLICATION->getThemedIcon("status-yellow");
return {};
}
case Qt::ToolTipRole: {
if (column == PackFormatColumn) {
//: The string being explained by this is in the format: ID (Lower version - Upper version)
return tr("The resource pack format ID, as well as the Minecraft versions it was designed for.");
}
if (column == NAME_COLUMN) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
"\nCanonical Path: %1")
.arg(at(row)->fileinfo().canonicalFilePath());;
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
return m_resources[row]->internal_id();
}
case Qt::CheckStateRole:

View File

@ -17,7 +17,7 @@ public:
NUM_COLUMNS
};
explicit ResourcePackFolderModel(const QString &dir);
explicit ResourcePackFolderModel(const QString &dir, std::shared_ptr<const BaseInstance> instance);
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

View File

@ -6,5 +6,7 @@ class ShaderPackFolderModel : public ResourceFolderModel {
Q_OBJECT
public:
explicit ShaderPackFolderModel(const QString& dir) : ResourceFolderModel(QDir(dir)) {}
explicit ShaderPackFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance)
: ResourceFolderModel(QDir(dir), instance)
{}
};

View File

@ -39,7 +39,9 @@
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalTexturePackParseTask.h"
TexturePackFolderModel::TexturePackFolderModel(const QString &dir) : ResourceFolderModel(QDir(dir)) {}
TexturePackFolderModel::TexturePackFolderModel(const QString& dir, std::shared_ptr<const BaseInstance> instance)
: ResourceFolderModel(QDir(dir), instance)
{}
Task* TexturePackFolderModel::createUpdateTask()
{

View File

@ -43,7 +43,7 @@ class TexturePackFolderModel : public ResourceFolderModel
Q_OBJECT
public:
explicit TexturePackFolderModel(const QString &dir);
explicit TexturePackFolderModel(const QString &dir, std::shared_ptr<const BaseInstance> instance);
[[nodiscard]] Task* createUpdateTask() override;
[[nodiscard]] Task* createParseTask(Resource&) override;
};

View File

@ -242,7 +242,7 @@ ModDetails ReadQuiltModInfo(QByteArray contents)
return details;
}
ModDetails ReadForgeInfo(QByteArray contents)
ModDetails ReadForgeInfo(QString fileName)
{
ModDetails details;
// Read the data
@ -250,7 +250,7 @@ ModDetails ReadForgeInfo(QByteArray contents)
details.mod_id = "Forge";
details.homeurl = "http://www.minecraftforge.net/forum/";
INIFile ini;
if (!ini.loadFile(contents))
if (!ini.loadFile(fileName))
return details;
QString major = ini.get("forge.major.number", "0").toString();
@ -422,7 +422,7 @@ bool processZIP(Mod& mod, ProcessingLevel level)
return false;
}
details = ReadForgeInfo(file.readAll());
details = ReadForgeInfo(file.getFileName());
file.close();
zip.close();