PageRenderTime 9ms CodeModel.GetById 4ms app.highlight 118ms RepoModel.GetById 2ms 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

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

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

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