diff --git a/api/logic/BaseInstance.cpp b/api/logic/BaseInstance.cpp index c81b70c6..f07b2bd0 100644 --- a/api/logic/BaseInstance.cpp +++ b/api/logic/BaseInstance.cpp @@ -99,16 +99,11 @@ void BaseInstance::iconUpdated(QString key) void BaseInstance::invalidate() { changeStatus(Status::Gone); + m_group = QString(); + emit groupChanged(); qDebug() << "Instance" << id() << "has been invalidated."; } -void BaseInstance::nuke() -{ - changeStatus(Status::Gone); - qDebug() << "Instance" << id() << "has been deleted by MultiMC."; - FS::deletePath(instanceRoot()); -} - void BaseInstance::changeStatus(BaseInstance::Status newStatus) { Status status = currentStatus(); diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h index a7004701..d9713a58 100644 --- a/api/logic/BaseInstance.h +++ b/api/logic/BaseInstance.h @@ -72,10 +72,6 @@ public: virtual void init() = 0; virtual void saveNow() = 0; - /// nuke thoroughly - deletes the instance contents, notifies the list/model which is - /// responsible of cleaning up the husk - void nuke(); - /*** * the instance has been invalidated - it is no longer tracked by MultiMC for some reason, * but it has not necessarily been deleted. diff --git a/api/logic/BaseInstanceProvider.h b/api/logic/BaseInstanceProvider.h index 1eee0e4f..095d4dab 100644 --- a/api/logic/BaseInstanceProvider.h +++ b/api/logic/BaseInstanceProvider.h @@ -8,6 +8,7 @@ #include "multimc_logic_export.h" using InstanceId = QString; +using GroupId = QString; using InstanceLocator = std::pair; enum class InstCreateError diff --git a/api/logic/FolderInstanceProvider.cpp b/api/logic/FolderInstanceProvider.cpp index 4f89e5b7..4a20127a 100644 --- a/api/logic/FolderInstanceProvider.cpp +++ b/api/logic/FolderInstanceProvider.cpp @@ -48,6 +48,7 @@ FolderInstanceProvider::FolderInstanceProvider(SettingsObjectPtr settings, const QList< InstanceId > FolderInstanceProvider::discoverInstances() { + qDebug() << "Discovering instances in" << m_instDir; QList out; QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks); while (iter.hasNext()) @@ -71,6 +72,8 @@ QList< InstanceId > FolderInstanceProvider::discoverInstances() out.append(id); qDebug() << "Found instance ID" << id; } + instanceSet = out.toSet(); + m_instancesProbed = true; return out; } @@ -115,6 +118,12 @@ InstancePtr FolderInstanceProvider::loadInstance(const InstanceId& id) void FolderInstanceProvider::saveGroupList() { + qDebug() << "Will save group list now."; + if(!m_instancesProbed) + { + qDebug() << "Group saving prevented because we don't know the full list of instances yet."; + return; + } WatchLock foo(m_watcher, m_instDir); QString groupFileName = m_instDir + "/instgroups.json"; QMap> reverseGroupMap; @@ -124,6 +133,11 @@ void FolderInstanceProvider::saveGroupList() QString group = iter.value(); if (group.isEmpty()) continue; + if(!instanceSet.contains(id)) + { + qDebug() << "Skipping saving missing instance" << id << "to groups list."; + continue; + } if (!reverseGroupMap.count(group)) { @@ -159,6 +173,7 @@ void FolderInstanceProvider::saveGroupList() try { FS::write(groupFileName, doc.toJson()); + qDebug() << "Group list saved."; } catch (const FS::FileSystemException &e) { @@ -168,6 +183,7 @@ void FolderInstanceProvider::saveGroupList() void FolderInstanceProvider::loadGroupList() { + qDebug() << "Will load group list now."; QSet groupSet; QString groupFileName = m_instDir + "/instgroups.json"; @@ -262,6 +278,7 @@ void FolderInstanceProvider::loadGroupList() } m_groupsLoaded = true; emit groupsChanged(groupSet); + qDebug() << "Group list loaded."; } void FolderInstanceProvider::groupChanged() @@ -309,6 +326,7 @@ static void clamp(T& current, T min, T max) } } +namespace { // List of numbers from min to max. Next is exponent times bigger than previous. class ExponentialSeries { @@ -335,12 +353,8 @@ public: unsigned m_max; unsigned m_exponent; }; +} -/* - * WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows. - * Basically, it starts messing things up while MultiMC is extracting/creating instances - * and causes that horrible failure that is NTFS to lock files in place because they are open. - */ class FolderInstanceStaging : public Task { Q_OBJECT @@ -405,6 +419,11 @@ private slots: } private: + /* + * WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows. + * Basically, it starts messing things up while MultiMC is extracting/creating instances + * and causes that horrible failure that is NTFS to lock files in place because they are open. + */ ExponentialSeries backoff; QString m_stagingPath; FolderInstanceProvider * m_parent; @@ -449,6 +468,7 @@ bool FolderInstanceProvider::commitStagedInstance(const QString& path, const QSt return false; } groupMap[instID] = groupName; + instanceSet.insert(instID); emit groupsChanged({groupName}); emit instancesChanged(); } diff --git a/api/logic/FolderInstanceProvider.h b/api/logic/FolderInstanceProvider.h index fc14ba7a..e1f2ad64 100644 --- a/api/logic/FolderInstanceProvider.h +++ b/api/logic/FolderInstanceProvider.h @@ -19,24 +19,6 @@ public: /// used by InstanceList to (re)load an instance with the given @id. InstancePtr loadInstance(const InstanceId& id) override; - - /* - // create instance in this provider - Task * creationTask(BaseVersionPtr version, const QString &instName, const QString &instGroup, const QString &instIcon); - - // copy instance to this provider - Task * copyTask(const InstancePtr &oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves); - - // import zipped instance into this provider - Task * zipImportTask(const QUrl sourceUrl, const QString &instName, const QString &instGroup, const QString &instIcon); - - //create FtbInstance - Task * ftbCreationTask(FtbPackDownloader *downloader, const QString &instName, const QString &instGroup, const QString &instIcon); - - // migrate an instance to the current format - Task * legacyUpgradeTask(const InstancePtr& oldInstance); -*/ - // Wrap an instance creation task in some more task machinery and make it ready to be used Task * wrapInstanceTask(InstanceTask * task); @@ -70,6 +52,8 @@ private: /* methods */ private: /* data */ QString m_instDir; QFileSystemWatcher * m_watcher; - QMap groupMap; + QMap groupMap; + QSet instanceSet; bool m_groupsLoaded = false; + bool m_instancesProbed = false; }; diff --git a/api/logic/InstanceList.cpp b/api/logic/InstanceList.cpp index c7a22b08..9dd08b5b 100644 --- a/api/logic/InstanceList.cpp +++ b/api/logic/InstanceList.cpp @@ -25,6 +25,7 @@ #include "BaseInstance.h" #include "FolderInstanceProvider.h" +#include "FileSystem.h" InstanceList::InstanceList(QObject *parent) : QAbstractListModel(parent) @@ -118,6 +119,25 @@ void InstanceList::deleteGroup(const QString& name) } } +void InstanceList::deleteInstance(const InstanceId& id) +{ + auto inst = getInstanceById(id); + if(!inst) + { + qDebug() << "Cannot delete instance" << id << " No such instance is present."; + return; + } + + qDebug() << "Will delete instance" << id; + if(!FS::deletePath(inst->instanceRoot())) + { + qWarning() << "Deletion of instance" << id << "has not been completely successful ..."; + return; + } + + qDebug() << "Instance" << id << "has been deleted by MultiMC."; +} + static QMap getIdMapping(const QList &list) { QMap out; diff --git a/api/logic/InstanceList.h b/api/logic/InstanceList.h index 7fe5ea34..14d5280d 100644 --- a/api/logic/InstanceList.h +++ b/api/logic/InstanceList.h @@ -80,7 +80,8 @@ public: QModelIndex getInstanceIndexById(const QString &id) const; QStringList getGroups(); - void deleteGroup(const QString & name); + void deleteGroup(const GroupId & name); + void deleteInstance(const InstanceId & id); signals: void dataIsInvalid(); diff --git a/application/MainWindow.cpp b/application/MainWindow.cpp index acb8e92b..cf9e5099 100644 --- a/application/MainWindow.cpp +++ b/application/MainWindow.cpp @@ -1617,7 +1617,7 @@ void MainWindow::on_actionDeleteInstance_triggered() )->exec(); if (response == QMessageBox::Yes) { - m_selectedInstance->nuke(); + MMC->instances()->deleteInstance(m_selectedInstance->id()); } } diff --git a/application/dialogs/NewInstanceDialog.cpp b/application/dialogs/NewInstanceDialog.cpp index c68d6537..46e6ca34 100644 --- a/application/dialogs/NewInstanceDialog.cpp +++ b/application/dialogs/NewInstanceDialog.cpp @@ -64,8 +64,6 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString ui->groupBox->setCurrentIndex(index); ui->groupBox->lineEdit()->setPlaceholderText(tr("No group")); - m_buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - m_buttons->button(QDialogButtonBox::Ok)->setDefault(true); m_container = new PageContainer(this); @@ -73,8 +71,25 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString m_container->layout()->setContentsMargins(0, 0, 0, 0); ui->verticalLayout->insertWidget(2, m_container); + m_buttons = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_container->addButtons(m_buttons); - m_buttons->setFocus(); + + // Bonk Qt over its stupid head and make sure it understands which button is the default one... + // See: https://stackoverflow.com/questions/24556831/qbuttonbox-set-default-button + auto OkButton = m_buttons->button(QDialogButtonBox::Ok); + OkButton->setDefault(true); + OkButton->setAutoDefault(true); + connect(OkButton, &QPushButton::clicked, this, &NewInstanceDialog::accept); + + auto CancelButton = m_buttons->button(QDialogButtonBox::Cancel); + CancelButton->setDefault(false); + CancelButton->setAutoDefault(false); + connect(CancelButton, &QPushButton::clicked, this, &NewInstanceDialog::reject); + + auto HelpButton = m_buttons->button(QDialogButtonBox::Help); + HelpButton->setDefault(false); + HelpButton->setAutoDefault(false); + connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help); if(!url.isEmpty()) { @@ -82,14 +97,9 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString importPage->setUrl(url); } - connect(m_buttons->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &NewInstanceDialog::accept); - connect(m_buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &NewInstanceDialog::reject); - connect(m_buttons->button(QDialogButtonBox::Help), &QPushButton::clicked, m_container, &PageContainer::help); - updateDialogState(); restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("NewInstanceGeometry").toByteArray())); - } void NewInstanceDialog::reject() @@ -160,7 +170,11 @@ InstanceTask * NewInstanceDialog::extractTask() void NewInstanceDialog::updateDialogState() { auto allowOK = creationTask && !instName().isEmpty(); - m_buttons->button(QDialogButtonBox::Ok)->setEnabled(allowOK); + auto OkButton = m_buttons->button(QDialogButtonBox::Ok); + if(OkButton->isEnabled() != allowOK) + { + OkButton->setEnabled(allowOK); + } } QString NewInstanceDialog::instName() const