diff options
author | Petr Mrázek <peterix@gmail.com> | 2013-08-18 20:52:17 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2013-08-18 20:52:17 +0200 |
commit | c92ad7dcf86f2e5e71d71a68e24e79fbdeceb56d (patch) | |
tree | 7be301a5123e216a41646f9639ec887609497a87 | |
parent | 253067c782955380bbf66ac0475dc954375b1ff4 (diff) | |
download | MultiMC-c92ad7dcf86f2e5e71d71a68e24e79fbdeceb56d.tar MultiMC-c92ad7dcf86f2e5e71d71a68e24e79fbdeceb56d.tar.gz MultiMC-c92ad7dcf86f2e5e71d71a68e24e79fbdeceb56d.tar.lz MultiMC-c92ad7dcf86f2e5e71d71a68e24e79fbdeceb56d.tar.xz MultiMC-c92ad7dcf86f2e5e71d71a68e24e79fbdeceb56d.zip |
Drag and Drop, mod management.
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | depends/util/include/pathutils.h | 5 | ||||
-rw-r--r-- | depends/util/src/pathutils.cpp | 28 | ||||
-rw-r--r-- | gui/LegacyModEditDialog.cpp | 92 | ||||
-rw-r--r-- | gui/LegacyModEditDialog.h | 9 | ||||
-rw-r--r-- | gui/LegacyModEditDialog.ui | 40 | ||||
-rw-r--r-- | gui/ModListView.cpp | 32 | ||||
-rw-r--r-- | gui/ModListView.h | 10 | ||||
-rw-r--r-- | gui/mainwindow.cpp | 24 | ||||
-rw-r--r-- | gui/mainwindow.ui | 3 | ||||
-rw-r--r-- | logic/LegacyInstance.cpp | 14 | ||||
-rw-r--r-- | logic/LegacyInstance.h | 2 | ||||
-rw-r--r-- | logic/Mod.cpp | 2 | ||||
-rw-r--r-- | logic/Mod.h | 7 | ||||
-rw-r--r-- | logic/ModList.cpp | 254 | ||||
-rw-r--r-- | logic/ModList.h | 24 |
16 files changed, 469 insertions, 79 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 84d8a6f7..29d3a286 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,6 +173,7 @@ gui/lwjglselectdialog.h gui/instancesettings.h gui/IconPickerDialog.h gui/LegacyModEditDialog.h +gui/ModListView.h # Base classes and infrastructure logic/InstanceVersion.h @@ -242,6 +243,7 @@ gui/lwjglselectdialog.cpp gui/instancesettings.cpp gui/IconPickerDialog.cpp gui/LegacyModEditDialog.cpp +gui/ModListView.cpp # Base classes and infrastructure logic/InstanceFactory.cpp diff --git a/depends/util/include/pathutils.h b/depends/util/include/pathutils.h index d4f41da3..40bb9e74 100644 --- a/depends/util/include/pathutils.h +++ b/depends/util/include/pathutils.h @@ -33,5 +33,10 @@ LIBUTIL_EXPORT bool ensurePathExists(QString filenamepath); LIBUTIL_EXPORT bool copyPath(QString src, QString dst); +/// Opens the given file in the default application. +LIBUTIL_EXPORT void openFileInDefaultProgram ( QString filename ); + +/// Opens the given directory in the default application. +LIBUTIL_EXPORT void openDirInDefaultProgram ( QString dirpath, bool ensureExists = false ); #endif // PATHUTILS_H diff --git a/depends/util/src/pathutils.cpp b/depends/util/src/pathutils.cpp index 97287840..5bafdf0f 100644 --- a/depends/util/src/pathutils.cpp +++ b/depends/util/src/pathutils.cpp @@ -17,6 +17,8 @@ #include <QFileInfo> #include <QDir> +#include <QDesktopServices> +#include <QUrl> QString PathCombine(QString path1, QString path2) { @@ -70,7 +72,7 @@ bool ensurePathExists(QString filenamepath) { QFileInfo a ( filenamepath ); QDir dir; - return (dir.mkpath ( a.path() )); + return (dir.mkpath ( a.filePath() )); } bool copyPath(QString src, QString dst) @@ -78,12 +80,14 @@ bool copyPath(QString src, QString dst) QDir dir(src); if (!dir.exists()) return false; + if(!ensurePathExists(dst)) + return false; foreach (QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { - QString dst_path = dst + QDir::separator() + d; - dir.mkpath(dst_path); - copyPath(src+ QDir::separator() + d, dst_path); + QString inner_src = src+ QDir::separator() + d; + QString inner_dst = dst + QDir::separator() + d; + copyPath(inner_src, inner_dst); } foreach (QString f, dir.entryList(QDir::Files)) @@ -92,3 +96,19 @@ bool copyPath(QString src, QString dst) } return true; } + +void openDirInDefaultProgram ( QString path, bool ensureExists ) +{ + QDir parentPath; + QDir dir( path ); + if(!dir.exists()) + { + parentPath.mkpath(dir.absolutePath()); + } + QDesktopServices::openUrl ( "file:///" + dir.absolutePath() ); +} + +void openFileInDefaultProgram ( QString filename ) +{ + QDesktopServices::openUrl ( "file:///" + QFileInfo ( filename ).absolutePath() ); +} diff --git a/gui/LegacyModEditDialog.cpp b/gui/LegacyModEditDialog.cpp index a6becd7d..61e3872e 100644 --- a/gui/LegacyModEditDialog.cpp +++ b/gui/LegacyModEditDialog.cpp @@ -15,6 +15,9 @@ #include "LegacyModEditDialog.h" #include "ui_LegacyModEditDialog.h" +#include <logic/ModList.h> +#include <pathutils.h> +#include <QFileDialog> LegacyModEditDialog::LegacyModEditDialog( LegacyInstance* inst, QWidget* parent ) : m_inst(inst), @@ -22,7 +25,22 @@ LegacyModEditDialog::LegacyModEditDialog( LegacyInstance* inst, QWidget* parent ui(new Ui::LegacyModEditDialog) { ui->setupUi(this); + ensurePathExists(m_inst->coreModsDir()); + ensurePathExists(m_inst->mlModsDir()); + ensurePathExists(m_inst->jarModsDir()); + m_mods = m_inst->loaderModList(); + m_coremods = m_inst->coreModList(); + m_jarmods = m_inst->jarModList(); + /* + m_mods->startWatching(); + m_coremods->startWatching(); + m_jarmods->startWatching(); + */ + + ui->jarModsTreeView->setModel(m_jarmods.data()); + ui->coreModsTreeView->setModel(m_coremods.data()); + ui->mlModTreeView->setModel(m_mods.data()); } LegacyModEditDialog::~LegacyModEditDialog() @@ -30,6 +48,80 @@ LegacyModEditDialog::~LegacyModEditDialog() delete ui; } +void LegacyModEditDialog::on_addCoreBtn_clicked() +{ + QStringList fileNames = QFileDialog::getOpenFileNames(this, "Select Core Mods"); + for(auto filename:fileNames) + { + m_coremods->installMod(QFileInfo(filename)); + } +} +void LegacyModEditDialog::on_addForgeBtn_clicked() +{ + +} +void LegacyModEditDialog::on_addJarBtn_clicked() +{ + +} +void LegacyModEditDialog::on_addModBtn_clicked() +{ + +} +void LegacyModEditDialog::on_addTexPackBtn_clicked() +{ + +} +void LegacyModEditDialog::on_moveJarDownBtn_clicked() +{ + +} +void LegacyModEditDialog::on_moveJarUpBtn_clicked() +{ + +} +void LegacyModEditDialog::on_rmCoreBtn_clicked() +{ + auto sm = ui->coreModsTreeView->selectionModel(); + auto selection = sm->selectedRows(); + if(!selection.size()) + return; + m_coremods->deleteMod(selection[0].row()); +} +void LegacyModEditDialog::on_rmJarBtn_clicked() +{ + auto sm = ui->jarModsTreeView->selectionModel(); + auto selection = sm->selectedRows(); + if(!selection.size()) + return; + m_jarmods->deleteMod(selection[0].row()); +} +void LegacyModEditDialog::on_rmModBtn_clicked() +{ + auto sm = ui->mlModTreeView->selectionModel(); + auto selection = sm->selectedRows(); + if(!selection.size()) + return; + m_mods->deleteMod(selection[0].row()); +} +void LegacyModEditDialog::on_rmTexPackBtn_clicked() +{ + +} +void LegacyModEditDialog::on_viewCoreBtn_clicked() +{ + openDirInDefaultProgram(m_inst->coreModsDir(), true); +} +void LegacyModEditDialog::on_viewModBtn_clicked() +{ + openDirInDefaultProgram(m_inst->mlModsDir(), true); +} +void LegacyModEditDialog::on_viewTexPackBtn_clicked() +{ + //openDirInDefaultProgram(m_inst->mlModsDir(), true); +} + + void LegacyModEditDialog::on_buttonBox_rejected() { close(); diff --git a/gui/LegacyModEditDialog.h b/gui/LegacyModEditDialog.h index 3af6c8d7..0654300f 100644 --- a/gui/LegacyModEditDialog.h +++ b/gui/LegacyModEditDialog.h @@ -31,9 +31,9 @@ public: ~LegacyModEditDialog(); private slots: - /* Mapped for implementation + void on_addJarBtn_clicked(); - void on_rmJarBtn_clicked(); + void on_rmJarBtn_clicked(); void on_addForgeBtn_clicked(); void on_moveJarUpBtn_clicked(); void on_moveJarDownBtn_clicked(); @@ -49,11 +49,14 @@ private slots: void on_addTexPackBtn_clicked(); void on_rmTexPackBtn_clicked(); void on_viewTexPackBtn_clicked(); - */ // Questionable: SettingsDialog doesn't need this for some reason? void on_buttonBox_rejected(); private: Ui::LegacyModEditDialog *ui; + QSharedPointer<ModList> m_mods; + QSharedPointer<ModList> m_coremods; + QSharedPointer<ModList> m_jarmods; + QSharedPointer<ModList> m_texturepacks; LegacyInstance * m_inst; }; diff --git a/gui/LegacyModEditDialog.ui b/gui/LegacyModEditDialog.ui index d55c3e1c..c219e7c2 100644 --- a/gui/LegacyModEditDialog.ui +++ b/gui/LegacyModEditDialog.ui @@ -16,9 +16,6 @@ <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QTabWidget" name="tabWidget"> - <property name="acceptDrops"> - <bool>false</bool> - </property> <property name="currentIndex"> <number>0</number> </property> @@ -28,12 +25,12 @@ </attribute> <layout class="QHBoxLayout" name="horizontalLayout"> <item> - <widget class="QListView" name="jarModsListView"> - <property name="acceptDrops"> - <bool>true</bool> + <widget class="ModListView" name="jarModsTreeView"> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOn</enum> </property> - <property name="dragDropMode"> - <enum>QAbstractItemView::DropOnly</enum> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> </property> </widget> </item> @@ -55,6 +52,9 @@ </item> <item> <widget class="QPushButton" name="addForgeBtn"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="text"> <string>MCForge</string> </property> @@ -97,10 +97,7 @@ </attribute> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> - <widget class="QListView" name="coreModsListView"> - <property name="acceptDrops"> - <bool>true</bool> - </property> + <widget class="ModListView" name="coreModsTreeView"> <property name="dragDropMode"> <enum>QAbstractItemView::DropOnly</enum> </property> @@ -152,7 +149,7 @@ </attribute> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> - <widget class="QListView" name="mlModListView"> + <widget class="ModListView" name="mlModTreeView"> <property name="acceptDrops"> <bool>true</bool> </property> @@ -210,19 +207,13 @@ </attribute> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> - <widget class="QListView" name="texPackListView"> + <widget class="ModListView" name="texPackTreeView"> <property name="acceptDrops"> <bool>true</bool> </property> - <property name="dragEnabled"> - <bool>false</bool> - </property> <property name="dragDropMode"> <enum>QAbstractItemView::DropOnly</enum> </property> - <property name="defaultDropAction"> - <enum>Qt::IgnoreAction</enum> - </property> </widget> </item> <item> @@ -270,12 +261,19 @@ <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="standardButtons"> - <set>QDialogButtonBox::Close</set> + <set>QDialogButtonBox::Close|QDialogButtonBox::Reset</set> </property> </widget> </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>ModListView</class> + <extends>QTreeView</extends> + <header>gui/ModListView.h</header> + </customwidget> + </customwidgets> <resources/> <connections/> </ui> diff --git a/gui/ModListView.cpp b/gui/ModListView.cpp new file mode 100644 index 00000000..a9898851 --- /dev/null +++ b/gui/ModListView.cpp @@ -0,0 +1,32 @@ +#include "ModListView.h" +#include <QHeaderView> +#include <QMouseEvent> + +ModListView::ModListView ( QWidget* parent ) + :QTreeView ( parent ) +{ + setAllColumnsShowFocus ( true ); + setExpandsOnDoubleClick ( false ); + setRootIsDecorated ( false ); + setSortingEnabled ( false ); + setAlternatingRowColors ( true ); + setSelectionMode ( QAbstractItemView::SingleSelection ); + setHeaderHidden ( false ); + setSelectionBehavior(QAbstractItemView::SelectRows); + setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOn ); + setHorizontalScrollBarPolicy ( Qt::ScrollBarAsNeeded ); + setDropIndicatorShown(true); + setDragEnabled(true); + setDragDropMode(QAbstractItemView::DragDrop); + viewport()->setAcceptDrops(true); +} + +void ModListView::setModel ( QAbstractItemModel* model ) +{ + QTreeView::setModel ( model ); + auto head = header(); + head->setStretchLastSection(false); + head->setSectionResizeMode(0, QHeaderView::Stretch); + head->setSectionResizeMode(1, QHeaderView::ResizeToContents); + dropIndicatorPosition(); +} diff --git a/gui/ModListView.h b/gui/ModListView.h new file mode 100644 index 00000000..1cbf86b7 --- /dev/null +++ b/gui/ModListView.h @@ -0,0 +1,10 @@ +#pragma once +#include <QTreeView> + +class ModListView: public QTreeView +{ + Q_OBJECT +public: + explicit ModListView ( QWidget* parent = 0 ); + virtual void setModel ( QAbstractItemModel* model ); +};
\ No newline at end of file diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 5087b38f..ba7e135c 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -64,11 +64,6 @@ #include "instancedelegate.h" #include "IconPickerDialog.h" -// Opens the given file in the default application. -// TODO: Move this somewhere. -void openFileInDefaultProgram ( QString filename ); -void openDirInDefaultProgram ( QString dirpath, bool ensureExists = false ); - MainWindow::MainWindow ( QWidget *parent ) : QMainWindow ( parent ), ui ( new Ui::MainWindow ), @@ -376,7 +371,8 @@ void MainWindow::on_actionEditInstMods_triggered() if (inst) { auto dialog = inst->createModEditDialog(this); - dialog->exec(); + if(dialog) + dialog->exec(); } } @@ -537,22 +533,6 @@ void MainWindow::openWebPage ( QUrl url ) browser->exec(); } -void openDirInDefaultProgram ( QString path, bool ensureExists ) -{ - QDir parentPath; - QDir dir( path ); - if(!dir.exists()) - { - parentPath.mkpath(dir.absolutePath()); - } - QDesktopServices::openUrl ( "file:///" + dir.absolutePath() ); -} - -void openFileInDefaultProgram ( QString filename ) -{ - QDesktopServices::openUrl ( "file:///" + QFileInfo ( filename ).absolutePath() ); -} - void MainWindow::on_actionChangeInstMCVersion_triggered() { if (view->selectionModel()->selectedIndexes().count() < 1) diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index ae34bf51..893062a5 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -347,9 +347,6 @@ </property> </action> <action name="actionEditInstMods"> - <property name="enabled"> - <bool>false</bool> - </property> <property name="text"> <string>Edit Mods</string> </property> 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 <pathutils.h> +#include <QMimeData> +#include <QUrl> +#include <QDebug> +#include <QUuid> 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<Mod> 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<Mod> mods; }; |