summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2015-09-09 23:53:33 +0200
committerPetr Mrázek <peterix@gmail.com>2015-09-09 23:53:33 +0200
commita1fd50e920eba0f198b898e5df4ff5f60424d355 (patch)
tree6b2a3e0ec704d5edfec221c84ca1571376df9c1e
parent51070a13f711e53835f9aadc220c8177440590d9 (diff)
downloadMultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.tar
MultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.tar.gz
MultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.tar.lz
MultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.tar.xz
MultiMC-a1fd50e920eba0f198b898e5df4ff5f60424d355.zip
GH-1227: World import using drag and drop - zip files and folders
-rw-r--r--application/GuiUtil.cpp22
-rw-r--r--application/GuiUtil.h2
-rw-r--r--application/pages/LegacyJarModPage.cpp2
-rw-r--r--application/pages/ModFolderPage.cpp7
-rw-r--r--application/pages/VersionPage.cpp2
-rw-r--r--application/pages/WorldListPage.cpp20
-rw-r--r--application/pages/WorldListPage.h1
-rw-r--r--application/pages/WorldListPage.ui35
-rw-r--r--depends/util/include/pathutils.h2
-rw-r--r--depends/util/src/pathutils.cpp25
-rw-r--r--logic/minecraft/ModList.cpp1
-rw-r--r--logic/minecraft/World.cpp188
-rw-r--r--logic/minecraft/World.h10
-rw-r--r--logic/minecraft/WorldList.cpp116
-rw-r--r--logic/minecraft/WorldList.h15
15 files changed, 336 insertions, 112 deletions
diff --git a/application/GuiUtil.cpp b/application/GuiUtil.cpp
index 922d38fa..edfe9951 100644
--- a/application/GuiUtil.cpp
+++ b/application/GuiUtil.cpp
@@ -51,14 +51,13 @@ void GuiUtil::setClipboardText(const QString &text)
QApplication::clipboard()->setText(text);
}
-QStringList GuiUtil::BrowseForMods(QString context, QString caption, QString filter,
- QWidget *parentWidget)
+
+QStringList GuiUtil::BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget)
{
static QMap<QString, QString> savedPaths;
QFileDialog w(parentWidget, caption);
QSet<QString> locations;
- QString modsFolder = MMC->settings()->get("CentralModsDir").toString();
auto f = [&](QStandardPaths::StandardLocation l)
{
QString location = QStandardPaths::writableLocation(l);
@@ -76,19 +75,30 @@ QStringList GuiUtil::BrowseForMods(QString context, QString caption, QString fil
{
urls.append(QUrl::fromLocalFile(location));
}
- urls.append(QUrl::fromLocalFile(modsFolder));
+ urls.append(QUrl::fromLocalFile(defaultPath));
w.setFileMode(QFileDialog::ExistingFiles);
w.setAcceptMode(QFileDialog::AcceptOpen);
w.setNameFilter(filter);
+
+ QString pathToOpen;
if(savedPaths.contains(context))
{
- w.setDirectory(savedPaths[context]);
+ pathToOpen = savedPaths[context];
}
else
{
- w.setDirectory(modsFolder);
+ pathToOpen = defaultPath;
}
+ if(!pathToOpen.isEmpty())
+ {
+ QFileInfo finfo(pathToOpen);
+ if(finfo.exists() && finfo.isDir())
+ {
+ w.setDirectory(finfo.absoluteFilePath());
+ }
+ }
+
w.setSidebarUrls(urls);
if (w.exec())
diff --git a/application/GuiUtil.h b/application/GuiUtil.h
index 4c8bdaa7..381683e4 100644
--- a/application/GuiUtil.h
+++ b/application/GuiUtil.h
@@ -6,5 +6,5 @@ namespace GuiUtil
{
void uploadPaste(const QString &text, QWidget *parentWidget);
void setClipboardText(const QString &text);
-QStringList BrowseForMods(QString context, QString caption, QString filter, QWidget *parentWidget);
+QStringList BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget);
}
diff --git a/application/pages/LegacyJarModPage.cpp b/application/pages/LegacyJarModPage.cpp
index 92977de9..87a9f3b5 100644
--- a/application/pages/LegacyJarModPage.cpp
+++ b/application/pages/LegacyJarModPage.cpp
@@ -100,7 +100,7 @@ bool LegacyJarModPage::eventFilter(QObject *obj, QEvent *ev)
void LegacyJarModPage::on_addJarBtn_clicked()
{
- auto list = GuiUtil::BrowseForMods("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), this->parentWidget());
+ auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), MMC->settings()->get("CentralModsDir").toString(), this->parentWidget());
if(!list.empty())
{
m_jarmods->stopWatching();
diff --git a/application/pages/ModFolderPage.cpp b/application/pages/ModFolderPage.cpp
index a70c1620..9a2a43a4 100644
--- a/application/pages/ModFolderPage.cpp
+++ b/application/pages/ModFolderPage.cpp
@@ -131,12 +131,13 @@ bool ModFolderPage::eventFilter(QObject *obj, QEvent *ev)
void ModFolderPage::on_addModBtn_clicked()
{
- auto list = GuiUtil::BrowseForMods(
+ auto list = GuiUtil::BrowseForFiles(
m_helpName,
tr("Select %1",
"Select whatever type of files the page contains. Example: 'Loader Mods'")
.arg(m_displayName),
- m_filter.arg(m_displayName), this->parentWidget());
+ m_filter.arg(m_displayName), MMC->settings()->get("CentralModsDir").toString(),
+ this->parentWidget());
if (!list.empty())
{
m_mods->stopWatching();
@@ -146,8 +147,8 @@ void ModFolderPage::on_addModBtn_clicked()
}
m_mods->startWatching();
}
-
}
+
void ModFolderPage::on_rmModBtn_clicked()
{
int first, last;
diff --git a/application/pages/VersionPage.cpp b/application/pages/VersionPage.cpp
index d56bbd28..36091630 100644
--- a/application/pages/VersionPage.cpp
+++ b/application/pages/VersionPage.cpp
@@ -180,7 +180,7 @@ void VersionPage::on_jarmodBtn_clicked()
nagShown = true;
}
}
- auto list = GuiUtil::BrowseForMods("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), this->parentWidget());
+ auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), MMC->settings()->get("CentralModsDir").toString(), this->parentWidget());
if(!list.empty())
{
m_version->installJarMods(list);
diff --git a/application/pages/WorldListPage.cpp b/application/pages/WorldListPage.cpp
index 8b95b2b0..be1d58b8 100644
--- a/application/pages/WorldListPage.cpp
+++ b/application/pages/WorldListPage.cpp
@@ -25,6 +25,7 @@
#include "MultiMC.h"
+#include <GuiUtil.h>
WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr<WorldList> worlds, QString id,
QString iconName, QString displayName, QString helpPage,
@@ -67,8 +68,6 @@ WorldListPage::~WorldListPage()
bool WorldListPage::shouldDisplay() const
{
- if (m_inst)
- return !m_inst->isRunning();
return true;
}
@@ -200,3 +199,20 @@ void WorldListPage::worldChanged(const QModelIndex &current, const QModelIndex &
ui->mcEditBtn->setEnabled(enable);
ui->rmWorldBtn->setEnabled(enable);
}
+
+void WorldListPage::on_addBtn_clicked()
+{
+ auto list = GuiUtil::BrowseForFiles(
+ m_helpName,
+ tr("Select a Minecraft world zip"),
+ tr("Minecraft World Zip File (*.zip)"), QString(), this->parentWidget());
+ if (!list.empty())
+ {
+ m_worlds->stopWatching();
+ for (auto filename : list)
+ {
+ m_worlds->installWorld(QFileInfo(filename));
+ }
+ m_worlds->startWatching();
+ }
+} \ No newline at end of file
diff --git a/application/pages/WorldListPage.h b/application/pages/WorldListPage.h
index c79f1be6..f3111f93 100644
--- a/application/pages/WorldListPage.h
+++ b/application/pages/WorldListPage.h
@@ -81,6 +81,7 @@ private slots:
void on_copySeedBtn_clicked();
void on_mcEditBtn_clicked();
void on_rmWorldBtn_clicked();
+ void on_addBtn_clicked();
void on_viewFolderBtn_clicked();
void worldChanged(const QModelIndex &current, const QModelIndex &previous);
};
diff --git a/application/pages/WorldListPage.ui b/application/pages/WorldListPage.ui
index 9d396fa6..ecd9709f 100644
--- a/application/pages/WorldListPage.ui
+++ b/application/pages/WorldListPage.ui
@@ -48,7 +48,7 @@
<bool>true</bool>
</property>
<property name="dragDropMode">
- <enum>QAbstractItemView::DropOnly</enum>
+ <enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
@@ -64,23 +64,33 @@
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <widget class="QPushButton" name="mcEditBtn">
+ <widget class="QPushButton" name="addBtn">
<property name="text">
- <string>MCEdit</string>
+ <string>Add</string>
</property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="copySeedBtn">
+ <widget class="QPushButton" name="rmWorldBtn">
<property name="text">
- <string>Copy Seed</string>
+ <string>&amp;Remove</string>
</property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="rmWorldBtn">
+ <widget class="LineSeparator" name="separator" native="true"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="mcEditBtn">
<property name="text">
- <string>&amp;Remove</string>
+ <string>MCEdit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="copySeedBtn">
+ <property name="text">
+ <string>Copy Seed</string>
</property>
</widget>
</item>
@@ -112,12 +122,21 @@
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>LineSeparator</class>
+ <extends>QWidget</extends>
+ <header>widgets/LineSeparator.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>worldTreeView</tabstop>
+ <tabstop>addBtn</tabstop>
+ <tabstop>rmWorldBtn</tabstop>
<tabstop>mcEditBtn</tabstop>
<tabstop>copySeedBtn</tabstop>
- <tabstop>rmWorldBtn</tabstop>
<tabstop>viewFolderBtn</tabstop>
</tabstops>
<resources/>
diff --git a/depends/util/include/pathutils.h b/depends/util/include/pathutils.h
index f31b96d1..82d55cd7 100644
--- a/depends/util/include/pathutils.h
+++ b/depends/util/include/pathutils.h
@@ -54,7 +54,7 @@ LIBUTIL_EXPORT bool ensureFolderPathExists(QString filenamepath);
/**
* Copy a folder recursively
*/
-LIBUTIL_EXPORT bool copyPath(QString src, QString dst, bool follow_symlinks = true);
+LIBUTIL_EXPORT bool copyPath(const QString &src, const QString &dst, bool follow_symlinks = true);
/**
* Delete a folder recursively
diff --git a/depends/util/src/pathutils.cpp b/depends/util/src/pathutils.cpp
index ce9138be..35c7f901 100644
--- a/depends/util/src/pathutils.cpp
+++ b/depends/util/src/pathutils.cpp
@@ -81,16 +81,24 @@ QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
QString DirNameFromString(QString string, QString inDir)
{
int num = 0;
- QString dirName = RemoveInvalidFilenameChars(string, '-');
- while (QFileInfo(PathCombine(inDir, dirName)).exists())
+ QString baseName = RemoveInvalidFilenameChars(string, '-');
+ QString dirName;
+ do
{
- num++;
- dirName = RemoveInvalidFilenameChars(dirName, '-') + QString::number(num);
+ if(num == 0)
+ {
+ dirName = baseName;
+ }
+ else
+ {
+ dirName = baseName + QString::number(num);;
+ }
// If it's over 9000
if (num > 9000)
return "";
- }
+ num++;
+ } while (QFileInfo(PathCombine(inDir, dirName)).exists());
return dirName;
}
@@ -112,7 +120,7 @@ bool ensureFolderPathExists(QString foldernamepath)
return success;
}
-bool copyPath(QString src, QString dst, bool follow_symlinks)
+bool copyPath(const QString &src, const QString &dst, bool follow_symlinks)
{
//NOTE always deep copy on windows. the alternatives are too messy.
#if defined Q_OS_WIN32
@@ -127,21 +135,26 @@ bool copyPath(QString src, QString dst, bool follow_symlinks)
bool OK = true;
+ qDebug() << "Looking at " << dir.absolutePath();
foreach(QString f, dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System))
{
QString inner_src = src + QDir::separator() + f;
QString inner_dst = dst + QDir::separator() + f;
+ qDebug() << f << "translates to"<< inner_src << "to" << inner_dst;
QFileInfo fileInfo(inner_src);
if(!follow_symlinks && fileInfo.isSymLink())
{
+ qDebug() << "creating symlink" << inner_src << " - " << inner_dst;
OK &= QFile::link(fileInfo.symLinkTarget(),inner_dst);
}
else if (fileInfo.isDir())
{
+ qDebug() << "recursing" << inner_src << " - " << inner_dst;
OK &= copyPath(inner_src, inner_dst, follow_symlinks);
}
else if (fileInfo.isFile())
{
+ qDebug() << "copying file" << inner_src << " - " << inner_dst;
OK &= QFile::copy(inner_src, inner_dst);
}
else
diff --git a/logic/minecraft/ModList.cpp b/logic/minecraft/ModList.cpp
index a77c5890..8f1bc041 100644
--- a/logic/minecraft/ModList.cpp
+++ b/logic/minecraft/ModList.cpp
@@ -539,6 +539,7 @@ QMimeData *ModList::mimeData(const QModelIndexList &indexes) const
data->setText(params.join('|'));
return data;
}
+
bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
const QModelIndex &parent)
{
diff --git a/logic/minecraft/World.cpp b/logic/minecraft/World.cpp
index 977a03cf..3b66f113 100644
--- a/logic/minecraft/World.cpp
+++ b/logic/minecraft/World.cpp
@@ -20,10 +20,13 @@
#include <pathutils.h>
#include "GZip.h"
+#include <MMCZip.h>
#include <sstream>
#include <io/stream_reader.h>
#include <tag_string.h>
#include <tag_primitive.h>
+#include <quazip.h>
+#include <quazipfile.h>
World::World(const QFileInfo &file)
{
@@ -32,8 +35,20 @@ World::World(const QFileInfo &file)
void World::repath(const QFileInfo &file)
{
- m_file = file;
+ m_containerFile = file;
m_folderName = file.fileName();
+ if(file.isFile() && file.suffix() == "zip")
+ {
+ readFromZip(file);
+ }
+ else if(file.isDir())
+ {
+ readFromFS(file);
+ }
+}
+
+void World::readFromFS(const QFileInfo &file)
+{
QDir worldDir(file.filePath());
is_valid = file.isDir() && worldDir.exists("level.dat");
if(!is_valid)
@@ -48,66 +63,123 @@ void World::repath(const QFileInfo &file)
{
return;
}
+ QFileInfo finfo(fullFilePath);
+ levelDatTime = finfo.lastModified();
+ parseLevelDat(f.readAll());
+}
- QByteArray output;
- is_valid = GZip::inflate(f.readAll(), output);
- if(!is_valid)
+void World::readFromZip(const QFileInfo &file)
+{
+ QuaZip zip(file.absoluteFilePath());
+ is_valid = zip.open(QuaZip::mdUnzip);
+ if (!is_valid)
+ {
+ return;
+ }
+ QuaZipFile zippedFile(&zip);
+ // read the install profile
+ is_valid = zip.setCurrentFile("level.dat");
+ if (!is_valid)
{
return;
}
- f.close();
+ is_valid = zippedFile.open(QIODevice::ReadOnly);
+ QuaZipFileInfo64 levelDatInfo;
+ zippedFile.getFileInfo(&levelDatInfo);
+ auto modTime = levelDatInfo.getNTFSmTime();
+ if(!modTime.isValid())
+ {
+ modTime = levelDatInfo.dateTime;
+ }
+ levelDatTime = modTime;
+ if (!is_valid)
+ {
+ return;
+ }
+ parseLevelDat(zippedFile.readAll());
+ zippedFile.close();
+}
- auto read_string = [](nbt::value& parent, const char * name, const QString & fallback = QString()) -> QString
+bool World::install(QString to)
+{
+ auto finalPath = PathCombine(to, DirNameFromString(m_actualName, to));
+ if(!ensureFolderPathExists(finalPath))
{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::String)
- {
- return fallback;
- }
- auto & tag_str = namedValue.as<nbt::tag_string>();
- return QString::fromStdString(tag_str.get());
- }
- catch(std::out_of_range e)
- {
- // fallback for old world formats
- qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback;
- return fallback;
- }
- catch(std::bad_cast e)
- {
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback;
- return fallback;
- }
- };
+ return false;
+ }
+ if(m_containerFile.isFile())
+ {
+ // FIXME: check if this is OK.
+ return !MMCZip::extractDir(m_containerFile.absoluteFilePath(), finalPath).isEmpty();
+ }
+ else if(m_containerFile.isDir())
+ {
+ QString from = m_containerFile.filePath();
+ return copyPath(from, finalPath);
+ }
+ return false;
+}
- auto read_long = [](nbt::value& parent, const char * name, const int64_t & fallback = 0) -> int64_t
+static QString read_string (nbt::value& parent, const char * name, const QString & fallback = QString())
+{
+ try
{
- try
- {
- auto &namedValue = parent.at(name);
- if(namedValue.get_type() != nbt::tag_type::Long)
- {
- return fallback;
- }
- auto & tag_str = namedValue.as<nbt::tag_long>();
- return tag_str.get();
- }
- catch(std::out_of_range e)
+ auto &namedValue = parent.at(name);
+ if(namedValue.get_type() != nbt::tag_type::String)
{
- // fallback for old world formats
- qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback;
return fallback;
}
- catch(std::bad_cast e)
+ auto & tag_str = namedValue.as<nbt::tag_string>();
+ return QString::fromStdString(tag_str.get());
+ }
+ catch(std::out_of_range e)
+ {
+ // fallback for old world formats
+ qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback;
+ return fallback;
+ }
+ catch(std::bad_cast e)
+ {
+ // type mismatch
+ qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback;
+ return fallback;
+ }
+};
+
+static int64_t read_long (nbt::value& parent, const char * name, const int64_t & fallback = 0)
+{
+ try
+ {
+ auto &namedValue = parent.at(name);
+ if(namedValue.get_type() != nbt::tag_type::Long)
{
- // type mismatch
- qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback;
return fallback;
}
- };
+ auto & tag_str = namedValue.as<nbt::tag_long>();
+ return tag_str.get();
+ }
+ catch(std::out_of_range e)
+ {
+ // fallback for old world formats
+ qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback;
+ return fallback;
+ }
+ catch(std::bad_cast e)
+ {
+ // type mismatch
+ qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback;
+ return fallback;
+ }
+};
+
+void World::parseLevelDat(QByteArray data)
+{
+ QByteArray output;
+ is_valid = GZip::inflate(data, output);
+ if(!is_valid)
+ {
+ return;
+ }
try
{
@@ -138,8 +210,7 @@ void World::repath(const QFileInfo &file)
int64_t temp = read_long(val, "LastPlayed", 0);
if(temp == 0)
{
- QFileInfo finfo(fullFilePath);
- m_lastPlayed = finfo.lastModified();
+ m_lastPlayed = levelDatTime;
}
else
{
@@ -164,11 +235,11 @@ bool World::replace(World &with)
{
if (!destroy())
return false;
- bool success = copyPath(with.m_file.filePath(), m_file.path());
+ bool success = copyPath(with.m_containerFile.filePath(), m_containerFile.path());
if (success)
{
m_folderName = with.m_folderName;
- m_file.refresh();
+ m_containerFile.refresh();
}
return success;
}
@@ -176,18 +247,15 @@ bool World::replace(World &with)
bool World::destroy()
{
if(!is_valid) return false;
- if (m_file.isDir())
+ if (m_containerFile.isDir())
{
- QDir d(m_file.filePath());
- if (d.removeRecursively())
- {
- return true;
- }
- return false;
+ QDir d(m_containerFile.filePath());
+ return d.removeRecursively();
}
- else
+ else if(m_containerFile.isFile())
{
- return false;
+ QFile file(m_containerFile.absoluteFilePath());
+ return file.remove();
}
return true;
}
diff --git a/logic/minecraft/World.h b/logic/minecraft/World.h
index 91cb2a83..27184e05 100644
--- a/logic/minecraft/World.h
+++ b/logic/minecraft/World.h
@@ -48,15 +48,23 @@ public:
// change the world's filesystem path (used by world lists for *MAGIC* purposes)
void repath(const QFileInfo &file);
+ bool install(QString to);
+
// WEAK compare operator - used for replacing worlds
bool operator==(const World &other) const;
bool strongCompare(const World &other) const;
+private:
+ void readFromZip(const QFileInfo &file);
+ void readFromFS(const QFileInfo &file);
+ void parseLevelDat(QByteArray data);
+
protected:
- QFileInfo m_file;
+ QFileInfo m_containerFile;
QString m_folderName;
QString m_actualName;
+ QDateTime levelDatTime;
QDateTime m_lastPlayed;
int64_t m_randomSeed = 0;
bool is_valid = false;
diff --git a/logic/minecraft/WorldList.cpp b/logic/minecraft/WorldList.cpp
index 7066093c..1c25f3b1 100644
--- a/logic/minecraft/WorldList.cpp
+++ b/logic/minecraft/WorldList.cpp
@@ -62,40 +62,28 @@ void WorldList::stopWatching()
}
}
-void WorldList::internalSort(QList<World> &what)
-{
- auto predicate = [](const World &left, const World &right)
- {
- return left.folderName().localeAwareCompare(right.folderName()) < 0;
- };
- std::sort(what.begin(), what.end(), predicate);
-}
-
bool WorldList::update()
{
if (!isValid())
return false;
- QList<World> orderedWorlds;
QList<World> newWorlds;
m_dir.refresh();
auto folderContents = m_dir.entryInfoList();
// if there are any untracked files...
- if (folderContents.size())
+ for (QFileInfo entry : folderContents)
{
- // the order surely changed!
- for (auto entry : folderContents)
+ if(!entry.isDir())
+ continue;
+
+ World w(entry);
+ if(w.isValid())
{
- World w(entry);
- if(w.isValid()) {
- newWorlds.append(w);
- }
+ newWorlds.append(w);
}
- internalSort(newWorlds);
- orderedWorlds.append(newWorlds);
}
beginResetModel();
- worlds.swap(orderedWorlds);
+ worlds.swap(newWorlds);
endResetModel();
return true;
}
@@ -232,6 +220,7 @@ QStringList WorldList::mimeTypes() const
{
QStringList types;
types << "text/plain";
+ types << "text/uri-list";
return types;
}
@@ -250,3 +239,90 @@ QMimeData *WorldList::mimeData(const QModelIndexList &indexes) const
data->setText(QString::number(row));
return data;
}
+
+Qt::ItemFlags WorldList::flags(const QModelIndex &index) const
+{
+ Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
+ if (index.isValid())
+ return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled |
+ defaultFlags;
+ else
+ return Qt::ItemIsDropEnabled | defaultFlags;
+}
+
+Qt::DropActions WorldList::supportedDragActions() const
+{
+ // move to other mod lists or VOID
+ return Qt::MoveAction;
+}
+
+Qt::DropActions WorldList::supportedDropActions() const
+{
+ // copy from outside, move from within and other mod lists
+ return Qt::CopyAction | Qt::MoveAction;
+}
+
+void WorldList::installWorld(QFileInfo filename)
+{
+ qDebug() << "installing: " << filename.absoluteFilePath();
+ World w(filename);
+ if(!w.isValid())
+ {
+ return;
+ }
+ w.install(m_dir.absolutePath());
+}
+
+bool WorldList::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;
+ // files dropped from outside?
+ if (data->hasUrls())
+ {
+ bool was_watching = is_watching;
+ if (was_watching)
+ stopWatching();
+ auto urls = data->urls();
+ for (auto url : urls)
+ {
+ // only local files may be dropped...
+ if (!url.isLocalFile())
+ continue;
+ QString filename = url.toLocalFile();
+
+ QFileInfo worldInfo(filename);
+ installWorld(worldInfo);
+ }
+ if (was_watching)
+ startWatching();
+ return true;
+ }
+ /*
+ else if (data->hasText())
+ {
+ QString sourcestr = data->text();
+ auto list = sourcestr.split('|');
+ if (list.size() != 2)
+ return false;
+ QString remoteId = list[0];
+ int remoteIndex = list[1].toInt();
+ qDebug() << "move: " << sourcestr;
+ // 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
+ moveModTo(remoteIndex, row);
+ return true;
+ }
+ */
+ return false;
+
+}
diff --git a/logic/minecraft/WorldList.h b/logic/minecraft/WorldList.h
index 7f119e81..90c7c6ed 100644
--- a/logic/minecraft/WorldList.h
+++ b/logic/minecraft/WorldList.h
@@ -19,6 +19,7 @@
#include <QString>
#include <QDir>
#include <QAbstractListModel>
+#include <QMimeData>
#include "minecraft/World.h"
#include "multimc_logic_export.h"
@@ -71,16 +72,28 @@ public:
/// Reloads the mod list and returns true if the list changed.
virtual bool update();
+ /// Install a world from location
+ void installWorld(QFileInfo filename);
+
/// Deletes the mod at the given index.
virtual bool deleteWorld(int index);
/// Deletes all the selected mods
virtual bool deleteWorlds(int first, int last);
+ /// 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;
void startWatching();
void stopWatching();
@@ -97,8 +110,6 @@ public:
return worlds;
}
-private:
- void internalSort(QList<World> &what);
private slots:
void directoryChanged(QString path);