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 "schematiceditorstate_drawwire.h"
24
25 #include "../../cmd/cmdchangenetsignalofschematicnetsegment.h"
26 #include "../../cmd/cmdcombineschematicnetsegments.h"
27 #include "../schematiceditor.h"
28
29 #include <librepcb/common/graphics/graphicsview.h>
30 #include <librepcb/common/undostack.h>
31 #include <librepcb/project/circuit/circuit.h>
32 #include <librepcb/project/circuit/cmd/cmdcompsiginstsetnetsignal.h>
33 #include <librepcb/project/circuit/cmd/cmdnetclassadd.h>
34 #include <librepcb/project/circuit/cmd/cmdnetsignaladd.h>
35 #include <librepcb/project/circuit/cmd/cmdnetsignaledit.h>
36 #include <librepcb/project/circuit/componentsignalinstance.h>
37 #include <librepcb/project/circuit/netsignal.h>
38 #include <librepcb/project/project.h>
39 #include <librepcb/project/schematics/cmd/cmdschematicnetsegmentadd.h>
40 #include <librepcb/project/schematics/cmd/cmdschematicnetsegmentaddelements.h>
41 #include <librepcb/project/schematics/cmd/cmdschematicnetsegmentremoveelements.h>
42 #include <librepcb/project/schematics/items/si_netpoint.h>
43 #include <librepcb/project/schematics/items/si_netsegment.h>
44 #include <librepcb/project/schematics/items/si_symbolpin.h>
45
46 #include <QtCore>
47 #include <QtWidgets>
48
49 /*******************************************************************************
50 * Namespace
51 ******************************************************************************/
52 namespace librepcb {
53 namespace project {
54 namespace editor {
55
56 /*******************************************************************************
57 * Constructors / Destructor
58 ******************************************************************************/
59
SchematicEditorState_DrawWire(const Context & context)60 SchematicEditorState_DrawWire::SchematicEditorState_DrawWire(
61 const Context& context) noexcept
62 : SchematicEditorState(context),
63 mCircuit(context.project.getCircuit()),
64 mSubState(SubState::IDLE),
65 mWireMode(WireMode_HV),
66 mCursorPos(),
67 mFixedStartAnchor(nullptr),
68 mPositioningNetLine1(nullptr),
69 mPositioningNetPoint1(nullptr),
70 mPositioningNetLine2(nullptr),
71 mPositioningNetPoint2(nullptr) {
72 }
73
~SchematicEditorState_DrawWire()74 SchematicEditorState_DrawWire::~SchematicEditorState_DrawWire() noexcept {
75 Q_ASSERT(mSubState == SubState::IDLE);
76 }
77
78 /*******************************************************************************
79 * General Methods
80 ******************************************************************************/
81
entry()82 bool SchematicEditorState_DrawWire::entry() noexcept {
83 Q_ASSERT(mSubState == SubState::IDLE);
84
85 // clear schematic selection because selection does not make sense in this
86 // state
87 if (Schematic* schematic = getActiveSchematic()) {
88 schematic->clearSelection();
89 }
90
91 // Add wire mode actions to the "command" toolbar
92 mWireModeActions.insert(
93 WireMode_HV,
94 mContext.editorUi.commandToolbar->addAction(
95 QIcon(":/img/command_toolbars/wire_h_v.png"), ""));
96 mWireModeActions.insert(
97 WireMode_VH,
98 mContext.editorUi.commandToolbar->addAction(
99 QIcon(":/img/command_toolbars/wire_v_h.png"), ""));
100 mWireModeActions.insert(
101 WireMode_9045,
102 mContext.editorUi.commandToolbar->addAction(
103 QIcon(":/img/command_toolbars/wire_90_45.png"), ""));
104 mWireModeActions.insert(
105 WireMode_4590,
106 mContext.editorUi.commandToolbar->addAction(
107 QIcon(":/img/command_toolbars/wire_45_90.png"), ""));
108 mWireModeActions.insert(
109 WireMode_Straight,
110 mContext.editorUi.commandToolbar->addAction(
111 QIcon(":/img/command_toolbars/wire_straight.png"), ""));
112 mActionSeparators.append(mContext.editorUi.commandToolbar->addSeparator());
113 updateWireModeActionsCheckedState();
114
115 // connect the wire mode actions with the slot
116 // updateWireModeActionsCheckedState()
117 foreach (WireMode mode, mWireModeActions.keys()) {
118 connect(mWireModeActions.value(mode), &QAction::triggered, [this, mode]() {
119 mWireMode = mode;
120 updateWireModeActionsCheckedState();
121 });
122 }
123
124 // change the cursor
125 mContext.editorGraphicsView.setCursor(Qt::CrossCursor);
126
127 return true;
128 }
129
exit()130 bool SchematicEditorState_DrawWire::exit() noexcept {
131 // abort the currently active command
132 if (mSubState != SubState::IDLE) {
133 abortPositioning(true);
134 }
135
136 // Remove actions / widgets from the "command" toolbar
137 qDeleteAll(mWireModeActions);
138 mWireModeActions.clear();
139 qDeleteAll(mActionSeparators);
140 mActionSeparators.clear();
141
142 // change the cursor
143 mContext.editorGraphicsView.setCursor(Qt::ArrowCursor);
144
145 return true;
146 }
147
148 /*******************************************************************************
149 * Event Handlers
150 ******************************************************************************/
151
processAbortCommand()152 bool SchematicEditorState_DrawWire::processAbortCommand() noexcept {
153 if (mSubState == SubState::POSITIONING_NETPOINT) {
154 return abortPositioning(true);
155 }
156
157 return false;
158 }
159
processKeyPressed(const QKeyEvent & e)160 bool SchematicEditorState_DrawWire::processKeyPressed(
161 const QKeyEvent& e) noexcept {
162 Schematic* schematic = getActiveSchematic();
163 if (!schematic) return false;
164
165 switch (e.key()) {
166 case Qt::Key_Shift: {
167 if (mSubState == SubState::POSITIONING_NETPOINT) {
168 updateNetpointPositions(*schematic, false);
169 return true;
170 }
171 break;
172 }
173
174 default: { break; }
175 }
176
177 return false;
178 }
179
processKeyReleased(const QKeyEvent & e)180 bool SchematicEditorState_DrawWire::processKeyReleased(
181 const QKeyEvent& e) noexcept {
182 Schematic* schematic = getActiveSchematic();
183 if (!schematic) return false;
184
185 switch (e.key()) {
186 case Qt::Key_Shift: {
187 if (mSubState == SubState::POSITIONING_NETPOINT) {
188 updateNetpointPositions(*schematic, true);
189 return true;
190 }
191 break;
192 }
193
194 default: { break; }
195 }
196
197 return false;
198 }
199
processGraphicsSceneMouseMoved(QGraphicsSceneMouseEvent & e)200 bool SchematicEditorState_DrawWire::processGraphicsSceneMouseMoved(
201 QGraphicsSceneMouseEvent& e) noexcept {
202 Schematic* schematic = getActiveSchematic();
203 if (!schematic) return false;
204
205 mCursorPos = Point::fromPx(e.scenePos());
206
207 if (mSubState == SubState::POSITIONING_NETPOINT) {
208 bool snap = !e.modifiers().testFlag(Qt::ShiftModifier);
209 updateNetpointPositions(*schematic, snap);
210 return true;
211 }
212
213 return false;
214 }
215
processGraphicsSceneLeftMouseButtonPressed(QGraphicsSceneMouseEvent & e)216 bool SchematicEditorState_DrawWire::processGraphicsSceneLeftMouseButtonPressed(
217 QGraphicsSceneMouseEvent& e) noexcept {
218 Schematic* schematic = getActiveSchematic();
219 if (!schematic) return false;
220
221 mCursorPos = Point::fromPx(e.scenePos());
222 bool snap = !e.modifiers().testFlag(Qt::ShiftModifier);
223
224 if (mSubState == SubState::IDLE) {
225 // start adding netpoints/netlines
226 return startPositioning(*schematic, snap);
227 } else if (mSubState == SubState::POSITIONING_NETPOINT) {
228 // fix the current point and add a new point + line
229 return addNextNetPoint(*schematic, snap);
230 }
231
232 return false;
233 }
234
235 bool SchematicEditorState_DrawWire::
processGraphicsSceneLeftMouseButtonDoubleClicked(QGraphicsSceneMouseEvent & e)236 processGraphicsSceneLeftMouseButtonDoubleClicked(
237 QGraphicsSceneMouseEvent& e) noexcept {
238 Schematic* schematic = getActiveSchematic();
239 if (!schematic) return false;
240
241 mCursorPos = Point::fromPx(e.scenePos());
242 bool snap = !e.modifiers().testFlag(Qt::ShiftModifier);
243
244 if (mSubState == SubState::POSITIONING_NETPOINT) {
245 // fix the current point and add a new point + line
246 return addNextNetPoint(*schematic, snap);
247 }
248
249 return false;
250 }
251
252 bool SchematicEditorState_DrawWire::
processGraphicsSceneRightMouseButtonReleased(QGraphicsSceneMouseEvent & e)253 processGraphicsSceneRightMouseButtonReleased(
254 QGraphicsSceneMouseEvent& e) noexcept {
255 Schematic* schematic = getActiveSchematic();
256 if (!schematic) return false;
257
258 mCursorPos = Point::fromPx(e.scenePos());
259
260 if (mSubState == SubState::POSITIONING_NETPOINT) {
261 // Only switch to next wire mode if cursor was not moved during click
262 if (e.screenPos() == e.buttonDownScreenPos(Qt::RightButton)) {
263 mWireMode = static_cast<WireMode>(mWireMode + 1);
264 if (mWireMode == WireMode_COUNT) mWireMode = static_cast<WireMode>(0);
265 updateWireModeActionsCheckedState();
266 bool snap = !e.modifiers().testFlag(Qt::ShiftModifier);
267 updateNetpointPositions(*schematic, snap);
268 }
269
270 // Always accept the event if we are drawing a wire! When ignoring the
271 // event, the state machine will abort the tool by a right click!
272 return true;
273 }
274
275 return false;
276 }
277
processSwitchToSchematicPage(int index)278 bool SchematicEditorState_DrawWire::processSwitchToSchematicPage(
279 int index) noexcept {
280 Q_UNUSED(index);
281 return mSubState == SubState::IDLE;
282 }
283
284 /*******************************************************************************
285 * Private Methods
286 ******************************************************************************/
287
startPositioning(Schematic & schematic,bool snap,SI_NetPoint * fixedPoint)288 bool SchematicEditorState_DrawWire::startPositioning(
289 Schematic& schematic, bool snap, SI_NetPoint* fixedPoint) noexcept {
290 try {
291 // start a new undo command
292 Q_ASSERT(mSubState == SubState::IDLE);
293 mContext.undoStack.beginCmdGroup(tr("Draw Wire"));
294 mSubState = SubState::POSITIONING_NETPOINT;
295
296 // determine the fixed anchor (create one if it doesn't exist already)
297 NetSignal* netsignal = nullptr;
298 SI_NetSegment* netsegment = nullptr;
299 tl::optional<CircuitIdentifier> forcedNetName;
300 Point pos = mCursorPos.mappedToGrid(getGridInterval());
301 if (snap || fixedPoint) {
302 if (fixedPoint) {
303 mFixedStartAnchor = fixedPoint;
304 netsegment = &fixedPoint->getNetSegment();
305 pos = fixedPoint->getPosition();
306 } else if (SI_NetPoint* netpoint = findNetPoint(schematic, mCursorPos)) {
307 mFixedStartAnchor = netpoint;
308 netsegment = &netpoint->getNetSegment();
309 pos = netpoint->getPosition();
310 } else if (SI_SymbolPin* pin = findSymbolPin(schematic, mCursorPos)) {
311 mFixedStartAnchor = pin;
312 netsegment = pin->getNetSegmentOfLines();
313 netsignal = pin->getCompSigInstNetSignal();
314 pos = pin->getPosition();
315 if (pin->getComponentSignalInstance()) {
316 QString name =
317 pin->getComponentSignalInstance()->getForcedNetSignalName();
318 try {
319 if (!name.isEmpty())
320 forcedNetName = CircuitIdentifier(name); // can throw
321 } catch (const Exception& e) {
322 QMessageBox::warning(
323 parentWidget(), tr("Invalid net name"),
324 tr("Could not apply the forced net name because '%1' is "
325 "not a valid net name.")
326 .arg(name));
327 }
328 }
329 } else if (SI_NetLine* netline = findNetLine(schematic, mCursorPos)) {
330 // split netline
331 netsegment = &netline->getNetSegment();
332 QScopedPointer<CmdSchematicNetSegmentAddElements> cmdAdd(
333 new CmdSchematicNetSegmentAddElements(*netsegment));
334 mFixedStartAnchor = cmdAdd->addNetPoint(pos);
335 cmdAdd->addNetLine(*mFixedStartAnchor, netline->getStartPoint());
336 cmdAdd->addNetLine(*mFixedStartAnchor, netline->getEndPoint());
337 mContext.undoStack.appendToCmdGroup(cmdAdd.take()); // can throw
338 QScopedPointer<CmdSchematicNetSegmentRemoveElements> cmdRemove(
339 new CmdSchematicNetSegmentRemoveElements(*netsegment));
340 cmdRemove->removeNetLine(*netline);
341 mContext.undoStack.appendToCmdGroup(cmdRemove.take()); // can throw
342 }
343 }
344
345 // find netsignal if name is given
346 if (forcedNetName) {
347 netsignal = mCircuit.getNetSignalByName(**forcedNetName);
348 }
349
350 // create new netsignal if none found
351 if ((!netsegment) && (!netsignal)) {
352 // get or add netclass with the name "default"
353 NetClass* netclass = mCircuit.getNetClassByName(ElementName("default"));
354 if (!netclass) {
355 CmdNetClassAdd* cmd =
356 new CmdNetClassAdd(mCircuit, ElementName("default"));
357 mContext.undoStack.appendToCmdGroup(cmd); // can throw
358 netclass = cmd->getNetClass();
359 Q_ASSERT(netclass);
360 }
361 // add new netsignal
362 CmdNetSignalAdd* cmd =
363 new CmdNetSignalAdd(mCircuit, *netclass, forcedNetName);
364 mContext.undoStack.appendToCmdGroup(cmd); // can throw
365 netsignal = cmd->getNetSignal();
366 Q_ASSERT(netsignal);
367 }
368
369 // create new netsegment if none found
370 if (!netsegment) {
371 // connect pin if needed
372 if (SI_SymbolPin* pin = dynamic_cast<SI_SymbolPin*>(mFixedStartAnchor)) {
373 Q_ASSERT(pin->getComponentSignalInstance());
374 mContext.undoStack.appendToCmdGroup(new CmdCompSigInstSetNetSignal(
375 *pin->getComponentSignalInstance(), netsignal));
376 }
377 // add net segment
378 Q_ASSERT(netsignal);
379 CmdSchematicNetSegmentAdd* cmd =
380 new CmdSchematicNetSegmentAdd(schematic, *netsignal);
381 mContext.undoStack.appendToCmdGroup(cmd); // can throw
382 netsegment = cmd->getNetSegment();
383 }
384
385 // add netpoint if none found
386 Q_ASSERT(netsegment);
387 CmdSchematicNetSegmentAddElements* cmd =
388 new CmdSchematicNetSegmentAddElements(*netsegment);
389 if (!mFixedStartAnchor) {
390 mFixedStartAnchor = cmd->addNetPoint(pos);
391 }
392 Q_ASSERT(mFixedStartAnchor);
393
394 // add more netpoints & netlines
395 SI_NetPoint* p2 = cmd->addNetPoint(pos);
396 Q_ASSERT(p2); // second netpoint
397 SI_NetLine* l1 = cmd->addNetLine(*mFixedStartAnchor, *p2);
398 Q_ASSERT(l1); // first netline
399 SI_NetPoint* p3 = cmd->addNetPoint(pos);
400 Q_ASSERT(p3); // third netpoint
401 SI_NetLine* l2 = cmd->addNetLine(*p2, *p3);
402 Q_ASSERT(l2); // second netline
403 mContext.undoStack.appendToCmdGroup(cmd); // can throw
404
405 // update members
406 mPositioningNetPoint1 = p2;
407 mPositioningNetLine1 = l1;
408 mPositioningNetPoint2 = p3;
409 mPositioningNetLine2 = l2;
410
411 // properly place the new netpoints/netlines according the current wire mode
412 updateNetpointPositions(schematic, snap);
413
414 // highlight all elements of the current netsignal
415 mCircuit.setHighlightedNetSignal(&netsegment->getNetSignal());
416
417 return true;
418 } catch (const Exception& e) {
419 QMessageBox::critical(parentWidget(), tr("Error"), e.getMsg());
420 if (mSubState != SubState::IDLE) {
421 abortPositioning(false);
422 }
423 return false;
424 }
425 }
426
addNextNetPoint(Schematic & schematic,bool snap)427 bool SchematicEditorState_DrawWire::addNextNetPoint(Schematic& schematic,
428 bool snap) noexcept {
429 Q_ASSERT(mSubState == SubState::POSITIONING_NETPOINT);
430
431 // Snap to the item under the cursor and make sure the lines are up to date.
432 Point pos = updateNetpointPositions(schematic, snap);
433
434 // abort if p2 == p0 (no line drawn)
435 if (pos == mFixedStartAnchor->getPosition()) {
436 abortPositioning(true);
437 return false;
438 } else {
439 bool finishCommand = false;
440
441 try {
442 // create a new undo command group to make all changes atomic
443 QScopedPointer<UndoCommandGroup> cmdGroup(
444 new UndoCommandGroup("Add schematic netline"));
445
446 // remove p1 if p1 == p0 || p1 == p2
447 if ((mPositioningNetPoint1->getPosition() ==
448 mFixedStartAnchor->getPosition()) ||
449 (mPositioningNetPoint1->getPosition() ==
450 mPositioningNetPoint2->getPosition())) {
451 QScopedPointer<CmdSchematicNetSegmentRemoveElements> cmdRemove(
452 new CmdSchematicNetSegmentRemoveElements(
453 mPositioningNetPoint1->getNetSegment()));
454 cmdRemove->removeNetPoint(*mPositioningNetPoint1);
455 cmdRemove->removeNetLine(*mPositioningNetLine1);
456 cmdRemove->removeNetLine(*mPositioningNetLine2);
457 QScopedPointer<CmdSchematicNetSegmentAddElements> cmdAdd(
458 new CmdSchematicNetSegmentAddElements(
459 mPositioningNetPoint1->getNetSegment()));
460 mPositioningNetLine2 =
461 cmdAdd->addNetLine(*mFixedStartAnchor, *mPositioningNetPoint2);
462 mContext.undoStack.appendToCmdGroup(cmdAdd.take());
463 mContext.undoStack.appendToCmdGroup(cmdRemove.take());
464 }
465
466 // find anchor under cursor
467 SI_NetLineAnchor* otherAnchor = nullptr;
468 SI_NetSegment* otherNetSegment = nullptr;
469 QString otherForcedNetName;
470 if (snap) {
471 if (SI_NetPoint* netpoint =
472 findNetPoint(schematic, pos, mPositioningNetPoint2)) {
473 otherAnchor = netpoint;
474 otherNetSegment = &netpoint->getNetSegment();
475 } else if (SI_SymbolPin* pin = findSymbolPin(schematic, pos)) {
476 otherAnchor = pin;
477 otherNetSegment = pin->getNetSegmentOfLines();
478 // connect pin if needed
479 if (!otherNetSegment) {
480 Q_ASSERT(pin->getComponentSignalInstance());
481 mContext.undoStack.appendToCmdGroup(new CmdCompSigInstSetNetSignal(
482 *pin->getComponentSignalInstance(),
483 &mPositioningNetPoint2->getNetSignalOfNetSegment()));
484 otherForcedNetName =
485 pin->getComponentSignalInstance()->getForcedNetSignalName();
486 }
487 } else if (SI_NetLine* netline =
488 findNetLine(schematic, pos, mPositioningNetLine2)) {
489 // split netline
490 otherNetSegment = &netline->getNetSegment();
491 QScopedPointer<CmdSchematicNetSegmentAddElements> cmdAdd(
492 new CmdSchematicNetSegmentAddElements(*otherNetSegment));
493 otherAnchor = cmdAdd->addNetPoint(pos);
494 cmdAdd->addNetLine(*otherAnchor, netline->getStartPoint());
495 cmdAdd->addNetLine(*otherAnchor, netline->getEndPoint());
496 mContext.undoStack.appendToCmdGroup(cmdAdd.take()); // can throw
497 QScopedPointer<CmdSchematicNetSegmentRemoveElements> cmdRemove(
498 new CmdSchematicNetSegmentRemoveElements(*otherNetSegment));
499 cmdRemove->removeNetLine(*netline);
500 mContext.undoStack.appendToCmdGroup(cmdRemove.take()); // can throw
501 }
502 }
503
504 // if anchor found under the cursor, replace "mPositioningNetPoint2" with
505 // it
506 if (otherAnchor) {
507 if ((!otherNetSegment) ||
508 (otherNetSegment == &mPositioningNetPoint2->getNetSegment())) {
509 QScopedPointer<CmdSchematicNetSegmentAddElements> cmdAdd(
510 new CmdSchematicNetSegmentAddElements(
511 mPositioningNetPoint2->getNetSegment()));
512 cmdAdd->addNetLine(*otherAnchor,
513 mPositioningNetLine2->getStartPoint());
514 mContext.undoStack.appendToCmdGroup(cmdAdd.take()); // can throw
515 QScopedPointer<CmdSchematicNetSegmentRemoveElements> cmdRemove(
516 new CmdSchematicNetSegmentRemoveElements(
517 mPositioningNetPoint2->getNetSegment()));
518 cmdRemove->removeNetPoint(*mPositioningNetPoint2);
519 cmdRemove->removeNetLine(*mPositioningNetLine2);
520 mContext.undoStack.appendToCmdGroup(cmdRemove.take()); // can throw
521 } else {
522 // change net signal if needed
523 NetSignal* thisSignal =
524 &mPositioningNetPoint2->getNetSignalOfNetSegment();
525 NetSignal* otherSignal = &otherNetSegment->getNetSignal();
526 if (thisSignal != otherSignal) {
527 NetSignal* resultingNetSignal = nullptr;
528 SI_NetSegment* netSegmentToChangeSignal = nullptr;
529 if (otherNetSegment->getForcedNetNames().count() > 0) {
530 resultingNetSignal = &otherNetSegment->getNetSignal();
531 netSegmentToChangeSignal =
532 &mPositioningNetPoint2->getNetSegment();
533 } else if (mPositioningNetPoint2->getNetSegment()
534 .getForcedNetNames()
535 .count() > 0) {
536 resultingNetSignal =
537 &mPositioningNetPoint2->getNetSignalOfNetSegment();
538 netSegmentToChangeSignal = otherNetSegment;
539 } else if (otherSignal->hasAutoName() &&
540 (!thisSignal->hasAutoName())) {
541 resultingNetSignal =
542 &mPositioningNetPoint2->getNetSignalOfNetSegment();
543 netSegmentToChangeSignal = otherNetSegment;
544 } else {
545 resultingNetSignal = &otherNetSegment->getNetSignal();
546 netSegmentToChangeSignal =
547 &mPositioningNetPoint2->getNetSegment();
548 }
549 mContext.undoStack.appendToCmdGroup(
550 new CmdChangeNetSignalOfSchematicNetSegment(
551 *netSegmentToChangeSignal, *resultingNetSignal));
552 }
553 // combine both net segments
554 mContext.undoStack.appendToCmdGroup(
555 new CmdCombineSchematicNetSegments(
556 mPositioningNetPoint2->getNetSegment(),
557 *mPositioningNetPoint2, *otherNetSegment, *otherAnchor));
558 }
559 if (!otherForcedNetName.isEmpty()) {
560 // change net name if connected to a pin with forced net name
561 try {
562 CircuitIdentifier name =
563 CircuitIdentifier(otherForcedNetName); // can throw
564 NetSignal* signal =
565 schematic.getProject().getCircuit().getNetSignalByName(*name);
566 if (signal) {
567 mContext.undoStack.appendToCmdGroup(
568 new CmdChangeNetSignalOfSchematicNetSegment(
569 mPositioningNetPoint2->getNetSegment(), *signal));
570 } else {
571 QScopedPointer<CmdNetSignalEdit> cmd(new CmdNetSignalEdit(
572 schematic.getProject().getCircuit(),
573 mPositioningNetPoint2->getNetSignalOfNetSegment()));
574 cmd->setName(name, false);
575 mContext.undoStack.appendToCmdGroup(cmd.take());
576 }
577 } catch (const Exception& e) {
578 QMessageBox::warning(
579 parentWidget(), tr("Invalid net name"),
580 QString(
581 tr("Could not apply the forced net name because '%1' is "
582 "not a valid net name."))
583 .arg(otherForcedNetName));
584 }
585 }
586 finishCommand = true;
587 } else {
588 finishCommand = false;
589 }
590 } catch (const UserCanceled& e) {
591 return false;
592 } catch (const Exception& e) {
593 QMessageBox::critical(parentWidget(), tr("Error"), e.getMsg());
594 return false;
595 }
596
597 try {
598 // finish the current command
599 mContext.undoStack.commitCmdGroup();
600 mSubState = SubState::IDLE;
601
602 // abort or start a new command
603 if (finishCommand) {
604 mContext.undoStack.beginCmdGroup(QString()); // this is ugly!
605 abortPositioning(true);
606 return false;
607 } else {
608 return startPositioning(schematic, snap, mPositioningNetPoint2);
609 }
610 } catch (const Exception& e) {
611 QMessageBox::critical(parentWidget(), tr("Error"), e.getMsg());
612 if (mSubState != SubState::IDLE) {
613 abortPositioning(false);
614 }
615 return false;
616 }
617 }
618 }
619
abortPositioning(bool showErrMsgBox)620 bool SchematicEditorState_DrawWire::abortPositioning(
621 bool showErrMsgBox) noexcept {
622 try {
623 mCircuit.setHighlightedNetSignal(nullptr);
624 mSubState = SubState::IDLE;
625 mFixedStartAnchor = nullptr;
626 mPositioningNetLine1 = nullptr;
627 mPositioningNetLine2 = nullptr;
628 mPositioningNetPoint1 = nullptr;
629 mPositioningNetPoint2 = nullptr;
630 mContext.undoStack.abortCmdGroup(); // can throw
631 return true;
632 } catch (const Exception& e) {
633 if (showErrMsgBox)
634 QMessageBox::critical(parentWidget(), tr("Error"), e.getMsg());
635 return false;
636 }
637 }
638
findSymbolPin(Schematic & schematic,const Point & pos) const639 SI_SymbolPin* SchematicEditorState_DrawWire::findSymbolPin(
640 Schematic& schematic, const Point& pos) const noexcept {
641 QList<SI_SymbolPin*> items = schematic.getPinsAtScenePos(pos);
642 for (int i = items.count() - 1; i >= 0; --i) {
643 // only choose pins which are connected to a component signal!
644 if (!items.at(i)->getComponentSignalInstance()) {
645 items.removeAt(i);
646 }
647 }
648 return (items.count() > 0) ? items.first() : nullptr;
649 }
650
findNetPoint(Schematic & schematic,const Point & pos,SI_NetPoint * except) const651 SI_NetPoint* SchematicEditorState_DrawWire::findNetPoint(
652 Schematic& schematic, const Point& pos, SI_NetPoint* except) const
653 noexcept {
654 QList<SI_NetPoint*> items = schematic.getNetPointsAtScenePos(pos);
655 items.removeAll(except);
656 return (items.count() > 0) ? items.first() : nullptr;
657 }
658
findNetLine(Schematic & schematic,const Point & pos,SI_NetLine * except) const659 SI_NetLine* SchematicEditorState_DrawWire::findNetLine(Schematic& schematic,
660 const Point& pos,
661 SI_NetLine* except) const
662 noexcept {
663 QList<SI_NetLine*> items = schematic.getNetLinesAtScenePos(pos);
664 items.removeAll(except);
665 return (items.count() > 0) ? items.first() : nullptr;
666 }
667
updateNetpointPositions(Schematic & schematic,bool snap)668 Point SchematicEditorState_DrawWire::updateNetpointPositions(
669 Schematic& schematic, bool snap) noexcept {
670 // Find anchor under cursor.
671 Point pos = mCursorPos.mappedToGrid(getGridInterval());
672 if (snap) {
673 if (SI_NetPoint* np = findNetPoint(schematic, mCursorPos)) {
674 pos = np->getPosition();
675 } else if (SI_SymbolPin* pin = findSymbolPin(schematic, mCursorPos)) {
676 pos = pin->getPosition();
677 }
678 }
679
680 mPositioningNetPoint1->setPosition(
681 calcMiddlePointPos(mFixedStartAnchor->getPosition(), pos, mWireMode));
682 mPositioningNetPoint2->setPosition(pos);
683 return pos;
684 }
685
686 void SchematicEditorState_DrawWire::
updateWireModeActionsCheckedState()687 updateWireModeActionsCheckedState() noexcept {
688 foreach (WireMode key, mWireModeActions.keys()) {
689 mWireModeActions.value(key)->setCheckable(key == mWireMode);
690 mWireModeActions.value(key)->setChecked(key == mWireMode);
691 }
692 }
693
calcMiddlePointPos(const Point & p1,const Point p2,WireMode mode) const694 Point SchematicEditorState_DrawWire::calcMiddlePointPos(const Point& p1,
695 const Point p2,
696 WireMode mode) const
697 noexcept {
698 Point delta = p2 - p1;
699 switch (mode) {
700 case WireMode_HV:
701 return Point(p2.getX(), p1.getY());
702 case WireMode_VH:
703 return Point(p1.getX(), p2.getY());
704 case WireMode_9045:
705 if (delta.getX().abs() >= delta.getY().abs())
706 return Point(
707 p2.getX() - delta.getY().abs() * (delta.getX() >= 0 ? 1 : -1),
708 p1.getY());
709 else
710 return Point(
711 p1.getX(),
712 p2.getY() - delta.getX().abs() * (delta.getY() >= 0 ? 1 : -1));
713 case WireMode_4590:
714 if (delta.getX().abs() >= delta.getY().abs())
715 return Point(
716 p1.getX() + delta.getY().abs() * (delta.getX() >= 0 ? 1 : -1),
717 p2.getY());
718 else
719 return Point(
720 p2.getX(),
721 p1.getY() + delta.getX().abs() * (delta.getY() >= 0 ? 1 : -1));
722 case WireMode_Straight:
723 return p1;
724 default:
725 Q_ASSERT(false);
726 return Point();
727 }
728 }
729
730 /*******************************************************************************
731 * End of File
732 ******************************************************************************/
733
734 } // namespace editor
735 } // namespace project
736 } // namespace librepcb
737