My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
packagedialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  packagedialog.cpp
3  -------------------
4  begin : Sun Jun 25 2006
5  copyright : (C) 2006 by Michael Margraf
6  email : michael.margraf@alumni.tu-berlin.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <stdlib.h>
23 
24 #include <qhbox.h>
25 #include <qvbox.h>
26 #include <qlabel.h>
27 #include <qlayout.h>
28 #include <qlineedit.h>
29 #include <qtextedit.h>
30 #include <qcheckbox.h>
31 #include <qfiledialog.h>
32 #include <qmessagebox.h>
33 #include <qpushbutton.h>
34 #include <qscrollview.h>
35 #include <qdatastream.h>
36 #include <qvbuttongroup.h>
37 
38 #include "packagedialog.h"
39 #include "qucs.h"
40 #include "main.h"
41 
42 
43 #define HEADER_LENGTH 32
44 #define CODE_ERROR 0x0000
45 #define CODE_DIR 0x0010
46 #define CODE_DIR_END 0x0018
47 #define CODE_FILE 0x0020
48 #define CODE_LIBRARY 0x0040
49 
50 
51 PackageDialog::PackageDialog(QWidget *parent_, bool create_)
52  : QDialog(parent_, 0, TRUE, Qt::WDestructiveClose)
53 {
54  all = new QVBoxLayout(this);
55  all->setMargin(5);
56  all->setSpacing(6);
57 
58  QHBox *h2 = new QHBox(this);
59 
60  if(create_) { // create or extract package ?
61  setCaption(tr("Create Project Package"));
62 
63  QHBox *h1 = new QHBox(this);
64  all->addWidget(h1);
65  new QLabel(tr("Package:"), h1);
66  NameEdit = new QLineEdit(h1);
67  QPushButton *ButtBrowse = new QPushButton(tr("Browse"), h1);
68  connect(ButtBrowse, SIGNAL(clicked()), SLOT(slotBrowse()));
69 
70  LibraryCheck = new QCheckBox(tr("include user libraries"), this);
71  all->addWidget(LibraryCheck);
72 
73  Group = new QVButtonGroup(tr("Choose projects:"), this);
74  all->addWidget(Group);
75 
76  QScrollView *Dia_Scroll = new QScrollView(Group);
77  Dia_Scroll->setMargin(5);
78  QVBox *Dia_Box = new QVBox(Dia_Scroll->viewport());
79  Dia_Scroll->addChild(Dia_Box);
80 
81  // ...........................................................
82  all->addWidget(h2);
83  QPushButton *ButtCreate = new QPushButton(tr("Create"), h2);
84  connect(ButtCreate, SIGNAL(clicked()), SLOT(slotCreate()));
85  QPushButton *ButtCancel = new QPushButton(tr("Cancel"), h2);
86  connect(ButtCancel, SIGNAL(clicked()), SLOT(reject()));
87 
88  // ...........................................................
89  // insert all projects
90  QStringList PrDirs = QucsHomeDir.entryList("*", QDir::Dirs, QDir::Name);
91  QStringList::iterator it;
92  for(it = PrDirs.begin(); it != PrDirs.end(); it++)
93  if((*it).right(4) == "_prj") // project directories end with "_prj"
94  BoxList.append(new QCheckBox((*it).left((*it).length()-4), Dia_Box));
95 
96  QColor theColor;
97  if(BoxList.isEmpty()) {
98  ButtCreate->setEnabled(false);
99  theColor =
100  (new QLabel(tr("No projects!"), Dia_Box))->paletteBackgroundColor();
101  }
102  else
103  theColor = BoxList.current()->paletteBackgroundColor();
104  Dia_Scroll->viewport()->setPaletteBackgroundColor(theColor);
105  }
106 
107  else { // of "if(create_)"
108  setCaption(tr("Extract Project Package"));
109 
110  MsgText = new QTextEdit(this);
111  MsgText->setTextFormat(Qt::PlainText);
112  MsgText->setWordWrap(QTextEdit::NoWrap);
113  MsgText->setReadOnly(true);
114  all->addWidget(MsgText);
115 
116  all->addWidget(h2);
117  h2->setStretchFactor(new QWidget(h2), 5); // stretchable placeholder
118  ButtClose = new QPushButton(tr("Close"), h2);
119  ButtClose->setDisabled(true);
120  connect(ButtClose, SIGNAL(clicked()), SLOT(accept()));
121 
122  resize(400, 250);
123  }
124 }
125 
127 {
128  delete all;
129 }
130 
131 // ---------------------------------------------------------------
132 void PackageDialog::slotBrowse()
133 {
134  QString s = QFileDialog::getSaveFileName(
135  lastDir.isEmpty() ? QString(".") : lastDir,
136  tr("Qucs Packages")+" (*.qucs);;"+
137  tr("Any File")+" (*)",
138  this, 0, tr("Enter a Package File Name"));
139  if(s.isEmpty()) return;
140 
141  QFileInfo Info(s);
142  lastDir = Info.dirPath(true); // remember last directory
143 
144  if(Info.extension().isEmpty())
145  s += ".qucs";
146  NameEdit->setText(s);
147 }
148 
149 // ***************************************************************
150 // ***** Functions for creating package *****
151 // ***************************************************************
152 
153 int PackageDialog::insertFile(const QString& FileName, QFile& File,
154  QDataStream& Stream)
155 {
156  QByteArray FileContent;
157 
158  if(!File.open(IO_ReadOnly)) {
159  QMessageBox::critical(this, tr("Error"),
160  tr("Cannot open \"%1\"!").arg(FileName));
161  return -1;
162  }
163 
164  Q_ULONG Count = File.size();
165  char *p = (char*)malloc(Count+FileName.length()+2);
166  strcpy(p, FileName.latin1());
167  File.readBlock(p+FileName.length()+1, Count);
168  File.close();
169 
170  Count += FileName.length()+1;
171  FileContent = qCompress((unsigned char*)p, Count);
172  free(p);
173 
174  Stream.writeBytes(FileContent.data(), FileContent.size());
175  return 0;
176 }
177 
178 // ---------------------------------------------------------------
179 int PackageDialog::insertDirectory(const QString& DirName,
180  QDataStream& Stream)
181 {
182  QFile File;
183  QDir myDir(DirName);
184 
185  // Put all files of this directory into the package.
186  QStringList Entries = myDir.entryList("*", QDir::Files, QDir::Name);
187  QStringList::iterator it;
188  for(it = Entries.begin(); it != Entries.end(); ++it) {
189  File.setName(myDir.absFilePath(*it));
190  Stream << Q_UINT32(CODE_FILE);
191  if(insertFile(*it, File, Stream) < 0)
192  return -1;
193  }
194 
195  // Put all subdirectories into the package.
196  Entries = myDir.entryList("*", QDir::Dirs, QDir::Name);
197  Entries.pop_front(); // delete "." from list
198  Entries.pop_front(); // delete ".." from list
199  for(it = Entries.begin(); it != Entries.end(); ++it) {
200  Stream << Q_UINT32(CODE_DIR) << (*it).latin1();
201  if(insertDirectory(myDir.absPath()+QDir::separator()+(*it), Stream) < 0)
202  return -1;
203  Stream << Q_UINT32(CODE_DIR_END) << Q_UINT32(0);
204  }
205  return 0;
206 }
207 
208 // ---------------------------------------------------------------
209 int PackageDialog::insertLibraries(QDataStream& Stream)
210 {
211  QFile File;
212  QDir myDir(QucsHomeDir.absPath() + QDir::separator() + "user_lib");
213  QStringList Entries = myDir.entryList("*", QDir::Files, QDir::Name);
214  QStringList::iterator it;
215  for(it = Entries.begin(); it != Entries.end(); ++it) {
216  File.setName(myDir.absFilePath(*it));
217  Stream << Q_UINT32(CODE_LIBRARY);
218  if(insertFile(*it, File, Stream) < 0)
219  return -1;
220  }
221  return 0;
222 }
223 
224 // ---------------------------------------------------------------
225 void PackageDialog::slotCreate()
226 {
227  if(NameEdit->text().isEmpty()) {
228  QMessageBox::critical(this, tr("Error"), tr("Please insert a package name!"));
229  return;
230  }
231 
232  QCheckBox *p;
233  if(!LibraryCheck->isChecked()) {
234  int count=0;
235  for(p = BoxList.first(); p != 0; p = BoxList.next())
236  if(p->isChecked())
237  count++;
238 
239  if(count < 1) {
240  QMessageBox::critical(this, tr("Error"), tr("Please choose at least one project!"));
241  return;
242  }
243  }
244 
245  QString s(NameEdit->text());
246  QFileInfo Info(s);
247  if(Info.extension().isEmpty())
248  s += ".qucs";
249  NameEdit->setText(s);
250 
251  QFile PkgFile(s);
252  if(PkgFile.exists())
253  if(QMessageBox::information(this, tr("Info"),
254  tr("Output file already exists!")+"\n"+tr("Overwrite it?"),
255  tr("&Yes"), tr("&No"), 0,1,1))
256  return;
257 
258  if(!PkgFile.open(IO_ReadWrite)) {
259  QMessageBox::critical(this, tr("Error"), tr("Cannot create package!"));
260  return;
261  }
262  QDataStream Stream(&PkgFile);
263 
264  // First write header.
265  char Header[HEADER_LENGTH];
266  memset(Header, 0, HEADER_LENGTH);
267  strcpy(Header, "Qucs package " PACKAGE_VERSION);
268  PkgFile.writeBlock(Header, HEADER_LENGTH);
269 
270 
271  // Write project files to package.
272  for(p = BoxList.first(); p != 0; p = BoxList.next())
273  if(p->isChecked()) {
274  s = p->text() + "_prj";
275  Stream << Q_UINT32(CODE_DIR) << s.latin1();
276  s = QucsHomeDir.absPath() + QDir::separator() + s;
277  if(insertDirectory(s, Stream) < 0) {
278  PkgFile.close();
279  PkgFile.remove();
280  return;
281  }
282  Stream << Q_UINT32(CODE_DIR_END) << Q_UINT32(0);
283  }
284 
285  // Write user libraries to package if desired.
286  if(LibraryCheck->isChecked())
287  if(insertLibraries(Stream) < 0) {
288  PkgFile.close();
289  PkgFile.remove();
290  return;
291  }
292 
293  // Calculate checksum and write it to package file.
294  PkgFile.at(0);
295  QByteArray Content = PkgFile.readAll();
296  Q_UINT16 Checksum = qChecksum(Content.data(), Content.size());
297  PkgFile.at(HEADER_LENGTH-sizeof(Q_UINT16));
298  Stream << Checksum;
299  PkgFile.close();
300 
301  QMessageBox::information(this, tr("Info"),
302  tr("Successfully created Qucs package!"));
303  accept();
304 }
305 
306 
307 
308 // ***************************************************************
309 // ***** Functions for extracting package *****
310 // ***************************************************************
311 
313 {
314  QString s = QFileDialog::getOpenFileName(
315  lastDir.isEmpty() ? QString(".") : lastDir,
316  tr("Qucs Packages")+" (*.qucs);;"+
317  tr("Any File")+" (*)",
318  this, 0, tr("Enter a Package File Name"));
319 
320  if(s.isEmpty()) {
321  reject();
322  return;
323  }
324 
325  QFileInfo Info(s);
326  lastDir = Info.dirPath(true); // remember last directory
327 
328  QFile PkgFile(s);
329  if(!PkgFile.open(IO_ReadOnly)) {
330  if(Info.extension().isEmpty()) s += ".qucs";
331  PkgFile.setName(s);
332  if(!PkgFile.open(IO_ReadOnly)) {
333  MsgText->append(tr("ERROR: Cannot open package!"));
334  ButtClose->setDisabled(false);
335  return;
336  }
337  }
338  QDataStream Stream(&PkgFile);
339 
340  QDir currDir = QucsHomeDir;
341  QString Version;
342  Q_UINT16 Checksum;
343  Q_UINT32 Code, Length;
344 
345  // First read and check header.
346  QByteArray Content = PkgFile.readAll();
347  if(strncmp(Content.data(), "Qucs package ", 13) != 0) {
348  MsgText->append(tr("ERROR: File contains wrong header!"));
349  goto ErrorEnd;
350  }
351 
352  Version = QString(Content.data()+13);
353  if(!checkVersion(Version)) {
354  MsgText->append(tr("ERROR: Wrong version number!"));
355  goto ErrorEnd;
356  }
357 
358  // checksum correct ?
359  PkgFile.at(HEADER_LENGTH-2);
360  Stream >> Checksum;
361  *((Q_UINT16*)(Content.data()+HEADER_LENGTH-2)) = 0;
362  if(Checksum != qChecksum(Content.data(), Content.size())) {
363  MsgText->append(tr("ERROR: Checksum mismatch!"));
364  goto ErrorEnd;
365  }
366  Content.resize(0); // dispose memory
367 
368 
369  // work on all files and directories in the package
370  for(;;) {
371  if(PkgFile.atEnd()) break;
372  Stream >> Code >> Length;
373 
374  switch(Code) {
375  case CODE_DIR:
376  if(extractDirectory(PkgFile, Length, currDir) > 0)
377  break;
378  goto ErrorEnd;
379  case CODE_DIR_END:
380  MsgText->append(tr("Leave directory \"%1\"").arg(currDir.absPath()));
381  currDir.cdUp();
382  break;
383  case CODE_FILE:
384  if(extractFile(PkgFile, Length, currDir) > 0)
385  break;
386  goto ErrorEnd;
387  case CODE_LIBRARY:
388  if(extractLibrary(PkgFile, Length) > 0)
389  break;
390  goto ErrorEnd;
391  default:
392  MsgText->append(tr("ERROR: Package is corrupt!"));
393  goto ErrorEnd;
394  }
395  }
396 
397 
398  MsgText->append(" ");
399  MsgText->append(tr("Successfully extracted package!"));
400 ErrorEnd:
401  MsgText->append(" ");
402  ButtClose->setDisabled(false);
403  PkgFile.close();
404 }
405 
406 // ---------------------------------------------------------------
407 int PackageDialog::extractDirectory(QFile& PkgFile, Q_UINT32 Count, QDir& currDir)
408 {
409  char *p = (char*)malloc(Count);
410  PkgFile.readBlock(p, Count);
411 
412  if(currDir.cd(QString(p))) { // directory exists ?
413  MsgText->append(tr("ERROR: Project directory \"%1\" already exists!").arg(QString(p)));
414  return -1;
415  }
416 
417  if(!currDir.mkdir(QString(p))) {
418  MsgText->append(tr("ERROR: Cannot create directory \"%1\"!").arg(QString(p)));
419  return -2;
420  }
421  currDir.cd(QString(p));
422  MsgText->append(tr("Create and enter directory \"%1\"").arg(currDir.absPath()));
423 
424  free(p);
425  return 1;
426 }
427 
428 // ---------------------------------------------------------------
429 int PackageDialog::extractFile(QFile& PkgFile, Q_UINT32 Count, QDir& currDir)
430 {
431  char *p = (char*)malloc(Count);
432  PkgFile.readBlock(p, Count);
433  QByteArray Content = qUncompress((unsigned char*)p, Count);
434  free(p);
435 
436  p = Content.data();
437  QFile File(currDir.absFilePath(QString(p)));
438  if(!File.open(IO_WriteOnly)) {
439  MsgText->append(tr("ERROR: Cannot create file \"%1\"!").arg(QString(p)));
440  return -1;
441  }
442 
443  File.writeBlock(p+strlen(p)+1, Content.size()-strlen(p)-1);
444  File.close();
445  MsgText->append(tr("Create file \"%1\"").arg(QString(p)));
446  return 1;
447 }
448 
449 // ---------------------------------------------------------------
450 int PackageDialog::extractLibrary(QFile& PkgFile, Q_UINT32 Count)
451 {
452  char *p = (char*)malloc(Count);
453  PkgFile.readBlock(p, Count);
454  QByteArray Content = qUncompress((unsigned char*)p, Count);
455  free(p);
456 
457  p = Content.data();
458  QFile File(QucsHomeDir.absPath() +
459  QDir::convertSeparators("/user_lib/") + QString(p));
460  if(File.exists()) {
461  MsgText->append(tr("ERROR: User library \"%1\" already exists!").arg(QString(p)));
462  return -1;
463  }
464 
465  if(!File.open(IO_WriteOnly)) {
466  MsgText->append(tr("ERROR: Cannot create library \"%1\"!").arg(QString(p)));
467  return -1;
468  }
469 
470  File.writeBlock(p+strlen(p)+1, Content.size()-strlen(p)-1);
471  File.close();
472  MsgText->append(tr("Create library \"%1\"").arg(QString(p)));
473  return 1;
474 }