From 93157a579d58afea7f30417f853389b977d8667e Mon Sep 17 00:00:00 2001 From: ispyisail Date: Fri, 23 Oct 2026 18:31:37 +1300 Subject: [PATCH] Move reading and writing a .qetz out of QETProject QETZ-DB-FIRST stage 3a. QETProject::openFile() unzipped and joined a .qetz itself and kept what it held beside the document in three members of its own (the entries, each folio's part, the pictures), and writeContainer() split and zipped it. Both now live in their own classes under sources/container: QetzReader reads a .qet or a .qetz and keeps what a .qetz holds for as long as the project is open; QetzWriter writes the document as a .qetz. No behaviour change: the same calls in the same order. This is the seam stage 3b needs, where opening fills the working database straight from project.sqlite instead of rebuilding it from the joined XML. Co-Authored-By: Claude Opus 5.5 --- cmake/qet_compilation_vars.cmake | 4 +++ sources/container/qetzreader.cpp | 50 +++++++++++++++++++++++++++ sources/container/qetzreader.h | 59 ++++++++++++++++++++++++++++++++ sources/container/qetzwriter.cpp | 39 +++++++++++++++++++++ sources/container/qetzwriter.h | 36 +++++++++++++++++++ sources/qetproject.cpp | 54 ++++++----------------------- sources/qetproject.h | 14 +++----- 7 files changed, 203 insertions(+), 53 deletions(-) create mode 100644 sources/container/qetzreader.cpp create mode 100644 sources/container/qetzreader.h create mode 100644 sources/container/qetzwriter.cpp create mode 100644 sources/container/qetzwriter.h 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