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