Skip to content

Commit

Permalink
Qt: Add 'Rename File' to memory card editor
Browse files Browse the repository at this point in the history
And context menu for ease of use.
  • Loading branch information
stenzek committed Oct 18, 2024
1 parent 50d8bb0 commit b99ee59
Show file tree
Hide file tree
Showing 7 changed files with 360 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/duckstation-qt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ set(SRCS
memorycardeditorwindow.cpp
memorycardeditorwindow.h
memorycardeditorwindow.ui
memorycardrenamefiledialog.ui
memorycardsettingswidget.cpp
memorycardsettingswidget.h
memoryscannerwindow.cpp
Expand Down
3 changes: 3 additions & 0 deletions src/duckstation-qt/duckstation-qt.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,9 @@
<QtUi Include="gamecheatcodechoiceeditordialog.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="memorycardrenamefiledialog.ui">
<FileType>Document</FileType>
</QtUi>
<None Include="translations\duckstation-qt_es-es.ts" />
<None Include="translations\duckstation-qt_tr.ts" />
</ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions src/duckstation-qt/duckstation-qt.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@
<QtUi Include="gamecheatsettingswidget.ui" />
<QtUi Include="gamepatchdetailswidget.ui" />
<QtUi Include="gamecheatcodechoiceeditordialog.ui" />
<QtUi Include="memorycardrenamefiledialog.ui" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="duckstation-qt.rc" />
Expand Down
175 changes: 175 additions & 0 deletions src/duckstation-qt/memorycardeditorwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <QtCore/QFileInfo>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMessageBox>

static constexpr char MEMORY_CARD_IMAGE_FILTER[] = QT_TRANSLATE_NOOP(
Expand All @@ -23,6 +24,11 @@ static constexpr char MEMORY_CARD_IMPORT_FILTER[] =
QT_TRANSLATE_NOOP("MemoryCardEditorWindow", "All Importable Memory Card Types (*.mcd *.mcr *.mc *.gme)");
static constexpr char SINGLE_SAVEFILE_FILTER[] =
TRANSLATE_NOOP("MemoryCardEditorWindow", "Single Save Files (*.mcs);;All Files (*.*)");
static constexpr std::array<std::pair<ConsoleRegion, const char*>, 3> MEMORY_CARD_FILE_REGION_PREFIXES = {{
{ConsoleRegion::NTSC_U, "BA"},
{ConsoleRegion::NTSC_J, "BI"},
{ConsoleRegion::PAL, "BE"},
}};

MemoryCardEditorWindow::MemoryCardEditorWindow() : QWidget()
{
Expand All @@ -31,6 +37,7 @@ MemoryCardEditorWindow::MemoryCardEditorWindow() : QWidget()

m_deleteFile = m_ui.centerButtonBox->addButton(tr("Delete File"), QDialogButtonBox::ActionRole);
m_undeleteFile = m_ui.centerButtonBox->addButton(tr("Undelete File"), QDialogButtonBox::ActionRole);
m_renameFile = m_ui.centerButtonBox->addButton(tr("Rename File"), QDialogButtonBox::ActionRole);
m_exportFile = m_ui.centerButtonBox->addButton(tr("Export File"), QDialogButtonBox::ActionRole);
m_moveLeft = m_ui.centerButtonBox->addButton(tr("<<"), QDialogButtonBox::ActionRole);
m_moveRight = m_ui.centerButtonBox->addButton(tr(">>"), QDialogButtonBox::ActionRole);
Expand Down Expand Up @@ -135,7 +142,11 @@ void MemoryCardEditorWindow::connectCardUi(Card* card, QDialogButtonBox* buttonB
void MemoryCardEditorWindow::connectUi()
{
connect(m_ui.cardA, &QTableWidget::itemSelectionChanged, this, &MemoryCardEditorWindow::onCardASelectionChanged);
connect(m_ui.cardA, &QTableWidget::customContextMenuRequested, this,
&MemoryCardEditorWindow::onCardContextMenuRequested);
connect(m_ui.cardB, &QTableWidget::itemSelectionChanged, this, &MemoryCardEditorWindow::onCardBSelectionChanged);
connect(m_ui.cardB, &QTableWidget::customContextMenuRequested, this,
&MemoryCardEditorWindow::onCardContextMenuRequested);
connect(m_moveLeft, &QPushButton::clicked, this, &MemoryCardEditorWindow::doCopyFile);
connect(m_moveRight, &QPushButton::clicked, this, &MemoryCardEditorWindow::doCopyFile);
connect(m_deleteFile, &QPushButton::clicked, this, &MemoryCardEditorWindow::doDeleteFile);
Expand All @@ -149,6 +160,7 @@ void MemoryCardEditorWindow::connectUi()
connect(m_ui.newCardB, &QPushButton::clicked, [this]() { newCard(&m_card_b); });
connect(m_ui.openCardA, &QPushButton::clicked, [this]() { openCard(&m_card_a); });
connect(m_ui.openCardB, &QPushButton::clicked, [this]() { openCard(&m_card_b); });
connect(m_renameFile, &QPushButton::clicked, this, &MemoryCardEditorWindow::doRenameSaveFile);
connect(m_exportFile, &QPushButton::clicked, this, &MemoryCardEditorWindow::doExportSaveFile);
}

Expand Down Expand Up @@ -522,6 +534,33 @@ void MemoryCardEditorWindow::doExportSaveFile()
}
}

void MemoryCardEditorWindow::doRenameSaveFile()
{
const auto [card, fi] = getSelectedFile();
if (!fi)
return;

const std::string new_name = MemoryCardRenameFileDialog::promptForNewName(this, fi->filename);
if (new_name.empty())
return;

Error error;
if (!MemoryCardImage::RenameFile(&card->data, *fi, new_name, &error))
{
QMessageBox::critical(this, tr("Error"),
tr("Failed to rename save file %1:\n%2")
.arg(QString::fromStdString(fi->filename))
.arg(QString::fromStdString(error.GetDescription())));
return;
}

clearSelection();
setCardDirty(card);
updateCardTable(card);
updateCardBlocksFree(card);
updateButtonState();
}

void MemoryCardEditorWindow::importCard(Card* card)
{
promptForSave(card);
Expand Down Expand Up @@ -597,6 +636,35 @@ void MemoryCardEditorWindow::importSaveFile(Card* card)
updateCardBlocksFree(card);
}

void MemoryCardEditorWindow::onCardContextMenuRequested(const QPoint& pos)
{
QTableWidget* table = qobject_cast<QTableWidget*>(sender());
if (!table)
return;

const auto& [card, fi] = getSelectedFile();
if (!card)
return;

QMenu menu(table);
QAction* action = menu.addAction(tr("Delete File"));
action->setEnabled(fi && !fi->deleted);
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doDeleteFile);
action = menu.addAction(tr("Undelete File"));
action->setEnabled(fi && fi->deleted);
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doUndeleteFile);
action = menu.addAction(tr("Rename File"));
action->setEnabled(fi != nullptr);
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doRenameSaveFile);
action = menu.addAction(tr("Export File"));
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doExportSaveFile);
action = menu.addAction(tr("Copy File"));
action->setEnabled(fi && !m_card_a.filename.empty() && !m_card_b.filename.empty());
connect(action, &QAction::triggered, this, &MemoryCardEditorWindow::doCopyFile);

menu.exec(table->mapToGlobal(pos));
}

std::tuple<MemoryCardEditorWindow::Card*, const MemoryCardImage::FileInfo*> MemoryCardEditorWindow::getSelectedFile()
{
QList<QTableWidgetSelectionRange> sel = m_card_a.table->selectedRanges();
Expand Down Expand Up @@ -629,8 +697,115 @@ void MemoryCardEditorWindow::updateButtonState()
m_deleteFile->setEnabled(has_selection);
m_undeleteFile->setEnabled(is_deleted);
m_exportFile->setEnabled(has_selection);
m_renameFile->setEnabled(has_selection);
m_moveLeft->setEnabled(both_cards_present && has_selection && is_card_b);
m_moveRight->setEnabled(both_cards_present && has_selection && !is_card_b);
m_ui.buttonBoxA->setEnabled(card_a_present);
m_ui.buttonBoxB->setEnabled(card_b_present);
}

MemoryCardRenameFileDialog::MemoryCardRenameFileDialog(QWidget* parent, std::string_view old_name) : QDialog(parent)
{
m_ui.setupUi(this);
setupAdditionalUi();

const QString original_name = QtUtils::StringViewToQString(old_name);
m_ui.originalName->setText(original_name);
m_ui.fullFilename->setText(original_name);
updateSimplifiedFieldsFromFullName();
}

MemoryCardRenameFileDialog::~MemoryCardRenameFileDialog() = default;

std::string MemoryCardRenameFileDialog::promptForNewName(QWidget* parent, std::string_view old_name)
{
MemoryCardRenameFileDialog dlg(parent, old_name);

std::string ret;
if (dlg.exec() != 1)
return ret;

ret = dlg.m_ui.fullFilename->text().toStdString();
return ret;
}

void MemoryCardRenameFileDialog::setupAdditionalUi()
{
m_ui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("memcard-line")).pixmap(32, 32));

for (const auto& [region, prefix] : MEMORY_CARD_FILE_REGION_PREFIXES)
{
m_ui.region->addItem(QtUtils::GetIconForRegion(region), Settings::GetConsoleRegionDisplayName(region),
QVariant(QString::fromUtf8(prefix)));
}

