Improve handling of destructive actions

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
TheKodeToad 2022-12-14 15:02:04 +00:00
parent e05ccc0e55
commit 127b094c41
17 changed files with 218 additions and 83 deletions

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -226,7 +227,7 @@ bool deletePath(QString path)
return err.value() == 0;
}
bool trash(QString path, QString *pathInTrash = nullptr)
bool trash(QString path, QString *pathInTrash)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
return false;

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -129,7 +130,7 @@ bool deletePath(QString path);
/**
* Trash a folder / file
*/
bool trash(QString path, QString *pathInTrash);
bool trash(QString path, QString *pathInTrash = nullptr);
QString PathCombine(const QString& path1, const QString& path2);
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -545,6 +546,10 @@ bool World::replace(World &with)
bool World::destroy()
{
if(!is_valid) return false;
if (FS::trash(m_containerFile.filePath()))
return true;
if (m_containerFile.isDir())
{
QDir d(m_containerFile.filePath());

View File

@ -143,5 +143,9 @@ bool Resource::enable(EnableAction action)
bool Resource::destroy()
{
m_type = ResourceType::UNKNOWN;
if (FS::trash(m_file_info.filePath()))
return true;
return FS::deletePath(m_file_info.filePath());
}

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -49,11 +50,35 @@
#include <DesktopServices.h>
#include <BuildConfig.h>
QString GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget)
QString GuiUtil::uploadPaste(const QString &name, const QString &text, QWidget *parentWidget)
{
ProgressDialog dialog(parentWidget);
auto pasteTypeSetting = static_cast<PasteUpload::PasteType>(APPLICATION->settings()->get("PastebinType").toInt());
auto pasteCustomAPIBaseSetting = APPLICATION->settings()->get("PastebinCustomAPIBase").toString();
{
QUrl baseUrl;
if (pasteCustomAPIBaseSetting.isEmpty())
baseUrl = PasteUpload::PasteTypes[pasteTypeSetting].defaultBase;
else
baseUrl = pasteCustomAPIBaseSetting;
if (baseUrl.isValid()) {
auto response = CustomMessageBox::selectable(parentWidget, "Confirm Upload",
QObject::tr("About to upload: %1\n"
"Uploading to: %2\n"
"You should double-check for personal information.\n\n"
"Are you sure?")
.arg(name)
.arg(baseUrl.host()),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
if (response != QMessageBox::Yes)
return "canceled";
}
}
std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, text, pasteCustomAPIBaseSetting, pasteTypeSetting));
dialog.execWithTask(paste.get());

View File

@ -4,7 +4,7 @@
namespace GuiUtil
{
QString uploadPaste(const QString &text, QWidget *parentWidget);
QString uploadPaste(const QString &name, const QString &text, QWidget *parentWidget);
void setClipboardText(const QString &text);
QStringList BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget);
QString BrowseForFile(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget);

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -490,7 +491,7 @@ public:
if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) {
helpMenu->addAction(actionReportBug);
}
if(!BuildConfig.MATRIX_URL.isEmpty()) {
helpMenu->addAction(actionMATRIX);
}
@ -2093,21 +2094,23 @@ void MainWindow::on_actionDeleteInstance_triggered()
auto id = m_selectedInstance->id();
auto response =
CustomMessageBox::selectable(this, tr("CAREFUL!"),
tr("About to delete: %1\nThis may be permanent and will completely delete the instance.\n\nAre you sure?")
.arg(m_selectedInstance->name()),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"),
tr("About to delete: %1\n"
"This may be permanent and will completely delete the instance.\n\n"
"Are you sure?")
.arg(m_selectedInstance->name()),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
if (response == QMessageBox::Yes) {
if (APPLICATION->instances()->trashInstance(id)) {
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
return;
}
if (response != QMessageBox::Yes)
return;
APPLICATION->instances()->deleteInstance(id);
if (APPLICATION->instances()->trashInstance(id)) {
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
return;
}
APPLICATION->instances()->deleteInstance(id);
}
void MainWindow::on_actionExportInstance_triggered()
@ -2252,7 +2255,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered()
}
QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.png");
QFile iconFile(iconPath);
if (!iconFile.open(QFile::WriteOnly))
{
@ -2261,7 +2264,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered()
}
bool success = icon->icon().pixmap(64, 64).save(&iconFile, "PNG");
iconFile.close();
if (!success)
{
iconFile.remove();
@ -2302,7 +2305,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered()
}
QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico");
// part of fix for weird bug involving the window icon being replaced
// dunno why it happens, but this 2-line fix seems to be enough, so w/e
auto appIcon = APPLICATION->getThemedIcon("logo");
@ -2325,7 +2328,7 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered()
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
return;
}
if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()),
QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() },
m_selectedInstance->name(), iconPath)) {

View File

@ -1,4 +1,5 @@
#include "ExternalResourcesPage.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui_ExternalResourcesPage.h"
#include "DesktopServices.h"
@ -128,7 +129,7 @@ bool ExternalResourcesPage::eventFilter(QObject* obj, QEvent* ev)
{
if (ev->type() != QEvent::KeyPress)
return QWidget::eventFilter(obj, ev);
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(ev);
if (obj == ui->treeView)
return listFilter(keyEvent);
@ -140,7 +141,6 @@ void ExternalResourcesPage::addItem()
{
if (!m_controlsEnabled)
return;
auto list = GuiUtil::BrowseForFiles(
helpPage(), tr("Select %1", "Select whatever type of files the page contains. Example: 'Loader Mods'").arg(displayName()),
@ -157,8 +157,49 @@ void ExternalResourcesPage::removeItem()
{
if (!m_controlsEnabled)
return;
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
int count = 0;
bool folder = false;
for (auto i : selection.indexes()) {
if (i.column() == 0) {
count++;
// if a folder is selected, show the confirmation dialog
if (m_model->at(i.row()).fileinfo().isDir())
folder = true;
}
}
bool enough = count > 1;
if (enough || folder) {
QString text;
if (enough)
text = tr("About to remove: %1 items\n"
"This may be permanent and they will be gone from the folder.\n\n"
"Are you sure?")
.arg(count);
else
text = tr("About to remove: %1 (folder)\n"
"This may be permanent and it will be gone from the parent folder.\n\n"
"Are you sure?")
.arg(m_model->at(selection.indexes().at(0).row()).fileinfo().fileName());
auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"), text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No,
QMessageBox::No)
->exec();
if (response != QMessageBox::Yes)
return;
}
removeItems(selection);
}
void ExternalResourcesPage::removeItems(const QItemSelection& selection)
{
m_model->deleteResources(selection.indexes());
}
@ -209,4 +250,3 @@ bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, const
return true;
}

View File

@ -50,7 +50,8 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
void filterTextChanged(const QString& newContents);
virtual void addItem();
virtual void removeItem();
void removeItem();
virtual void removeItems(const QItemSelection &selection);
virtual void enableItem();
virtual void disableItem();

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -277,28 +278,21 @@ void LogPage::on_btnPaste_clicked()
//FIXME: turn this into a proper task and move the upload logic out of GuiUtil!
m_model->append(
MessageLevel::Launcher,
QString("%2: Log upload triggered at: %1").arg(
QDateTime::currentDateTime().toString(Qt::RFC2822Date),
BuildConfig.LAUNCHER_DISPLAYNAME
QString("Log upload triggered at: %1").arg(
QDateTime::currentDateTime().toString(Qt::RFC2822Date)
)
);
auto url = GuiUtil::uploadPaste(m_model->toPlainText(), this);
if(!url.isEmpty())
auto url = GuiUtil::uploadPaste(tr("Minecraft Log"), m_model->toPlainText(), this);
if(url == "canceled")
{
m_model->append(
MessageLevel::Launcher,
QString("%2: Log uploaded to: %1").arg(
url,
BuildConfig.LAUNCHER_DISPLAYNAME
)
);
m_model->append(MessageLevel::Error, QString("Log upload canceled"));
}
else
else if(!url.isEmpty())
{
m_model->append(
MessageLevel::Error,
QString("%1: Log upload failed!").arg(BuildConfig.LAUNCHER_DISPLAYNAME)
);
m_model->append(MessageLevel::Launcher, QString("Log uploaded to: %1").arg(url));
}
else {
m_model->append(MessageLevel::Error, QString("Log upload failed!"));
}
}

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -139,13 +140,8 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, const QModelI
return true;
}
void ModFolderPage::removeItem()
void ModFolderPage::removeItems(const QItemSelection &selection)
{
if (!m_controlsEnabled)
return;
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
m_model->deleteMods(selection.indexes());
}

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -59,7 +60,7 @@ class ModFolderPage : public ExternalResourcesPage {
private slots:
void runningStateChanged(bool running);
void removeItem() override;
void removeItems(const QItemSelection &selection) override;
void installMods();
void updateMods();

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -204,7 +205,7 @@ void OtherLogsPage::on_btnReload_clicked()
void OtherLogsPage::on_btnPaste_clicked()
{
GuiUtil::uploadPaste(ui->text->toPlainText(), this);
GuiUtil::uploadPaste(m_currentFile, ui->text->toPlainText(), this);
}
void OtherLogsPage::on_btnCopy_clicked()
@ -219,13 +220,21 @@ void OtherLogsPage::on_btnDelete_clicked()
setControlsEnabled(false);
return;
}
if (QMessageBox::question(this, tr("Delete"),
tr("Do you really want to delete %1?").arg(m_currentFile),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
{
if (QMessageBox::question(this, tr("CAREFUL!"),
tr("About to delete: %1\n"
"This may be permanent and it will be gone from the logs folder.\n\n"
"Are you sure?")
.arg(m_currentFile),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
return;
}
QFile file(FS::PathCombine(m_path, m_currentFile));
if (FS::trash(file.fileName()))
{
return;
}
if (!file.remove())
{
QMessageBox::critical(this, tr("Error"), tr("Unable to delete %1: %2")
@ -243,15 +252,15 @@ void OtherLogsPage::on_btnClean_clicked()
return;
}
QMessageBox *messageBox = new QMessageBox(this);
messageBox->setWindowTitle(tr("Clean up"));
messageBox->setWindowTitle(tr("CAREFUL!"));
if(toDelete.size() > 5)
{
messageBox->setText(tr("Do you really want to delete all log files?"));
messageBox->setText(tr("Are you sure you want to delete all log files?"));
messageBox->setDetailedText(toDelete.join('\n'));
}
else
{
messageBox->setText(tr("Do you really want to delete these files?\n%1").arg(toDelete.join('\n')));
messageBox->setText(tr("Are you sure you want to delete all these files?\n%1").arg(toDelete.join('\n')));
}
messageBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
messageBox->setDefaultButton(QMessageBox::Ok);
@ -267,6 +276,10 @@ void OtherLogsPage::on_btnClean_clicked()
for(auto item: toDelete)
{
QFile file(FS::PathCombine(m_path, item));
if (FS::trash(file.fileName()))
{
continue;
}
if (!file.remove())
{
failed.push_back(item);

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -379,6 +380,24 @@ void ScreenshotsPage::on_actionUpload_triggered()
if (selection.isEmpty())
return;
QString text;
if (selection.size() > 1)
text = tr("About to upload: %1 screenshots\n\n"
"Are you sure?")
.arg(selection.size());
else
text =
tr("About to upload the selected screenshot.\n\n"
"Are you sure?");
auto response = CustomMessageBox::selectable(this, "Confirm Upload", text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No,
QMessageBox::No)
->exec();
if (response != QMessageBox::Yes)
return;
QList<ScreenShot::Ptr> uploaded;
auto job = NetJob::Ptr(new NetJob("Screenshot Upload", APPLICATION->network()));
if(selection.size() < 2)
@ -491,17 +510,32 @@ void ScreenshotsPage::on_actionCopy_File_s_triggered()
void ScreenshotsPage::on_actionDelete_triggered()
{
auto mbox = CustomMessageBox::selectable(
this, tr("Are you sure?"), tr("This will delete all selected screenshots."),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No);
std::unique_ptr<QMessageBox> box(mbox);
auto selected = ui->listView->selectionModel()->selectedIndexes();
if (box->exec() != QMessageBox::Yes)
int count = ui->listView->selectionModel()->selectedRows().size();
QString text;
if (count > 1)
text = tr("About to delete: %1 screenshots\n"
"This may be permanent and they will be gone from the folder.\n\n"
"Are you sure?")
.arg(count);
else
text = tr("About to delete the selected screenshot.\n"
"This may be permanent and it will be gone from the folder.\n\n"
"Are you sure?")
.arg(count);
auto response =
CustomMessageBox::selectable(this, tr("CAREFUL!"), text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No)->exec();
if (response != QMessageBox::Yes)
return;
auto selected = ui->listView->selectionModel()->selectedIndexes();
for (auto item : selected)
{
if (FS::trash(m_model->filePath(item)))
continue;
m_model->remove(item);
}
}

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,6 +36,7 @@
*/
#include "ServersPage.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui_ServersPage.h"
#include <FileSystem.h>
@ -799,6 +801,17 @@ void ServersPage::on_actionAdd_triggered()
void ServersPage::on_actionRemove_triggered()
{
auto response = CustomMessageBox::selectable(this, tr("CAREFUL!"),
tr("About to remove: %1\n"
"This is permanent and the server will be gone from your list forever (A LONG TIME).\n\n"
"Are you sure?")
.arg(m_model->at(currentServer)->m_name),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
if (response != QMessageBox::Yes)
return;
m_model->removeRow(currentServer);
}

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,6 +36,7 @@
*/
#include "WorldListPage.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui_WorldListPage.h"
#include "minecraft/WorldList.h"
@ -192,12 +194,14 @@ void WorldListPage::on_actionRemove_triggered()
if(!proxiedIndex.isValid())
return;
auto result = QMessageBox::question(this,
tr("Are you sure?"),
tr("This will remove the selected world permenantly.\n"
"The world will be gone forever (A LONG TIME).\n"
"\n"
"Do you want to continue?"));
auto result = CustomMessageBox::selectable(this, tr("CAREFUL!"),
tr("About to delete: %1\n"
"The world may be gone forever (A LONG TIME).\n\n"
"Are you sure?")
.arg(m_worlds->allWorlds().at(proxiedIndex.row()).name()),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
->exec();
if(result != QMessageBox::Yes)
{
return;

View File

@ -109,7 +109,7 @@
</action>
<action name="actionRemove">
<property name="text">
<string>Remove</string>
<string>Delete</string>
</property>
</action>
<action name="actionMCEdit">