HAL
export_project_dialog.cpp
Go to the documentation of this file.
6 
7 #include <QFileInfo>
8 #include <QTemporaryDir>
9 #include <QDir>
10 #include <QDirIterator>
11 #include <QFile>
12 #include <JlCompress.h> // deps/quazip-1.3
13 #include <QJsonObject>
14 #include <QJsonArray>
15 #include <QJsonDocument>
16 
17 namespace hal
18 {
20  : QFileDialog(parent), mStatus(NoExport)
21  {
22  QFileInfo pinfo(QString::fromStdString(ProjectManager::instance()->get_project_directory().get_default_filename()));
23  mProjectName = pinfo.baseName().simplified();
24  mProjectName.replace(" ","");
25  setDefaultSuffix(".zip");
27  setLabelText(QFileDialog::FileType, QString("Compressed archive (*.zip"));
28  setLabelText(QFileDialog::FileName, "Export project " + mProjectName + " to …");
29  selectFile("hal_project_" + mProjectName + ".zip");
30  setWindowTitle("Export project");
31  }
32 
34  {
35  if (selectedFiles().isEmpty())
36  return;
37  QString archiveFilename = selectedFiles().at(0);
38 
39  QString projTargetDir;
40  QTemporaryDir tempDir(QDir::temp().absoluteFilePath("hal_project_export_XXXXXX"));
41  if (!tempDir.isValid() || !QDir().mkpath(projTargetDir = QDir(tempDir.path()).absoluteFilePath(mProjectName))) {
42  log_warning("gui", "Failed to create temporary project export directory '{}'.", projTargetDir.toStdString());
43  mStatus = ErrorCreateTempDir;
44  return;
45  }
46 
47  QString projSourceDir = QString::fromStdString(ProjectManager::instance()->get_project_directory().string());
48  QStringList filesToCopy;
49  QStringList dirsToCreate;
50 
51  QDirIterator it(projSourceDir, QDir::NoDotAndDotDot |
54  QDir::Hidden |
55  QDir::Dirs |
58  while (it.hasNext())
59  {
60  QString relPath = it.next();
61  if (relPath.startsWith(projSourceDir))
62  relPath.remove(0,projSourceDir.size()+1);
63  else
64  log_warning("gui", "Project export found file '{}' not in project folder '{}'.",
65  relPath.toStdString(), projSourceDir.toStdString());
66 
67  if (it.fileInfo().isDir())
68  dirsToCreate.append(relPath);
69  else if (it.fileInfo().isFile())
70  {
71  if (relPath.startsWith("autosave/")) continue;
72  filesToCopy.append(relPath);
73  }
74  else
75  log_warning("gui", "Project export ignored directory entry '{}' with unknown type.", relPath.toStdString());
76  }
77 
78  // Create project subdirectories in export temp folder
79  for (QString dir : dirsToCreate)
80  {
81  QString fullpath = QDir(projTargetDir).absoluteFilePath(dir);
82  if (!QDir().mkpath(fullpath))
83  log_warning("gui", "Failed to create directory '{}' in project export folder.", dir.toStdString());
84  }
85 
86  // Copy files to export temp folder
87  for (const QString& s : filesToCopy)
88  {
89  QString src = QDir(projSourceDir).absoluteFilePath(s);
90  QString dst = QDir(projTargetDir).absoluteFilePath(s);
91  if (!QFile::copy(src,dst))
92  {
93  log_warning("gui", "Failed to copy file '{}' into project export folder.", s.toStdString());
94  if (s == ".project.json" || s.endsWith(".hal") || s.endsWith(".hgl"))
95  {
96  mStatus = ErrorCopy;
97  return;
98  }
99  }
100  }
101 
102  mStatus = ErrorPrepare;
103  // Copy relevant files into export temp folder
104  ProjectJson projFile(QDir(projTargetDir).absoluteFilePath(".project.json"));
105  if (projFile.status() != ProjectJson::Ok) return;
106  QFileInfo glFinfo(projFile.gateLibraryFilename());
107  if (glFinfo.isAbsolute())
108  {
109  if (!QFile::copy(glFinfo.absoluteFilePath(),QDir(projTargetDir).absoluteFilePath(glFinfo.fileName())))
110  log_warning("gui", "Failed to copy file '{}' into project export folder.", glFinfo.absoluteFilePath().toStdString());
111  else
112  projFile.setGateLibraryFilename(glFinfo.fileName());
113  }
114  copyExternalPython(projTargetDir);
115 
116  if (JlCompress::compressDir( archiveFilename, tempDir.path(), true, QDir::Hidden ))
117  mStatus = Ok;
118  else
119  {
120  log_warning("gui", "Failed to compress project to archive '{}'.", archiveFilename.toStdString());
121  mStatus = ErrorCompress;
122  }
123  }
124 
125  void ExportProjectDialog::copyExternalPython(const QString &projTempDir)
126  {
128  QFile ff(pyControlFileName);
129  if (!ff.open(QIODevice::ReadOnly)) return;
130 
131  QJsonParseError err;
132  QJsonDocument doc = QJsonDocument::fromJson(ff.readAll(), &err);
133  if (err.error != QJsonParseError::NoError) return;
134  ff.close();
135 
136  QJsonObject obj = doc.object();
137  if (!obj.contains("tabs") || !obj["tabs"].isArray()) return;
138 
139  int replaceCount = 0;
140  QString pyTempDir(QDir(projTempDir).absoluteFilePath(PythonSerializer::sPythonRelDir));
141 
142  QJsonArray oldTabs = obj["tabs"].toArray();
143  QJsonArray newTabs;
144  int ntabs = oldTabs.size();
145  for (int itab = 0; itab < ntabs; itab++)
146  {
147  QJsonObject tab = oldTabs.at(itab).toObject();
148  if (!tab.contains("filename")) continue;
149  QString absFilename = tab["filename"].toString();
150  if (QFileInfo(absFilename).isAbsolute())
151  {
152  QString relFilename = QFileInfo(absFilename).fileName();
153  QString tmpFilename = QDir(pyTempDir).absoluteFilePath(relFilename);
154  if (QFileInfo(tmpFilename).exists())
155  QFile::remove(tmpFilename);
156  if (QFile::copy(absFilename,tmpFilename))
157  {
158  ++replaceCount;
159  tab.remove("filename");
160  tab.insert("filename", relFilename);
161  }
162  }
163  newTabs.append(tab);
164  }
165 
166  if (replaceCount)
167  {
168  obj.remove("tabs");
169  obj.insert("tabs", newTabs);
170  QFile::remove(pyControlFileName);
171  QFile fo(pyControlFileName);
172  if (!fo.open(QIODevice::WriteOnly)) return;
173  fo.write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
174  }
175  }
176 }
ExportProjectDialog(QWidget *parent=nullptr)
Status status() const
Definition: project_json.h:46
QString gateLibraryFilename() const
Definition: project_json.h:48
void setGateLibraryFilename(const QString &gl)
static ProjectManager * instance()
static std::string sControlFileName
Definition: python_editor.h:87
static QString sPythonRelDir
Definition: python_editor.h:86
#define log_warning(channel,...)
Definition: log.h:76
NoDotAndDotDot
QString absoluteFilePath(const QString &fileName) const const
QDir temp()
QFileInfo fileInfo() const const
bool hasNext() const const
QString next()
bool copy(const QString &newName)
bool remove()
void setAcceptMode(QFileDialog::AcceptMode mode)
void setDefaultSuffix(const QString &suffix)
void selectFile(const QString &filename)
QStringList selectedFiles() const const
void setLabelText(QFileDialog::DialogLabel label, const QString &text)
QString absoluteFilePath() const const
QString baseName() const const
QString fileName() const const
bool isAbsolute() const const
bool isDir() const const
bool isFile() const const
void append(const QJsonValue &value)
QJsonValue at(int i) const const
int size() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
bool contains(const QString &key) const const
QJsonObject::iterator insert(const QString &key, const QJsonValue &value)
void remove(const QString &key)
QJsonObject toObject() const const
void append(const T &value)
const T & at(int i) const const
QString fromStdString(const std::string &str)
QString & remove(int position, int n)
QString & replace(int position, int n, QChar after)
QString simplified() const const
int size() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
std::string toStdString() const const
bool isValid() const const
QString path() const const
void setWindowTitle(const QString &)