From 89c3979957b609fb33ada44f5107f444edb8fcde Mon Sep 17 00:00:00 2001 From: ispyisail Date: Sun, 18 Oct 2026 22:56:22 +1300 Subject: [PATCH] Fix Save writing a .qetz beside an opened .qet: keep the .qet A project opened from a .qet was saved as a .qetz beside it, with a notice, and the .qet left as it was: a project kept in git did not stay a .qet. Save now writes the format the project was opened in; only Save As changes it (scorpio810, discussion #1512). A .qetz is saved as a .qetz as before. The save-beside path goes: the flag set on opening, the .qetz path made from the .qet's, write()'s switch to it and back on failure, and the notice and replace question in ProjectView::doSave(). tst_resaveunchanged::qetSavedAsQetzBeside becomes qetSavedAsQet: Save of an opened .qet writes no .qetz and the .qet holds what a plain save of it holds (it fails without the fix: a .qetz is written beside). tst_changedondisk makes its .qetz with Save As, as Save no longer does. Co-Authored-By: Claude Opus 5.5 --- sources/projectlock.cpp | 2 +- sources/projectview.cpp | 28 +-------------- sources/qetproject.cpp | 52 ++++------------------------ sources/qetproject.h | 8 ----- tests/qttest/tst_changedondisk.cpp | 6 ++-- tests/qttest/tst_resaveunchanged.cpp | 23 ++++++------ 6 files changed, 23 insertions(+), 96 deletions(-) diff --git a/sources/projectlock.cpp b/sources/projectlock.cpp index f3ab47a07..bcfc06c91 100644 --- a/sources/projectlock.cpp +++ b/sources/projectlock.cpp @@ -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 diff --git a/sources/projectview.cpp b/sources/projectview.cpp index 4764879f1..a206082c8 100644 --- a/sources/projectview.cpp +++ b/sources/projectview.cpp @@ -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); diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index 9b5510f3c..9a196d820 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -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. @@ -506,8 +504,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 @@ -1176,8 +1172,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()) { @@ -2750,20 +2744,6 @@ bool QETProject::writeContainerFile(const QString &path, QString *error) kept.isEmpty() ? QHash() : 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 @@ -2793,16 +2773,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 @@ -2813,7 +2783,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); @@ -2874,20 +2844,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 @@ -2961,7 +2921,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()) { diff --git a/sources/qetproject.h b/sources/qetproject.h index 4b3dc7986..9d9eca3fe 100644 --- a/sources/qetproject.h +++ b/sources/qetproject.h @@ -338,7 +338,6 @@ class QETProject : public QObject static QString spliceKeptFolios(QString text, const QHash &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; diff --git a/tests/qttest/tst_changedondisk.cpp b/tests/qttest/tst_changedondisk.cpp index 7b18aac68..d0b31c6d4 100644 --- a/tests/qttest/tst_changedondisk.cpp +++ b/tests/qttest/tst_changedondisk.cpp @@ -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)); } diff --git a/tests/qttest/tst_resaveunchanged.cpp b/tests/qttest/tst_resaveunchanged.cpp index 5fdeff080..955a36493 100644 --- a/tests/qttest/tst_resaveunchanged.cpp +++ b/tests/qttest/tst_resaveunchanged.cpp @@ -600,10 +600,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"); @@ -620,19 +621,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("]*name=\"saved(filename|filepath|filenamedir|date|date-us|date-eu|time)\"[^>]*>[^<]*")); 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