Disable/enable mods with checkboxes. Needs testing.

A lot of testing!
This commit is contained in:
Petr Mrázek 2013-12-26 05:14:32 +01:00
parent 449f55c3e6
commit acf25d8a33
7 changed files with 277 additions and 86 deletions

View File

@ -30,7 +30,7 @@ void MCModInfoFrame::updateWithMod(Mod &m)
QString text = ""; QString text = "";
QString name = ""; QString name = "";
if(m.name().isEmpty()) name = m.id(); if(m.name().isEmpty()) name = m.mmc_id();
else name = m.name(); else name = m.name();
if(m.homeurl().isEmpty()) text = name; if(m.homeurl().isEmpty()) text = name;

View File

@ -44,8 +44,9 @@ void ModListView::setModel ( QAbstractItemModel* model )
QTreeView::setModel ( model ); QTreeView::setModel ( model );
auto head = header(); auto head = header();
head->setStretchLastSection(false); head->setStretchLastSection(false);
head->setSectionResizeMode(0, QHeaderView::Stretch); head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
for(int i = 1; i < head->count(); i++) head->setSectionResizeMode(1, QHeaderView::Stretch);
for(int i = 2; i < head->count(); i++)
head->setSectionResizeMode(i, QHeaderView::ResizeToContents); head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
dropIndicatorPosition(); dropIndicatorPosition();
} }

View File

@ -150,6 +150,7 @@ std::shared_ptr<ModList> LegacyInstance::jarModList()
void LegacyInstance::jarModsChanged() void LegacyInstance::jarModsChanged()
{ {
QLOG_INFO() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt.";
setShouldRebuild(true); setShouldRebuild(true);
} }

View File

@ -35,20 +35,40 @@ Mod::Mod(const QFileInfo &file)
void Mod::repath(const QFileInfo &file) void Mod::repath(const QFileInfo &file)
{ {
m_file = file; m_file = file;
m_name = file.completeBaseName(); QString name_base = file.fileName();
m_id = file.fileName();
m_type = Mod::MOD_UNKNOWN; m_type = Mod::MOD_UNKNOWN;
if (m_file.isDir()) if (m_file.isDir())
{
m_type = MOD_FOLDER; m_type = MOD_FOLDER;
m_name = name_base;
m_mmc_id = name_base;
}
else if (m_file.isFile()) else if (m_file.isFile())
{ {
QString ext = m_file.suffix().toLower(); if(name_base.endsWith(".disabled"))
if (ext == "zip" || ext == "jar") {
m_type = MOD_ZIPFILE; m_enabled = false;
name_base.chop(9);
}
else else
{
m_enabled = true;
}
m_mmc_id = name_base;
if (name_base.endsWith(".zip") || name_base.endsWith(".jar"))
{
m_type = MOD_ZIPFILE;
name_base.chop(4);
}
else
{
m_type = MOD_SINGLEFILE; m_type = MOD_SINGLEFILE;
} }
m_name = name_base;
}
if (m_type == MOD_ZIPFILE) if (m_type == MOD_ZIPFILE)
{ {
QuaZip zip(m_file.filePath()); QuaZip zip(m_file.filePath());
@ -114,7 +134,7 @@ void Mod::ReadMCModInfo(QByteArray contents)
if (!arr.at(0).isObject()) if (!arr.at(0).isObject())
return; return;
auto firstObj = arr.at(0).toObject(); auto firstObj = arr.at(0).toObject();
m_id = firstObj.value("modid").toString(); m_mod_id = firstObj.value("modid").toString();
m_name = firstObj.value("name").toString(); m_name = firstObj.value("name").toString();
m_version = firstObj.value("version").toString(); m_version = firstObj.value("version").toString();
m_homeurl = firstObj.value("url").toString(); m_homeurl = firstObj.value("url").toString();
@ -163,7 +183,7 @@ void Mod::ReadForgeInfo(QByteArray contents)
{ {
// Read the data // Read the data
m_name = "Minecraft Forge"; m_name = "Minecraft Forge";
m_id = "Forge"; m_mod_id = "Forge";
m_homeurl = "http://www.minecraftforge.net/forum/"; m_homeurl = "http://www.minecraftforge.net/forum/";
INIFile ini; INIFile ini;
if (!ini.loadFile(contents)) if (!ini.loadFile(contents))
@ -183,9 +203,11 @@ bool Mod::replace(Mod &with)
return false; return false;
bool success = false; bool success = false;
auto t = with.type(); auto t = with.type();
if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE) if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE)
{ {
success = QFile::copy(with.m_file.filePath(), m_file.path()); QLOG_DEBUG() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath();
success = QFile::copy(with.m_file.filePath(), m_file.filePath());
} }
if (t == MOD_FOLDER) if (t == MOD_FOLDER)
{ {
@ -193,11 +215,17 @@ bool Mod::replace(Mod &with)
} }
if (success) if (success)
{ {
m_id = with.m_id;
m_mcversion = with.m_mcversion;
m_type = with.m_type;
m_name = with.m_name; m_name = with.m_name;
m_mmc_id = with.m_mmc_id;
m_mod_id = with.m_mod_id;
m_version = with.m_version; m_version = with.m_version;
m_mcversion = with.m_mcversion;
m_description = with.m_description;
m_authors = with.m_authors;
m_credits = with.m_credits;
m_homeurl = with.m_homeurl;
m_type = with.m_type;
m_file.refresh();
} }
return success; return success;
} }
@ -241,3 +269,42 @@ QString Mod::version() const
return "VOID"; return "VOID";
} }
} }
bool Mod::enable(bool value)
{
if(m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER)
return false;
if(m_enabled == value)
return false;
QString path = m_file.absoluteFilePath();
if(value)
{
QFile foo(path);
if(!path.endsWith(".disabled"))
return false;
path.chop(9);
if(!foo.rename(path))
return false;
}
else
{
QFile foo(path);
path += ".disabled";
if(!foo.rename(path))
return false;
}
m_file = QFileInfo(path);
m_enabled = value;
return true;
}
bool Mod::operator==(const Mod &other) const
{
return mmc_id() == other.mmc_id();
}
bool Mod::strongCompare(const Mod &other) const
{
return mmc_id() == other.mmc_id() &&
version() == other.version() && type() == other.type();
}

