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