Merge pull request #1543 from ispyisail/fix/save-keeps-qet
Linux build and tests / Build and test (Qt 6, Debug) (push) Failing after 1m54s

Fix Save writing a .qetz beside an opened .qet: keep the .qet
This commit is contained in:
ispyisail
2026-10-19 15:07:41 +13:00
committed by GitHub
6 changed files with 23 additions and 96 deletions
+1 -1
View File
@@ -313,7 +313,7 @@ ProjectLock::Result ProjectLock::acquire(const QString &project_path, bool take_
/**
@brief ProjectLock::moveTo
The project is now @p project_path (Save As, .qet saved as .qetz): take
The project is now @p project_path (Save As): take
the new lock, then let the old one go. Nothing changes when the new one
cannot be had.
@return true when the lock is now on @p project_path
+1 -27
View File
@@ -795,7 +795,7 @@ QETResult ProjectView::writeProject(bool *cancelled)
return(m_project -> write());
}
const QString name = QFileInfo(m_project -> writeTarget()).fileName();
const QString name = QFileInfo(m_project -> filePath()).fileName();
QMessageBox box(QMessageBox::Warning,
tr("File changed on disk", "message box title"),
tr("Someone else changed %1 on disk at %2 since you opened it.\n\n"
@@ -838,32 +838,6 @@ QETResult ProjectView::doSave()
return(saveAs());
}
//A project read from a .qet is saved as a .qetz beside it: say so,
//and ask before replacing a .qetz already there
if (m_project->savesBesideOpenedFile()) {
const QString beside = m_project->besideContainerPath();
if (!refuseIfLockedByOther(beside)) return(QETResult());
const QString message = tr("This project was opened from %1. It is saved as %2, "
"beside it, in the zipped project format; %1 stays as "
"it is.\n\nTo write a file for a version of "
"QElectroTech that cannot open %2, use File > Save a "
"copy for an older QElectroTech.")
.arg(QFileInfo(m_project->filePath()).fileName(), QFileInfo(beside).fileName());
if (QFileInfo::exists(beside)) {
if (QET::QetMessageBox::question(
this, tr("Save as a zipped project", "message box title"),
message + QStringLiteral("\n\n")
+ tr("%1 already exists. Replace it?").arg(QFileInfo(beside).fileName()),
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)
!= QMessageBox::Yes) {
return(QETResult());
}
} else {
QET::QetMessageBox::information(
this, tr("Save as a zipped project", "message box title"), message);
}
}
// write to file
bool cancelled = false;
QETResult result = writeProject(&cancelled);
+6 -46
View File
@@ -197,8 +197,6 @@ QETProject::QETProject(KAutoSaveFile *backup, QObject *parent) :
}
//Set the real path, instead of the path of the backup.
setFilePath(backup->managedFile().path());
//A project read from a .qet is saved as a .qetz beside it
m_opened_from_qet = !isContainerPath(m_file_path);
delete backup;
//Set the project to read only mode if the file it.
@@ -511,8 +509,6 @@ QETProject::ProjectState QETProject::openFile(QFile *file)
//from a .qet only, a .qetz is a zip of parts
if (!content.startsWith("PK\x03\x04"))
m_opened_content = content;
//A project read from a .qet is saved as a .qetz beside it
m_opened_from_qet = !content.startsWith("PK\x03\x04");
QDomDocument xml_project;
// PreserveSpacingOnlyNodes: without it, a text node that is entirely
// whitespace -- e.g. a title-block custom variable deliberately set to
@@ -1201,8 +1197,6 @@ void QETProject::setFilePath(const QString &filepath)
}
m_next_backup_slot = 0;
m_file_path = filepath;
//A path chosen is saved to as it is
m_opened_from_qet = false;
QFileInfo fi(m_file_path);
if (fi.isWritable()) {
@@ -2774,20 +2768,6 @@ bool QETProject::writeContainerFile(const QString &path, QString *error)
kept.isEmpty() ? QHash<QString, QByteArray>() : m_container_files);
}
/**
@brief QETProject::besideContainerPath
@return the .qetz a project read from a .qet is saved to: the same
name, beside it
*/
QString QETProject::besideContainerPath() const
{
QString path = m_file_path;
if (path.endsWith(QStringLiteral(".qet"), Qt::CaseInsensitive)) {
path.chop(4);
}
return path + QStringLiteral(".qetz");
}
/**
@brief QETProject::writeCopyForOlderVersions
Write this project as a .qet at @p path, which a QElectroTech that
@@ -2817,16 +2797,6 @@ void QETProject::rememberDiskState()
m_disk_mtime = info.exists() ? info.lastModified() : QDateTime();
}
/**
@brief QETProject::writeTarget
@return the file write() writes to: the .qetz beside a project read
from a .qet, else the project's own file
*/
QString QETProject::writeTarget() const
{
return m_opened_from_qet ? besideContainerPath() : m_file_path;
}
/**
@brief QETProject::changedOnDisk
@param when set to the modification time of the file now on disk
@@ -2837,7 +2807,7 @@ QString QETProject::writeTarget() const
*/
bool QETProject::changedOnDisk(QDateTime *when) const
{
const QString target = writeTarget();
const QString target = m_file_path;
if (target.isEmpty() || target != m_disk_path || m_disk_size < 0)
return false;
const QFileInfo info(target);
@@ -2898,20 +2868,10 @@ QETResult QETProject::write(bool overwrite_changed)
if (!m_locked_out_path.isEmpty() && m_locked_out_path == m_file_path)
return(tr("%1 was opened read-only because someone else has it open. Use Save As to keep your changes in another file.").arg(m_file_path));
//A project read from a .qet is saved as a .qetz beside it; the
//.qet stays as it was (stage D1). If the .qetz cannot be written
//(a read-only folder), the project stays the .qet it was.
const QString opened_qet = m_opened_from_qet ? m_file_path : QString();
if (m_opened_from_qet) {
setFilePath(besideContainerPath());
}
const auto failed = [this, &opened_qet](const QString &error) {
const QString message = tr("Cannot save %1: %2").arg(m_file_path, error);
if (!opened_qet.isEmpty()) {
setFilePath(opened_qet);
m_opened_from_qet = true;
}
return(QETResult(message));
//Saved in the format it was opened in: a .qet stays a .qet, and
//only Save As changes it (#1512)
const auto failed = [this](const QString &error) {
return(QETResult(tr("Cannot save %1: %2").arg(m_file_path, error)));
};
// If the project was opened read-only, only refuse when the target
@@ -2985,7 +2945,7 @@ QETResult QETProject::write(bool overwrite_changed)
if (isReadOnly())
setReadOnly(false);
//The file may be a new one (Save As, a .qet saved as .qetz): the lock
//The file may be a new one (Save As): the lock
//follows it. A project that had none (new, or opened read-only for
//another's lock) takes one now. Never stops the save: it is done.
if (ProjectLock::mayTake()) {
-8
View File
@@ -337,7 +337,6 @@ class QETProject : public QObject
static QString spliceKeptFolios(QString text, const QHash<int, QByteArray> &kept_folios);
bool close();
QETResult write(bool overwrite_changed = false);
QString writeTarget() const;
void rememberDiskState();
bool changedOnDisk(QDateTime *when = nullptr) const;
bool isReadOnly() const;
@@ -470,11 +469,6 @@ class QETProject : public QObject
public:
static bool isContainerPath(const QString &path);
static QString openFileFilter();
/// Saving writes a .qetz: one read from a .qet is saved as
/// a .qetz beside it, the .qet left as it is
/// (stage D1)
bool savesBesideOpenedFile() const {return m_opened_from_qet;}
QString besideContainerPath() const;
QETResult writeCopyForOlderVersions(const QString &path);
static bool writeContainer(const QDomDocument &project, const QString &path,
QString *error = nullptr,
@@ -571,8 +565,6 @@ class QETProject : public QObject
/// database rows came from it, and nothing project-wide that
/// a folio's saved items depend on has changed since
bool m_kept_folios_trusted = false;
/// Read from a .qet whose path nobody chose since: saved beside it
bool m_opened_from_qet = false;
/// The file the saved date and path were set for, and the undo
/// index then: an unchanged project keeps them (stampSaved())
QString m_stamped_path;
+3 -3
View File
@@ -55,10 +55,10 @@ private slots:
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
const QString qet = m_dir.filePath(QStringLiteral("p.qet"));
QVERIFY(QFile::copy(QStringLiteral(QET_EXAMPLES_DIR "/741.qet"), qet));
// a .qet is saved as a .qetz beside it; that .qetz is the project below
const QString out = run(QStringLiteral("qet.log('R ' + qet.save(''));\n"), qet);
QVERIFY2(out.contains(QStringLiteral("R true")), qPrintable(out));
// the .qet saved as a .qetz is the project below
m_project = m_dir.filePath(QStringLiteral("p.qetz"));
const QString out = run(QStringLiteral("qet.log('R ' + qet.save('%1'));\n").arg(m_project), qet);
QVERIFY2(out.contains(QStringLiteral("R true")), qPrintable(out));
QVERIFY(QFile::exists(m_project));
}
+12 -11
View File
@@ -732,10 +732,11 @@ private slots:
QCOMPARE(rowsOf(edited), rowsOf(reopened));
}
// A project read from a .qet and saved where it is, is saved as a .qetz
// beside it, and the .qet is left as it was (stage D1).
// What the .qetz holds is what a plain save of the .qet holds.
void qetSavedAsQetzBeside()
// A project read from a .qet and saved where it is stays a .qet: Save
// keeps the format it was opened in, only Save As changes it (#1512).
// It used to be saved as a .qetz beside the .qet (stage D1). What the
// .qet holds is what a plain save of it holds.
void qetSavedAsQet()
{
#ifndef QET_HAS_SCRIPTING
QSKIP("needs --run: this QElectroTech is built without Qt Qml");
@@ -752,19 +753,19 @@ private slots:
const QString log = runChecked({QStringLiteral("--run"), script, qet});
QVERIFY2(log.contains(QStringLiteral("SAVED true")), qPrintable(log.right(400)));
QCOMPARE(read(qet), before);
const QString qetz = m_dir.filePath(QStringLiteral("beside.qetz"));
QVERIFY2(read(qetz).startsWith("PK\x03\x04"), "no .qetz beside the .qet");
QVERIFY2(!QFile::exists(m_dir.filePath(QStringLiteral("beside.qetz"))), "a .qetz written beside the .qet");
const QByteArray saved = read(qet);
QVERIFY2(saved.startsWith("<project"), "the .qet is no longer XML");
QVERIFY(saved != before); //written: the saved date and path are new
auto without_save_stamps = [](QByteArray xml) {
static const QRegularExpression stamp(QStringLiteral(
"\\s*<property [^>]*name=\"saved(filename|filepath|filenamedir|date|date-us|date-eu|time)\"[^>]*>[^<]*</property>"));
return QString::fromUtf8(xml).remove(stamp);
};
const QString straight = resave(qet);
const QString back = resave(qetz);
QVERIFY(!straight.isEmpty() && !back.isEmpty());
QCOMPARE(without_save_stamps(read(back)), without_save_stamps(read(straight)));
const QString straight = resave(QStringLiteral(QET_EXAMPLES_DIR "/industrial.qet"));
QVERIFY(!straight.isEmpty());
QCOMPARE(without_save_stamps(saved), without_save_stamps(read(straight)));
}
// A title-block value that is a single space is kept through two saves