schematic.cpp
---------------
begin : Sat Mar 3 2006
copyright : (C) 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. *
* *
***************************************************************************/
#include <algorithm>
#include <cassert>
#include <limits.h>
#include <stdlib.h>
#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QDebug>
#include <QDir>
#include <QDragEnterEvent>
#include <QDragLeaveEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QEvent>
#include <QFileInfo>
#include <QLineEdit>
#include <QListWidget>
#include <QMouseEvent>
#include <QPaintDevice>
#include <QPainter>
#include <QPixmap>
#include <QPoint>
#include <QPrinter>
#include <QRect>
#include <QTextStream>
#include <QUrl>
#include <QWheelEvent>
#include <qt3_compat/qt_compat.h>
#include <QRegularExpression>
#include "components/vafile.h"
#include "components/verilogfile.h"
#include "components/vhdlfile.h"
#include "diagrams/diagrams.h"
#include "main.h"
#include "mouseactions.h"
#include "node.h"
#include "paintings/paintings.h"
#include "qucs.h"
#include "schematic.h"
#include "settings.h"
#include "textdoc.h"
#include "misc.h"
Q3PtrList<Wire> SymbolWires;
Q3PtrList<Node> SymbolNodes;
Q3PtrList<Diagram> SymbolDiags;
Q3PtrList<Component> SymbolComps;
If \c point does not lie within \c rect then returns a new
rectangle made by enlarging the source rectangle to include
the \c point. Otherwise returns a rectangle of the same size.
*/
static QRect includePoint(QRect rect, QPoint point) {
if (rect.contains(point)) {
return rect;
} else {
return rect.united(QRect{point, point});
}
}
Schematic::Schematic(QucsApp *App_, const QString &Name_) :
QucsDoc(App_, Name_),
a_Wires(&a_DocWires),
a_DocWires(),
a_Nodes(&a_DocNodes),
a_DocNodes(),
a_Diagrams(&a_DocDiags),
a_DocDiags(),
a_Paintings(&a_DocPaints),
a_DocPaints(),
a_Components(&a_DocComps),
a_DocComps(),
a_SymbolPaints(),
a_PostedPaintEvents(),
a_symbolMode(false),
a_isSymbolOnly(false),
a_GridX(10),
a_GridY(10),
a_ViewX1(0),
a_ViewY1(0),
a_ViewX2(1),
a_ViewY2(1),
a_showFrame(0),
a_Frame_Text0(tr("Title")),
a_Frame_Text1(tr("Drawn By:")),
a_Frame_Text2(tr("Date:")),
a_Frame_Text3(tr("Revision:")),
a_tmpScale(1.0),
a_tmpViewX1(-200),
a_tmpViewY1(-200),
a_tmpViewX2(200),
a_tmpViewY2(200),
a_tmpUsedX1(-200),
a_tmpUsedY1(-200),
a_tmpUsedX2(200),
a_tmpUsedY2(200),
a_undoActionIdx(0),
a_undoAction((QVector<QString*>() << new QString(" i\n</>\n</>\n</>\n</>\n"))),
a_undoSymbolIdx(0),
a_undoSymbol((QVector<QString*>() << new QString(" i\n</>\n</>\n</>\n</>\n"))),
a_UsedX1(INT_MAX),
a_UsedY1(INT_MAX),
a_UsedX2(INT_MAX),
a_UsedY2(INT_MAX),
a_previousCursorPosition(),
a_dragIsOkay(false),
a_FileInfo(),
a_Signals(),
a_PortTypes(),
a_isAnalog(false),
a_isVerilog(false),
a_creatingLib(false)
{
setFont(QucsSettings.font);
a_GridColor = _settings::Get().item<QString>("GridColor");
a_tmpPosX = a_tmpPosY = -100;
a_DocComps.setAutoDelete(true);
a_DocWires.setAutoDelete(true);
a_DocNodes.setAutoDelete(true);
a_DocDiags.setAutoDelete(true);
a_DocPaints.setAutoDelete(true);
a_SymbolPaints.setAutoDelete(true);
setVScrollBarMode(Q3ScrollView::AlwaysOn);
setHScrollBarMode(Q3ScrollView::AlwaysOn);
misc::setWidgetBackgroundColor(viewport(), QucsSettings.BGColor);
viewport()->setMouseTracking(true);
viewport()->setAcceptDrops(true);
connect(this, SIGNAL(horizontalSliderReleased()), viewport(), SLOT(update()));
connect(this, SIGNAL(verticalSliderReleased()), viewport(), SLOT(update()));
if (App_) {
connect(this,SIGNAL(signalCursorPosChanged(int, int, QString)),App_,SLOT(printCursorPosition(int, int, QString)));
connect(this, SIGNAL(horizontalSliderPressed()), App_, SLOT(slotHideEdit()));
connect(this, SIGNAL(verticalSliderPressed()), App_, SLOT(slotHideEdit()));
connect(this, SIGNAL(signalUndoState(bool)), App_, SLOT(slotUpdateUndo(bool)));
connect(this, SIGNAL(signalRedoState(bool)), App_, SLOT(slotUpdateRedo(bool)));
connect(this, SIGNAL(signalFileChanged(bool)), App_, SLOT(slotFileChanged(bool)));
}
}
Schematic::~Schematic() {}
bool Schematic::createSubcircuitSymbol()
{
unsigned int countPort = adjustPortNumbers();
if (a_SymbolPaints.count() != countPort)
return false;
int h = 30 * ((countPort - 1) / 2) + 10;
a_SymbolPaints.prepend(new ID_Text(-20, h + 4));
a_SymbolPaints.append(new GraphicLine(-20, -h, 40, 0, QPen(Qt::darkBlue, 2)));
a_SymbolPaints.append(new GraphicLine(20, -h, 0, 2 * h, QPen(Qt::darkBlue, 2)));
a_SymbolPaints.append(new GraphicLine(-20, h, 40, 0, QPen(Qt::darkBlue, 2)));
a_SymbolPaints.append(new GraphicLine(-20, -h, 0, 2 * h, QPen(Qt::darkBlue, 2)));
unsigned int i = 0, y = 10 - h;
while (i < countPort) {
i++;
a_SymbolPaints.append(new GraphicLine(-30, y, 10, 0, QPen(Qt::darkBlue, 2)));
a_SymbolPaints.at(i)->setCenter(-30, y);
if (i == countPort)
break;
i++;
a_SymbolPaints.append(new GraphicLine(20, y, 10, 0, QPen(Qt::darkBlue, 2)));
a_SymbolPaints.at(i)->setCenter(30, y);
y += 60;
}
return true;
}
void Schematic::becomeCurrent(bool update)
{
emit signalCursorPosChanged(0, 0, "");
if (a_symbolMode) {
a_App->symEdit->setText(tr("Edit Schematic"));
a_App->symEdit->setStatusTip(tr("Edits the schematic"));
a_App->symEdit->setWhatsThis(tr("Edit Schematic\n\nEdits the schematic"));
} else {
a_App->symEdit->setText(tr("Edit Circuit Symbol"));
a_App->symEdit->setStatusTip(tr("Edits the symbol for this schematic"));
a_App->symEdit->setWhatsThis(
tr("Edit Circuit Symbol\n\nEdits the symbol for this schematic"));
}
if (a_symbolMode) {
a_Nodes = &SymbolNodes;
a_Wires = &SymbolWires;
a_Diagrams = &SymbolDiags;
a_Paintings = &a_SymbolPaints;
a_Components = &SymbolComps;
if (!a_DocName.endsWith(".sym") && createSubcircuitSymbol()) {
updateAllBoundingRect();
setChanged(true, true);
}
emit signalUndoState(a_undoSymbolIdx != 0);
emit signalRedoState(a_undoSymbolIdx != a_undoSymbol.size() - 1);
} else {
a_Nodes = &a_DocNodes;
a_Wires = &a_DocWires;
a_Diagrams = &a_DocDiags;
a_Paintings = &a_DocPaints;
a_Components = &a_DocComps;
emit signalUndoState(a_undoActionIdx != 0);
emit signalRedoState(a_undoActionIdx != a_undoAction.size() - 1);
if (update)
reloadGraphs();
}
}
void Schematic::setName(const QString &Name_)
{
a_DocName = Name_;
QFileInfo Info(a_DocName);
QString base = Info.completeBaseName();
QString ext = Info.suffix();
a_DataSet = base + ".dat";
a_Script = base + ".m";
if (ext != "dpl")
a_DataDisplay = base + ".dpl";
else
a_DataDisplay = base + ".sch";
}
void Schematic::setChanged(bool c, bool fillStack, char Op)
{
if ((!a_DocChanged) && c)
emit signalFileChanged(true);
else if (a_DocChanged && (!c))
emit signalFileChanged(false);
a_DocChanged = c;
a_showBias = -1;
if (!fillStack)
return;
if (a_symbolMode) {
while (a_undoSymbol.size() > a_undoSymbolIdx + 1) {
delete a_undoSymbol.last();
a_undoSymbol.pop_back();
}
a_undoSymbol.append(new QString(createSymbolUndoString(Op)));
a_undoSymbolIdx++;
emit signalUndoState(true);
emit signalRedoState(false);
while (static_cast<unsigned int>(a_undoSymbol.size())
> QucsSettings.maxUndo) {
delete a_undoSymbol.first();
a_undoSymbol.pop_front();
a_undoSymbolIdx--;
}
return;
}
while (a_undoAction.size() > a_undoActionIdx + 1) {
delete a_undoAction.last();
a_undoAction.pop_back();
}
if (Op == 'm') {
if (a_undoAction.at(a_undoActionIdx)->at(0) == Op) {
delete a_undoAction.last();
a_undoAction.pop_back();
a_undoActionIdx--;
}
}
a_undoAction.append(new QString(createUndoString(Op)));
a_undoActionIdx++;
emit signalUndoState(true);
emit signalRedoState(false);
while (static_cast<unsigned int>(a_undoAction.size())
> QucsSettings.maxUndo) {
delete a_undoAction.first();
a_undoAction.pop_front();
a_undoActionIdx--;
}
return;
}
bool Schematic::sizeOfFrame(int &xall, int &yall)
{
switch (a_showFrame) {
case 1:
xall = 1020;
yall = 765;
break;
case 2:
xall = 765;
yall = 1020;
break;
case 3:
xall = 1530;
yall = 1020;
break;
case 4:
xall = 1020;
yall = 1530;
break;
case 5:
xall = 2295;
yall = 1530;
break;
case 6:
xall = 1530;
yall = 2295;
break;
case 7:
xall = 1414;
yall = 1054;
break;
case 8:
xall = 1054;
yall = 1414;
break;
default:
return false;
}
return true;
}
void Schematic::paintFrame(QPainter* painter) {
int frame_width, frame_height;
if (!sizeOfFrame(frame_width, frame_height))
return;
painter->save();
painter->setPen(QPen(Qt::darkGray, 1));
const int frame_margin = painter->fontMetrics().lineSpacing() + 4;
painter->drawRect(0, 0, frame_width, frame_height);
painter->drawRect(frame_margin, frame_margin, frame_width - 2 * frame_margin, frame_height - 2 * frame_margin);
{
const int h_step = frame_width / ((frame_width + 127) / 255);
uint column_number = 1;
for (int x = h_step; x <= frame_width; x += h_step) {
painter->drawLine(x, 0, x, frame_margin);
painter->drawLine(x, frame_height - frame_margin, x, frame_height);
auto cn = QString::number(column_number);
auto tx = x - h_step / 2 + 5;
painter->drawText(tx, 3, 1, 1, Qt::TextDontClip, cn);
painter->drawText(tx, frame_height - frame_margin + 3, 1, 1, Qt::TextDontClip, cn);
column_number++;
}
}
{
const int v_step = frame_height / ((frame_height + 127) / 255);
char row_letter = 'A';
for (int y = v_step; y <= frame_height; y += v_step) {
painter->drawLine(0, y, frame_margin, y);
painter->drawLine(frame_width - frame_margin, y, frame_width, y);
auto rl = QString::fromLatin1(&row_letter, 1);
auto ty = y - v_step/2 + 5;
painter->drawText(5, ty, rl);
painter->drawText(frame_width - frame_margin + 5, ty, rl);
row_letter++;
}
}
int x1_ = frame_width - 340 - frame_margin;
int y1_ = frame_height - 3 - frame_margin;
int x2_ = frame_width - frame_margin - 3;
int y2_ = frame_height - frame_margin - 3;
const int d = 6;
const double z = 200.0;
y1_ -= painter->fontMetrics().lineSpacing() + d;
painter->drawLine(x1_, y1_, x2_, y1_);
painter->drawText(x1_ + d, y1_ + (d >> 1), 1, 1, Qt::TextDontClip, a_Frame_Text2);
painter->drawLine(x1_ + z, y1_, x1_ + z, y1_ + painter->fontMetrics().lineSpacing() + d);
painter->drawText(x1_ + d + z, y1_ + (d >> 1), 1, 1, Qt::TextDontClip, a_Frame_Text3);
y1_ -= painter->fontMetrics().lineSpacing() + d;
painter->drawLine(x1_, y1_, x2_, y1_);
painter->drawText(x1_ + d, y1_ + (d >> 1), 1, 1, Qt::TextDontClip, a_Frame_Text1);
y1_ -= (a_Frame_Text0.count('\n') + 1) * painter->fontMetrics().lineSpacing() + d;
painter->drawRect(x2_, y2_, x1_ - x2_ - 1, y1_ - y2_ - 1);
painter->drawText(x1_ + d, y1_ + (d >> 1), 1, 1, Qt::TextDontClip, a_Frame_Text0);
painter->restore();
}
void Schematic::drawContents(QPainter *p, int, int, int, int)
{
QTransform trf{p->transform()};
trf
.scale(a_Scale, a_Scale)
.translate(-a_ViewX1, -a_ViewY1);
p->setTransform(trf);
auto renderHints = p->renderHints();
renderHints
.setFlag(QPainter::Antialiasing)
.setFlag(QPainter::TextAntialiasing)
.setFlag(QPainter::SmoothPixmapTransform);
p->setRenderHints(renderHints);
p->setFont(QucsSettings.font);
drawGrid(p);
if (!a_symbolMode)
paintFrame(p);
drawElements(p);
if (a_showBias > 0) {
drawDcBiasPoints(p);
}
drawPostPaintEvents(p);
}
void Schematic::drawElements(QPainter* painter) {
for (auto* component : *a_Components) {
component->paint(painter);
}
for (auto* wire : *a_Wires) {
wire->paint(painter);
if (wire->Label) {
wire->Label->paint(painter);
}
}
for (auto* node : *a_Nodes) {
node->paint(painter);
if (node->Label) {
node->Label->paint(painter);
}
}
for (auto* diagram : *a_Diagrams) {
diagram->paint(painter);
}
for (auto* painting : *a_Paintings) {
painting->paint(painter);
}
}
void Schematic::drawDcBiasPoints(QPainter* painter) {
painter->save();
int x, y, z;
const int xOffset = 10;
const int yOffset = 10;
for (auto* pn : *a_Nodes) {
if (pn->Name.isEmpty())
continue;
QString value = misc::formatValue(pn->Name, 4);
x = pn->cx;
y = pn->cy + 4;
z = pn->x1;
QRect textRect = painter->fontMetrics().boundingRect(value);
int rectWidth = textRect.width() + 6;
int rectHeight = textRect.height() + 4;
if (z & 0x10) {
x += xOffset;
y -= yOffset;
} else {
x -= xOffset;
y -= yOffset;
}
int rectX = x - rectWidth / 2;
int rectY = y - rectHeight / 2;
painter->setBrush(QBrush(QColor(230,230,230)));
painter->setPen(Qt::NoPen);
painter->drawRoundedRect( QRectF(rectX, rectY, rectWidth, rectHeight),15,15,Qt::RelativeSize);
painter->setPen(z & 0x10 ? Qt::darkGreen : Qt::blue);
painter->drawText(x - textRect.width() / 2, y + textRect.height() / 4, value);
}
painter->restore();
}
void Schematic::drawPostPaintEvents(QPainter* painter) {
painter->save();
* The following events used to be drawn from mouseactions.cpp, but since Qt4
* Paint actions can only be called from within the paint event, so they
* are put into a QList (PostedPaintEvents) and processed here
*/
for (auto p : a_PostedPaintEvents) {
QPen pen(Qt::black);
painter->setPen(Qt::black);
switch (p.pe) {
case _NotRop:
painter->setCompositionMode(QPainter::RasterOp_SourceAndNotDestination);
break;
case _Rect:
painter->drawRect(p.x1, p.y1, p.x2, p.y2);
break;
case _SelectionRect:
pen.setCosmetic(true);
pen.setStyle(Qt::DashLine);
pen.setColor(QColor(50, 50, 50, 100));
painter->setPen(pen);
painter->fillRect(p.x1, p.y1, p.x2, p.y2, QColor(200, 220, 240, 100));
painter->drawRect(p.x1, p.y1, p.x2, p.y2);
break;
case _Line: {
painter->save();
QPen lp{painter->pen()};
lp.setWidth(p.a == 0 ? 1 : p.a);
painter->setPen(lp);
painter->drawLine(p.x1, p.y1, p.x2, p.y2);
painter->restore();
break;
}
case _Ellipse:
painter->drawEllipse(p.x1, p.y1, p.x2, p.y2);
break;
case _Arc:
painter->drawArc(p.x1, p.y1, p.x2, p.y2, p.a, p.b);
break;
case _DotLine:
painter->setPen(Qt::DotLine);
painter->drawLine(p.x1, p.y1, p.x2, p.y2);
break;
case _DotRect:
painter->setPen(Qt::DotLine);
painter->drawRect(p.x1, p.y1, p.x2, p.y2);
break;
case _Translate:;
case _Scale:;
break;
}
}
a_PostedPaintEvents.clear();
painter->restore();
}
void Schematic::PostPaintEvent(
PE pe, int x1, int y1, int x2, int y2, int a, int b, bool PaintOnViewport)
{
PostedPaintEvent p = {pe, x1, y1, x2, y2, a, b, PaintOnViewport};
a_PostedPaintEvents.push_back(p);
viewport()->update();
update();
}
void Schematic::contentsMouseMoveEvent(QMouseEvent *Event)
{
const QPoint modelPos = contentsToModel(Event->pos());
auto xpos = modelPos.x();
auto ypos = modelPos.y();
QString text = "";
auto doubleToString = [](bool condition, double number) {
return condition ? misc::num2str(number) : misc::StringNiceNum(number);
};
if (a_Diagrams == nullptr) return;
for (Diagram* diagram = a_Diagrams->last(); diagram != nullptr; diagram = a_Diagrams->prev()) {
if (diagram->getSelected(xpos, ypos) && diagram->Name == "Rect") {
bool hasY1, hasY2 = false;
for (auto graph: diagram->Graphs) {
hasY1 |= graph->yAxisNo == 0;
hasY2 |= graph->yAxisNo == 1;
}
QPointF mouseClickPoint = QPointF(xpos - diagram->cx, diagram->cy - ypos);
MappedPoint mp = diagram->pointToValue(mouseClickPoint);
auto _x = doubleToString(diagram->engineeringNotation, mp.x);
text = "X=" + _x;
if (hasY1) {
text.append("; Y1=");
auto _y1 = doubleToString(diagram->engineeringNotation, mp.y1);
text.append(_y1);
}
if (hasY2) {
text.append("; Y2=");
auto _y2 = doubleToString(diagram->engineeringNotation, mp.y2);
text.append(_y2);
}
break;
}
}
emit signalCursorPosChanged(xpos, ypos, text);
if (Event->buttons() & Qt::MiddleButton) {
const QPoint currentCursorPosition = contentsToViewport(Event->pos());
const int dx = currentCursorPosition.x() - a_previousCursorPosition.x();
if (dx < 0) {
scrollRight(std::abs(dx));
} else if (dx > 0) {
scrollLeft(dx);
}
const int dy = currentCursorPosition.y() - a_previousCursorPosition.y();
if (dy < 0) {
scrollDown(std::abs(dy));
} else if (dy > 0) {
scrollUp(dy);
}
a_previousCursorPosition = currentCursorPosition;
}
if (a_App->MouseMoveAction)
(a_App->view->*(a_App->MouseMoveAction))(this, Event);
}
void Schematic::contentsMousePressEvent(QMouseEvent *Event)
{
a_App->editText->setHidden(true);
this->setFocus();
if (a_App->MouseReleaseAction == &MouseActions::MReleasePaste)
return;
const QPoint inModel = contentsToModel(Event->pos());
if (Event->button() == Qt::RightButton)
if (a_App->MousePressAction != &MouseActions::MPressElement)
if (a_App->MousePressAction != &MouseActions::MPressWire2) {
a_App->view->rightPressMenu(this, Event, inModel.x(), inModel.y());
if (a_App->MouseReleaseAction)
(a_App->view->*(a_App->MouseReleaseAction))(this, Event);
return;
}
if (Event->button() == Qt::MiddleButton) {
a_previousCursorPosition = contentsToViewport(Event->pos());
setCursor(Qt::ClosedHandCursor);
return;
}
if (a_App->MousePressAction)
(a_App->view->*(a_App->MousePressAction))(this, Event, inModel.x(), inModel.y());
}
void Schematic::contentsMouseReleaseEvent(QMouseEvent *Event)
{
if (Event->button() == Qt::MiddleButton) {
unsetCursor();
return;
}
if (a_App->MouseReleaseAction)
(a_App->view->*(a_App->MouseReleaseAction))(this, Event);
}
void Schematic::contentsMouseDoubleClickEvent(QMouseEvent *Event)
{
if (a_App->MouseDoubleClickAction)
(a_App->view->*(a_App->MouseDoubleClickAction))(this, Event);
}
void Schematic::print(QPrinter*, QPainter* painter, bool printAll,
bool fitToPage, QMargins margins) {
painter->save();
const QRectF pageSize{0, 0, static_cast<double>(painter->device()->width()),
static_cast<double>(painter->device()->height())};
QRect printedArea = printAll ? allBoundingRect() : currentSelection().bounds;
if (printAll && a_showFrame) {
int frame_width, frame_height;
sizeOfFrame(frame_width, frame_height);
printedArea |= QRect{0, 0, frame_width, frame_height};
}
printedArea = printedArea.marginsAdded(margins);
double scale = 1.0;
if (fitToPage) {
scale = std::min(pageSize.width() / printedArea.width(),
pageSize.height() / printedArea.height());
} else {
QFontInfo printerFontInfo{QFont{QucsSettings.font, painter->device()}};
QFontInfo schematicFontInfo{QucsSettings.font};
scale = static_cast<double>(printerFontInfo.pixelSize()) /
static_cast<double>(schematicFontInfo.pixelSize());
}
painter->scale(scale, scale);
painter->translate(-printedArea.left(), -printedArea.top());
{
auto w = pageSize.width() / scale;
if (printedArea.width() <= w) {
auto d = (w - printedArea.width()) / 2;
painter->translate(d, 0);
}
auto h = pageSize.height() / scale;
if (printedArea.height() <= h) {
auto d = (h - printedArea.height()) / 2;
painter->translate(0, d);
}
}
auto f = QucsSettings.font;
QFontInfo fi{f};
f.setPixelSize(fi.pixelSize());
painter->setFont(f);
paintSchToViewpainter(painter, printAll);
painter->restore();
}
namespace {
template <typename T> void draw_preserve_selection(T* elem, QPainter* p) {
bool selected = elem->isSelected;
elem->isSelected = false;
elem->paint(p);
elem->isSelected = selected;
}
}
void Schematic::paintSchToViewpainter(QPainter* painter, bool printAll) {
if (printAll && a_showFrame && !a_symbolMode) {
paintFrame(painter);
}
const auto should_draw = [=](Element* drawable) {
return printAll || drawable->isSelected;
};
for (auto* component : *a_Components) {
if (should_draw(component)) {
draw_preserve_selection(component, painter);
}
}
for (auto* wire : *a_Wires) {
if (should_draw(wire)) {
draw_preserve_selection(wire, painter);
}
if (auto* label = wire->Label) {
if (should_draw(label)) {
draw_preserve_selection(label, painter);
}
}
}
for (auto* node : *a_Nodes) {
for (auto* connected : *node) {
if (should_draw(connected)) {
draw_preserve_selection(node, painter);
break;
}
}
if (auto* label = node->Label) {
if (should_draw(label)) {
draw_preserve_selection(label, painter);
}
}
}
for (auto* painting : *a_Paintings) {
if (should_draw(painting)) {
draw_preserve_selection(painting, painter);
}
}
for (auto* diagram : *a_Diagrams) {
if (!should_draw(diagram)) {
continue;
}
for (Graph* pg : diagram->Graphs) {
if (pg->isSelected) {
pg->Type |= 1;
}
pg->isSelected = false;
for (Marker* pm : pg->Markers) {
if (pm->isSelected) {
pm->Type |= 1;
}
pm->isSelected = false;
}
}
draw_preserve_selection(diagram, painter);
for (Graph* pg : diagram->Graphs) {
if (pg->Type & 1) {
pg->isSelected = true;
}
pg->Type &= -2;
for (Marker* pm : pg->Markers) {
if (pm->Type & 1) {
pm->isSelected = true;
}
pm->Type &= -2;
}
}
}
if (a_showBias > 0) {
drawDcBiasPoints(painter);
}
}
void Schematic::zoomAroundPoint(double offeredScaleChange, QPoint coords, bool viewportRelative=true)
{
const double desiredScale = a_Scale * offeredScaleChange;
const auto viewportCoords =
viewportRelative ? coords : coords - QPoint{contentsX(), contentsY()};
const auto focusPoint = viewportToModel(viewportCoords);
const auto model = includePoint(modelRect(), focusPoint);
renderModel(desiredScale, model, focusPoint, viewportCoords);
}
float Schematic::zoomBy(float s)
{
const double newScale = a_Scale * s;
const auto vpCenter = viewportRect().center();
const auto centerPoint = viewportToModel(vpCenter);
const auto model = includePoint(modelRect(), centerPoint);
return renderModel(newScale, model, centerPoint, vpCenter);
}
void Schematic::showAll()
{
sizeOfAll(a_UsedX1, a_UsedY1, a_UsedX2, a_UsedY2);
if (a_UsedX1 == 0)
if (a_UsedX2 == 0)
if (a_UsedY1 == 0)
if (a_UsedY2 == 0) {
a_UsedX1 = a_UsedY1 = INT_MAX;
a_UsedX2 = a_UsedY2 = INT_MIN;
return;
}
constexpr int margin = 40;
QRect newModelBounds = modelRect();
newModelBounds.setLeft(a_UsedX1 - margin);
newModelBounds.setTop(a_UsedY1 - margin);
newModelBounds.setRight(a_UsedX2 + margin);
newModelBounds.setBottom(a_UsedY2 + margin);
const double xScale = static_cast<double>(viewport()->width()) /
static_cast<double>(newModelBounds.width());
const double yScale = static_cast<double>(viewport()->height()) /
static_cast<double>(newModelBounds.height());
const double newScale = std::min(xScale, yScale);
renderModel(newScale, newModelBounds, newModelBounds.center(), viewportRect().center());
}
void Schematic::zoomToSelection() {
sizeOfAll(a_UsedX1, a_UsedY1, a_UsedX2, a_UsedY2);
if (a_UsedX1 == 0)
if (a_UsedX2 == 0)
if (a_UsedY1 == 0)
if (a_UsedY2 == 0) {
a_UsedX1 = a_UsedY1 = INT_MAX;
a_UsedX2 = a_UsedY2 = INT_MIN;
return;
}
const QRect selectedBoundingRect{ currentSelection().bounds };
const QRect usedBoundingRect{a_UsedX1, a_UsedY1, a_UsedX2 - a_UsedX1, a_UsedY2 - a_UsedY1};
if (selectedBoundingRect.width() == 0 || selectedBoundingRect.height() == 0) {
return;
}
constexpr int margin = 40;
QRect modelBounds = modelRect();
modelBounds.setLeft(usedBoundingRect.left() - margin);
modelBounds.setTop(usedBoundingRect.top() - margin);
modelBounds.setRight(usedBoundingRect.right() + margin);
modelBounds.setBottom(usedBoundingRect.bottom() + margin);
const double xScale = static_cast<double>(viewport()->width()) /
static_cast<double>(selectedBoundingRect.width());
const double yScale = static_cast<double>(viewport()->height()) /
static_cast<double>(selectedBoundingRect.height());
const double newScale = std::min(xScale, yScale);
renderModel(newScale, modelBounds, selectedBoundingRect.center(), viewportRect().center());
}
void Schematic::showNoZoom()
{
constexpr double noScale = 1.0;
const QPoint vpCenter = viewportRect().center();
const QPoint displayedInCenter = viewportToModel(vpCenter);
sizeOfAll(a_UsedX1, a_UsedY1, a_UsedX2, a_UsedY2);
if (a_UsedX1 == 0)
if (a_UsedX2 == 0)
if (a_UsedY1 == 0)
if (a_UsedY2 == 0) {
a_UsedX1 = a_UsedY1 = INT_MAX;
a_UsedX2 = a_UsedY2 = INT_MIN;
renderModel(noScale, includePoint(modelRect(), displayedInCenter), displayedInCenter, vpCenter);
return;
}
const QRect usedBoundingRect{a_UsedX1, a_UsedY1, a_UsedX2 - a_UsedX1, a_UsedY2 - a_UsedY1};
constexpr int margin = 40;
QRect newModelBounds = modelRect();
newModelBounds.setLeft(usedBoundingRect.left() - margin);
newModelBounds.setTop(usedBoundingRect.top() - margin);
newModelBounds.setRight(usedBoundingRect.right() + margin);
newModelBounds.setBottom(usedBoundingRect.bottom() + margin);
if (usedBoundingRect.contains(displayedInCenter)) {
renderModel(noScale, newModelBounds, displayedInCenter, vpCenter);
} else {
renderModel(noScale, newModelBounds, usedBoundingRect.center(), vpCenter);
}
}
void Schematic::enlargeView(int x1, int y1, int x2, int y2) {
if (x1 < a_UsedX1)
a_UsedX1 = x1;
if (y1 < a_UsedY1)
a_UsedY1 = y1;
if (x2 > a_UsedX2)
a_UsedX2 = x2;
if (y2 > a_UsedY2)
a_UsedY2 = y2;
constexpr int margin = 40;
QRect newModel = modelRect();
if (x1 < a_ViewX1)
newModel.setLeft(x1 - margin);
if (y1 < a_ViewY1)
newModel.setTop(y1 - margin);
if (x2 > a_ViewX2)
newModel.setRight(x2 + margin);
if (y2 > a_ViewY2)
newModel.setBottom(y2 + margin);
const auto vpCenter = viewportRect().center();
const auto displayedInCenter = viewportToModel(vpCenter);
newModel = includePoint(newModel, displayedInCenter);
renderModel(a_Scale, newModel, displayedInCenter, vpCenter);
}
QPoint Schematic::setOnGrid(const QPoint& p) {
QPoint snappedToGrid{p.x(), p.y()};
setOnGrid(snappedToGrid.rx(), snappedToGrid.ry());
return snappedToGrid;
}
void Schematic::setOnGrid(int &x, int &y)
{
if (x < 0)
x -= (a_GridX >> 1) - 1;
else
x += a_GridX >> 1;
x -= x % a_GridX;
if (y < 0)
y -= (a_GridY >> 1) - 1;
else
y += a_GridY >> 1;
y -= y % a_GridY;
}
void Schematic::drawGrid(QPainter* painter) {
if (!a_GridOn)
return;
painter->save();
painter->setTransform(QTransform{});
painter->setPen(QPen{ a_GridColor, 1.0 });
{
const QPoint origin = modelToViewport(QPoint{0, 0});
painter->drawLine(origin.x() - 3, origin.y(), origin.x() + 4, origin.y());
painter->drawLine(origin.x(), origin.y() - 3, origin.x(), origin.y() + 4);
}
QPoint topLeft = viewportToModel(viewportRect().topLeft());
const QPoint gridTopLeft = modelToViewport(setOnGrid(topLeft));
QPoint bottomRight = viewportToModel(viewportRect().bottomRight());
const QPoint gridBottomRight = modelToViewport(setOnGrid(bottomRight));
constexpr double minimalVisibleGridStep = 8.0;
double horizontalStep{ a_GridX * a_Scale };
for (int n = 2; horizontalStep < minimalVisibleGridStep; n++) {
horizontalStep = n * a_GridX * a_Scale;
}
double verticalStep{ a_GridY * a_Scale };
for (int n = 2; verticalStep < minimalVisibleGridStep; n++) {
verticalStep = n * a_GridY * a_Scale;
}
for (double x = gridTopLeft.x(); x <= gridBottomRight.x(); x += horizontalStep) {
for (double y = gridTopLeft.y(); y <= gridBottomRight.y(); y += verticalStep) {
painter->drawPoint(std::round(x), std::round(y));
}
}
painter->restore();
}
void Schematic::relativeRotation(int &newX, int &newY, int comX, int comY, int oldX, int oldY)
{
newX = (oldY-comY)+comX;
newY = -(oldX-comX)+comY;
}
void Schematic::updateAllBoundingRect() {
sizeOfAll(a_UsedX1, a_UsedY1, a_UsedX2, a_UsedY2);
}
QRect Schematic::allBoundingRect() {
updateAllBoundingRect();
return QRect{a_UsedX1, a_UsedY1, (a_UsedX2 - a_UsedX1), (a_UsedY2 - a_UsedY1)};
}
void Schematic::sizeOfAll(int &xmin, int &ymin, int &xmax, int &ymax)
{
xmin = INT_MAX;
ymin = INT_MAX;
xmax = INT_MIN;
ymax = INT_MIN;
if (a_Components->isEmpty() && a_Wires->isEmpty() && a_Diagrams->isEmpty() && a_Paintings->isEmpty()) {
xmin = xmax = 0;
ymin = ymax = 0;
return;
}
int x1, y1, x2, y2;
for (auto* pc : *a_Components) {
pc->entireBounds(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
for (auto* pw : *a_Wires) {
xmin = std::min(pw->x1, xmin);
xmax = std::max(pw->x2, xmax);
ymin = std::min(pw->y1, ymin);
ymax = std::max(pw->y2, ymax);
if (auto* pl = pw->Label; pl) {
pl->getLabelBounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
}
for (auto* pn : *a_Nodes) {
if (auto* pl = pn->Label; pl) {
pl->getLabelBounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
}
for (auto* pd : *a_Diagrams) {
pd->Bounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
for (auto* pg : pd->Graphs)
for (auto* pm : pg->Markers) {
pm->Bounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
}
for (auto* pp : *a_Paintings) {
pp->Bounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
}
Schematic::Selection Schematic::currentSelection() const {
int xmin = INT_MAX;
int ymin = INT_MAX;
int xmax = INT_MIN;
int ymax = INT_MIN;
bool isAnySelected = false;
if (a_Components->isEmpty() && a_Wires->isEmpty() && a_Diagrams->isEmpty() &&
a_Paintings->isEmpty()) {
return {};
}
Selection selection;
int x1, y1, x2, y2;
for (auto* pc : *a_Components) {
if (!pc->isSelected) {
continue;
}
isAnySelected = true;
selection.components.push_back(pc);
pc->entireBounds(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
for (auto* pw : *a_Wires) {
if (pw->isSelected) {
isAnySelected = true;
selection.wires.push_back(pw);
xmin = std::min(pw->x1, xmin);
xmax = std::max(pw->x2, xmax);
ymin = std::min(pw->y1, ymin);
ymax = std::max(pw->y2, ymax);
}
if (pw->Label != nullptr && pw->Label->isSelected) {
isAnySelected = true;
selection.labels.push_back(pw->Label);
pw->Label->getLabelBounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
}
for (auto* pn : *a_Nodes) {
if (std::all_of(pn->begin(), pn->end(), [](auto* e) { return e->isSelected; })) {
selection.nodes.push_back(pn);
}
if (pn->Label != nullptr && pn->Label->isSelected) {
selection.labels.push_back(pn->Label);
isAnySelected = true;
pn->Label->getLabelBounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
}
for (auto* pd : *a_Diagrams) {
if (pd->isSelected) {
selection.diagrams.push_back(pd);
isAnySelected = true;
pd->Bounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
for (Graph* pg : pd->Graphs) {
for (Marker* pm : pg->Markers) {
if (!pm->isSelected) continue;
isAnySelected = true;
selection.markers.push_back(pm);
pm->Bounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
}
}
for (auto* pp : *a_Paintings) {
if (!pp->isSelected) {
continue;
}
selection.paintings.push_back(pp);
isAnySelected = true;
pp->Bounding(x1, y1, x2, y2);
xmin = std::min(x1, xmin);
xmax = std::max(x2, xmax);
ymin = std::min(y1, ymin);
ymax = std::max(y2, ymax);
}
if (!isAnySelected) {
return {};
}
selection.bounds = QRect{xmin, ymin, xmax - xmin, ymax - ymin};
return selection;
}
bool Schematic::rotateElements()
{
a_Wires->setAutoDelete(false);
a_Components->setAutoDelete(false);
int x1 = INT_MAX, y1 = INT_MAX;
int x2 = INT_MIN, y2 = INT_MIN;
QList<Element *> ElementCache;
copyLabels(x1, y1, x2, y2, &ElementCache);
copyComponents(x1, y1, x2, y2, &ElementCache);
copyWires(x1, y1, x2, y2, &ElementCache);
copyPaintings(x1, y1, x2, y2, &ElementCache);
if (y1 == INT_MAX)
{
return false;
}
int comX = (x1 + ((x2-x1) / 2));
int comY = (y1 + ((y2-y1) / 2));
int newPosX = INT_MIN;
int newPosY = INT_MIN;
a_Wires->setAutoDelete(true);
a_Components->setAutoDelete(true);
setOnGrid(comX, comY);
Wire *pw;
Painting *pp;
Component *pc;
WireLabel *pl;
for (Element *pe : ElementCache)
switch (pe->Type) {
case isComponent:
case isAnalogComponent:
case isDigitalComponent:
pc = (Component *) pe;
pc->rotate();
relativeRotation(newPosX, newPosY, comX, comY, pc->cx, pc->cy);
pc->setCenter(newPosX, newPosY);
insertRawComponent(pc);
break;
case isWire:
pw = (Wire *) pe;
relativeRotation(newPosX, newPosY, comX, comY, pw->x1, pw->y1);
pw->x1 = newPosX;
pw->y1 = newPosY;
relativeRotation(newPosX, newPosY, comX, comY, pw->x2, pw->y2);
pw->x2 = newPosX;
pw->y2 = newPosY;
pl = pw->Label;
if (pl) {
x2 = pl->cx;
relativeRotation(newPosX, newPosY, comX, comY, pl->cx, pl->cy);
pl->cx = newPosX;
pl->cy = newPosY;
if (pl->Type == isHWireLabel)
pl->Type = isVWireLabel;
else
pl->Type = isHWireLabel;
}
insertWire(pw);
break;
case isHWireLabel:
case isVWireLabel:
pl = (WireLabel *) pe;
relativeRotation(newPosX, newPosY, comX, comY, pl->x1, pl->y1);
pl->x1 = newPosX;
pl->y1 = newPosY;
break;
case isNodeLabel:
pl = (WireLabel *) pe;
if (pl->pOwner == 0) {
relativeRotation(newPosX, newPosY, comX, comY, pl->x1, pl->y1);
pl->x1 = newPosX;
pl->y1 = newPosY;
}
relativeRotation(newPosX, newPosY, comX, comY, pl->cx, pl->cy);
pl->cx = newPosX;
pl->cy = newPosY;
insertNodeLabel(pl);
break;
case isPainting:
pp = (Painting *) pe;
pp->rotate(x1, y1);
a_Paintings->append(pp);
break;
default:;
}
ElementCache.clear();
setChanged(true, true);
return true;
}
bool Schematic::mirrorXComponents()
{
a_Wires->setAutoDelete(false);
a_Components->setAutoDelete(false);
int x1, y1, x2, y2;
QList<Element *> ElementCache;
if (!copyComps2WiresPaints(x1, y1, x2, y2, &ElementCache))
return false;
a_Wires->setAutoDelete(true);
a_Components->setAutoDelete(true);
y1 = (y1 + y2) >> 1;
setOnGrid(y2, y1);
y1 <<= 1;
Wire *pw;
Painting *pp;
Component *pc;
WireLabel *pl;
for (Element *pe : ElementCache)
switch (pe->Type) {
case isComponent:
case isAnalogComponent:
case isDigitalComponent:
pc = (Component *) pe;
pc->mirrorX();
pc->setCenter(pc->cx, y1 - pc->cy);
insertRawComponent(pc);
break;
case isWire:
pw = (Wire *) pe;
pw->y1 = y1 - pw->y1;
pw->y2 = y1 - pw->y2;
pl = pw->Label;
if (pl)
pl->cy = y1 - pl->cy;
insertWire(pw);
break;
case isHWireLabel:
case isVWireLabel:
pl = (WireLabel *) pe;
pl->y1 = y1 - pl->y1;
break;
case isNodeLabel:
pl = (WireLabel *) pe;
if (pl->pOwner == 0)
pl->y1 = y1 - pl->y1;
pl->cy = y1 - pl->cy;
insertNodeLabel(pl);
break;
case isPainting:
pp = (Painting *) pe;
pp->getCenter(x2, y2);
pp->mirrorX();
pp->setCenter(x2, y1 - y2);
a_Paintings->append(pp);
break;
default:;
}
ElementCache.clear();
setChanged(true, true);
return true;
}
bool Schematic::mirrorYComponents()
{
a_Wires->setAutoDelete(false);
a_Components->setAutoDelete(false);
int x1, y1, x2, y2;
QList<Element *> ElementCache;
if (!copyComps2WiresPaints(x1, y1, x2, y2, &ElementCache))
return false;
a_Wires->setAutoDelete(true);
a_Components->setAutoDelete(true);
x1 = (x1 + x2) >> 1;
setOnGrid(x1, x2);
x1 <<= 1;
Wire *pw;
Painting *pp;
Component *pc;
WireLabel *pl;
for (Element *pe : ElementCache)
switch (pe->Type) {
case isComponent:
case isAnalogComponent:
case isDigitalComponent:
pc = (Component *) pe;
pc->mirrorY();
pc->setCenter(x1 - pc->cx, pc->cy);
insertRawComponent(pc);
break;
case isWire:
pw = (Wire *) pe;
pw->x1 = x1 - pw->x1;
pw->x2 = x1 - pw->x2;
pl = pw->Label;
if (pl)
pl->cx = x1 - pl->cx;
insertWire(pw);
break;
case isHWireLabel:
case isVWireLabel:
pl = (WireLabel *) pe;
pl->x1 = x1 - pl->x1;
break;
case isNodeLabel:
pl = (WireLabel *) pe;
if (pl->pOwner == 0)
pl->x1 = x1 - pl->x1;
pl->cx = x1 - pl->cx;
insertNodeLabel(pl);
break;
case isPainting:
pp = (Painting *) pe;
pp->getCenter(x2, y2);
pp->mirrorY();
pp->setCenter(x1 - x2, y2);
a_Paintings->append(pp);
break;
default:;
}
ElementCache.clear();
setChanged(true, true);
return true;
}
void Schematic::reloadGraphs()
{
QFileInfo Info(a_DocName);
for (Diagram *pd = a_Diagrams->first(); pd != 0; pd = a_Diagrams->next())
pd->loadGraphData(Info.path() + QDir::separator() + a_DataSet);
}
void Schematic::copy()
{
QString s = createClipboardFile();
QClipboard *cb = QApplication::clipboard();
if (!s.isEmpty()) {
cb->setText(s, QClipboard::Clipboard);
}
}
void Schematic::cut()
{
copy();
deleteElements();
viewport()->update();
}
bool Schematic::paste(QTextStream *stream, QList<Element*> *pe)
{
return pasteFromClipboard(stream, pe);
}
bool Schematic::load()
{
a_DocComps.clear();
a_DocWires.clear();
a_DocNodes.clear();
a_DocDiags.clear();
a_DocPaints.clear();
a_SymbolPaints.clear();
if (!loadDocument())
return false;
a_lastSaved = QDateTime::currentDateTime();
while (!a_undoAction.isEmpty()) {
delete a_undoAction.last();
a_undoAction.pop_back();
}
a_undoActionIdx = 0;
while (!a_undoSymbol.isEmpty()) {
delete a_undoSymbol.last();
a_undoSymbol.pop_back();
}
a_symbolMode = true;
setChanged(false, true);
a_undoSymbolIdx = 0;
a_undoSymbol.at(a_undoSymbolIdx)->replace(1, 1, 'i');
a_symbolMode = false;
setChanged(false, true);
a_undoActionIdx = 0;
a_undoAction.at(a_undoActionIdx)->replace(1, 1, 'i');
becomeCurrent(false);
showAll();
a_tmpViewX1 = a_tmpViewY1 = -200;
return true;
}
int Schematic::save()
{
int result = 0;
if (!a_isSymbolOnly) {
result = adjustPortNumbers();
} else {
orderSymbolPorts();
}
if (saveDocument() < 0)
return -1;
QFileInfo Info(a_DocName);
a_lastSaved = Info.lastModified();
if (result >= 0) {
setChanged(false);
QVector<QString *>::iterator it;
for (it = a_undoAction.begin(); it != a_undoAction.end(); it++) {
(*it)->replace(1, 1, ' ');
}
a_undoAction.at(a_undoActionIdx)->replace(1, 1, 'i');
for (it = a_undoSymbol.begin(); it != a_undoSymbol.end(); it++) {
(*it)->replace(1, 1, ' ');
}
a_undoSymbol.at(a_undoSymbolIdx)->replace(1, 1, 'i');
}
return result;
}
int Schematic::adjustPortNumbers()
{
int x1, x2, y1, y2;
if (a_symbolMode)
sizeOfAll(x1, y1, x2, y2);
else {
a_Components = &SymbolComps;
a_Wires = &SymbolWires;
a_Nodes = &SymbolNodes;
a_Diagrams = &SymbolDiags;
a_Paintings = &a_SymbolPaints;
sizeOfAll(x1, y1, x2, y2);
a_Components = &a_DocComps;
a_Wires = &a_DocWires;
a_Nodes = &a_DocNodes;
a_Diagrams = &a_DocDiags;
a_Paintings = &a_DocPaints;
}
x1 += 40;
y2 += 20;
setOnGrid(x1, y2);
Painting *pp;
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next())
if (pp->Name == ".PortSym ")
((PortSymbol *) pp)->nameStr = "";
QString Str;
int countPort = 0;
QFileInfo Info(a_DataDisplay);
QString Suffix = Info.suffix();
if (Suffix == "vhd" || Suffix == "vhdl") {
QStringList::iterator it;
QStringList Names, GNames, GTypes, GDefs;
int Number;
QFileInfo Info(a_DocName);
QString Name = Info.path() + QDir::separator() + a_DataDisplay;
VHDL_File_Info VInfo;
TextDoc *d = (TextDoc *) a_App->findDoc(Name);
if (d)
VInfo = VHDL_File_Info(d->document()->toPlainText());
else
VInfo = VHDL_File_Info(Name, true);
if (!VInfo.PortNames.isEmpty())
Names = VInfo.PortNames.split(",", Qt::SkipEmptyParts);
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next())
if (pp->Name == ".ID ") {
ID_Text *id = (ID_Text *) pp;
id->Prefix = VInfo.EntityName.toUpper();
id->Parameter.clear();
if (!VInfo.GenNames.isEmpty())
GNames = VInfo.GenNames.split(",", Qt::SkipEmptyParts);
if (!VInfo.GenTypes.isEmpty())
GTypes = VInfo.GenTypes.split(",", Qt::SkipEmptyParts);
if (!VInfo.GenDefs.isEmpty())
GDefs = VInfo.GenDefs.split(",", Qt::SkipEmptyParts);
;
for (Number = 1, it = GNames.begin(); it != GNames.end(); ++it) {
id->Parameter.append(
new SubParameter(true,
*it + "=" + GDefs[Number - 1],
tr("generic") + " " + QString::number(Number),
GTypes[Number - 1]));
Number++;
}
}
for (Number = 1, it = Names.begin(); it != Names.end(); ++it, Number++) {
countPort++;
Str = QString::number(Number);
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next())
if (pp->Name == ".PortSym ")
if (((PortSymbol *) pp)->numberStr == Str)
break;
if (pp)
((PortSymbol *) pp)->nameStr = *it;
else {
a_SymbolPaints.append(new PortSymbol(x1, y2, Str, *it));
y2 += 40;
}
}
}
else if (Suffix == "v") {
QStringList::iterator it;
QStringList Names;
int Number;
QFileInfo Info(a_DocName);
QString Name = Info.path() + QDir::separator() + a_DataDisplay;
Verilog_File_Info VInfo;
TextDoc *d = (TextDoc *) a_App->findDoc(Name);
if (d)
VInfo = Verilog_File_Info(d->document()->toPlainText());
else
VInfo = Verilog_File_Info(Name, true);
if (!VInfo.PortNames.isEmpty())
Names = VInfo.PortNames.split(",", Qt::SkipEmptyParts);
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next())
if (pp->Name == ".ID ") {
ID_Text *id = (ID_Text *) pp;
id->Prefix = VInfo.ModuleName.toUpper();
id->Parameter.clear();
}
for (Number = 1, it = Names.begin(); it != Names.end(); ++it, Number++) {
countPort++;
Str = QString::number(Number);
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next())
if (pp->Name == ".PortSym ")
if (((PortSymbol *) pp)->numberStr == Str)
break;
if (pp)
((PortSymbol *) pp)->nameStr = *it;
else {
a_SymbolPaints.append(new PortSymbol(x1, y2, Str, *it));
y2 += 40;
}
}
}
else if (Suffix == "va") {
QStringList::iterator it;
QStringList Names;
int Number;
QFileInfo Info(a_DocName);
QString Name = Info.path() + QDir::separator() + a_DataDisplay;
VerilogA_File_Info VInfo;
TextDoc *d = (TextDoc *) a_App->findDoc(Name);
if (d)
VInfo = VerilogA_File_Info(d->toPlainText());
else
VInfo = VerilogA_File_Info(Name, true);
if (!VInfo.PortNames.isEmpty())
Names = VInfo.PortNames.split(",", Qt::SkipEmptyParts);
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next())
if (pp->Name == ".ID ") {
ID_Text *id = (ID_Text *) pp;
id->Prefix = VInfo.ModuleName.toUpper();
id->Parameter.clear();
}
for (Number = 1, it = Names.begin(); it != Names.end(); ++it, Number++) {
countPort++;
Str = QString::number(Number);
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next())
if (pp->Name == ".PortSym ")
if (((PortSymbol *) pp)->numberStr == Str)
break;
if (pp)
((PortSymbol *) pp)->nameStr = *it;
else {
a_SymbolPaints.append(new PortSymbol(x1, y2, Str, *it));
y2 += 40;
}
}
}
else {
for (Component *pc = a_DocComps.first(); pc != 0; pc = a_DocComps.next()) {
if (pc->Model == "Port") {
countPort++;
Str = pc->Props.front()->Value;
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next()) {
if (pp->Name == ".PortSym ") {
if (((PortSymbol *) pp)->numberStr == Str)
break;
}
}
if (pp) {
((PortSymbol *) pp)->nameStr = pc->Name;
} else {
a_SymbolPaints.append(new PortSymbol(x1, y2, Str, pc->Name));
y2 += 40;
}
}
}
}
for (pp = a_SymbolPaints.first(); pp != 0;) {
if (pp->Name == ".PortSym ")
if (((PortSymbol *) pp)->nameStr.isEmpty()) {
a_SymbolPaints.remove();
pp = a_SymbolPaints.current();
continue;
}
pp = a_SymbolPaints.next();
}
return countPort;
}
int Schematic::orderSymbolPorts()
{
Painting *pp;
int countPorts = 0;
QSet<int> port_numbers, existing_numbers, free_numbers;
int max_port_number = 0;
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next()) {
if (pp->Name == ".PortSym ") {
countPorts++;
QString numstr = ((PortSymbol *) pp)->numberStr;
if (numstr != "0") {
if (numstr.toInt() > max_port_number) {
max_port_number = numstr.toInt();
}
existing_numbers.insert(numstr.toInt());
}
}
}
max_port_number = std::max(countPorts,max_port_number);
for (int i = 1; i <= max_port_number; i++) {
port_numbers.insert(i);
}
free_numbers = port_numbers - existing_numbers;
for (pp = a_SymbolPaints.first(); pp != 0; pp = a_SymbolPaints.next()) {
if (pp->Name == ".PortSym ") {
QString numstr = ((PortSymbol *) pp)->numberStr;
if (numstr == "0") {
int free_num = *free_numbers.constBegin();
free_numbers.remove(free_num);
((PortSymbol *) pp)->numberStr = QString::number(free_num);
}
}
}
return countPorts;
}
bool Schematic::undo()
{
if (a_symbolMode) {
if (a_undoSymbolIdx == 0) {
return false;
}
rebuildSymbol(a_undoSymbol.at(--a_undoSymbolIdx));
emit signalUndoState(a_undoSymbolIdx != 0);
emit signalRedoState(a_undoSymbolIdx != a_undoSymbol.size() - 1);
if (a_undoSymbol.at(a_undoSymbolIdx)->at(1) == 'i'
&& a_undoAction.at(a_undoActionIdx)->at(1) == 'i') {
setChanged(false, false);
return true;
}
setChanged(true, false);
return true;
}
if (a_undoActionIdx == 0) {
return false;
}
rebuild(a_undoAction.at(--a_undoActionIdx));
reloadGraphs();
emit signalUndoState(a_undoActionIdx != 0);
emit signalRedoState(a_undoActionIdx != a_undoAction.size() - 1);
if (a_undoAction.at(a_undoActionIdx)->at(1) == 'i') {
if (a_undoSymbol.isEmpty()) {
setChanged(false, false);
return true;
} else if (a_undoSymbol.at(a_undoSymbolIdx)->at(1) == 'i') {
setChanged(false, false);
return true;
}
}
setChanged(true, false);
return true;
}
bool Schematic::redo()
{
if (a_symbolMode) {
if (a_undoSymbolIdx == a_undoSymbol.size() - 1) {
return false;
}
rebuildSymbol(a_undoSymbol.at(++a_undoSymbolIdx));
adjustPortNumbers();
emit signalUndoState(a_undoSymbolIdx != 0);
emit signalRedoState(a_undoSymbolIdx != a_undoSymbol.size() - 1);
if (a_undoSymbol.at(a_undoSymbolIdx)->at(1) == 'i'
&& a_undoAction.at(a_undoActionIdx)->at(1) == 'i') {
setChanged(false, false);
return true;
}
setChanged(true, false);
return true;
}
if (a_undoActionIdx == a_undoAction.size() - 1) {
return false;
}
rebuild(a_undoAction.at(++a_undoActionIdx));
reloadGraphs();
emit signalUndoState(a_undoActionIdx != 0);
emit signalRedoState(a_undoActionIdx != a_undoAction.size() - 1);
if (a_undoAction.at(a_undoActionIdx)->at(1) == 'i') {
if (a_undoSymbol.isEmpty()) {
setChanged(false, false);
return true;
} else if (a_undoSymbol.at(a_undoSymbolIdx)->at(1) == 'i') {
setChanged(false, false);
return true;
}
}
setChanged(true, false);
return true;
}
bool Schematic::elementsOnGrid()
{
int x, y, No;
bool count = false;
WireLabel *pl, *pLabel;
Q3PtrList<WireLabel> LabelCache;
a_Components->setAutoDelete(false);
for (Component *pc = a_Components->last(); pc != nullptr; pc = a_Components->prev())
if (pc->isSelected) {
for (Port *pp : pc->Ports)
if (pp->Connection->Label)
if (pp->Connection->conn_count() < 2) {
LabelCache.append(pp->Connection->Label);
pp->Connection->Label->pOwner = 0;
pp->Connection->Label = 0;
}
x = pc->cx;
y = pc->cy;
No = a_Components->at();
deleteComp(pc);
setOnGrid(pc->cx, pc->cy);
insertRawComponent(pc);
a_Components->at(No);
pc->isSelected = false;
count = true;
x -= pc->cx;
y -= pc->cy;
for (pl = LabelCache.first(); pl != 0; pl = LabelCache.next()) {
pl->cx -= x;
pl->cy -= y;
insertNodeLabel(pl);
}
LabelCache.clear();
}
a_Components->setAutoDelete(true);
a_Wires->setAutoDelete(false);
for (Wire *pw = a_Wires->last(); pw != 0; pw = a_Wires->prev()) {
pl = pw->Label;
pw->Label = nullptr;
if (pw->isSelected) {
pLabel = nullptr;
if (pw->Port1->Label) {
if (pw->Port1->conn_count() < 2) {
pLabel = pw->Port1->Label;
pw->Port1->Label = nullptr;
}
} else if (pw->Port2->Label) {
if (pw->Port2->conn_count() < 2) {
pLabel = pw->Port2->Label;
pw->Port2->Label = nullptr;
}
}
No = a_Wires->at();
deleteWire(pw);
setOnGrid(pw->x1, pw->y1);
setOnGrid(pw->x2, pw->y2);
insertWire(pw);
a_Wires->at(No);
pw->isSelected = false;
count = true;
if (pl)
setOnGrid(pl->cx, pl->cy);
if (pLabel) {
setOnGrid(pLabel->cx, pLabel->cy);
insertNodeLabel(pLabel);
}
}
if (pl) {
pw->Label = pl;
if (pl->isSelected) {
setOnGrid(pl->x1, pl->y1);
pl->isSelected = false;
count = true;
}
}
}
a_Wires->setAutoDelete(true);
for (Node *pn = a_Nodes->first(); pn != 0; pn = a_Nodes->next())
if (pn->Label)
if (pn->Label->isSelected) {
setOnGrid(pn->Label->x1, pn->Label->y1);
pn->Label->isSelected = false;
count = true;
}
for (Diagram *pd = a_Diagrams->last(); pd != 0; pd = a_Diagrams->prev()) {
if (pd->isSelected) {
setOnGrid(pd->cx, pd->cy);
pd->isSelected = false;
count = true;
}
for (Graph *pg : pd->Graphs)
for (Marker *pm : pg->Markers)
if (pm->isSelected) {
x = pm->x1 + pd->cx;
y = pm->y1 + pd->cy;
setOnGrid(x, y);
pm->x1 = x - pd->cx;
pm->y1 = y - pd->cy;
pm->isSelected = false;
count = true;
}
}
for (Painting *pa = a_Paintings->last(); pa != 0; pa = a_Paintings->prev())
if (pa->isSelected) {
setOnGrid(pa->cx, pa->cy);
pa->isSelected = false;
count = true;
}
if (count)
setChanged(true, true);
return count;
}
void Schematic::switchPaintMode()
{
a_symbolMode = !a_symbolMode;
int tmp, t2;
float temp;
temp = a_Scale;
a_Scale = a_tmpScale;
a_tmpScale = temp;
tmp = contentsX();
t2 = contentsY();
setContentsPos(a_tmpPosX, a_tmpPosY);
a_tmpPosX = tmp;
a_tmpPosY = t2;
tmp = a_ViewX1;
a_ViewX1 = a_tmpViewX1;
a_tmpViewX1 = tmp;
tmp = a_ViewY1;
a_ViewY1 = a_tmpViewY1;
a_tmpViewY1 = tmp;
tmp = a_ViewX2;
a_ViewX2 = a_tmpViewX2;
a_tmpViewX2 = tmp;
tmp = a_ViewY2;
a_ViewY2 = a_tmpViewY2;
a_tmpViewY2 = tmp;
tmp = a_UsedX1;
a_UsedX1 = a_tmpUsedX1;
a_tmpUsedX1 = tmp;
tmp = a_UsedY1;
a_UsedY1 = a_tmpUsedY1;
a_tmpUsedY1 = tmp;
tmp = a_UsedX2;
a_UsedX2 = a_tmpUsedX2;
a_tmpUsedX2 = tmp;
tmp = a_UsedY2;
a_UsedY2 = a_tmpUsedY2;
a_tmpUsedY2 = tmp;
}
void Schematic::contentsWheelEvent(QWheelEvent *Event)
{
a_App->editText->setHidden(true);
const int horizontalWheelAngleDelta = Event->angleDelta().x();
const int verticalWheelAngleDelta = Event->angleDelta().y();
if ((Event->modifiers() & Qt::ShiftModifier) || horizontalWheelAngleDelta) {
int delta = (horizontalWheelAngleDelta ? horizontalWheelAngleDelta : verticalWheelAngleDelta) / 2;
if (delta > 0) {
scrollLeft(delta);
} else {
scrollRight(-delta);
}
}
else if (Event->modifiers() & Qt::ControlModifier) {
double scaleCoef = pow(1.1, verticalWheelAngleDelta / 60.0);
const QPoint pointer{
static_cast<int>(Event->position().x()),
static_cast<int>(Event->position().y())};
zoomAroundPoint(scaleCoef, pointer);
}
else {
int delta = verticalWheelAngleDelta / 2;
if (delta > 0) {
scrollUp(delta);
} else {
scrollDown(-delta);
}
}
Event->accept();
}
void Schematic::scrollUp(int step)
{
assert(step >= 0);
const int stepInModel = static_cast<int>(std::round(step/a_Scale));
const QPoint viewportTopLeft = viewportRect().topLeft();
QPoint mtl = viewportToModel(viewportTopLeft);
mtl.setY(mtl.y() - stepInModel);
QRect modelBounds = modelRect();
modelBounds.setTop(std::min(mtl.y(), modelBounds.top()));
const auto b = modelBounds.bottom() - stepInModel;
if (b > a_UsedY2) {
modelBounds.setBottom(b);
}
renderModel(a_Scale, modelBounds, mtl, viewportTopLeft);
}
void Schematic::scrollDown(int step)
{
assert(step >= 0);
const int stepInModel = static_cast<int>(std::round(step/a_Scale));
const QPoint viewportBottomLeft = viewportRect().bottomLeft();
QPoint mbl = viewportToModel(viewportBottomLeft);
mbl.setY(mbl.y() + stepInModel);
QRect modelBounds = modelRect();
modelBounds.setBottom(std::max(mbl.y(), modelBounds.bottom()));
const auto t = modelBounds.top() + stepInModel;
if (t < a_UsedY1) {
modelBounds.setTop(t);
}
renderModel(a_Scale, modelBounds, mbl, viewportBottomLeft);
}
void Schematic::scrollLeft(int step)
{
assert(step >= 0);
const int stepInModel = static_cast<int>(std::round(step/a_Scale));
const QPoint viewportTopLeft = viewportRect().topLeft();
QPoint mtl = viewportToModel(viewportTopLeft);
mtl.setX(mtl.x() - stepInModel);
QRect modelBounds = modelRect();
modelBounds.setLeft(std::min(mtl.x(), modelBounds.left()));
const auto r = modelBounds.right() - stepInModel;
if (r > a_UsedX2) {
modelBounds.setRight(r);
}
renderModel(a_Scale, modelBounds, mtl, viewportTopLeft);
}
void Schematic::scrollRight(int step)
{
assert(step >= 0);
const int stepInModel = static_cast<int>(std::round(step/a_Scale));
const QPoint viewportTopRight = viewportRect().topRight();
QPoint mtr = viewportToModel(viewportTopRight);
mtr.setX(mtr.x() + stepInModel);
QRect modelBounds = modelRect();
modelBounds.setRight(std::max(mtr.x(), modelBounds.right()));
const auto l = modelBounds.left() + stepInModel;
if (l < a_UsedX1) {
modelBounds.setLeft(l);
}
renderModel(a_Scale, modelBounds, mtr, viewportTopRight);
}
void Schematic::slotScrollUp()
{
a_App->editText->setHidden(true);
scrollUp(verticalScrollBar()->singleStep());
}
void Schematic::slotScrollDown()
{
a_App->editText->setHidden(true);
scrollDown(verticalScrollBar()->singleStep());
}
void Schematic::slotScrollLeft()
{
a_App->editText->setHidden(true);
scrollLeft(horizontalScrollBar()->singleStep());
}
void Schematic::slotScrollRight()
{
a_App->editText->setHidden(true);
scrollRight(horizontalScrollBar()->singleStep());
}
void Schematic::contentsDropEvent(QDropEvent *Event)
{
if (a_dragIsOkay) {
QList<QUrl> urls = Event->mimeData()->urls();
if (urls.isEmpty()) {
return;
}
QucsDoc *d = QucsMain->getDoc(0);
bool changed = d->getDocChanged();
d->setDocChanged(true);
for (QUrl url : urls) {
a_App->gotoPage(QDir::toNativeSeparators(url.toLocalFile()));
}
d->setDocChanged(changed);
return;
}
auto ev_pos = Event->position();
QPoint inModel = contentsToModel(ev_pos.toPoint());
QMouseEvent e(QEvent::MouseButtonPress, ev_pos, mapToGlobal(ev_pos), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
a_App->view->MPressElement(this, &e, inModel.x(), inModel.y());
delete a_App->view->selElem;
a_App->view->selElem = nullptr;
if (formerAction) {
formerAction->setChecked(true);
} else {
QucsMain->select->setChecked(true);
}
}
void Schematic::contentsDragEnterEvent(QDragEnterEvent *Event)
{
formerAction = nullptr;
a_dragIsOkay = false;
if (Event->mimeData()->hasUrls()) {
a_dragIsOkay = true;
Event->accept();
return;
}
if (Event->mimeData()->hasText()) {
QString s = Event->mimeData()->text();
if (s.left(15) == "QucsComponent:<") {
s = s.mid(14);
a_App->view->selElem = getComponentFromName(s);
if (a_App->view->selElem) {
Event->accept();
return;
}
}
Event->ignore();
return;
}
if (Event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) {
QListWidgetItem *Item = a_App->CompComps->currentItem();
if (Item) {
formerAction = a_App->activeAction;
a_App->slotSelectComponent(Item);
a_App->MouseMoveAction = 0;
a_App->MousePressAction = 0;
Event->accept();
return;
}
}
Event->ignore();
}
void Schematic::contentsDragLeaveEvent(QDragLeaveEvent *)
{
if (formerAction)
formerAction->setChecked(true);
}
void Schematic::contentsNativeGestureZoomEvent( QNativeGestureEvent* Event) {
a_App->editText->setHidden(true);
const auto factor = 1.0 + Event->value();
const auto pointer = mapFromGlobal(Event->globalPosition().toPoint());
zoomAroundPoint(factor,pointer);
}
void Schematic::contentsDragMoveEvent(QDragMoveEvent *Event)
{
if (!a_dragIsOkay) {
if (a_App->view->selElem == 0) {
Event->ignore();
return;
}
auto ev_pos = Event->position();
Event->position(),
Qt::NoButton,
Qt::NoButton,
Qt::NoModifier);*/
QMouseEvent e(QEvent::MouseButtonPress, ev_pos, mapToGlobal(ev_pos), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
a_App->view->MMoveElement(this, &e);
}
Event->accept();
}
bool Schematic::checkDplAndDatNames()
{
QFileInfo Info(a_DocName);
if (!a_DocName.isEmpty() && a_DataSet.size() > 4 && a_DataDisplay.size() > 4) {
QString base = Info.completeBaseName();
QString base_dat = a_DataSet;
base_dat.chop(4);
QString base_dpl = a_DataDisplay;
base_dpl.chop(4);
if (base != base_dat || base != base_dpl) {
QString msg = QObject::tr(
"The schematic name and dataset/display file name is not matching! "
"This may happen if schematic was copied using the file manager "
"instead of using File->SaveAs. Correct dataset and display names "
"automatically?\n\n");
msg += QString(QObject::tr("Schematic file: ")) + base + ".sch\n";
msg += QString(QObject::tr("Dataset file: ")) + a_DataSet + "\n";
msg += QString(QObject::tr("Display file: ")) + a_DataDisplay + "\n";
auto r = QMessageBox::information(this,
QObject::tr("Open document"),
msg,
QMessageBox::Yes,
QMessageBox::No);
if (r == QMessageBox::Yes) {
a_DataSet = base + ".dat";
a_DataDisplay = base + ".dpl";
return true;
}
}
} else {
return false;
}
return false;
}