PageRenderTime 43ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sketch/pcbsketchwidget.cpp

https://bitbucket.org/steve918/fritzing
C++ | 1699 lines | 1303 code | 287 blank | 109 comment | 360 complexity | 257d91e4e15735031ccd04ececb44ae9 MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. /*******************************************************************
  2. Part of the Fritzing project - http://fritzing.org
  3. Copyright (c) 2007-2009 Fachhochschule Potsdam - http://fh-potsdam.de
  4. Fritzing is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. Fritzing is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
  14. ********************************************************************
  15. $Revision: 3999 $:
  16. $Author: cohen@irascible.com $:
  17. $Date: 2010-02-21 02:47:45 -0800 (Sun, 21 Feb 2010) $
  18. ********************************************************************/
  19. #include "pcbsketchwidget.h"
  20. #include "../debugdialog.h"
  21. #include "../svg/svgfilesplitter.h"
  22. #include "../items/tracewire.h"
  23. #include "../items/virtualwire.h"
  24. #include "../items/resizableboard.h"
  25. #include "../waitpushundostack.h"
  26. #include "../autoroute/autorouter1.h"
  27. #include "../connectors/connectoritem.h"
  28. #include "../items/moduleidnames.h"
  29. #include "../help/sketchmainhelp.h"
  30. #include "../utils/ratsnestcolors.h"
  31. #include <limits>
  32. static int MAX_INT = std::numeric_limits<int>::max();
  33. struct DistanceThing {
  34. int distance;
  35. bool fromConnector0;
  36. };
  37. QHash <ConnectorItem *, DistanceThing *> distances;
  38. bool bySize(QList<ConnectorItem *> * l1, QList<ConnectorItem *> * l2) {
  39. return l1->count() >= l2->count();
  40. }
  41. bool distanceLessThan(ConnectorItem * end0, ConnectorItem * end1) {
  42. if (end0->connectorType() == Connector::Male && end1->connectorType() == Connector::Female) {
  43. return true;
  44. }
  45. if (end1->connectorType() == Connector::Male && end0->connectorType() == Connector::Female) {
  46. return false;
  47. }
  48. DistanceThing * dt0 = distances.value(end0, NULL);
  49. DistanceThing * dt1 = distances.value(end1, NULL);
  50. if (dt0 && dt1) {
  51. return dt0->distance <= dt1->distance;
  52. }
  53. if (dt0) {
  54. return true;
  55. }
  56. if (dt1) {
  57. return false;
  58. }
  59. return true;
  60. }
  61. //////////////////////////////////////////////////////
  62. PCBSketchWidget::PCBSketchWidget(ViewIdentifierClass::ViewIdentifier viewIdentifier, QWidget *parent)
  63. : SketchWidget(viewIdentifier, parent)
  64. {
  65. m_viewName = QObject::tr("PCB View");
  66. m_shortName = QObject::tr("pcb");
  67. m_standardBackgroundColor = QColor(160,168,179);
  68. initBackgroundColor();
  69. m_routingStatus.zero();
  70. m_traceColor = "trace";
  71. m_jumperColor = "jumper";
  72. m_jumperWidth = 3;
  73. m_cleanType = noClean;
  74. m_addBoard = false;
  75. }
  76. void PCBSketchWidget::setWireVisible(Wire * wire)
  77. {
  78. bool visible = wire->getRatsnest() || wire->getTrace() || wire->getJumper();
  79. wire->setVisible(visible);
  80. wire->setEverVisible(visible);
  81. }
  82. void PCBSketchWidget::addViewLayers() {
  83. addPcbViewLayers();
  84. // disable these for now
  85. ViewLayer * viewLayer = m_viewLayers.value(ViewLayer::Vias);
  86. viewLayer->action()->setEnabled(false);
  87. viewLayer = m_viewLayers.value(ViewLayer::Copper1);
  88. viewLayer->action()->setEnabled(false);
  89. //viewLayer = m_viewLayers.value(ViewLayer::Keepout);
  90. //viewLayer->action()->setEnabled(false);
  91. }
  92. void PCBSketchWidget::makeWires(QList<ConnectorItem *> & partsConnectorItems, QList <Wire *> & ratsnestWires, Wire * & modelWire, RatsnestCommand * ratsnestCommand)
  93. {
  94. int count = partsConnectorItems.count();
  95. for (int i = 0; i < count - 1; i++) {
  96. ConnectorItem * source = partsConnectorItems[i];
  97. for (int j = i + 1; j < count; j++) {
  98. ConnectorItem * dest = partsConnectorItems[j];
  99. // if you can't get from i to j via wires, then add a virtual ratsnest wire
  100. Wire* tempWire = source->wiredTo(dest, ViewGeometry::RatsnestFlag);
  101. if (tempWire == NULL) {
  102. Wire * newWire = makeOneRatsnestWire(source, dest, ratsnestCommand, source->wiredTo(dest, ViewGeometry::TraceFlag | ViewGeometry::JumperFlag) == NULL);
  103. if (newWire != NULL) {
  104. ratsnestWires.append(newWire);
  105. if (source->wiredTo(dest, ViewGeometry::TraceFlag | ViewGeometry::JumperFlag)) {
  106. newWire->setRouted(true);
  107. }
  108. }
  109. }
  110. else {
  111. modelWire = tempWire;
  112. }
  113. }
  114. }
  115. }
  116. ViewLayer::ViewLayerID PCBSketchWidget::multiLayerGetViewLayerID(ModelPart * modelPart, ViewIdentifierClass::ViewIdentifier viewIdentifier, QDomElement & layers, QString & layerName) {
  117. Q_UNUSED(modelPart);
  118. Q_UNUSED(viewIdentifier);
  119. // priviledge Copper0 if it's available
  120. QDomElement layer = layers.firstChildElement("layer");
  121. while (!layer.isNull()) {
  122. QString lName = layer.attribute("layerId");
  123. if (ViewLayer::viewLayerIDFromXmlString(lName) == ViewLayer::Copper0) {
  124. return ViewLayer::Copper0;
  125. }
  126. layer = layer.nextSiblingElement("layer");
  127. }
  128. return ViewLayer::viewLayerIDFromXmlString(layerName);
  129. }
  130. bool PCBSketchWidget::canDeleteItem(QGraphicsItem * item)
  131. {
  132. VirtualWire * wire = dynamic_cast<VirtualWire *>(item);
  133. if (wire != NULL) return false;
  134. return SketchWidget::canDeleteItem(item);
  135. }
  136. bool PCBSketchWidget::canCopyItem(QGraphicsItem * item)
  137. {
  138. VirtualWire * wire = dynamic_cast<VirtualWire *>(item);
  139. if (wire != NULL) {
  140. if (wire->getRatsnest()) return false;
  141. }
  142. return SketchWidget::canDeleteItem(item);
  143. }
  144. bool PCBSketchWidget::canChainWire(Wire * wire) {
  145. bool result = SketchWidget::canChainWire(wire);
  146. if (!result) return result;
  147. if (wire->getRatsnest()) return false;
  148. return result;
  149. }
  150. void PCBSketchWidget::createJumper() {
  151. QString commandString = tr("Create Jumper from this Wire");
  152. createJumperOrTrace(commandString, ViewGeometry::JumperFlag, m_jumperColor);
  153. ensureJumperLayerVisible();
  154. }
  155. void PCBSketchWidget::createTrace() {
  156. QString commandString = tr("Create Trace from this Wire");
  157. createJumperOrTrace(commandString, ViewGeometry::TraceFlag, m_traceColor);
  158. ensureTraceLayerVisible();
  159. }
  160. void PCBSketchWidget::createJumperOrTrace(const QString & commandString, ViewGeometry::WireFlag flag, const QString & colorString)
  161. {
  162. QList<Wire *> done;
  163. QUndoCommand * parentCommand = NULL;
  164. foreach (QGraphicsItem * item, scene()->selectedItems()) {
  165. Wire * wire = dynamic_cast<Wire *>(item);
  166. if (wire == NULL) continue;
  167. if (done.contains(wire)) continue;
  168. createOneJumperOrTrace(wire, flag, false, done, parentCommand, commandString, colorString);
  169. }
  170. if (parentCommand == NULL) return;
  171. new CleanUpWiresCommand(this, false, parentCommand);
  172. m_undoStack->push(parentCommand);
  173. }
  174. void PCBSketchWidget::createOneJumperOrTrace(Wire * wire, ViewGeometry::WireFlag flag, bool allowAny, QList<Wire *> & done,
  175. QUndoCommand * & parentCommand, const QString & commandString, const QString & colorString)
  176. {
  177. QList<ConnectorItem *> ends;
  178. Wire * jumperOrTrace = NULL;
  179. if (wire->getRatsnest()) {
  180. jumperOrTrace = wire->findJumperOrTraced(ViewGeometry::JumperFlag | ViewGeometry::TraceFlag, ends);
  181. }
  182. else if (wire->getJumper()) {
  183. jumperOrTrace = wire;
  184. }
  185. else if (wire->getTrace()) {
  186. jumperOrTrace = wire;
  187. }
  188. else if (!allowAny) {
  189. // not eligible
  190. return;
  191. }
  192. else {
  193. jumperOrTrace = wire->findJumperOrTraced(ViewGeometry::JumperFlag | ViewGeometry::TraceFlag, ends);
  194. }
  195. if (jumperOrTrace && jumperOrTrace->hasFlag(flag)) {
  196. return;
  197. }
  198. if (parentCommand == NULL) {
  199. parentCommand = new QUndoCommand(commandString);
  200. }
  201. if (jumperOrTrace != NULL) {
  202. QList<Wire *> chained;
  203. QList<ConnectorItem *> uniqueEnds;
  204. jumperOrTrace->collectChained(chained, ends, uniqueEnds);
  205. makeWiresChangeConnectionCommands(chained, parentCommand);
  206. foreach (Wire * c, chained) {
  207. makeDeleteItemCommand(c, BaseCommand::SingleView, parentCommand);
  208. done.append(c);
  209. }
  210. }
  211. long newID = createWire(ends[0], ends[1], flag, false, false, BaseCommand::SingleView, parentCommand);
  212. new WireColorChangeCommand(this, newID, colorString, colorString, getRatsnestOpacity(false), getRatsnestOpacity(false), parentCommand);
  213. new WireWidthChangeCommand(this, newID, Wire::STANDARD_TRACE_WIDTH, Wire::STANDARD_TRACE_WIDTH, parentCommand);
  214. Wire* rat = NULL;
  215. if (wire->getRatsnest()) {
  216. rat = wire;
  217. }
  218. else {
  219. rat = ends[0]->wiredTo(ends[1], ViewGeometry::RatsnestFlag);
  220. }
  221. if (rat != NULL) {
  222. QList<ConnectorItem *> ends;
  223. QList<Wire *> rats;
  224. QList<ConnectorItem *> uniqueEnds;
  225. rat->collectChained(rats, ends, uniqueEnds);
  226. foreach (Wire * r, rats) {
  227. makeChangeRoutedCommand(r, true, getRatsnestOpacity(true), parentCommand);
  228. }
  229. }
  230. }
  231. void PCBSketchWidget::excludeFromAutoroute(bool exclude)
  232. {
  233. foreach (QGraphicsItem * item, scene()->selectedItems()) {
  234. Wire * wire = dynamic_cast<Wire *>(item);
  235. if (wire == NULL) continue;
  236. if (wire->getTrace() || wire->getJumper()) {
  237. QList<Wire *> wires;
  238. QList<ConnectorItem *> ends;
  239. QList<ConnectorItem *> uniqueEnds;
  240. wire->collectChained(wires, ends, uniqueEnds);
  241. foreach (Wire * w, wires) {
  242. w->setAutoroutable(!exclude);
  243. }
  244. }
  245. }
  246. }
  247. bool PCBSketchWidget::ratsAllRouted() {
  248. foreach (QGraphicsItem * item, scene()->items()) {
  249. Wire * wire = dynamic_cast<Wire *>(item);
  250. if (wire == NULL) continue;
  251. if (!wire->getRatsnest()) continue;
  252. if (!wire->getRouted()) return false;
  253. }
  254. return true;
  255. }
  256. void PCBSketchWidget::makeChangeRoutedCommand(Wire * wire, bool routed, qreal opacity, QUndoCommand * parentCommand) {
  257. new WireColorChangeCommand(this, wire->id(), wire->colorString(), wire->colorString(), wire->opacity(), opacity, parentCommand);
  258. ViewGeometry::WireFlags wireFlags = wire->wireFlags();
  259. wireFlags |= ViewGeometry::RoutedFlag;
  260. if (!routed) {
  261. wireFlags &= ~ViewGeometry::RoutedFlag;
  262. }
  263. new WireFlagChangeCommand(this, wire->id(), wire->wireFlags(), wireFlags, parentCommand);
  264. }
  265. void PCBSketchWidget::clearRouting(QUndoCommand * parentCommand) {
  266. Q_UNUSED(parentCommand);
  267. //kill copperfill here
  268. //Autorouter1::clearTraces(this, true, parentCommand);
  269. //updateRatsnestStatus(NULL, parentCommand);
  270. }
  271. void PCBSketchWidget::updateRatsnestStatus(CleanUpWiresCommand* command, QUndoCommand * undoCommand, RoutingStatus & routingStatus)
  272. {
  273. //DebugDialog::debug("update ratsnest status");
  274. QHash<ConnectorItem *, int> indexer;
  275. QList< QList<ConnectorItem *>* > allPartConnectorItems;
  276. Autorouter1::collectAllNets(this, indexer, allPartConnectorItems, false);
  277. routingStatus.zero();
  278. // TODO: handle delete in updateRatsnestColors...
  279. if (command) {
  280. removeRatsnestWires(allPartConnectorItems, command);
  281. }
  282. foreach (QList<ConnectorItem *>* list, allPartConnectorItems) {
  283. delete list;
  284. }
  285. updateRatsnestColors(command, undoCommand, false, routingStatus);
  286. if (routingStatus != m_routingStatus) {
  287. if (command) {
  288. // changing state after the command has already been executed
  289. command->addRoutingStatus(this, m_routingStatus, routingStatus);
  290. }
  291. if (undoCommand) {
  292. // the command is still to be executed
  293. new RoutingStatusCommand(this, m_routingStatus, routingStatus, undoCommand);
  294. }
  295. emit routingStatusSignal(this, routingStatus);
  296. m_routingStatus = routingStatus;
  297. }
  298. }
  299. void PCBSketchWidget::forwardRoutingStatus(const RoutingStatus & routingStatus)
  300. {
  301. m_routingStatus = routingStatus;
  302. SketchWidget::forwardRoutingStatus(routingStatus);
  303. }
  304. void PCBSketchWidget::selectAllExcludedTraces()
  305. {
  306. QList<Wire *> wires;
  307. foreach (QGraphicsItem * item, scene()->items()) {
  308. TraceWire * wire = dynamic_cast<TraceWire *>(item);
  309. if (wire == NULL) continue;
  310. if (wire->parentItem() != NULL) {
  311. // skip module wires
  312. continue;
  313. }
  314. if (!wire->getAutoroutable()) {
  315. wires.append(wire);
  316. }
  317. }
  318. QUndoCommand * parentCommand = new QUndoCommand(QObject::tr("Select all traces marked \"Don't autoroute\""));
  319. stackSelectionState(false, parentCommand);
  320. SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
  321. foreach (Wire * wire, wires) {
  322. selectItemCommand->addRedo(wire->id());
  323. }
  324. scene()->clearSelection();
  325. m_undoStack->push(parentCommand);
  326. }
  327. const QString & PCBSketchWidget::hoverEnterPartConnectorMessage(QGraphicsSceneHoverEvent * event, ConnectorItem * item)
  328. {
  329. Q_UNUSED(event);
  330. Q_UNUSED(item);
  331. static QString message = tr("Click this connector to drag out a new trace.");
  332. return message;
  333. }
  334. bool PCBSketchWidget::modifyNewWireConnections(Wire * dragWire, ConnectorItem * fromDragWire, ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, QUndoCommand * parentCommand)
  335. {
  336. Q_UNUSED(fromDragWire);
  337. if (fromConnectorItem == toConnectorItem) {
  338. // can't drag a wire to itself
  339. return false;
  340. }
  341. bool result = false;
  342. QList<ConnectorItem *> connectorItems;
  343. connectorItems.append(fromConnectorItem);
  344. ConnectorItem::collectEqualPotential(connectorItems);
  345. if (connectorItems.contains(toConnectorItem)) {
  346. // don't generate a wire in bb view if the connectors are already connected
  347. result = true;
  348. }
  349. else {
  350. if (fromConnectorItem->attachedToItemType() == ModelPart::Symbol ||
  351. toConnectorItem->attachedToItemType() == ModelPart::Symbol)
  352. {
  353. // set up extra connection for breadboard view (for now; won't be necessary if breadboard gets ratsnest)
  354. connectSymbols(fromConnectorItem, toConnectorItem, parentCommand);
  355. }
  356. bool fromWire = fromConnectorItem->attachedToItemType() == ModelPart::Wire;
  357. bool toWire = toConnectorItem->attachedToItemType() == ModelPart::Wire;
  358. if (fromWire && toWire)
  359. {
  360. ConnectorItem * originalFromConnectorItem = fromConnectorItem;
  361. ConnectorItem * originalToConnectorItem = toConnectorItem;
  362. ConnectorItem * newFromConnectorItem = lookForBreadboardConnection(fromConnectorItem);
  363. ConnectorItem * newToConnectorItem = lookForBreadboardConnection(toConnectorItem);
  364. if (newFromConnectorItem->attachedToItemType() == ModelPart::Breadboard &&
  365. newToConnectorItem->attachedToItemType() == ModelPart::Breadboard)
  366. {
  367. // connection can be made with one wire
  368. makeModifiedWire(newFromConnectorItem, newToConnectorItem, BaseCommand::CrossView, 0, parentCommand);
  369. }
  370. else if (newToConnectorItem->attachedToItemType() == ModelPart::Breadboard) {
  371. makeTwoWires(originalToConnectorItem, newToConnectorItem, originalFromConnectorItem, newFromConnectorItem, parentCommand);
  372. }
  373. else {
  374. makeTwoWires(originalFromConnectorItem, newFromConnectorItem, originalToConnectorItem, newToConnectorItem, parentCommand);
  375. }
  376. result = true;
  377. }
  378. else if (fromWire) {
  379. modifyNewWireConnectionsAux(fromConnectorItem, toConnectorItem, parentCommand);
  380. result = true;
  381. }
  382. else if (toWire) {
  383. modifyNewWireConnectionsAux(toConnectorItem, fromConnectorItem, parentCommand);
  384. result = true;
  385. }
  386. }
  387. dragWire->connector0()->tempConnectTo(fromConnectorItem, false);
  388. dragWire->connector1()->tempConnectTo(toConnectorItem, false);
  389. QList<ConnectorItem *> ends;
  390. Wire * jumperOrTrace = dragWire->findJumperOrTraced(ViewGeometry::JumperFlag | ViewGeometry::TraceFlag, ends);
  391. dragWire->connector0()->tempRemove(fromConnectorItem, false);
  392. dragWire->connector1()->tempRemove(toConnectorItem, false);
  393. if (jumperOrTrace == NULL) {
  394. long newID = makeModifiedWire(fromConnectorItem, toConnectorItem, BaseCommand::SingleView, ViewGeometry::TraceFlag, parentCommand);
  395. new WireColorChangeCommand(this, newID, m_traceColor, m_traceColor, 1.0, 1.0, parentCommand);
  396. new WireWidthChangeCommand(this, newID, Wire::STANDARD_TRACE_WIDTH, Wire::STANDARD_TRACE_WIDTH, parentCommand);
  397. if (fromConnectorItem->attachedToItemType() == ModelPart::Wire) {
  398. foreach (ConnectorItem * connectorItem, fromConnectorItem->connectedToItems()) {
  399. if (connectorItem->attachedToItemType() == ModelPart::Wire) {
  400. // not sure about the wire type check
  401. new ChangeConnectionCommand(this, BaseCommand::SingleView,
  402. newID, "connector0",
  403. connectorItem->attachedToID(), connectorItem->connectorSharedID(),
  404. true, true, parentCommand);
  405. }
  406. }
  407. }
  408. if (toConnectorItem->attachedToItemType() == ModelPart::Wire) {
  409. foreach (ConnectorItem * connectorItem, toConnectorItem->connectedToItems()) {
  410. if (connectorItem->attachedToItemType() == ModelPart::Wire) {
  411. // not sure about the wire type check
  412. new ChangeConnectionCommand(this, BaseCommand::SingleView,
  413. newID, "connector1",
  414. connectorItem->attachedToID(), connectorItem->connectorSharedID(),
  415. true, true, parentCommand);
  416. }
  417. }
  418. }
  419. }
  420. return result;
  421. }
  422. void PCBSketchWidget::connectSymbols(ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, QUndoCommand * parentCommand) {
  423. ConnectorItem * target1 = NULL;
  424. ConnectorItem * target2 = NULL;
  425. if (fromConnectorItem->attachedToItemType() == ModelPart::Symbol && toConnectorItem->attachedToItemType() == ModelPart::Symbol) {
  426. QList<ConnectorItem *> connectorItems;
  427. connectorItems.append(fromConnectorItem);
  428. ConnectorItem::collectEqualPotential(connectorItems);
  429. foreach (ConnectorItem * c, connectorItems) {
  430. if (c->attachedToItemType() == ModelPart::Part) {
  431. target1 = c;
  432. break;
  433. }
  434. }
  435. connectorItems.clear();
  436. connectorItems.append(toConnectorItem);
  437. ConnectorItem::collectEqualPotential(connectorItems);
  438. foreach (ConnectorItem * c, connectorItems) {
  439. if (c->attachedToItemType() == ModelPart::Part) {
  440. target2 = c;
  441. break;
  442. }
  443. }
  444. }
  445. else if (fromConnectorItem->attachedToItemType() == ModelPart::Symbol) {
  446. connectSymbolPrep(fromConnectorItem, toConnectorItem, target1, target2);
  447. }
  448. else if (toConnectorItem->attachedToItemType() == ModelPart::Symbol) {
  449. connectSymbolPrep(toConnectorItem, fromConnectorItem, target1, target2);
  450. }
  451. if (target1 == NULL) return;
  452. if (target2 == NULL) return;
  453. makeModifiedWire(target1, target2, BaseCommand::CrossView, 0, parentCommand);
  454. }
  455. void PCBSketchWidget::connectSymbolPrep(ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, ConnectorItem * & target1, ConnectorItem * & target2) {
  456. QList<ConnectorItem *> connectorItems;
  457. connectorItems.append(fromConnectorItem);
  458. ConnectorItem::collectEqualPotential(connectorItems);
  459. foreach (ConnectorItem * c, connectorItems) {
  460. if (c->attachedToItemType() == ModelPart::Part) {
  461. target1 = c;
  462. break;
  463. }
  464. }
  465. if (target1 == NULL) return;
  466. if (toConnectorItem->attachedToItemType() == ModelPart::Part) {
  467. target2 = toConnectorItem;
  468. }
  469. else if (toConnectorItem->attachedToItemType() == ModelPart::Wire) {
  470. target2 = findNearestPartConnectorItem(toConnectorItem);
  471. }
  472. }
  473. void PCBSketchWidget::addBoard() {
  474. long newID = ItemBase::getNextID();
  475. ViewGeometry viewGeometry;
  476. viewGeometry.setLoc(QPointF(0, 0));
  477. m_addedBoard = addItem(paletteModel()->retrieveModelPart(ModuleIDNames::rectangleModuleIDName), BaseCommand::SingleView, viewGeometry, newID, -1, -1, NULL, NULL);
  478. // have to put this off until later, because positioning the item doesn't work correctly until the view is visible
  479. // so position it in setCurrent()
  480. m_addBoard = true;
  481. }
  482. void PCBSketchWidget::setCurrent(bool current) {
  483. SketchWidget::setCurrent(current);
  484. if (current && m_addBoard && (m_addedBoard != NULL)) {
  485. m_addBoard = false;
  486. if (m_fixedToCenterItem != NULL) {
  487. QSizeF helpsize = m_fixedToCenterItem->size();
  488. QSizeF vp = this->viewport()->size();
  489. QPointF p;
  490. p.setX((int) ((vp.width() - helpsize.width()) / 2.0));
  491. p.setY((int) ((vp.height() - helpsize.height()) / 2.0));
  492. // TODO: make these constants less arbitrary
  493. p += QPointF(10, 30);
  494. // add a board to the empty sketch, and place it in the help area.
  495. m_addedBoard->setPos(p);
  496. qobject_cast<ResizableBoard *>(m_addedBoard)->resizePixels(110, helpsize.height() - 30 - 30, m_viewLayers);
  497. }
  498. }
  499. }
  500. void PCBSketchWidget::setClipEnds(ClipableWire * vw, bool clipEnds) {
  501. vw->setClipEnds(clipEnds);
  502. }
  503. ViewLayer::ViewLayerID PCBSketchWidget::getDragWireViewLayerID() {
  504. return ViewLayer::Copper0Trace;
  505. }
  506. ViewLayer::ViewLayerID PCBSketchWidget::getWireViewLayerID(const ViewGeometry & viewGeometry) {
  507. if (viewGeometry.getJumper()) {
  508. return ViewLayer::Jumperwires;
  509. }
  510. if (viewGeometry.getTrace()) {
  511. return ViewLayer::Copper0Trace;
  512. }
  513. if (viewGeometry.getRatsnest()) {
  514. return ViewLayer::Ratsnest;
  515. }
  516. return SketchWidget::getWireViewLayerID(viewGeometry);
  517. }
  518. void PCBSketchWidget::initWire(Wire * wire, int penWidth) {
  519. Q_UNUSED(penWidth);
  520. wire->setColorString("unrouted", 1.0);
  521. wire->setPenWidth(1, this);
  522. }
  523. bool PCBSketchWidget::autorouteNeedsBounds() {
  524. return true;
  525. }
  526. bool PCBSketchWidget::autorouteCheckWires() {
  527. return true;
  528. }
  529. bool PCBSketchWidget::autorouteCheckConnectors() {
  530. return true;
  531. }
  532. bool PCBSketchWidget::autorouteCheckParts() {
  533. return false;
  534. }
  535. const QString & PCBSketchWidget::traceColor() {
  536. return m_traceColor;
  537. }
  538. const QString & PCBSketchWidget::jumperColor() {
  539. return m_jumperColor;
  540. }
  541. qreal PCBSketchWidget::jumperWidth() {
  542. return m_jumperWidth;
  543. }
  544. PCBSketchWidget::CleanType PCBSketchWidget::cleanType() {
  545. return m_cleanType;
  546. }
  547. void PCBSketchWidget::ensureTraceLayersVisible() {
  548. ensureLayerVisible(ViewLayer::Copper0);
  549. ensureLayerVisible(ViewLayer::Copper0Trace);
  550. ensureLayerVisible(ViewLayer::GroundPlane);
  551. ensureLayerVisible(ViewLayer::Jumperwires);
  552. }
  553. void PCBSketchWidget::ensureTraceLayerVisible() {
  554. ensureLayerVisible(ViewLayer::Copper0);
  555. ensureLayerVisible(ViewLayer::Copper0Trace);
  556. }
  557. void PCBSketchWidget::ensureJumperLayerVisible() {
  558. ensureLayerVisible(ViewLayer::Jumperwires);
  559. }
  560. bool PCBSketchWidget::canChainMultiple() {
  561. return false;
  562. }
  563. void PCBSketchWidget::setNewPartVisible(ItemBase * itemBase) {
  564. if (itemBase->itemType() == ModelPart::Breadboard || itemBase->itemType() == ModelPart::Symbol) {
  565. // don't need to see the breadboard in the other views
  566. // but it's there so connections can be more easily synched between views
  567. itemBase->setVisible(false);
  568. itemBase->setEverVisible(false);
  569. }
  570. }
  571. bool PCBSketchWidget::canDropModelPart(ModelPart * modelPart) {
  572. switch (modelPart->itemType()) {
  573. case ModelPart::Jumper:
  574. case ModelPart::Logo:
  575. case ModelPart::Ruler:
  576. return true;
  577. case ModelPart::Wire:
  578. case ModelPart::Breadboard:
  579. case ModelPart::Symbol:
  580. case ModelPart::CopperFill:
  581. // can't drag and drop these parts in this view
  582. return false;
  583. case ModelPart::Board:
  584. case ModelPart::ResizableBoard:
  585. return matchesLayer(modelPart);
  586. default:
  587. return true;
  588. }
  589. return true;
  590. }
  591. bool PCBSketchWidget::alreadyRatsnest(ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem) {
  592. if (fromConnectorItem->attachedToItemType() == ModelPart::Wire) {
  593. Wire * wire = dynamic_cast<Wire *>(fromConnectorItem->attachedTo());
  594. if (wire->getRatsnest() || wire->getJumper() || wire->getTrace()) {
  595. // don't make further ratsnest's from ratsnest
  596. return true;
  597. }
  598. }
  599. if (toConnectorItem->attachedToItemType() == ModelPart::Wire) {
  600. Wire * wire = dynamic_cast<Wire *>(toConnectorItem->attachedTo());
  601. if (wire->getRatsnest() || wire->getJumper() || wire->getTrace()) {
  602. // don't make further ratsnest's from ratsnest
  603. return true;
  604. }
  605. }
  606. return false;
  607. }
  608. void PCBSketchWidget::dealWithRatsnest(long fromID, const QString & fromConnectorID,
  609. long toID, const QString & toConnectorID,
  610. bool connect, class RatsnestCommand * ratsnestCommand, bool doEmit)
  611. {
  612. if (!connect) {
  613. return;
  614. }
  615. ConnectorItem * fromConnectorItem = NULL;
  616. ConnectorItem * toConnectorItem = NULL;
  617. if (dealWithRatsnestAux(fromConnectorItem, toConnectorItem, fromID, fromConnectorID,
  618. toID, toConnectorID,
  619. connect, ratsnestCommand, doEmit))
  620. {
  621. return;
  622. }
  623. DebugDialog::debug(QString("deal with ratsnest %1 %2 %3, %4 %5 %6")
  624. .arg(fromConnectorItem->attachedToTitle())
  625. .arg(fromConnectorItem->attachedToID())
  626. .arg(fromConnectorItem->connectorSharedID())
  627. .arg(toConnectorItem->attachedToTitle())
  628. .arg(toConnectorItem->attachedToID())
  629. .arg(toConnectorItem->connectorSharedID())
  630. );
  631. QList<ConnectorItem *> connectorItems;
  632. QList<ConnectorItem *> partsConnectorItems;
  633. connectorItems.append(fromConnectorItem);
  634. ConnectorItem::collectEqualPotential(connectorItems);
  635. ConnectorItem::collectParts(connectorItems, partsConnectorItems, includeSymbols());
  636. QList <Wire *> ratsnestWires;
  637. Wire * modelWire = NULL;
  638. makeWires(partsConnectorItems, ratsnestWires, modelWire, ratsnestCommand);
  639. if (ratsnestWires.count() > 0) {
  640. QColor color;
  641. if (modelWire) {
  642. color = modelWire->color();
  643. }
  644. else {
  645. color = RatsnestColors::netColor(m_viewIdentifier);
  646. }
  647. foreach (Wire * wire, ratsnestWires) {
  648. wire->setColor(color, getRatsnestOpacity(wire));
  649. checkSticky(wire->id(), false, false, NULL);
  650. }
  651. }
  652. return;
  653. }
  654. void PCBSketchWidget::removeRatsnestWires(QList< QList<ConnectorItem *>* > & allPartConnectorItems, CleanUpWiresCommand * command)
  655. {
  656. /*
  657. DebugDialog::debug("----------");
  658. foreach (QList<ConnectorItem *>* list, allPartConnectorItems) {
  659. foreach (ConnectorItem * ci, *list) {
  660. DebugDialog::debug(QString("%1 %2 %3")
  661. .arg(ci->attachedToTitle())
  662. .arg(ci->attachedTo()->instanceTitle())
  663. .arg(ci->connectorSharedName()));
  664. }
  665. DebugDialog::debug("-----");
  666. }
  667. */
  668. QSet<Wire *> deleteWires;
  669. QSet<Wire *> visitedWires;
  670. foreach (QGraphicsItem * item, scene()->items()) {
  671. Wire * wire = dynamic_cast<Wire *>(item);
  672. if (wire == NULL) continue;
  673. if (visitedWires.contains(wire)) continue;
  674. ViewGeometry::WireFlags flag = wire->wireFlags() & (ViewGeometry::RatsnestFlag | ViewGeometry::TraceFlag | ViewGeometry::JumperFlag);
  675. if (flag == 0) continue;
  676. // if a ratsnest is connecting two items that aren't connected any longer
  677. // delete the ratsnest
  678. QList<Wire *> wires;
  679. QList<ConnectorItem *> ends;
  680. QList<ConnectorItem *> uniqueEnds;
  681. wire->collectChained(wires, ends, uniqueEnds);
  682. foreach (Wire * w, wires) {
  683. visitedWires.insert(w);
  684. }
  685. QList<Wire *> wiresCopy(wires);
  686. // this is ugly, but for the moment I can't think of anything better.
  687. // it prevents disconnected deleted traces (traces which have been directly deleted,
  688. // as opposed to traces that are indirectly deleted by deleting or disconnecting parts)
  689. // from being deleted twice on the undo stack
  690. // and therefore added twice, and causing other problems
  691. if (flag == ViewGeometry::TraceFlag || flag == ViewGeometry::JumperFlag) {
  692. if (ends.count() == 0)
  693. {
  694. continue;
  695. }
  696. }
  697. foreach (QList<ConnectorItem *>* list, allPartConnectorItems) {
  698. foreach (ConnectorItem * ci, ends) {
  699. if (!list->contains(ci)) continue;
  700. foreach (ConnectorItem * tci, ci->connectedToItems()) {
  701. if (tci->attachedToItemType() != ModelPart::Wire) continue;
  702. Wire * w = dynamic_cast<Wire *>(tci->attachedTo());
  703. if (!wires.contains(w)) continue; // already been tested and removed so keep going
  704. ViewGeometry::WireFlags wflag = w->wireFlags() & (ViewGeometry::RatsnestFlag | ViewGeometry::TraceFlag | ViewGeometry::JumperFlag);
  705. if (wflag != flag) continue;
  706. // assumes one end is connected to a part, checks to see if the other end is also, possibly indirectly, connected
  707. bothEndsConnected(w, flag, tci, wires, *list);
  708. }
  709. }
  710. if (wires.count() == 0) break;
  711. }
  712. foreach (Wire * w, wires) {
  713. deleteWires.insert(w);
  714. }
  715. }
  716. foreach (Wire * wire, deleteWires) {
  717. command->addWire(this, wire);
  718. deleteItem(wire, true, false, false);
  719. }
  720. }
  721. bool PCBSketchWidget::bothEndsConnected(Wire * wire, ViewGeometry::WireFlags flag, ConnectorItem * oneEnd, QList<Wire *> & wires, QList<ConnectorItem *> & partConnectorItems)
  722. {
  723. QList<Wire *> visited;
  724. return bothEndsConnectedAux(wire, flag, oneEnd, wires, partConnectorItems, visited);
  725. }
  726. bool PCBSketchWidget::bothEndsConnectedAux(Wire * wire, ViewGeometry::WireFlags flag, ConnectorItem * oneEnd, QList<Wire *> & wires, QList<ConnectorItem *> & partConnectorItems, QList<Wire *> & visited)
  727. {
  728. if (visited.contains(wire)) return false;
  729. visited.append(wire);
  730. bool result = false;
  731. ConnectorItem * otherEnd = wire->otherConnector(oneEnd);
  732. foreach (ConnectorItem * toConnectorItem, otherEnd->connectedToItems()) {
  733. if (partConnectorItems.contains(toConnectorItem)) {
  734. result = true;
  735. continue;
  736. }
  737. if (toConnectorItem->attachedToItemType() != ModelPart::Wire) continue;
  738. Wire * w = dynamic_cast<Wire *>(toConnectorItem->attachedTo());
  739. ViewGeometry::WireFlags wflag = w->wireFlags() & (ViewGeometry::RatsnestFlag | ViewGeometry::TraceFlag | ViewGeometry::JumperFlag);
  740. if (wflag != flag) continue;
  741. result = bothEndsConnectedAux(w, flag, toConnectorItem, wires, partConnectorItems, visited) || result; // let it recurse
  742. }
  743. if (result) {
  744. wires.removeOne(wire);
  745. }
  746. return result;
  747. }
  748. bool PCBSketchWidget::reviewDeletedConnections(QSet<ItemBase *> & deletedItems, QHash<ItemBase *, ConnectorPairHash *> & deletedConnections, QUndoCommand * parentCommand)
  749. {
  750. Q_UNUSED(parentCommand);
  751. Q_UNUSED(deletedItems);
  752. foreach (ConnectorPairHash * connectorHash, deletedConnections.values())
  753. {
  754. QList <ConnectorItem *> removeKeys;
  755. foreach (ConnectorItem * fromConnectorItem, connectorHash->uniqueKeys()) {
  756. if (fromConnectorItem->attachedTo()->getVirtual()) {
  757. removeKeys.append(fromConnectorItem);
  758. continue;
  759. }
  760. QList<ConnectorItem *> removeValues;
  761. foreach (ConnectorItem * toConnectorItem, connectorHash->values(fromConnectorItem)) {
  762. if (toConnectorItem->attachedTo()->getVirtual()) {
  763. removeValues.append(toConnectorItem);
  764. }
  765. }
  766. foreach (ConnectorItem * toConnectorItem, removeValues) {
  767. connectorHash->remove(fromConnectorItem, toConnectorItem);
  768. }
  769. }
  770. foreach (ConnectorItem * fromConnectorItem, removeKeys) {
  771. connectorHash->remove(fromConnectorItem);
  772. }
  773. }
  774. return false;
  775. }
  776. bool PCBSketchWidget::canCreateWire(Wire * dragWire, ConnectorItem * from, ConnectorItem * to)
  777. {
  778. Q_UNUSED(dragWire);
  779. return ((from != NULL) && (to != NULL));
  780. }
  781. Wire * PCBSketchWidget::makeOneRatsnestWire(ConnectorItem * source, ConnectorItem * dest, RatsnestCommand * ratsnestCommand, bool select) {
  782. if (source->attachedTo() == dest->attachedTo()) {
  783. if (source == dest) return NULL;
  784. if (source->bus() == dest->bus() && dest->bus() != NULL) {
  785. return NULL; // don't draw a wire within the same part on the same bus
  786. }
  787. }
  788. long newID = ItemBase::getNextID();
  789. ViewGeometry viewGeometry;
  790. makeRatsnestViewGeometry(viewGeometry, source, dest);
  791. /*
  792. DebugDialog::debug(QString("creating ratsnest %10: %1, from %6 %7, to %8 %9, frompos: %2 %3, topos: %4 %5")
  793. .arg(newID)
  794. .arg(fromPos.x()).arg(fromPos.y())
  795. .arg(toPos.x()).arg(toPos.y())
  796. .arg(source->attachedToTitle()).arg(source->connectorSharedID())
  797. .arg(dest->attachedToTitle()).arg(dest->connectorSharedID())
  798. .arg(m_viewIdentifier)
  799. );
  800. */
  801. ItemBase * newItemBase = addItem(m_paletteModel->retrieveModelPart(ModuleIDNames::wireModuleIDName), BaseCommand::SingleView, viewGeometry, newID, -1, -1, NULL, NULL);
  802. Wire * wire = dynamic_cast<Wire *>(newItemBase);
  803. tempConnectWire(wire, source, dest);
  804. if (!select) {
  805. wire->setSelected(false);
  806. }
  807. Wire * tempWire = source->wiredTo(dest, ViewGeometry::TraceFlag);
  808. if (tempWire) {
  809. wire->setOpacity(getRatsnestOpacity(true));
  810. }
  811. if (ratsnestCommand) {
  812. ratsnestCommand->addWire(this, wire, source, dest, select);
  813. }
  814. return wire ;
  815. }
  816. void PCBSketchWidget::makeRatsnestViewGeometry(ViewGeometry & viewGeometry, ConnectorItem * source, ConnectorItem * dest)
  817. {
  818. QPointF fromPos = source->sceneAdjustedTerminalPoint(NULL);
  819. viewGeometry.setLoc(fromPos);
  820. QPointF toPos = dest->sceneAdjustedTerminalPoint(NULL);
  821. QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y());
  822. viewGeometry.setLine(line);
  823. viewGeometry.setWireFlags(ViewGeometry::RatsnestFlag | ViewGeometry::VirtualFlag);
  824. }
  825. bool PCBSketchWidget::dealWithRatsnestAux(ConnectorItem * & fromConnectorItem, ConnectorItem * & toConnectorItem,
  826. long fromID, const QString & fromConnectorID,
  827. long toID, const QString & toConnectorID,
  828. bool connect, class RatsnestCommand * ratsnestCommand, bool doEmit)
  829. {
  830. SketchWidget::dealWithRatsnest(fromID, fromConnectorID, toID, toConnectorID, connect, ratsnestCommand, doEmit);
  831. ItemBase * from = findItem(fromID);
  832. if (from == NULL) return true;
  833. fromConnectorItem = findConnectorItem(from, fromConnectorID, true);
  834. if (fromConnectorItem == NULL) return true;
  835. ItemBase * to = findItem(toID);
  836. if (to == NULL) return true;
  837. toConnectorItem = findConnectorItem(to, toConnectorID, true);
  838. if (toConnectorItem == NULL) return true;
  839. return alreadyRatsnest(fromConnectorItem, toConnectorItem);
  840. }
  841. bool PCBSketchWidget::doRatsnestOnCopy()
  842. {
  843. return true;
  844. }
  845. qreal PCBSketchWidget::getRatsnestOpacity(Wire * wire) {
  846. return getRatsnestOpacity(wire->getRouted());
  847. }
  848. qreal PCBSketchWidget::getRatsnestOpacity(bool routed) {
  849. return (routed ? 0.2 : 1.0);
  850. }
  851. ConnectorItem * PCBSketchWidget::lookForBreadboardConnection(ConnectorItem * connectorItem)
  852. {
  853. Wire * wire = dynamic_cast<Wire *>(connectorItem->attachedTo());
  854. QList<ConnectorItem *> ends;
  855. if (wire != NULL) {
  856. QList<ConnectorItem *> uniqueEnds;
  857. QList<Wire *> wires;
  858. wire->collectChained(wires, ends, uniqueEnds);
  859. foreach (ConnectorItem * end, ends) {
  860. foreach (ConnectorItem * toConnectorItem, end->connectedToItems()) {
  861. if (toConnectorItem->attachedToItemType() == ModelPart::Breadboard) {
  862. return findEmptyBusConnectorItem(toConnectorItem);
  863. }
  864. }
  865. }
  866. }
  867. ends.clear();
  868. ends.append(connectorItem);
  869. ConnectorItem::collectEqualPotential(ends);
  870. foreach (ConnectorItem * end, ends) {
  871. if (end->attachedToItemType() == ModelPart::Breadboard) {
  872. return findEmptyBusConnectorItem(end);
  873. }
  874. }
  875. return connectorItem;
  876. }
  877. ConnectorItem * PCBSketchWidget::findEmptyBusConnectorItem(ConnectorItem * busConnectorItem) {
  878. Bus * bus = busConnectorItem->bus();
  879. if (bus == NULL) return busConnectorItem;
  880. QList<ConnectorItem *> connectorItems;
  881. busConnectorItem->attachedTo()->busConnectorItems(bus, connectorItems);
  882. foreach (ConnectorItem * connectorItem, connectorItems) {
  883. if (connectorItem == busConnectorItem) continue;
  884. if (connectorItem->connectionsCount() == 0) {
  885. return connectorItem;
  886. }
  887. }
  888. return busConnectorItem;
  889. }
  890. long PCBSketchWidget::makeModifiedWire(ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, BaseCommand::CrossViewType cvt, ViewGeometry::WireFlags wireFlags, QUndoCommand * parentCommand)
  891. {
  892. // create a new real wire
  893. long newID = ItemBase::getNextID();
  894. DebugDialog::debug(QString("new real wire %1").arg(newID));
  895. ViewGeometry viewGeometry;
  896. makeRatsnestViewGeometry(viewGeometry, fromConnectorItem, toConnectorItem);
  897. viewGeometry.setWireFlags(wireFlags);
  898. new AddItemCommand(this, cvt, ModuleIDNames::wireModuleIDName, viewGeometry, newID, true, -1, -1, parentCommand);
  899. new CheckStickyCommand(this, cvt, newID, false, parentCommand);
  900. new ChangeConnectionCommand(this, cvt,
  901. newID, "connector0",
  902. fromConnectorItem->attachedToID(), fromConnectorItem->connectorSharedID(),
  903. true, true, parentCommand);
  904. new ChangeConnectionCommand(this, cvt,
  905. newID, "connector1",
  906. toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
  907. true, true, parentCommand);
  908. if (wireFlags == 0) {
  909. new RatsnestCommand(this, cvt,
  910. newID, "connector0",
  911. fromConnectorItem->attachedToID(), fromConnectorItem->connectorSharedID(),
  912. true, true, parentCommand);
  913. new RatsnestCommand(this, cvt,
  914. newID, "connector1",
  915. toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
  916. true, true, parentCommand);
  917. }
  918. return newID;
  919. }
  920. void PCBSketchWidget::modifyNewWireConnectionsAux(ConnectorItem * fromConnectorItem,
  921. ConnectorItem * toConnectorItem,
  922. QUndoCommand * parentCommand)
  923. {
  924. ConnectorItem * originalFromConnectorItem = fromConnectorItem;
  925. fromConnectorItem = lookForBreadboardConnection(fromConnectorItem);
  926. if (fromConnectorItem->attachedToItemType() == ModelPart::Breadboard) {
  927. makeModifiedWire(fromConnectorItem, toConnectorItem, BaseCommand::CrossView, 0, parentCommand);
  928. return;
  929. }
  930. makeTwoWires(originalFromConnectorItem, fromConnectorItem, toConnectorItem, toConnectorItem, parentCommand);
  931. }
  932. void PCBSketchWidget::makeTwoWires(ConnectorItem * originalFromConnectorItem, ConnectorItem * fromConnectorItem,
  933. ConnectorItem * originalToConnectorItem, ConnectorItem * toConnectorItem,
  934. QUndoCommand * parentCommand)
  935. {
  936. ItemBase * newBreadboard = NULL;
  937. if (!(fromConnectorItem->attachedToItemType() == ModelPart::Breadboard)) {
  938. // find an empty bus on a breadboard
  939. fromConnectorItem = lookForNewBreadboardConnection(fromConnectorItem, newBreadboard);
  940. if (fromConnectorItem == NULL) {
  941. // this shouldn't happen
  942. return;
  943. }
  944. if (newBreadboard) {
  945. new AddItemCommand(this, BaseCommand::CrossView, newBreadboard->modelPart()->moduleID(), newBreadboard->getViewGeometry(), newBreadboard->id(), true, -1, -1, parentCommand);
  946. m_temporaries.append(newBreadboard); // puts it on a list to be deleted
  947. }
  948. }
  949. ConnectorItem * nearestPartConnectorItem = findNearestPartConnectorItem(originalFromConnectorItem);
  950. if (nearestPartConnectorItem == NULL) return;
  951. // make a wire, from the part nearest to fromConnectorItem, to the breadboard
  952. toConnectorItem = nearestPartConnectorItem;
  953. makeModifiedWire(fromConnectorItem, toConnectorItem, BaseCommand::CrossView, 0, parentCommand);
  954. if (originalToConnectorItem->attachedToItemType() == ModelPart::Wire) {
  955. originalToConnectorItem = findNearestPartConnectorItem(originalToConnectorItem);
  956. if (originalToConnectorItem == NULL) return;
  957. }
  958. // draw a wire from that bus on the breadboard to the other part (toConnectorItem)
  959. ConnectorItem * otherPartBusConnectorItem = findEmptyBusConnectorItem(fromConnectorItem);
  960. makeModifiedWire(otherPartBusConnectorItem, originalToConnectorItem, BaseCommand::CrossView, 0, parentCommand);
  961. }
  962. ConnectorItem * PCBSketchWidget::lookForNewBreadboardConnection(ConnectorItem * connectorItem, ItemBase * & newBreadboard) {
  963. Q_UNUSED(connectorItem);
  964. newBreadboard = NULL;
  965. QList<ItemBase *> breadboards;
  966. qreal maxY = 0;
  967. foreach (QGraphicsItem * item, scene()->items()) {
  968. ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
  969. if (itemBase == NULL) continue;
  970. if (itemBase->itemType() == ModelPart::Breadboard) {
  971. breadboards.append(itemBase);
  972. break;
  973. }
  974. qreal y = itemBase->pos().y() + itemBase->size().height();
  975. if (y > maxY) {
  976. maxY = y;
  977. }
  978. }
  979. ConnectorItem * busConnectorItem = NULL;
  980. foreach (ItemBase * breadboard, breadboards) {
  981. busConnectorItem = findEmptyBus(breadboard);
  982. if (busConnectorItem != NULL) return busConnectorItem;
  983. }
  984. ViewGeometry vg;
  985. vg.setLoc(QPointF(0, maxY + 50));
  986. long id = ItemBase::getNextID();
  987. newBreadboard = this->addItem(ModuleIDNames::tinyBreadboardModuleIDName, BaseCommand::SingleView, vg, id, -1, 0, NULL);
  988. busConnectorItem = findEmptyBus(newBreadboard);
  989. return busConnectorItem;
  990. }
  991. ConnectorItem * PCBSketchWidget::findEmptyBus(ItemBase * breadboard) {
  992. foreach (Bus * bus, breadboard->buses()) {
  993. QList<ConnectorItem *> busConnectorItems;
  994. breadboard->busConnectorItems(bus, busConnectorItems);
  995. bool allEmpty = true;
  996. foreach (ConnectorItem * busConnectorItem, busConnectorItems) {
  997. if (busConnectorItem->connectionsCount() > 0) {
  998. allEmpty = false;
  999. break;
  1000. }
  1001. }
  1002. if (allEmpty && busConnectorItems.count() > 0) {
  1003. return busConnectorItems[0];
  1004. }
  1005. }
  1006. return NULL;
  1007. }
  1008. ConnectorItem * PCBSketchWidget::findNearestPartConnectorItem(ConnectorItem * fromConnectorItem) {
  1009. // find the nearest part to fromConnectorItem
  1010. Wire * wire = dynamic_cast<Wire *>(fromConnectorItem->attachedTo());
  1011. if (wire == NULL) return NULL;
  1012. QList<ConnectorItem *> ends;
  1013. calcDistances(wire, ends);
  1014. clearDistances();
  1015. if (ends.count() < 1) return NULL;
  1016. return ends[0];
  1017. }
  1018. void PCBSketchWidget::calcDistances(Wire * wire, QList<ConnectorItem *> & ends) {
  1019. QList<Wire *> chained;
  1020. QList<ConnectorItem *> uniqueEnds;
  1021. wire->collectChained(chained, ends, uniqueEnds);
  1022. if (ends.count() < 2) return;
  1023. clearDistances();
  1024. foreach (ConnectorItem * end, ends) {
  1025. bool fromConnector0;
  1026. QList<Wire *> distanceWires;
  1027. int distance = calcDistance(wire, end, 0, distanceWires, fromConnector0);
  1028. DistanceThing * dt = new DistanceThing;
  1029. dt->distance = distance;
  1030. dt->fromConnector0 = fromConnector0;
  1031. DebugDialog::debug(QString("distance %1 %2 %3, %4 %5")
  1032. .arg(end->attachedToID()).arg(end->attachedToTitle()).arg(end->connectorSharedID())
  1033. .arg(distance).arg(fromConnector0 ? "connector0" : "connector1"));
  1034. distances.insert(end, dt);
  1035. }
  1036. qSort(ends.begin(), ends.end(), distanceLessThan);
  1037. }
  1038. void PCBSketchWidget::clearDistances() {
  1039. foreach (ConnectorItem * c, distances.keys()) {
  1040. DistanceThing * dt = distances.value(c, NULL);
  1041. if (dt) delete dt;
  1042. }
  1043. distances.clear();
  1044. }
  1045. int PCBSketchWidget::calcDistanceAux(ConnectorItem * from, ConnectorItem * to, int distance, QList<Wire *> & distanceWires) {
  1046. //DebugDialog::debug(QString("calc distance aux: %1 %2, %3 %4, %5").arg(from->attachedToID()).arg(from->connectorSharedID())
  1047. //.arg(to->attachedToTitle()).arg(to->connectorSharedID()).arg(distance));
  1048. foreach (ConnectorItem * toConnectorItem, from->connectedToItems()) {
  1049. if (toConnectorItem == to) {
  1050. return distance;
  1051. }
  1052. }
  1053. int result = MAX_INT;
  1054. foreach (ConnectorItem * toConnectorItem, from->connectedToItems()) {
  1055. if (toConnectorItem->attachedToItemType() != ModelPart::Wire) continue;
  1056. Wire * w = dynamic_cast<Wire *>(toConnectorItem->attachedTo());
  1057. if (distanceWires.contains(w)) continue;
  1058. bool fromConnector0;
  1059. int temp = calcDistance(w, to, distance + 1, distanceWires, fromConnector0);
  1060. if (temp < result) {
  1061. result = temp;
  1062. }
  1063. }
  1064. return result;
  1065. }
  1066. int PCBSketchWidget::calcDistance(Wire * wire, ConnectorItem * end, int distance, QList<Wire *> & distanceWires, bool & fromConnector0) {
  1067. //DebugDialog::debug(QString("calc distance wire: %1 rat:%2 to %3 %4, %5").arg(wire->id()).arg(wire->getRatsnest())
  1068. //.arg(end->attachedToTitle()).arg(end->connectorSharedID()).arg(distance));
  1069. distanceWires.append(wire);
  1070. int d0 = calcDistanceAux(wire->connector0(), end, distance, distanceWires);
  1071. if (d0 == distance) {
  1072. fromConnector0 = true;
  1073. return d0;
  1074. }
  1075. int d1 = calcDistanceAux(wire->connector1(), end, distance, distanceWires);
  1076. if (d0 <= d1) {
  1077. fromConnector0 = true;
  1078. return d0;
  1079. }
  1080. fromConnector0 = false;
  1081. return d1;
  1082. }
  1083. void PCBSketchWidget::setJumperFlags(ViewGeometry & vg) {
  1084. vg.setJumper(true);
  1085. }
  1086. bool PCBSketchWidget::usesJumperItem() {
  1087. return true;
  1088. }
  1089. void PCBSketchWidget::showGroundTraces(bool show) {
  1090. foreach (QGraphicsItem * item, scene()->items()) {
  1091. TraceWire * trace = dynamic_cast<TraceWire *>(item);
  1092. if (trace == NULL) continue;
  1093. if (trace->isGrounded()) {
  1094. trace->setVisible(show);
  1095. }
  1096. }
  1097. }
  1098. void PCBSketchWidget::getLabelFont(QFont & font, QColor & color) {
  1099. font.setFamily("OCRA"); // ocra10
  1100. font.setPointSize(getLabelFontSizeMedium());
  1101. color.setAlpha(255);
  1102. color.setRgb(0xffffff);
  1103. }
  1104. void PCBSketchWidget::makeWiresChangeConnectionCommands(const QList<Wire *> & wires, QUndoCommand * parentCommand)
  1105. {
  1106. QStringList alreadyList;
  1107. foreach (Wire * wire, wires) {
  1108. QList<ConnectorItem *> wireConnectorItems;
  1109. wireConnectorItems << wire->connector0() << wire->connector1();
  1110. foreach (ConnectorItem * fromConnectorItem, wireConnectorItems) {
  1111. foreach(ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
  1112. QString already = ((fromConnectorItem->attachedToID() <= toConnectorItem->attachedToID()) ? QString("%1.%2.%3.%4") : QString("%3.%4.%1.%2"))
  1113. .arg(fromConnectorItem->attachedToID()).arg(fromConnectorItem->connectorSharedID())
  1114. .arg(toConnectorItem->attachedToID()).arg(toConnectorItem->connectorSharedID());
  1115. if (alreadyList.contains(already)) continue;
  1116. alreadyList.append(already);
  1117. new ChangeConnectionCommand(this, BaseCommand::SingleView,
  1118. fromConnectorItem->attachedToID(), fromConnectorItem->connectorSharedID(),
  1119. toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
  1120. false, true, parentCommand);
  1121. }
  1122. }
  1123. }
  1124. }
  1125. qreal PCBSketchWidget::getLabelFontSizeSmall() {
  1126. return 5;
  1127. }
  1128. qreal PCBSketchWidget::getLabelFontSizeMedium() {
  1129. return 7;
  1130. }
  1131. qreal PCBSketchWidget::getLabelFontSizeLarge() {
  1132. return 12;
  1133. }
  1134. void PCBSketchWidget::resizeBoard(qreal mmW, qreal mmH, bool doEmit)
  1135. {
  1136. Q_UNUSED(doEmit);
  1137. PaletteItem * item = getSelectedPart();
  1138. if (item == NULL) return;
  1139. switch (item->itemType()) {
  1140. case ModelPart::ResizableBoard:
  1141. case ModelPart::Logo:
  1142. break;
  1143. default:
  1144. return SketchWidget::resizeBoard(mmW, mmH, doEmit);
  1145. }
  1146. qreal origw = item->modelPart()->prop("width").toDouble();
  1147. qreal origh = item->modelPart()->prop("height").toDouble();
  1148. if (mmH == 0 || mmW == 0) {
  1149. dynamic_cast<ResizableBoard *>(item)->setInitialSize();
  1150. qreal w = item->modelPart()->prop("width").toDouble();
  1151. qreal h = item->modelPart()->prop("height").toDouble();
  1152. if (origw == w && origh == h) {
  1153. // no change
  1154. return;
  1155. }
  1156. viewItemInfo(item);
  1157. mmW = w;
  1158. mmH = h;
  1159. }
  1160. QUndoCommand * parentCommand = new QUndoCommand(tr("Resize board to %1 %2").arg(mmW).arg(mmH));
  1161. new ResizeBoardCommand(this, item->id(), origw, origh, mmW, mmH, parentCommand);
  1162. new CheckStickyCommand(this, BaseCommand::SingleView, item->id(), true, parentCommand);
  1163. m_undoStack->push(parentCommand);
  1164. }
  1165. void PCBSketchWidget::showLabelFirstTime(long itemID, bool show, bool doEmit) {
  1166. SketchWidget::showLabelFirstTime(itemID, show, doEmit);
  1167. ItemBase * itemBase = findItem(itemID);
  1168. if (itemBase == NULL) return;
  1169. switch (itemBase->itemType()) {
  1170. case ModelPart::Part:
  1171. itemBase->showPartLabel(true, m_viewLayers.value(getLabelViewLayerID()));
  1172. break;
  1173. default:
  1174. break;
  1175. }
  1176. }
  1177. ItemBase * PCBSketchWidget::findBoard() {
  1178. foreach (QGraphicsItem * childItem, items()) {
  1179. ItemBase * board = dynamic_cast<ItemBase *>(childItem);
  1180. if (board == NULL) continue;
  1181. //for now take the first board you find
  1182. if (board->itemType() == ModelPart::ResizableBoard || board->itemType() == ModelPart::Board) {
  1183. return board;
  1184. }
  1185. }
  1186. return NULL;
  1187. }
  1188. void PCBSketchWidget::collectConnectorNames(QList<ConnectorItem *> & connectorItems, QStringList & connectorNames)
  1189. {
  1190. foreach(ConnectorItem * connectorItem, connectorItems) {
  1191. if (!connectorNames.contains(connectorItem->connectorSharedName())) {
  1192. connectorNames.append(connectorItem->connectorSharedName());
  1193. //DebugDialog::debug("name " + connectorItem->connectorSharedName());
  1194. }
  1195. }
  1196. }
  1197. void PCBSketchWidget::updateRatsnestColors(BaseCommand * command, QUndoCommand * parentCommand, bool forceUpdate, RoutingStatus & routingStatus)
  1198. {
  1199. //DebugDialog::debug("update ratsnest colors");
  1200. // TODO: think about ways to optimize this...
  1201. QList<ConnectorItem *> virtualWireConnectors;
  1202. foreach (QGraphicsItem * item, items()) {
  1203. VirtualWire * vw = dynamic_cast<VirtualWire *>(item);
  1204. if (vw == NULL) continue;
  1205. virtualWireConnectors.append(vw->connector0());
  1206. virtualWireConnectors.append(vw->connector1());
  1207. }
  1208. while (virtualWireConnectors.count() > 0) {
  1209. QList<ConnectorItem *> connectorItems;
  1210. connectorItems.append(virtualWireConnectors.takeFirst());
  1211. ConnectorItem::collectEqualPotential(connectorItems, ViewGeometry::NormalFlag | ViewGeometry::TraceFlag | ViewGeometry::JumperFlag);
  1212. for (int i = 1; i < connectorItems.count(); i++) {
  1213. virtualWireConnectors.removeOne(connectorItems[i]);
  1214. }
  1215. scoreOneNet(connectorItems, routingStatus);
  1216. recolor(connectorItems, command, parentCommand, forceUpdate);
  1217. }
  1218. routingStatus.m_jumperWireCount /= 2; // since we counted each connector
  1219. routingStatus.m_jumperItemCount /= 2; // since we counted each connector
  1220. }
  1221. void traceAdjacency(QVector< QVector<bool> > & adjacency, int count)
  1222. {
  1223. QString string = "\n";
  1224. for (int i = 0; i < count; i++) {
  1225. for (int j = 0; j < count; j++) {
  1226. string += adjacency[i][j] ? "1" : "0";
  1227. }
  1228. string += "\n";
  1229. }
  1230. DebugDialog::debug(string);
  1231. }
  1232. void PCBSketchWidget::scoreOneNet(QList<ConnectorItem *> & connectorItems, RoutingStatus & routingStatus)
  1233. {
  1234. routingStatus.m_netCount++;
  1235. QList<ConnectorItem *> partConnectorItems;
  1236. ConnectorItem::collectParts(connectorItems, partConnectorItems, includeSymbols());
  1237. int count = partConnectorItems.count();
  1238. // want adjacency[count][count] but some C++ compilers don't like it
  1239. QVector< QVector<bool> > adjacency(count);
  1240. for (int i = 0; i < count; i++) {
  1241. QVector<bool> row(count, false);
  1242. adjacency[i] = row;
  1243. }
  1244. // initialize adjaceny
  1245. for (int i = 0; i < count; i++) {
  1246. adjacency[i][i] = true;
  1247. ConnectorItem * from = partConnectorItems[i];
  1248. for (int j = i + 1; j < count; j++) {
  1249. if (j == i) continue;
  1250. ConnectorItem * to = partConnectorItems[j];
  1251. if (to->attachedTo() != from->attachedTo()) continue;
  1252. if (to->bus() == NULL) continue;
  1253. if (to->bus() != from->bus()) continue;
  1254. adjacency[i][j] = true;
  1255. adjacency[j][i] = true;
  1256. }
  1257. }
  1258. //traceAdjacency(adjacency, count);
  1259. for (int i = 0; i < count; i++) {
  1260. ConnectorItem * fromConnectorItem = partConnectorItems[i];
  1261. if (fromConnectorItem->attachedToItemType() == ModelPart::Jumper) {
  1262. routingStatus.m_jumperItemCount++;
  1263. }
  1264. foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
  1265. if (toConnectorItem->attachedToItemType() != ModelPart::Wire) {
  1266. continue;
  1267. }
  1268. Wire * wire = dynamic_cast<Wire *>(toConnectorItem->attachedTo());
  1269. if (wire == NULL) continue;
  1270. if (!(wire->getJumper() || wire->getTrace())) continue;
  1271. if (wire->getJumper()) {
  1272. routingStatus.m_jumperWireCount++;
  1273. }
  1274. QList<Wire *> wires;
  1275. QList<ConnectorItem *> ends;
  1276. QList<ConnectorItem *> uniqueEnds;
  1277. wire->collectChained(wires, ends, uniqueEnds);
  1278. foreach (ConnectorItem * end, ends) {
  1279. if (end == fromConnectorItem) continue;
  1280. int j = partConnectorItems.indexOf(end);
  1281. if (j >= 0) {
  1282. adjacency[i][j] = true;
  1283. adjacency

Large files files are truncated, but you can click here to view the full file