mirror of
https://github.com/ra3xdh/qucs_s
synced 2025-03-28 21:13:26 +00:00
3760 lines
120 KiB
C++
3760 lines
120 KiB
C++
/***************************************************************************
|
|
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 "dialogs/vtabwidget.h"
|
|
//#include "dialogs/vtabbeddockwidget.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 "extsimkernels/codemodelgen.h"
|
|
#include "symbolwidget.h"
|
|
|
|
QucsApp::QucsApp()
|
|
{
|
|
|
|
windowTitle = misc::getWindowTitle();
|
|
setWindowTitle(windowTitle);
|
|
|
|
QucsSettings.hasDarkTheme = misc::isDarkTheme();
|
|
|
|
// Instantiate settings singleton and restore window geometry.
|
|
const auto geometry = _settings::Get().item<QByteArray>("MainWindowGeometry");
|
|
|
|
if (!geometry.isEmpty()) {
|
|
qDebug() << "Saved geometry is: " << geometry;
|
|
restoreGeometry(geometry);
|
|
}
|
|
// Set a default size and position if geometry does not yet exist.
|
|
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")+" (*)";
|
|
|
|
//updateSchNameHash();
|
|
//updateSpiceNameHash();
|
|
|
|
MouseMoveAction = 0;
|
|
MousePressAction = 0;
|
|
MouseReleaseAction = 0;
|
|
MouseDoubleClickAction = 0;
|
|
|
|
initView();
|
|
initActions();
|
|
initMenuBar();
|
|
fillSimulatorsComboBox();
|
|
initToolBar();
|
|
initStatusBar();
|
|
viewBrowseDock->setChecked(true);
|
|
slotViewOctaveDock(false);
|
|
slotUpdateRecentFiles();
|
|
initCursorMenu();
|
|
//Module::registerModules ();
|
|
|
|
// instance of small text search dialog
|
|
SearchDia = new SearchDialog(this);
|
|
TuningMode = false;
|
|
|
|
// creates a document called "untitled"
|
|
Schematic *d = new Schematic(this, "");
|
|
int i = addDocumentTab(d);
|
|
DocumentTab->setCurrentIndex(i);
|
|
|
|
select->setChecked(true); // switch on the 'select' action
|
|
switchSchematicDoc(true); // "untitled" document is schematic
|
|
QDir homeDir = QDir::homePath();
|
|
lastExportFilename = homeDir.absoluteFilePath("export.png");
|
|
|
|
// load documents given as command line arguments
|
|
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) { // try to find Ngspice
|
|
QString ngspice_exe_name = "ngspice";
|
|
#ifdef Q_OS_WIN
|
|
ngspice_exe_name+="_con";
|
|
#endif
|
|
/* search own path */
|
|
#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
|
|
|
|
/* search system path */
|
|
QString ngspice_exe2 = QStandardPaths::findExecutable(ngspice_exe_name);
|
|
|
|
/* search own path */
|
|
QString qucsator_exe1 = QStandardPaths::findExecutable("qucsator_rf",{QucsBinDir.absolutePath()});
|
|
/* search system path */
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
|
|
// fillLibrariesTreeView();
|
|
}
|
|
|
|
QucsApp::~QucsApp()
|
|
{
|
|
Module::unregisterModules ();
|
|
}
|
|
|
|
|
|
// #######################################################################
|
|
// ########## ##########
|
|
// ########## Creates the working area (QTabWidget etc.) ##########
|
|
// ########## ##########
|
|
// #######################################################################
|
|
/**
|
|
* @brief QucsApp::initView Setup the layour of all widgets
|
|
*/
|
|
void QucsApp::initView()
|
|
{
|
|
// set application icon
|
|
// APPLE sets the QApplication icon with Info.plist
|
|
#ifndef __APPLE__
|
|
setWindowIcon (QPixmap(QStringLiteral(":/bitmaps/hicolor/scalable/apps/qucs.svg")));
|
|
#else
|
|
// setUnifiedTitleAndToolBarOnMac(true);
|
|
setStyleSheet("QToolButton { padding: 0px; }");
|
|
#endif
|
|
|
|
DocumentTab = new QTabWidget(this);
|
|
#if __APPLE__
|
|
DocumentTab->setDocumentMode(true);
|
|
#endif
|
|
setCentralWidget(DocumentTab);
|
|
|
|
connect(DocumentTab,
|
|
SIGNAL(currentChanged(int)), SLOT(slotChangeView()));
|
|
|
|
// Give every tab a close button, and connect the button's signal to
|
|
// slotFileClose
|
|
DocumentTab->setTabsClosable(true);
|
|
connect(DocumentTab,
|
|
SIGNAL(tabCloseRequested(int)), SLOT(slotFileClose(int)));
|
|
#ifdef HAVE_QTABWIDGET_SETMOVABLE
|
|
// make tabs draggable if supported
|
|
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); // for editing component properties
|
|
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()));
|
|
|
|
// ----------------------------------------------------------
|
|
// "Project Tab" of the left QTabWidget
|
|
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" Tab of the left QTabWidget
|
|
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 &)));
|
|
|
|
// ----------------------------------------------------------
|
|
// "Component" Tab of the left QTabWidget
|
|
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);
|
|
|
|
// Setup component search box and button.
|
|
CompSearch = new QLineEdit(this);
|
|
CompSearch->setPlaceholderText(tr("Search Components"));
|
|
CompSearchClear = new QPushButton(tr("Clear"));
|
|
CompSearchLayout->addWidget(CompSearch);
|
|
CompSearchLayout->addWidget(CompSearchClear);
|
|
|
|
// Setup the component chooser widgets.
|
|
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()));
|
|
|
|
// ----------------------------------------------------------
|
|
// "Libraries" Tab of the left QTabWidget
|
|
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);
|
|
|
|
//fillLibrariesTreeView ();
|
|
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*)));
|
|
|
|
// ----------------------------------------------------------
|
|
// put the tab widget in the dock
|
|
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
|
|
|
|
// ----------------------------------------------------------
|
|
// Octave docking window
|
|
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);
|
|
|
|
// initial projects directory model
|
|
m_homeDirModel = new QucsFileSystemModel(this);
|
|
m_proxyModel = new QucsSortFilterProxyModel();
|
|
//m_proxyModel->setDynamicSortFilter(true);
|
|
// show all directories (project and non-project)
|
|
m_homeDirModel->setFilter(QDir::NoDot | QDir::AllDirs);
|
|
|
|
// ............................................
|
|
QString path = QucsSettings.qucsWorkspaceDir.absolutePath();
|
|
QDir ProjDir(path);
|
|
// initial projects directory is the Qucs home directory
|
|
QucsSettings.projsDir.setPath(path);
|
|
|
|
// create home dir if not exist
|
|
if(!ProjDir.exists()) {
|
|
if(!ProjDir.mkdir(path)) {
|
|
QMessageBox::warning(this, tr("Warning"),
|
|
tr("Cannot create work directory !"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// ............................................
|
|
readProjects(); // reads all projects and inserts them into the ListBox
|
|
}
|
|
|
|
// Put all available libraries into ComboBox.
|
|
void QucsApp::fillLibrariesTreeView ()
|
|
{
|
|
QList<QTreeWidgetItem *> topitems;
|
|
|
|
libTreeWidget->clear();
|
|
|
|
// make the system libraries section header
|
|
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);
|
|
// newitem->setBackground
|
|
topitems.append (newitem);
|
|
|
|
populateLibTreeFromDir(QucsSettings.LibDir, topitems, true);
|
|
|
|
// make the user libraries section header
|
|
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);
|
|
|
|
// make the user libraries section header
|
|
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) { // exclude blacklisted files
|
|
LibFiles.removeAll(ss);
|
|
}
|
|
// create top level library items, base on the library names
|
|
for(auto it = LibFiles.begin(); it != LibFiles.end(); it++)
|
|
{
|
|
QString libPath(LibDir.absoluteFilePath(*it));
|
|
libPath.chop(4); // remove extension
|
|
|
|
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);
|
|
|
|
// Silence warning from the compiler about unused variable newcompitem
|
|
// we pass the pointer to the parent item in the constructor
|
|
Q_UNUSED( newcompitem )
|
|
}
|
|
|
|
topitems.append (newlibitem);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// Returns a pointer to the QucsDoc object whose number is "No".
|
|
// If No < 0 then a pointer to the current document is returned.
|
|
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;
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// Returns a pointer to the QucsDoc object whose file name is "Name".
|
|
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;
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// Put the component groups into the ComboBox. It is possible to
|
|
// only put the paintings in it, because of "symbol painting mode".
|
|
|
|
// if setAll, add all categories to combobox
|
|
// if not, set just paintings (symbol painting mode)
|
|
// return stored index
|
|
int QucsApp::fillComboBox(bool setAll) {
|
|
fillLibrariesTreeView(); // заполняем библиотеки
|
|
//CompChoose->setMaxVisibleItems (13); // Increase this if you add items below.
|
|
auto currentText = CompChoose->currentText();
|
|
|
|
CompChoose->clear();
|
|
CompSearch->clear(); // clear the search box, in case search was active...
|
|
|
|
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);
|
|
//CompChoose->setCurrentIndex(idx == -1 ? 0 : idx);
|
|
}
|
|
return idx == -1 ? 0 : idx;
|
|
}
|
|
|
|
void QucsApp::fillSimulatorsComboBox() {
|
|
|
|
simulatorsCombobox->clear();
|
|
//simulatorsCombobox->addItem(spicecompat::getDefaultSimulatorName(spicecompat::simNotSpecified), 0);
|
|
|
|
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);
|
|
|
|
// Call update() to update subcircuit symbols in current Schematic document.
|
|
// TextDoc has no viewport, it needs no update.
|
|
QString tabType = DocumentTab->currentWidget()->metaObject()->className();
|
|
|
|
if (tabType == "Schematic") {
|
|
((Q3ScrollView*)DocumentTab->currentWidget())->viewport()->update();
|
|
}
|
|
//SimulatorLabel->setText(spicecompat::getDefaultSimulatorName(QucsSettings.DefaultSimulator));
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Whenever the Component Library ComboBox is changed, this slot fills the
|
|
// Component IconView with the appropriate components.
|
|
void QucsApp::slotSetCompView (int index)
|
|
{
|
|
//qDebug() << "QucsApp::slotSetCompView(" << index << ")";
|
|
|
|
editText->setHidden (true); // disable text edit of component property
|
|
|
|
QList<Module *> Comps;
|
|
if (CompChoose->count () <= 0) return;
|
|
|
|
// was in "search mode" ?
|
|
if (CompChoose->itemText(0) == tr("Search results")) {
|
|
if (index == 0) // user selected "Search results" item
|
|
return;
|
|
CompChoose->removeItem(0);
|
|
CompSearch->clear(); // clear the search box
|
|
--index; // adjust requested index since item 0 was removed
|
|
}
|
|
|
|
while (CompComps->count() > 0) {
|
|
QListWidgetItem * item = CompComps->takeItem(0);
|
|
if (item != nullptr) {
|
|
delete item;
|
|
}
|
|
}
|
|
CompComps->clear (); // clear the IconView
|
|
|
|
// make sure the right index is selected
|
|
// (might have been called by a cleared search and not by user action)
|
|
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 something was registered dynamically, get and draw icons into dock
|
|
if (item == QObject::tr("verilog-a user devices")) {
|
|
|
|
compIdx = 0;
|
|
QMapIterator<QString, QString> i(Module::vaComponents);
|
|
while (i.hasNext()) {
|
|
i.next();
|
|
|
|
// default icon initially matches the module name
|
|
//Name = i.key();
|
|
|
|
// Just need path to bitmap, do not create an object
|
|
QString vaBitmap;
|
|
Component * c = (Component *)
|
|
vacomponent::info (Name, vaBitmap, false, i.value());
|
|
if (c) delete c;
|
|
|
|
// check if icon exists, fall back to default
|
|
QString iconPath = QucsSettings.QucsWorkDir.filePath(vaBitmap+".png");
|
|
|
|
QFile iconFile(iconPath);
|
|
QPixmap vaIcon;
|
|
|
|
if(iconFile.exists())
|
|
{
|
|
// load bitmap defined on the JSON symbol file
|
|
vaIcon = QPixmap(iconPath);
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::information(this, tr("Info"),
|
|
tr("Default icon not found:\n %1.png").arg(vaBitmap));
|
|
// default icon
|
|
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 {
|
|
// static components
|
|
char * File;
|
|
// Populate list of component bitmaps
|
|
compIdx = 0;
|
|
QList<Module *>::const_iterator it;
|
|
for (it = Comps.constBegin(); it != Comps.constEnd(); it++) {
|
|
Infos = (*it)->info;
|
|
if (Infos) {
|
|
/// \todo warning: expression result unused, can we rewrite this?
|
|
//(void) *((*it)->info) (Name, File, false);
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// When CompSearch is being edited, create a temp page show the
|
|
// search result
|
|
void QucsApp::slotSearchComponent(const QString &searchText)
|
|
{
|
|
// qDebug() << "User search: " << searchText;
|
|
CompComps->clear (); // clear the IconView
|
|
|
|
// not already in "search mode"
|
|
if (CompChoose->itemText(0) != tr("Search results")) {
|
|
ccCurIdx = CompChoose->currentIndex(); // remember current panel
|
|
// insert "Search results" at the beginning, so that it is visible
|
|
CompChoose->insertItem(-1, tr("Search results"));
|
|
CompChoose->setCurrentIndex(0);
|
|
}
|
|
|
|
if (searchText.isEmpty()) {
|
|
slotSetCompView(CompChoose->currentIndex());
|
|
} else {
|
|
CompChoose->setCurrentIndex(0); // make sure the "Search results" category is selected
|
|
editText->setHidden (true); // disable text edit of component property
|
|
|
|
//traverse all component and match searchText with name
|
|
QString Name;
|
|
char * File;
|
|
QList<Module *> Comps;
|
|
|
|
QStringList cats = Category::getCategories ();
|
|
int catIdx = 0;
|
|
for (const QString& it : cats) {
|
|
// this will go also over the "verilog-a user devices" category, if present
|
|
// but since modules there have no 'info' function it won't handle them
|
|
Comps = Category::getModules(it);
|
|
QList<Module *>::const_iterator modit;
|
|
int compIdx = 0;
|
|
for (modit = Comps.constBegin(); modit != Comps.constEnd(); modit++) {
|
|
if ((*modit)->info) {
|
|
/// \todo warning: expression result unused, can we rewrite this?
|
|
(void) *((*modit)->info) (Name, File, false);
|
|
|
|
if((Name.indexOf(searchText, 0, Qt::CaseInsensitive)) != -1) {
|
|
//match
|
|
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);
|
|
// add component category and module indexes to the icon
|
|
icon->setData(Qt::UserRole + 1, catIdx);
|
|
icon->setData(Qt::UserRole + 2, compIdx);
|
|
CompComps->addItem(icon);
|
|
}
|
|
}
|
|
compIdx++;
|
|
}
|
|
catIdx++;
|
|
}
|
|
// the "verilog-a user devices" is the last category, if present
|
|
QMapIterator<QString, QString> i(Module::vaComponents);
|
|
int compIdx = 0;
|
|
while (i.hasNext()) {
|
|
i.next();
|
|
// Just need path to bitmap, do not create an object
|
|
QString vaName, vaBitmap;
|
|
vacomponent::info (vaName, vaBitmap, false, i.value());
|
|
|
|
if((vaName.indexOf(searchText, 0, Qt::CaseInsensitive)) != -1) {
|
|
//match
|
|
|
|
// check if icon exists, fall back to default
|
|
QString iconPath = QucsSettings.QucsWorkDir.filePath(vaBitmap+".png");
|
|
|
|
QFile iconFile(iconPath);
|
|
QPixmap vaIcon;
|
|
|
|
if(iconFile.exists())
|
|
{
|
|
// load bitmap defined on the JSON symbol file
|
|
vaIcon = QPixmap(iconPath);
|
|
}
|
|
else
|
|
{
|
|
// default icon
|
|
vaIcon = QPixmap(":/bitmaps/editdelete.png");
|
|
}
|
|
|
|
// Add icon an name tag to dock
|
|
QListWidgetItem *icon = new QListWidgetItem(vaIcon, vaName);
|
|
icon->setToolTip(tr("verilog-a user devices") + ": " + vaName);
|
|
// Verilog-A is the last category
|
|
icon->setData(Qt::UserRole + 1, catIdx-1);
|
|
icon->setData(Qt::UserRole + 2, compIdx);
|
|
CompComps->addItem(icon);
|
|
}
|
|
compIdx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
void QucsApp::slotSearchClear()
|
|
{
|
|
// was in "search mode" ?
|
|
if (CompChoose->itemText(0) == tr("Search results")) {
|
|
CompChoose->removeItem(0); // remove the added "Search results" item
|
|
CompSearch->clear();
|
|
// go back to the panel selected before search started
|
|
slotSetCompView(ccCurIdx);
|
|
// the added "Search results" panel text will be removed by slotSetCompView()
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Is called when the mouse is clicked within the Component QIconView.
|
|
void QucsApp::slotSelectComponent(QListWidgetItem *item)
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
// delete previously selected elements
|
|
if(view->selElem != 0) delete view->selElem;
|
|
view->selElem = 0; // no component/diagram/painting selected
|
|
|
|
if(item == 0) { // mouse button pressed not over an item ?
|
|
CompComps->clearSelection(); // deselect component in ViewList
|
|
return;
|
|
}
|
|
|
|
//if(view->drawn)
|
|
// ((Q3ScrollView*)DocumentTab->currentWidget())->viewport()->update();
|
|
//view->drawn = false;
|
|
|
|
// toggle last toolbar button off
|
|
if(activeAction) {
|
|
activeAction->blockSignals(true); // do not call toggle slot
|
|
activeAction->setChecked(false); // set last toolbar button off
|
|
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;
|
|
|
|
// if symbol mode, only paintings are enabled.
|
|
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) {
|
|
// static component
|
|
view->selElem = (*mod->info) (CompName, CompFile_cptr, true);
|
|
} else {
|
|
// Verilog-A component
|
|
InfosVA = mod->infoVA;
|
|
// get JSON file out of item name on widgetitem
|
|
QString filename = Module::vaComponents[name];
|
|
if (InfosVA) {
|
|
view->selElem = (*InfosVA) (CompName, CompFile_qstr, true, filename);
|
|
}
|
|
}
|
|
|
|
// in "search mode" ?
|
|
if (CompChoose->itemText(0) == tr("Search results")) {
|
|
if (Infos || InfosVA) {
|
|
// change currently selected category, so the user will
|
|
// see where the component comes from
|
|
CompChoose->setCurrentIndex(catIdx+1); // +1 due to the added "Search Results" item
|
|
ccCurIdx = catIdx; // remember the category to select when exiting search
|
|
//!! comment out the above two lines if you would like that the search
|
|
//!! returns back to the last selected category instead
|
|
}
|
|
}
|
|
}
|
|
|
|
// ####################################################################
|
|
// ##### Functions for the menu that appears when right-clicking #####
|
|
// ##### on a file in the "Content" ListView. #####
|
|
// ####################################################################
|
|
|
|
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&)));
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Shows the menu.
|
|
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();
|
|
|
|
//test the item is valid
|
|
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);
|
|
|
|
//check changed file save
|
|
int z = 0; //search if the doc is loaded
|
|
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; //generate unique name
|
|
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))) { //check New Name exists
|
|
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;
|
|
}
|
|
//TODO: maybe require disable edit here
|
|
|
|
// refresh the schematic file path
|
|
//this->updateSchNameHash();
|
|
//this->updateSpiceNameHash();
|
|
|
|
slotUpdateTreeview();
|
|
}
|
|
}
|
|
|
|
void QucsApp::slotCMenuRename()
|
|
{
|
|
QModelIndex idx = Content->currentIndex();
|
|
|
|
//test the item is valid
|
|
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();
|
|
|
|
//test the item is valid
|
|
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());
|
|
}
|
|
|
|
// ################################################################
|
|
// ##### Functions that handle the project operations. #####
|
|
// ################################################################
|
|
|
|
// Checks for qucs directory and reads all existing Qucs projects.
|
|
void QucsApp::readProjects()
|
|
{
|
|
QString path = QucsSettings.projsDir.absolutePath();
|
|
QString homepath = QucsSettings.qucsWorkspaceDir.absolutePath();
|
|
|
|
if (path == homepath) {
|
|
// in Qucs Home, disallow further up in the dirs tree
|
|
m_homeDirModel->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs);
|
|
} else {
|
|
m_homeDirModel->setFilter(QDir::NoDot | QDir::AllDirs);
|
|
}
|
|
|
|
// set the root path
|
|
QModelIndex rootModelIndex = m_homeDirModel->setRootPath(path);
|
|
// assign the model to the proxy and the proxy to the view
|
|
m_proxyModel->setSourceModel(m_homeDirModel);
|
|
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
|
// sort by first column (file name, only column show in the QListView)
|
|
m_proxyModel->sort(0);
|
|
Projects->setModel(m_proxyModel);
|
|
// fix the listview on the root path of the model
|
|
Projects->setRootIndex(m_proxyModel->mapFromSource(rootModelIndex));
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Is called, when "Create New Project" button is pressed.
|
|
void QucsApp::slotButtonProjNew()
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Opens an existing project.
|
|
void QucsApp::openProject(const QString& Path)
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
QDir ProjDir(QDir::cleanPath(Path)); // the full path
|
|
QString openProjName = ProjDir.dirName(); // only the project directory name
|
|
|
|
if(!ProjDir.exists() || !ProjDir.isReadable()) { // check project directory
|
|
QMessageBox::critical(this, tr("Error"),
|
|
tr("Cannot access project directory: %1").arg(Path));
|
|
return;
|
|
}
|
|
|
|
if (!openProjName.endsWith("_prj")) { // should not happen
|
|
QMessageBox::critical(this, tr("Error"),
|
|
tr("Project directory name does not end in '_prj'(%1)").arg(openProjName));
|
|
return;
|
|
}
|
|
|
|
if(!closeAllFiles()) return; // close files and ask for saving them
|
|
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); // switch to "Content"-Tab
|
|
|
|
openProjName.chop(4); // remove "_prj" from name
|
|
ProjName = openProjName; // remember the name of project
|
|
QDir parentDir = QucsSettings.QucsWorkDir;
|
|
parentDir.cdUp();
|
|
// show name in title of main window
|
|
setWindowTitle( tr("Project: ") + ProjName + " (" + parentDir.absolutePath() + ") - " + windowTitle);
|
|
fillLibrariesTreeView();
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Is called when the open project menu is called.
|
|
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);
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Is called, when "Open Project" button is pressed.
|
|
void QucsApp::slotButtonProjOpen()
|
|
{
|
|
slotHideEdit();
|
|
|
|
QModelIndex idx = Projects->currentIndex();
|
|
if (!idx.isValid()) {
|
|
QMessageBox::information(this, tr("Info"),
|
|
tr("No project is selected !"));
|
|
} else {
|
|
slotListProjOpen(idx);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Is called when project is double-clicked to open it.
|
|
void QucsApp::slotListProjOpen(const QModelIndex &idx)
|
|
{
|
|
QString dName = idx.data().toString();
|
|
if (dName.endsWith("_prj")) { // it's a Qucs project
|
|
openProject(QucsSettings.projsDir.filePath(dName));
|
|
} else { // it's a normal directory
|
|
// change projects directory to the selected one
|
|
QucsSettings.projsDir.setPath(QucsSettings.projsDir.filePath(dName));
|
|
readProjects();
|
|
//repaint();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Is called when the close project menu is called.
|
|
void QucsApp::slotMenuProjClose()
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
if(!closeAllFiles()) return; // close files and ask for saving them
|
|
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); // switch to "Projects"-Tab
|
|
ProjName = "";
|
|
fillLibrariesTreeView();
|
|
}
|
|
|
|
// remove a directory recursively
|
|
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(); // only project directory name
|
|
|
|
if (!delProjName.endsWith("_prj")) { // should not happen
|
|
QMessageBox::critical(this, tr("Error"),
|
|
tr("Project directory name does not end in '_prj' (%1)").arg(delProjName));
|
|
return false;
|
|
}
|
|
|
|
delProjName.chop(4); // remove "_prj" from name
|
|
|
|
if(delProjName == ProjName) {
|
|
QMessageBox::information(this, tr("Info"),
|
|
tr("Cannot delete an open project !"));
|
|
return false;
|
|
}
|
|
|
|
// first ask, if really delete project ?
|
|
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;
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Is called, when "Delete Project" menu is activated.
|
|
void QucsApp::slotMenuProjDel()
|
|
{
|
|
QString d = QFileDialog::getExistingDirectory(
|
|
this, tr("Choose Project Directory for Deleting"),
|
|
QucsSettings.qucsWorkspaceDir.path(),
|
|
QFileDialog::ShowDirsOnly
|
|
| QFileDialog::DontResolveSymlinks);
|
|
|
|
deleteProject(d);
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Is called, when "Delete Project" button is pressed.
|
|
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()));
|
|
}
|
|
|
|
|
|
// ################################################################
|
|
// ##### Functions that handle the file operations for the #####
|
|
// ##### documents. #####
|
|
// ################################################################
|
|
|
|
void QucsApp::slotFileNew()
|
|
{
|
|
statusBar()->showMessage(tr("Creating new schematic..."));
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
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(); // disable text edit of component property
|
|
|
|
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(); // disable text edit of component property
|
|
TextDoc *d = new TextDoc(this, "");
|
|
int i = addDocumentTab(d);
|
|
DocumentTab->setCurrentIndex(i);
|
|
|
|
statusBar()->showMessage(tr("Ready."));
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
// Changes to the document "Name". If already open then it goes to it
|
|
// directly, otherwise it loads it.
|
|
bool QucsApp::gotoPage(const QString& Name)
|
|
{
|
|
int No = DocumentTab->currentIndex();
|
|
|
|
int i = 0;
|
|
QucsDoc * d = findDoc (Name, &i); // search, if page is already loaded
|
|
|
|
if(d) { // open page found ?
|
|
d->becomeCurrent(true);
|
|
DocumentTab->setCurrentIndex(i); // make new document the current
|
|
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()) { // load document if possible
|
|
delete d;
|
|
DocumentTab->setCurrentIndex(No);
|
|
return false;
|
|
}
|
|
slotChangeView();
|
|
if (Info.suffix() == "sym") {
|
|
// We dealing with a file containing *only* a symbol definition.
|
|
// Because of that we want to switch straight to symbol editing mode
|
|
// and skip any actions performed with a usual schematic.
|
|
Schematic *sch = (Schematic *)d;
|
|
slotSymbolEdit();
|
|
sch->setIsSymbolOnly(true);
|
|
} else if (is_sch) {
|
|
Schematic *sch = (Schematic *)d;
|
|
if (sch->checkDplAndDatNames()) sch->setChanged(true,true);
|
|
}
|
|
|
|
// if only an untitled document was open -> close it
|
|
if(getDoc(0)->getDocName().isEmpty())
|
|
if(!getDoc(0)->getDocChanged())
|
|
delete DocumentTab->widget(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
QString lastDirOpenSave; // to remember last directory and file
|
|
|
|
// --------------------------------------------------------------
|
|
void QucsApp::slotFileOpen()
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
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; // remember last directory and file
|
|
|
|
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;
|
|
|
|
// It's assumed that *.sym files contain *only* a symbol
|
|
// definition. We don't want these files to be subject
|
|
// of any activities or "optimizations" which may change
|
|
// the symbol.
|
|
if (!Doc->getDocName().endsWith(".sym")) {
|
|
updatePortNumber(Doc, Result);
|
|
}
|
|
slotUpdateTreeview();
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
void QucsApp::slotFileSave()
|
|
{
|
|
statusBar()->showMessage(tr("Saving file..."));
|
|
DocumentTab->blockSignals(true); // no user interaction during that time
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
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()) { // which is default directory ?
|
|
if(ProjName.isEmpty()) {
|
|
if(lastDirOpenSave.isEmpty()) s = QDir::homePath();
|
|
else s = lastDirOpenSave;
|
|
}
|
|
else s = QucsSettings.QucsWorkDir.path();
|
|
} else {
|
|
file_ext = QString("*.") + Info.suffix();
|
|
}
|
|
|
|
// list of known file extensions
|
|
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); // try to guess the best extension ...
|
|
ext = Info.suffix();
|
|
|
|
if(ext.isEmpty() || !extlist.contains(ext))
|
|
{
|
|
// if no extension was specified or is unknown
|
|
if (!isTextDocument (w))
|
|
{
|
|
// assume it is a schematic
|
|
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; // cancel
|
|
if(n == QMessageBox::No) continue;
|
|
}
|
|
|
|
// search, if document is open
|
|
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(); // remember last directory and file
|
|
|
|
n = Doc->save(); // SAVE
|
|
if(n < 0) return false;
|
|
|
|
// It's assumed that *.sym files contain *only* a symbol
|
|
// definition. We don't want these files to be subject
|
|
// of any activities or "optimizations" which may change
|
|
// the symbol.
|
|
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); // no user interaction during the time
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
if(!saveAs()) {
|
|
DocumentTab->blockSignals(false);
|
|
statusBar()->showMessage(tr("Saving aborted"), 3000);
|
|
statusBar()->showMessage(tr("Ready."));
|
|
return;
|
|
}
|
|
|
|
DocumentTab->blockSignals(false);
|
|
statusBar()->showMessage(tr("Ready."));
|
|
|
|
// refresh the schematic file path
|
|
slotRefreshSchPath();
|
|
|
|
if(!ProjName.isEmpty())
|
|
slotUpdateTreeview();
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------
|
|
void QucsApp::slotFileSaveAll()
|
|
{
|
|
statusBar()->showMessage(tr("Saving all files..."));
|
|
slotHideEdit(); // disable text edit of component property
|
|
DocumentTab->blockSignals(true); // no user interaction during the time
|
|
|
|
int No=0;
|
|
QucsDoc *Doc; // search, if page is already loaded
|
|
while((Doc=getDoc(No++)) != 0) {
|
|
if(Doc->getDocName().isEmpty()) // make document the current ?
|
|
DocumentTab->setCurrentIndex(No-1);
|
|
if (saveFile(Doc)) { // Hack! TODO: Maybe it's better to let slotFileChanged()
|
|
setDocumentTabChanged(No-1, false); // know about Tab number?
|
|
}
|
|
}
|
|
|
|
DocumentTab->blockSignals(false);
|
|
// Call update() to update subcircuit symbols in current Schematic document.
|
|
// TextDoc has no viewport, it needs no update.
|
|
QString tabType = DocumentTab->currentWidget()->metaObject()->className();
|
|
|
|
if (tabType == "Schematic") {
|
|
((Q3ScrollView*)DocumentTab->currentWidget())->viewport()->update();
|
|
}
|
|
statusBar()->showMessage(tr("Ready."));
|
|
|
|
// refresh the schematic file path
|
|
slotRefreshSchPath();
|
|
slotUpdateTreeview();
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
// Close the currently active file tab
|
|
void QucsApp::slotFileClose()
|
|
{
|
|
// Using file index -1 closes the currently active file window
|
|
closeFile(-1);
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
// Close the file tab specified by its index
|
|
void QucsApp::slotFileClose(int index)
|
|
{
|
|
// Call closeFile with a specific tab index
|
|
closeFile(index);
|
|
// Reset Tunerdialog
|
|
if (TuningMode) tunerDia->slotResetTunerDialog();
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
// Common function to close a file tab specified by its index
|
|
// checking for changes in the file before doing so. If called
|
|
// index == -1, the active document will be closed
|
|
void QucsApp::closeFile(int index)
|
|
{
|
|
statusBar()->showMessage(tr("Closing file..."));
|
|
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
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) { // if no document left, create an untitled
|
|
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;
|
|
|
|
|
|
//switchEditMode(true); // set schematic edit mode
|
|
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()
|
|
{
|
|
// pass the QUrl representation of a local file
|
|
QUrl url = QUrl::fromLocalFile(QDir::cleanPath(QucsSettings.DocDir + "/tutorial/" + QObject::sender()->objectName()));
|
|
QDesktopServices::openUrl(url);
|
|
}
|
|
|
|
void QucsApp::slotHelpTechnical()
|
|
{
|
|
// pass the QUrl representation of a local file
|
|
QUrl url = QUrl::fromLocalFile(QDir::cleanPath(QucsSettings.DocDir + "/technical/" + QObject::sender()->objectName()));
|
|
QDesktopServices::openUrl(url);
|
|
}
|
|
|
|
void QucsApp::slotHelpReport()
|
|
{
|
|
// pass the QUrl representation of a local file
|
|
QUrl url = QUrl::fromLocalFile(QDir::cleanPath(QucsSettings.DocDir + "/report/" + QObject::sender()->objectName()));
|
|
QDesktopServices::openUrl(url);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------
|
|
// Is called when another document is selected via the TabBar.
|
|
void QucsApp::slotChangeView()
|
|
{
|
|
QWidget *w = DocumentTab->currentWidget();
|
|
editText->setHidden (true); // disable text edit of component property
|
|
QucsDoc * Doc;
|
|
if(w==NULL)return;
|
|
// for text documents
|
|
if (isTextDocument (w)) {
|
|
TextDoc *d = (TextDoc*)w;
|
|
Doc = (QucsDoc*)d;
|
|
// update menu entries, etc. if necessary
|
|
magAll->setDisabled(true);
|
|
magSel->setDisabled(true);
|
|
if(cursorLeft->isEnabled())
|
|
switchSchematicDoc (false);
|
|
}
|
|
// for schematic documents
|
|
else {
|
|
Schematic *d = (Schematic*)w;
|
|
Doc = (QucsDoc*)d;
|
|
magAll->setDisabled(false);
|
|
magSel->setDisabled(false);
|
|
// already in schematic?
|
|
if(cursorLeft->isEnabled()) {
|
|
// which mode: schematic or symbol editor ?
|
|
if((CompChoose->count() > 1) == d->getSymbolMode())
|
|
changeSchematicSymbolMode (d);
|
|
}
|
|
else {
|
|
switchSchematicDoc(true);
|
|
changeSchematicSymbolMode(d);
|
|
}
|
|
|
|
showGrid->setChecked(d->getGridOn());
|
|
}
|
|
|
|
Doc->becomeCurrent(true);
|
|
|
|
// TODO proper window title
|
|
// QFileInfo Info (Doc-> getDocName());
|
|
//
|
|
// if (!ProjName.isEmpty()) {
|
|
// QDir parentDir = QucsSettings.QucsWorkDir;
|
|
// parentDir.cdUp();
|
|
// setWindowTitle(tr("Project: ") + ProjName + " (" + parentDir.absolutePath() + ") "
|
|
// + windowTitle);
|
|
// } else {
|
|
// setWindowTitle(Info.fileName() + " (" + Info.filePath() +") - " + windowTitle);
|
|
// }
|
|
|
|
HierarchyHistory.clear();
|
|
popH->setEnabled(false);
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
void QucsApp::slotFileSettings ()
|
|
{
|
|
editText->setHidden (true); // disable text edit of component property
|
|
|
|
QWidget * w = DocumentTab->currentWidget ();
|
|
if (isTextDocument (w)) {
|
|
QucsDoc * Doc = (QucsDoc *) ((TextDoc *) w);
|
|
QString ext = Doc->fileSuffix ();
|
|
// Octave properties
|
|
if (ext == "m" || ext == "oct") {
|
|
}
|
|
// Verilog-A properties
|
|
else if (ext == "va") {
|
|
VASettingsDialog * d = new VASettingsDialog ((TextDoc *) w);
|
|
d->exec ();
|
|
}
|
|
// VHDL and Verilog-HDL properties
|
|
else {
|
|
DigiSettingsDialog * d = new DigiSettingsDialog ((TextDoc *) w);
|
|
d->exec ();
|
|
}
|
|
}
|
|
// schematic properties
|
|
else {
|
|
SettingsDialog * d = new SettingsDialog ((Schematic *) w);
|
|
d->exec ();
|
|
|
|
// TODO: It would be better to emit a signal to notify all subscribers
|
|
// that the diagram settings have changed.
|
|
showGrid->setChecked(static_cast<Schematic*>(w)->getGridOn());
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
void QucsApp::slotApplSettings()
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
QucsSettingsDialog *d = new QucsSettingsDialog(this);
|
|
d->exec();
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
void QucsApp::slotRefreshSchPath()
|
|
{
|
|
//this->updateSchNameHash();
|
|
//this->updateSpiceNameHash();
|
|
|
|
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";
|
|
}
|
|
|
|
// update all occurencies of subcircuit in all open documents
|
|
No = 0;
|
|
QWidget *w;
|
|
Component *pc_tmp;
|
|
while((w=DocumentTab->widget(No++)) != 0) {
|
|
if(isTextDocument (w)) continue;
|
|
|
|
// start from the last to omit re-appended components
|
|
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); // delete and re-append component
|
|
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
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
// printCurrentDocument: call printerwriter to print document
|
|
void QucsApp::printCurrentDocument(bool fitToPage)
|
|
{
|
|
statusBar()->showMessage(tr("Printing..."));
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
PrinterWriter *writer = new PrinterWriter();
|
|
writer->setFitToPage(fitToPage);
|
|
writer->print(DocumentTab->currentWidget());
|
|
delete writer;
|
|
|
|
statusBar()->showMessage(tr("Ready."));
|
|
return;
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
void QucsApp::slotFilePrint()
|
|
{
|
|
printCurrentDocument(false);
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
// Fit printed content to page size.
|
|
void QucsApp::slotFilePrintFit()
|
|
{
|
|
printCurrentDocument(true);
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// Exits the application.
|
|
void QucsApp::slotFileQuit()
|
|
{
|
|
statusBar()->showMessage(tr("Exiting application..."));
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
saveSettings();
|
|
if(closeAllFiles()) {
|
|
emit signalKillEmAll(); // kill all subprocesses
|
|
qApp->quit();
|
|
}
|
|
|
|
statusBar()->showMessage(tr("Ready."));
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
// To get all close events.
|
|
void QucsApp::closeEvent(QCloseEvent* Event)
|
|
{
|
|
saveSettings();
|
|
if(closeAllFiles()) {
|
|
emit signalKillEmAll(); // kill all subprocesses
|
|
Event->accept();
|
|
qApp->quit();
|
|
}
|
|
else
|
|
Event->ignore();
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
// Saves settings
|
|
void QucsApp::saveSettings()
|
|
{
|
|
saveApplSettings();
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// Is called when the toolbar button is pressed to go into a subcircuit.
|
|
void QucsApp::slotIntoHierarchy()
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
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()); //remember for the way back
|
|
popH->setEnabled(true);
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// Is called when the toolbar button is pressed to leave a subcircuit.
|
|
void QucsApp::slotPopHierarchy()
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
|
|
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(); // disable text edit of component property
|
|
getDoc()->showAll();
|
|
}
|
|
|
|
// --------------------------------------------------------------
|
|
void QucsApp::slotZoomToSelection()
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
getDoc()->zoomToSelection();
|
|
}
|
|
|
|
// -----------------------------------------------------------
|
|
// Sets the scale factor to 1.
|
|
void QucsApp::slotShowOne()
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
getDoc()->showNoZoom();
|
|
}
|
|
|
|
// -----------------------------------------------------------
|
|
void QucsApp::slotZoomOut()
|
|
{
|
|
slotHideEdit(); // disable text edit of component property
|
|
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(); // remember from which Tab the tuner was started
|
|
if (isTextDocument(w))
|
|
{
|
|
//Probably digital Simulation
|
|
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;
|
|
}
|
|
|
|
|
|
// instance of tuner
|
|
TuningMode = true;
|
|
tunerDia = new TunerDialog(w, this);//The object can be instantiated here since when checked == false the memory will be freed
|
|
// inform the Tuner Dialog when a component is deleted
|
|
connect(d, SIGNAL(signalComponentDeleted(Component *)),
|
|
tunerDia, SLOT(slotComponentDeleted(Component *)));
|
|
|
|
slotHideEdit(); // disable text edit of component property
|
|
simulateToolbar->setEnabled(false); // disable workToolbar to preserve TuneMouseAction
|
|
|
|
MousePressAction = &MouseActions::MPressTune;
|
|
MouseReleaseAction = 0; //While Tune is active release is not needed. This puts Press Action back to normal select
|
|
|
|
tunerDia->show();
|
|
}
|
|
else
|
|
{
|
|
this->simulateToolbar->setEnabled(true);
|
|
|
|
// MouseActions are reset in closing of tunerDialog class
|
|
tunerDia->close();//According to QWidget documentation (http://doc.qt.io/qt-4.8/qwidget.html#close),
|
|
//the object is removed since it has the Qt::WA_DeleteOnClose flag
|
|
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); // check if schematic is already open in a Tab
|
|
|
|
if (d)
|
|
{
|
|
// schematic already loaded
|
|
// this should be the simulation schematic of this data display
|
|
w = DocumentTab->widget(z);
|
|
}
|
|
else
|
|
{
|
|
// schematic not yet loaded
|
|
int i = 0;
|
|
int No = DocumentTab->currentIndex(); // remember current Tab
|
|
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); // temporarily switch to the newly created Tab
|
|
|
|
if(d->load()) {
|
|
// document loaded successfully
|
|
w = DocumentTab->widget(i);
|
|
} else {
|
|
// failed loading document
|
|
// load() above has already shown a QMessageBox about not being able to load the file
|
|
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();
|
|
|
|
//Check is schematic digital
|
|
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(); // disable text edit of component property
|
|
|
|
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 document 'untitled' ...
|
|
if(!saveAs()) return; // ... save schematic before
|
|
|
|
// Perhaps the document was modified from another program ?
|
|
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") {
|
|
// It is an Octave script.
|
|
if(Doc->getDocChanged())
|
|
Doc->save();
|
|
slotViewOctaveDock(true);
|
|
octave->runOctaveScript(Doc->getDocName());
|
|
return;
|
|
}
|
|
|
|
if (ext == "dpl") {
|
|
// simulation started from Data Display: open referenced schematic
|
|
w = getSchematicWidget(Doc);
|
|
}
|
|
|
|
SimMessage *sim = new SimMessage(w, this);
|
|
sim->setDocWidget(w);
|
|
// disconnect is automatically performed, if one of the involved objects
|
|
// is destroyed !
|
|
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 { //It doesn't make sense to connect the slot outside the tuning mode
|
|
sim->show();
|
|
}
|
|
|
|
if(!sim->startProcess()) {
|
|
if (TuningMode == true) sim->show();//The message window is hidden when the tuning mode is active, but in case of error such window pops up
|
|
return;
|
|
}
|
|
|
|
// to kill it before qucs ends
|
|
connect(this, SIGNAL(signalKillEmAll()), sim, SLOT(slotClose()));
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Is called after the simulation process terminates.
|
|
void QucsApp::slotAfterSimulation(int Status, SimMessage *sim)
|
|
{
|
|
|
|
if(Status != 0) { // errors ocurred ?
|
|
if (TuningMode) {
|
|
sim->show();
|
|
tunerDia->SimulationEnded();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(sim->ErrText->document()->lineCount() > 1) // were there warnings ?
|
|
slotShowWarnings();
|
|
|
|
int i=0;
|
|
QWidget *w; // search, if page is still open
|
|
while((w=DocumentTab->widget(i++)) != 0)
|
|
if(w == sim->DocWidget)
|
|
break;
|
|
|
|
if(sim->showBias == 0) { // paint dc bias into schematic ?
|
|
sim->slotClose(); // close and delete simulation window
|
|
if(w) { // schematic still open ?
|
|
SweepDialog *Dia = new SweepDialog((Schematic*)sim->DocWidget);
|
|
|
|
// silence warning about unused variable.
|
|
Q_UNUSED(Dia)
|
|
}
|
|
}
|
|
else {
|
|
if(sim->SimRunScript) {
|
|
// run script
|
|
octave->startOctave();
|
|
octave->runOctaveScript(sim->Script);
|
|
}
|
|
if(sim->SimOpenDpl) {
|
|
// switch to data display
|
|
if(sim->DataDisplay.right(2) == ".m" ||
|
|
sim->DataDisplay.right(4) == ".oct") { // Is it an Octave script?
|
|
octave->startOctave();
|
|
octave->runOctaveScript(sim->DataDisplay);
|
|
}
|
|
else
|
|
slotChangePage(sim->DocName, sim->DataDisplay);
|
|
//sim->slotClose(); // close and delete simulation window
|
|
}
|
|
else {
|
|
if(w) {
|
|
if(!isTextDocument (sim->DocWidget)) {
|
|
int idx = Category::getModulesNr (QObject::tr("diagrams"));
|
|
CompChoose->setCurrentIndex(idx); // switch to diagrams
|
|
slotSetCompView (idx);
|
|
// load recent simulation data (if document is still open)
|
|
((Schematic*)sim->DocWidget)->reloadGraphs();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!isTextDocument (sim->DocWidget)) {
|
|
//((Schematic*)sim->DocWidget)->viewport()->update();
|
|
((Schematic*)DocumentTab->currentWidget())->viewport()->update();
|
|
}
|
|
|
|
// Kill the simulation process, otherwise we have 200+++ sims in the background
|
|
if(TuningMode) {
|
|
sim->slotClose();
|
|
tunerDia->SimulationEnded();
|
|
}
|
|
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
void QucsApp::slotDCbias()
|
|
{
|
|
getDoc()->setShowBias(0);
|
|
slotSimulate();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Changes to the corresponding data display page or vice versa.
|
|
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; // search, if page is already loaded
|
|
QucsDoc * d = findDoc (Name, &z);
|
|
|
|
if(d)
|
|
DocumentTab->setCurrentIndex(z);
|
|
else { // no open page found ?
|
|
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)) { // try to load document
|
|
file.close();
|
|
if(!d->load()) {
|
|
delete d;
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
if(file.open(QIODevice::ReadWrite)) { // if document doesn't exist, create
|
|
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 page not ...
|
|
if(!isTextDocument (w))
|
|
((Schematic*)w)->reloadGraphs(); // ... changes, reload here !
|
|
|
|
TabView->setCurrentIndex(2); // switch to "Component"-Tab
|
|
if (Name.right(4) == ".dpl") {
|
|
int i = Category::getModulesNr (QObject::tr("diagrams"));
|
|
CompChoose->setCurrentIndex(i); // switch to diagrams
|
|
slotSetCompView (i);
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// Changes to the data display of current page.
|
|
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());
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// Called when file name in Project View is double-clicked
|
|
// or open is selected in the context menu
|
|
void QucsApp::slotOpenContent(const QModelIndex &idx)
|
|
{
|
|
editText->setHidden(true); // disable text edit of component property
|
|
|
|
//test the item is valid
|
|
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()) // is subcircuit ?
|
|
if(extName == "sch") return;
|
|
|
|
select->blockSignals(true); // switch on the 'select' action ...
|
|
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()); // open datasets with text editor
|
|
return;
|
|
}
|
|
|
|
// File is no Qucs file, so go through list and search a user
|
|
// defined program to open it.
|
|
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();
|
|
//Program->setCommunication(0);
|
|
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++;
|
|
}
|
|
|
|
// If no appropriate program was found, open as text file.
|
|
editFile(Info.absoluteFilePath()); // open datasets with text editor
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Is called when the mouse is clicked within the Content QListView.
|
|
void QucsApp::slotSelectSubcircuit(const QModelIndex &idx)
|
|
{
|
|
editText->setHidden(true); // disable text edit of component property
|
|
|
|
if(!idx.isValid()) { // mouse button pressed not over an item ?
|
|
Content->clearSelection(); // deselect component in ListView
|
|
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; // return, if not a subcircuit
|
|
}
|
|
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; // Forbid to paste subcircuit into itself.
|
|
|
|
// delete previously selected elements
|
|
if(view->selElem != 0) delete view->selElem;
|
|
view->selElem = 0;
|
|
|
|
// toggle last toolbar button off
|
|
if(activeAction) {
|
|
activeAction->blockSignals(true); // do not call toggle slot
|
|
activeAction->setChecked(false); // set last toolbar button off
|
|
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;
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Is called when the mouse is clicked within the Content QListView.
|
|
void QucsApp::slotSelectLibComponent(QTreeWidgetItem *item)
|
|
{
|
|
// get the current document
|
|
Schematic *Doc = (Schematic*)DocumentTab->currentWidget();
|
|
|
|
// if the current document is a schematic activate the paste
|
|
if(!isTextDocument(Doc))
|
|
{
|
|
// if there's not a higher level item, this is a top level item,
|
|
// not a component item so return
|
|
if(item->parent() == 0) return;
|
|
if(item->text(1).isEmpty()) return; // return, if not a subcircuit
|
|
|
|
// copy the subcircuit schematic to the clipboard
|
|
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);
|
|
|
|
// activate the paste command
|
|
slotEditPaste (true);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------
|
|
// This function is called if the document type changes, i.e.
|
|
// from schematic to text document or vice versa.
|
|
void QucsApp::switchSchematicDoc (bool SchematicMode)
|
|
{
|
|
// switch our scroll key actions on/off according to SchematicMode
|
|
cursorLeft->setEnabled(SchematicMode);
|
|
cursorRight->setEnabled(SchematicMode);
|
|
cursorUp->setEnabled(SchematicMode);
|
|
cursorDown->setEnabled(SchematicMode);
|
|
|
|
// text document
|
|
if (!SchematicMode) {
|
|
if (activeAction) {
|
|
activeAction->blockSignals (true); // do not call toggle slot
|
|
activeAction->setChecked(false); // set last toolbar button off
|
|
activeAction->blockSignals (false);
|
|
}
|
|
activeAction = select;
|
|
select->blockSignals (true);
|
|
select->setChecked(true);
|
|
select->blockSignals (false);
|
|
}
|
|
// schematic document
|
|
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); // only export schematic, no text
|
|
|
|
editFind->setEnabled (!SchematicMode);
|
|
insEntity->setEnabled (!SchematicMode);
|
|
|
|
buildModule->setEnabled(!SchematicMode); // only build if VA document
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
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);
|
|
// no search in "symbol painting mode" as only paintings should be used
|
|
CompSearch->setEnabled(SchematicMode);
|
|
CompSearchClear->setEnabled(SchematicMode);
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
void QucsApp::changeSchematicSymbolMode(Schematic *Doc)
|
|
{
|
|
if(Doc->getSymbolMode()) {
|
|
// go into select modus to avoid placing a forbidden element
|
|
select->setChecked(true);
|
|
|
|
switchEditMode(false);
|
|
}
|
|
else
|
|
switchEditMode(true);
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
bool QucsApp::isTextDocument(QWidget *w) {
|
|
return w->inherits("QPlainTextEdit");
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// Is called if the "symEdit" action is activated, i.e. if the user
|
|
// switches between the two painting mode: Schematic and (subcircuit)
|
|
// symbol.
|
|
void QucsApp::slotSymbolEdit()
|
|
{
|
|
QWidget *w = DocumentTab->currentWidget();
|
|
|
|
// in a text document (e.g. VHDL)
|
|
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."));
|
|
}
|
|
// set 'DataDisplay' document of text file to symbol file
|
|
QFileInfo Info(TDoc->getDocName());
|
|
QString sym = Info.completeBaseName()+".sym";
|
|
TDoc->setDataDisplay(sym);
|
|
|
|
// symbol file already loaded?
|
|
int paint_mode = 0;
|
|
if (!findDoc (QucsSettings.QucsWorkDir.filePath(sym)))
|
|
paint_mode = 1;
|
|
|
|
// change current page to appropriate symbol file
|
|
slotChangePage(TDoc->getDocName(),TDoc->getDataDisplay());
|
|
|
|
// set 'DataDisplay' document of symbol file to original text file
|
|
Schematic *SDoc = (Schematic*)DocumentTab->currentWidget();
|
|
SDoc->setDataDisplay(Info.fileName());
|
|
|
|
// change into symbol mode
|
|
if (paint_mode) // but only switch coordinates if newly loaded
|
|
SDoc->switchPaintMode();
|
|
SDoc->setSymbolMode(true);
|
|
changeSchematicSymbolMode(SDoc);
|
|
SDoc->becomeCurrent(true);
|
|
SDoc->viewport()->update();
|
|
}
|
|
// in a normal schematic, symbol file
|
|
else {
|
|
Schematic *SDoc = (Schematic*)w;
|
|
if (!SDoc->getIsSymbolOnly()) {
|
|
slotHideEdit(); // disable text edit of component property
|
|
SDoc->switchPaintMode(); // twist the view coordinates
|
|
changeSchematicSymbolMode(SDoc);
|
|
SDoc->becomeCurrent(true);
|
|
SDoc->viewport()->update();
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------
|
|
void QucsApp::slotPowerMatching()
|
|
{
|
|
QWidget *w = DocumentTab->currentWidget(); // remember from which Tab the tuner was started
|
|
if (isTextDocument(w)) return;
|
|
if(!view->focusElement) return;
|
|
if(view->focusElement->Type != isMarker) return;
|
|
Marker *pm = (Marker*)view->focusElement;
|
|
|
|
// double Z0 = 50.0;
|
|
QString Var = pm->pGraph->Var;
|
|
double Imag = pm->powImag();
|
|
if(Var == "Sopt") // noise matching ?
|
|
Imag *= -1.0;
|
|
|
|
MatchDialog *Dia = new MatchDialog(this);
|
|
// Dia->Ref1Edit->setText(QString::number(Z0));
|
|
Dia->setS11LineEdits(pm->powReal(), Imag);
|
|
Dia->setFrequency(pm->powFreq());
|
|
Dia->setTwoPortMatch(false); // will also cause the corresponding impedance LineEdit to be updated
|
|
|
|
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(); // remember from which Tab the tuner was started
|
|
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();
|
|
|
|
// FIXME: use normal Diagrams.
|
|
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;
|
|
// search for values for chosen frequency
|
|
for(z=0; z<Data->count; z++)
|
|
if(*(Value++) == Freq) break;
|
|
|
|
// get S-parameters
|
|
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;
|
|
}
|
|
|
|
// -----------------------------------------------------------
|
|
// Is called if the "edit" action is clicked on right mouse button menu.
|
|
void QucsApp::slotEditElement()
|
|
{
|
|
if(view->focusMEvent)
|
|
view->editElement((Schematic*)DocumentTab->currentWidget(), view->focusMEvent);
|
|
}
|
|
|
|
// -----------------------------------------------------------
|
|
// Hides the edit for component property. Called e.g. if QLineEdit
|
|
// looses the focus.
|
|
void QucsApp::slotHideEdit()
|
|
{
|
|
editText->setParent(this, Qt::WindowFlags());
|
|
editText->setHidden(true);
|
|
}
|
|
|
|
// -----------------------------------------------------------
|
|
// set document tab icon to smallsave_xpm or empty_xpm
|
|
void QucsApp::slotFileChanged(bool changed)
|
|
{
|
|
setDocumentTabChanged(DocumentTab->currentIndex(), changed);
|
|
}
|
|
|
|
// -----------------------------------------------------------
|
|
// Update project view by call refresh function
|
|
// looses the focus.
|
|
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();
|
|
}
|
|
}
|
|
*/
|
|
// -----------------------------------------------------------
|
|
// update the list of paths, pruning non-existing paths
|
|
void QucsApp::updatePathList()
|
|
{
|
|
// check each path actually exists, if not remove it
|
|
QMutableListIterator<QString> i(qucsPathList);
|
|
while (i.hasNext()) {
|
|
i.next();
|
|
QDir thispath(i.value());
|
|
if (!thispath.exists())
|
|
{
|
|
// the path does not exist, remove it from the list
|
|
i.remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
// replace the old path list with a new one
|
|
void QucsApp::updatePathList(QStringList newPathList)
|
|
{
|
|
// clear out the old path list
|
|
qucsPathList.clear();
|
|
|
|
// copy the new path into the path list
|
|
for (const QString& path : newPathList)
|
|
{
|
|
qucsPathList.append(path);
|
|
}
|
|
// do the normal path update operations
|
|
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 *sch = (Schematic*)DocumentTab->currentWidget();
|
|
if (TuningMode) {
|
|
QFileInfo Info(sch->getDocName());
|
|
QString ext = Info.suffix();
|
|
if (ext == "dpl") {
|
|
QucsDoc *Doc = (QucsDoc *)sch;
|
|
sch = (Schematic *) getSchematicWidget(Doc);
|
|
if (sch == nullptr) return;
|
|
}
|
|
}
|
|
|
|
if (sch->getDocName().isEmpty()) {
|
|
auto biasState = sch->getShowBias();
|
|
QMessageBox::warning(this,tr("Simulate schematic"),
|
|
tr("Schematic not saved! Simulation of unsaved schematic "
|
|
"not possible. Save schematic first!"));
|
|
slotFileSaveAs();
|
|
sch->setShowBias(biasState);
|
|
}
|
|
ExternSimDialog *SimDlg = new ExternSimDialog(sch);
|
|
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 || sch->getShowBias() == 0) SimDlg->slotStart();
|
|
else SimDlg->exec();
|
|
/*disconnect(SimDlg,SIGNAL(simulated()),this,SLOT(slotAfterSpiceSimulation()));
|
|
disconnect(SimDlg,SIGNAL(warnings()),this,SLOT(slotShowWarnings()));
|
|
disconnect(SimDlg,SIGNAL(success()),this,SLOT(slotResetWarnings()));*/
|
|
/*if (SimDlg->wasSimulated && sch->getSimOpenDpl())
|
|
if (sch->getShowBias() < 1) slotChangePage(sch->getDocName(),sch->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 *sch = (Schematic*)DocumentTab->currentWidget();
|
|
ExternSimDialog *SimDlg = new ExternSimDialog(sch, true);
|
|
SimDlg->slotSaveNetlist();
|
|
delete SimDlg;
|
|
}
|
|
}
|
|
|
|
void QucsApp::slotSaveCdlNetlist()
|
|
{
|
|
if (!isTextDocument(DocumentTab->currentWidget()))
|
|
{
|
|
Schematic* schematic = dynamic_cast<Schematic*>(DocumentTab->currentWidget());
|
|
Q_ASSERT(schematic != nullptr);
|
|
|
|
#ifdef NETLIST_CDL_TO_CONSOLE // for fast testing purposes
|
|
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();
|
|
}
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
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); // switch to diagrams
|
|
slotSetCompView (idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
sch->reloadGraphs();
|
|
sch->viewport()->update();
|
|
if(sch->getSimRunScript()) {
|
|
// run script
|
|
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) { // it's an icon
|
|
QString dName = fileName(index);
|
|
if (dName.endsWith("_prj")) { // it's a Qucs project
|
|
// for some reason SVG does not always work on Windows, so use PNG
|
|
return QIcon(":bitmaps/hicolor/128x128/apps/qucs.png");
|
|
}
|
|
}
|
|
// return default system icon
|
|
return QFileSystemModel::data(index, role);
|
|
}
|
|
|
|
// function below is adapted from https://stackoverflow.com/questions/10789284/qfilesystemmodel-sorting-dirsfirst
|
|
bool QucsSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
|
{
|
|
// if sorting by file names column
|
|
if (sortColumn() == 0) {
|
|
QucsFileSystemModel *model = qobject_cast<QucsFileSystemModel*>(sourceModel());
|
|
// get the current sort order (do we need this ?)
|
|
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 DotAndDot move in the beginning
|
|
if (sourceModel()->data(left).toString() == "..")
|
|
return asc;
|
|
if (sourceModel()->data(right).toString() == "..")
|
|
return !asc;
|
|
|
|
// move dirs upper
|
|
if (!leftFileInfo.isDir() && rightFileInfo.isDir()) {
|
|
return !asc;
|
|
}
|
|
if (leftFileInfo.isDir() && !rightFileInfo.isDir()) {
|
|
return asc;
|
|
}
|
|
// move dirs ending in '_prj' upper
|
|
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);
|
|
}
|