Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
8a92ff80bThis file is pinned to the selected release. Download the original to open it in your design tool.
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include "schematic.h"
#include "../../application.h"
#include "../../exceptions.h"
#include "../../geometry/polygon.h"
#include "../../library/sym/symbolpin.h"
#include "../../serialization/sexpression.h"
#include "../../utils/scopeguardlist.h"
#include "../project.h"
#include "items/si_bussegment.h"
#include "items/si_image.h"
#include "items/si_netlabel.h"
#include "items/si_netline.h"
#include "items/si_netpoint.h"
#include "items/si_netsegment.h"
#include "items/si_polygon.h"
#include "items/si_symbol.h"
#include "items/si_symbolpin.h"
#include "items/si_text.h"
#include <QtCore>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
Schematic::Schematic(Project& project,
std::unique_ptr<TransactionalDirectory> directory,
const QString& directoryName, const Uuid& uuid,
const ElementName& name)
: QObject(&project),
mProject(project),
mDirectoryName(directoryName),
mDirectory(std::move(directory)),
mIsAddedToProject(false),
mUuid(uuid),
mName(name),
mGridInterval(2540000),
mGridUnit(LengthUnit::millimeters()) {
if (mDirectoryName.isEmpty()) {
throw LogicError(__FILE__, __LINE__);
}
// Emit the "attributesChanged" signal when the project has emitted it.
connect(&mProject, &Project::attributesChanged, this,
&Schematic::attributesChanged);
}
Schematic::~Schematic() noexcept {
Q_ASSERT(!mIsAddedToProject);
// delete all items
qDeleteAll(mImages);
mImages.clear();
qDeleteAll(mTexts);
mTexts.clear();
qDeleteAll(mPolygons);
mPolygons.clear();
qDeleteAll(mNetSegments);
mNetSegments.clear();
qDeleteAll(mBusSegments);
mBusSegments.clear();
qDeleteAll(mSymbols);
mSymbols.clear();
}
/*******************************************************************************
* Getters: General
******************************************************************************/
bool Schematic::isEmpty() const noexcept {
return (mSymbols.isEmpty() && mBusSegments.isEmpty() &&
mNetSegments.isEmpty() && mPolygons.isEmpty() && mTexts.isEmpty() &&
mImages.isEmpty());
}
/*******************************************************************************
* Setters
******************************************************************************/
void Schematic::setName(const ElementName& name) noexcept {
if (name != mName) {
mName = name;
emit nameChanged(mName);
emit mProject.attributesChanged();
}
}
/*******************************************************************************
* Symbol Methods
******************************************************************************/
void Schematic::addSymbol(SI_Symbol& symbol) {
if ((!mIsAddedToProject) || (mSymbols.values().contains(&symbol)) ||
(&symbol.getSchematic() != this)) {
throw LogicError(__FILE__, __LINE__);
}
if (mSymbols.contains(symbol.getUuid())) {
throw RuntimeError(
__FILE__, __LINE__,
QString("There is already a symbol with the UUID \"%1\"!")
.arg(symbol.getUuid().toStr()));
}
symbol.addToSchematic(); // can throw
mSymbols.insert(symbol.getUuid(), &symbol);
emit symbolAdded(symbol);
}
void Schematic::removeSymbol(SI_Symbol& symbol) {
if ((!mIsAddedToProject) || (mSymbols.value(symbol.getUuid()) != &symbol)) {
throw LogicError(__FILE__, __LINE__);
}
symbol.removeFromSchematic(); // can throw
mSymbols.remove(symbol.getUuid());
emit symbolRemoved(symbol);
}
/*******************************************************************************
* BusSegment Methods
******************************************************************************/
void Schematic::addBusSegment(SI_BusSegment& s) {
if ((!mIsAddedToProject) || (mBusSegments.values().contains(&s)) ||
(&s.getSchematic() != this)) {
throw LogicError(__FILE__, __LINE__);
}
if (mBusSegments.contains(s.getUuid())) {
throw RuntimeError(
__FILE__, __LINE__,
QString("There is already a bus segment with the UUID \"%1\"!")
.arg(s.getUuid().toStr()));
}
s.addToSchematic(); // can throw
mBusSegments.insert(s.getUuid(), &s);
emit busSegmentAdded(s);
}
void Schematic::removeBusSegment(SI_BusSegment& s) {
if ((!mIsAddedToProject) || (mBusSegments.value(s.getUuid()) != &s)) {
throw LogicError(__FILE__, __LINE__);
}
s.removeFromSchematic(); // can throw
mBusSegments.remove(s.getUuid());
emit busSegmentRemoved(s);
}
/*******************************************************************************
* NetSegment Methods
******************************************************************************/
void Schematic::addNetSegment(SI_NetSegment& netsegment) {
if ((!mIsAddedToProject) || (mNetSegments.values().contains(&netsegment)) ||
(&netsegment.getSchematic() != this)) {
throw LogicError(__FILE__, __LINE__);
}
if (mNetSegments.contains(netsegment.getUuid())) {
throw RuntimeError(
__FILE__, __LINE__,
QString("There is already a netsegment with the UUID \"%1\"!")
.arg(netsegment.getUuid().toStr()));
}
netsegment.addToSchematic(); // can throw
mNetSegments.insert(netsegment.getUuid(), &netsegment);
emit netSegmentAdded(netsegment);
}
void Schematic::removeNetSegment(SI_NetSegment& netsegment) {
if ((!mIsAddedToProject) ||
(mNetSegments.value(netsegment.getUuid()) != &netsegment)) {
throw LogicError(__FILE__, __LINE__);
}
netsegment.removeFromSchematic(); // can throw
mNetSegments.remove(netsegment.getUuid());
emit netSegmentRemoved(netsegment);
}
/*******************************************************************************
* Polygon Methods
******************************************************************************/
void Schematic::addPolygon(SI_Polygon& polygon) {
if ((!mIsAddedToProject) || (mPolygons.values().contains(&polygon)) ||
(&polygon.getSchematic() != this)) {
throw LogicError(__FILE__, __LINE__);
}
if (mPolygons.contains(polygon.getUuid())) {
throw RuntimeError(
__FILE__, __LINE__,
QString("There is already a polygon with the UUID \"%1\"!")
.arg(polygon.getUuid().toStr()));
}
polygon.addToSchematic(); // can throw
mPolygons.insert(polygon.getUuid(), &polygon);
emit polygonAdded(polygon);
}
void Schematic::removePolygon(SI_Polygon& polygon) {
if ((!mIsAddedToProject) ||
(mPolygons.value(polygon.getUuid()) != &polygon)) {
throw LogicError(__FILE__, __LINE__);
}
polygon.removeFromSchematic(); // can throw
mPolygons.remove(polygon.getUuid());
emit polygonRemoved(polygon);
}
/*******************************************************************************
* Text Methods
******************************************************************************/
void Schematic::addText(SI_Text& text) {
if ((!mIsAddedToProject) || (mTexts.values().contains(&text)) ||
(&text.getSchematic() != this)) {
throw LogicError(__FILE__, __LINE__);
}
if (mTexts.contains(text.getUuid())) {
throw RuntimeError(__FILE__, __LINE__,
QString("There is already a text with the UUID \"%1\"!")
.arg(text.getUuid().toStr()));
}
text.addToSchematic(); // can throw
mTexts.insert(text.getUuid(), &text);
emit textAdded(text);
}
void Schematic::removeText(SI_Text& text) {
if ((!mIsAddedToProject) || (mTexts.value(text.getUuid()) != &text)) {
throw LogicError(__FILE__, __LINE__);
}
text.removeFromSchematic(); // can throw
mTexts.remove(text.getUuid());
emit textRemoved(text);
}
/*******************************************************************************
* Image Methods
******************************************************************************/
void Schematic::addImage(SI_Image& image) {
if ((!mIsAddedToProject) || (mImages.values().contains(&image)) ||
(&image.getSchematic() != this)) {
throw LogicError(__FILE__, __LINE__);
}
if (mImages.contains(image.getUuid())) {
throw RuntimeError(
__FILE__, __LINE__,
QString("There is already an image with the UUID \"%1\"!")
.arg(image.getUuid().toStr()));
}
// Note that we don't verify here that the referenced image file actually
// exists in the schematic directory. We could do that, but if for any reason
// the file is missing, it would somehow be bad user experience if the project
// cannot be opened at all. Better the problem is just reported in some other
// way (to be implemented) so the user can easily fix it by removing and
// re-adding the image. Missing images in symbols aren't fatal errors either.
image.addToSchematic(); // can throw
mImages.insert(image.getUuid(), &image);
emit imageAdded(image);
}
void Schematic::removeImage(SI_Image& image) {
if ((!mIsAddedToProject) || (mImages.value(image.getUuid()) != &image)) {
throw LogicError(__FILE__, __LINE__);
}
image.removeFromSchematic(); // can throw
mImages.remove(image.getUuid());
emit imageRemoved(image);
}
/*******************************************************************************
* General Methods
******************************************************************************/
void Schematic::addToProject() {
if (mIsAddedToProject) {
throw LogicError(__FILE__, __LINE__);
}
ScopeGuardList sgl(mSymbols.count() + mBusSegments.count() +
mNetSegments.count() + mPolygons.count() + mTexts.count() +
mImages.count());
foreach (SI_Symbol* symbol, mSymbols) {
symbol->addToSchematic(); // can throw
sgl.add([symbol]() { symbol->removeFromSchematic(); });
}
foreach (SI_BusSegment* segment, mBusSegments) {
segment->addToSchematic(); // can throw
sgl.add([segment]() { segment->removeFromSchematic(); });
}
foreach (SI_NetSegment* segment, mNetSegments) {
segment->addToSchematic(); // can throw
sgl.add([segment]() { segment->removeFromSchematic(); });
}
foreach (SI_Polygon* polygon, mPolygons) {
polygon->addToSchematic(); // can throw
sgl.add([polygon]() { polygon->removeFromSchematic(); });
}
foreach (SI_Text* text, mTexts) {
text->addToSchematic(); // can throw
sgl.add([text]() { text->removeFromSchematic(); });
}
foreach (SI_Image* image, mImages) {
image->addToSchematic(); // can throw
sgl.add([image]() { image->removeFromSchematic(); });
}
// Move directory atomically (last step which could throw an exception).
if (mDirectory->getFileSystem() != mProject.getDirectory().getFileSystem()) {
TransactionalDirectory dst(mProject.getDirectory(),
"schematics/" % mDirectoryName);
mDirectory->moveTo(dst); // can throw
}
mIsAddedToProject = true;
sgl.dismiss();
}
void Schematic::removeFromProject() {
if (!mIsAddedToProject) {
throw LogicError(__FILE__, __LINE__);
}
ScopeGuardList sgl(mSymbols.count() + mBusSegments.count() +
mNetSegments.count() + mPolygons.count() + mTexts.count() +
mImages.count());
foreach (SI_Image* image, mImages) {
image->removeFromSchematic(); // can throw
sgl.add([image]() { image->addToSchematic(); });
}
foreach (SI_Text* text, mTexts) {
text->removeFromSchematic(); // can throw
sgl.add([text]() { text->addToSchematic(); });
}
foreach (SI_Polygon* polygon, mPolygons) {
polygon->removeFromSchematic(); // can throw
sgl.add([polygon]() { polygon->addToSchematic(); });
}
foreach (SI_NetSegment* segment, mNetSegments) {
segment->removeFromSchematic(); // can throw
sgl.add([segment]() { segment->addToSchematic(); });
}
foreach (SI_BusSegment* segment, mBusSegments) {
segment->removeFromSchematic(); // can throw
sgl.add([segment]() { segment->addToSchematic(); });
}
foreach (SI_Symbol* symbol, mSymbols) {
symbol->removeFromSchematic(); // can throw
sgl.add([symbol]() { symbol->addToSchematic(); });
}
// Move directory atomically (last step which could throw an exception).
TransactionalDirectory tmp;
mDirectory->moveTo(tmp); // can throw
mIsAddedToProject = false;
sgl.dismiss();
}
void Schematic::save() {
// Content.
{
std::unique_ptr<SExpression> root =
SExpression::createList("librepcb_schematic");
root->appendChild(mUuid);
root->ensureLineBreak();
root->appendChild("name", mName);
root->ensureLineBreak();
SExpression& gridNode = root->appendList("grid");
gridNode.appendChild("interval", mGridInterval);
gridNode.appendChild("unit", mGridUnit);
root->ensureLineBreak();
for (const SI_Symbol* obj : mSymbols) {
root->ensureLineBreak();
obj->serialize(root->appendList("symbol"));
}
root->ensureLineBreak();
for (const SI_BusSegment* obj : mBusSegments) {
root->ensureLineBreak();
obj->serialize(root->appendList("bussegment"));
}
root->ensureLineBreak();
for (const SI_NetSegment* obj : mNetSegments) {
root->ensureLineBreak();
obj->serialize(root->appendList("netsegment"));
}
root->ensureLineBreak();
for (const SI_Polygon* obj : mPolygons) {
root->ensureLineBreak();
obj->getPolygon().serialize(root->appendList("polygon"));
}
root->ensureLineBreak();
for (const SI_Text* obj : mTexts) {
root->ensureLineBreak();
obj->getTextObj().serialize(root->appendList("text"));
}
root->ensureLineBreak();
for (const SI_Image* obj : mImages) {
root->ensureLineBreak();
obj->getImage()->serialize(root->appendList("image"));
}
root->ensureLineBreak();
mDirectory->write("schematic.lp", root->toByteArray());
}
// User settings.
{
std::unique_ptr<SExpression> root =
SExpression::createList("librepcb_schematic_user_settings");
root->ensureLineBreak();
mDirectory->write("settings.user.lp", root->toByteArray());
}
}
void Schematic::updateAllLabelAnchors() noexcept {
foreach (SI_BusSegment* s, mBusSegments) {
s->updateAllLabelAnchors();
}
foreach (SI_NetSegment* s, mNetSegments) {
s->updateAllNetLabelAnchors();
}
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace librepcb