Loading files
/*
* 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
7e4fdaba9This file is pinned to the selected release. Download the original to open it in your design tool.