View File

@ -33,9 +33,13 @@ public:
{ {
return m_file; return m_file;
} }
QString id() const QString mmc_id() const
{ {
return m_id; return m_mmc_id;
}
QString mod_id() const
{
return m_mod_id;
} }
ModType type() const ModType type() const
{ {
@ -77,6 +81,13 @@ public:
return m_credits; return m_credits;
} }
bool enabled() const
{
return m_enabled;
}
bool enable(bool value);
// delete all the files of this mod // delete all the files of this mod
bool destroy(); bool destroy();
// replace this mod with a copy of the other // replace this mod with a copy of the other
@ -85,15 +96,8 @@ public:
void repath(const QFileInfo &file); void repath(const QFileInfo &file);
// WEAK compare operator - used for replacing mods // WEAK compare operator - used for replacing mods
bool operator==(const Mod &other) const bool operator==(const Mod &other) const;
{ bool strongCompare(const Mod &other) const;
return filename() == other.filename();
}
bool strongCompare(const Mod &other) const
{
return filename() == other.filename() && id() == other.id() &&
version() == other.version() && type() == other.type();
}
private: private:
void ReadMCModInfo(QByteArray contents); void ReadMCModInfo(QByteArray contents);
@ -108,7 +112,9 @@ protected:
*/ */
QFileInfo m_file; QFileInfo m_file;
QString m_id; QString m_mmc_id;
QString m_mod_id;
bool m_enabled = true;
QString m_name; QString m_name;
QString m_version; QString m_version;
QString m_mcversion; QString m_mcversion;

View File

@ -19,6 +19,7 @@
#include <QMimeData> #include <QMimeData>
#include <QUrl> #include <QUrl>
#include <QUuid> #include <QUuid>
#include <QString>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include "logger/QsLog.h" #include "logger/QsLog.h"
@ -27,7 +28,7 @@ ModList::ModList(const QString &dir, const QString &list_file)
{ {
m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs |
QDir::NoSymLinks); QDir::NoSymLinks);
m_dir.setSorting(QDir::Name); m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
m_list_id = QUuid::createUuid().toString(); m_list_id = QUuid::createUuid().toString();
m_watcher = new QFileSystemWatcher(this); m_watcher = new QFileSystemWatcher(this);
is_watching = false; is_watching = false;
@ -66,52 +67,79 @@ bool ModList::update()
if (!isValid()) if (!isValid())
return false; return false;
QList<Mod> orderedMods;
QList<Mod> newMods; QList<Mod> newMods;
m_dir.refresh(); m_dir.refresh();
auto folderContents = m_dir.entryInfoList(); auto folderContents = m_dir.entryInfoList();
bool orderWasInvalid = false; bool orderOrStateChanged = false;
// first, process the ordered items (if any) // first, process the ordered items (if any)
QStringList listOrder = readListFile(); OrderList listOrder = readListFile();
for (auto item : listOrder) for (auto item : listOrder)
{ {
QFileInfo info(m_dir.filePath(item)); QFileInfo infoEnabled(m_dir.filePath(item.id));
int idx = folderContents.indexOf(info); QFileInfo infoDisabled(m_dir.filePath(item.id + ".disabled"));
int idxEnabled = folderContents.indexOf(infoEnabled);
int idxDisabled = folderContents.indexOf(infoDisabled);
// if both enabled and disabled versions are present, PANIC!
if (idxEnabled >= 0 && idxDisabled >= 0)
{
return false;
}
bool isEnabled = idxEnabled >= 0;
int idx = isEnabled ? idxEnabled : idxDisabled;
QFileInfo info = isEnabled ? infoEnabled : infoDisabled;
// if the file from the index file exists // if the file from the index file exists
if (idx != -1) if (idx != -1)
{ {
// remove from the actual folder contents list // remove from the actual folder contents list
folderContents.takeAt(idx); folderContents.takeAt(idx);
// append the new mod // append the new mod
newMods.append(Mod(info)); orderedMods.append(Mod(info));
if (isEnabled != item.enabled)
orderOrStateChanged = true;
} }
else else
{ {
orderWasInvalid = true; orderOrStateChanged = true;
} }
} }
// if there are any untracked files...
if (folderContents.size())
{
// the order surely changed!
for (auto entry : folderContents) for (auto entry : folderContents)
{ {
newMods.append(Mod(entry)); newMods.append(Mod(entry));
} }
if (mods.size() != newMods.size()) std::sort(newMods.begin(), newMods.end(), [](const Mod & left, const Mod & right)
{ { return left.name().localeAwareCompare(right.name()) <= 0; });
orderWasInvalid = true; orderedMods.append(newMods);
orderOrStateChanged = true;
} }
// otherwise, if we were already tracking some mods
else if (mods.size())
{
// if the number doesn't match, order changed.
if (mods.size() != orderedMods.size())
orderOrStateChanged = true;
// if it does match, compare the mods themselves
else else
for (int i = 0; i < mods.size(); i++) for (int i = 0; i < mods.size(); i++)
{ {
if (!mods[i].strongCompare(newMods[i])) if (!mods[i].strongCompare(orderedMods[i]))
{ {
orderWasInvalid = true; orderOrStateChanged = true;
break; break;
} }
} }
}
beginResetModel(); beginResetModel();
mods.swap(newMods); mods.swap(orderedMods);
endResetModel(); endResetModel();
if (orderWasInvalid) if (orderOrStateChanged && !m_list_file.isEmpty())
{ {
QLOG_INFO() << "Mod list " << m_list_file << " changed!";
saveListFile(); saveListFile();
emit changed(); emit changed();
} }
@ -123,17 +151,19 @@ void ModList::directoryChanged(QString path)
update(); update();
} }
QStringList ModList::readListFile() ModList::OrderList ModList::readListFile()
{ {
QStringList stringList; OrderList itemList;
if (m_list_file.isNull() || m_list_file.isEmpty()) if (m_list_file.isNull() || m_list_file.isEmpty())
return stringList; return itemList;
QFile textFile(m_list_file); QFile textFile(m_list_file);
if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
return QStringList(); return OrderList();
QTextStream textStream(&textFile); QTextStream textStream;
textStream.setAutoDetectUnicode(true);
textStream.setDevice(&textFile);
while (true) while (true)
{ {
QString line = textStream.readLine(); QString line = textStream.readLine();
@ -141,11 +171,18 @@ QStringList ModList::readListFile()
break; break;
else else
{ {
stringList.append(line); OrderItem it;
it.enabled = !line.endsWith(".disabled");
if (!it.enabled)
{
line.chop(9);
}
it.id = line;
itemList.append(it);
} }
} }
textFile.close(); textFile.close();
return stringList; return itemList;
} }
bool ModList::saveListFile() bool ModList::saveListFile()
@ -155,12 +192,16 @@ bool ModList::saveListFile()
QFile textFile(m_list_file); QFile textFile(m_list_file);
if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
return false; return false;
QTextStream textStream(&textFile); QTextStream textStream;
textStream.setGenerateByteOrderMark(true);
textStream.setCodec("UTF-8");
textStream.setDevice(&textFile);
for (auto mod : mods) for (auto mod : mods)
{ {
auto pathname = mod.filename(); textStream << mod.mmc_id();
QString filename = pathname.fileName(); if (!mod.enabled())
textStream << filename << endl; textStream << ".disabled";
textStream << endl;
} }
textFile.close(); textFile.close();
return false; return false;
@ -327,7 +368,7 @@ bool ModList::moveModsDown(int first, int last)
int ModList::columnCount(const QModelIndex &parent) const int ModList::columnCount(const QModelIndex &parent) const
{ {
return 2; return 3;
} }
QVariant ModList::data(const QModelIndex &index, int role) const QVariant ModList::data(const QModelIndex &index, int role) const
@ -341,43 +382,96 @@ QVariant ModList::data(const QModelIndex &index, int role) const
if (row < 0 || row >= mods.size()) if (row < 0 || row >= mods.size())
return QVariant(); return QVariant();
if (role != Qt::DisplayRole) switch (role)
return QVariant();
switch (column)
{ {
case 0: case Qt::DisplayRole:
switch (index.column())
{
case NameColumn:
return mods[row].name(); return mods[row].name();
case 1: case VersionColumn:
return mods[row].version(); return mods[row].version();
case 2:
return mods[row].mcversion(); default:
return QVariant();
}
case Qt::ToolTipRole:
return mods[row].mmc_id();
case Qt::CheckStateRole:
switch (index.column())
{
case ActiveColumn:
return mods[row].enabled();
default:
return QVariant();
}
default: default:
return QVariant(); return QVariant();
} }
} }
bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
{
return false;
}
if (role == Qt::CheckStateRole)
{
auto &mod = mods[index.row()];
if (mod.enable(!mod.enabled()))
{
emit dataChanged(index, index);
return true;
}
}
return false;
}
QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const
{ {
if (role != Qt::DisplayRole || orientation != Qt::Horizontal) switch (role)
return QVariant(); {
case Qt::DisplayRole:
switch (section) switch (section)
{ {
case 0: case ActiveColumn:
return QString("Name");
case 1:
return QString("Version");
case 2:
return QString("Minecraft");
}
return QString(); return QString();
case NameColumn:
return QString("Name");
case VersionColumn:
return QString("Version");
default:
return QVariant();
}
case Qt::ToolTipRole:
switch (section)
{
case ActiveColumn:
return "Is the mod enabled?";
case NameColumn:
return "The name of the mod.";
case VersionColumn:
return "The version of the mod.";
default:
return QVariant();
}
default:
return QVariant();
}
return QVariant();
} }
Qt::ItemFlags ModList::flags(const QModelIndex &index) const Qt::ItemFlags ModList::flags(const QModelIndex &index) const
{ {
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
if (index.isValid()) if (index.isValid())
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled |
defaultFlags;
else else
return Qt::ItemIsDropEnabled | defaultFlags; return Qt::ItemIsDropEnabled | defaultFlags;
} }
@ -456,6 +550,14 @@ bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row
QString filename = url.toLocalFile(); QString filename = url.toLocalFile();
installMod(filename, row); installMod(filename, row);
QLOG_INFO() << "installing: " << filename; QLOG_INFO() << "installing: " << filename;
// if there is no ordering, re-sort the list
if (m_list_file.isEmpty())
{
beginResetModel();
std::sort(mods.begin(), mods.end(), [](const Mod & left, const Mod & right)
{ return left.name().localeAwareCompare(right.name()) <= 0; });
endResetModel();
}
} }
if (was_watching) if (was_watching)
startWatching(); startWatching();

View File

@ -34,9 +34,18 @@ class ModList : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public: public:
enum Columns
{
ActiveColumn = 0,
NameColumn,
VersionColumn
};
ModList(const QString &dir, const QString &list_file = QString()); ModList(const QString &dir, const QString &list_file = QString());
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const virtual int rowCount(const QModelIndex &parent = QModelIndex()) const
{ {
return size(); return size();
@ -59,7 +68,6 @@ public:
{ {
return mods[index]; return mods[index];
} }
;
/// Reloads the mod list and returns true if the list changed. /// Reloads the mod list and returns true if the list changed.
virtual bool update(); virtual bool update();
@ -119,7 +127,13 @@ public:
} }
private: private:
QStringList readListFile(); struct OrderItem
{
QString id;
bool enabled = false;
};
typedef QList<OrderItem> OrderList;
OrderList readListFile();
bool saveListFile(); bool saveListFile();
private private
slots: slots: