mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-10 06:04:12 +02:00
Add saving and loading part of the settings (discussion #1405)
Save settings as... now asks which parts to save: toolbars, keyboard shortcuts and mouse gestures; defaults for new projects (folio, title block, wires, numbering); everything else. All three ticked writes the same complete file as before (format 1, collection folders included). A file with only some parts is format 2 and lists its parts. Loading it replaces those parts and keeps every other setting. The folders of the collections are never in such a file: they rarely exist on another computer. A QElectroTech that only knows format 1 refuses it rather than loading it as complete and losing every other setting. A file holding only the defaults for new projects loads without closing QElectroTech, since QETProject reads them again for every new project. Which part a key belongs to is ConfigProfile::partOf(); a key it does not list falls in "everything else", so a new setting can never be left out of every part. The test checks that what BorderProperties writes for the folio defaults lands in the new-project part. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+138
-10
@@ -61,14 +61,19 @@
|
||||
#include <iostream>
|
||||
#define QUOTE(x) STRINGIFY(x)
|
||||
#define STRINGIFY(x) #x
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QFontDatabase>
|
||||
#include <QLabel>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QRegularExpression>
|
||||
#include <QSettings>
|
||||
#include <QStyleFactory>
|
||||
#include <QStyleHints>
|
||||
#include <QVBoxLayout>
|
||||
#ifdef BUILD_WITHOUT_KF
|
||||
# include "ui/nokde/kautosavefile.h"
|
||||
#else
|
||||
@@ -2291,6 +2296,88 @@ void QETApp::customizeQET(int tab)
|
||||
dialog.setParent(nullptr, dialog.windowFlags());
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
@brief partLabel
|
||||
@return the name of @a part shown to the user
|
||||
*/
|
||||
QString partLabel(ConfigProfile::Part part)
|
||||
{
|
||||
switch (part) {
|
||||
case ConfigProfile::Part::Controls:
|
||||
return QETApp::tr("Toolbars, keyboard shortcuts and mouse gestures");
|
||||
case ConfigProfile::Part::NewProject:
|
||||
return QETApp::tr("Defaults for new projects: folio, title block, "
|
||||
"wires, numbering");
|
||||
case ConfigProfile::Part::Other:
|
||||
return QETApp::tr("All other settings: appearance, grid, language...");
|
||||
case ConfigProfile::Part::Folders:
|
||||
return QETApp::tr("Folders of the collections");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief choosePartsToSave
|
||||
Ask which parts of the settings to save, all of them ticked to begin
|
||||
with.
|
||||
@return the parts chosen, or an empty list if the user cancelled
|
||||
*/
|
||||
QList<ConfigProfile::Part> choosePartsToSave(QWidget *parent)
|
||||
{
|
||||
QDialog dialog(parent);
|
||||
dialog.setWindowTitle(QETApp::tr("Save settings as...", "dialog title"));
|
||||
|
||||
auto *layout = new QVBoxLayout(&dialog);
|
||||
layout->addWidget(new QLabel(QETApp::tr("Settings to save:"), &dialog));
|
||||
|
||||
QMap<ConfigProfile::Part, QCheckBox *> boxes;
|
||||
for (ConfigProfile::Part part : ConfigProfile::choosableParts()) {
|
||||
auto *box = new QCheckBox(partLabel(part), &dialog);
|
||||
box->setChecked(true);
|
||||
layout->addWidget(box);
|
||||
boxes.insert(part, box);
|
||||
}
|
||||
|
||||
auto *note = new QLabel(QETApp::tr("The folders of your collections are "
|
||||
"saved only when every part is ticked."),
|
||||
&dialog);
|
||||
note->setWordWrap(true);
|
||||
layout->addWidget(note);
|
||||
|
||||
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok
|
||||
| QDialogButtonBox::Cancel,
|
||||
&dialog);
|
||||
layout->addWidget(buttons);
|
||||
QObject::connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
|
||||
QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
|
||||
|
||||
//Nothing ticked, nothing to save
|
||||
auto update_ok = [&boxes, buttons]() {
|
||||
bool any = false;
|
||||
for (QCheckBox *box : std::as_const(boxes)) {
|
||||
any = any || box->isChecked();
|
||||
}
|
||||
buttons->button(QDialogButtonBox::Ok)->setEnabled(any);
|
||||
};
|
||||
for (QCheckBox *box : std::as_const(boxes)) {
|
||||
QObject::connect(box, &QCheckBox::toggled, &dialog, update_ok);
|
||||
}
|
||||
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<ConfigProfile::Part> parts;
|
||||
for (ConfigProfile::Part part : ConfigProfile::choosableParts()) {
|
||||
if (boxes.value(part)->isChecked()) {
|
||||
parts << part;
|
||||
}
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETApp::exportConfiguration
|
||||
Save the settings of QElectroTech to a file the user chooses, to keep
|
||||
@@ -2298,11 +2385,19 @@ void QETApp::customizeQET(int tab)
|
||||
#610). The file is always written in the ini format, whatever the
|
||||
platform stores its live settings in, so a profile saved on Windows
|
||||
loads on Linux and macOS. See ConfigProfile for the keys left out.
|
||||
|
||||
The user first chooses which parts of the settings to save, to share
|
||||
the defaults for new projects alone for example (discussion #1405).
|
||||
*/
|
||||
void QETApp::exportConfiguration()
|
||||
{
|
||||
QWidget *parent_widget = qApp->activeWindow();
|
||||
|
||||
const QList<ConfigProfile::Part> parts = choosePartsToSave(parent_widget);
|
||||
if (parts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QFileDialog::getSaveFileName(
|
||||
parent_widget,
|
||||
tr("Save settings as...", "dialog title"),
|
||||
@@ -2317,7 +2412,7 @@ void QETApp::exportConfiguration()
|
||||
|
||||
QSettings live_settings;
|
||||
QSettings file_settings(path, QSettings::IniFormat);
|
||||
ConfigProfile::exportTo(live_settings, file_settings);
|
||||
ConfigProfile::exportTo(live_settings, file_settings, parts);
|
||||
|
||||
if (file_settings.status() != QSettings::NoError) {
|
||||
QET::QetMessageBox::critical(
|
||||
@@ -2330,7 +2425,10 @@ void QETApp::exportConfiguration()
|
||||
/**
|
||||
@brief QETApp::importConfiguration
|
||||
Replace the settings of QElectroTech with a file saved by
|
||||
exportConfiguration(), then close QElectroTech.
|
||||
exportConfiguration(), then close QElectroTech. A file holding only
|
||||
some parts of the settings replaces only those parts. A file holding
|
||||
only the defaults for new projects needs no restart: they are read
|
||||
again for every new project.
|
||||
|
||||
The settings are read by each part of QElectroTech when it starts, and
|
||||
there is no signal telling all of them that a setting changed, so the
|
||||
@@ -2352,8 +2450,10 @@ void QETApp::importConfiguration()
|
||||
}
|
||||
|
||||
QSettings file_settings(path, QSettings::IniFormat);
|
||||
const QList<ConfigProfile::Part> parts = ConfigProfile::partsOf(file_settings);
|
||||
if (file_settings.status() != QSettings::NoError
|
||||
|| !ConfigProfile::isProfile(file_settings))
|
||||
|| !ConfigProfile::isProfile(file_settings)
|
||||
|| parts.isEmpty())
|
||||
{
|
||||
QET::QetMessageBox::critical(
|
||||
parent_widget,
|
||||
@@ -2362,24 +2462,52 @@ void QETApp::importConfiguration()
|
||||
return;
|
||||
}
|
||||
|
||||
const bool complete = parts.contains(ConfigProfile::Part::Folders);
|
||||
const bool restart = ConfigProfile::needsRestart(parts);
|
||||
QString question;
|
||||
if (complete) {
|
||||
question = tr("These settings will replace your current ones, except the "
|
||||
"window layout and the list of recent files.\n"
|
||||
"\n"
|
||||
"QElectroTech will then close. Start it again to use the new "
|
||||
"settings.\n"
|
||||
"\n"
|
||||
"Do you want to continue?");
|
||||
} else {
|
||||
QStringList labels;
|
||||
for (ConfigProfile::Part part : parts) {
|
||||
labels << QStringLiteral("• ") + partLabel(part);
|
||||
}
|
||||
question = tr("These settings will replace your current ones for:\n"
|
||||
"%1\n"
|
||||
"All your other settings are kept.")
|
||||
.arg(labels.join(QLatin1Char('\n')));
|
||||
question += QStringLiteral("\n\n");
|
||||
question += restart
|
||||
? tr("QElectroTech will then close. Start it again to use the new "
|
||||
"settings.")
|
||||
: tr("Projects already open keep their own defaults.");
|
||||
question += QStringLiteral("\n\n") + tr("Do you want to continue?");
|
||||
}
|
||||
|
||||
//Said before anything closes: once the last window is closed,
|
||||
//QElectroTech quits by itself (checkRemainingWindows()).
|
||||
const auto answer = QET::QetMessageBox::question(
|
||||
parent_widget,
|
||||
tr("Load settings", "message box title"),
|
||||
tr("These settings will replace your current ones, except the "
|
||||
"window layout and the list of recent files.\n"
|
||||
"\n"
|
||||
"QElectroTech will then close. Start it again to use the new "
|
||||
"settings.\n"
|
||||
"\n"
|
||||
"Do you want to continue?"),
|
||||
question,
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!restart) {
|
||||
QSettings live_settings;
|
||||
ConfigProfile::importFrom(file_settings, live_settings);
|
||||
return;
|
||||
}
|
||||
|
||||
//Asks to save any modified project, as quitQET() does. The settings
|
||||
//are only replaced once every editor is closed, so that the editors
|
||||
//saving their own settings on close cannot overwrite them, and a
|
||||
|
||||
Reference in New Issue
Block a user