Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve UX in controller mapping editing workflow #3278

Merged
merged 1 commit into from
Nov 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 111 additions & 21 deletions src/controllers/dlgprefcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <QDesktopServices>
#include <QFileDialog>
#include <QFileInfo>
#include <QInputDialog>
#include <QStandardPaths>
#include <QTableWidget>
#include <QTableWidgetItem>
Expand All @@ -23,11 +24,15 @@
#include "preferences/usersettings.h"
#include "util/version.h"

DlgPrefController::DlgPrefController(QWidget* parent, Controller* controller,
ControllerManager* controllerManager,
UserSettingsPointer pConfig)
const QString kPresetExt(".midi.xml");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, this should go into an anonymous namespace.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


DlgPrefController::DlgPrefController(QWidget* parent,
Controller* controller,
ControllerManager* controllerManager,
UserSettingsPointer pConfig)
: DlgPreferencePage(parent),
m_pConfig(pConfig),
m_pUserDir(userPresetsPath(pConfig)),
m_pControllerManager(controllerManager),
m_pController(controller),
m_pDlgControllerLearning(NULL),
Expand Down Expand Up @@ -491,33 +496,118 @@ void DlgPrefController::savePreset() {
}

if (!m_pPreset->isDirty()) {
qDebug() << "Preset is not dirty, no need to save it.";
qDebug() << "Preset has not been edited, no need to save it.";
return;
}

QFileInfo fileInfo(m_pPreset->filePath());
QString fileName = fileInfo.fileName();

// Add " (edited)" to preset name (if it's not already present)
QString editedSuffix = QStringLiteral(" (") + tr("edited") + QStringLiteral(")");
if (!m_pPreset->name().endsWith(editedSuffix)) {
m_pPreset->setName(m_pPreset->name() + editedSuffix);
qDebug() << "Renamed preset to " << m_pPreset->name();
QString oldFilePath = m_pPreset->filePath();
QString newFilePath;
QFileInfo fileInfo(oldFilePath);
QString presetName = m_pPreset->name();

bool isUserPreset = fileInfo.absoluteDir().absolutePath().append("/") == m_pUserDir;
bool saveAsNew = true;
if (m_pOverwritePresets.contains(oldFilePath) &&
m_pOverwritePresets.value(oldFilePath) == true) {
saveAsNew = false;
}

// If this is a user preset, ask whether to overwrite or save with new name.
// Optionally, tick checkbox to always overwrite this preset in the current session.
if (isUserPreset && saveAsNew) {
QString overwriteTitle = tr("Preset already exists.");
QString overwriteLabel = tr(
"<b>%1</b> already exists in user preset folder.<br>"
"Overwrite or save with a new name?");
QString overwriteCheckLabel = tr("Always overwrite during this session");

QMessageBox overwriteMsgBox;
overwriteMsgBox.setIcon(QMessageBox::Question);
overwriteMsgBox.setWindowTitle(overwriteTitle);
overwriteMsgBox.setText(overwriteLabel.arg(presetName));
QCheckBox overwriteCheckBox;
overwriteCheckBox.setText(overwriteCheckLabel);
overwriteCheckBox.blockSignals(true);
overwriteCheckBox.setCheckState(Qt::Unchecked);
overwriteMsgBox.addButton(&overwriteCheckBox, QMessageBox::ActionRole);
QPushButton* pSaveAsNew = overwriteMsgBox.addButton(
tr("Save As"), QMessageBox::AcceptRole);
QPushButton* pOverwrite = overwriteMsgBox.addButton(
tr("Overwrite"), QMessageBox::AcceptRole);
overwriteMsgBox.setDefaultButton(pSaveAsNew);
overwriteMsgBox.exec();

if (overwriteMsgBox.clickedButton() == pOverwrite) {
saveAsNew = false;
if (overwriteCheckBox.checkState() == Qt::Checked) {
m_pOverwritePresets.insert(m_pPreset->filePath(), true);
}
} else if (overwriteMsgBox.close()) {
return;
}
}

// Add " (edited)" to file name (if it's not already present)
QString baseName = fileInfo.baseName();
if (baseName.endsWith(editedSuffix)) {
baseName.chop(editedSuffix.size());
// Ask for a preset name when
// * initially saving a modified Mixxx preset to the user folder
// * saving a user preset with a new name.
// The name will be used as display name and file name.
if (!saveAsNew) {
newFilePath = oldFilePath;
} else {
QString savePresetTitle = tr("Save user preset");
QString savePresetLabel = tr("Enter the name for saving the preset to the user folder.");
QString savingFailedTitle = tr("Saving preset failed");
QString invalidNameLabel =
tr("A preset cannot have a blank name and may not contain "
"special characters.");
QString fileExistsLabel = tr("A preset file with that name already exists.");
// Only allow the name to contain letters, numbers, whitespaces and _-+()/
const QRegExp rxRemove = QRegExp("[^[(a-zA-Z0-9\\_\\-\\+\\(\\)\\/|\\s]");

// Choose a new file (base) name
bool validPresetName = false;
while (!validPresetName) {
QString userDir = m_pUserDir;
bool ok = false;
presetName = QInputDialog::getText(nullptr,
savePresetTitle,
savePresetLabel,
QLineEdit::Normal,
presetName,
&ok)
.remove(rxRemove)
.trimmed();
if (!ok) {
return;
}
if (presetName.isEmpty()) {
QMessageBox::warning(nullptr,
savingFailedTitle,
invalidNameLabel);
continue;
}
// While / is allowed for the display name we can't use it for the file name.
QString fileName = presetName.replace(QString("/"), QString("-"));
newFilePath = userDir + fileName + kPresetExt;
if (QFile::exists(newFilePath)) {
QMessageBox::warning(nullptr,
savingFailedTitle,
fileExistsLabel);
continue;
}
validPresetName = true;
}
fileName = baseName + editedSuffix + QStringLiteral(".") + fileInfo.completeSuffix();
m_pPreset->setName(presetName);
qDebug() << "Preset renamed to" << m_pPreset->name();
}
QString filePath = QDir(userPresetsPath(m_pConfig)).absoluteFilePath(fileName);

if (!m_pPreset->savePreset(filePath)) {
qDebug() << "Failed to save preset!";
if (!m_pPreset->savePreset(newFilePath)) {
qDebug() << "Failed to save preset as" << newFilePath;
return;
}
qDebug() << "Preset saved as" << newFilePath;

m_pPreset->setFilePath(filePath);
m_pPreset->setFilePath(newFilePath);
m_pPreset->setDirty(false);

enumeratePresets(m_pPreset->filePath());
Expand Down
2 changes: 2 additions & 0 deletions src/controllers/dlgprefcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ class DlgPrefController : public DlgPreferencePage {

Ui::DlgPrefControllerDlg m_ui;
UserSettingsPointer m_pConfig;
const QString m_pUserDir;
ControllerManager* m_pControllerManager;
Controller* m_pController;
DlgControllerLearning* m_pDlgControllerLearning;
ControllerPresetPointer m_pPreset;
QMap<QString, bool> m_pOverwritePresets;
ControllerInputMappingTableModel* m_pInputTableModel;
QSortFilterProxyModel* m_pInputProxyModel;
ControllerOutputMappingTableModel* m_pOutputTableModel;
Expand Down