From c92ad7dcf86f2e5e71d71a68e24e79fbdeceb56d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 18 Aug 2013 20:52:17 +0200 Subject: Drag and Drop, mod management. --- logic/LegacyInstance.cpp | 14 ++- logic/LegacyInstance.h | 2 +- logic/Mod.cpp | 2 +- logic/Mod.h | 7 +- logic/ModList.cpp | 254 +++++++++++++++++++++++++++++++++++++++++++---- logic/ModList.h | 24 ++++- 6 files changed, 277 insertions(+), 26 deletions(-) (limited to 'logic') diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index db2a72d9..c2a4b66a 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -88,8 +88,10 @@ QSharedPointer< ModList > LegacyInstance::coreModList() I_D(LegacyInstance); if(!d->core_mod_list) { - d->core_mod_list.reset(new ModList(coreModsDir(), QString())); + d->core_mod_list.reset(new ModList(coreModsDir())); } + else + d->core_mod_list->update(); return d->core_mod_list; } @@ -98,10 +100,12 @@ QSharedPointer< ModList > LegacyInstance::jarModList() I_D(LegacyInstance); if(!d->jar_mod_list) { - auto list = new ModList(instModsDir(), modListFile()); + auto list = new ModList(jarModsDir(), modListFile()); connect(list, SIGNAL(changed()), SLOT(jarModsChanged())); d->jar_mod_list.reset(list); } + else + d->jar_mod_list->update(); return d->jar_mod_list; } @@ -116,8 +120,10 @@ QSharedPointer< ModList > LegacyInstance::loaderModList() I_D(LegacyInstance); if(!d->loader_mod_list) { - d->loader_mod_list.reset(new ModList(mlModsDir(), QString())); + d->loader_mod_list.reset(new ModList(mlModsDir())); } + else + d->loader_mod_list->update(); return d->loader_mod_list; } @@ -133,7 +139,7 @@ void LegacyInstance::cleanupAfterRun() } -QString LegacyInstance::instModsDir() const +QString LegacyInstance::jarModsDir() const { return PathCombine(instanceRoot(), "instMods"); } diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index 43a66a2b..ef7006ab 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -28,7 +28,7 @@ public: ////// Directories ////// QString savesDir() const; - QString instModsDir() const; + QString jarModsDir() const; QString binDir() const; QString mlModsDir() const; QString coreModsDir() const; diff --git a/logic/Mod.cpp b/logic/Mod.cpp index 652bbda7..4f9a1685 100644 --- a/logic/Mod.cpp +++ b/logic/Mod.cpp @@ -26,7 +26,7 @@ Mod::Mod( const QFileInfo& file ) void Mod::repath ( const QFileInfo& file ) { m_file = file; - m_name = file.baseName(); + m_name = file.completeBaseName(); m_id = file.fileName(); m_type = Mod::MOD_UNKNOWN; diff --git a/logic/Mod.h b/logic/Mod.h index f14818d1..ea0827bd 100644 --- a/logic/Mod.h +++ b/logic/Mod.h @@ -47,12 +47,15 @@ public: // change the mod's filesystem path (used by mod lists for *MAGIC* purposes) 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(); + } protected: //FIXME: what do do with those? HMM... diff --git a/logic/ModList.cpp b/logic/ModList.cpp index d9e67574..2a98eec7 100644 --- a/logic/ModList.cpp +++ b/logic/ModList.cpp @@ -17,12 +17,17 @@ #include "ModList.h" #include "LegacyInstance.h" #include +#include +#include +#include +#include ModList::ModList ( const QString& dir, const QString& list_file ) : QAbstractListModel(), m_dir(dir), m_list_file(list_file) { m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::NoSymLinks); m_dir.setSorting(QDir::Name); + m_list_id = QUuid::createUuid().toString(); update(); } @@ -30,24 +35,103 @@ bool ModList::update() { if (!isValid()) return false; + + QList newMods; + + auto folderContents = m_dir.entryInfoList(); + bool orderWasInvalid = false; + + // first, process the ordered items (if any) + int currentOrderIndex = 0; + QStringList listOrder = readListFile(); + for(auto item: listOrder) + { + QFileInfo info (m_dir.filePath(item)); + int idx = folderContents.indexOf(info); + // 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)); + } + else + { + orderWasInvalid = true; + } + } + for(auto entry: folderContents) + { + newMods.append(Mod(entry)); + } + if(mods.size() != newMods.size()) + { + orderWasInvalid = true; + } + else for(int i = 0; i < mods.size(); i++) + { + if(!mods[i].strongCompare(newMods[i])) + { + orderWasInvalid = true; + break; + } + } + beginResetModel(); + mods.swap(newMods); + endResetModel(); + if(orderWasInvalid) + { + saveListFile(); + emit changed(); + } + return true; +} - bool initial = mods.empty(); - - bool listChanged = false; - - auto list = m_dir.entryInfoList(); - for(auto entry: list) +QStringList ModList::readListFile() +{ + QStringList stringList; + if(m_list_file.isNull() || m_list_file.isEmpty()) + return stringList; + + QFile textFile(m_list_file); + if(!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) + return QStringList(); + + QTextStream textStream(&textFile); + while (true) { - Mod mod(entry); - if (initial || !mods.contains(mod)) + QString line = textStream.readLine(); + if (line.isNull() || line.isEmpty()) + break; + else { - mods.push_back(mod); - listChanged = true; + stringList.append(line); } } - return listChanged; + textFile.close(); + return stringList; +} + +bool ModList::saveListFile() +{ + if(m_list_file.isNull() || m_list_file.isEmpty()) + return false; + QFile textFile(m_list_file); + if(!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + return false; + QTextStream textStream(&textFile); + for(auto mod:mods) + { + auto pathname = mod.filename(); + QString filename = pathname.fileName(); + textStream << filename << endl; + } + textFile.close(); + return false; } + bool ModList::isValid() { return m_dir.exists() && m_dir.isReadable(); @@ -69,6 +153,11 @@ bool ModList::installMod ( const QFileInfo& filename, size_t index ) { if(mods[idx].replace(m)) { + + auto left = this->index(index); + auto right = this->index(index, columnCount(QModelIndex()) - 1); + emit dataChanged(left, right); + saveListFile(); emit changed(); return true; } @@ -84,17 +173,25 @@ bool ModList::installMod ( const QFileInfo& filename, size_t index ) if(!QFile::copy(filename.filePath(), newpath)) return false; m.repath(newpath); - mods.append(m); + beginInsertRows(QModelIndex(), index, index); + mods.insert(index,m); + endInsertRows(); + saveListFile(); emit changed(); return true; } else if(type == Mod::MOD_FOLDER) { - QString newpath = PathCombine(m_dir.path(), filename.fileName()); - if(!copyPath(filename.filePath(), newpath)) + + QString from = filename.filePath(); + QString to = PathCombine(m_dir.path(), filename.fileName()); + if(!copyPath(from, to)) return false; - m.repath(newpath); - mods.append(m); + m.repath(to); + beginInsertRows(QModelIndex(), index, index); + mods.insert(index,m); + endInsertRows(); + saveListFile(); emit changed(); return true; } @@ -108,7 +205,10 @@ bool ModList::deleteMod ( size_t index ) Mod & m = mods[index]; if(m.destroy()) { + beginRemoveRows(QModelIndex(), index, index); mods.erase(mods.begin() + index); + endRemoveRows(); + saveListFile(); emit changed(); return true; } @@ -117,7 +217,22 @@ bool ModList::deleteMod ( size_t index ) bool ModList::moveMod ( size_t from, size_t to ) { - return false; + if(from < 0 || from >= mods.size()) + return false; + if (to >= rowCount()) + to = rowCount() - 1; + if (to == -1) + to = rowCount() - 1; + // FIXME: this should be better, but segfaults for some reason + //beginMoveRows(QModelIndex(), from, from, QModelIndex(), to); + + beginResetModel(); + mods.move(from, to); + endResetModel(); + //endMoveRows(); + saveListFile(); + emit changed(); + return true; } int ModList::columnCount ( const QModelIndex& parent ) const @@ -168,6 +283,111 @@ QVariant ModList::headerData ( int section, Qt::Orientation orientation, int rol } +Qt::ItemFlags ModList::flags ( const QModelIndex& index ) const +{ + Qt::ItemFlags defaultFlags = QAbstractListModel::flags ( index ); + if (index.isValid()) + return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + else + return Qt::ItemIsDropEnabled | defaultFlags; +} + +QStringList ModList::mimeTypes() const +{ + QStringList types; + types << "text/uri-list"; + types << "application/x-mcmod"; + return types; +} + +Qt::DropActions ModList::supportedDropActions() const +{ + // copy from outside, move from within and other mod lists + return Qt::CopyAction | Qt::MoveAction; +} + +Qt::DropActions ModList::supportedDragActions() const +{ + // move to other mod lists or VOID + return Qt::MoveAction; +} + +QMimeData* ModList::mimeData ( const QModelIndexList& indexes ) const +{ + if(indexes.size() == 0) + return nullptr; + + auto idx = indexes[0]; + int row = idx.row(); + if(row <0 || row >= mods.size()) + return nullptr; + + QMimeData * data = new QMimeData(); + + QStringList params; + params << m_list_id << QString::number(row); + data->setData("application/x-mcmod", params.join('|').toLatin1()); + return data; +} + +bool ModList::dropMimeData ( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent ) +{ + if (action == Qt::IgnoreAction) + return true; + // check if the action is supported + if (!data || !(action & supportedDropActions())) + return false; + qDebug() << "row: " << row << " column: " << column; + if(parent.isValid()) + { + row = parent.row(); + column = parent.column(); + } + + if (row > rowCount()) + row = rowCount(); + if (row == -1) + row = rowCount(); + if (column == -1) + column = 0; + qDebug() << "row: " << row << " column: " << column; + + // files dropped from outside? + if(data->hasFormat("text/uri-list") && data->hasUrls()) + { + auto urls = data->urls(); + for(auto url: urls) + { + // only local files may be dropped... + if(!url.isLocalFile()) + continue; + QString filename = url.toLocalFile(); + installMod(filename, row); + qDebug() << "installing: " << filename; + } + return true; + } + else if(data->hasFormat("application/x-mcmod")) + { + QString sourcestr = QString::fromLatin1(data->data("application/x-mcmod")); + auto list = sourcestr.split('|'); + if(list.size() != 2) + return false; + QString remoteId = list[0]; + int remoteIndex = list[1].toInt(); + // no moving of things between two lists + if(remoteId != m_list_id) + return false; + // no point moving to the same place... + if(row == remoteIndex) + return false; + // otherwise, move the mod :D + moveMod(remoteIndex, row); + return true; + } + return false; +} + /* ModList::ModList(const QString &dir) diff --git a/logic/ModList.h b/logic/ModList.h index 41d26491..0e172efd 100644 --- a/logic/ModList.h +++ b/logic/ModList.h @@ -26,7 +26,7 @@ class ModList : public QAbstractListModel { Q_OBJECT public: - ModList(const QString& dir, const QString& list_file); + ModList(const QString& dir, const QString& list_file = QString()); virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const; virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const @@ -56,12 +56,34 @@ public: */ virtual bool moveMod(size_t from, size_t to); + /// flags, mostly to support drag&drop + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + /// get data for drag action + virtual QMimeData* mimeData(const QModelIndexList& indexes) const; + /// get the supported mime types + virtual QStringList mimeTypes() const; + /// process data from drop action + virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); + /// what drag actions do we support? + virtual Qt::DropActions supportedDragActions() const; + /// what drop actions do we support? + virtual Qt::DropActions supportedDropActions() const; + virtual bool isValid(); + QDir dir() + { + return m_dir; + } +private: + QStringList readListFile(); + bool saveListFile(); + signals: void changed(); protected: QDir m_dir; QString m_list_file; + QString m_list_id; QList mods; }; -- cgit v1.2.3