Save as .qetz by default; a .qet opened is saved beside it (stage D1)

The zipped project (.qetz) is the format QElectroTech saves.

- Save As and the first Save of a new project offer .qetz first
  ("untitled.qetz"); .qet can still be chosen.
- A project read from a .qet is saved as a .qetz beside it, the .qet left
  as it is (QETProject::write(), so Save, autosave, a script's save and the
  assistant's live save all do it). The project then works on the .qetz.
  A path chosen afterwards (Save As, a script naming one) is saved to as it
  is. The editor says so before the first such save, and asks before
  replacing a .qetz already there.
- File > Save a copy for an older QElectroTech writes a .qet copy, for a
  version that cannot open a .qetz; the project keeps its own file.
- The operating systems know the file: a freedesktop MIME type
  application/x-qelectrotech-project+zip (*.qetz, and by its mimetype
  entry when the name says nothing), the desktop file, macOS's document
  types and the Windows installer's association.

Crash backups stay XML, written beside the project as before.

tst_resaveunchanged::qetSavedAsQetzBeside: a .qet saved where it is leaves
the .qet byte for byte and writes a .qetz beside it holding what a plain
save of the .qet holds; it fails when the switch is off. In a real window:
Ctrl+S on perceuse.qet shows the notice, writes perceuse.qetz and leaves
perceuse.qet's md5 unchanged; the copy for an older version (reached by
command search) equals a plain save but for time_spent; a new project's
Save offers untitled.qetz. xdg-mime finds the type by name and by content.
ctest 96/96 with and without QET_LAZY_FOLIOS=1; srclint R-C clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-11 17:09:31 +13:00
parent 00e200b504
commit ecc7a39131
11 changed files with 175 additions and 5 deletions
+55 -2
View File
@@ -341,13 +341,15 @@ QString ProjectView::askUserForFilePath(bool assign) {
// ask the user for a filepath in order to save the project
const QString qet_filter = tr("Project QElectroTech (*.qet)", "filetypes allowed when saving a project file");
const QString qetz_filter = tr("Zipped project QElectroTech (*.qetz)", "filetypes allowed when saving a project file");
QString selected_filter = QETProject::isContainerPath(m_project->filePath()) ? qetz_filter : qet_filter;
//A project is saved as a .qetz unless a .qet is chosen
//(stage D1)
QString selected_filter = qetz_filter;
QString filepath = QFileDialog::getSaveFileName(
this,
tr("Save as", "dialog title"),
m_project -> currentDir() + "/" + tr("untitled")
+ (selected_filter == qetz_filter ? ".qetz" : ".qet"),
qet_filter + ";;" + qetz_filter,
qetz_filter + ";;" + qet_filter,
&selected_filter
);
@@ -725,6 +727,32 @@ QETResult ProjectView::saveAs()
return(doSave());
}
/**
@brief ProjectView::saveCopyForOlderVersions
Ask for a .qet path and write the project there, for a version of
QElectroTech that cannot open a .qetz. The project keeps its file.
@return a QETResult object reflecting the situation; a valid one if
the operation was cancelled
*/
QETResult ProjectView::saveCopyForOlderVersions()
{
if (!m_project) return(noProjectResult());
QString base = m_project->filePath().isEmpty()
? m_project->currentDir() + "/" + tr("untitled")
: m_project->filePath();
for (const QString &suffix : {QStringLiteral(".qetz"), QStringLiteral(".qet")})
if (base.endsWith(suffix, Qt::CaseInsensitive)) base.chop(suffix.size());
QString path = QFileDialog::getSaveFileName(
this,
tr("Save a copy for an older QElectroTech", "dialog title"),
base + ".qet",
tr("Project QElectroTech (*.qet)", "filetypes allowed when saving a project file"));
if (path.isEmpty()) return(QETResult());
if (!path.endsWith(".qet", Qt::CaseInsensitive)) path += ".qet";
return(m_project->writeCopyForOlderVersions(path));
}
/**
Save project content, then write the project file. May
call saveAs if no filepath was provided before.
@@ -741,6 +769,31 @@ 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();
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
QETResult result = m_project -> write();
updateWindowTitle();