diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake
index 44a3186cb..fb22d5a8d 100644
--- a/cmake/qet_compilation_vars.cmake
+++ b/cmake/qet_compilation_vars.cmake
@@ -173,6 +173,10 @@ set(QET_SRC_FILES
${QET_DIR}/sources/container/qetcontainer.h
${QET_DIR}/sources/container/qetzip.cpp
${QET_DIR}/sources/container/qetzip.h
+ ${QET_DIR}/sources/container/qetzreader.cpp
+ ${QET_DIR}/sources/container/qetzreader.h
+ ${QET_DIR}/sources/container/qetzwriter.cpp
+ ${QET_DIR}/sources/container/qetzwriter.h
${QET_DIR}/sources/borderproperties.cpp
${QET_DIR}/sources/borderproperties.h
${QET_DIR}/sources/bordertitleblock.cpp
diff --git a/sources/container/qetzreader.cpp b/sources/container/qetzreader.cpp
new file mode 100644
index 000000000..fba67b7ab
--- /dev/null
+++ b/sources/container/qetzreader.cpp
@@ -0,0 +1,50 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#include "qetzreader.h"
+
+#include "qetzip.h"
+
+/**
+ @brief QetzReader::read
+ Read @p content, the bytes of a .qet or a .qetz, into @p document.
+ For a .qetz, its entries, folio parts, pictures and format are kept.
+ @return false, with @p error set, if it cannot be read
+*/
+bool QetzReader::read(const QByteArray &content, QDomDocument *document, QString *error)
+{
+ m_files.clear();
+ m_folio_parts.clear();
+ m_format = 0;
+ bool read = false;
+ if (QetContainer::isZip(content)) {
+ QList entries;
+ read = QetZip::fromBytes(content, &entries, error)
+ && QetContainer::join(entries, document, error, &m_folio_parts,
+ &m_pictures, &m_format);
+ if (read)
+ for (const QetZip::Entry &entry : std::as_const(entries))
+ m_files.insert(entry.name, entry.data);
+ } else {
+ read = QetContainer::readDocument(content, document, error);
+ }
+ if (!read) {
+ m_pictures.clear();
+ m_folio_parts.clear();
+ }
+ return read;
+}
diff --git a/sources/container/qetzreader.h b/sources/container/qetzreader.h
new file mode 100644
index 000000000..2b636001f
--- /dev/null
+++ b/sources/container/qetzreader.h
@@ -0,0 +1,59 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#ifndef QETZREADER_H
+#define QETZREADER_H
+
+#include "qetcontainer.h"
+
+#include
+#include
+#include
+#include
+#include
+
+/**
+ @brief The QetzReader class
+ Reads the bytes of a project file, a .qet or a .qetz, into its project
+ document, and keeps what a .qetz holds beside that document for as long
+ as the project is open: its entries (a save copies the parts of the
+ folios not built from them), the part each folio was read from, its
+ pictures and its format.
+*/
+class QetzReader
+{
+ public:
+ bool read(const QByteArray &content, QDomDocument *document, QString *error = nullptr);
+
+ /// The format the .qetz says it is; 0 for a .qet
+ int format() const {return m_format;}
+ /// The entries of the .qetz read, by name
+ const QHash &files() const {return m_files;}
+ QByteArray file(const QString &name) const {return m_files.value(name);}
+ /// The part each folio was read from, in order
+ const QStringList &folioParts() const {return m_folio_parts;}
+ /// The picture in @p file, or an empty array when there is none
+ QByteArray picture(const QString &file) const {return m_pictures.value(file);}
+
+ private:
+ QHash m_files;
+ QStringList m_folio_parts;
+ QetContainer::Pictures m_pictures;
+ int m_format = 0;
+};
+
+#endif // QETZREADER_H
diff --git a/sources/container/qetzwriter.cpp b/sources/container/qetzwriter.cpp
new file mode 100644
index 000000000..7191c8805
--- /dev/null
+++ b/sources/container/qetzwriter.cpp
@@ -0,0 +1,39 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#include "qetzwriter.h"
+
+#include "../qetversion.h"
+#include "qetcontainer.h"
+#include "qetzip.h"
+
+/**
+ @brief QetzWriter::write
+ Write the project document @p project to @p path as a .qetz: the same
+ document, in parts, zipped (#1440). The file is replaced only once
+ the whole archive is written. @p kept_from: the entries of the .qetz
+ read, whose parts the folios not built are copied from.
+*/
+bool QetzWriter::write(const QDomDocument &project, const QString &path, QString *error,
+ const QHash &kept_from)
+{
+ QList entries = QetContainer::split(
+ project,
+ QStringLiteral("QElectroTech %1").arg(QetVersion::currentVersion().toString()),
+ error, kept_from);
+ return !entries.isEmpty() && QetZip::write(path, entries, error);
+}
diff --git a/sources/container/qetzwriter.h b/sources/container/qetzwriter.h
new file mode 100644
index 000000000..02f6b6fa9
--- /dev/null
+++ b/sources/container/qetzwriter.h
@@ -0,0 +1,36 @@
+/*
+ Copyright 2006-2026 The QElectroTech Team
+ This file is part of QElectroTech.
+
+ QElectroTech is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ QElectroTech is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with QElectroTech. If not, see .
+*/
+#ifndef QETZWRITER_H
+#define QETZWRITER_H
+
+#include
+#include
+#include
+#include
+
+/**
+ @brief The QetzWriter namespace
+ Writes a project document as a .qetz file.
+*/
+namespace QetzWriter
+{
+ bool write(const QDomDocument &project, const QString &path, QString *error = nullptr,
+ const QHash &kept_from = QHash());
+}
+
+#endif // QETZWRITER_H
diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp
index a199a4675..742ff9494 100644
--- a/sources/qetproject.cpp
+++ b/sources/qetproject.cpp
@@ -19,6 +19,7 @@
#include "qetproject.h"
#include "container/qetcontainer.h"
#include "container/qetcontainerdb.h"
+#include "container/qetzwriter.h"
#include "ElementsCollection/xmlelementcollection.h"
#include "autoNum/assignvariables.h"
@@ -294,7 +295,7 @@ QUuid QETProject::uuid() const
*/
QByteArray QETProject::containerPicture(const QString &file) const
{
- return m_container_pictures.value(file);
+ return m_container.picture(file);
}
/**
@@ -528,27 +529,10 @@ QETProject::ProjectState QETProject::openFile(QFile *file)
QString read_error;
//A .qetz: its entries are kept, for a save to copy the parts of the
//folios not built (stage D4)
- m_container_files.clear();
- m_folio_parts.clear();
- bool read = false;
- int container_format = 0;
- if (QetContainer::isZip(content)) {
- QList entries;
- read = QetZip::fromBytes(content, &entries, &read_error)
- && QetContainer::join(entries, &xml_project, &read_error, &m_folio_parts,
- &m_container_pictures, &container_format);
- if (read)
- for (const QetZip::Entry &entry : std::as_const(entries))
- m_container_files.insert(entry.name, entry.data);
- } else {
- read = QetContainer::readDocument(content, &xml_project, &read_error);
- }
- if (!read)
+ if (!m_container.read(content, &xml_project, &read_error))
{
qWarning().noquote() << "Unable to read" << fi.fileName() << ":" << read_error;
m_open_error = read_error;
- m_container_pictures.clear();
- m_folio_parts.clear();
if(opened_here) {
file->close();
}
@@ -581,13 +565,13 @@ QETProject::ProjectState QETProject::openFile(QFile *file)
//Written by a later QElectroTech that lets this one read it: saving
//here would drop what this version does not know
- m_from_later_version = container_format > QetContainer::Format;
+ m_from_later_version = m_container.format() > QetContainer::Format;
if (!fi.isWritable() || m_from_later_version) {
setReadOnly(true);
}
if (m_from_later_version) {
qInfo().noquote() << QStringLiteral("Project %1 is of .qetz format %2 (this version writes %3): opened read-only")
- .arg(fi.fileName()).arg(container_format).arg(QetContainer::Format);
+ .arg(fi.fileName()).arg(m_container.format()).arg(QetContainer::Format);
}
if(opened_here) {
file->close();
@@ -2757,22 +2741,6 @@ QString QETProject::openFileFilter()
return tr("QElectroTech project (*.qet *.qetz)");
}
-/**
- @brief QETProject::writeContainer
- Write the project document @p project to @p path as a .qetz: the same
- document, in parts, zipped (#1440). The file is replaced only once
- the whole archive is written.
-*/
-bool QETProject::writeContainer(const QDomDocument &project, const QString &path, QString *error,
- const QHash &kept_from)
-{
- QList entries = QetContainer::split(
- project,
- QStringLiteral("QElectroTech %1").arg(QetVersion::currentVersion().toString()),
- error, kept_from);
- return !entries.isEmpty() && QetZip::write(path, entries, error);
-}
-
/**
@brief QETProject::writeContainerFile
Write this project to @p path as a .qetz
@@ -2783,8 +2751,8 @@ bool QETProject::writeContainerFile(const QString &path, QString *error)
//what building them would write (toXml()); every other folio built
QStringList kept;
const QDomDocument document = toXml(nullptr, &kept);
- return writeContainer(document, path, error,
- kept.isEmpty() ? QHash() : m_container_files);
+ return QetzWriter::write(document, path, error,
+ kept.isEmpty() ? QHash() : m_container.files());
}
/**
@@ -3651,10 +3619,10 @@ void QETProject::readProjectXml(QDomDocument &xml_project)
for (Diagram *diagram : std::as_const(m_diagrams_list))
if (!diagram->isLoaded() && diagram->keptText().isEmpty())
m_kept_folios_trusted = false;
- m_kept_parts_trusted = !m_folio_parts.isEmpty() && unloadedFolioCount() > 0
+ m_kept_parts_trusted = !m_container.folioParts().isEmpty() && unloadedFolioCount() > 0
&& m_project_qet_version == QetVersion::currentVersion()
&& m_data_base.rowsFromDocument()
- && QetContainerDb::currentFormat(m_container_files.value(QStringLiteral("project.sqlite")));
+ && QetContainerDb::currentFormat(m_container.file(QStringLiteral("project.sqlite")));
for (Diagram *diagram : std::as_const(m_diagrams_list))
if (!diagram->isLoaded() && diagram->keptPart().isEmpty())
m_kept_parts_trusted = false;
@@ -3823,8 +3791,8 @@ void QETProject::readDiagramsXml(QDomDocument &xml_project)
m_deferring_folios = false;
if (!diagram->isLoaded() && !line_starts.isEmpty())
diagram->setKeptText(folio_text(diagram_xml_element));
- if (!diagram->isLoaded() && m_folio_parts.size() == diagram_nodes.length())
- diagram->setKeptPart(m_folio_parts.at(i));
+ if (!diagram->isLoaded() && m_container.folioParts().size() == diagram_nodes.length())
+ diagram->setKeptPart(m_container.folioParts().at(i));
m_data_base.placeFolio(diagram);
if(dlgWaiting)
dlgWaiting->setDetail(diagram->title());
diff --git a/sources/qetproject.h b/sources/qetproject.h
index 049b91b5f..8e4e17eb6 100644
--- a/sources/qetproject.h
+++ b/sources/qetproject.h
@@ -34,6 +34,7 @@
#include "diagram.h"
#include "autoNum/assignvariables.h"
#include "container/qetcontainerdb.h"
+#include "container/qetzreader.h"
#include "projectlock.h"
#ifdef BUILD_WITHOUT_KF
# include "ui/nokde/kautosavefile.h"
@@ -472,9 +473,6 @@ class QETProject : public QObject
static bool isContainerPath(const QString &path);
static QString openFileFilter();
QETResult writeCopyForOlderVersions(const QString &path);
- static bool writeContainer(const QDomDocument &project, const QString &path,
- QString *error = nullptr,
- const QHash &kept_from = QHash());
bool writeContainerFile(const QString &path, QString *error = nullptr);
private:
static QUuid derivedUuid(const QByteArray &content);
@@ -486,9 +484,9 @@ class QETProject : public QObject
static bool m_backup_enabled;
/// Something changed since the last backup, see writeBackup()
bool m_backup_needed = true;
- /// The pictures of the .qetz this project was opened from, see
- /// containerPicture()
- QHash m_container_pictures;
+ /// What the file this project was opened from holds beside its
+ /// document: a .qetz's entries, folio parts and pictures
+ QetzReader m_container;
/// File path this project is saved to
QString m_file_path;
/// Current state of the project
@@ -562,10 +560,6 @@ class QETProject : public QObject
/// Saving a .qetz may copy the part of a folio not built
/// (toXml(), stage D4)
bool m_kept_parts_trusted = false;
- /// The entries of the .qetz opened, and the part each folio
- /// was read from, in order
- QHash m_container_files;
- QStringList m_folio_parts;
bool m_kept_folios_stale = false;
/// Something was edited since the project was opened: a table
/// filled from the database may have changed