1 /*
2 * LibrePCB - Professional EDA for everyone!
3 * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
4 * https://librepcb.org/
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*******************************************************************************
21 * Includes
22 ******************************************************************************/
23 #include "cmdpasteschematicitems.h"
24
25 #include "../schematiceditor/schematicclipboarddata.h"
26 #include "cmdchangenetsignalofschematicnetsegment.h"
27
28 #include <librepcb/common/scopeguard.h>
29 #include <librepcb/library/cmp/component.h>
30 #include <librepcb/library/sym/symbol.h>
31 #include <librepcb/project/circuit/circuit.h>
32 #include <librepcb/project/circuit/cmd/cmdcomponentinstanceadd.h>
33 #include <librepcb/project/circuit/cmd/cmdcompsiginstsetnetsignal.h>
34 #include <librepcb/project/circuit/cmd/cmdnetclassadd.h>
35 #include <librepcb/project/circuit/cmd/cmdnetsignaladd.h>
36 #include <librepcb/project/circuit/cmd/cmdnetsignaledit.h>
37 #include <librepcb/project/circuit/componentinstance.h>
38 #include <librepcb/project/circuit/componentsignalinstance.h>
39 #include <librepcb/project/library/cmd/cmdprojectlibraryaddelement.h>
40 #include <librepcb/project/library/projectlibrary.h>
41 #include <librepcb/project/project.h>
42 #include <librepcb/project/schematics/cmd/cmdschematicnetlabeladd.h>
43 #include <librepcb/project/schematics/cmd/cmdschematicnetsegmentadd.h>
44 #include <librepcb/project/schematics/cmd/cmdschematicnetsegmentaddelements.h>
45 #include <librepcb/project/schematics/cmd/cmdsymbolinstanceadd.h>
46 #include <librepcb/project/schematics/items/si_netlabel.h>
47 #include <librepcb/project/schematics/items/si_netline.h>
48 #include <librepcb/project/schematics/items/si_netpoint.h>
49 #include <librepcb/project/schematics/items/si_netsegment.h>
50 #include <librepcb/project/schematics/items/si_symbol.h>
51 #include <librepcb/project/schematics/items/si_symbolpin.h>
52 #include <librepcb/project/schematics/schematic.h>
53 #include <librepcb/project/settings/projectsettings.h>
54
55 #include <QtCore>
56
57 /*******************************************************************************
58 * Namespace
59 ******************************************************************************/
60 namespace librepcb {
61 namespace project {
62 namespace editor {
63
64 /*******************************************************************************
65 * Constructors / Destructor
66 ******************************************************************************/
67
CmdPasteSchematicItems(Schematic & schematic,std::unique_ptr<SchematicClipboardData> data,const Point & posOffset)68 CmdPasteSchematicItems::CmdPasteSchematicItems(
69 Schematic& schematic, std::unique_ptr<SchematicClipboardData> data,
70 const Point& posOffset) noexcept
71 : UndoCommandGroup(tr("Paste Schematic Elements")),
72 mProject(schematic.getProject()),
73 mSchematic(schematic),
74 mData(std::move(data)),
75 mPosOffset(posOffset) {
76 Q_ASSERT(mData);
77 }
78
~CmdPasteSchematicItems()79 CmdPasteSchematicItems::~CmdPasteSchematicItems() noexcept {
80 }
81
82 /*******************************************************************************
83 * Inherited from UndoCommand
84 ******************************************************************************/
85
performExecute()86 bool CmdPasteSchematicItems::performExecute() {
87 // if an error occurs, undo all already executed child commands
88 auto undoScopeGuard = scopeGuard([&]() { performUndo(); });
89
90 // Notes:
91 //
92 // - If a component name is already existing, generate a new name. Otherwise
93 // keep the original name.
94 // - The graphics items of the added elements are selected immediately to
95 // allow dragging them afterwards.
96
97 // Copy new components to project library
98 std::unique_ptr<TransactionalDirectory> cmpDir = mData->getDirectory("cmp");
99 foreach (const QString& dirname, cmpDir->getDirs()) {
100 if (!mProject.getLibrary().getComponent(Uuid::fromString(dirname))) {
101 QScopedPointer<library::Component> cmp(
102 new library::Component(std::unique_ptr<TransactionalDirectory>(
103 new TransactionalDirectory(*cmpDir, dirname))));
104 execNewChildCmd(new CmdProjectLibraryAddElement<library::Component>(
105 mProject.getLibrary(), *cmp.take()));
106 }
107 }
108
109 // Copy new symbols to project library
110 std::unique_ptr<TransactionalDirectory> symDir = mData->getDirectory("sym");
111 foreach (const QString& dirname, symDir->getDirs()) {
112 if (!mProject.getLibrary().getSymbol(Uuid::fromString(dirname))) {
113 QScopedPointer<library::Symbol> cmp(
114 new library::Symbol(std::unique_ptr<TransactionalDirectory>(
115 new TransactionalDirectory(*symDir, dirname))));
116 execNewChildCmd(new CmdProjectLibraryAddElement<library::Symbol>(
117 mProject.getLibrary(), *cmp.take()));
118 }
119 }
120
121 // Paste components
122 QHash<Uuid, Uuid> componentInstanceMap;
123 for (const SchematicClipboardData::ComponentInstance& cmp :
124 mData->getComponentInstances()) {
125 const library::Component* libCmp =
126 mProject.getLibrary().getComponent(cmp.libComponentUuid);
127 if (!libCmp) throw LogicError(__FILE__, __LINE__);
128
129 CircuitIdentifier name = cmp.name;
130 if (mProject.getCircuit().getComponentInstanceByName(*name)) {
131 name = CircuitIdentifier(
132 mProject.getCircuit().generateAutoComponentInstanceName(
133 libCmp->getPrefixes().value(
134 mProject.getSettings().getLocaleOrder())));
135 }
136 QScopedPointer<ComponentInstance> copy(
137 new ComponentInstance(mProject.getCircuit(), *libCmp,
138 cmp.libVariantUuid, name, cmp.libDeviceUuid));
139 copy->setValue(cmp.value);
140 copy->setAttributes(cmp.attributes);
141 componentInstanceMap.insert(cmp.uuid, copy->getUuid());
142 execNewChildCmd(
143 new CmdComponentInstanceAdd(mProject.getCircuit(), copy.take()));
144 }
145
146 // Paste symbols
147 QHash<Uuid, Uuid> symbolMap;
148 for (const SchematicClipboardData::SymbolInstance& sym :
149 mData->getSymbolInstances()) {
150 ComponentInstance* cmpInst =
151 mProject.getCircuit().getComponentInstanceByUuid(
152 componentInstanceMap.value(sym.componentInstanceUuid,
153 Uuid::createRandom()));
154 if (!cmpInst) throw LogicError(__FILE__, __LINE__);
155
156 QScopedPointer<SI_Symbol> copy(
157 new SI_Symbol(mSchematic, *cmpInst, sym.symbolVariantItemUuid,
158 sym.position + mPosOffset, sym.rotation, sym.mirrored));
159 copy->setSelected(true);
160 symbolMap.insert(sym.uuid, copy->getUuid());
161 execNewChildCmd(new CmdSymbolInstanceAdd(*copy.take()));
162 }
163
164 // Paste net segments
165 for (const SchematicClipboardData::NetSegment& seg :
166 mData->getNetSegments()) {
167 // Get or add netclass with the name "default"
168 NetClass* netclass =
169 mProject.getCircuit().getNetClassByName(ElementName("default"));
170 if (!netclass) {
171 CmdNetClassAdd* cmd =
172 new CmdNetClassAdd(mProject.getCircuit(), ElementName("default"));
173 execNewChildCmd(cmd);
174 netclass = cmd->getNetClass();
175 Q_ASSERT(netclass);
176 }
177
178 // Add a new netsignal
179 CmdNetSignalAdd* cmdAddNetSignal =
180 new CmdNetSignalAdd(mProject.getCircuit(), *netclass);
181 execNewChildCmd(cmdAddNetSignal);
182 NetSignal* netSignal = cmdAddNetSignal->getNetSignal();
183 Q_ASSERT(netSignal);
184 tl::optional<CircuitIdentifier> forcedNetName;
185
186 // Add new segment
187 SI_NetSegment* copy = new SI_NetSegment(mSchematic, *netSignal);
188 copy->setSelected(true);
189 execNewChildCmd(new CmdSchematicNetSegmentAdd(*copy));
190
191 // Add netpoints and netlines
192 QScopedPointer<CmdSchematicNetSegmentAddElements> cmdAddElements(
193 new CmdSchematicNetSegmentAddElements(*copy));
194 QHash<Uuid, SI_NetPoint*> netPointMap;
195 for (const Junction& junction : seg.junctions) {
196 SI_NetPoint* netpoint =
197 cmdAddElements->addNetPoint(junction.getPosition() + mPosOffset);
198 netpoint->setSelected(true);
199 netPointMap.insert(junction.getUuid(), netpoint);
200 }
201 for (const NetLine& nl : seg.lines) {
202 SI_NetLineAnchor* start = nullptr;
203 if (tl::optional<Uuid> anchor = nl.getStartPoint().tryGetJunction()) {
204 start = netPointMap[*anchor];
205 Q_ASSERT(start);
206 } else if (tl::optional<NetLineAnchor::PinAnchor> anchor =
207 nl.getStartPoint().tryGetPin()) {
208 SI_Symbol* symbol = mSchematic.getSymbolByUuid(
209 symbolMap.value(anchor->symbol, Uuid::createRandom()));
210 Q_ASSERT(symbol);
211 SI_SymbolPin* pin = symbol->getPin(anchor->pin);
212 Q_ASSERT(pin);
213 start = pin;
214 ComponentSignalInstance* sigInst = pin->getComponentSignalInstance();
215 if (sigInst && (sigInst->getNetSignal() != netSignal)) {
216 execNewChildCmd(new CmdCompSigInstSetNetSignal(*sigInst, netSignal));
217 }
218 if (sigInst && (sigInst->isNetSignalNameForced()) && (!forcedNetName)) {
219 forcedNetName = CircuitIdentifier(sigInst->getForcedNetSignalName());
220 }
221 } else {
222 throw LogicError(__FILE__, __LINE__);
223 }
224 SI_NetLineAnchor* end = nullptr;
225 if (tl::optional<Uuid> anchor = nl.getEndPoint().tryGetJunction()) {
226 end = netPointMap[*anchor];
227 Q_ASSERT(end);
228 } else if (tl::optional<NetLineAnchor::PinAnchor> anchor =
229 nl.getEndPoint().tryGetPin()) {
230 SI_Symbol* symbol = mSchematic.getSymbolByUuid(
231 symbolMap.value(anchor->symbol, Uuid::createRandom()));
232 Q_ASSERT(symbol);
233 SI_SymbolPin* pin = symbol->getPin(anchor->pin);
234 Q_ASSERT(pin);
235 end = pin;
236 ComponentSignalInstance* sigInst = pin->getComponentSignalInstance();
237 if (sigInst && (sigInst->getNetSignal() != netSignal)) {
238 execNewChildCmd(new CmdCompSigInstSetNetSignal(*sigInst, netSignal));
239 }
240 if (sigInst && (sigInst->isNetSignalNameForced()) && (!forcedNetName)) {
241 forcedNetName = CircuitIdentifier(sigInst->getForcedNetSignalName());
242 }
243 } else {
244 throw LogicError(__FILE__, __LINE__);
245 }
246 SI_NetLine* netline = cmdAddElements->addNetLine(*start, *end);
247 netline->setSelected(true);
248 }
249 execNewChildCmd(cmdAddElements.take());
250
251 // Add netlabels
252 for (const NetLabel& nl : seg.labels) {
253 CmdSchematicNetLabelAdd* cmd = new CmdSchematicNetLabelAdd(
254 *copy, nl.getPosition() + mPosOffset, nl.getRotation());
255 execNewChildCmd(cmd);
256 cmd->getNetLabel()->setSelected(true);
257 if (!forcedNetName) {
258 // If the net segment has at least one net label, copy the original
259 // net name.
260 forcedNetName = seg.netName;
261 }
262 }
263
264 // If the net signal name is enforced, rename it or merge it with an
265 // existing net signal.
266 if (forcedNetName) {
267 if (NetSignal* ns =
268 mProject.getCircuit().getNetSignalByName(**forcedNetName)) {
269 // merge nets
270 execNewChildCmd(
271 new CmdChangeNetSignalOfSchematicNetSegment(*copy, *ns));
272 } else {
273 // rename net
274 CmdNetSignalEdit* cmd =
275 new CmdNetSignalEdit(mProject.getCircuit(), *netSignal);
276 cmd->setName(*forcedNetName, false);
277 execNewChildCmd(cmd);
278 }
279 }
280 }
281
282 undoScopeGuard.dismiss(); // no undo required
283 return getChildCount() > 0;
284 }
285
286 /*******************************************************************************
287 * End of File
288 ******************************************************************************/
289
290 } // namespace editor
291 } // namespace project
292 } // namespace librepcb
293