summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2013-12-26 05:14:32 +0100
committerPetr Mrázek <peterix@gmail.com>2013-12-26 05:14:32 +0100
commitacf25d8a33ef67b79d8e8a8859f5559e011373a5 (patch)
tree513fee2d62fb97b9d68c44f2a2a58f733f01a43c
parent449f55c3e668c0e01eee0868226473f12d0b109f (diff)
downloadMultiMC-acf25d8a33ef67b79d8e8a8859f5559e011373a5.tar
MultiMC-acf25d8a33ef67b79d8e8a8859f5559e011373a5.tar.gz
MultiMC-acf25d8a33ef67b79d8e8a8859f5559e011373a5.tar.lz
MultiMC-acf25d8a33ef67b79d8e8a8859f5559e011373a5.tar.xz
MultiMC-acf25d8a33ef67b79d8e8a8859f5559e011373a5.zip
Disable/enable mods with checkboxes. Needs testing.
A lot of testing!
-rw-r--r--gui/widgets/MCModInfoFrame.cpp2
-rw-r--r--gui/widgets/ModListView.cpp5
-rw-r--r--logic/LegacyInstance.cpp1
-rw-r--r--logic/Mod.cpp87
-rw-r--r--logic/Mod.h30
-rw-r--r--logic/ModList.cpp210
-rw-r--r--logic/ModList.h18
7 files changed, 272 insertions, 81 deletions
diff --git a/gui/widgets/MCModInfoFrame.cpp b/gui/widgets/MCModInfoFrame.cpp
index ad167bc9..abcea6c6 100644
--- a/gui/widgets/MCModInfoFrame.cpp
+++ b/gui/widgets/MCModInfoFrame.cpp
@@ -30,7 +30,7 @@ void MCModInfoFrame::updateWithMod(Mod &m)
QString text = "";
QString name = "";
- if(m.name().isEmpty()) name = m.id();
+ if(m.name().isEmpty()) name = m.mmc_id();
else name = m.name();
if(m.homeurl().isEmpty()) text = name;
diff --git a/gui/widgets/ModListView.cpp b/gui/widgets/ModListView.cpp
index 838af75e..9d5950c3 100644
--- a/gui/widgets/ModListView.cpp
+++ b/gui/widgets/ModListView.cpp
@@ -44,8 +44,9 @@ void ModListView::setModel ( QAbstractItemModel* model )
QTreeView::setModel ( model );
auto head = header();
head->setStretchLastSection(false);
- head->setSectionResizeMode(0, QHeaderView::Stretch);
- for(int i = 1; i < head->count(); i++)
+ head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ head->setSectionResizeMode(1, QHeaderView::Stretch);
+ for(int i = 2; i < head->count(); i++)
head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
dropIndicatorPosition();
}
diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp
index fef27bcd..5c82b837 100644
--- a/logic/LegacyInstance.cpp
+++ b/logic/LegacyInstance.cpp
@@ -150,6 +150,7 @@ std::shared_ptr<ModList> LegacyInstance::jarModList()
void LegacyInstance::jarModsChanged()
{
+ QLOG_INFO() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt.";
setShouldRebuild(true);
}
diff --git a/logic/Mod.cpp b/logic/Mod.cpp
index cff9467e..8e880be1 100644
--- a/logic/Mod.cpp
+++ b/logic/Mod.cpp
@@ -35,20 +35,40 @@ Mod::Mod(const QFileInfo &file)
void Mod::repath(const QFileInfo &file)
{
m_file = file;
- m_name = file.completeBaseName();
- m_id = file.fileName();
+ QString name_base = file.fileName();
m_type = Mod::MOD_UNKNOWN;
+
if (m_file.isDir())
+ {
m_type = MOD_FOLDER;
+ m_name = name_base;
+ m_mmc_id = name_base;
+ }
else if (m_file.isFile())
{
- QString ext = m_file.suffix().toLower();
- if (ext == "zip" || ext == "jar")
+ if(name_base.endsWith(".disabled"))
+ {
+ m_enabled = false;
+ name_base.chop(9);
+ }
+ 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_name = name_base;
}
+
if (m_type == MOD_ZIPFILE)
{
QuaZip zip(m_file.filePath());
@@ -114,7 +134,7 @@ void Mod::ReadMCModInfo(QByteArray contents)
if (!arr.at(0).isObject())
return;
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_version = firstObj.value("version").toString();
m_homeurl = firstObj.value("url").toString();
@@ -163,7 +183,7 @@ void Mod::ReadForgeInfo(QByteArray contents)
{
// Read the data
m_name = "Minecraft Forge";
- m_id = "Forge";
+ m_mod_id = "Forge";
m_homeurl = "http://www.minecraftforge.net/forum/";
INIFile ini;
if (!ini.loadFile(contents))
@@ -183,9 +203,11 @@ bool Mod::replace(Mod &with)
return false;
bool success = false;
auto t = with.type();
+
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)
{
@@ -193,11 +215,17 @@ bool Mod::replace(Mod &with)
}
if (success)
{
- m_id = with.m_id;
- m_mcversion = with.m_mcversion;
- m_type = with.m_type;
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_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;
}
@@ -241,3 +269,42 @@ QString Mod::version() const
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();
+}
diff --git a/logic/Mod.h b/logic/Mod.h
index ca362a9d..05d3cea2 100644
--- a/logic/Mod.h
+++ b/logic/Mod.h
@@ -33,9 +33,13 @@ public:
{
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
{
@@ -77,6 +81,13 @@ public:
return m_credits;
}
+ bool enabled() const
+ {
+ return m_enabled;
+ }
+
+ bool enable(bool value);
+
// delete all the files of this mod
bool destroy();
// replace this mod with a copy of the other
@@ -85,15 +96,8 @@ public:
void repath(const QFileInfo &file);
// WEAK compare operator - used for replacing mods
- bool operator==(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();
- }
+ bool operator==(const Mod &other) const;
+ bool strongCompare(const Mod &other) const;
private:
void ReadMCModInfo(QByteArray contents);
@@ -108,7 +112,9 @@ protected:
*/
QFileInfo m_file;
- QString m_id;
+ QString m_mmc_id;
+ QString m_mod_id;
+ bool m_enabled = true;
QString m_name;
QString m_version;
QString m_mcversion;
diff --git a/logic/ModList.cpp b/logic/ModList.cpp
index d5235fe9..dbc85320 100644
--- a/logic/ModList.cpp
+++ b/logic/ModList.cpp
@@ -19,6 +19,7 @@
#include <QMimeData>
#include <QUrl>
#include <QUuid>
+#include <QString>
#include <QFileSystemWatcher>
#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 |
QDir::NoSymLinks);
- m_dir.setSorting(QDir::Name);
+ m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
m_list_id = QUuid::createUuid().toString();
m_watcher = new QFileSystemWatcher(this);
is_watching = false;
@@ -66,52 +67,79 @@ bool ModList::update()
if (!isValid())
return false;
+ QList<Mod> orderedMods;
QList<Mod> newMods;
m_dir.refresh();
auto folderContents = m_dir.entryInfoList();
- bool orderWasInvalid = false;
+ bool orderOrStateChanged = false;
// first, process the ordered items (if any)
- QStringList listOrder = readListFile();
+ OrderList listOrder = readListFile();
for (auto item : listOrder)
{
- QFileInfo info(m_dir.filePath(item));
- int idx = folderContents.indexOf(info);
+ QFileInfo infoEnabled(m_dir.filePath(item.id));
+ 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 (idx != -1)
{
// remove from the actual folder contents list
folderContents.takeAt(idx);
// append the new mod
- newMods.append(Mod(info));
+ orderedMods.append(Mod(info));
+ if (isEnabled != item.enabled)
+ orderOrStateChanged = true;
}
else
{
- orderWasInvalid = true;
+ orderOrStateChanged = true;
}
}
- for (auto entry : folderContents)
+ // if there are any untracked files...
+ if (folderContents.size())
{
- newMods.append(Mod(entry));
+ // the order surely changed!
+ for (auto entry : folderContents)
+ {
+ newMods.append(Mod(entry));
+ }
+ std::sort(newMods.begin(), newMods.end(), [](const Mod & left, const Mod & right)
+ { return left.name().localeAwareCompare(right.name()) <= 0; });
+ orderedMods.append(newMods);
+ orderOrStateChanged = true;
}
- if (mods.size() != newMods.size())
+ // otherwise, if we were already tracking some mods
+ else if (mods.size())
{
- orderWasInvalid = true;
- }
- else
- for (int i = 0; i < mods.size(); i++)
- {
- if (!mods[i].strongCompare(newMods[i]))
+ // if the number doesn't match, order changed.
+ if (mods.size() != orderedMods.size())
+ orderOrStateChanged = true;
+ // if it does match, compare the mods themselves
+ else
+ for (int i = 0; i < mods.size(); i++)
{
- orderWasInvalid = true;
- break;
+ if (!mods[i].strongCompare(orderedMods[i]))
+ {
+ orderOrStateChanged = true;
+ break;
+ }
}
- }
+ }
beginResetModel();
- mods.swap(newMods);
+ mods.swap(orderedMods);
endResetModel();
- if (orderWasInvalid)
+ if (orderOrStateChanged && !m_list_file.isEmpty())
{
+ QLOG_INFO() << "Mod list " << m_list_file << " changed!";
saveListFile();
emit changed();
}
@@ -123,17 +151,19 @@ void ModList::directoryChanged(QString path)
update();
}
-QStringList ModList::readListFile()
+ModList::OrderList ModList::readListFile()
{
- QStringList stringList;
+ OrderList itemList;
if (m_list_file.isNull() || m_list_file.isEmpty())
- return stringList;
+ return itemList;
QFile textFile(m_list_file);
if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text))
- return QStringList();
+ return OrderList();
- QTextStream textStream(&textFile);
+ QTextStream textStream;
+ textStream.setAutoDetectUnicode(true);
+ textStream.setDevice(&textFile);
while (true)
{
QString line = textStream.readLine();
@@ -141,11 +171,18 @@ QStringList ModList::readListFile()
break;
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();
- return stringList;
+ return itemList;
}
bool ModList::saveListFile()
@@ -155,12 +192,16 @@ bool ModList::saveListFile()
QFile textFile(m_list_file);
if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
return false;
- QTextStream textStream(&textFile);
+ QTextStream textStream;
+ textStream.setGenerateByteOrderMark(true);
+ textStream.setCodec("UTF-8");
+ textStream.setDevice(&textFile);
for (auto mod : mods)
{
- auto pathname = mod.filename();
- QString filename = pathname.fileName();
- textStream << filename << endl;
+ textStream << mod.mmc_id();
+ if (!mod.enabled())
+ textStream << ".disabled";
+ textStream << endl;
}
textFile.close();
return false;
@@ -327,7 +368,7 @@ bool ModList::moveModsDown(int first, int last)
int ModList::columnCount(const QModelIndex &parent) const
{
- return 2;
+ return 3;
}
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())
return QVariant();
- if (role != Qt::DisplayRole)
- return QVariant();
-
- switch (column)
+ switch (role)
{
- case 0:
- return mods[row].name();
- case 1:
- return mods[row].version();
- case 2:
- return mods[row].mcversion();
+ case Qt::DisplayRole:
+ switch (index.column())
+ {
+ case NameColumn:
+ return mods[row].name();
+ case VersionColumn:
+ return mods[row].version();
+
+ 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:
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
{
- if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
- return QVariant();
- switch (section)
+ switch (role)
{
- case 0:
- return QString("Name");
- case 1:
- return QString("Version");
- case 2:
- return QString("Minecraft");
+ case Qt::DisplayRole:
+ switch (section)
+ {
+ case ActiveColumn:
+ 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 QString();
+ return QVariant();
}
Qt::ItemFlags ModList::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
if (index.isValid())
- return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
+ return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled |
+ defaultFlags;
else
return Qt::ItemIsDropEnabled | defaultFlags;
}
@@ -456,6 +550,14 @@ bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row
QString filename = url.toLocalFile();
installMod(filename, row);
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)
startWatching();
diff --git a/logic/ModList.h b/logic/ModList.h
index 803a5429..0d6507fb 100644
--- a/logic/ModList.h
+++ b/logic/ModList.h
@@ -34,9 +34,18 @@ class ModList : public QAbstractListModel
{
Q_OBJECT
public:
+ enum Columns
+ {
+ ActiveColumn = 0,
+ NameColumn,
+ VersionColumn
+ };
ModList(const QString &dir, const QString &list_file = QString());
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
{
return size();
@@ -59,7 +68,6 @@ public:
{
return mods[index];
}
- ;
/// Reloads the mod list and returns true if the list changed.
virtual bool update();
@@ -119,7 +127,13 @@ public:
}
private:
- QStringList readListFile();
+ struct OrderItem
+ {
+ QString id;
+ bool enabled = false;
+ };
+ typedef QList<OrderItem> OrderList;
+ OrderList readListFile();
bool saveListFile();
private
slots: