Loading files
ce705b508This 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 : 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 : 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 : 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 : 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