HAL
import_project_dialog.cpp
Go to the documentation of this file.
5 #include <QRegularExpression>
6 #include <QGridLayout>
7 #include <QVBoxLayout>
8 #include <QFileDialog>
9 #include <QLabel>
10 #include <QUuid>
11 #include <QMessageBox>
12 #include <JlCompress.h>
13 
14 namespace hal {
15  FileSelectWidget::FileSelectWidget(const QString &defaultEntry, bool existingDir, QWidget* parent)
16  : QFrame(parent), mExistingDir(existingDir), mValid(false)
17  {
18  setMaximumHeight(48);
20  setLineWidth(3);
22 
23  QGridLayout* layout = new QGridLayout(this);
24  mEditor = new QLineEdit(this);
26  mEditor->setMinimumWidth(320);
27  mEditor->setText(defaultEntry);
28  connect(mEditor,&QLineEdit::textChanged,this,&FileSelectWidget::handleTextChanged);
29  layout->addWidget(mEditor,0,0);
30 
31  mButton = new QPushButton(gui_utility::getStyledSvgIcon("all->#3192C5",":/icons/folder"),"",this);
33  connect(mButton,&QPushButton::clicked,this,&FileSelectWidget::handleActivateFileDialog);
34  layout->addWidget(mButton,0,1);
35  handleTextChanged(defaultEntry);
36  }
37 
38  void FileSelectWidget::handleActivateFileDialog()
39  {
40  QString entry = mExistingDir
41  ? QFileDialog::getExistingDirectory(this, "Select directory:", mEditor->text())
42  : QFileDialog::getOpenFileName(this, "Select file:", mEditor->text(), "Zipped files (*.zip)");
43  mEditor->setText(entry);
44  }
45 
46  void FileSelectWidget::handleTextChanged(const QString &txt)
47  {
48  bool val = !txt.isEmpty();
49  if (val) val = QFileInfo(txt).exists();
50  if (val == mValid) return;
51  mValid = val;
53  }
54 
56  {
57  mEditor->setText(entry);
58  mEditor->setReadOnly(true);
59  mButton->setDisabled(true);
60  mButton->hide();
61  }
62 
64  : QDialog(parent), mStatus(NoImport)
65  {
66  setWindowTitle("Import Project");
67  QVBoxLayout* layout = new QVBoxLayout(this);
68  layout->addWidget(new QLabel("Zipped HAL project file:",this));
69  mZippedFile = new FileSelectWidget("", false, this);
70  connect(mZippedFile,&FileSelectWidget::selectionStatusChanged, this, &ImportProjectDialog::handleSelectionStatusChanged);
71  layout->addWidget(mZippedFile);
72 
73  layout->addStretch();
74 
75  layout->addWidget(new QLabel("Decompress in directory:",this));
76  mTargetDirectory = new FileSelectWidget(QDir::currentPath(), true, this);
77  connect(mTargetDirectory,&FileSelectWidget::selectionStatusChanged, this, &ImportProjectDialog::handleSelectionStatusChanged);
78  layout->addWidget(mTargetDirectory);
79 
80  layout->addStretch();
81 
82  layout->addWidget(new QLabel("Decompressed HAL project name:"));
83  mExtractProjectEdit = new QLineEdit(this);
84  layout->addWidget(mExtractProjectEdit);
85 
86  layout->addStretch();
87 
91  layout->addWidget(mButtonBox);
92  handleSelectionStatusChanged();
93  }
94 
96  {
97  mZippedFile->setFixedEntry(filename);
98  handleSelectionStatusChanged();
99  disconnect(mZippedFile,&FileSelectWidget::selectionStatusChanged, this, &ImportProjectDialog::handleSelectionStatusChanged);
100  }
101 
102  void ImportProjectDialog::handleSelectionStatusChanged()
103  {
104  bool valid = mTargetDirectory->valid() && mZippedFile->valid();
105  if (valid)
106  {
107  QRegularExpression reProj("(.*)/\\.project.json$");
108  QStringList zipFlist = JlCompress::getFileList(mZippedFile->selection());
109  int inx = zipFlist.indexOf(reProj);
110  if (inx < 0)
111  {
112  QMessageBox::warning(this,"Invalid ZIP-file", "The zipped file " + mZippedFile->selection() + " does not seem to be a hal project.\n");
113  valid = false;
114  }
115  else
116  {
117  QRegularExpressionMatch match = reProj.match(zipFlist.at(inx));
118  mTargetProjectName = match.captured(1);
119  QString suggestedDir = suggestedProjectDir(QDir(mTargetDirectory->selection()).absoluteFilePath(mTargetProjectName));
120  mExtractProjectEdit->setText(QFileInfo(suggestedDir).fileName());
121  }
122  }
123  mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
124  }
125 
127  {
128  mStatus = NoFileSelected;
129  if (mZippedFile->selection().isEmpty()) return false;
130 
131  mExtractedProjectAbsolutePath = QDir(mTargetDirectory->selection()).absoluteFilePath(mExtractProjectEdit->text());
132  if (QFileInfo(mExtractedProjectAbsolutePath).exists())
133  {
134  QMessageBox::warning(this, "Cannot Create Project", "Direcotry " + mExtractedProjectAbsolutePath + " already exists");
135  return false;
136  }
137  QStringList extracted;
138  QString tempdir;
139  if (mExtractProjectEdit->text() != mTargetProjectName)
140  {
141  tempdir = QDir(mTargetDirectory->selection()).absoluteFilePath("hal_temp_" + QUuid::createUuid().toString(QUuid::WithoutBraces));
142  QDir().mkpath(tempdir);
143  extracted = JlCompress::extractDir(mZippedFile->selection(),tempdir);
144  }
145  else
146  {
147  extracted = JlCompress::extractDir(mZippedFile->selection(),mTargetDirectory->selection());
148  }
149  mStatus = ErrorDecompress;
150  if (extracted.isEmpty()) return false;
151 
152  QString extractedDir = extracted.at(0);
153  while (extractedDir.back().unicode() == '/' || extractedDir.back().unicode() == '\\')
154  extractedDir.chop(1);
155 
156  switch (FileManager::directoryStatus(extractedDir))
157  {
159  mStatus = Ok;
160  break;
162  log_warning("gui", "No hal project file found in '{}' archive.", mZippedFile->selection().toStdString());
163  mStatus = NotAHalProject;
164  break;
165  case FileManager::IsFile:
166  log_warning("gui", "Archive '{}' contains single file, not a hal project directory.", mZippedFile->selection().toStdString());
167  mStatus = NotAHalProject;
168  break;
170  log_warning("gui", "Failed to decompress archive '{}', extracted HAL directory not found.", mZippedFile->selection().toStdString());
171  mStatus = NotAHalProject;
172  break;
174  log_warning("gui", "HAL directory extracted from archive '{}' must not have an extension.", mZippedFile->selection().toStdString());
175  mStatus = NotAHalProject;
176  break;
178  log_warning("gui", "Failed to parse HAL project file from archive '{}'.", mZippedFile->selection().toStdString());
179  mStatus = NotAHalProject;
180  break;
182  log_warning("gui", "Failed to find netlist in archiv '{}'.", mZippedFile->selection().toStdString());
183  mStatus = NotAHalProject;
184  break;
186  log_warning("gui", "Failed to find gate library in archiv '{}'.", mZippedFile->selection().toStdString());
187  mStatus = NotAHalProject;
188  break;
190  log_warning("gui", "Failed to decompress archive '{}', result is neither a file nor a directory.", mZippedFile->selection().toStdString());
191  mStatus = NotAHalProject;
192  break;
193  }
194 
195  if (!tempdir.isEmpty())
196  {
197  if (!QFile::rename(extractedDir,mExtractedProjectAbsolutePath))
198  {
199  log_warning("gui", "Failed to move decompressed archive to new location '{}'.", mExtractedProjectAbsolutePath.toStdString());
200  mStatus = ErrorDecompress;
201  }
202  else
203  {
204  // delete everything left in tempdir after moving project to project dir
205  deleteFilesRecursion(tempdir);
206  }
207  }
208 
209  if (mStatus != Ok)
210  {
211  // Not a hal project, delete all extracted
212  deleteFilesList(extracted);
213  return false;
214  }
215 
216  return true;
217  }
218 
219  void ImportProjectDialog::deleteFilesRecursion(QString dir)
220  {
221  for (QFileInfo finfo : QDir(dir).entryInfoList(QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot))
222  {
223  if (finfo.isDir())
224  deleteFilesRecursion(finfo.absoluteFilePath());
225  else
226  QFile::remove(finfo.absoluteFilePath());
227  }
228  QDir().rmdir(dir);
229  }
230 
231  void ImportProjectDialog::deleteFilesList(QStringList files)
232  {
233  // Delete files, keep directories (might be not empty)
234  auto it = files.begin();
235  while (it != files.end())
236  if (QFileInfo(*it).isDir())
237  ++it;
238  else
239  {
240  QFile::remove(*it);
241  it = files.erase(it);
242  }
243 
244  // Delete directories in revers order
245  if (!files.isEmpty())
246  {
247  for (;;)
248  {
249  --it;
250  QFile::remove(*it);
251  if (it == files.begin())
252  break;
253  }
254  }
255  }
256 
258  {
259  QString retval = filename;
260  retval.remove(QRegularExpression("\\.\\w*$"));
261 
262  QString basedir = retval;
263  int count = 2;
264 
265  for (;;) // loop until non existing directory found
266  {
267  if (!QFileInfo(retval).exists())
268  return retval;
269  retval = QString("%1_%2").arg(basedir).arg(count++);
270  }
271  return retval;
272  }
273 }
Copyright The Montserrat Project in Original or Modified may be sold by itself Original or Modified Versions of the Font Software may be redistributed and or sold with any provided that each copy contains the above copyright notice and this license These can be included either as stand alone text files
Definition: OFL.txt:59
static DirectoryStatus directoryStatus(const QString &pathname)
FileSelectWidget(const QString &defaultEntry, bool existingDir, QWidget *parent=nullptr)
void setFixedEntry(const QString &entry)
static QString suggestedProjectDir(const QString &filename)
ImportProjectDialog(QWidget *parent=nullptr)
void setZippedFile(const QString &filename)
#define log_warning(channel,...)
Definition: log.h:76
QIcon getStyledSvgIcon(const QString &from_to_colors_enabled, const QString &svg_path, QString from_to_colors_disabled=QString())
Definition: graphics.cpp:60
void clicked(bool checked)
ushort unicode() const const
virtual void accept()
virtual void reject()
QPushButton * button(QDialogButtonBox::StandardButton which) const const
QString absoluteFilePath(const QString &fileName) const const
QString currentPath()
bool mkpath(const QString &dirPath) const const
bool rmdir(const QString &dirName) const const
bool remove()
bool rename(const QString &newName)
QString getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options)
bool exists() const const
bool isDir() const const
void setLineWidth(int)
void setFrameStyle(int style)
void addWidget(QWidget *w)
void setReadOnly(bool)
void setText(const QString &)
void textChanged(const QString &text)
const T & at(int i) const const
bool isEmpty() const const
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QString captured(int nth) const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QChar back() const const
void chop(int n)
bool isEmpty() const const
QString & remove(int position, int n)
std::string toStdString() const const
int indexOf(QStringView str, int from) const const
QUuid createUuid()
void setEnabled(bool)
void hide()
QLayout * layout() const const
void setMaximumHeight(int maxh)
void setMinimumWidth(int minw)
void setDisabled(bool disable)
void setSizePolicy(QSizePolicy)
void setWindowTitle(const QString &)