Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
0e185510cThis 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 "cmdpasteboarditems.h"
#include "../../project/cmd/cmdboardholeadd.h"
#include "../../project/cmd/cmdboardnetsegmentadd.h"
#include "../../project/cmd/cmdboardnetsegmentaddelements.h"
#include "../../project/cmd/cmdboardplaneadd.h"
#include "../../project/cmd/cmdboardpolygonadd.h"
#include "../../project/cmd/cmdboardstroketextadd.h"
#include "../../project/cmd/cmdboardzoneadd.h"
#include "../../project/cmd/cmddeviceinstanceadd.h"
#include "../../project/cmd/cmdnetclassadd.h"
#include "../../project/cmd/cmdnetsignaladd.h"
#include "../../project/cmd/cmdprojectlibraryaddelement.h"
#include "../board/boardclipboarddata.h"
#include "../board/boardgraphicsscene.h"
#include "../board/graphicsitems/bgi_device.h"
#include "../board/graphicsitems/bgi_hole.h"
#include "../board/graphicsitems/bgi_netline.h"
#include "../board/graphicsitems/bgi_netpoint.h"
#include "../board/graphicsitems/bgi_pad.h"
#include "../board/graphicsitems/bgi_plane.h"
#include "../board/graphicsitems/bgi_polygon.h"
#include "../board/graphicsitems/bgi_stroketext.h"
#include "../board/graphicsitems/bgi_via.h"
#include "../board/graphicsitems/bgi_zone.h"
#include "cmdremoveboarditems.h"
#include <librepcb/core/library/dev/device.h>
#include <librepcb/core/library/pkg/package.h>
#include <librepcb/core/project/board/board.h>
#include <librepcb/core/project/board/boardnetsegmentsplitter.h>
#include <librepcb/core/project/board/items/bi_device.h>
#include <librepcb/core/project/board/items/bi_hole.h>
#include <librepcb/core/project/board/items/bi_netline.h>
#include <librepcb/core/project/board/items/bi_netpoint.h>
#include <librepcb/core/project/board/items/bi_netsegment.h>
#include <librepcb/core/project/board/items/bi_pad.h>
#include <librepcb/core/project/board/items/bi_plane.h>
#include <librepcb/core/project/board/items/bi_polygon.h>
#include <librepcb/core/project/board/items/bi_stroketext.h>
#include <librepcb/core/project/board/items/bi_via.h>
#include <librepcb/core/project/board/items/bi_zone.h>
#include <librepcb/core/project/circuit/circuit.h>
#include <librepcb/core/project/circuit/netsignal.h>
#include <librepcb/core/project/project.h>
#include <librepcb/core/project/projectlibrary.h>
#include <librepcb/core/utils/scopeguard.h>
#include <librepcb/core/utils/toolbox.h>
#include <QtCore>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace editor {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
CmdPasteBoardItems::CmdPasteBoardItems(BoardGraphicsScene& scene,
std::unique_ptr<BoardClipboardData> data,
const Point& posOffset) noexcept
: UndoCommandGroup(tr("Paste Board Elements")),
mScene(scene),
mBoard(mScene.getBoard()),
mProject(mBoard.getProject()),
mData(std::move(data)),
mPosOffset(posOffset) {
Q_ASSERT(mData);
}
CmdPasteBoardItems::~CmdPasteBoardItems() noexcept {
}
/*******************************************************************************
* Inherited from UndoCommand
******************************************************************************/
bool CmdPasteBoardItems::performExecute() {
// if an error occurs, undo all already executed child commands
auto undoScopeGuard = scopeGuard([&]() { performUndo(); });
// Notes:
//
// - Devices are only pasted if the corresponding component exists in the
// circuit, and the device does not yet exist on the board (one cannot
// paste a device if it is already added to the board).
// - Netlines which were attached to a pad or via which was not copy/pasted
// will be attached to newly created freestanding netpoints.
// - The graphics items of the added elements are selected immediately to
// allow dragging them afterwards.
// Paste devices which do not yet exist on the board
QSet<Uuid> pastedDevices;
for (const BoardClipboardData::Device& dev : mData->getDevices()) {
ComponentInstance* cmpInst =
mProject.getCircuit().getComponentInstanceByUuid(dev.componentUuid);
if (!cmpInst) {
continue; // Corresponding component does not exist (anymore) in circuit.
}
BI_Device* devInst =
mBoard.getDeviceInstanceByComponentUuid(dev.componentUuid);
if (devInst) {
continue; // Device already exist on the board.
}
// Copy new device to project library, if not existing already
std::optional<Uuid> pgkUuid;
if (const Device* libDev =
mProject.getLibrary().getDevice(dev.libDeviceUuid)) {
pgkUuid = libDev->getPackageUuid();
} else {
std::unique_ptr<Device> newLibDev =
Device::open(mData->getDirectory("dev/" % dev.libDeviceUuid.toStr()));
pgkUuid = newLibDev->getPackageUuid();
execNewChildCmd(new CmdProjectLibraryAddElement<Device>(
mProject.getLibrary(), *newLibDev.release()));
}
Q_ASSERT(pgkUuid);
// Copy new package to project library, if not existing already
if (!mProject.getLibrary().getPackage(*pgkUuid)) {
std::unique_ptr<Package> newLibPgk =
Package::open(mData->getDirectory("pkg/" % pgkUuid->toStr()));
execNewChildCmd(new CmdProjectLibraryAddElement<Package>(
mProject.getLibrary(), *newLibPgk.release()));
}
// Add device instance to board
std::unique_ptr<BI_Device> device(
new BI_Device(mBoard, *cmpInst, dev.libDeviceUuid, dev.libFootprintUuid,
dev.position + mPosOffset, dev.rotation, dev.mirrored,
dev.locked, dev.glue, false));
for (const BoardStrokeTextData& text : dev.strokeTexts) {
// Note: Keep the UUID since it acts as a reference to the original
// library footprint text.
BoardStrokeTextData copy(text);
copy.setPosition(copy.getPosition() + mPosOffset); // move
BI_StrokeText* item = new BI_StrokeText(mBoard, copy);
device->addStrokeText(*item);
}
execNewChildCmd(new CmdDeviceInstanceAdd(*device));
if (auto item = mScene.getDevices().value(device.release())) {
item->setSelected(true);
}
pastedDevices.insert(dev.componentUuid);
}
// Paste net segments
for (const BoardClipboardData::NetSegment& seg : mData->getNetSegments()) {
BoardNetSegmentSplitter splitter;
for (auto it = mData->getPadPositions().constBegin();
it != mData->getPadPositions().constEnd(); ++it) {
const Uuid& device = it.key().first;
const Uuid& pad = it.key().second;
if (!pastedDevices.contains(device)) {
// Device was not pasted, so we have to replace all pads by junctions
splitter.replaceFootprintPadByJunctions(
TraceAnchor::footprintPad(device, pad), it.value());
}
}
for (const BoardPadData& p : seg.pads) {
splitter.addPad(p, false);
}
for (const Via& v : seg.vias) {
splitter.addVia(v, false);
}
for (const Junction& junction : seg.junctions) {
splitter.addJunction(junction);
}
for (const Trace& trace : seg.traces) {
splitter.addTrace(trace);
}
foreach (const BoardNetSegmentSplitter::Segment& segment,
splitter.split()) {
// Add new segment
NetSignal* netsignal =
seg.netName ? getOrCreateNetSignal(**seg.netName) : nullptr;
BI_NetSegment* copy =
new BI_NetSegment(mBoard, Uuid::createRandom(), netsignal);
execNewChildCmd(new CmdBoardNetSegmentAdd(*copy));
// Add pads, vias, netpoints and netlines
std::unique_ptr<CmdBoardNetSegmentAddElements> cmdAddElements(
new CmdBoardNetSegmentAddElements(*copy));
QHash<Uuid, BI_Pad*> padMap;
for (const BoardPadData& p : segment.pads) {
BI_Pad* pad = cmdAddElements->addPad(BoardPadData(
Uuid::createRandom(), p.getPosition() + mPosOffset, p.getRotation(),
p.getShape(), p.getWidth(), p.getHeight(), p.getRadius(),
p.getCustomShapeOutline(), p.getStopMaskConfig(),
p.getSolderPasteConfig(), p.getCopperClearance(),
p.getComponentSide(), p.getFunction(), p.getHoles(), p.isLocked()));
padMap.insert(p.getUuid(), pad);
}
QHash<Uuid, BI_Via*> viaMap;
for (const Via& v : segment.vias) {
BI_Via* via = cmdAddElements->addVia(
Via(Uuid::createRandom(), v.getStartLayer(), v.getEndLayer(),
v.getPosition() + mPosOffset, v.getDrillDiameter(), v.getSize(),
v.getExposureConfig()));
viaMap.insert(v.getUuid(), via);
}
QHash<Uuid, BI_NetPoint*> netPointMap;
for (const Junction& junction : segment.junctions) {
BI_NetPoint* netpoint =
cmdAddElements->addNetPoint(junction.getPosition() + mPosOffset);
netPointMap.insert(junction.getUuid(), netpoint);
}
for (const Trace& trace : segment.traces) {
BI_NetLineAnchor* p1 = nullptr;
if (std::optional<Uuid> anchor = trace.getP1().tryGetJunction()) {
p1 = netPointMap[*anchor];
} else if (std::optional<Uuid> anchor = trace.getP1().tryGetPad()) {
p1 = padMap[*anchor];
} else if (std::optional<Uuid> anchor = trace.getP1().tryGetVia()) {
p1 = viaMap[*anchor];
} else if (std::optional<TraceAnchor::PadAnchor> anchor =
trace.getP1().tryGetFootprintPad()) {
Q_ASSERT(pastedDevices.contains(anchor->device));
BI_Device* device =
mBoard.getDeviceInstanceByComponentUuid(anchor->device);
p1 = device ? device->getPad(anchor->pad) : nullptr;
}
BI_NetLineAnchor* p2 = nullptr;
if (std::optional<Uuid> anchor = trace.getP2().tryGetJunction()) {
p2 = netPointMap[*anchor];
} else if (std::optional<Uuid> anchor = trace.getP2().tryGetPad()) {
p2 = padMap[*anchor];
} else if (std::optional<Uuid> anchor = trace.getP2().tryGetVia()) {
p2 = viaMap[*anchor];
} else if (std::optional<TraceAnchor::PadAnchor> anchor =
trace.getP2().tryGetFootprintPad()) {
Q_ASSERT(pastedDevices.contains(anchor->device));
BI_Device* device =
mBoard.getDeviceInstanceByComponentUuid(anchor->device);
p2 = device ? device->getPad(anchor->pad) : nullptr;
}
if ((!p1) || (!p2)) {
throw LogicError(__FILE__, __LINE__);
}
cmdAddElements->addNetLine(*p1, *p2, trace.getLayer(),
trace.getWidth());
}
execNewChildCmd(cmdAddElements.release());
// Select pasted net segment items.
foreach (BI_Pad* pad, copy->getPads()) {
if (auto item = mScene.getPads().value(pad)) {
item->setSelected(true);
}
}
foreach (BI_Via* via, copy->getVias()) {
if (auto item = mScene.getVias().value(via)) {
item->setSelected(true);
}
}
foreach (BI_NetPoint* netPoint, copy->getNetPoints()) {
if (auto item = mScene.getNetPoints().value(netPoint)) {
item->setSelected(true);
}
}
foreach (BI_NetLine* netLine, copy->getNetLines()) {
if (auto item = mScene.getNetLines().value(netLine)) {
item->setSelected(true);
}
}
}
}
// Paste planes
for (const BoardClipboardData::Plane& plane : mData->getPlanes()) {
BI_Plane* copy = new BI_Plane(
mBoard,
Uuid::createRandom(), // assign new UUID
*plane.layer,
plane.netSignalName ? getOrCreateNetSignal(**plane.netSignalName)
: nullptr,
plane.outline.translated(mPosOffset) // move
);
copy->setMinWidth(plane.minWidth);
copy->setMinClearanceToCopper(plane.minClearanceToCopper);
copy->setMinClearanceToBoard(plane.minClearanceToBoard);
copy->setMinClearanceToNpth(plane.minClearanceToNpth);
copy->setKeepIslands(plane.keepIslands);
copy->setPriority(plane.priority);
copy->setConnectStyle(plane.connectStyle);
copy->setThermalGap(plane.thermalGap);
copy->setThermalSpokeWidth(plane.thermalSpokeWidth);
copy->setLocked(plane.locked);
execNewChildCmd(new CmdBoardPlaneAdd(*copy));
if (auto item = mScene.getPlanes().value(copy)) {
item->setSelected(true);
}
}
// Paste zones
for (const BoardZoneData& zone : std::as_const(mData->getZones())) {
BoardZoneData copy(Uuid::createRandom(), zone); // assign new UUID
copy.setOutline(copy.getOutline().translated(mPosOffset)); // move
BI_Zone* item = new BI_Zone(mBoard, copy);
execNewChildCmd(new CmdBoardZoneAdd(*item));
if (auto graphicsItem = mScene.getZones().value(item)) {
graphicsItem->setSelected(true);
}
}
// Paste polygons
for (const BoardPolygonData& polygon : std::as_const(mData->getPolygons())) {
BoardPolygonData copy(Uuid::createRandom(), polygon); // assign new UUID
copy.setPath(copy.getPath().translated(mPosOffset)); // move
BI_Polygon* item = new BI_Polygon(mBoard, copy);
execNewChildCmd(new CmdBoardPolygonAdd(*item));
if (auto graphicsItem = mScene.getPolygons().value(item)) {
graphicsItem->setSelected(true);
}
}
// Paste stroke texts
for (const BoardStrokeTextData& text :
std::as_const(mData->getStrokeTexts())) {
BoardStrokeTextData copy(Uuid::createRandom(), text); // assign new UUID
copy.setPosition(copy.getPosition() + mPosOffset); // move
BI_StrokeText* item = new BI_StrokeText(mBoard, copy);
execNewChildCmd(new CmdBoardStrokeTextAdd(*item));
if (auto graphicsItem = mScene.getStrokeTexts().value(item)) {
graphicsItem->setSelected(true);
}
}
// Paste holes
for (const BoardHoleData& hole : std::as_const(mData->getHoles())) {
BoardHoleData copy(Uuid::createRandom(), hole); // assign new UUID
copy.setPath(NonEmptyPath(copy.getPath()->translated(mPosOffset))); // move
BI_Hole* item = new BI_Hole(mBoard, copy);
execNewChildCmd(new CmdBoardHoleAdd(*item));
if (auto graphicsItem = mScene.getHoles().value(item)) {
graphicsItem->setSelected(true);
}
}
undoScopeGuard.dismiss(); // no undo required
return getChildCount() > 0;
}
NetSignal* CmdPasteBoardItems::getOrCreateNetSignal(const QString& name) {
NetSignal* netSignal = mProject.getCircuit().getNetSignalByName(name);
if (netSignal) {
return netSignal;
}
// Get or create netclass with the name "default"
NetClass* netclass =
mProject.getCircuit().getNetClassByName(ElementName("default"));
if (!netclass) {
CmdNetClassAdd* cmd =
new CmdNetClassAdd(mProject.getCircuit(), ElementName("default"));
execNewChildCmd(cmd);
netclass = cmd->getNetClass();
Q_ASSERT(netclass);
}
// Create new net signal
CmdNetSignalAdd* cmdAddNetSignal =
new CmdNetSignalAdd(mProject.getCircuit(), *netclass);
execNewChildCmd(cmdAddNetSignal);
return cmdAddNetSignal->getNetSignal();
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace editor
} // namespace librepcb