connect(m_ui.region, &QComboBox::currentIndexChanged, this,
&MemoryCardRenameFileDialog::updateFullNameFromSimplifiedFields);
connect(m_ui.serial, &QLineEdit::textChanged, this, &MemoryCardRenameFileDialog::updateFullNameFromSimplifiedFields);
connect(m_ui.filename, &QLineEdit::textChanged, this,
&MemoryCardRenameFileDialog::updateFullNameFromSimplifiedFields);

connect(m_ui.fullFilename, &QLineEdit::textChanged, this,
&MemoryCardRenameFileDialog::updateSimplifiedFieldsFromFullName);

connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &MemoryCardRenameFileDialog::accept);
connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &MemoryCardRenameFileDialog::reject);

m_ui.fullFilename->setFocus();
}

void MemoryCardRenameFileDialog::updateSimplifiedFieldsFromFullName()
{
const QString full_name = m_ui.fullFilename->text();

const QString region = full_name.mid(0, MemoryCardImage::FILE_REGION_LENGTH);
const QString serial = full_name.mid(MemoryCardImage::FILE_REGION_LENGTH, MemoryCardImage::FILE_SERIAL_LENGTH);
const QString filename = full_name.mid(MemoryCardImage::FILE_REGION_LENGTH + MemoryCardImage::FILE_SERIAL_LENGTH);

{
QSignalBlocker sb(m_ui.region);

while (m_ui.region->count() > static_cast<int>(MEMORY_CARD_FILE_REGION_PREFIXES.size()))
m_ui.region->removeItem(m_ui.region->count() - 1);

const std::string regionStr = region.toStdString();
size_t i;
for (i = 0; i < MEMORY_CARD_FILE_REGION_PREFIXES.size(); i++)
{
if (regionStr == MEMORY_CARD_FILE_REGION_PREFIXES[i].second)
{
m_ui.region->setCurrentIndex(static_cast<int>(i));
break;
}
}
if (i == MEMORY_CARD_FILE_REGION_PREFIXES.size())
{
m_ui.region->addItem(tr("Unknown (%1)").arg(region), region);
m_ui.region->setCurrentIndex(m_ui.region->count() - 1);
}
}

{
QSignalBlocker sb(m_ui.serial);
m_ui.serial->setText(serial);
}

{
QSignalBlocker sb(m_ui.filename);
m_ui.filename->setText(filename);
}
}

void MemoryCardRenameFileDialog::updateFullNameFromSimplifiedFields()
{
const QString region = m_ui.region->currentData().toString();
const QString serial = m_ui.serial->text()
.left(MemoryCardImage::FILE_SERIAL_LENGTH)
.leftJustified(MemoryCardImage::FILE_SERIAL_LENGTH, QChar(' '));
const QString filename = m_ui.filename->text()
.left(MemoryCardImage::FILE_FILENAME_LENGTH)
.leftJustified(MemoryCardImage::FILE_FILENAME_LENGTH, QChar(' '));

const QSignalBlocker sb(m_ui.fullFilename);
m_ui.fullFilename->setText(QStringLiteral("%1%2%3").arg(region).arg(serial).arg(filename));
}
23 changes: 23 additions & 0 deletions src/duckstation-qt/memorycardeditorwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include "ui_memorycardeditorwindow.h"
#include "ui_memorycardrenamefiledialog.h"

#include "core/memory_card_image.h"

Expand Down Expand Up @@ -36,6 +37,7 @@ class MemoryCardEditorWindow : public QWidget
private Q_SLOTS:
void onCardASelectionChanged();
void onCardBSelectionChanged();
void onCardContextMenuRequested(const QPoint& pos);
void doCopyFile();
void doDeleteFile();
void doUndeleteFile();
Expand Down Expand Up @@ -76,6 +78,7 @@ private Q_SLOTS:
void importCard(Card* card);
void formatCard(Card* card);

void doRenameSaveFile();
void doExportSaveFile();
void importSaveFile(Card* card);

Expand All @@ -85,10 +88,30 @@ private Q_SLOTS:
Ui::MemoryCardEditorDialog m_ui;
QPushButton* m_deleteFile;
QPushButton* m_undeleteFile;
QPushButton* m_renameFile;
QPushButton* m_exportFile;
QPushButton* m_moveLeft;
QPushButton* m_moveRight;

Card m_card_a;
Card m_card_b;
};

class MemoryCardRenameFileDialog final : public QDialog
{
Q_OBJECT
public:
MemoryCardRenameFileDialog(QWidget* parent, std::string_view old_name);
~MemoryCardRenameFileDialog() override;

static std::string promptForNewName(QWidget* parent, std::string_view old_name);

private Q_SLOTS:
void updateSimplifiedFieldsFromFullName();
void updateFullNameFromSimplifiedFields();

private:
void setupAdditionalUi();

Ui::MemoryCardRenameFileDialog m_ui;
};
Loading

0 comments on commit b99ee59

Please sign in to comment.