qucs.cpp
----------
begin : Thu Aug 28 2003
copyright : (C) 2003, 2004, 2005, 2006 by Michael Margraf
email : michael.margraf@alumni.tu-berlin.de
***************************************************************************/
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <QModelIndex>
#include <QAction>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QDockWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QListWidget>
#include <QComboBox>
#include <QMenu>
#include <QMessageBox>
#include <QFileDialog>
#include <QStatusBar>
#include <QShortcut>
#include <QApplication>
#include <QClipboard>
#include <QInputDialog>
#include <QDesktopServices>
#include <QFileSystemModel>
#include <QSortFilterProxyModel>
#include <QUrl>
#include <QSettings>
#include <QVariant>
#include <QDebug>
#include "main.h"
#include "qucs.h"
#include "qucsdoc.h"
#include "textdoc.h"
#include "schematic.h"
#include "mouseactions.h"
#include "messagedock.h"
#include "settings.h"
#include "wire.h"
#include "module.h"
#include "projectView.h"
#include "components/component.h"
#include "components/vacomponent.h"
#include "components/vhdlfile.h"
#include "components/verilogfile.h"
#include "components/subcircuit.h"
#include "dialogs/savedialog.h"
#include "dialogs/newprojdialog.h"
#include "dialogs/settingsdialog.h"
#include "dialogs/digisettingsdialog.h"
#include "dialogs/vasettingsdialog.h"
#include "dialogs/qucssettingsdialog.h"
#include "dialogs/searchdialog.h"
#include "dialogs/sweepdialog.h"
#include "dialogs/labeldialog.h"
#include "dialogs/matchdialog.h"
#include "dialogs/simmessage.h"
#include "dialogs/exportdialog.h"
#include "dialogs/displaydialog.h"
#include "extsimkernels/externsimdialog.h"
#include "dialogs/tuner.h"
#include "octave_window.h"
#include "printerwriter.h"
#include "imagewriter.h"
#include "qucslib_common.h"
#include "misc.h"
#include "extsimkernels/verilogawriter.h"
#include "extsimkernels/CdlNetlistWriter.h"
#include "extsimkernels/simsettingsdialog.h"
#include "symbolwidget.h"
QucsApp::QucsApp(bool netlist2Console) :
a_netlist2Console(netlist2Console)
{
windowTitle = misc::getWindowTitle();
setWindowTitle(windowTitle);
QucsSettings.hasDarkTheme = misc::isDarkTheme();
const auto geometry = _settings::Get().item<QByteArray>("MainWindowGeometry");
if (!geometry.isEmpty()) {
qDebug() << "Saved geometry is: " << geometry;
restoreGeometry(geometry);
}
else {
QSize size = QGuiApplication::primaryScreen()->size();
int w = size.width();
int h = size.height();
setGeometry (w * 0.25, h * 0.25, w * 0.5, h * 0.5);
}
QucsFileFilter =
tr("Schematic") + " (*.sch);;" +
tr("Symbol only") + " (*.sym);;" +
tr("Data Display") + " (*.dpl);;" +
tr("Qucs Documents") + " (*.sch *.dpl);;" +
tr("VHDL Sources") + " (*.vhdl *.vhd);;" +
tr("Verilog Sources") + " (*.v);;" +
tr("Verilog-A Sources") + " (*.va);;" +
tr("Octave Scripts") + " (*.m *.oct);;" +
tr("Spice Files") + QStringLiteral(" (") + QucsSettings.spiceExtensions.join(" ") + QStringLiteral(");;") +
tr("Any File")+" (*)";
MouseMoveAction = 0;
MousePressAction = 0;
MouseReleaseAction = 0;
MouseDoubleClickAction = 0;
initView();
initActions();
initMenuBar();
fillSimulatorsComboBox();
initToolBar();
initStatusBar();
viewBrowseDock->setChecked(true);
slotViewOctaveDock(false);
slotUpdateRecentFiles();
initCursorMenu();
SearchDia = new SearchDialog(this);
TuningMode = false;
Schematic *d = new Schematic(this, "");
int i = addDocumentTab(d);
DocumentTab->setCurrentIndex(i);
select->setChecked(true);
switchSchematicDoc(true);
QDir homeDir = QDir::homePath();
lastExportFilename = homeDir.absoluteFilePath("export.png");
for(int z=1; z<qApp->arguments().size(); z++) {
QString arg = qApp->arguments()[z];
QByteArray ba = arg.toLatin1();
const char *c_arg = ba.data();
if(*(c_arg) != '-') {
QFileInfo Info(arg);
QucsSettings.QucsWorkDir.setPath(Info.absoluteDir().absolutePath());
arg = QucsSettings.QucsWorkDir.filePath(Info.fileName());
gotoPage(arg);
}
}
QDir QucsBinDir(QucsSettings.BinDir);
if (QucsSettings.firstRun) {
QString ngspice_exe_name = "ngspice";
#ifdef Q_OS_WIN
ngspice_exe_name+="_con";
#endif
#ifdef Q_OS_MACOS
QString ngspice_exe1 = QStandardPaths::findExecutable(ngspice_exe_name,{"/opt/homebrew/bin"});
#else
QString ngspice_exe1 = QStandardPaths::findExecutable(ngspice_exe_name,{QucsBinDir.absolutePath()});
#endif
QString ngspice_exe2 = QStandardPaths::findExecutable(ngspice_exe_name);
QString qucsator_exe1 = QStandardPaths::findExecutable("qucsator_rf",{QucsBinDir.absolutePath()});
QString qucsator_exe2 = QStandardPaths::findExecutable("qucsator_rf");
QString ngspice_exe;
bool ngspice_found = false;
if(!ngspice_exe1.isEmpty()){
ngspice_found = true;
ngspice_exe = ngspice_exe1;
}else if(!ngspice_exe2.isEmpty()){
ngspice_found = true;
ngspice_exe = ngspice_exe2;
}
QString qucsator_exe;
bool qucsator_found = false;
if(!qucsator_exe1.isEmpty()){
qucsator_found = true;
qucsator_exe = qucsator_exe1;
}else if(!qucsator_exe2.isEmpty()){
qucsator_found = true;
qucsator_exe = qucsator_exe2;
}
QString info_string;
if (ngspice_found) {
QucsSettings.DefaultSimulator = spicecompat::simNgspice;
QucsSettings.NgspiceExecutable = ngspice_exe;
info_string += tr("Ngspice found at: ") + ngspice_exe + "\n";
}
if(qucsator_found){
QucsSettings.Qucsator = qucsator_exe;
info_string += tr("QucsatorRF found at: ") + qucsator_exe + "\n";
}
info_string += tr("\nYou can specify another location later"
" using Simulation->Simulators Setings\n");
if (!ngspice_found && qucsator_found) {
QucsSettings.DefaultSimulator = spicecompat::simQucsator;
info_string += tr("\nNOTE: Only QucsatorRF found. This simulator is not"
" recommended for general purpose schematics. "
" Please install Ngspice.");
}
if (ngspice_found || qucsator_found) {
QMessageBox::information(nullptr,tr("Set simulator"), info_string);
fillSimulatorsComboBox();
} else {
#ifdef Q_OS_WIN
QucsSettings.NgspiceExecutable = "ngspice_con.exe";
QucsSettings.Qucsator = "qucsator_rf.exe";
#else
QucsSettings.NgspiceExecutable = "ngspice";
QucsSettings.Qucsator = "qucsator_rf";
#endif
QMessageBox::information(this,tr("Qucs"),tr("No simulators found automatically. Please specify simulators"
" in the next dialog window."));
slotSimSettings();
}
QucsSettings.firstRun = false;
} else {
if (!QucsSettings.Qucsator.contains("qucsator_rf")) {
QucsSettings.Qucsator = QStandardPaths::findExecutable("qucsator_rf",{QucsBinDir.absolutePath()});
if(!QucsSettings.Qucsator.isEmpty()){
QMessageBox::information(this, "Qucs",
tr("QucsatorRF found at: ") + QucsSettings.Qucsator + "\n");
}
}
}
}
QucsApp::~QucsApp()
{
Module::unregisterModules ();
}
* @brief QucsApp::initView Setup the layour of all widgets
*/
void QucsApp::initView()
{
#ifndef __APPLE__
setWindowIcon (QPixmap(QStringLiteral(":/bitmaps/hicolor/scalable/apps/qucs.svg")));
#else
setStyleSheet("QToolButton { padding: 0px; }");
#endif
DocumentTab = new QTabWidget(this);
#if __APPLE__
DocumentTab->setDocumentMode(true);
#endif
setCentralWidget(DocumentTab);
connect(DocumentTab,
SIGNAL(currentChanged(int)), SLOT(slotChangeView()));
DocumentTab->setTabsClosable(true);
connect(DocumentTab,
SIGNAL(tabCloseRequested(int)), SLOT(slotFileClose(int)));
#ifdef HAVE_QTABWIDGET_SETMOVABLE
DocumentTab->setMovable (true);
#endif
dock = new QDockWidget(tr("Main Dock"),this);
TabView = new QTabWidget(dock);
TabView->setTabPosition(QTabWidget::West);
#if __APPLE__
TabView->setDocumentMode(true);
#endif
connect(dock, SIGNAL(visibilityChanged(bool)), SLOT(slotToggleDock(bool)));
view = new MouseActions(this);
editText = new QLineEdit(this);
editText->setFrame(false);
editText->setHidden(true);
QPalette p = palette();
p.setColor(backgroundRole(), QucsSettings.BGColor);
editText->setPalette(p);
connect(editText, SIGNAL(returnPressed()), SLOT(slotApplyCompText()));
connect(editText, SIGNAL(textChanged(const QString&)),
SLOT(slotResizePropEdit(const QString&)));
connect(editText, SIGNAL(editingFinished()), SLOT(slotHideEdit()));
QWidget *ProjGroup = new QWidget();
QVBoxLayout *ProjGroupLayout = new QVBoxLayout();
QWidget *ProjButts = new QWidget();
QPushButton *ProjNew = new QPushButton(tr("New"));
connect(ProjNew, SIGNAL(clicked()), SLOT(slotButtonProjNew()));
QPushButton *ProjOpen = new QPushButton(tr("Open"));
connect(ProjOpen, SIGNAL(clicked()), SLOT(slotButtonProjOpen()));
QPushButton *ProjDel = new QPushButton(tr("Delete"));
connect(ProjDel, SIGNAL(clicked()), SLOT(slotButtonProjDel()));
QHBoxLayout *ProjButtsLayout = new QHBoxLayout();
ProjButtsLayout->addWidget(ProjNew);
ProjButtsLayout->addWidget(ProjOpen);
ProjButtsLayout->addWidget(ProjDel);
ProjButts->setLayout(ProjButtsLayout);
#if __APPLE__
ProjGroupLayout->setContentsMargins(0, 0, 0, 0);
ProjGroupLayout->setSpacing(0);
ProjButtsLayout->setContentsMargins(5, 0, 5, 0);
ProjButtsLayout->setSpacing(5);
#endif
ProjGroupLayout->addWidget(ProjButts);
Projects = new QListView();
ProjGroupLayout->addWidget(Projects);
ProjGroup->setLayout(ProjGroupLayout);
TabView->addTab(ProjGroup, tr("Projects"));
TabView->setTabToolTip(TabView->indexOf(ProjGroup), tr("content of project directory"));
connect(Projects, SIGNAL(doubleClicked(const QModelIndex &)),
this, SLOT(slotListProjOpen(const QModelIndex &)));
Content = new ProjectView(this);
Content->setContextMenuPolicy(Qt::CustomContextMenu);
TabView->addTab(Content, tr("Content"));
TabView->setTabToolTip(TabView->indexOf(Content), tr("content of current project"));
connect(Content, SIGNAL(clicked(const QModelIndex &)),
SLOT(slotSelectSubcircuit(const QModelIndex &)));
connect(Content, SIGNAL(doubleClicked(const QModelIndex &)),
SLOT(slotOpenContent(const QModelIndex &)));
QWidget *CompGroup = new QWidget();
QVBoxLayout *CompGroupLayout = new QVBoxLayout();
QHBoxLayout *CompSearchLayout = new QHBoxLayout();
simulatorsCombobox = new QComboBox(this);
simulatorsCombobox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
connect(simulatorsCombobox, SIGNAL(activated(int)), SLOT(slotChangeSimulator(int)));
CompChoose = new QComboBox(this);
CompComps = new QListWidget(this);
CompComps->setViewMode(QListView::IconMode);
CompComps->setGridSize(QSize(110,90));
CompComps->setResizeMode(QListView::Adjust);
CompComps->setIconSize(QSize(64,64));
CompComps->setAcceptDrops(false);
const QString itemStyle = R"(
QListWidget {
background: white;
color: black;
outline: none;
padding: 0px;
margin: 0px;
}
QListWidget::item {
position: relative;
margin-top: 0px;
margin-left: 3px;
margin-right: 3px;
margin-bottom: 0px;
border: none;
border-radius: 2px;
outline: none;
background-color: transparent;
}
QListWidget::item:hover {
background-color: rgba(220, 242, 255, 0.75);
}
QListWidget::item:selected {
position: relative;
background-color: rgba(181, 227, 255, 0.75);
border: none;
color: black;
margin: 0px;
}
QListWidget::item:selected:focus {
position: relative;
background-color: rgba(181, 227, 255, 0.75);
border: none;
color: black;
margin: 0px;
}
QListWidget::item:selected:!focus {
position: relative;
background-color: rgba(222, 222, 223, 0.75);
border: none;
color: black;
margin: 0px;
}
QListWidget::item:selected:hover {
position: relative;
border: 1px solid black; /* Black border when selected and hovered */
padding-top:-1px;
}
QListWidget::item:focus {
outline: none; /* Remove the default focus outline */
}
)";
CompComps->setStyleSheet(itemStyle);
CompSearch = new QLineEdit(this);
CompSearch->setPlaceholderText(tr("Search Components"));
CompSearchClear = new QPushButton(tr("Clear"));
CompSearchLayout->addWidget(CompSearch);
CompSearchLayout->addWidget(CompSearchClear);
CompGroupLayout->setSpacing(5);
CompGroupLayout->addLayout(CompSearchLayout);
CompGroupLayout->addWidget(CompChoose);
CompGroupLayout->addWidget(CompComps);
CompGroup->setLayout(CompGroupLayout);
#if __APPLE__
CompGroupLayout->setContentsMargins(0, 0, 0, 0);
CompGroupLayout->setSpacing(0);
CompSearchLayout->setContentsMargins(5, 0, 5, 0);
CompSearchLayout->setSpacing(5);
#endif
TabView->addTab(CompGroup,tr("Components"));
TabView->setTabToolTip(TabView->indexOf(CompGroup), tr("components and diagrams"));
connect(CompChoose, SIGNAL(activated(int)), SLOT(slotSetCompView(int)));
connect(CompComps, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(slotSelectComponent(QListWidgetItem*)));
connect(CompComps, SIGNAL(itemPressed(QListWidgetItem*)), SLOT(slotSelectComponent(QListWidgetItem*)));
connect(CompSearch, SIGNAL(textEdited(const QString &)), SLOT(slotSearchComponent(const QString &)));
connect(CompSearchClear, SIGNAL(clicked()), SLOT(slotSearchClear()));
QWidget *LibGroup = new QWidget ();
QVBoxLayout *LibGroupLayout = new QVBoxLayout ();
LibCompSearch = new QLineEdit(this);
LibCompSearch->setPlaceholderText(tr("Search Lib Components"));
QPushButton *LibCompSearchClear = new QPushButton(tr("Clear"));
connect(LibCompSearch, SIGNAL(textEdited(const QString &)), SLOT(slotSearchLibComponent(const QString &)));
connect(LibCompSearchClear, SIGNAL(clicked()), SLOT(slotSearchLibClear()));
QHBoxLayout *LibCompSearchLayout = new QHBoxLayout;
LibCompSearchLayout->addWidget(LibCompSearch);
LibCompSearchLayout->addWidget(LibCompSearchClear);
LibGroupLayout->addLayout(LibCompSearchLayout);
#if __APPLE__
LibGroupLayout->setContentsMargins(0, 0, 0, 0);
LibGroupLayout->setSpacing(0);
LibCompSearchLayout->setContentsMargins(5, 0, 5, 0);
LibCompSearchLayout->setSpacing(5);
#endif
libTreeWidget = new QTreeWidget (this);
libTreeWidget->setColumnCount (1);
QStringList headers;
headers.clear ();
headers << tr ("Libraries");
libTreeWidget->setHeaderLabels (headers);
LibGroupLayout->addWidget (libTreeWidget,6);
btnShowModel = new QPushButton(tr("Show model"));
connect(btnShowModel,SIGNAL(clicked(bool)),this,SLOT(slotShowModel()));
LibGroupLayout->addWidget(btnShowModel);
CompDescr = new QTextEdit;
CompDescr->setReadOnly(true);
LibGroupLayout->addWidget(CompDescr,2);
Symbol = new SymbolWidget;
Symbol->disableDragNDrop();
LibGroupLayout->addWidget(Symbol);
LibGroup->setLayout (LibGroupLayout);
fillComboBox(true);
slotSetCompView(0);
TabView->addTab (LibGroup, tr("Libraries"));
TabView->setTabToolTip (TabView->indexOf (CompGroup), tr ("system and user component libraries"));
connect(libTreeWidget, SIGNAL(itemPressed (QTreeWidgetItem*, int)),
SLOT(slotSelectLibComponent (QTreeWidgetItem*)));
dock->setWidget(TabView);
dock->setAllowedAreas(Qt::LeftDockWidgetArea);
this->addDockWidget(Qt::LeftDockWidgetArea, dock);
TabView->setCurrentIndex(2);
#if __APPLE__
QWidgetList widgets = TabView->findChildren<QWidget*>();
foreach(QWidget* widget, widgets) {
widget->setAttribute(Qt::WA_MacShowFocusRect, false);
}
#endif
octDock = new QDockWidget(tr("Octave Dock"));
connect(octDock, SIGNAL(visibilityChanged(bool)), SLOT(slotToggleOctave(bool)));
octave = new OctaveWindow(octDock);
this->addDockWidget(Qt::BottomDockWidgetArea, octDock);
this->setCorner(Qt::BottomLeftCorner , Qt::LeftDockWidgetArea);
messageDock = new MessageDock(this);
a_homeDirModel = new QucsFileSystemModel(this);
a_proxyModel = new QucsSortFilterProxyModel();
a_homeDirModel->setFilter(QDir::NoDot | QDir::AllDirs);
QString path = QucsSettings.qucsWorkspaceDir.absolutePath();
QDir ProjDir(path);
QucsSettings.projsDir.setPath(path);
if(!ProjDir.exists()) {
if(!ProjDir.mkdir(path)) {
QMessageBox::warning(this, tr("Warning"),
tr("Cannot create work directory !"));
return;
}
}
readProjects();
}
void QucsApp::fillLibrariesTreeView ()
{
QList<QTreeWidgetItem *> topitems;
libTreeWidget->clear();
QTreeWidgetItem* newitem = new QTreeWidgetItem((QTreeWidget*)0, QStringList("System Libraries"));
newitem->setChildIndicatorPolicy (QTreeWidgetItem::DontShowIndicator);
QFont sectionFont = newitem->font(0);
sectionFont.setItalic (true);
sectionFont.setBold (true);
newitem->setFont (0, sectionFont);
topitems.append (newitem);
populateLibTreeFromDir(QucsSettings.LibDir, topitems, true);
newitem = new QTreeWidgetItem((QTreeWidget*)0, QStringList("User Libraries"));
newitem->setChildIndicatorPolicy (QTreeWidgetItem::DontShowIndicator);
newitem->setFont (0, sectionFont);
topitems.append (newitem);
QString UserLibDirPath = QucsSettings.qucsWorkspaceDir.canonicalPath () + "/user_lib/";
populateLibTreeFromDir(UserLibDirPath, topitems);
newitem = new QTreeWidgetItem((QTreeWidget*)0, QStringList("Project Libraries"));
newitem->setChildIndicatorPolicy (QTreeWidgetItem::DontShowIndicator);
newitem->setFont (0, sectionFont);
topitems.append (newitem);
if (!ProjName.isEmpty()) {
populateLibTreeFromDir(QucsSettings.QucsWorkDir.absolutePath(), topitems, true);
}
libTreeWidget->insertTopLevelItems(0, topitems);
}
bool QucsApp::populateLibTreeFromDir(const QString &LibDirPath, QList<QTreeWidgetItem *> &topitems, bool relpath)
{
QDir LibDir(LibDirPath);
QStringList LibFiles = LibDir.entryList(QStringList("*.lib"), QDir::Files, QDir::Name);
QStringList blacklist = getBlacklistedLibraries(QucsSettings.LibDir);
for (const QString& ss: blacklist) {
LibFiles.removeAll(ss);
}
for(auto it = LibFiles.begin(); it != LibFiles.end(); it++)
{
QString libPath(LibDir.absoluteFilePath(*it));
libPath.chop(4);
ComponentLibrary parsedlibrary;
int result = parseComponentLibrary (libPath , parsedlibrary, QUCS_COMP_LIB_FULL, relpath);
QStringList nameAndFileName;
nameAndFileName.append (parsedlibrary.name);
nameAndFileName.append (LibDirPath + *it);
QTreeWidgetItem* newlibitem = new QTreeWidgetItem((QTreeWidget*)nullptr, nameAndFileName);
switch (result)
{
case QUCS_COMP_LIB_IO_ERROR:
{
QString filename = getLibAbsPath(libPath);
QMessageBox::critical(nullptr, tr ("Error"), tr("Cannot open \"%1\".").arg (filename));
return false;
}
case QUCS_COMP_LIB_CORRUPT:
QMessageBox::critical(nullptr, tr("Error"), tr("Library is corrupt."));
return false;
default:
break;
}
for (int i = 0; i < parsedlibrary.components.count (); i++)
{
QStringList compNameAndDefinition;
compNameAndDefinition.append (parsedlibrary.components[i].name);
QString s = "<Qucs Schematic " PACKAGE_VERSION ">\n";
s += "<Components>\n " +
parsedlibrary.components[i].modelString + "\n" +
"</Components>\n";
compNameAndDefinition.append (s);
compNameAndDefinition.append(parsedlibrary.components[i].definition);
compNameAndDefinition.append(libPath);
QTreeWidgetItem* newcompitem = new QTreeWidgetItem(newlibitem, compNameAndDefinition);
Q_UNUSED( newcompitem )
}
topitems.append (newlibitem);
}
return true;
}
QucsDoc* QucsApp::getDoc(int No)
{
QWidget *w;
if(No < 0)
w = DocumentTab->currentWidget();
else
w = DocumentTab->widget(No);
if(w) {
if(isTextDocument (w))
return (QucsDoc*) ((TextDoc*)w);
else
return (QucsDoc*) ((Schematic*)w);
}
return 0;
}
QucsDoc * QucsApp::findDoc (QString File, int * Pos)
{
QucsDoc * d;
int No = 0;
File = QDir::toNativeSeparators (File);
while ((d = getDoc (No++)) != 0)
if (QDir::toNativeSeparators (d->getDocName()) == File) {
if (Pos) *Pos = No - 1;
return d;
}
return 0;
}
int QucsApp::fillComboBox(bool setAll) {
fillLibrariesTreeView();
auto currentText = CompChoose->currentText();
CompChoose->clear();
CompSearch->clear();
Module::unregisterModules();
Module::registerModules();
Module::registerDynamicComponents();
int idx = 0;
if (!setAll) {
CompChoose->insertItem(CompChoose->count(), QObject::tr("paintings"));
} else {
QStringList cats = Category::getCategories();
for (const QString &it: cats) {
CompChoose->insertItem(CompChoose->count(), it);
}
idx = CompChoose->findText(currentText);
}
return idx == -1 ? 0 : idx;
}
void QucsApp::fillSimulatorsComboBox() {
simulatorsCombobox->clear();
if (misc::simulatorExists(QucsSettings.NgspiceExecutable)) {
QucsSettings.NgspiceExecutable = misc::unwrapExePath(QucsSettings.NgspiceExecutable);
simulatorsCombobox->addItem(spicecompat::getDefaultSimulatorName(spicecompat::simNgspice), 1);
}
if (misc::simulatorExists(QucsSettings.XyceExecutable)) {
QucsSettings.XyceExecutable = misc::unwrapExePath(QucsSettings.XyceExecutable);
simulatorsCombobox->addItem(spicecompat::getDefaultSimulatorName(spicecompat::simXyce), 2);
}
if (misc::simulatorExists(QucsSettings.SpiceOpusExecutable)) {
QucsSettings.SpiceOpusExecutable = misc::unwrapExePath(QucsSettings.SpiceOpusExecutable);
simulatorsCombobox->addItem(spicecompat::getDefaultSimulatorName(spicecompat::simSpiceOpus), 4);
}
if (misc::simulatorExists(QucsSettings.Qucsator)) {
QucsSettings.Qucsator = misc::unwrapExePath(QucsSettings.Qucsator);
simulatorsCombobox->addItem(spicecompat::getDefaultSimulatorName(spicecompat::simQucsator), 8);
}
bool anySimulatorsFound = simulatorsCombobox->count() > 0;
if (anySimulatorsFound) {
QString current = spicecompat::getDefaultSimulatorName(QucsSettings.DefaultSimulator);
int idx = simulatorsCombobox->findText(current);
idx = idx < 0 ? 0 : idx;
simulatorsCombobox->setCurrentIndex(idx);
QucsSettings.DefaultSimulator = simulatorsCombobox->itemData(idx).toInt();
} else {
QucsSettings.DefaultSimulator = spicecompat::simNotSpecified;
}
simulate->setEnabled(anySimulatorsFound);
simulatorsCombobox->setEnabled(anySimulatorsFound);
}
void QucsApp::slotChangeSimulator(int index) {
int simu = spicecompat::simNotSpecified;
int idx = simulatorsCombobox->itemData(index).toInt();
switch (idx) {
case 1:
simu = spicecompat::simNgspice;
break;
case 2:
simu = spicecompat::simXyce;
break;
case 4:
simu = spicecompat::simSpiceOpus;
break;
case 8:
simu = spicecompat::simQucsator;
break;
default:
break;
}
if (QucsSettings.DefaultSimulator == simu) {
return;
}
QucsSettings.DefaultSimulator = simu;
saveApplSettings();
int idx1 = fillComboBox(true);
slotSetCompView(idx1);
QString tabType = DocumentTab->currentWidget()->metaObject()->className();
if (tabType == "Schematic") {
((Q3ScrollView*)DocumentTab->currentWidget())->viewport()->update();
}
}
void QucsApp::slotSetCompView (int index)
{
editText->setHidden (true);
QList<Module *> Comps;
if (CompChoose->count () <= 0) return;
if (CompChoose->itemText(0) == tr("Search results")) {
if (index == 0)
return;
CompChoose->removeItem(0);
CompSearch->clear();
--index;
}
while (CompComps->count() > 0) {
QListWidgetItem * item = CompComps->takeItem(0);
if (item != nullptr) {
delete item;
}
}
CompComps->clear ();
CompChoose->setCurrentIndex(index);
int compIdx;
QString item = CompChoose->itemText (index);
int catIdx = Category::getModulesNr(item);
Comps = Category::getModules(item);
QString Name;
pInfoFunc Infos = 0;
if (item == QObject::tr("verilog-a user devices")) {
compIdx = 0;
QMapIterator<QString, QString> i(Module::vaComponents);
while (i.hasNext()) {
i.next();
QString vaBitmap;
Component * c = (Component *)
vacomponent::info (Name, vaBitmap, false, i.value());
if (c) delete c;
QString iconPath = QucsSettings.QucsWorkDir.filePath(vaBitmap+".png");
QFile iconFile(iconPath);
QPixmap vaIcon;
if(iconFile.exists())
{
vaIcon = QPixmap(iconPath);
}
else
{
QMessageBox::information(this, tr("Info"),
tr("Default icon not found:\n %1.png").arg(vaBitmap));
vaIcon = QPixmap(":/bitmaps/editdelete.png");
}
QListWidgetItem *icon = new QListWidgetItem(vaIcon, Name);
icon->setToolTip(Name);
icon->setData(Qt::UserRole + 1, catIdx);
icon->setData(Qt::UserRole + 2, compIdx);
CompComps->addItem(icon);
compIdx++;
}
} else {
char * File;
compIdx = 0;
QList<Module *>::const_iterator it;
for (it = Comps.constBegin(); it != Comps.constEnd(); it++) {
Infos = (*it)->info;
if (Infos) {
Component* c = (Component*)Infos(Name, File, true);
if (c) delete c;
QString icon_path = misc::getIconPath(QString (File));
QListWidgetItem *icon = new QListWidgetItem(Name);
if (QFileInfo::exists(icon_path)) {
icon->setIcon(QPixmap(icon_path));
} else {
icon->setIcon(*(*it)->icon);
}
icon->setToolTip(Name);
icon->setData(Qt::UserRole + 1, catIdx);
icon->setData(Qt::UserRole + 2, compIdx);
CompComps->addItem(icon);
}
compIdx++;
}
}
}
void QucsApp::slotSearchComponent(const QString &searchText)
{
CompComps->clear ();
if (CompChoose->itemText(0) != tr("Search results")) {
ccCurIdx = CompChoose->currentIndex();
CompChoose->insertItem(-1, tr("Search results"));
CompChoose->setCurrentIndex(0);
}
if (searchText.isEmpty()) {
slotSetCompView(CompChoose->currentIndex());
} else {
CompChoose->setCurrentIndex(0);
editText->setHidden (true);
QString Name;
char * File;
QList<Module *> Comps;
QStringList cats = Category::getCategories ();
int catIdx = 0;
for (const QString& it : cats) {
Comps = Category::getModules(it);
QList<Module *>::const_iterator modit;
int compIdx = 0;
for (modit = Comps.constBegin(); modit != Comps.constEnd(); modit++) {
if ((*modit)->info) {
(void) *((*modit)->info) (Name, File, false);
if((Name.indexOf(searchText, 0, Qt::CaseInsensitive)) != -1) {
QString icon_path = misc::getIconPath(QString (File));
QListWidgetItem *icon = new QListWidgetItem(Name);
if (QFileInfo::exists(icon_path)) {
icon->setIcon(QPixmap(icon_path));
} else {
icon->setIcon(*(*modit)->icon);
}
icon->setToolTip(it + ": " + Name);
icon->setData(Qt::UserRole + 1, catIdx);
icon->setData(Qt::UserRole + 2, compIdx);
CompComps->addItem(icon);
}
}
compIdx++;
}
catIdx++;
}
QMapIterator<QString, QString> i(Module::vaComponents);
int compIdx = 0;
while (i.hasNext()) {
i.next();
QString vaName, vaBitmap;
vacomponent::info (vaName, vaBitmap, false, i.value());
if((vaName.indexOf(searchText, 0, Qt::CaseInsensitive)) != -1) {
QString iconPath = QucsSettings.QucsWorkDir.filePath(vaBitmap+".png");
QFile iconFile(iconPath);
QPixmap vaIcon;
if(iconFile.exists())
{
vaIcon = QPixmap(iconPath);
}
else
{
vaIcon = QPixmap(":/bitmaps/editdelete.png");
}
QListWidgetItem *icon = new QListWidgetItem(vaIcon, vaName);
icon->setToolTip(tr("verilog-a user devices") + ": " + vaName);
icon->setData(Qt::UserRole + 1, catIdx-1);
icon->setData(Qt::UserRole + 2, compIdx);
CompComps->addItem(icon);
}
compIdx++;
}
}
}
void QucsApp::slotSearchClear()
{
if (CompChoose->itemText(0) == tr("Search results")) {
CompChoose->removeItem(0);
CompSearch->clear();
slotSetCompView(ccCurIdx);
}
}
void QucsApp::slotSelectComponent(QListWidgetItem *item)
{
slotHideEdit();
if(view->selElem != 0) delete view->selElem;
view->selElem = 0;
if(item == 0) {
CompComps->clearSelection();
return;
}
if(activeAction) {
activeAction->blockSignals(true);
activeAction->setChecked(false);
activeAction->blockSignals(false);
}
activeAction = 0;
MouseMoveAction = &MouseActions::MMoveElement;
MousePressAction = &MouseActions::MPressElement;
MouseReleaseAction = 0;
MouseDoubleClickAction = 0;
pInfoFunc Infos = 0;
pInfoVAFunc InfosVA = 0;
int i = CompComps->row(item);
QList<Module *> Comps;
Comps = Category::getModules(CompChoose->currentText());
QString name = CompComps->item(i)->text();
QString CompName;
QString CompFile_qstr;
char *CompFile_cptr;
qDebug() << "pressed CompComps id" << i << name;
int catIdx = CompComps->item(i)->data(Qt::UserRole + 1).toInt();
int compIdx = CompComps->item(i)->data(Qt::UserRole + 2).toInt();
qDebug() << "slotSelectComponent()" << catIdx << compIdx;
Category* cat = Category::Categories.at(catIdx);
Module *mod = cat->Content.at(compIdx);
qDebug() << "mod->info" << mod->info;
qDebug() << "mod->infoVA" << mod->infoVA;
Infos = mod->info;
if (Infos) {
view->selElem = (*mod->info) (CompName, CompFile_cptr, true);
} else {
InfosVA = mod->infoVA;
QString filename = Module::vaComponents[name];
if (InfosVA) {
view->selElem = (*InfosVA) (CompName, CompFile_qstr, true, filename);
}
}
if (CompChoose->itemText(0) == tr("Search results")) {
if (Infos || InfosVA) {
CompChoose->setCurrentIndex(catIdx+1);
ccCurIdx = catIdx;
}
}
}
void QucsApp::initCursorMenu()
{
ContentMenu = new QMenu(this);
#define APPEND_MENU(action, slot, text) \
{ \
action = new QAction(tr(text), ContentMenu); \
connect(action, SIGNAL(triggered()), SLOT(slot())); \
ContentMenu->addAction(action); \
}
APPEND_MENU(ActionCMenuOpen, slotCMenuOpen, "Open")
APPEND_MENU(ActionCMenuCopy, slotCMenuCopy, "Duplicate")
APPEND_MENU(ActionCMenuRename, slotCMenuRename, "Rename")
APPEND_MENU(ActionCMenuDelete, slotCMenuDelete, "Delete")
APPEND_MENU(ActionCMenuInsert, slotCMenuInsert, "Insert")
#undef APPEND_MENU
connect(Content, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(slotShowContentMenu(const QPoint&)));
}
void QucsApp::slotShowContentMenu(const QPoint& pos)
{
QModelIndex idx = Content->indexAt(pos);
if (idx.isValid() && idx.parent().isValid()) {
ActionCMenuInsert->setVisible(
idx.sibling(idx.row(), 1).data().toString().contains(tr("-port"))
);
ContentMenu->popup(Content->mapToGlobal(pos));
}
}
QString QucsApp::fileType (const QString& Ext)
{
QString Type = tr("unknown");
if (Ext == "v")
Type = tr("Verilog source");
else if (Ext == "va")
Type = tr("Verilog-A source");
else if (Ext == "vhd" || Ext == "vhdl")
Type = tr("VHDL source");
else if (Ext == "dat")
Type = tr("data file");
else if (Ext == "dpl")
Type = tr("data display");
else if (Ext == "sch")
Type = tr("schematic");
else if (Ext == "sym")
Type = tr("symbol");
else if (Ext == "vhdl.cfg" || Ext == "vhd.cfg")
Type = tr("VHDL configuration");
else if (Ext == "cfg")
Type = tr("configuration");
return Type;
}
void QucsApp::slotCMenuOpen()
{
slotOpenContent(Content->currentIndex());
}
void QucsApp::slotCMenuCopy()
{
QModelIndex idx = Content->currentIndex();
if (!idx.isValid() || !idx.parent().isValid()) { return; }
QString filename = idx.sibling(idx.row(), 0).data().toString();
QDir dir(QucsSettings.QucsWorkDir);
QString file(dir.filePath(filename));
QFileInfo fileinfo(file);
int z = 0;
QucsDoc *d = findDoc(file, &z);
if (d != NULL && d->getDocChanged()) {
DocumentTab->setCurrentIndex(z);
int ret = QMessageBox::question(this, tr("Copying Qucs document"),
tr("The document contains unsaved changes!\n") +
tr("Do you want to save the changes before copying?"),
QMessageBox::Ignore|QMessageBox::Save);
if (ret == QMessageBox::Save) {
d->save();
}
}
QString suffix = fileinfo.suffix();
QString base = fileinfo.completeBaseName();
if(base.isEmpty()) {
base = filename;
}
bool exists = true;
int i = 0;
QString defaultName;
while (exists) {
++i;
defaultName = base + "_copy" + QString::number(i) + "." + suffix;
exists = QFile::exists(dir.filePath(defaultName));
}
bool ok;
QString s = QInputDialog::getText(this, tr("Copy file"), tr("Enter new name:"), QLineEdit::Normal, defaultName, &ok);
if(ok && !s.isEmpty()) {
if (!s.endsWith(suffix)) {
s += QStringLiteral(".") + suffix;
}
if (QFile::exists(dir.filePath(s))) {
QMessageBox::critical(this, tr("error"), tr("Cannot copy file to identical name: %1").arg(filename));
return;
}
if (!QFile::copy(dir.filePath(filename), dir.filePath(s))) {
QMessageBox::critical(this, tr("Error"), tr("Cannot copy schematic: %1").arg(filename));
return;
}
slotUpdateTreeview();
}
}
void QucsApp::slotCMenuRename()
{
QModelIndex idx = Content->currentIndex();
if (!idx.isValid() || !idx.parent().isValid()) { return; }
QString filename = idx.sibling(idx.row(), 0).data().toString();
QString file(QucsSettings.QucsWorkDir.filePath(filename));
QFileInfo fileinfo(file);
if (findDoc(file)) {
QMessageBox::critical(this, tr("Error"),
tr("Cannot rename an open file!"));
return;
}
QString suffix = fileinfo.suffix();
QString base = fileinfo.completeBaseName();
if(base.isEmpty()) {
base = filename;
}
bool ok;
QString s = QInputDialog::getText(this, tr("Rename file"), tr("Enter new filename:"), QLineEdit::Normal, base, &ok);
if(ok && !s.isEmpty()) {
if (!s.endsWith(suffix)) {
s += QStringLiteral(".") + suffix;
}
QDir dir(QucsSettings.QucsWorkDir.path());
if(!dir.rename(filename, s)) {
QMessageBox::critical(this, tr("Error"), tr("Cannot rename file: %1").arg(filename));
return;
}
slotUpdateTreeview();
}
}
void QucsApp::slotCMenuDelete()
{
QModelIndex idx = Content->currentIndex();
if (!idx.isValid() || !idx.parent().isValid()) { return; }
QString filename = idx.sibling(idx.row(), 0).data().toString();
QString file(QucsSettings.QucsWorkDir.filePath(filename));
if (findDoc (file)) {
QMessageBox::critical(this, tr("Error"), tr("Cannot delete an open file!"));
return;
}
int No;
No = QMessageBox::warning(this, tr("Warning"),
tr("This will delete the file permanently! Continue ?"),
QMessageBox::No | QMessageBox::Yes);
if(No == QMessageBox::Yes) {
if(!QFile::remove(file)) {
QMessageBox::critical(this, tr("Error"),
tr("Cannot delete file: %1").arg(filename));
return;
}
}
slotUpdateTreeview();
}
void QucsApp::slotCMenuInsert()
{
slotSelectSubcircuit(Content->currentIndex());
}
void QucsApp::readProjects()
{
QString path = QucsSettings.projsDir.absolutePath();
QString homepath = QucsSettings.qucsWorkspaceDir.absolutePath();
if (path == homepath) {
a_homeDirModel->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs);
} else {
a_homeDirModel->setFilter(QDir::NoDot | QDir::AllDirs);
}
QModelIndex rootModelIndex = a_homeDirModel->setRootPath(path);
a_proxyModel->setSourceModel(a_homeDirModel);
a_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
a_proxyModel->sort(0);
Projects->setModel(a_proxyModel);
Projects->setRootIndex(a_proxyModel->mapFromSource(rootModelIndex));
}
void QucsApp::slotButtonProjNew()
{
slotHideEdit();
NewProjDialog *d = new NewProjDialog(this);
if(d->exec() != QDialog::Accepted) return;
QDir projDir(QucsSettings.projsDir.path());
QString name = d->ProjName->text();
bool open = d->OpenProj->isChecked();
if (!name.endsWith("_prj")) {
name += "_prj";
}
if(!projDir.mkdir(name)) {
QMessageBox::information(this, tr("Info"),
tr("Cannot create project directory !"));
}
if(open) {
openProject(QucsSettings.projsDir.filePath(name));
}
}
void QucsApp::openProject(const QString& Path)
{
slotHideEdit();
QDir ProjDir(QDir::cleanPath(Path));
QString openProjName = ProjDir.dirName();
if(!ProjDir.exists() || !ProjDir.isReadable()) {
QMessageBox::critical(this, tr("Error"),
tr("Cannot access project directory: %1").arg(Path));
return;
}
if (!openProjName.endsWith("_prj")) {
QMessageBox::critical(this, tr("Error"),
tr("Project directory name does not end in '_prj'(%1)").arg(openProjName));
return;
}
if(!closeAllFiles()) return;
Schematic *d = new Schematic(this, "");
int i = addDocumentTab(d);
DocumentTab->setCurrentIndex(i);
slotResetWarnings();
QucsSettings.QucsWorkDir.setPath(ProjDir.path());
octave->adjustDirectory();
Content->setProjPath(QucsSettings.QucsWorkDir.absolutePath());
TabView->setCurrentIndex(1);
openProjName.chop(4);
ProjName = openProjName;
QDir parentDir = QucsSettings.QucsWorkDir;
parentDir.cdUp();
setWindowTitle( tr("Project: ") + ProjName + " (" + parentDir.absolutePath() + ") - " + windowTitle);
fillLibrariesTreeView();
}
void QucsApp::slotMenuProjOpen()
{
QString d = QFileDialog::getExistingDirectory(
this, tr("Choose Project Directory for Opening"),
QucsSettings.qucsWorkspaceDir.path(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if(d.isEmpty()) return;
openProject(d);
}
void QucsApp::slotButtonProjOpen()
{
slotHideEdit();
QModelIndex idx = Projects->currentIndex();
if (!idx.isValid()) {
QMessageBox::information(this, tr("Info"),
tr("No project is selected !"));
} else {
slotListProjOpen(idx);
}
}
void QucsApp::slotListProjOpen(const QModelIndex &idx)
{
QString dName = idx.data().toString();
if (dName.endsWith("_prj")) {
openProject(QucsSettings.projsDir.filePath(dName));
} else {
QucsSettings.projsDir.setPath(QucsSettings.projsDir.filePath(dName));
readProjects();
}
}
void QucsApp::slotMenuProjClose()
{
slotHideEdit();
if(!closeAllFiles()) return;
Schematic *d = new Schematic(this, "");
int i = addDocumentTab(d);
DocumentTab->setCurrentIndex(i);
slotResetWarnings();
setWindowTitle(windowTitle);
QucsSettings.QucsWorkDir.setPath(QucsSettings.qucsWorkspaceDir.absolutePath());
octave->adjustDirectory();
Content->setProjPath("");
TabView->setCurrentIndex(0);
ProjName = "";
fillLibrariesTreeView();
}
bool QucsApp::recurRemove(const QString &Path)
{
QDir projDir = QDir(Path);
return projDir.removeRecursively();
}
bool QucsApp::deleteProject(const QString& Path)
{
slotHideEdit();
if(Path.isEmpty()) return false;
QString delProjName = QDir(Path).dirName();
if (!delProjName.endsWith("_prj")) {
QMessageBox::critical(this, tr("Error"),
tr("Project directory name does not end in '_prj' (%1)").arg(delProjName));
return false;
}
delProjName.chop(4);
if(delProjName == ProjName) {
QMessageBox::information(this, tr("Info"),
tr("Cannot delete an open project !"));
return false;
}
if(QMessageBox::warning(this, tr("Warning"),
tr("This will destroy all the project files permanently ! Continue ?"),
QMessageBox::Yes|QMessageBox::No) == QMessageBox::No) return false;
if (!recurRemove(Path)) {
QMessageBox::information(this, tr("Info"),
tr("Cannot remove project directory!"));
return false;
}
return true;
}
void QucsApp::slotMenuProjDel()
{
QString d = QFileDialog::getExistingDirectory(
this, tr("Choose Project Directory for Deleting"),
QucsSettings.qucsWorkspaceDir.path(),
QFileDialog::ShowDirsOnly
| QFileDialog::DontResolveSymlinks);
deleteProject(d);
}
void QucsApp::slotButtonProjDel()
{
QModelIndex idx = Projects->currentIndex();
if(!idx.isValid()) {
QMessageBox::information(this, tr("Info"),
tr("No project is selected!"));
return;
}
deleteProject(QucsSettings.projsDir.filePath(idx.data().toString()));
}
void QucsApp::slotFileNew()
{
statusBar()->showMessage(tr("Creating new schematic..."));
slotHideEdit();
Schematic *d = new Schematic(this, "");
int i = addDocumentTab(d);
DocumentTab->setCurrentIndex(i);
statusBar()->showMessage(tr("Ready."));
}
void QucsApp::slotSymbolNew()
{
statusBar()->showMessage(tr("Creating new schematic..."));
slotHideEdit();
Schematic *d = new Schematic(this, "");
int i = addDocumentTab(d);
DocumentTab->setCurrentIndex(i);
slotSymbolEdit();
d->setIsSymbolOnly(true);
statusBar()->showMessage(tr("Ready."));
}
void QucsApp::slotTextNew()
{
statusBar()->showMessage(tr("Creating new text editor..."));
slotHideEdit();
TextDoc *d = new TextDoc(this, "");
int i = addDocumentTab(d);
DocumentTab->setCurrentIndex(i);
statusBar()->showMessage(tr("Ready."));
}
bool QucsApp::gotoPage(const QString& Name)
{
int No = DocumentTab->currentIndex();
int i = 0;
QucsDoc * d = findDoc (Name, &i);
if(d) {
d->becomeCurrent(true);
DocumentTab->setCurrentIndex(i);
return true;
}
QFileInfo Info(Name);
bool is_sch = false;
if(Info.suffix() == "sch" || Info.suffix() == "dpl" ||
Info.suffix() == "sym") {
d = new Schematic(this, Name);
i = addDocumentTab((Schematic *)d, Info.fileName());
is_sch = true;
}
else {
d = new TextDoc(this, Name);
i = addDocumentTab((TextDoc *)d, Info.fileName());
}
DocumentTab->setCurrentIndex(i);
if (!Info.isWritable()) {
QMessageBox::warning(this,tr("Open file"),
tr("Document opened in read-only mode! "
"Simulation will not work. Please copy the document "
"to the directory where you have write permission!"));
statusBar()->showMessage("Read only");
}
if(!d->load()) {
delete d;
DocumentTab->setCurrentIndex(No);
return false;
}
slotChangeView();
if (Info.suffix() == "sym") {
Schematic *sch = (Schematic *)d;
slotSymbolEdit();
sch->setIsSymbolOnly(true);
} else if (is_sch) {
Schematic *sch = (Schematic *)d;
if (sch->checkDplAndDatNames()) sch->setChanged(true,true);
}
if(getDoc(0)->getDocName().isEmpty())
if(!getDoc(0)->getDocChanged())
delete DocumentTab->widget(0);
return true;
}
QString lastDirOpenSave;
void QucsApp::slotFileOpen()
{
slotHideEdit();
statusBar()->showMessage(tr("Opening file..."));
QString s = QFileDialog::getOpenFileName(this, tr("Enter a Schematic Name"),
lastDirOpenSave.isEmpty() ? QString(QDir::homePath()) : lastDirOpenSave, QucsFileFilter);
if(s.isEmpty())
statusBar()->showMessage(tr("Opening aborted"), 2000);
else {
updateRecentFilesList(s);
gotoPage(s);
lastDirOpenSave = s;
statusBar()->showMessage(tr("Ready."));
}
}
bool QucsApp::saveFile(QucsDoc *Doc)
{
if(!Doc)
Doc = getDoc();
if(Doc->getDocName().isEmpty())
return saveAs();
int Result = Doc->save();
if(Result < 0) return false;
if (!Doc->getDocName().endsWith(".sym")) {
updatePortNumber(Doc, Result);
}
slotUpdateTreeview();
return true;
}
void QucsApp::slotFileSave()
{
statusBar()->showMessage(tr("Saving file..."));
DocumentTab->blockSignals(true);
slotHideEdit();
if(!saveFile()) {
DocumentTab->blockSignals(false);
statusBar()->showMessage(tr("Saving aborted"), 2000);
statusBar()->showMessage(tr("Ready."));
return;
}
DocumentTab->blockSignals(false);
statusBar()->showMessage(tr("Ready."));
if(!ProjName.isEmpty())
slotUpdateTreeview();
}
bool QucsApp::saveAs()
{
QWidget *w = DocumentTab->currentWidget();
QucsDoc *Doc = getDoc();
int n = -1;
QString s, Filter, selfilter;
QStringList Filters;
QFileInfo Info;
while(true) {
QString file_ext;
s = Doc->getDocName();
Info.setFile(s);
if(s.isEmpty()) {
if(ProjName.isEmpty()) {
if(lastDirOpenSave.isEmpty()) s = QDir::homePath();
else s = lastDirOpenSave;
}
else s = QucsSettings.QucsWorkDir.path();
} else {
file_ext = QString("*.") + Info.suffix();
}
QString ext = "vhdl;vhd;v;va;sch;dpl;m;oct;net;qnet;ckt;cir;sp;txt;sym";
QStringList extlist = ext.split (';');
if(isTextDocument (w)) {
Filters << tr("VHDL Sources")+" (*.vhdl *.vhd);;"
<< tr("Verilog Sources")+" (*.v);;"
<< tr("Verilog-A Sources")+" (*.va);;"
<< tr("Octave Scripts")+" (*.m *.oct);;"
<< tr("Qucs Netlist")+" (*.net *.qnet);;"
<< tr("SPICE Netlist")+" (*.ckt *.cir *.sp);;"
<< tr("Plain Text")+" (*.txt);;"
<< tr("Any File")+" (*)";
Filter = Filters.join("");
bool found = false;
for (const auto &ss: Filters) {
if (ss.contains(file_ext)) {
found = true;
selfilter = ss;
selfilter.chop(2);
break;
}
}
if (!found) {
selfilter = Filters.first();
}
} else {
Schematic *sch = (Schematic *) Doc;
if (sch->getIsSymbolOnly()) {
Filter = tr("Subcircuit symbol") + "(*.sym)";
selfilter = tr("Subcircuit symbol") + "(*.sym)";
} else {
Filter = QucsFileFilter;
selfilter = tr("Schematic") + " (*.sch)";
}
}
s = QFileDialog::getSaveFileName(this, tr("Enter a Document Name"),
s, Filter, &selfilter);
if(s.isEmpty()) return false;
Info.setFile(s);
ext = Info.suffix();
if(ext.isEmpty() || !extlist.contains(ext))
{
if (!isTextDocument (w))
{
s += ".sch";
}
}
Info.setFile(s);
if(QFile::exists(s)) {
n = QMessageBox::warning(this, tr("Warning"),
tr("The file '")+Info.fileName()+tr("' already exists!\n")+
tr("Saving will overwrite the old one! Continue?"),
QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,QMessageBox::Cancel);
if(n == QMessageBox::Cancel) return false;
if(n == QMessageBox::No) continue;
}
QucsDoc * d = findDoc (s);
if(d) {
QMessageBox::information(this, tr("Info"),
tr("Cannot overwrite an open document"));
return false;
}
break;
}
Doc->setName(s);
DocumentTab->setTabText(DocumentTab->indexOf(w), misc::properFileName(s));
lastDirOpenSave = Info.absolutePath();
n = Doc->save();
if(n < 0) return false;
if (!Doc->getDocName().endsWith(".sym")) {
updatePortNumber(Doc, n);
}
slotUpdateTreeview();
updateRecentFilesList(s);
return true;
}
void QucsApp::slotFileSaveAs()
{
statusBar()->showMessage(tr("Saving file under new filename..."));
DocumentTab->blockSignals(true);
slotHideEdit();
if(!saveAs()) {
DocumentTab->blockSignals(false);
statusBar()->showMessage(tr("Saving aborted"), 3000);
statusBar()->showMessage(tr("Ready."));
return;
}
DocumentTab->blockSignals(false);
statusBar()->showMessage(tr("Ready."));
slotRefreshSchPath();
if(!ProjName.isEmpty())
slotUpdateTreeview();
}
void QucsApp::slotFileSaveAll()
{
statusBar()->showMessage(tr("Saving all files..."));
slotHideEdit();
DocumentTab->blockSignals(true);
int No=0;
QucsDoc *Doc;
while((Doc=getDoc(No++)) != 0) {
if(Doc->getDocName().isEmpty())
DocumentTab->setCurrentIndex(No-1);
if (saveFile(Doc)) {
setDocumentTabChanged(No-1, false);
}
}
DocumentTab->blockSignals(false);
QString tabType = DocumentTab->currentWidget()->metaObject()->className();
if (tabType == "Schematic") {
((Q3ScrollView*)DocumentTab->currentWidget())->viewport()->update();
}
statusBar()->showMessage(tr("Ready."));
slotRefreshSchPath();
slotUpdateTreeview();
}
void QucsApp::slotFileClose()
{
closeFile(-1);
}
void QucsApp::slotFileClose(int index)
{
closeFile(index);
if (TuningMode) tunerDia->slotResetTunerDialog();
}
void QucsApp::closeFile(int index)
{
statusBar()->showMessage(tr("Closing file..."));
slotHideEdit();
QucsDoc *Doc = getDoc(index);
if(Doc->getDocChanged()) {
switch(QMessageBox::warning(this,tr("Closing Qucs document"),
tr("The document contains unsaved changes!\n")+
tr("Do you want to save the changes before closing?"),
QMessageBox::Save|QMessageBox::Discard|QMessageBox::Cancel)) {
case QMessageBox::Save : slotFileSave();
break;
case QMessageBox::Cancel : return;
default: break;
}
}
DocumentTab->removeTab(index);
delete Doc;
if(DocumentTab->count() < 1) {
Schematic *d = new Schematic(this, "");
addDocumentTab(d);
DocumentTab->setCurrentIndex(0);
}
statusBar()->showMessage(tr("Ready."));
}
bool QucsApp::closeAllFiles()
{
SaveDialog *sd = new SaveDialog(this);
sd->setApp(this);
for(int i=0; i < DocumentTab->count(); ++i) {
QucsDoc *doc = getDoc(i);
if(doc->getDocChanged())
sd->addUnsavedDoc(doc);
}
int Result = SaveDialog::DontSave;
if(!sd->isEmpty())
Result = sd->exec();
delete sd;
if(Result == SaveDialog::AbortClosing)
return false;
QucsDoc *doc = 0;
while((doc = getDoc()) != 0)
delete doc;
return true;
}
void QucsApp::slotFileExamples() {
statusBar()->showMessage(tr("Open example…"));
auto exampleFile =
QFileDialog::getOpenFileName(this, tr("Select example schematic"),
QucsSettings.ExamplesDir, QucsFileFilter);
if (exampleFile.isEmpty()) {
statusBar()->showMessage(tr("Open example canceled"), 2000);
return;
}
gotoPage(exampleFile);
statusBar()->showMessage(tr("Ready."));
}
void QucsApp::slotHelpTutorial()
{
QUrl url = QUrl::fromLocalFile(QDir::cleanPath(QucsSettings.DocDir + "/tutorial/" + QObject::sender()->objectName()));
QDesktopServices::openUrl(url);
}
void QucsApp::slotHelpTechnical()
{
QUrl url = QUrl::fromLocalFile(QDir::cleanPath(QucsSettings.DocDir + "/technical/" + QObject::sender()->objectName()));
QDesktopServices::openUrl(url);
}
void QucsApp::slotHelpReport()
{
QUrl url = QUrl::fromLocalFile(QDir::cleanPath(QucsSettings.DocDir + "/report/" + QObject::sender()->objectName()));
QDesktopServices::openUrl(url);
}
void QucsApp::slotChangeView()
{
QWidget *w = DocumentTab->currentWidget();
editText->setHidden (true);
QucsDoc * Doc;
if(w==NULL)return;
if (isTextDocument (w)) {
TextDoc *d = (TextDoc*)w;
Doc = (QucsDoc*)d;
magAll->setDisabled(true);
magSel->setDisabled(true);
if(cursorLeft->isEnabled())
switchSchematicDoc (false);
}
else {
Schematic *d = (Schematic*)w;
Doc = (QucsDoc*)d;
magAll->setDisabled(false);
magSel->setDisabled(false);
if(cursorLeft->isEnabled()) {
if((CompChoose->count() > 1) == d->getSymbolMode())
changeSchematicSymbolMode (d);
}
else {
switchSchematicDoc(true);
changeSchematicSymbolMode(d);
}
showGrid->setChecked(d->getGridOn());
}
Doc->becomeCurrent(true);
HierarchyHistory.clear();
popH->setEnabled(false);
}
void QucsApp::slotFileSettings ()
{
editText->setHidden (true);
QWidget * w = DocumentTab->currentWidget ();
if (isTextDocument (w)) {
QucsDoc * Doc = (QucsDoc *) ((TextDoc *) w);
QString ext = Doc->fileSuffix ();
if (ext == "m" || ext == "oct") {
}
else if (ext == "va") {
VASettingsDialog * d = new VASettingsDialog ((TextDoc *) w);
d->exec ();
}
else {
DigiSettingsDialog * d = new DigiSettingsDialog ((TextDoc *) w);
d->exec ();
}
}
else {
SettingsDialog * d = new SettingsDialog ((Schematic *) w);
d->exec ();
showGrid->setChecked(static_cast<Schematic*>(w)->getGridOn());
}
}
void QucsApp::slotApplSettings()
{
slotHideEdit();
QucsSettingsDialog *d = new QucsSettingsDialog(this);
d->exec();
}
void QucsApp::slotRefreshSchPath()
{
statusBar()->showMessage(tr("The schematic search path has been refreshed."), 2000);
}
void QucsApp::updatePortNumber(QucsDoc *currDoc, int No)
{
if(No<0) return;
QString pathName = currDoc->getDocName();
QString ext = currDoc->fileSuffix ();
QFileInfo Info (pathName);
QString Model, File, Name = Info.fileName();
if (ext == "sch") {
Model = "Sub";
}
else if (ext == "vhdl" || ext == "vhd") {
Model = "VHDL";
}
else if (ext == "v") {
Model = "Verilog";
}
No = 0;
QWidget *w;
Component *pc_tmp;
while((w=DocumentTab->widget(No++)) != 0) {
if(isTextDocument (w)) continue;
Schematic *Doc = (Schematic*)w;
for(Component *pc=Doc->a_Components->last(); pc!=0; ) {
if(pc->Model == Model) {
File = pc->Props.front()->Value;
if((File == pathName) || (File == Name)) {
pc_tmp = Doc->a_Components->prev();
Doc->recreateComponent(pc);
if(!pc_tmp) break;
Doc->a_Components->findRef(pc_tmp);
pc = Doc->a_Components->current();
continue;
}
}
pc = Doc->a_Components->prev();
}
}
}
int QucsApp::addDocumentTab(QFrame* widget, const QString& title)
{
int index = DocumentTab->addTab(widget, title.isEmpty() ? tr("untitled") : title);
#if __APPLE__
widget->setFrameStyle(QFrame::NoFrame);
QTabBar* tabBar = DocumentTab->tabBar();
QLabel* modifiedLabel = new QLabel(" ", tabBar);
modifiedLabel->setFixedWidth(10);
tabBar->setTabButton(index, QTabBar::RightSide, modifiedLabel);
#endif
return index;
}
void QucsApp::setDocumentTabChanged(int index, bool changed)
{
#ifdef __APPLE__
((QLabel *)DocumentTab->tabBar()->tabButton(index, QTabBar::RightSide))->setText(changed ? "\u26AB" : " ");
#else
if (changed) {
DocumentTab->setTabIcon(index,QIcon(":bitmaps/svg/filesave.svg"));
} else {
DocumentTab->setTabIcon(index,QIcon());
}
#endif
}
void QucsApp::printCurrentDocument(bool fitToPage)
{
statusBar()->showMessage(tr("Printing..."));
slotHideEdit();
PrinterWriter *writer = new PrinterWriter();
writer->setFitToPage(fitToPage);
writer->print(DocumentTab->currentWidget());
delete writer;
statusBar()->showMessage(tr("Ready."));
return;
}
void QucsApp::slotFilePrint()
{
printCurrentDocument(false);
}
void QucsApp::slotFilePrintFit()
{
printCurrentDocument(true);
}
void QucsApp::slotFileQuit()
{
statusBar()->showMessage(tr("Exiting application..."));
slotHideEdit();
saveSettings();
if(closeAllFiles()) {
emit signalKillEmAll();
qApp->quit();
}
statusBar()->showMessage(tr("Ready."));
}
void QucsApp::closeEvent(QCloseEvent* Event)
{
saveSettings();
if(closeAllFiles()) {
emit signalKillEmAll();
Event->accept();
qApp->quit();
}
else
Event->ignore();
}
void QucsApp::saveSettings()
{
saveApplSettings();
}
void QucsApp::slotIntoHierarchy()
{
slotHideEdit();
Schematic *Doc = (Schematic*)DocumentTab->currentWidget();
Component *pc = Doc->searchSelSubcircuit();
if(pc == 0) { return; }
QString s = pc->getSubcircuitFile();
if(!gotoPage(s)) { return; }
HierarchyHistory.push(Doc->getDocName());
popH->setEnabled(true);
}
void QucsApp::slotPopHierarchy()
{
slotHideEdit();
if(HierarchyHistory.size() == 0) return;
QString Doc = HierarchyHistory.pop();
if(!gotoPage(Doc)) {
HierarchyHistory.push(Doc);
return;
}
if(HierarchyHistory.size() == 0) {
popH->setEnabled(false);
}
}
void QucsApp::slotShowAll()
{
slotHideEdit();
getDoc()->showAll();
}
void QucsApp::slotZoomToSelection()
{
slotHideEdit();
getDoc()->zoomToSelection();
}
void QucsApp::slotShowOne()
{
slotHideEdit();
getDoc()->showNoZoom();
}
void QucsApp::slotZoomOut()
{
slotHideEdit();
getDoc()->zoomBy(0.5f);
}
* \brief QucsApp::slotTune
* is called when the tune toolbar button is pressed.
*/
void QucsApp::slotTune(bool checked)
{
if (checked)
{
QWidget *w = DocumentTab->currentWidget();
if (isTextDocument(w))
{
QMessageBox::warning(this, "Not implemented",
"Currently tuning is not supported for this document type", QMessageBox::Ok);
tune->blockSignals(true);
tune->setChecked(false);
tune->blockSignals(false);
return;
}
Schematic* d(dynamic_cast<Schematic*>(w));
assert(d);
bool found = false;
bool digi_found = false;
bool exit = false;
for(Component *pc = d->a_DocComps.first(); pc != 0; pc = d->a_DocComps.next()) {
if (pc->isSimulation) {
found = true;
}
if (pc->Type == isDigitalComponent) {
digi_found = true;
exit = true;
}
}
if (!found) {
QMessageBox::warning(this,tr("Error"),tr("No simulations found. Tuning not possible."
" Please add at least one simulation."));
exit = true;
}
if (digi_found) {
QMessageBox::warning(this,tr("Error"),tr("Tuning not possible for digital simulation. "
"Only analog simulation supported."));
}
if (d->a_Diagrams->isEmpty() && !d->getSimOpenDpl()) {
QMessageBox::warning(this,tr("Error"),tr("Tuning has no effect without diagrams. "
"Add at least one diagram on schematic."));
exit = true;
}
if (exit) {
tune->blockSignals(true);
tune->setChecked(false);
tune->blockSignals(false);
return;
}
TuningMode = true;
tunerDia = new TunerDialog(w, this);
connect(d, SIGNAL(signalComponentDeleted(Component *)),
tunerDia, SLOT(slotComponentDeleted(Component *)));
slotHideEdit();
simulateToolbar->setEnabled(false);
MousePressAction = &MouseActions::MPressTune;
MouseReleaseAction = 0;
tunerDia->show();
}
else
{
this->simulateToolbar->setEnabled(true);
tunerDia->close();
TuningMode = false;
}
}
QWidget *QucsApp::getSchematicWidget(QucsDoc *Doc)
{
QWidget *w = nullptr;
QFileInfo Info(QucsSettings.QucsWorkDir.filePath(Doc->getDataDisplay()));
int z = 0;
QFileInfo sch_inf(Doc->getDocName());
QString sch_name = sch_inf.absolutePath() + QDir::separator() + Doc->getDataDisplay();
QucsDoc *d = findDoc(sch_name, &z);
if (d)
{
w = DocumentTab->widget(z);
}
else
{
int i = 0;
int No = DocumentTab->currentIndex();
if(Info.suffix() == "sch" || Info.suffix() == "dpl" ||
Info.suffix() == "sym") {
d = new Schematic(this, Info.absoluteFilePath());
i = DocumentTab->addTab((Schematic *)d, QPixmap(":/bitmaps/empty.xpm"), Info.fileName());
} else {
d = new TextDoc(this, Info.absoluteFilePath());
i = DocumentTab->addTab((TextDoc *)d, QPixmap(":/bitmaps/empty.xpm"), Info.fileName());
}
DocumentTab->setCurrentIndex(i);
if(d->load()) {
w = DocumentTab->widget(i);
} else {
delete d;
DocumentTab->setCurrentIndex(No);
}
DocumentTab->setCurrentIndex(No);
}
return w;
}
* \brief QucsApp::slotSimulate
* is called when the simulate toolbar button is pressed.
*/
void QucsApp::slotSimulate(QWidget *w)
{
if (w == nullptr)
w = DocumentTab->currentWidget();
bool isDigital = false;
if (!isTextDocument(w)) {
Schematic* schematicPtr = (Schematic*)w;
isDigital = schematicPtr->isDigitalCircuit();
if (isDigital && schematicPtr->getShowBias() == 0) {
QMessageBox::warning(this,tr("Simulate schematic"),
tr("DC bias simulation mode is not supported "
"for digital schematic!"));
return;
}
}
if (QucsSettings.DefaultSimulator!=spicecompat::simQucsator && !isDigital) {
slotSimulateWithSpice();
return;
}
slotHideEdit();
QucsDoc *Doc;
if(isTextDocument (w)) {
Doc = (QucsDoc*)((TextDoc*)w);
if(Doc->getSimTime().isEmpty() && ((TextDoc*)Doc)->simulation) {
DigiSettingsDialog *d = new DigiSettingsDialog((TextDoc*)Doc);
if(d->exec() == QDialog::Rejected)
return;
}
}
else
Doc = (QucsDoc*)((Schematic*)w);
if(Doc->getDocName().isEmpty())
if(!saveAs()) return;
QFileInfo Info(Doc->getDocName());
QString ext = Info.suffix();
if(Doc->getLastSaved().isValid()) {
if(Doc->getLastSaved() < Info.lastModified()) {
int No = QMessageBox::warning(this, tr("Warning"),
tr("The document was modified by another program !") + '\n' +
tr("Do you want to reload or keep this version ?"),
QMessageBox::Yes|QMessageBox::No);
if(No == QMessageBox::Yes)
Doc->load();
}
}
slotResetWarnings();
if(Info.suffix() == "m" || Info.suffix() == "oct") {
if(Doc->getDocChanged())
Doc->save();
slotViewOctaveDock(true);
octave->runOctaveScript(Doc->getDocName());
return;
}
if (ext == "dpl") {
w = getSchematicWidget(Doc);
}
SimMessage *sim = new SimMessage(w, this);
sim->setDocWidget(w);
connect(sim, SIGNAL(SimulationEnded(int, SimMessage*)), this,
SLOT(slotAfterSimulation(int, SimMessage*)));
connect(sim, SIGNAL(displayDataPage(QString&, QString&)),
this, SLOT(slotChangePage(QString&, QString&)));
if (TuningMode == true) {
connect(sim, SIGNAL(progressBarChanged(int)), tunerDia, SLOT(slotUpdateProgressBar(int)));
} else {
sim->show();
}
if(!sim->startProcess()) {
if (TuningMode == true) sim->show();
return;
}
connect(this, SIGNAL(signalKillEmAll()), sim, SLOT(slotClose()));
}
void QucsApp::slotAfterSimulation(int Status, SimMessage *sim)
{
if(Status != 0) {
if (TuningMode) {
sim->show();
tunerDia->SimulationEnded();
}
return;
}
if(sim->ErrText->document()->lineCount() > 1)
slotShowWarnings();
int i=0;
QWidget *w;
while((w=DocumentTab->widget(i++)) != 0)
if(w == sim->DocWidget)
break;
if(sim->showBias == 0) {
sim->slotClose();
if(w) {
SweepDialog *Dia = new SweepDialog((Schematic*)sim->DocWidget);
Q_UNUSED(Dia)
}
}
else {
if(sim->SimRunScript) {
octave->startOctave();
octave->runOctaveScript(sim->Script);
}
if(sim->SimOpenDpl) {
if(sim->DataDisplay.right(2) == ".m" ||
sim->DataDisplay.right(4) == ".oct") {
octave->startOctave();
octave->runOctaveScript(sim->DataDisplay);
}
else
slotChangePage(sim->DocName, sim->DataDisplay);
}
else {
if(w) {
if(!isTextDocument (sim->DocWidget)) {
int idx = Category::getModulesNr (QObject::tr("diagrams"));
CompChoose->setCurrentIndex(idx);
slotSetCompView (idx);
((Schematic*)sim->DocWidget)->reloadGraphs();
}
}
}
}
if(!isTextDocument (sim->DocWidget)) {
((Schematic*)DocumentTab->currentWidget())->viewport()->update();
}
if(TuningMode) {
sim->slotClose();
tunerDia->SimulationEnded();
}
}
void QucsApp::slotDCbias()
{
getDoc()->setShowBias(0);
slotSimulate();
}
void QucsApp::slotChangePage(const QString& DocName, const QString& DataDisplay)
{
if(DataDisplay.isEmpty()) return;
QFileInfo Info(DocName);
QString Name = Info.path() + QDir::separator() + DataDisplay;
QWidget *w = DocumentTab->currentWidget();
int z = 0;
QucsDoc * d = findDoc (Name, &z);
if(d)
DocumentTab->setCurrentIndex(z);
else {
QString ext = QucsDoc::fileSuffix (DataDisplay);
int i = 0;
if (ext != "vhd" && ext != "vhdl" && ext != "v" && ext != "va" &&
ext != "oct" && ext != "m") {
d = new Schematic(this, Name);
i = addDocumentTab((Schematic *)d, DataDisplay);
}
else {
d = new TextDoc(this, Name);
i = addDocumentTab((TextDoc *)d, DataDisplay);
}
DocumentTab->setCurrentIndex(i);
QFile file(Name);
if(file.open(QIODevice::ReadOnly)) {
file.close();
if(!d->load()) {
delete d;
return;
}
}
else {
if(file.open(QIODevice::ReadWrite)) {
d->setDataDisplay(Info.fileName());
slotUpdateTreeview();
}
else {
QMessageBox::critical(this, tr("Error"), tr("Cannot create ")+Name);
return;
}
file.close();
}
d->becomeCurrent(true);
}
if(DocumentTab->currentWidget() == w)
if(!isTextDocument (w))
((Schematic*)w)->reloadGraphs();
TabView->setCurrentIndex(2);
if (Name.right(4) == ".dpl") {
int i = Category::getModulesNr (QObject::tr("diagrams"));
CompChoose->setCurrentIndex(i);
slotSetCompView (i);
}
}
void QucsApp::slotToPage()
{
QucsDoc *d = getDoc();
if(d->getDataDisplay().isEmpty()) {
QMessageBox::critical(this, tr("Error"), tr("No page set !"));
return;
}
if(d->getDocName().right(2) == ".m" ||
d->getDocName().right(4) == ".oct")
slotViewOctaveDock(true);
else
slotChangePage(d->getDocName(), d->getDataDisplay());
}
void QucsApp::slotOpenContent(const QModelIndex &idx)
{
editText->setHidden(true);
if (!idx.isValid()) { return; }
if (!idx.parent().isValid()) { return; }
QString filename = idx.sibling(idx.row(), 0).data().toString();
QString note = idx.sibling(idx.row(), 1).data().toString();
QFileInfo Info(QucsSettings.QucsWorkDir.filePath(filename));
QString extName = Info.suffix();
if (extName == "sch" || extName == "dpl" || extName == "vhdl" ||
extName == "vhd" || extName == "v" || extName == "va" ||
extName == "m" || extName == "oct") {
gotoPage(Info.absoluteFilePath());
updateRecentFilesList(Info.absoluteFilePath());
slotUpdateRecentFiles();
if(note.isEmpty())
if(extName == "sch") return;
select->blockSignals(true);
select->setChecked(true);
select->blockSignals(false);
activeAction = select;
MouseMoveAction = 0;
MousePressAction = &MouseActions::MPressSelect;
MouseReleaseAction = &MouseActions::MReleaseSelect;
MouseDoubleClickAction = &MouseActions::MDoubleClickSelect;
return;
}
if(extName == "dat") {
editFile(Info.absoluteFilePath());
return;
}
QStringList com;
QStringList::const_iterator it = QucsSettings.FileTypes.constBegin();
while(it != QucsSettings.FileTypes.constEnd()) {
if(extName == (*it).section('/',0,0)) {
QString progName = (*it).section('/',1,1);
com = progName.split(" ");
com << Info.absoluteFilePath();
QProcess *Program = new QProcess();
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("PATH", env.value("PATH") );
Program->setProcessEnvironment(env);
QString cmd = com.at(0);
QStringList com_args = com;
com_args.removeAt(0);
Program->start(cmd, com_args);
if(Program->state()!=QProcess::Running&&
Program->state()!=QProcess::Starting) {
QMessageBox::critical(this, tr("Error"),
tr("Cannot start \"%1\"!").arg(Info.absoluteFilePath()));
delete Program;
}
return;
}
it++;
}
editFile(Info.absoluteFilePath());
}
void QucsApp::slotSelectSubcircuit(const QModelIndex &idx)
{
editText->setHidden(true);
if(!idx.isValid()) {
Content->clearSelection();
return;
}
bool isVHDL = false;
bool isVerilog = false;
QModelIndex parentIdx = idx.parent();
if(!parentIdx.isValid()) { return; }
QString category = parentIdx.data().toString();
if(category == tr("Schematics")) {
if(idx.sibling(idx.row(), 1).data().toString().isEmpty())
return;
}
else if(category == tr("VHDL"))
isVHDL = true;
else if(category == tr("Verilog"))
isVerilog = true;
else
return;
QString filename = idx.sibling(idx.row(), 0).data().toString();
QString note = idx.sibling(idx.row(), 1).data().toString();
int idx_pag = DocumentTab->currentIndex();
QString tab_titl = "";
if (idx_pag>=0) tab_titl = DocumentTab->tabText(idx_pag);
if (filename == tab_titl ) return;
if(view->selElem != 0) delete view->selElem;
view->selElem = 0;
if(activeAction) {
activeAction->blockSignals(true);
activeAction->setChecked(false);
activeAction->blockSignals(false);
}
activeAction = 0;
Component *Comp;
if(isVHDL)
Comp = new VHDL_File();
else if(isVerilog)
Comp = new Verilog_File();
else
Comp = new Subcircuit();
Comp->Props.first()->Value = idx.sibling(idx.row(), 0).data().toString();
Comp->recreate(0);
view->selElem = Comp;
MouseMoveAction = &MouseActions::MMoveElement;
MousePressAction = &MouseActions::MPressElement;
MouseReleaseAction = 0;
MouseDoubleClickAction = 0;
}
void QucsApp::slotSelectLibComponent(QTreeWidgetItem *item)
{
Schematic *Doc = (Schematic*)DocumentTab->currentWidget();
if(!isTextDocument(Doc))
{
if(item->parent() == 0) return;
if(item->text(1).isEmpty()) return;
QClipboard *cb = QApplication::clipboard();
QString model = item->text(1);
QString name = item->text(0);
QString list = item->text(2);
QString lib = item->text(3);
cb->setText(item->text(1));
qDebug()<<name<<lib;
QString content;
CompDescr->clear();
CompDescr->setText("Name: " + name);
CompDescr->append("Library: " + lib);
CompDescr->append("----------------------------");
if(!getSection("Description", list, content)) {
QMessageBox::critical(this, tr("Error"), tr("Library is corrupt."));
return;
}
CompDescr->append(content);
CompDescr->moveCursor(QTextCursor::Start);
if(getSection("Model", list, content)) {
Symbol->ModelString = content;
}
if(getSection("Spice", list, content)) {
Symbol->SpiceString = content;
}
if(getSection("VHDLModel", list, content)) {
Symbol->VHDLModelString = content;
}
if(getSection("VerilogModel", list, content)) {
Symbol->VerilogModelString = content;
}
if(!getSection("Symbol", list, content)) {
QMessageBox::critical(this, tr("Error"), tr("Library is corrupt."));
return;
}
Symbol->setSymbol(content,lib,name);
slotEditPaste (true);
}
}
void QucsApp::switchSchematicDoc (bool SchematicMode)
{
cursorLeft->setEnabled(SchematicMode);
cursorRight->setEnabled(SchematicMode);
cursorUp->setEnabled(SchematicMode);
cursorDown->setEnabled(SchematicMode);
if (!SchematicMode) {
if (activeAction) {
activeAction->blockSignals (true);
activeAction->setChecked(false);
activeAction->blockSignals (false);
}
activeAction = select;
select->blockSignals (true);
select->setChecked(true);
select->blockSignals (false);
}
else {
MouseMoveAction = 0;
MousePressAction = &MouseActions::MPressSelect;
MouseReleaseAction = &MouseActions::MReleaseSelect;
MouseDoubleClickAction = &MouseActions::MDoubleClickSelect;
}
selectMarker->setEnabled (SchematicMode);
alignTop->setEnabled (SchematicMode);
alignBottom->setEnabled (SchematicMode);
alignLeft->setEnabled (SchematicMode);
alignRight->setEnabled (SchematicMode);
centerHor->setEnabled (SchematicMode);
centerVert->setEnabled (SchematicMode);
distrHor->setEnabled (SchematicMode);
distrVert->setEnabled (SchematicMode);
onGrid->setEnabled (SchematicMode);
moveText->setEnabled (SchematicMode);
filePrintFit->setEnabled (SchematicMode);
editRotate->setEnabled (SchematicMode);
editMirror->setEnabled (SchematicMode);
editMirrorY->setEnabled (SchematicMode);
intoH->setEnabled (SchematicMode);
popH->setEnabled (SchematicMode);
dcbias->setEnabled (SchematicMode);
insWire->setEnabled (SchematicMode);
insLabel->setEnabled (SchematicMode);
insPort->setEnabled (SchematicMode);
insGround->setEnabled (SchematicMode);
insEquation->setEnabled (SchematicMode);
setMarker->setEnabled (SchematicMode);
setDiagramLimits->setEnabled (SchematicMode);
exportAsImage->setEnabled (SchematicMode);
editFind->setEnabled (!SchematicMode);
insEntity->setEnabled (!SchematicMode);
buildModule->setEnabled(!SchematicMode);
}
void QucsApp::switchEditMode(bool SchematicMode)
{
fillComboBox(SchematicMode);
slotSetCompView(0);
intoH->setEnabled(SchematicMode);
popH->setEnabled(SchematicMode);
editActivate->setEnabled(SchematicMode);
changeProps->setEnabled(SchematicMode);
insEquation->setEnabled(SchematicMode);
insGround->setEnabled(SchematicMode);
insWire->setEnabled(SchematicMode);
insLabel->setEnabled(SchematicMode);
setMarker->setEnabled(SchematicMode);
setDiagramLimits->setEnabled(SchematicMode);
selectMarker->setEnabled(SchematicMode);
simulate->setEnabled(SchematicMode);
CompSearch->setEnabled(SchematicMode);
CompSearchClear->setEnabled(SchematicMode);
}
void QucsApp::changeSchematicSymbolMode(Schematic *Doc)
{
if(Doc->getSymbolMode()) {
select->setChecked(true);
switchEditMode(false);
}
else
switchEditMode(true);
}
bool QucsApp::isTextDocument(QWidget *w) {
return w->inherits("QPlainTextEdit");
}
void QucsApp::slotSymbolEdit()
{
QWidget *w = DocumentTab->currentWidget();
if (isTextDocument (w)) {
TextDoc *TDoc = (TextDoc*)w;
if (!TDoc->getDocName().endsWith(".va")) {
QMessageBox::warning(this,tr("Error"),
tr("Symbol editing supported only for schematics and Verilog-A documents!"));
return;
} else {
QMessageBox::warning(this,tr("Warning"),
tr("Attaching symbols to Verilog-A sources is deprecated and not recommended "
"for new designs. Use SPICE generic device instead. See the documentation "
"for more details."));
}
QFileInfo Info(TDoc->getDocName());
QString sym = Info.completeBaseName()+".sym";
TDoc->setDataDisplay(sym);
int paint_mode = 0;
if (!findDoc (QucsSettings.QucsWorkDir.filePath(sym)))
paint_mode = 1;
slotChangePage(TDoc->getDocName(),TDoc->getDataDisplay());
Schematic *SDoc = (Schematic*)DocumentTab->currentWidget();
SDoc->setDataDisplay(Info.fileName());
if (paint_mode)
SDoc->switchPaintMode();
SDoc->setSymbolMode(true);
changeSchematicSymbolMode(SDoc);
SDoc->becomeCurrent(true);
SDoc->viewport()->update();
}
else {
Schematic *SDoc = (Schematic*)w;
if (!SDoc->getIsSymbolOnly()) {
slotHideEdit();
SDoc->switchPaintMode();
changeSchematicSymbolMode(SDoc);
SDoc->becomeCurrent(true);
SDoc->viewport()->update();
}
}
}
void QucsApp::slotPowerMatching()
{
QWidget *w = DocumentTab->currentWidget();
if (isTextDocument(w)) return;
if(!view->focusElement) return;
if(view->focusElement->Type != isMarker) return;
Marker *pm = (Marker*)view->focusElement;
QString Var = pm->pGraph->Var;
double Imag = pm->powImag();
if(Var == "Sopt")
Imag *= -1.0;
MatchDialog *Dia = new MatchDialog(this);
Dia->setS11LineEdits(pm->powReal(), Imag);
Dia->setFrequency(pm->powFreq());
Dia->setTwoPortMatch(false);
Schematic *sch = dynamic_cast<Schematic*>(w);
if (sch->getSimOpenDpl() || sch->getDocName().endsWith(".dpl")) {
slotToPage();
}
if(Dia->exec() != QDialog::Accepted)
return;
}
void QucsApp::slot2PortMatching()
{
QWidget *w = DocumentTab->currentWidget();
if (isTextDocument(w)) return;
if(!view->focusElement) return;
if(view->focusElement->Type != isMarker) return;
Marker *pm = (Marker*)view->focusElement;
QString DataSet;
Schematic *Doc = (Schematic*)DocumentTab->currentWidget();
int z = pm->pGraph->Var.indexOf(':');
if(z <= 0) DataSet = Doc->getDataSet();
else DataSet = pm->pGraph->Var.mid(z+1);
double Freq = pm->powFreq();
QFileInfo Info(Doc->getDocName());
DataSet = Info.path()+QDir::separator()+DataSet;
Diagram *Diag = new Diagram();
Graph *pg = new Graph(Diag, "S[1,1]");
Diag->Graphs.append(pg);
if(!pg->loadDatFile(DataSet)) {
QMessageBox::critical(0, tr("Error"), tr("Could not load S[1,1]."));
return;
}
pg = new Graph(Diag, "S[1,2]");
Diag->Graphs.append(pg);
if(!pg->loadDatFile(DataSet)) {
QMessageBox::critical(0, tr("Error"), tr("Could not load S[1,2]."));
return;
}
pg = new Graph(Diag, "S[2,1]");
Diag->Graphs.append(pg);
if(!pg->loadDatFile(DataSet)) {
QMessageBox::critical(0, tr("Error"), tr("Could not load S[2,1]."));
return;
}
pg = new Graph(Diag, "S[2,2]");
Diag->Graphs.append(pg);
if(!pg->loadDatFile(DataSet)) {
QMessageBox::critical(0, tr("Error"), tr("Could not load S[2,2]."));
return;
}
DataX const *Data = Diag->Graphs.first()->axis(0);
if(Data->Var != "frequency") {
QMessageBox::critical(0, tr("Error"), tr("Wrong dependency!"));
return;
}
double *Value = Data->Points;
for(z=0; z<Data->count; z++)
if(*(Value++) == Freq) break;
double S11real = *(Diag->Graphs.at(0)->cPointsY + 2*z);
double S11imag = *(Diag->Graphs.at(0)->cPointsY + 2*z + 1);
double S12real = *(Diag->Graphs.at(1)->cPointsY + 2*z);
double S12imag = *(Diag->Graphs.at(1)->cPointsY + 2*z + 1);
double S21real = *(Diag->Graphs.at(2)->cPointsY + 2*z);
double S21imag = *(Diag->Graphs.at(2)->cPointsY + 2*z + 1);
double S22real = *(Diag->Graphs.at(3)->cPointsY + 2*z);
double S22imag = *(Diag->Graphs.at(3)->cPointsY + 2*z + 1);
delete Diag;
MatchDialog *Dia = new MatchDialog(this);
Dia->setTwoPortMatch(true);
Dia->setFrequency(Freq);
Dia->setS11LineEdits(S11real, S11imag);
Dia->setS12LineEdits(S12real, S12imag);
Dia->setS21LineEdits(S21real, S21imag);
Dia->setS22LineEdits(S22real, S22imag);
Schematic *sch = dynamic_cast<Schematic*>(w);
if (sch->getSimOpenDpl() || sch->getDocName().endsWith(".dpl")) {
slotToPage();
}
if(Dia->exec() != QDialog::Accepted)
return;
}
void QucsApp::slotEditElement()
{
if(view->focusMEvent)
view->editElement((Schematic*)DocumentTab->currentWidget(), view->focusMEvent);
}
void QucsApp::slotHideEdit()
{
editText->setParent(this, Qt::WindowFlags());
editText->setHidden(true);
}
void QucsApp::slotFileChanged(bool changed)
{
setDocumentTabChanged(DocumentTab->currentIndex(), changed);
}
void QucsApp::slotUpdateTreeview()
{
Content->refresh();
}
// -----------------------------------------------------------
// Searches the qucs path list for all schematic files and creates
// a hash for lookup later
void QucsApp::updateSchNameHash(void)
{
// update the list of paths to search in qucsPathList, this
// removes nonexisting entries
updatePathList();
// now go through the paths creating a map to all the schematic files
// found in the directories. Note that we go through the list of paths from
// first index to last index. Since keys are unique it means schematic files
// in directories at the end of the list take precedence over those at the
// start of the list, we should warn about shadowing of schematic files in
// this way in the future
QStringList nameFilter;
nameFilter << "*.sch";
// clear out any existing hash table entries
schNameHash.clear();
for (const QString& qucspath : qucsPathList) {
QDir thispath(qucspath);
// get all the schematic files in the directory
QFileInfoList schfilesList = thispath.entryInfoList( nameFilter, QDir::Files );
// put each one in the hash table with the unique key the base name of
// the file, note this will overwrite the value if the key already exists
for (const QFileInfo& schfile : schfilesList) {
schNameHash[schfile.completeBaseName()] = schfile.absoluteFilePath();
}
}
// finally check the home/working directory
QDir thispath(QucsSettings.QucsWorkDir);
QFileInfoList schfilesList = thispath.entryInfoList( nameFilter, QDir::Files );
// put each one in the hash table with the unique key the base name of
// the file, note this will overwrite the value if the key already exists
for (const QFileInfo& schfile : schfilesList) {
schNameHash[schfile.completeBaseName()] = schfile.absoluteFilePath();
}
}
*/
// -----------------------------------------------------------
// Searches the qucs path list for all spice files and creates
// a hash for lookup later
void QucsApp::updateSpiceNameHash()
{
// update the list of paths to search in qucsPathList, this
// removes nonexisting entries
updatePathList();
// now go through the paths creating a map to all the spice files
// found in the directories. Note that we go through the list of paths from
// first index to last index. Since keys are unique it means spice files
// in directories at the end of the list take precedence over those at the
// start of the list, we should warn about shadowing of spice files in
// this way in the future
// clear out any existing hash table entries
spiceNameHash.clear();
for (const QString& qucspath : qucsPathList) {
QDir thispath(qucspath);
// get all the schematic files in the directory
QFileInfoList spicefilesList = thispath.entryInfoList( QucsSettings.spiceExtensions, QDir::Files );
// put each one in the hash table with the unique key the base name of
// the file, note this will overwrite the value if the key already exists
for (const QFileInfo& spicefile : spicefilesList) {
schNameHash[spicefile.completeBaseName()] = spicefile.absoluteFilePath();
}
}
// finally check the home/working directory
QDir thispath(QucsSettings.QucsWorkDir);
QFileInfoList spicefilesList = thispath.entryInfoList( QucsSettings.spiceExtensions, QDir::Files );
// put each one in the hash table with the unique key the base name of
// the file, note this will overwrite the value if the key already exists
for (const QFileInfo& spicefile : spicefilesList) {
spiceNameHash[spicefile.completeBaseName()] = spicefile.absoluteFilePath();
}
}
*/
void QucsApp::updatePathList()
{
QMutableListIterator<QString> i(qucsPathList);
while (i.hasNext()) {
i.next();
QDir thispath(i.value());
if (!thispath.exists())
{
i.remove();
}
}
}
void QucsApp::updatePathList(QStringList newPathList)
{
qucsPathList.clear();
for (const QString& path : newPathList)
{
qucsPathList.append(path);
}
updatePathList();
}
void QucsApp::updateRecentFilesList(QString s)
{
QSettings* settings = new QSettings("qucs","qucs_s");
QucsSettings.RecentDocs.removeAll(s);
QucsSettings.RecentDocs.prepend(s);
if (QucsSettings.RecentDocs.size() > MaxRecentFiles) {
QucsSettings.RecentDocs.removeLast();
}
settings->setValue("RecentDocs",QucsSettings.RecentDocs.join("*"));
delete settings;
slotUpdateRecentFiles();
}
void QucsApp::slotSaveDiagramToGraphicsFile()
{
slotSaveSchematicToGraphicsFile(true);
}
void QucsApp::slotSaveSchematicToGraphicsFile(bool diagram)
{
ImageWriter *writer = new ImageWriter(lastExportFilename);
writer->setDiagram(diagram);
if (!writer->print(DocumentTab->currentWidget())) {
lastExportFilename = writer->getLastSavedFile();
statusBar()->showMessage(QObject::tr("Successfully exported"), 2000);
}
delete writer;
}
void QucsApp::slotSimSettings()
{
SimSettingsDialog *SetDlg = new SimSettingsDialog(this);
SetDlg->exec();
delete SetDlg;
fillSimulatorsComboBox();
}
void QucsApp::slotSimulateWithSpice()
{
if (!isTextDocument(DocumentTab->currentWidget()))
{
Schematic* schematic(dynamic_cast<Schematic*>(DocumentTab->currentWidget()));
if (TuningMode)
{
QFileInfo Info(schematic->getDocName());
QString ext = Info.suffix();
if (ext == "dpl")
{
QucsDoc *doc(dynamic_cast<QucsDoc*>(schematic));
Q_ASSERT(doc != nullptr);
schematic = dynamic_cast<Schematic*>(getSchematicWidget(doc));
if (schematic == nullptr)
{
return;
}
}
}
if (schematic->getDocName().isEmpty())
{
auto biasState = schematic->getShowBias();
QMessageBox::warning(
this,
tr("Simulate schematic"),
tr("Schematic not saved! Simulation of unsaved schematic "
"not possible. Save schematic first!"));
slotFileSaveAs();
schematic->setShowBias(biasState);
}
ExternSimDialog *SimDlg = new ExternSimDialog(schematic, false);
connect(SimDlg, SIGNAL(simulated(ExternSimDialog*)), this, SLOT(slotAfterSpiceSimulation(ExternSimDialog*)));
connect(SimDlg, SIGNAL(warnings()), this, SLOT(slotShowWarnings()));
connect(SimDlg, SIGNAL(success()), this, SLOT(slotResetWarnings()));
if (TuningMode || schematic->getShowBias() == 0)
{
SimDlg->slotStart();
}
else
{
SimDlg->exec();
}
disconnect(SimDlg, SIGNAL(warnings()), this, SLOT(slotShowWarnings()));
disconnect(SimDlg, SIGNAL(success()), this, SLOT(slotResetWarnings()));*/
if (schematic->getShowBias() < 1) slotChangePage(schematic->getDocName(), schematic->getDataDisplay());
delete SimDlg;*/
}
else
{
QMessageBox::warning(
this,
tr("Simulate schematic"),
tr("Simulation of text document is not possible!"));
}
}
void QucsApp::slotSaveNetlist()
{
if (QucsSettings.DefaultSimulator == spicecompat::simQucsator)
{
QMessageBox::information(
this,
tr("Save netlist"),
tr("This action is supported only for SPICE simulators!"));
return;
}
if (!isTextDocument(DocumentTab->currentWidget()))
{
Schematic* schematic(dynamic_cast<Schematic*>(DocumentTab->currentWidget()));
Q_ASSERT(schematic != nullptr);
ExternSimDialog simDlg(schematic, a_netlist2Console, true);
simDlg.slotSaveNetlist();
}
}
void QucsApp::slotSaveCdlNetlist()
{
if (!isTextDocument(DocumentTab->currentWidget()))
{
Schematic* schematic = dynamic_cast<Schematic*>(DocumentTab->currentWidget());
Q_ASSERT(schematic != nullptr);
if (a_netlist2Console)
{
QString netlistString;
{
QTextStream netlistStream(&netlistString);
CdlNetlistWriter cdlWriter(netlistStream, schematic);
if (!cdlWriter.write())
{
QMessageBox::critical(
this,
tr("Save CDL netlist"),
tr("Save CDL netlist failed!"),
QMessageBox::Ok);
}
printf("\nCDL netlist:\n%s\n", netlistString.toUtf8().constData());
Content->refresh();
}
}
else
{
QFileInfo inf(schematic->getDocName());
QString filename = QFileDialog::getSaveFileName(
this,
tr("Save CDL netlist"),
inf.path() + QDir::separator() + "netlist.cdl",
"CDL netlist (*.cdl)");
if (filename.isEmpty())
{
return;
}
QFile netlistFile(filename);
if (netlistFile.open(QIODevice::WriteOnly))
{
QTextStream netlistStream(&netlistFile);
CdlNetlistWriter cdlWriter(netlistStream, schematic);
if (!cdlWriter.write())
{
QMessageBox::critical(
this,
tr("Save CDL netlist"),
tr("Save CDL netlist failed!"),
QMessageBox::Ok);
}
netlistFile.close();
Content->refresh();
}
}
}
}
void QucsApp::slotAfterSpiceSimulation(ExternSimDialog *SimDlg)
{
Schematic *sch = (Schematic*)DocumentTab->currentWidget();
disconnect(SimDlg,SIGNAL(simulated(ExternSimDialog *)),
this,SLOT(slotAfterSpiceSimulation(ExternSimDialog *)));
disconnect(SimDlg,SIGNAL(warnings()),this,SLOT(slotShowWarnings()));
disconnect(SimDlg,SIGNAL(success()),this,SLOT(slotResetWarnings()));
if (TuningMode && SimDlg->hasError()) {
SimDlg->show();
return;
}
if (SimDlg->wasSimulated()) {
if(sch->getSimOpenDpl()) {
if (sch->getShowBias() < 1) {
if (!TuningMode) {
slotChangePage(sch->getDocName(),sch->getDataDisplay());
} else if (!sch->getDocName().endsWith(".dpl")) {
slotChangePage(sch->getDocName(),sch->getDataDisplay());
}
}
} else {
if (sch->getShowBias() < 1 && !TuningMode) {
int idx = Category::getModulesNr (QObject::tr("diagrams"));
CompChoose->setCurrentIndex(idx);
slotSetCompView (idx);
}
}
}
sch->reloadGraphs();
sch->viewport()->update();
if(sch->getSimRunScript()) {
octave->startOctave();
octave->runOctaveScript(sch->getScript());
}
if (TuningMode) {
tunerDia->SimulationEnded();
}
if (sch->getShowBias()>0 || QucsMain->TuningMode) SimDlg->close();
}
void QucsApp::slotBuildVAModule()
{
if (!isTextDocument(DocumentTab->currentWidget())) {
Schematic *Sch = (Schematic*)DocumentTab->currentWidget();
QFileInfo inf(Sch->getDocName());
QString filename = QFileDialog::getSaveFileName(this,tr("Save Verilog-A module"),
inf.path()+QDir::separator()+"testmodule.va",
"Verilog-A (*.va)");
if (filename.isEmpty()) return;
QFile f(filename);
if (f.open(QIODevice::WriteOnly)) {
QTextStream stream(&f);
VerilogAwriter *writer = new VerilogAwriter;
if (!writer->createVA_module(stream,Sch)) {
QMessageBox::critical(this,tr("Build Verilog-A module"),
tr("This schematic is not a subcircuit!\n"
"Use subcircuit to crete Verilog-A module!"),
QMessageBox::Ok);
}
delete writer;
f.close();
Content->refresh();
}
}
}
void QucsApp::slotShowModel()
{
DisplayDialog *dlg = new DisplayDialog(this,Symbol->ModelString,
Symbol->SpiceString);
dlg->exec();
delete dlg;
}
void QucsApp::slotSearchLibComponent(const QString &comp)
{
if (comp.isEmpty()) {
QTreeWidgetItemIterator top_itm(libTreeWidget);
while (*top_itm) {
(*top_itm)->setExpanded(false);
(*top_itm)->setHidden(false);
for (int i = 0; i < (*top_itm)->childCount(); i++ ) {
auto itm = (*top_itm)->child(i);
itm->setHidden(false);
}
top_itm++;
}
return;
}
QTreeWidgetItemIterator top_itm(libTreeWidget);
while (*top_itm) {
bool found = false;
int cnt = (*top_itm)->childCount();
if (cnt < 1) {
top_itm++;
continue;
}
for (int i = 0; i < cnt; i++ ) {
auto itm = (*top_itm)->child(i);
QString name =itm->text(0);
if (name.contains(comp,Qt::CaseInsensitive)) {
itm->setHidden(false);
found=true;
} else {
itm->setHidden(true);
}
}
if (found) {
(*top_itm)->setExpanded(true);
(*top_itm)->setHidden(false);
} else {
(*top_itm)->setExpanded(false);
(*top_itm)->setHidden(true);
}
top_itm++;
}
}
void QucsApp::slotSearchLibClear()
{
LibCompSearch->clear();
slotSearchLibComponent("");
}
QVariant QucsFileSystemModel::data( const QModelIndex& index, int role ) const
{
if (role == Qt::DecorationRole) {
QString dName = fileName(index);
if (dName.endsWith("_prj")) {
return QIcon(":bitmaps/hicolor/128x128/apps/qucs.png");
}
}
return QFileSystemModel::data(index, role);
}
bool QucsSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
if (sortColumn() == 0) {
QucsFileSystemModel *model = qobject_cast<QucsFileSystemModel*>(sourceModel());
bool asc = sortOrder() == Qt::AscendingOrder;
QFileInfo leftFileInfo = model->fileInfo(left);
QFileInfo rightFileInfo = model->fileInfo(right);
QString leftFileName = model->fileName(left);
QString rightFileName = model->fileName(right);
if (sourceModel()->data(left).toString() == "..")
return asc;
if (sourceModel()->data(right).toString() == "..")
return !asc;
if (!leftFileInfo.isDir() && rightFileInfo.isDir()) {
return !asc;
}
if (leftFileInfo.isDir() && !rightFileInfo.isDir()) {
return asc;
}
if (leftFileInfo.isDir() && rightFileInfo.isDir()) {
if (!leftFileName.endsWith("_prj") && rightFileName.endsWith("_prj")) {
return !asc;
}
if (leftFileName.endsWith("_prj") && !rightFileName.endsWith("_prj")) {
return asc;
}
}
}
return QSortFilterProxyModel::lessThan(left, right);
}