My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
spicedialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  spicedialog.cpp
3  -----------------
4  begin : Tue May 3 2005
5  copyright : (C) 2005 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 #include "spicedialog.h"
19 #include "spicefile.h"
20 #include "main.h"
21 #include "qucs.h"
22 #include "schematic.h"
23 
24 #include <qlabel.h>
25 #include <qlayout.h>
26 #include <qhbox.h>
27 #include <qvbox.h>
28 #include <qlineedit.h>
29 #include <qvalidator.h>
30 #include <qfiledialog.h>
31 #include <qpushbutton.h>
32 #include <qlistbox.h>
33 #include <qcheckbox.h>
34 #include <qprocess.h>
35 #include <qmessagebox.h>
36 #include <qcombobox.h>
37 
38 
40  : QDialog(d, 0, TRUE, Qt::WDestructiveClose)
41 {
42  resize(400, 250);
43  setCaption(tr("Edit SPICE Component Properties"));
44  Comp = c;
45  Doc = d;
46 
47  all = new QVBoxLayout(this); // to provide neccessary size
48  QWidget *myParent = this;
49 
50  Expr.setPattern("[^\"=]+"); // valid expression for property 'edit' etc
51  Validator = new QRegExpValidator(Expr, this);
52  Expr.setPattern("[\\w_]+"); // valid expression for property 'NameEdit' etc
53  ValRestrict = new QRegExpValidator(Expr, this);
54 
55 
56  // ...........................................................
57  QGridLayout *topGrid = new QGridLayout(0, 4,3,3,3);
58  all->addLayout(topGrid);
59 
60  topGrid->addWidget(new QLabel(tr("Name:"), myParent), 0,0);
61  CompNameEdit = new QLineEdit(myParent);
62  CompNameEdit->setValidator(ValRestrict);
63  topGrid->addWidget(CompNameEdit, 0,1);
64  connect(CompNameEdit, SIGNAL(returnPressed()), SLOT(slotButtOK()));
65 
66  topGrid->addWidget(new QLabel(tr("File:"), myParent), 1,0);
67  FileEdit = new QLineEdit(myParent);
68  FileEdit->setValidator(ValRestrict);
69  topGrid->addWidget(FileEdit, 1,1);
70  connect(FileEdit, SIGNAL(returnPressed()), SLOT(slotButtOK()));
71 
72  ButtBrowse = new QPushButton(tr("Browse"), myParent);
73  topGrid->addWidget(ButtBrowse, 1,2);
74  connect(ButtBrowse, SIGNAL(clicked()), SLOT(slotButtBrowse()));
75 
76  ButtEdit = new QPushButton(tr("Edit"), myParent);
77  topGrid->addWidget(ButtEdit, 2,2);
78  connect(ButtEdit, SIGNAL(clicked()), SLOT(slotButtEdit()));
79 
80  FileCheck = new QCheckBox(tr("show file name in schematic"), myParent);
81  topGrid->addWidget(FileCheck, 2,1);
82 
83  SimCheck = new QCheckBox(tr("include SPICE simulations"), myParent);
84  topGrid->addWidget(SimCheck, 3,1);
85 
86  QHBox *h1 = new QHBox(myParent);
87  h1->setSpacing(5);
88  PrepCombo = new QComboBox(h1);
89  PrepCombo->insertItem("none");
90  PrepCombo->insertItem("ps2sp");
91  PrepCombo->insertItem("spicepp");
92  PrepCombo->insertItem("spiceprm");
93  QLabel * PrepLabel = new QLabel(tr("preprocessor"), h1);
94  PrepLabel->setMargin(5);
95  topGrid->addWidget(h1, 4,1);
96  connect(PrepCombo, SIGNAL(activated(int)), SLOT(slotPrepChanged(int)));
97 
98  // ...........................................................
99  QGridLayout *midGrid = new QGridLayout(0, 2,3,5,5);
100  all->addLayout(midGrid);
101 
102  midGrid->addWidget(new QLabel(tr("SPICE net nodes:"), myParent), 0,0);
103  NodesList = new QListBox(myParent);
104  midGrid->addWidget(NodesList, 1,0);
105  connect(NodesList, SIGNAL(doubleClicked(QListBoxItem*)),
106  SLOT(slotAddPort(QListBoxItem*)));
107 
108  QVBox *v0 = new QVBox(myParent);
109  v0->setSpacing(5);
110  midGrid->addWidget(v0, 1,1);
111  ButtAdd = new QPushButton(tr("Add >>"), v0);
112  connect(ButtAdd, SIGNAL(clicked()), SLOT(slotButtAdd()));
113  ButtRemove = new QPushButton(tr("<< Remove"), v0);
114  connect(ButtRemove, SIGNAL(clicked()), SLOT(slotButtRemove()));
115  v0->setStretchFactor(new QWidget(v0), 5); // stretchable placeholder
116 
117  midGrid->addWidget(new QLabel(tr("Component ports:"), myParent), 0,2);
118  PortsList = new QListBox(myParent);
119  midGrid->addWidget(PortsList, 1,2);
120  connect(PortsList, SIGNAL(doubleClicked(QListBoxItem*)),
121  SLOT(slotRemovePort(QListBoxItem*)));
122 
123 
124  // ...........................................................
125  QHBox *h0 = new QHBox(this);
126  h0->setSpacing(5);
127  all->addWidget(h0);
128  connect(new QPushButton(tr("OK"),h0), SIGNAL(clicked()),
129  SLOT(slotButtOK()));
130  connect(new QPushButton(tr("Apply"),h0), SIGNAL(clicked()),
131  SLOT(slotButtApply()));
132  connect(new QPushButton(tr("Cancel"),h0), SIGNAL(clicked()),
133  SLOT(slotButtCancel()));
134 
135  // ------------------------------------------------------------
136  CompNameEdit->setText(Comp->Name);
137  changed = false;
138 
139  // insert all properties into the ListBox
140  Property *pp = Comp->Props.first();
141  FileEdit->setText(pp->Value);
142  FileCheck->setChecked(pp->display);
143  SimCheck->setChecked(Comp->Props.at(2)->Value == "yes");
144  for(int i=0; i<PrepCombo->count(); i++) {
145  if(PrepCombo->text(i) == Comp->Props.at(3)->Value) {
146  PrepCombo->setCurrentItem(i);
147  currentPrep = i;
148  break;
149  }
150  }
151 
152  loadSpiceNetList(pp->Value); // load netlist nodes
153 }
154 
156 {
157  delete all;
158  delete Validator;
159  delete ValRestrict;
160 }
161 
162 // -------------------------------------------------------------------------
163 // Is called if the "OK"-button is pressed.
164 void SpiceDialog::slotButtOK()
165 {
166  slotButtApply();
167  slotButtCancel();
168 }
169 
170 // -------------------------------------------------------------------------
171 // Is called if the "Cancel"-button is pressed.
172 void SpiceDialog::slotButtCancel()
173 {
174  if(changed) done(1); // changed could have been done before
175  else done(0); // (by "Apply"-button)
176 }
177 
178 //-----------------------------------------------------------------
179 // To get really all close events (even <Escape> key).
181 {
182  slotButtCancel();
183 }
184 
185 // -------------------------------------------------------------------------
186 // Is called, if the "Apply"-button is pressed.
187 void SpiceDialog::slotButtApply()
188 {
189  Component *pc;
190  if(CompNameEdit->text().isEmpty()) CompNameEdit->setText(Comp->Name);
191  else
192  if(CompNameEdit->text() != Comp->Name) {
193  for(pc = Doc->Components->first(); pc!=0; pc = Doc->Components->next())
194  if(pc->Name == CompNameEdit->text())
195  break; // found component with the same name ?
196  if(pc) CompNameEdit->setText(Comp->Name);
197  else {
198  Comp->Name = CompNameEdit->text();
199  changed = true;
200  }
201  }
202 
203 
204  // apply all the new property values
205  Property *pp = Comp->Props.first();
206  if(pp->Value != FileEdit->text()) {
207  pp->Value = FileEdit->text();
208  changed = true;
209  }
210  if(pp->display != FileCheck->isChecked()) {
211  pp->display = FileCheck->isChecked();
212  changed = true;
213  }
214 
215  QString tmp;
216  for(unsigned int i=0; i<PortsList->count(); i++) {
217  if(!tmp.isEmpty()) tmp += ',';
218  tmp += "_net" + PortsList->text(i); // chosen ports
219  }
220  pp = Comp->Props.next();
221  if(pp->Value != tmp) {
222  pp->Value = tmp;
223  changed = true;
224  }
225  pp = Comp->Props.next();
226  if((pp->Value=="yes") != SimCheck->isChecked()) {
227  if(SimCheck->isChecked()) pp->Value = "yes";
228  else pp->Value = "no";
229  changed = true;
230  }
231  if(pp->Value != "yes") Comp->withSim = false;
232 
233  pp = Comp->Props.next();
234  if(pp->Value != PrepCombo->currentText()) {
235  pp->Value = PrepCombo->currentText();
236  changed = true;
237  }
238 
239  if(changed || Comp->withSim) { // because of "sim" text
240  Doc->recreateComponent(Comp); // to apply changes to the schematic symbol
241  Doc->viewport()->repaint();
242  }
243 }
244 
245 // -------------------------------------------------------------------------
246 void SpiceDialog::slotButtBrowse()
247 {
248  QString s = QFileDialog::getOpenFileName(
249  lastDir.isEmpty() ? QString(".") : lastDir,
250  tr("SPICE netlist")+" (*.cir);;"+tr("All Files")+" (*.*)",
251  this, "", tr("Select a file"));
252  if(s.isEmpty()) return;
253 
254  QFileInfo Info(s);
255  lastDir = Info.dirPath(true); // remember last directory
256 
257  // snip path if file in current directory
258  if(QucsWorkDir.exists(Info.fileName()) &&
259  QucsWorkDir.absPath() == Info.dirPath(true)) s = Info.fileName();
260  FileEdit->setText(s);
261 
262  Comp->Props.at(1)->Value = "";
263  loadSpiceNetList(s);
264 }
265 
266 // -------------------------------------------------------------------------
267 void SpiceDialog::slotPrepChanged(int i)
268 {
269  if(currentPrep != i) {
270  currentPrep = i;
271  PrepCombo->setCurrentItem(i);
272  loadSpiceNetList(FileEdit->text()); // reload netlist nodes
273  }
274 }
275 
276 // -------------------------------------------------------------------------
277 bool SpiceDialog::loadSpiceNetList(const QString& s)
278 {
279  Comp->withSim = false;
280  if(s.isEmpty()) return false;
281  QFileInfo FileInfo(QucsWorkDir, s);
282 
283  NodesList->clear();
284  PortsList->clear();
285  textStatus = 0;
286  Line = Error = "";
287 
288  QString preprocessor = PrepCombo->currentText();
289  if (preprocessor != "none") {
290  bool piping = true;
291  QString script;
292 #ifdef __MINGW32__
293  QString interpreter = "tinyperl.exe";
294 #else
295  QString interpreter = "perl";
296 #endif
297  if (preprocessor == "ps2sp") {
298  script = "ps2sp";
299  } else if (preprocessor == "spicepp") {
300  script = "spicepp.pl";
301  } else if (preprocessor == "spiceprm") {
302  script = "spiceprm";
303  piping = false;
304  }
305  script = QucsSettings.BinDir + script;
306  SpicePrep = new QProcess(this);
307  SpicePrep->addArgument(interpreter);
308  SpicePrep->addArgument(script);
309  SpicePrep->addArgument(FileInfo.filePath());
310 
311  QFile PrepFile;
312  QFileInfo PrepInfo(QucsWorkDir, s + ".pre");
313  QString PrepName = PrepInfo.filePath();
314 
315  if (!piping) {
316  SpicePrep->addArgument(PrepName);
317  connect(SpicePrep, SIGNAL(readyReadStdout()), SLOT(slotSkipOut()));
318  connect(SpicePrep, SIGNAL(readyReadStderr()), SLOT(slotGetPrepErr()));
319  } else {
320  connect(SpicePrep, SIGNAL(readyReadStdout()), SLOT(slotGetPrepOut()));
321  connect(SpicePrep, SIGNAL(readyReadStderr()), SLOT(slotGetPrepErr()));
322  }
323 
324  QMessageBox *MBox = new QMessageBox(tr("Info"),
325  tr("Preprocessing SPICE file \"%1\".").arg(FileInfo.filePath()),
326  QMessageBox::NoIcon, QMessageBox::Abort,
327  QMessageBox::NoButton, QMessageBox::NoButton, this, 0, true,
328  Qt::WStyle_DialogBorder | Qt::WDestructiveClose);
329  connect(SpicePrep, SIGNAL(processExited()), MBox, SLOT(close()));
330 
331  if (piping) {
332  PrepFile.setName(PrepName);
333  if(!PrepFile.open(IO_WriteOnly)) {
334  QMessageBox::critical(this, tr("Error"),
335  tr("Cannot save preprocessed SPICE file \"%1\".").
336  arg(PrepName));
337  return false;
338  }
339  prestream = new QTextStream(&PrepFile);
340  }
341 
342  if(!SpicePrep->start()) {
343  QMessageBox::critical(this, tr("Error"),
344  tr("Cannot execute \"%1\".").arg(interpreter + " " + script));
345  if (piping) {
346  PrepFile.close();
347  delete prestream;
348  }
349  return false;
350  }
351  SpicePrep->closeStdin();
352 
353  MBox->exec();
354  delete SpicePrep;
355  if (piping) {
356  PrepFile.close();
357  delete prestream;
358  }
359 
360  if(!Error.isEmpty()) {
361  QMessageBox::critical(this, tr("SPICE Preprocessor Error"), Error);
362  return false;
363  }
364  FileInfo = QFileInfo(QucsWorkDir, s + ".pre");
365  }
366 
367  // first call Qucsconv ............
368  QucsConv = new QProcess(this);
369  QucsConv->addArgument(QucsSettings.BinDir + "qucsconv");
370  QucsConv->addArgument("-if");
371  QucsConv->addArgument("spice");
372  QucsConv->addArgument("-of");
373  QucsConv->addArgument("qucs");
374  QucsConv->addArgument("-i");
375  QucsConv->addArgument(FileInfo.filePath());
376  connect(QucsConv, SIGNAL(readyReadStdout()), SLOT(slotGetNetlist()));
377  connect(QucsConv, SIGNAL(readyReadStderr()), SLOT(slotGetError()));
378 
379  QMessageBox *MBox = new QMessageBox(tr("Info"),
380  tr("Converting SPICE file \"%1\".").arg(FileInfo.filePath()),
381  QMessageBox::NoIcon, QMessageBox::Abort,
382  QMessageBox::NoButton, QMessageBox::NoButton, this, 0, true,
383  Qt::WStyle_DialogBorder | Qt::WDestructiveClose);
384  connect(QucsConv, SIGNAL(processExited()), MBox, SLOT(close()));
385 
386  if(!QucsConv->start()) {
387  QMessageBox::critical(this, tr("Error"),
388  tr("Cannot execute \"%1\".").arg(QucsSettings.BinDir + "qucsconv"));
389  return false;
390  }
391  QucsConv->closeStdin();
392 
393  MBox->exec();
394  delete QucsConv;
395 
396  if(!Error.isEmpty())
397  QMessageBox::critical(this, tr("QucsConv Error"), Error);
398 
399  Property *pp = Comp->Props.at(1);
400  if(!pp->Value.isEmpty()) {
401  PortsList->clear();
402  PortsList->insertStringList(QStringList::split(',', pp->Value));
403  }
404 
405  QString tmp;
406  QListBoxItem *pi;
407  for(unsigned int i=0; i<PortsList->count(); i++) {
408  tmp = PortsList->text(i).remove(0, 4);
409  PortsList->changeItem(tmp, i);
410 
411  pi = NodesList->findItem(tmp, Qt::CaseSensitive | Qt::ExactMatch);
412  if(pi) delete pi;
413  else PortsList->removeItem(i--);
414  }
415  return true;
416 }
417 
418 // -------------------------------------------------------------------------
419 void SpiceDialog::slotSkipErr()
420 {
421  SpicePrep->readStderr();
422 }
423 
424 // -------------------------------------------------------------------------
425 void SpiceDialog::slotSkipOut()
426 {
427  SpicePrep->readStdout();
428 }
429 
430 // -------------------------------------------------------------------------
431 void SpiceDialog::slotGetPrepErr()
432 {
433  Error += QString(SpicePrep->readStderr());
434 }
435 
436 // -------------------------------------------------------------------------
437 void SpiceDialog::slotGetPrepOut()
438 {
439  (*prestream) << QString(SpicePrep->readStdout());
440 }
441 
442 // -------------------------------------------------------------------------
443 void SpiceDialog::slotGetError()
444 {
445  Error += QString(QucsConv->readStderr());
446 }
447 
448 // -------------------------------------------------------------------------
449 void SpiceDialog::slotGetNetlist()
450 {
451  int i;
452  QString s;
453  Line += QString(QucsConv->readStdout());
454 
455  while((i = Line.find('\n')) >= 0) {
456 
457  s = Line.left(i);
458  Line.remove(0, i+1);
459 
460 
461  s = s.stripWhiteSpace();
462  if(s.at(0) == '.') {
463  if(s.left(5) != ".Def:") Comp->withSim = true;
464  continue;
465  }
466 
467  switch(textStatus) {
468  case 0:
469  if(s == "### TOPLEVEL NODELIST BEGIN")
470  textStatus = 1;
471  else if(s == "### SPICE OUTPUT NODELIST BEGIN")
472  textStatus = 2;
473  break;
474 
475  case 1:
476  if(s == "### TOPLEVEL NODELIST END") {
477  textStatus = 0;
478  break;
479  }
480  if(s.left(2) != "# ") break;
481  s.remove(0, 2);
482 
483  if(s.left(4) == "_net")
484  NodesList->insertItem(s.remove(0, 4));
485  break;
486 
487  case 2:
488  if(s == "### SPICE OUTPUT NODELIST END") {
489  textStatus = 0;
490  break;
491  }
492  if(s.left(2) != "# ") break;
493  s.remove(0, 2);
494 
495  if(s.left(4) == "_net")
496  PortsList->insertItem(s); // prefix "_net" is removed later on
497  break;
498  }
499  }
500 }
501 
502 // -------------------------------------------------------------------------
503 void SpiceDialog::slotButtEdit()
504 {
505  Doc->App->editFile(QucsWorkDir.filePath(FileEdit->text()));
506 }
507 
508 // -------------------------------------------------------------------------
509 // Is called if the add button is pressed.
510 void SpiceDialog::slotButtAdd()
511 {
512  int i = NodesList->currentItem();
513  if(i < 0) return;
514  PortsList->insertItem(NodesList->currentText());
515  NodesList->removeItem(i);
516 }
517 
518 // -------------------------------------------------------------------------
519 // Is called if the remove button is pressed.
520 void SpiceDialog::slotButtRemove()
521 {
522  int i = PortsList->currentItem();
523  if(i < 0) return;
524  NodesList->insertItem(PortsList->currentText());
525  PortsList->removeItem(i);
526 }
527 
528 // -------------------------------------------------------------------------
529 // Is called when double-click on NodesList-Box
530 void SpiceDialog::slotAddPort(QListBoxItem *Item)
531 {
532  if(Item) slotButtAdd();
533 }
534 
535 // -------------------------------------------------------------------------
536 // Is called when double-click on PortsList-Box
537 void SpiceDialog::slotRemovePort(QListBoxItem *Item)
538 {
539  if(Item) slotButtRemove();
540 }