mirror of
https://github.com/ra3xdh/qucs_s
synced 2025-03-28 21:13:26 +00:00
522 lines
16 KiB
C++
522 lines
16 KiB
C++
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "misc.h"
|
|
#include "main.h"
|
|
#include "component.h"
|
|
|
|
#include "schematic.h"
|
|
#include "symbolwidget.h"
|
|
#include "spicelibcompdialog.h"
|
|
|
|
|
|
SpiceLibCompDialog::SpiceLibCompDialog(Component *pc, Schematic *sch) : QDialog{sch}
|
|
{
|
|
comp = pc;
|
|
Doc = sch;
|
|
symbolPinsCount = 0;
|
|
isChanged = false;
|
|
libError = false;
|
|
|
|
QString file = comp->Props.at(0)->Value;
|
|
if (!file.isEmpty()) {
|
|
file = misc::properAbsFileName(file, Doc);
|
|
QFileInfo inf(file);
|
|
lastLibDir = inf.absoluteDir().path();
|
|
} else {
|
|
QFileInfo inf = Doc->getFileInfo();
|
|
lastLibDir = inf.absoluteDir().path();
|
|
}
|
|
bool show_lib = comp->Props.at(0)->display;
|
|
QString device = comp->Props.at(1)->Value;
|
|
bool show_model = comp->Props.at(1)->display;
|
|
QString sym = comp->Props.at(2)->Value;
|
|
QString par = comp->Props.at(3)->Value;
|
|
bool show_par = comp->Props.at(3)->display;
|
|
QString pin_list = comp->Props.at(4)->Value;
|
|
|
|
QLabel *lblLibfile = new QLabel("SPICE library:");
|
|
edtLibPath = new QLineEdit;
|
|
edtLibPath->setText(file);
|
|
connect(edtLibPath,SIGNAL(textChanged(QString)),this,SLOT(slotFillSubcirComboBox()));
|
|
btnOpenLib = new QPushButton(tr("Open"));
|
|
connect(btnOpenLib,SIGNAL(clicked(bool)),this,SLOT(slotBtnOpenLib()));
|
|
|
|
QLabel *lbl_par = new QLabel("Component parameters");
|
|
edtParams = new QLineEdit;
|
|
edtParams->setText(par);
|
|
|
|
QLabel *lblDevice = new QLabel("Subcircuit:");
|
|
cbxSelectSubcir = new QComboBox;
|
|
connect(cbxSelectSubcir,SIGNAL(currentIndexChanged(int)),this,SLOT(slotFillPinsTable()));
|
|
|
|
listSymPattern = new QListWidget;
|
|
QStringList lst_patterns;
|
|
misc::getSymbolPatternsList(lst_patterns);
|
|
listSymPattern->addItems(lst_patterns);
|
|
listSymPattern->setCurrentRow(0);
|
|
listSymPattern->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
connect(listSymPattern,SIGNAL(currentRowChanged(int)),this,SLOT(slotSetSymbol()));
|
|
|
|
rbAutoSymbol = new QRadioButton(tr("Automatic symbol"));
|
|
rbSymFromTemplate = new QRadioButton(tr("Symbol from template"));
|
|
rbUserSym = new QRadioButton(tr("Symbol from file"));
|
|
btnOpenSym = new QPushButton(tr("Open"));
|
|
edtSymFile = new QLineEdit();
|
|
connect(btnOpenSym,SIGNAL(clicked(bool)),this,SLOT(slotBtnOpenSym()));
|
|
|
|
chbShowLib = new QCheckBox(tr("Show"));
|
|
chbShowLib->setChecked(show_lib);
|
|
chbShowModel = new QCheckBox(tr("Show"));
|
|
chbShowModel->setChecked(show_model);
|
|
chbShowParams = new QCheckBox(tr("Show"));
|
|
chbShowParams->setChecked(show_par);
|
|
connect(chbShowLib,SIGNAL(toggled(bool)),this,SLOT(slotChanged()));
|
|
connect(chbShowModel,SIGNAL(toggled(bool)),this,SLOT(slotChanged()));
|
|
connect(chbShowParams,SIGNAL(toggled(bool)),this,SLOT(slotChanged()));
|
|
|
|
if (QFileInfo::exists(misc::properAbsFileName(sym, Doc))) {
|
|
edtSymFile->setText(misc::properAbsFileName(sym, Doc));
|
|
rbUserSym->setChecked(true);
|
|
QFileInfo inf(misc::properAbsFileName(sym, Doc));
|
|
lastSymbolDir = inf.absoluteDir().path();
|
|
} else {
|
|
QFileInfo inf = Doc->getFileInfo();
|
|
lastSymbolDir = inf.absoluteDir().path();
|
|
if (sym == "auto") {
|
|
rbAutoSymbol->setChecked(true);
|
|
} else {
|
|
rbSymFromTemplate->setChecked(true);
|
|
}
|
|
}
|
|
|
|
symbol = new SymbolWidget;
|
|
symbol->disableDragNDrop();
|
|
symbol->enableShowPinNumbers();
|
|
symbol->setPaintText("");
|
|
|
|
tbwPinsTable = new QTableWidget;
|
|
tbwPinsTable->setColumnCount(2);
|
|
tbwPinsTable->setRowCount(100);
|
|
QStringList lbl_cols;
|
|
lbl_cols<<"Subcircuit pin"<<"Symbol pin";
|
|
tbwPinsTable->setHorizontalHeaderLabels(lbl_cols);
|
|
tbwPinsTable->setMinimumWidth(2.2*tbwPinsTable->columnWidth(0));
|
|
tbwPinsTable->setMinimumHeight(5*tbwPinsTable->rowHeight(0));
|
|
connect(tbwPinsTable,SIGNAL(cellDoubleClicked(int,int)),this,SLOT(slotTableCellDoubleClick()));
|
|
|
|
edtSPICE = new QPlainTextEdit;
|
|
edtSPICE->setReadOnly(true);
|
|
edtSPICE->setMinimumSize(tbwPinsTable->minimumSize());
|
|
|
|
btnOK = new QPushButton(tr("OK"));
|
|
connect(btnOK,SIGNAL(clicked(bool)),this,SLOT(slotBtnOK()));
|
|
btnApply = new QPushButton(tr("Apply"));
|
|
connect(btnApply,SIGNAL(clicked(bool)),this,SLOT(slotBtnApply()));
|
|
btnCancel = new QPushButton(tr("Cancel"));
|
|
connect(btnCancel,SIGNAL(clicked(bool)),this,SLOT(slotBtnCancel()));
|
|
|
|
QVBoxLayout *top = new QVBoxLayout;
|
|
QHBoxLayout *l2 = new QHBoxLayout;
|
|
l2->addWidget(lblLibfile);
|
|
l2->addWidget(edtLibPath,4);
|
|
l2->addWidget(btnOpenLib,1);
|
|
l2->addWidget(chbShowLib);
|
|
top->addLayout(l2);
|
|
|
|
QHBoxLayout *l7 = new QHBoxLayout;
|
|
l7->addWidget(lbl_par);
|
|
l7->addWidget(edtParams);
|
|
l7->addWidget(chbShowParams);
|
|
top->addLayout(l7);
|
|
|
|
QGridLayout *gl1 = new QGridLayout;
|
|
gl1->addWidget(rbAutoSymbol,0,0);
|
|
gl1->addWidget(rbSymFromTemplate,1,0);
|
|
gl1->addWidget(rbUserSym,2,0);
|
|
gl1->addWidget(edtSymFile,2,1);
|
|
gl1->addWidget(btnOpenSym,2,2);
|
|
gl1->addWidget(symbol,0,3,3,2);
|
|
gl1->addWidget(listSymPattern,0,5,3,2);
|
|
top->addLayout(gl1);
|
|
|
|
QHBoxLayout *l3 = new QHBoxLayout;
|
|
l3->addWidget(tbwPinsTable);
|
|
QGroupBox *gpb1 = new QGroupBox(tr("SPICE model"));
|
|
QVBoxLayout *l8 = new QVBoxLayout;
|
|
QHBoxLayout *l5 = new QHBoxLayout;
|
|
l5->addWidget(lblDevice);
|
|
l5->addWidget(cbxSelectSubcir);
|
|
l5->addWidget(chbShowModel);
|
|
l8->addLayout(l5);
|
|
l8->addWidget(edtSPICE);
|
|
gpb1->setLayout(l8);
|
|
l3->addWidget(gpb1);
|
|
top->addLayout(l3,3);
|
|
|
|
|
|
QHBoxLayout *l4 = new QHBoxLayout;
|
|
l4->addWidget(btnOK);
|
|
l4->addWidget(btnApply);
|
|
l4->addWidget(btnCancel);
|
|
l4->addStretch();
|
|
top->addLayout(l4);
|
|
|
|
this->slotSetSymbol();
|
|
this->setLayout(top);
|
|
this->setWindowTitle(tr("Edit SPICE library device"));
|
|
slotFillSubcirComboBox();
|
|
|
|
cbxSelectSubcir->setCurrentText(device);
|
|
auto items = listSymPattern->findItems(sym,Qt::MatchExactly);
|
|
if (!items.isEmpty()) listSymPattern->setCurrentItem(items.at(0));
|
|
//listSymPattern->setCurrentText(sym);
|
|
|
|
if (!pin_list.isEmpty()) {
|
|
QStringList pins = pin_list.split(";");
|
|
for(int i = 0; i < pins.count(); i++) {
|
|
QTableWidgetItem *itm = tbwPinsTable->item(i,1);
|
|
if (itm != nullptr) {
|
|
itm->setText(pins.at(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
listSymPattern->setMinimumWidth(0.75*tbwPinsTable->minimumWidth());
|
|
symbol->setMinimumWidth(0.75*tbwPinsTable->minimumWidth());
|
|
|
|
btnApply->setEnabled(false);
|
|
connect(rbAutoSymbol,SIGNAL(toggled(bool)),this,SLOT(slotSetSymbol()));
|
|
connect(rbSymFromTemplate,SIGNAL(toggled(bool)),this,SLOT(slotSetSymbol()));
|
|
connect(rbUserSym,SIGNAL(toggled(bool)),this,SLOT(slotSetSymbol()));
|
|
connect(edtLibPath,SIGNAL(textChanged(QString)),this,SLOT(slotChanged()));
|
|
connect(edtParams,SIGNAL(textChanged(QString)),this,SLOT(slotChanged()));
|
|
connect(tbwPinsTable,SIGNAL(cellChanged(int,int)),this,SLOT(slotChanged()));
|
|
connect(listSymPattern,SIGNAL(currentIndexChanged(int)),this,SLOT(slotChanged()));
|
|
connect(cbxSelectSubcir,SIGNAL(currentIndexChanged(int)),this,SLOT(slotChanged()));
|
|
connect(edtSymFile,SIGNAL(textChanged(QString)),this,SLOT(slotSetSymbol()));
|
|
connect(edtSymFile,SIGNAL(textChanged(QString)),this,SLOT(slotChanged()));
|
|
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotFillSubcirComboBox()
|
|
{
|
|
QString libfile = edtLibPath->text();
|
|
if (!QFile::exists(libfile)) return;
|
|
int r = parseLibFile(libfile);
|
|
libError = false;
|
|
if (r != noError) {
|
|
libError = true;
|
|
QString msg;
|
|
switch (r) {
|
|
case failedOpenFile:
|
|
msg = tr("Failed open file: ") + libfile;
|
|
break;
|
|
case noSUBCKT:
|
|
msg = tr("SPICE library parse error.\n"
|
|
"No SUBCKT directive found in library ") + libfile;
|
|
break;
|
|
default:
|
|
msg = tr("SPICE library parse error");
|
|
}
|
|
QMessageBox::critical(this,tr("Error"),msg);
|
|
return;
|
|
}
|
|
|
|
cbxSelectSubcir->blockSignals(true);
|
|
cbxSelectSubcir->clear();
|
|
for(const auto &key: subcirPins.keys()) {
|
|
cbxSelectSubcir->addItem(key);
|
|
}
|
|
cbxSelectSubcir->blockSignals(false);
|
|
slotFillPinsTable();
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotFillPinsTable()
|
|
{
|
|
QString subcir_name = cbxSelectSubcir->currentText();
|
|
if (subcirPins.find(subcir_name) == subcirPins.end()) return;
|
|
QStringList pins = subcirPins[subcir_name];
|
|
tbwPinsTable->clearContents();
|
|
tbwPinsTable->setRowCount(pins.count());
|
|
for (int i = 0; i < pins.count(); i++) {
|
|
QTableWidgetItem *itm1 = new QTableWidgetItem(pins.at(i));
|
|
tbwPinsTable->setItem(i,0,itm1);
|
|
QTableWidgetItem *itm2 = new QTableWidgetItem("NC");
|
|
tbwPinsTable->setItem(i,1,itm2);
|
|
}
|
|
|
|
edtSPICE->clear();
|
|
edtSPICE->setPlainText(subcirSPICE[subcir_name]);
|
|
}
|
|
|
|
int SpiceLibCompDialog::parseLibFile(const QString &filename)
|
|
{
|
|
if (!QFileInfo::exists(filename)) return false;
|
|
QFile f(filename);
|
|
if (!f.open(QIODevice::ReadOnly)) {
|
|
QMessageBox::critical(this,tr("Error"),tr("Failed to open file: ") + filename);
|
|
return failedOpenFile;
|
|
}
|
|
|
|
subcirPins.clear();
|
|
subcirSPICE.clear();
|
|
QTextStream ts(&f);
|
|
|
|
bool subcir_start = false;
|
|
QString subname;
|
|
QString subcir_body;
|
|
while (!ts.atEnd()) {
|
|
QString line = ts.readLine();
|
|
line = line.trimmed();
|
|
line = line.toUpper();
|
|
if (line.startsWith(".SUBCKT")) {
|
|
subcir_start = true;
|
|
subcir_body.clear();
|
|
QStringList pin_names;
|
|
QStringList tokens = line.split(QRegularExpression("[ \\t]"),Qt::SkipEmptyParts);
|
|
if (tokens.count() > 3) {
|
|
subname = tokens.at(1);
|
|
} else continue;
|
|
tokens.removeFirst();
|
|
tokens.removeFirst();
|
|
for (const auto &s1: tokens) {
|
|
if (!s1.contains('=') && (s1 != "PARAMS:")) {
|
|
pin_names.append(s1);
|
|
}
|
|
}
|
|
subcirPins[subname] = pin_names;
|
|
}
|
|
if (subcir_start) {
|
|
subcir_body += line + "\n";
|
|
}
|
|
if (line == ".ENDS") {
|
|
subcir_start = false;
|
|
subcirSPICE[subname] = subcir_body;
|
|
}
|
|
}
|
|
|
|
f.close();
|
|
if (subcirPins.isEmpty()) {
|
|
return noSUBCKT;
|
|
}
|
|
return noError;
|
|
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotSetSymbol()
|
|
{
|
|
int result = -1;
|
|
if (rbAutoSymbol->isChecked()) {
|
|
tbwPinsTable->setEnabled(false);
|
|
listSymPattern->setEnabled(false);
|
|
edtSymFile->setEnabled(false);
|
|
btnOpenSym->setEnabled(false);
|
|
QString s1 = "";
|
|
QString s2 = "SpLib";
|
|
symbol->setSymbol(s1, s1, s2);
|
|
result = 0;
|
|
symbol->setWarning(tr("No symbol loaded"));
|
|
symbolPinsCount = 0;
|
|
} else if (rbSymFromTemplate->isChecked()) {
|
|
tbwPinsTable->setEnabled(true);
|
|
listSymPattern->setEnabled(true);
|
|
edtSymFile->setEnabled(false);
|
|
btnOpenSym->setEnabled(false);
|
|
QString dir_name = QucsSettings.BinDir + "/../share/" QUCS_NAME "/symbols/";
|
|
QString file = dir_name + listSymPattern->currentItem()->text() + ".sym";
|
|
result = symbol->loadSymFile(file);
|
|
symbolPinsCount = symbol->getPortsNumber();
|
|
} else if (rbUserSym->isChecked()) {
|
|
tbwPinsTable->setEnabled(true);
|
|
listSymPattern->setEnabled(false);
|
|
edtSymFile->setEnabled(true);
|
|
btnOpenSym->setEnabled(true);
|
|
if (edtSymFile->text().isEmpty()) {
|
|
symbolPinsCount = 0;
|
|
return;
|
|
}
|
|
result = symbol->loadSymFile(edtSymFile->text());
|
|
symbolPinsCount = symbol->getPortsNumber();
|
|
}
|
|
|
|
if (result < 0) {
|
|
QMessageBox::critical(this,tr("Error"),tr("Failed to load symbol file!"));
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < tbwPinsTable->rowCount(); i++) {
|
|
QTableWidgetItem *itm = new QTableWidgetItem("NC");
|
|
tbwPinsTable->setItem(i,1,itm);
|
|
}
|
|
isChanged = true;
|
|
btnApply->setEnabled(true);
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotTableCellDoubleClick()
|
|
{
|
|
int r = tbwPinsTable->currentRow();
|
|
int c = tbwPinsTable->currentColumn();
|
|
if (c == 0) return; // do not edit the forst column
|
|
QComboBox *cbxSelectPin = new QComboBox;
|
|
cbxSelectPin->addItem("NC");
|
|
for (int i = 1; i <= symbolPinsCount; i++) {
|
|
bool pinAssigned = false;
|
|
for(int j = 0; j < tbwPinsTable->rowCount(); j++) {
|
|
if (j == r) continue;
|
|
auto itm = tbwPinsTable->item(j,1);
|
|
if (itm == nullptr) continue;
|
|
QString s = itm->text();
|
|
if (s.isEmpty()) continue;
|
|
int pin_num = s.toInt();
|
|
if (pin_num == i) {
|
|
pinAssigned = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!pinAssigned) {
|
|
cbxSelectPin->addItem(QString::number(i));
|
|
}
|
|
}
|
|
tbwPinsTable->setCellWidget(r,c,cbxSelectPin);
|
|
connect(cbxSelectPin,SIGNAL(activated(int)),this,SLOT(slotSelectPin()));
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotSelectPin()
|
|
{
|
|
QComboBox *cbxSelectPin = qobject_cast<QComboBox*>(sender());
|
|
QString pin = cbxSelectPin->currentText();
|
|
int r = tbwPinsTable->currentRow();
|
|
int c = tbwPinsTable->currentColumn();
|
|
QTableWidgetItem *itm = new QTableWidgetItem(pin);
|
|
tbwPinsTable->removeCellWidget(r,c);
|
|
tbwPinsTable->setItem(r,c,itm);
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotBtnOpenLib()
|
|
{
|
|
QString s = QFileDialog::getOpenFileName(this, tr("Open SPICE library"),
|
|
lastLibDir,
|
|
tr("SPICE files (*.cir +.ckt *.sp *.lib)"));
|
|
if (!s.isEmpty()) {
|
|
QFileInfo inf(s);
|
|
lastLibDir = inf.absoluteDir().path();
|
|
edtLibPath->setText(s);
|
|
}
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotBtnOpenSym()
|
|
{
|
|
QString s = QFileDialog::getOpenFileName(this, tr("Open symbol file"),
|
|
lastSymbolDir,
|
|
tr("Schematic symbol (*.sym)"));
|
|
if (!s.isEmpty()) {
|
|
QFileInfo inf(s);
|
|
lastSymbolDir = inf.absoluteDir().path();
|
|
edtSymFile->setText(s);
|
|
}
|
|
}
|
|
|
|
bool SpiceLibCompDialog::setCompProps()
|
|
{
|
|
QStringList pins;
|
|
QString pin_string;
|
|
if (rbAutoSymbol->isChecked()) {
|
|
pin_string = "";
|
|
} else {
|
|
for (int i = 0; i < tbwPinsTable->rowCount(); i++) {
|
|
QTableWidgetItem *itm = tbwPinsTable->item(i,1);
|
|
if (itm == nullptr) continue;
|
|
QString s = itm->text();
|
|
if (s == "NC") {
|
|
QMessageBox::warning(this,tr("Warning"),tr("All pins must be assigned"));
|
|
return false;
|
|
}
|
|
pins.append(s);
|
|
}
|
|
pin_string = pins.join(";");
|
|
}
|
|
|
|
if (rbUserSym->isChecked() &&
|
|
!QFileInfo::exists(edtSymFile->text())) {
|
|
QMessageBox::warning(this,tr("Warning"),tr("Set a valid symbol file name"));
|
|
return false;
|
|
}
|
|
|
|
QString sch_dir = Doc->getFileInfo().absoluteDir().path();
|
|
QString libpath = edtLibPath->text();
|
|
QString sympath = edtSymFile->text();
|
|
if (libpath.startsWith(sch_dir)) {
|
|
libpath = QDir(sch_dir).relativeFilePath(libpath);
|
|
}
|
|
if (sympath.startsWith(sch_dir)) {
|
|
sympath = QDir(sch_dir).relativeFilePath(sympath);
|
|
}
|
|
|
|
Property *pp = comp->Props.first();
|
|
pp->Value = libpath;
|
|
pp->display = chbShowLib->isChecked();
|
|
pp = comp->Props.next();
|
|
pp->Value = cbxSelectSubcir->currentText();
|
|
pp->display = chbShowModel->isChecked();
|
|
pp = comp->Props.next();
|
|
if (rbAutoSymbol->isChecked()) {
|
|
pp->Value = "auto";
|
|
} else if (rbSymFromTemplate->isChecked()) {
|
|
pp->Value = listSymPattern->currentItem()->text();
|
|
} else if (rbUserSym->isChecked()) {
|
|
pp->Value = sympath;
|
|
}
|
|
pp = comp->Props.next();
|
|
pp->Value = edtParams->text();
|
|
pp->display = chbShowParams->isChecked();
|
|
pp = comp->Props.next();
|
|
pp->Value = QString(pin_string);
|
|
Doc->recreateComponent(comp);
|
|
Doc->viewport()->repaint();
|
|
Doc->setChanged(true,true);
|
|
return true;
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotBtnApply()
|
|
{
|
|
if (isChanged) {
|
|
if (libError) {
|
|
QMessageBox::critical(this,tr("Error"),
|
|
tr("There were library file parse error! Cannot apply changes."));
|
|
return;
|
|
}
|
|
if (setCompProps()) {
|
|
isChanged = false;
|
|
btnApply->setEnabled(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotBtnOK()
|
|
{
|
|
if (isChanged) {
|
|
if (libError) {
|
|
QMessageBox::critical(this,tr("Error"),
|
|
tr("There were library file parse error! Cannot apply changes."));
|
|
reject();
|
|
return;
|
|
}
|
|
if (setCompProps()) accept();
|
|
} else {
|
|
accept();
|
|
}
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotBtnCancel()
|
|
{
|
|
reject();
|
|
}
|
|
|
|
void SpiceLibCompDialog::slotChanged()
|
|
{
|
|
isChanged = true;
|
|
btnApply->setEnabled(true);
|
|
}
|