1 /*******************************************************************
2 
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2015 Fachhochschule Potsdam - http://fh-potsdam.de
5 
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with Fritzing.  If not, see <http://www.gnu.org/licenses/>.
18 
19 ********************************************************************
20 
21 $Revision: 6980 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $
24 
25 ********************************************************************/
26 
27 /* TO DO ******************************************
28 
29 
30     ///////////////////////////////// first release ///////////////////////////////
31 
32     clicking set bus mode fucks up anchor points
33 
34     terminal point area is not highlighting properly
35 
36     ////////////////////// second release /////////////////////////////////
37 
38 		icon editor?
39 
40         need simplified infoview
41 
42         change color of breadboard text item
43 
44         for svg import check for flaws:
45             multiple connector or terminal ids
46 			trash any other matching id
47 
48 		loading tht into smd or v.v.
49 			can't seem to assign connectors after adding connectors
50 
51 		why isn't swapping available when a family has new parts with multiple variant values?
52 
53 		test button with export etchable to make sure the part is right?
54 			export etchable is disabled without a board
55 
56 		align to grid when moving?
57 
58 		restore editable pin labels functionality
59 			requires storing labels in the part rather than in the sketch
60 
61 		hidden connectors
62 			allow multiple hits to the same place and add copies of the svg element if necessary
63 
64 		restore parts bin
65 
66         buses
67 			secondary representation (list view)
68 				display during bus mode instead of connector list
69 				allow to delete bus, delete nodemember, add nodemember using right-click for now
70 
71 		use the actual svg shape instead of rectangles
72 			construct a new svg with the width and height of the bounding box of the element
73 			translate the element by the current top-left of the element
74 			set the fill color and kill the stroke width
75 
76 		properties and tags entries allow duplicates
77 
78 		delete temp files after crash
79 
80         show in OS button only shows folder and not file in linux
81 
82         smd vs. tht
83             after it's all over remove copper0 if part is all smd
84             split copper0 and copper1
85             allow mixed tht/smd parts in fritzing but for now disable flipsmd
86             allow smd with holes to flip?
87 
88         import
89             kicad sch files?
90 				kicad schematic does not match our schematic
91 
92         on svg import detect all connector IDs
93             if any are invisible, tell user this is obsolete
94 
95         sort connector list alphabetically or numerically?
96 
97         full svg outline view
98 
99         bendable legs
100 			same as terminalID, but must be a line
101 			use a radio buttons to distinguish?
102 
103         set flippable
104 
105         connector duplicate op
106 
107         add layers:  put everything in silkscreen, then give copper1, copper0 checkbox
108             what about breadboardbreadboard or other odd layers?
109             if you click something as a connector, automatically move it into copper
110                 how to distinguish between both and top--default to both, let user set "pad"
111 			setting layer for top level group sets all children?
112 
113         swap connector metadata op
114 
115         delete op
116 
117         move connectors with arrow keys, or typed coordinates
118 	    drag and drop later
119 
120         import
121             eagle lbr
122             eagle brd
123 
124         for schematic view
125             offer lines-or-pins, rects, and a selection of standard schematic icons in the parts bin
126 
127         for breadboard view
128             import
129             generate ICs, dips, sips, breakouts
130 
131         for pcb view
132             pads, pins (circle, rect, oblong), holes
133             lines and curves?
134             import silkscreen
135 
136         hybrids
137 
138         flip and rotate images w/in the view?
139 
140         taxonomy entry like tag entry?
141 
142         new schematic layout specs
143 
144         give users a family popup with all family names
145             ditto for other properties
146 
147         matrix problem with move and duplicate (i.e. if element inherits a matrix from far above)
148             even a problem when inserting hole, pad, or pin
149             eliminate internal transforms, then insert inverted matrix, then use untransformed coords based on viewbox
150 
151 
152 ***************************************************/
153 
154 #include "pemainwindow.h"
155 #include "pemetadataview.h"
156 #include "peconnectorsview.h"
157 #include "pecommands.h"
158 #include "petoolview.h"
159 #include "pesvgview.h"
160 #include "pegraphicsitem.h"
161 #include "kicadmoduledialog.h"
162 #include "../debugdialog.h"
163 #include "../sketch/breadboardsketchwidget.h"
164 #include "../sketch/schematicsketchwidget.h"
165 #include "../sketch/pcbsketchwidget.h"
166 #include "../mainwindow/sketchareawidget.h"
167 #include "../referencemodel/referencemodel.h"
168 #include "../utils/graphicsutils.h"
169 #include "../utils/textutils.h"
170 #include "../utils/folderutils.h"
171 #include "../utils/s2s.h"
172 #include "../mainwindow/fdockwidget.h"
173 #include "../fsvgrenderer.h"
174 #include "../partsbinpalette/binmanager/binmanager.h"
175 #include "../svg/gedaelement2svg.h"
176 #include "../svg/kicadmodule2svg.h"
177 #include "../svg/kicadschematic2svg.h"
178 #include "../sketchtoolbutton.h"
179 #include "../items/virtualwire.h"
180 #include "../connectors/connectoritem.h"
181 #include "../connectors/bus.h"
182 #include "../installedfonts.h"
183 #include "../dock/layerpalette.h"
184 #include "../utils/cursormaster.h"
185 #include "../infoview/htmlinfoview.h"
186 
187 #include <QtDebug>
188 #include <QInputDialog>
189 #include <QApplication>
190 #include <QSvgGenerator>
191 #include <QMenuBar>
192 #include <QMessageBox>
193 #include <QDesktopServices>
194 #include <QUrl>
195 #include <QBuffer>
196 #include <QClipboard>
197 #include <limits>
198 
199 ////////////////////////////////////////////////////
200 
201 static bool RubberBandLegWarning = false;
202 
203 static bool GotZeroConnector = false;
204 
205 static const QString ReferenceFileString("referenceFile");
206 
207 static const int IconViewIndex = 3;
208 static const int MetadataViewIndex = 4;
209 static const int ConnectorsViewIndex = 5;
210 
211 const static int PegiZ = 5000;
212 const static int RatZ = 6000;
213 
214 static long FakeGornSiblingNumber = 0;
215 
removeGornAux(QDomElement & element)216 void removeGornAux(QDomElement & element) {
217     bool hasGorn = element.hasAttribute("gorn");
218     bool hasOld = element.hasAttribute("oldid");
219     if (hasGorn && hasOld) {
220         element.removeAttribute("gorn");
221         QString oldid = element.attribute("oldid");
222         if (oldid.isEmpty()) element.removeAttribute("id");
223         else element.setAttribute("id", oldid);
224         element.removeAttribute("oldid");
225     }
226     QDomElement child = element.firstChildElement();
227     while (!child.isNull()) {
228         removeGornAux(child);
229         child = child.nextSiblingElement();
230     }
231 }
232 
removeGorn(QString & svg)233 QString removeGorn(QString & svg) {
234     QDomDocument doc;
235 
236     QString errorStr;
237     int errorLine;
238     int errorColumn;
239     if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) {
240         // shouldn't happen
241         DebugDialog::debug(QString("remove gorn failure: %1 %2 %3 %4").arg(errorStr).arg(errorLine).arg(errorColumn).arg(svg));
242         return svg;
243     }
244 
245     QDomElement root = doc.documentElement();
246     removeGornAux(root);
247 
248 	return doc.toString(4);
249 }
250 
byID(QDomElement & c1,QDomElement & c2)251 bool byID(QDomElement & c1, QDomElement & c2)
252 {
253     int c1id = -1;
254     int c2id = -1;
255 	int ix = IntegerFinder.indexIn(c1.attribute("id"));
256     if (ix >= 0) c1id = IntegerFinder.cap(0).toInt();
257     ix = IntegerFinder.indexIn(c2.attribute("id"));
258     if (ix >= 0) c2id = IntegerFinder.cap(0).toInt();
259 
260     if (c1id == 0 || c2id == 0) GotZeroConnector = true;
261 
262 	return c1id <= c2id;
263 }
264 
removeID(QDomElement & root,const QString & value)265 void removeID(QDomElement & root, const QString & value) {
266     if (root.attribute("id") == value) {
267         root.removeAttribute("id");
268     }
269 
270 	QDomNodeList nodeList = root.childNodes();
271 	for (int i = 0; i < nodeList.count(); i++) {
272         QDomNode node = nodeList.item(i);
273         if (node.nodeType() == QDomNode::ElementNode) {
274             QDomElement element = node.toElement();
275 		    removeID(element, value);
276         }
277 	}
278 }
279 
280 ////////////////////////////////////////////////////
281 
IconSketchWidget(ViewLayer::ViewID viewID,QWidget * parent)282 IconSketchWidget::IconSketchWidget(ViewLayer::ViewID viewID, QWidget *parent)
283     : SketchWidget(viewID, parent)
284 {
285 	m_shortName = QObject::tr("ii");
286 	m_viewName = QObject::tr("Icon View");
287 	initBackgroundColor();
288 }
289 
addViewLayers()290 void IconSketchWidget::addViewLayers() {
291 	setViewLayerIDs(ViewLayer::Icon, ViewLayer::Icon, ViewLayer::Icon, ViewLayer::Icon, ViewLayer::Icon);
292 	addViewLayersAux(ViewLayer::layersForView(ViewLayer::IconView), ViewLayer::layersForViewFromBelow(ViewLayer::IconView));
293 }
294 
295 ////////////////////////////////////////////////////
296 
ViewThing()297 ViewThing::ViewThing() {
298     itemBase = NULL;
299     document = NULL;
300     svgChangeCount = 0;
301     everZoomed = false;
302     sketchWidget = NULL;
303 	firstTime = true;
304     busMode = false;
305 }
306 
307 /////////////////////////////////////////////////////
308 
PEMainWindow(ReferenceModel * referenceModel,QWidget * parent)309 PEMainWindow::PEMainWindow(ReferenceModel * referenceModel, QWidget * parent)
310 	: MainWindow(referenceModel, parent)
311 {
312     m_viewThings.insert(ViewLayer::BreadboardView, new ViewThing);
313 	m_viewThings.insert(ViewLayer::SchematicView, new ViewThing);
314 	m_viewThings.insert(ViewLayer::PCBView, new ViewThing);
315 	m_viewThings.insert(ViewLayer::IconView, new ViewThing);
316 
317 	m_useNextPick = m_inPickMode = false;
318 	m_autosaveTimer.stop();
319     disconnect(&m_autosaveTimer, SIGNAL(timeout()), this, SLOT(backupSketch()));
320     m_gaveSaveWarning = m_canSave = false;
321     m_settingsPrefix = "pe/";
322     m_guid = TextUtils::getRandText();
323     m_prefix = "prefix0000";
324     m_fileIndex = 0;
325 	m_userPartsFolderPath = FolderUtils::getUserDataStorePath("parts")+"/user/";
326 	m_userPartsFolderSvgPath = FolderUtils::getUserDataStorePath("parts")+"/svg/user/";
327     m_peToolView = NULL;
328     m_peSvgView = NULL;
329     m_connectorsView = NULL;
330 }
331 
~PEMainWindow()332 PEMainWindow::~PEMainWindow()
333 {
334     // PEGraphicsItems are still holding QDomElement so delete them before m_fzpDocument is deleted
335     killPegi();
336 
337 	// kill temp files
338 	foreach (QString string, m_filesToDelete) {
339 		QFile::remove(string);
340 	}
341 	QDir dir = QDir::temp();
342     dir.rmdir(makeDirName());
343 }
344 
closeEvent(QCloseEvent * event)345 void PEMainWindow::closeEvent(QCloseEvent *event)
346 {
347 	qDebug() << "close event";
348 
349 	QStringList messages;
350 
351 	if (m_inFocusWidgets.count() > 0) {
352 		bool gotOne = false;
353 		// should only be one in-focus widget
354 		foreach (QWidget * widget, m_inFocusWidgets) {
355 			QLineEdit * lineEdit = qobject_cast<QLineEdit *>(widget);
356 			if (lineEdit) {
357 				if (lineEdit->isModified()) {
358 					lineEdit->clearFocus();
359                     lineEdit->setModified(false);
360 					gotOne = true;
361 				}
362 			}
363 			else {
364 				QTextEdit * textEdit = qobject_cast<QTextEdit *>(widget);
365 				if (textEdit) {
366 					if (textEdit->document()->isModified()) {
367 						textEdit->clearFocus();
368                         textEdit->document()->setModified(false);
369 						gotOne = true;
370 					}
371 				}
372 			}
373 		}
374 		if (gotOne) {
375 			messages << tr("There is one last edit still pending.");
376 		}
377 	}
378 
379 	QString family = m_metadataView->family();
380 	if (family.isEmpty()) {
381 		messages << tr("The 'family' property can not be blank.");
382 	}
383 
384 	QStringList keys = m_metadataView->properties().keys();
385 
386 	if (keys.contains("family", Qt::CaseInsensitive)) {
387 		messages << tr("A duplicate 'family' property is not allowed");
388 	}
389 
390 	if (keys.contains("variant", Qt::CaseInsensitive)) {
391 		messages << tr("A duplicate 'variant' property is not allowed");
392 	}
393 
394 	bool discard = true;
395 	if (messages.count() > 0) {
396 	    QMessageBox messageBox(this);
397 	    messageBox.setWindowTitle(tr("Close without saving?"));
398 
399 		QString message = tr("This part can not be saved as-is:\n\n");
400 		foreach (QString string, messages) {
401 			message.append('\t');
402 			message.append(string);
403 			messages.append("\n\n");
404 		}
405 
406         message += tr("Do you want to keep working or close without saving?");
407 
408 	    messageBox.setText(message);
409 	    messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
410 	    messageBox.setDefaultButton(QMessageBox::Cancel);
411 	    messageBox.setIcon(QMessageBox::Warning);
412 	    messageBox.setWindowModality(Qt::WindowModal);
413 	    messageBox.setButtonText(QMessageBox::Ok, tr("Close without saving"));
414 	    messageBox.setButtonText(QMessageBox::Cancel, tr("Keep working"));
415 	    QMessageBox::StandardButton answer = (QMessageBox::StandardButton) messageBox.exec();
416 
417 	    if (answer != QMessageBox::Ok) {
418 			event->ignore();
419 		    return;
420 	    }
421 	}
422 	else if (!beforeClosing(true, discard)) {
423         event->ignore();
424         return;
425     }
426 
427 	if (!discard) {
428 		// if all connectors not found, put up a message
429 		connectorWarning();
430 	}
431 
432 	QSettings settings;
433 	settings.setValue(m_settingsPrefix + "state", saveState());
434 	settings.setValue(m_settingsPrefix + "geometry", saveGeometry());
435 
436 	QMainWindow::closeEvent(event);
437 }
438 
initLockedFiles(bool)439 void PEMainWindow::initLockedFiles(bool) {
440 }
441 
initSketchWidgets(bool whatever)442 void PEMainWindow::initSketchWidgets(bool whatever)
443 {
444     Q_UNUSED(whatever);
445 
446     MainWindow::initSketchWidgets(false);
447 
448 	m_iconGraphicsView = new IconSketchWidget(ViewLayer::IconView, this);
449 	initSketchWidget(m_iconGraphicsView);
450 	m_iconWidget = new SketchAreaWidget(m_iconGraphicsView,this);
451 	addTab(m_iconWidget, tr("Icon"));
452     initSketchWidget(m_iconGraphicsView);
453 
454 	ViewThing * viewThing = m_viewThings.value(m_breadboardGraphicsView->viewID());
455 	viewThing->sketchWidget = m_breadboardGraphicsView;
456 	viewThing->document = &m_breadboardDocument;
457 
458 	viewThing = m_viewThings.value(m_schematicGraphicsView->viewID());
459 	viewThing->sketchWidget = m_schematicGraphicsView;
460 	viewThing->document = &m_schematicDocument;
461 
462 	viewThing = m_viewThings.value(m_pcbGraphicsView->viewID());
463 	viewThing->sketchWidget = m_pcbGraphicsView;
464 	viewThing->document = &m_pcbDocument;
465 
466 	viewThing = m_viewThings.value(m_iconGraphicsView->viewID());
467 	viewThing->sketchWidget = m_iconGraphicsView;
468 	viewThing->document = &m_iconDocument;
469 
470     foreach (ViewThing * viewThing, m_viewThings.values()) {
471         viewThing->sketchWidget->setAcceptWheelEvents(true);
472 		viewThing->sketchWidget->setChainDrag(false);				// no bendpoints
473         viewThing->firstTime = true;
474         viewThing->everZoomed = true;
475 		connect(viewThing->sketchWidget, SIGNAL(newWireSignal(Wire *)), this, SLOT(newWireSlot(Wire *)));
476 		connect(viewThing->sketchWidget, SIGNAL(showing(SketchWidget *)), this, SLOT(showing(SketchWidget *)));
477 		connect(viewThing->sketchWidget, SIGNAL(itemMovedSignal(ItemBase *)), this, SLOT(itemMovedSlot(ItemBase *)));
478 		connect(viewThing->sketchWidget, SIGNAL(resizedSignal(ItemBase *)), this, SLOT(resizedSlot(ItemBase *)));
479 		connect(viewThing->sketchWidget, SIGNAL(clickedItemCandidateSignal(QGraphicsItem *, bool &)), this, SLOT(clickedItemCandidateSlot(QGraphicsItem *, bool &)), Qt::DirectConnection);
480     	connect(viewThing->sketchWidget, SIGNAL(itemAddedSignal(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long, SketchWidget *)),
481 							 this, SLOT(itemAddedSlot(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long, SketchWidget *)));
482 
483 
484     }
485 
486     m_metadataView = new PEMetadataView(this);
487     SketchAreaWidget * sketchAreaWidget = new SketchAreaWidget(m_metadataView, this, false, false);
488 	addTab(sketchAreaWidget, tr("Metadata"));
489     connect(m_metadataView, SIGNAL(metadataChanged(const QString &, const QString &)), this, SLOT(metadataChanged(const QString &, const QString &)), Qt::DirectConnection);
490     connect(m_metadataView, SIGNAL(tagsChanged(const QStringList &)), this, SLOT(tagsChanged(const QStringList &)), Qt::DirectConnection);
491     connect(m_metadataView, SIGNAL(propertiesChanged(const QHash<QString, QString> &)), this, SLOT(propertiesChanged(const QHash<QString, QString> &)), Qt::DirectConnection);
492 
493     m_connectorsView = new PEConnectorsView(this);
494     sketchAreaWidget = new SketchAreaWidget(m_connectorsView, this, false, false);
495 	addTab(sketchAreaWidget, tr("Connectors"));
496 
497     connect(m_connectorsView, SIGNAL(connectorMetadataChanged(ConnectorMetadata *)), this, SLOT(connectorMetadataChanged(ConnectorMetadata *)), Qt::DirectConnection);
498     connect(m_connectorsView, SIGNAL(connectorsTypeChanged(Connector::ConnectorType)), this, SLOT(connectorsTypeChanged(Connector::ConnectorType)));
499     connect(m_connectorsView, SIGNAL(removedConnectors(QList<ConnectorMetadata *> &)), this, SLOT(removedConnectors(QList<ConnectorMetadata *> &)), Qt::DirectConnection);
500     connect(m_connectorsView, SIGNAL(connectorCountChanged(int)), this, SLOT(connectorCountChanged(int)), Qt::DirectConnection);
501     connect(m_connectorsView, SIGNAL(smdChanged(const QString &)), this, SLOT(smdChanged(const QString &)));
502 
503 }
504 
initDock()505 void PEMainWindow::initDock()
506 {
507 	m_layerPalette = new LayerPalette(this);
508 
509     /*
510     m_infoView = new HtmlInfoView();
511     m_infoView->init(true);
512 
513     m_binManager = new BinManager(m_referenceModel, m_infoView, m_undoStack, this);
514     m_binManager->openBin(":/resources/bins/pe.fzb");
515     m_binManager->hideTabBar();
516     */
517 }
518 
moreInitDock()519 void PEMainWindow::moreInitDock()
520 {
521     static int BinMinHeight = 75;
522     static int ToolDefaultHeight = 150;
523     static int SvgDefaultHeight = 50;
524 
525 
526     m_peToolView = new PEToolView();
527     connect(m_peToolView, SIGNAL(getSpinAmount(double &)), this, SLOT(getSpinAmount(double &)), Qt::DirectConnection);
528     connect(m_peToolView, SIGNAL(terminalPointChanged(const QString &)), this, SLOT(terminalPointChanged(const QString &)));
529     connect(m_peToolView, SIGNAL(terminalPointChanged(const QString &, double)), this, SLOT(terminalPointChanged(const QString &, double)));
530     connect(m_peToolView, SIGNAL(switchedConnector(int)), this, SLOT(switchedConnector(int)));
531     connect(m_peToolView, SIGNAL(removedConnector(const QDomElement &)), this, SLOT(removedConnector(const QDomElement &)));
532     connect(m_peToolView, SIGNAL(pickModeChanged(bool)), this, SLOT(pickModeChanged(bool)));
533     connect(m_peToolView, SIGNAL(busModeChanged(bool)), this, SLOT(busModeChanged(bool)));
534     connect(m_peToolView, SIGNAL(connectorMetadataChanged(ConnectorMetadata *)), this, SLOT(connectorMetadataChanged(ConnectorMetadata *)), Qt::DirectConnection);
535     makeDock(tr("Connectors"), m_peToolView, DockMinWidth, ToolDefaultHeight);
536     m_peToolView->setMinimumSize(DockMinWidth, ToolDefaultHeight);
537 
538     m_peSvgView = new PESvgView();
539     makeDock(tr("SVG"), m_peSvgView, DockMinWidth, SvgDefaultHeight);
540     m_peSvgView->setMinimumSize(DockMinWidth, SvgDefaultHeight);
541 
542     if (m_binManager) {
543 	    QDockWidget * dockWidget = makeDock(BinManager::Title, m_binManager, DockMinWidth, BinMinHeight);
544         dockWidget->resize(0, 0);
545     }
546 
547     if (m_infoView) {
548         makeDock(tr("Inspector"), m_infoView, InfoViewMinHeight, InfoViewHeightDefault);
549         this -> setObjectName("PEInspector");
550     }
551 
552     makeDock(tr("Layers"), m_layerPalette, DockMinWidth, DockMinHeight)->hide();
553     m_layerPalette->setMinimumSize(DockMinWidth, DockMinHeight);
554 	m_layerPalette->setShowAllLayersAction(m_showAllLayersAct);
555 	m_layerPalette->setHideAllLayersAction(m_hideAllLayersAct);
556 
557 }
558 
createFileMenuActions()559 void PEMainWindow::createFileMenuActions() {
560 	MainWindow::createFileMenuActions();
561 
562 	m_reuseBreadboardAct = new QAction(tr("Reuse breadboard image"), this);
563 	m_reuseBreadboardAct->setStatusTip(tr("Reuse the breadboard image in this view"));
564 	connect(m_reuseBreadboardAct, SIGNAL(triggered()), this, SLOT(reuseBreadboard()));
565 
566 	m_reuseSchematicAct = new QAction(tr("Reuse schematic image"), this);
567 	m_reuseSchematicAct->setStatusTip(tr("Reuse the schematic image in this view"));
568 	connect(m_reuseSchematicAct, SIGNAL(triggered()), this, SLOT(reuseSchematic()));
569 
570 	m_reusePCBAct = new QAction(tr("Reuse PCB image"), this);
571 	m_reusePCBAct->setStatusTip(tr("Reuse the PCB image in this view"));
572 	connect(m_reusePCBAct, SIGNAL(triggered()), this, SLOT(reusePCB()));
573 
574 }
575 
createActions()576 void PEMainWindow::createActions()
577 {
578     createFileMenuActions();
579 
580 	m_openAct->setText(tr("Load image for view..."));
581 	m_openAct->setStatusTip(tr("Open a file to use as the image for this view of the part."));
582 	disconnect(m_openAct, SIGNAL(triggered()), this, SLOT(mainLoad()));
583 	connect(m_openAct, SIGNAL(triggered()), this, SLOT(loadImage()));
584 
585 	m_showInOSAct = new QAction(tr("Show in Folder"), this);
586 	m_showInOSAct->setStatusTip(tr("On the desktop, open the folder containing the current svg file."));
587 	connect(m_showInOSAct, SIGNAL(triggered()), this, SLOT(showInOS()));
588 
589     createEditMenuActions();
590 
591 	m_convertToTenthAct = new QAction(tr("Convert schematic to 0.1 inch standard"), this);
592 	m_convertToTenthAct->setStatusTip(tr("Convert pre-0.8.6 schematic image to new 0.1 inch standard"));
593 	connect(m_convertToTenthAct, SIGNAL(triggered()), this, SLOT(convertToTenth()));
594 
595 	m_deleteBusConnectionAct = new WireAction(tr("Remove Internal Connection"), this);
596 	connect(m_deleteBusConnectionAct, SIGNAL(triggered()), this, SLOT(deleteBusConnection()));
597 
598     createViewMenuActions(false);
599     createHelpMenuActions();
600     createWindowMenuActions();
601 	createActiveLayerActions();
602 
603 }
604 
createMenus()605 void PEMainWindow::createMenus()
606 {
607     createFileMenu();
608     createEditMenu();
609     createViewMenu();
610     createWindowMenu();
611     createHelpMenu();
612 }
613 
createFileMenu()614 void PEMainWindow::createFileMenu() {
615     m_fileMenu = menuBar()->addMenu(tr("&File"));
616     m_fileMenu->addAction(m_openAct);
617     m_fileMenu->addAction(m_reuseBreadboardAct);
618     m_fileMenu->addAction(m_reuseSchematicAct);
619     m_fileMenu->addAction(m_reusePCBAct);
620 
621     //m_fileMenu->addAction(m_revertAct);
622 
623     m_fileMenu->addSeparator();
624     m_fileMenu->addAction(m_closeAct);
625     m_fileMenu->addAction(m_saveAct);
626     m_fileMenu->addAction(m_saveAsAct);
627     m_saveAsAct->setText(tr("Save as new part"));
628     m_saveAsAct->setStatusTip(tr("Make a copy of the part and save it in the 'My Parts' Bin"));
629 
630     m_fileMenu->addSeparator();
631 	m_exportMenu = m_fileMenu->addMenu(tr("&Export"));
632     //m_fileMenu->addAction(m_pageSetupAct);
633     m_fileMenu->addAction(m_printAct);
634     m_fileMenu->addAction(m_showInOSAct);
635 
636 	m_fileMenu->addSeparator();
637 	m_fileMenu->addAction(m_quitAct);
638 
639 	populateExportMenu();
640 
641     connect(m_fileMenu, SIGNAL(aboutToShow()), this, SLOT(updateFileMenu()));
642 }
643 
createEditMenu()644 void PEMainWindow::createEditMenu()
645 {
646     m_editMenu = menuBar()->addMenu(tr("&Edit"));
647     m_editMenu->addAction(m_undoAct);
648     m_editMenu->addAction(m_redoAct);
649     m_editMenu->addSeparator();
650     m_editMenu->addAction(m_convertToTenthAct);
651 
652     updateEditMenu();
653     connect(m_editMenu, SIGNAL(aboutToShow()), this, SLOT(updateEditMenu()));
654 }
655 
getButtonsForView(ViewLayer::ViewID viewID)656 QList<QWidget*> PEMainWindow::getButtonsForView(ViewLayer::ViewID viewID) {
657 
658 	QList<QWidget*> retval;
659 	SketchAreaWidget *parent;
660 	switch(viewID) {
661 		case ViewLayer::BreadboardView: parent = m_breadboardWidget; break;
662 		case ViewLayer::SchematicView: parent = m_schematicWidget; break;
663 		case ViewLayer::PCBView: parent = m_pcbWidget; break;
664 		default: return retval;
665 	}
666 
667 	//retval << createExportEtchableButton(parent);
668 
669 	switch (viewID) {
670 		case ViewLayer::BreadboardView:
671 			break;
672 		case ViewLayer::SchematicView:
673 			break;
674 		case ViewLayer::PCBView:
675 			// retval << createActiveLayerButton(parent);
676 			break;
677 		default:
678 			break;
679 	}
680 
681 	return retval;
682 }
683 
activeLayerWidgetAlwaysOn()684 bool PEMainWindow::activeLayerWidgetAlwaysOn() {
685 	return true;
686 }
687 
connectPairs()688 void PEMainWindow::connectPairs() {
689     bool succeeded = true;
690 	succeeded =  succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_breadboardGraphicsView, SLOT(spaceBarIsPressedSlot(bool)));
691 	succeeded =  succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_schematicGraphicsView, SLOT(spaceBarIsPressedSlot(bool)));
692 	succeeded =  succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_pcbGraphicsView, SLOT(spaceBarIsPressedSlot(bool)));
693 
694 	succeeded =  succeeded && connect(m_pcbGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double)));
695 	succeeded =  succeeded && connect(m_breadboardGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double)));
696 	succeeded =  succeeded && connect(m_schematicGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double)));
697 
698 	connect(m_breadboardGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *)));
699 	connect(m_schematicGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *)));
700 	connect(m_pcbGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *)));
701 
702 }
703 
breadboardWireMenu()704 QMenu *PEMainWindow::breadboardWireMenu() {
705 	QMenu *menu = new QMenu(QObject::tr("Internal Connections"), this);
706 
707 	menu->addAction(m_deleteBusConnectionAct);
708     connect( menu, SIGNAL(aboutToShow()), this, SLOT(updateWireMenu()));
709 	return menu;
710 }
711 
breadboardItemMenu()712 QMenu *PEMainWindow::breadboardItemMenu() {
713     return NULL;
714 }
715 
schematicWireMenu()716 QMenu *PEMainWindow::schematicWireMenu() {
717     return breadboardWireMenu();
718 }
719 
schematicItemMenu()720 QMenu *PEMainWindow::schematicItemMenu() {
721     return NULL;
722 }
723 
pcbWireMenu()724 QMenu *PEMainWindow::pcbWireMenu() {
725     return breadboardWireMenu();
726 }
727 
pcbItemMenu()728 QMenu *PEMainWindow::pcbItemMenu() {
729     return NULL;
730 }
731 
setInitialItem(PaletteItem * paletteItem)732 bool PEMainWindow::setInitialItem(PaletteItem * paletteItem)
733 {
734     m_pcbGraphicsView->setBoardLayers(2, true);
735 	m_pcbGraphicsView->setLayerActive(ViewLayer::Copper1, true);
736 	m_pcbGraphicsView->setLayerActive(ViewLayer::Copper0, true);
737 	m_pcbGraphicsView->setLayerActive(ViewLayer::Silkscreen1, true);
738 	m_pcbGraphicsView->setLayerActive(ViewLayer::Silkscreen0, true);
739 
740     ModelPart * originalModelPart = NULL;
741     if (paletteItem == NULL) {
742         // this shouldn't happen
743         originalModelPart = m_referenceModel->retrieveModelPart("generic_ic_dip_8_300mil");
744     }
745     else {
746         originalModelPart = paletteItem->modelPart();
747     }
748 
749     m_originalFzpPath = originalModelPart->path();
750     m_originalModuleID = originalModelPart->moduleID();
751 
752     QFileInfo info(originalModelPart->path());
753     QString basename = info.completeBaseName();
754     int ix = GuidMatcher.indexIn(basename);
755     if (ix > 1 && basename.at(ix - 1) == '_')  {
756         int dix = ix + 32 + 1;
757         if (basename.count() > dix) {
758             bool gotPrefix = true;
759             for (int i = dix; i < basename.count(); i++) {
760                 if (!basename.at(i).isDigit()) {
761                     gotPrefix = false;
762                     break;
763                 }
764             }
765             if (gotPrefix) {
766                 m_prefix = basename.left(ix - 1);
767             }
768         }
769     }
770 
771     //DebugDialog::debug(QString("%1, %2").arg(info.absoluteFilePath()).arg(m_userPartsFolderPath));
772     m_canSave = info.absoluteFilePath().contains(m_userPartsFolderPath);
773 
774     if (!loadFzp(originalModelPart->path())) return false;
775 
776     QDomElement fzpRoot = m_fzpDocument.documentElement();
777     QString referenceFile = fzpRoot.attribute(ReferenceFileString);
778     if (referenceFile.isEmpty()) {
779         fzpRoot.setAttribute(ReferenceFileString, info.fileName());
780     }
781     QDomElement views = fzpRoot.firstChildElement("views");
782     QDomElement author = fzpRoot.firstChildElement("author");
783     if (author.isNull()) {
784         author = m_fzpDocument.createElement("author");
785         fzpRoot.appendChild(author);
786     }
787     if (author.text().isEmpty()) {
788         TextUtils::replaceChildText(author, QString(getenvUser()));
789     }
790     QDomElement date = fzpRoot.firstChildElement("date");
791     if (date.isNull()) {
792         date = m_fzpDocument.createElement("date");
793         fzpRoot.appendChild(date);
794     }
795     TextUtils::replaceChildText(date, QDate::currentDate().toString());
796 
797 	QDomElement properties = fzpRoot.firstChildElement("properties");
798 	if (properties.isNull()) {
799 		properties = m_fzpDocument.createElement("properties");
800 		fzpRoot.appendChild(properties);
801 	}
802 	QHash<QString,QString> props = originalModelPart->properties();
803 	foreach (QString key, props.keys()) {
804 		replaceProperty(key, props.value(key), properties);
805 	}
806 	// record "local" properties
807 	foreach (QByteArray byteArray, originalModelPart->dynamicPropertyNames()) {
808 		replaceProperty(byteArray, originalModelPart->property(byteArray).toString(), properties);
809 	}
810 
811     bool hasLegID = false;
812 	QDomElement connectors = fzpRoot.firstChildElement("connectors");
813 	QDomElement connector = connectors.firstChildElement("connector");
814 	while (!connector.isNull()) {
815 		QString localName = originalModelPart->connectorLocalName(connector.attribute("id"));
816 		if (!localName.isEmpty()) {
817 			connector.setAttribute("name", localName);
818 		}
819         if (!hasLegID) {
820             QDomNodeList plist = connector.elementsByTagName("p");
821             for (int i = 0; i < plist.count(); i++) {
822                 QDomElement p = plist.at(i).toElement();
823                 if (!p.attribute("legId").isEmpty()) {
824                     hasLegID = true;
825                     break;
826                 }
827             }
828         }
829 		connector = connector.nextSiblingElement("connector");
830 	}
831 
832     if (hasLegID && !RubberBandLegWarning) {
833         RubberBandLegWarning = true;
834         QMessageBox::warning(NULL, tr("Parts Editor"),
835                                     tr("This part has bendable legs. ") +
836                                     tr("This version of the Parts Editor does not yet support editing bendable legs, and the legs may not be displayed correctly in breadboard view . ") +
837                                     tr("If you make changes to breadboard view, or change connector metadata, the legs may no longer work. ") +
838                                     tr("You can safely make changes to Schematic or PCB view.\n\n") +
839                                     tr("This warning will not be repeated in this session of Fritzing")
840                                 );
841     }
842 
843 
844 
845 	// for now kill the editable pin labels property, otherwise the saved part will try to use the labels that are only found in the sketch
846 	QDomElement epl = TextUtils::findElementWithAttribute(properties, "name", "editable pin labels");
847 	if (!epl.isNull()) {
848 		TextUtils::replaceChildText(epl, "false");
849 	}
850 
851 	QDomElement family = TextUtils::findElementWithAttribute(properties, "name", "family");
852 	if (family.isNull()) {
853 		replaceProperty("family", m_guid, properties);
854 	}
855 	QDomElement variant = TextUtils::findElementWithAttribute(properties, "name", "variant");
856 	if (variant.isNull()) {
857 		QString newVariant = makeNewVariant(family.text());
858 		replaceProperty("variant", newVariant, properties);
859 	}
860 
861     foreach (ViewThing * viewThing, m_viewThings.values()) {
862         if (viewThing->sketchWidget == NULL) continue;
863 
864         ItemBase * itemBase = originalModelPart->viewItem(viewThing->sketchWidget->viewID());
865         if (itemBase == NULL) continue;
866 
867         viewThing->referenceFile = getSvgReferenceFile(itemBase->filename());
868 
869         if (!itemBase->hasCustomSVG()) {
870             QFile file(itemBase->filename());
871             if (!file.open(QFile::ReadOnly)) {
872                 QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to load '%1'. Please close the parts editor without saving and try again.").arg(itemBase->filename()));
873                 continue;
874             }
875 
876             QString svg = file.readAll();
877             insertDesc(viewThing->referenceFile, svg);
878             TextUtils::fixMuch(svg, true);
879             QString svgPath = makeSvgPath2(viewThing->sketchWidget);
880             bool result = writeXml(m_userPartsFolderSvgPath + svgPath, removeGorn(svg), true);
881             if (!result) {
882                 QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to write svg to  %1").arg(svgPath));
883 		        return false;
884             }
885 
886             continue;
887         }
888 
889         QHash<QString, QString> svgHash;
890 		QStringList svgList;
891         double factor;
892 		foreach (ViewLayer * vl, viewThing->sketchWidget->viewLayers().values()) {
893 			QString string = itemBase->retrieveSvg(vl->viewLayerID(), svgHash, false, GraphicsUtils::StandardFritzingDPI, factor);
894 			if (!string.isEmpty()) {
895 				svgList.append(string);
896 			}
897 		}
898 
899         if (svgList.count() == 0) {
900             DebugDialog::debug(QString("pe: missing custom svg %1").arg(originalModelPart->moduleID()));
901             continue;
902         }
903 
904 
905 		QString svg;
906 		if (svgList.count() == 1) {
907 			svg = svgList.at(0);
908 		}
909 		else {
910 			// deal with copper0 and copper1 layers as parent/child
911             // have to remove whitespace in order to compare the two svgs
912 			QRegExp white("\\s");
913 			QStringList whiteList;
914 			foreach (QString string, svgList) {
915 				string.remove(white);
916 				whiteList << string;
917 			}
918 			bool keepGoing = true;
919 			while (keepGoing) {
920 				keepGoing = false;
921 				for (int i = 0; i < whiteList.count() - 1; i++)  {
922 					for (int j = i + 1; j < whiteList.count(); j++) {
923 						if (whiteList.at(i).contains(whiteList.at(j))) {
924 							keepGoing = true;
925 							whiteList.removeAt(j);
926 							svgList.removeAt(j);
927 							break;
928 						}
929 						if (whiteList.at(j).contains(whiteList.at(i))) {
930 							keepGoing = true;
931 							whiteList.removeAt(i);
932 							svgList.removeAt(i);
933 							break;
934 						}
935 					}
936 					if (keepGoing) break;
937 				}
938 			}
939 			svg = svgList.join("\n");
940 		}
941 
942 		QSizeF size = itemBase->size();
943         QString header = TextUtils::makeSVGHeader(GraphicsUtils::SVGDPI, GraphicsUtils::StandardFritzingDPI, size.width(), size.height());
944         header += makeDesc(viewThing->referenceFile);
945 		svg = header + svg + "</svg>";
946         QString svgPath = makeSvgPath2(viewThing->sketchWidget);
947         bool result = writeXml(m_userPartsFolderSvgPath + svgPath, removeGorn(svg), true);
948         if (!result) {
949             QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to write svg to  %1").arg(svgPath));
950 		    return false;
951         }
952 
953         QDomElement view = views.firstChildElement(ViewLayer::viewIDXmlName(viewThing->sketchWidget->viewID()));
954         QDomElement layers = view.firstChildElement("layers");
955         if (layers.isNull()) {
956             QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to parse fzp file  %1").arg(originalModelPart->path()));
957 		    return false;
958         }
959 
960 		setImageAttribute(layers, svgPath);
961     }
962 
963     reload(true);
964 
965     foreach (ViewThing * viewThing, m_viewThings.values()) {
966         viewThing->originalSvgPath = viewThing->itemBase->filename();
967         viewThing->svgChangeCount = 0;
968     }
969 
970     setTitle();
971     createRaiseWindowActions();
972     return true;
973 }
974 
initZoom()975 void PEMainWindow::initZoom() {
976     if (m_peToolView == NULL) return;
977     if (m_currentGraphicsView == NULL) return;
978 
979 	ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID());
980 	if (viewThing->itemBase == NULL) return;
981 
982     if (!viewThing->everZoomed) {
983         viewThing->everZoomed = true;
984         m_currentGraphicsView->fitInWindow();
985     }
986 
987 	m_peSvgView->setFilename(viewThing->referenceFile);
988 }
989 
setTitle()990 void PEMainWindow::setTitle() {
991     QString title = tr("Fritzing (New) Parts Editor");
992     QString partTitle = getPartTitle();
993 
994     QString viewName;
995     if (m_currentGraphicsView) viewName = m_currentGraphicsView->viewName();
996     else if (currentTabIndex() == IconViewIndex) viewName = tr("Icon View");
997     else if (currentTabIndex() == MetadataViewIndex) viewName = tr("Metadata View");
998     else if (currentTabIndex() == ConnectorsViewIndex) viewName = tr("Connectors View");
999 
1000 	setWindowTitle(QString("%1: %2 [%3]%4").arg(title).arg(partTitle).arg(viewName).arg(QtFunkyPlaceholder));
1001 }
1002 
createViewMenuActions(bool showWelcome)1003 void PEMainWindow::createViewMenuActions(bool showWelcome) {
1004     MainWindow::createViewMenuActions(showWelcome);
1005 
1006 	m_showIconAct = new QAction(tr("Show Icon"), this);
1007 	m_showIconAct->setShortcut(tr("Ctrl+4"));
1008 	m_showIconAct->setStatusTip(tr("Show the icon view"));
1009 	connect(m_showIconAct, SIGNAL(triggered()), this, SLOT(showIconView()));
1010 
1011 	m_showMetadataViewAct = new QAction(tr("Show Metadata"), this);
1012 	m_showMetadataViewAct->setShortcut(tr("Ctrl+5"));
1013 	m_showMetadataViewAct->setStatusTip(tr("Show the metadata view"));
1014 	connect(m_showMetadataViewAct, SIGNAL(triggered()), this, SLOT(showMetadataView()));
1015 
1016     m_showConnectorsViewAct = new QAction(tr("Show Connectors"), this);
1017 	m_showConnectorsViewAct->setShortcut(tr("Ctrl+6"));
1018 	m_showConnectorsViewAct->setStatusTip(tr("Show the connector metadata in a list view"));
1019 	connect(m_showConnectorsViewAct, SIGNAL(triggered()), this, SLOT(showConnectorsView()));
1020 
1021     m_hideOtherViewsAct = new QAction(tr("Make only this view visible"), this);
1022 	m_hideOtherViewsAct->setStatusTip(tr("The part will only be visible in this view and icon view"));
1023 	connect(m_hideOtherViewsAct, SIGNAL(triggered()), this, SLOT(hideOtherViews()));
1024 
1025 }
1026 
createViewMenu()1027 void PEMainWindow::createViewMenu() {
1028     MainWindow::createViewMenu();
1029 
1030     bool afterNext = false;
1031     foreach (QAction * action, m_viewMenu->actions()) {
1032         if (action == m_setBackgroundColorAct) {
1033 			afterNext = true;
1034 		}
1035 		else if (afterNext) {
1036             m_viewMenu->insertSeparator(action);
1037             m_viewMenu->insertAction(action, m_hideOtherViewsAct);
1038             break;
1039         }
1040     }
1041 
1042     afterNext = false;
1043     foreach (QAction * action, m_viewMenu->actions()) {
1044         if (action == m_showPCBAct) {
1045             afterNext = true;
1046         }
1047         else if (afterNext) {
1048             m_viewMenu->insertAction(action, m_showIconAct);
1049             m_viewMenu->insertAction(action, m_showMetadataViewAct);
1050             m_viewMenu->insertAction(action, m_showConnectorsViewAct);
1051             break;
1052         }
1053     }
1054     m_numFixedActionsInViewMenu = m_viewMenu->actions().size();
1055 }
1056 
showMetadataView()1057 void PEMainWindow::showMetadataView() {
1058     setCurrentTabIndex(MetadataViewIndex);
1059 }
1060 
showConnectorsView()1061 void PEMainWindow::showConnectorsView() {
1062     setCurrentTabIndex(ConnectorsViewIndex);
1063 }
1064 
showIconView()1065 void PEMainWindow::showIconView() {
1066     setCurrentTabIndex(IconViewIndex);
1067 }
1068 
changeSpecialProperty(const QString & name,const QString & value)1069 void PEMainWindow::changeSpecialProperty(const QString & name, const QString & value)
1070 {
1071     QHash<QString, QString> oldProperties = getOldProperties();
1072 
1073 	if (value.isEmpty()) {
1074 		QMessageBox::warning(NULL, tr("Blank not allowed"), tr("The value of '%1' can not be blank.").arg(name));
1075 		m_metadataView->resetProperty(name, value);
1076 		return;
1077 	}
1078 
1079 	if (oldProperties.value(name) == value) {
1080 		return;
1081 	}
1082 
1083     QHash<QString, QString> newProperties(oldProperties);
1084     newProperties.insert(name, value);
1085 
1086     ChangePropertiesCommand * cpc = new ChangePropertiesCommand(this, oldProperties, newProperties, NULL);
1087     cpc->setText(tr("Change %1 to %2").arg(name).arg(value));
1088     cpc->setSkipFirstRedo();
1089     changeProperties(newProperties, false);
1090     m_undoStack->waitPush(cpc, SketchWidget::PropChangeDelay);
1091 }
1092 
metadataChanged(const QString & name,const QString & value)1093 void PEMainWindow::metadataChanged(const QString & name, const QString & value)
1094 {
1095 	qDebug() << "metadata changed";
1096 
1097 	if (name.compare("family") == 0) {
1098 		changeSpecialProperty(name, value);
1099         return;
1100     }
1101 
1102     if (name.compare("variant") == 0) {
1103 		QString family = m_metadataView->family();
1104 		QHash<QString, QString> variants = m_referenceModel->allPropValues(family, "variant");
1105 		QStringList values = variants.values(value);
1106 		if (m_canSave) {
1107 			QString moduleID = m_fzpDocument.documentElement().attribute("moduleId");
1108 			values.removeOne(moduleID);
1109 		}
1110 		if (values.count() > 0) {
1111 			QMessageBox::warning(NULL, tr("Must be unique"), tr("Variant '%1' is in use. The variant name must be unique.").arg(value));
1112 			return;
1113 		}
1114 
1115 		changeSpecialProperty(name, value);
1116         return;
1117     }
1118 
1119     QString menuText = (name.compare("description") == 0) ? tr("Change description") : tr("Change %1 to '%2'").arg(name).arg(value);
1120 
1121     // called from metadataView
1122     QDomElement root = m_fzpDocument.documentElement();
1123     QDomElement element = root.firstChildElement(name);
1124     QString oldValue = element.text();
1125 	if (oldValue == value) return;
1126 
1127     ChangeMetadataCommand * cmc = new ChangeMetadataCommand(this, name, oldValue, value, NULL);
1128     cmc->setText(menuText);
1129     cmc->setSkipFirstRedo();
1130     changeMetadata(name, value, false);
1131     m_undoStack->waitPush(cmc, SketchWidget::PropChangeDelay);
1132 }
1133 
changeMetadata(const QString & name,const QString & value,bool updateDisplay)1134 void PEMainWindow::changeMetadata(const QString & name, const QString & value, bool updateDisplay)
1135 {
1136     // called from command object
1137     QDomElement root = m_fzpDocument.documentElement();
1138     TextUtils::replaceElementChildText(root, name, value);
1139 
1140     //QString test = m_fzpDocument.toString();
1141 
1142     if (updateDisplay) {
1143         m_metadataView->initMetadata(m_fzpDocument);
1144     }
1145 }
1146 
tagsChanged(const QStringList & newTags)1147 void PEMainWindow::tagsChanged(const QStringList & newTags)
1148 {
1149 
1150     // called from metadataView
1151     QDomElement root = m_fzpDocument.documentElement();
1152     QDomElement tags = root.firstChildElement("tags");
1153     QDomElement tag = tags.firstChildElement("tag");
1154     QStringList oldTags;
1155     while (!tag.isNull()) {
1156         oldTags << tag.text();
1157         tag = tag.nextSiblingElement("tag");
1158     }
1159 
1160     ChangeTagsCommand * ctc = new ChangeTagsCommand(this, oldTags, newTags, NULL);
1161     ctc->setText(tr("Change tags"));
1162     ctc->setSkipFirstRedo();
1163     changeTags(newTags, false);
1164     m_undoStack->waitPush(ctc, SketchWidget::PropChangeDelay);
1165 }
1166 
changeTags(const QStringList & newTags,bool updateDisplay)1167 void PEMainWindow::changeTags(const QStringList & newTags, bool updateDisplay)
1168 {
1169     QDomElement root = m_fzpDocument.documentElement();
1170     QDomElement tags = root.firstChildElement("tags");
1171     QDomElement tag = tags.firstChildElement("tag");
1172     while (!tag.isNull()) {
1173         tags.removeChild(tag);
1174         tag = tags.firstChildElement("tag");
1175     }
1176 
1177     foreach (QString newTag, newTags) {
1178         QDomElement tag = m_fzpDocument.createElement("tag");
1179         tags.appendChild(tag);
1180         TextUtils::replaceChildText(tag, newTag);
1181     }
1182 
1183     if (updateDisplay) {
1184         m_metadataView->initMetadata(m_fzpDocument);
1185     }
1186 }
1187 
propertiesChanged(const QHash<QString,QString> & newProperties)1188 void PEMainWindow::propertiesChanged(const QHash<QString, QString> & newProperties)
1189 {
1190 	qDebug() << "properties changed";
1191 
1192 	QStringList keys = newProperties.keys();
1193 	if (keys.contains("family", Qt::CaseInsensitive)) {
1194 		QMessageBox::warning(NULL, tr("Duplicate problem"), tr("Duplicate 'family' property not allowed"));
1195 		return;
1196 	}
1197 
1198 	if (keys.contains("variant", Qt::CaseInsensitive)) {
1199 		QMessageBox::warning(NULL, tr("Duplicate problem"), tr("Duplicate 'variant' property not allowed"));
1200 		return;
1201 	}
1202 
1203     // called from metadataView
1204     QHash<QString, QString> oldProperties = getOldProperties();
1205 
1206     ChangePropertiesCommand * cpc = new ChangePropertiesCommand(this, oldProperties, newProperties, NULL);
1207     cpc->setText(tr("Change properties"));
1208     cpc->setSkipFirstRedo();
1209     changeProperties(newProperties, false);
1210     m_undoStack->waitPush(cpc, SketchWidget::PropChangeDelay);
1211 }
1212 
1213 
changeProperties(const QHash<QString,QString> & newProperties,bool updateDisplay)1214 void PEMainWindow::changeProperties(const QHash<QString, QString> & newProperties, bool updateDisplay)
1215 {
1216     bool incomingFamily = newProperties.keys().contains("family");
1217     bool incomingVariant = newProperties.keys().contains("variant");
1218 
1219     QDomElement root = m_fzpDocument.documentElement();
1220     QDomElement properties = root.firstChildElement("properties");
1221     QDomElement prop = properties.firstChildElement("property");
1222     while (!prop.isNull()) {
1223 		QDomElement next = prop.nextSiblingElement("property");
1224         bool doRemove = true;
1225         if (prop.attribute("name") == "family" && !incomingFamily) doRemove = false;
1226         if (prop.attribute("name") == "variant" && !incomingVariant) doRemove = false;
1227         if (doRemove) {
1228             properties.removeChild(prop);
1229         }
1230         prop = next;
1231     }
1232 
1233     foreach (QString name, newProperties.keys()) {
1234         QDomElement prop = m_fzpDocument.createElement("property");
1235         properties.appendChild(prop);
1236         prop.setAttribute("name", name);
1237         TextUtils::replaceChildText(prop, newProperties.value(name));
1238     }
1239 
1240     if (updateDisplay) {
1241         m_metadataView->initMetadata(m_fzpDocument);
1242     }
1243 }
1244 
getOldProperties()1245 QHash<QString, QString> PEMainWindow::getOldProperties()
1246 {
1247     QDomElement root = m_fzpDocument.documentElement();
1248     QDomElement properties = root.firstChildElement("properties");
1249     QDomElement prop = properties.firstChildElement("property");
1250     QHash<QString, QString> oldProperties;
1251     while (!prop.isNull()) {
1252         QString name = prop.attribute("name");
1253         QString value = prop.text();
1254         oldProperties.insert(name, value);
1255         prop = prop.nextSiblingElement("property");
1256     }
1257 
1258     return oldProperties;
1259 }
1260 
connectorMetadataChanged(ConnectorMetadata * cmd)1261 void PEMainWindow::connectorMetadataChanged(ConnectorMetadata * cmd)
1262 {
1263     int index;
1264     QDomElement connector = findConnector(cmd->connectorID, index);
1265     if (connector.isNull()) return;
1266 
1267     ConnectorMetadata oldcmd;
1268 	fillInMetadata(connector, oldcmd);
1269 
1270     ChangeConnectorMetadataCommand * ccmc = new ChangeConnectorMetadataCommand(this, &oldcmd, cmd, NULL);
1271     ccmc->setText(tr("Change connector %1").arg(cmd->connectorName));
1272     bool skipFirstRedo = (sender() == m_connectorsView);
1273     if (skipFirstRedo) {
1274         ccmc->setSkipFirstRedo();
1275         changeConnectorElement(connector, cmd);
1276         initConnectors(false);
1277     }
1278     m_undoStack->waitPush(ccmc, SketchWidget::PropChangeDelay);
1279 }
1280 
findConnector(const QString & id,int & index)1281 QDomElement PEMainWindow::findConnector(const QString & id, int & index)
1282 {
1283     QDomElement root = m_fzpDocument.documentElement();
1284     QDomElement connectors = root.firstChildElement("connectors");
1285     QDomElement connector = connectors.firstChildElement("connector");
1286     index = 0;
1287     while (!connector.isNull()) {
1288         if (id.compare(connector.attribute("id")) == 0) {
1289             return connector;
1290         }
1291         connector = connector.nextSiblingElement("connector");
1292         index++;
1293     }
1294 
1295     return QDomElement();
1296 }
1297 
1298 
changeConnectorMetadata(ConnectorMetadata * cmd,bool updateDisplay)1299 void PEMainWindow::changeConnectorMetadata(ConnectorMetadata * cmd, bool updateDisplay) {
1300     int index;
1301     QDomElement connector = findConnector(cmd->connectorID, index);
1302     if (connector.isNull()) return;
1303 
1304     changeConnectorElement(connector, cmd);
1305     if (updateDisplay) {
1306         initConnectors(true);
1307         m_peToolView->setCurrentConnector(index);
1308     }
1309 }
1310 
changeConnectorElement(QDomElement & connector,ConnectorMetadata * cmd)1311 void PEMainWindow::changeConnectorElement(QDomElement & connector, ConnectorMetadata * cmd)
1312 {
1313     connector.setAttribute("name", cmd->connectorName);
1314     connector.setAttribute("id", cmd->connectorID);
1315     connector.setAttribute("type", Connector::connectorNameFromType(cmd->connectorType));
1316     TextUtils::replaceElementChildText(connector, "description", cmd->connectorDescription);
1317 
1318 #ifndef QT_NO_DEBUG
1319     QDomElement description = connector.firstChildElement("description");
1320     QString text = description.text();
1321     DebugDialog::debug(QString("description %1").arg(text));
1322 #endif
1323 
1324 }
1325 
initSvgTree(SketchWidget * sketchWidget,ItemBase * itemBase,QDomDocument & svgDocument)1326 void PEMainWindow::initSvgTree(SketchWidget * sketchWidget, ItemBase * itemBase, QDomDocument & svgDocument)
1327 {
1328     QString errorStr;
1329     int errorLine;
1330     int errorColumn;
1331 
1332     QDomDocument tempSvgDoc;
1333     QFile file(itemBase->filename());
1334     if (!tempSvgDoc.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
1335 		DebugDialog::debug(QString("unable to parse svg: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn));
1336         return;
1337 	}
1338 
1339 	if (itemBase->viewID() == ViewLayer::PCBView) {
1340 		QDomElement root = tempSvgDoc.documentElement();
1341 		QDomElement copper0 = TextUtils::findElementWithAttribute(root, "id", "copper0");
1342 		QDomElement copper1 = TextUtils::findElementWithAttribute(root, "id", "copper1");
1343 		if (!copper0.isNull() && !copper1.isNull()) {
1344 			QDomElement parent0 = copper0.parentNode().toElement();
1345 			QDomElement parent1 = copper1.parentNode().toElement();
1346 			if (parent0.attribute("id") == "copper1") ;
1347 			else if (parent1.attribute("id") == "copper0") ;
1348 			else {
1349     			QMessageBox::warning(NULL, tr("SVG problem"),
1350 					tr("This version of the new Parts Editor can not deal with separate copper0 and copper1 layers in '%1'. ").arg(itemBase->filename()) +
1351 					tr("So editing may produce an invalid PCB view image"));
1352 			}
1353 		}
1354 	}
1355 
1356     int z = PegiZ;
1357 
1358     FSvgRenderer tempRenderer;
1359     QByteArray rendered = tempRenderer.loadSvg(tempSvgDoc.toByteArray(), "", false);
1360     // cleans up the svg
1361     if (!svgDocument.setContent(rendered, true, &errorStr, &errorLine, &errorColumn)) {
1362 		DebugDialog::debug(QString("unable to parse svg (2): %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn));
1363         return;
1364 	}
1365 
1366     TextUtils::gornTree(svgDocument);
1367     FSvgRenderer renderer;
1368     renderer.loadSvg(svgDocument.toByteArray(), "", false);
1369 
1370 	QHash<QString, PEGraphicsItem *> pegiHash;
1371 
1372     QList<QDomElement> traverse;
1373     traverse << svgDocument.documentElement();
1374     while (traverse.count() > 0) {
1375         QDomElement element = traverse.takeFirst();
1376 
1377         // depth first
1378         QList<QDomElement> next;
1379         QDomElement child = element.firstChildElement();
1380         while (!child.isNull()) {
1381             next << child;
1382             child = child.nextSiblingElement();
1383         }
1384         while (next.count() > 0) {
1385             traverse.push_front(next.takeLast());
1386         }
1387 
1388         bool isG = false;
1389         bool isSvg = false;
1390         QString tagName = element.tagName();
1391         if      (tagName.compare("rect") == 0);
1392         else if (tagName.compare("g") == 0) {
1393             isG = true;
1394         }
1395         else if (tagName.compare("svg") == 0) {
1396             isSvg = true;
1397         }
1398         else if (tagName.compare("circle") == 0);
1399         else if (tagName.compare("ellipse") == 0);
1400         else if (tagName.compare("path") == 0);
1401         else if (tagName.compare("line") == 0);
1402         else if (tagName.compare("polyline") == 0);
1403         else if (tagName.compare("polygon") == 0);
1404         else if (tagName.compare("text") == 0);
1405         else continue;
1406 
1407         QRectF bounds = getPixelBounds(renderer, element);
1408         // known Qt bug: boundsOnElement returns zero width and height for text elements.
1409         if (bounds.width() > 0 && bounds.height() > 0) {
1410             PEGraphicsItem * pegi = makePegi(bounds.size(), bounds.topLeft(), itemBase, element, z++);
1411 			pegiHash.insert(element.attribute("id"), pegi);
1412 
1413             /*
1414             QString string;
1415             QTextStream stream(&string);
1416             element.save(stream, 0);
1417             DebugDialog::debug("........");
1418             DebugDialog::debug(string);
1419             */
1420         }
1421     }
1422 
1423     QDomElement root = m_fzpDocument.documentElement();
1424     QDomElement connectors = root.firstChildElement("connectors");
1425     QDomElement connector = connectors.firstChildElement("connector");
1426     while (!connector.isNull()) {
1427 		QString svgID, terminalID;
1428         bool ok = ViewLayer::getConnectorSvgIDs(connector, sketchWidget->viewID(), svgID, terminalID);
1429 		if (ok) {
1430 			PEGraphicsItem * terminalItem = pegiHash.value(terminalID, NULL);
1431 			PEGraphicsItem * pegi = pegiHash.value(svgID);
1432 			if (pegi == NULL) {
1433 				DebugDialog::debug(QString("missing pegi for svg id %1").arg(svgID));
1434 			}
1435 			else {
1436 				QPointF terminalPoint = pegi->rect().center();
1437 				if (terminalItem) {
1438 					terminalPoint = terminalItem->pos() - pegi->pos() + terminalItem->rect().center();
1439 				}
1440 				pegi->setTerminalPoint(terminalPoint);
1441 				pegi->showTerminalPoint(true);
1442 			}
1443 		}
1444 
1445         connector = connector.nextSiblingElement("connector");
1446     }
1447 }
1448 
highlightSlot(PEGraphicsItem * pegi)1449 void PEMainWindow::highlightSlot(PEGraphicsItem * pegi) {
1450     if (m_peToolView) {
1451         bool enableTerminalPointControls = anyMarquee();
1452         bool vis = anyVisible();
1453         m_peToolView->enableConnectorChanges(vis && pegi->showingMarquee(), vis && enableTerminalPointControls, m_connectorList.count() > 0, vis);
1454     }
1455 
1456     if (m_peSvgView) {
1457         m_peSvgView->highlightElement(pegi);
1458     }
1459 }
1460 
initConnectors(bool updateConnectorsView)1461 void PEMainWindow::initConnectors(bool updateConnectorsView) {
1462     QDomElement root = m_fzpDocument.documentElement();
1463     QDomElement connectors = root.firstChildElement("connectors");
1464     QDomElement connector = connectors.firstChildElement("connector");
1465     m_connectorList.clear();
1466     while (!connector.isNull()) {
1467         m_connectorList.append(connector);
1468         connector = connector.nextSiblingElement("connector");
1469     }
1470 
1471     qSort(m_connectorList.begin(), m_connectorList.end(), byID);
1472 
1473     if (updateConnectorsView) {
1474         m_connectorsView->initConnectors(&m_connectorList);
1475     }
1476     m_peToolView->initConnectors(&m_connectorList);
1477 	updateAssignedConnectors();
1478 }
1479 
switchedConnector(int ix)1480 void PEMainWindow::switchedConnector(int ix)
1481 {
1482     if (m_currentGraphicsView == NULL) return;
1483 
1484     switchedConnector(ix, m_currentGraphicsView);
1485 }
1486 
switchedConnector(int ix,SketchWidget * sketchWidget)1487 void PEMainWindow::switchedConnector(int ix, SketchWidget * sketchWidget)
1488 {
1489 	if (m_connectorList.count() == 0) return;
1490 
1491 	QDomElement element = m_connectorList.at(ix);
1492     if (element.isNull()) return;
1493 
1494 	QString svgID, terminalID;
1495     if (!ViewLayer::getConnectorSvgIDs(element, sketchWidget->viewID(), svgID, terminalID)) return;
1496 
1497     QList<PEGraphicsItem *> pegiList = getPegiList(sketchWidget);
1498     bool gotOne = false;
1499     foreach (PEGraphicsItem * pegi, pegiList) {
1500         QDomElement pegiElement = pegi->element();
1501         if (pegiElement.attribute("id").compare(svgID) == 0) {
1502             gotOne = true;
1503             pegi->showMarquee(true);
1504             pegi->setHighlighted(true);
1505 			m_peToolView->setTerminalPointCoords(pegi->terminalPoint());
1506 			m_peToolView->setTerminalPointLimits(pegi->rect().size());
1507             break;
1508         }
1509     }
1510 
1511     if (!gotOne) {
1512         foreach (PEGraphicsItem * pegi, pegiList) {
1513             pegi->showMarquee(false);
1514             pegi->setHighlighted(false);
1515         }
1516     }
1517 }
1518 
loadImage()1519 void PEMainWindow::loadImage()
1520 {
1521     if (m_currentGraphicsView == NULL) return;
1522 
1523 	QStringList extras;
1524 	extras.append("");
1525 	extras.append("");
1526 	QString imageFiles;
1527 	if (m_currentGraphicsView->viewID() == ViewLayer::PCBView) {
1528 		imageFiles = tr("Image & Footprint Files (%1 %2 %3 %4 %5);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3);;gEDA Footprint Files (%4);;Kicad Module Files (%5)");   //
1529 		extras[0] = "*.fp";
1530 		extras[1] = "*.mod";
1531 	}
1532 	else {
1533 		imageFiles = tr("Image Files (%1 %2 %3);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3)%4%5");
1534 	}
1535 
1536     /*
1537 	if (m_currentGraphicsView->viewID() == ViewLayer::SchematicView) {
1538 		extras[0] = "*.lib";
1539 		imageFiles = tr("Image & Footprint Files (%1 %2 %3 %4);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3);;Kicad Schematic Files (%4)%5");   //
1540 	}
1541     */
1542 
1543     QString initialPath = FolderUtils::openSaveFolder();
1544 	ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID());
1545     ItemBase * itemBase = viewThing->itemBase;
1546     if (itemBase) {
1547         initialPath = itemBase->filename();
1548     }
1549 	QString origPath = FolderUtils::getOpenFileName(this,
1550 		tr("Open Image"),
1551 		initialPath,
1552 		imageFiles.arg("*.svg").arg("*.jpg *.jpeg").arg("*.png").arg(extras[0]).arg(extras[1])
1553 	);
1554 
1555 	if (origPath.isEmpty()) {
1556 		return; // Cancel pressed
1557 	}
1558 
1559     QString newReferenceFile;
1560     QString svg;
1561 	if (origPath.endsWith(".svg")) {
1562         newReferenceFile = getSvgReferenceFile(origPath);
1563 		QFile origFile(origPath);
1564 		if (!origFile.open(QFile::ReadOnly)) {
1565     		QMessageBox::warning(NULL, tr("Conversion problem"), tr("Unable to load '%1'").arg(origPath));
1566 			return;
1567 		}
1568 
1569 		svg = origFile.readAll();
1570 		origFile.close();
1571 		if (svg.contains("coreldraw", Qt::CaseInsensitive) && svg.contains("cdata", Qt::CaseInsensitive)) {
1572     		QMessageBox::warning(NULL, tr("Conversion problem"),
1573 				tr("The SVG file '%1' appears to have been exported from CorelDRAW without the 'presentation attributes' setting. ").arg(origPath) +
1574 				tr("Please re-export the SVG file using that setting, and try loading again.")
1575 			);
1576 			return;
1577 		}
1578 
1579 		QStringList availFonts = InstalledFonts::InstalledFontsList.toList();
1580 		if (availFonts.count() > 0) {
1581 			QString destFont = availFonts.at(0);
1582 			foreach (QString f, availFonts) {
1583 				if (f.contains("droid", Qt::CaseInsensitive)) {
1584 					destFont = f;
1585 					break;
1586 				}
1587 			}
1588             bool reallyFixed = false;
1589             TextUtils::fixFonts(svg, destFont, reallyFixed);
1590 			if (reallyFixed) {
1591     			QMessageBox::information(NULL, tr("Fonts"),
1592 					tr("Fritzing currently only supports OCRA and Droid fonts--these have been substituted in for the fonts in '%1'").arg(origPath));
1593 			}
1594 		}
1595     }
1596     else {
1597         newReferenceFile = origPath;
1598         if (origPath.endsWith("png") || origPath.endsWith("jpg") || origPath.endsWith("jpeg")) {
1599                 QString message = tr("You may use a PNG or JPG image to construct your part, but it is better to use an SVG. ") +
1600                                 tr("PNG and JPG images retain their nature as bitmaps and do not look good when scaled--") +
1601                                 tr("so for Fritzing parts it is best to use PNG and JPG only as placeholders.")
1602                  ;
1603 
1604     		    QMessageBox::information(NULL, tr("Use of PNG and JPG discouraged"), message);
1605 
1606         }
1607 
1608 		try {
1609 			svg = createSvgFromImage(origPath);
1610 		}
1611 		catch (const QString & msg) {
1612     		QMessageBox::warning(NULL, tr("Conversion problem"), tr("Unable to load image file '%1': \n\n%2").arg(origPath).arg(msg));
1613 			return;
1614 		}
1615 	}
1616 
1617     if (svg.isEmpty()) {
1618         QMessageBox::warning(NULL, tr("Conversion problem"), tr("Unable to load image file '%1'").arg(origPath));
1619         return;
1620     }
1621 
1622 	QString errorStr;
1623 	int errorLine;
1624 	int errorColumn;
1625     QDomDocument doc;
1626 	bool result = doc.setContent(svg.toUtf8(), &errorStr, &errorLine, &errorColumn);
1627     if (!result) {
1628     	QMessageBox::warning(NULL, tr("SVG problem"), tr("Unable to parse '%1': %2 line:%3 column:%4").arg(origPath).arg(errorStr).arg(errorLine).arg(errorColumn));
1629         return;
1630     }
1631 
1632     if (m_currentGraphicsView == m_pcbGraphicsView) {
1633         QDomElement root = doc.documentElement();
1634         QDomElement check = TextUtils::findElementWithAttribute(root, "id", "copper1");
1635         if (check.isNull()) {
1636             check = TextUtils::findElementWithAttribute(root, "id", "copper0");
1637         }
1638         if (check.isNull()) {
1639             QString message = tr("There are no copper layers defined in: %1. ").arg(origPath) +
1640                             tr("See <a href=\"http://fritzing.org/learning/tutorials/creating-custom-parts/providing-part-graphics/\">this explanation</a>.") +
1641                             tr("<br/><br/>This will not be a problem in the next release of the Parts Editor, ") +
1642                             tr("but for now please modify the file according to the instructions in the link.")
1643                 ;
1644 
1645     		QMessageBox::warning(NULL, tr("SVG problem"), message);
1646             return;
1647         }
1648     }
1649 
1650     TextUtils::fixMuch(svg, true);
1651     insertDesc(newReferenceFile, svg);
1652     QString newPath = m_userPartsFolderSvgPath + makeSvgPath2(m_currentGraphicsView);
1653     bool success = writeXml(newPath, removeGorn(svg), true);
1654     if (!success) {
1655     	QMessageBox::warning(NULL, tr("Copy problem"), tr("Unable to make a local copy of: '%1'").arg(origPath));
1656 		return;
1657     }
1658 
1659     QFileInfo info(origPath);
1660     QUndoCommand * parentCommand = new QUndoCommand(QString("Load '%1'").arg(info.fileName()));
1661 	new ChangeSvgCommand(this, m_currentGraphicsView, itemBase->filename(), newPath, parentCommand);
1662     m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay);
1663 }
1664 
createSvgFromImage(const QString & origFilePath)1665 QString PEMainWindow::createSvgFromImage(const QString &origFilePath) {
1666 	if (origFilePath.endsWith(".fp")) {
1667 		// this is a geda footprint file
1668 		GedaElement2Svg geda;
1669 		QString svg = geda.convert(origFilePath, false);
1670 		return svg;
1671 	}
1672 
1673 	if (origFilePath.endsWith(".lib")) {
1674 		// Kicad schematic library file
1675 		QStringList defs = KicadSchematic2Svg::listDefs(origFilePath);
1676 		if (defs.count() == 0) {
1677 			throw tr("no schematics found in %1").arg(origFilePath);
1678 		}
1679 
1680 		QString def;
1681 		if (defs.count() > 1) {
1682 			KicadModuleDialog kmd(tr("schematic part"), origFilePath, defs, this);
1683 			int result = kmd.exec();
1684 			if (result != QDialog::Accepted) {
1685 				return "";
1686 			}
1687 
1688 			def = kmd.selectedModule();
1689 		}
1690 		else {
1691 			def = defs.at(0);
1692 		}
1693 
1694 		KicadSchematic2Svg kicad;
1695 		QString svg = kicad.convert(origFilePath, def);
1696 		return svg;
1697 	}
1698 
1699 	if (origFilePath.endsWith(".mod")) {
1700 		// Kicad footprint (Module) library file
1701 		QStringList modules = KicadModule2Svg::listModules(origFilePath);
1702 		if (modules.count() == 0) {
1703 			throw tr("no footprints found in %1").arg(origFilePath);
1704 		}
1705 
1706 		QString module;
1707 		if (modules.count() > 1) {
1708 			KicadModuleDialog kmd("footprint", origFilePath, modules, this);
1709 			int result = kmd.exec();
1710 			if (result != QDialog::Accepted) {
1711 				return "";
1712 			}
1713 
1714 			module = kmd.selectedModule();
1715 		}
1716 		else {
1717 			module = modules.at(0);
1718 		}
1719 
1720 		KicadModule2Svg kicad;
1721 		QString svg = kicad.convert(origFilePath, module, false);
1722 		return svg;
1723 	}
1724 
1725 	// deal with png, jpg:
1726     QBuffer buffer;
1727 	QImage img(origFilePath);
1728 	QSvgGenerator svgGenerator;
1729 	svgGenerator.setResolution(90);
1730 	svgGenerator.setOutputDevice(&buffer);
1731 	QSize sz = img.size();
1732     svgGenerator.setSize(sz);
1733 	svgGenerator.setViewBox(QRect(0, 0, sz.width(), sz.height()));
1734 	QPainter svgPainter(&svgGenerator);
1735 	svgPainter.drawImage(QPoint(0,0), img);
1736 	svgPainter.end();
1737 
1738 	return QString(buffer.buffer());
1739 }
1740 
1741 /*
1742 QString PEMainWindow::makeSvgPath(const QString & referenceFile, SketchWidget * sketchWidget, bool useIndex)
1743 {
1744     QString rf = referenceFile;
1745     if (!rf.isEmpty()) rf += "_";
1746     QString viewName = ViewLayer::viewIDNaturalName(sketchWidget->viewID());
1747     QString indexString;
1748     if (useIndex) indexString = QString("_%3").arg(m_fileIndex++);
1749     return QString("%1/%2%3_%1%4.svg").arg(viewName).arg(rf).arg(m_guid).arg(indexString);
1750 }
1751 */
1752 
makeSvgPath2(SketchWidget * sketchWidget)1753 QString PEMainWindow::makeSvgPath2(SketchWidget * sketchWidget)
1754 {
1755     QString viewName = ViewLayer::viewIDNaturalName(sketchWidget->viewID());
1756     return QString("%1/%2_%3_%4_%1.svg").arg(viewName).arg(m_prefix).arg(m_guid).arg(m_fileIndex);
1757 }
1758 
changeSvg(SketchWidget * sketchWidget,const QString & filename,int changeDirection)1759 void PEMainWindow::changeSvg(SketchWidget * sketchWidget, const QString & filename, int changeDirection) {
1760     QDomElement fzpRoot = m_fzpDocument.documentElement();
1761     setImageAttribute(fzpRoot, filename, sketchWidget->viewID());
1762 
1763     foreach (ViewThing * viewThing, m_viewThings.values()) {
1764         foreach(ItemBase * lk, viewThing->itemBase->layerKin()) {
1765             delete lk;
1766         }
1767         delete viewThing->itemBase;
1768 		viewThing->itemBase = NULL;
1769     }
1770 
1771     updateChangeCount(sketchWidget, changeDirection);
1772 
1773     reload(false);
1774 }
1775 
makeDirName()1776 QString PEMainWindow::makeDirName() {
1777     return "fritzing_pe_" + m_guid;
1778 }
1779 
saveFzp()1780 QString PEMainWindow::saveFzp() {
1781     QDir dir = QDir::temp();
1782     QString dirName = makeDirName();
1783     dir.mkdir(dirName);
1784     dir.cd(dirName);
1785     QString fzpPath = dir.absoluteFilePath(QString("%1_%2_%3.fzp").arg(m_prefix).arg(m_guid).arg(m_fileIndex++));
1786     DebugDialog::debug("temp path " + fzpPath);
1787     writeXml(fzpPath, m_fzpDocument.toString(), true);
1788     return fzpPath;
1789 }
1790 
reload(bool firstTime)1791 void PEMainWindow::reload(bool firstTime)
1792 {
1793 	Q_UNUSED(firstTime);
1794 
1795 	CursorMaster::instance()->addCursor(this, Qt::WaitCursor);
1796 
1797 	QList<ItemBase *> toDelete;
1798 
1799 	foreach (ViewThing * viewThing, m_viewThings.values()) {
1800         if (viewThing->sketchWidget == NULL) continue;
1801 
1802 		foreach (QGraphicsItem * item, viewThing->sketchWidget->scene()->items()) {
1803 			ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
1804 			if (itemBase) toDelete << itemBase;
1805 		}
1806 	}
1807 
1808 	foreach (ItemBase * itemBase, toDelete) {
1809 		delete itemBase;
1810 	}
1811 
1812     killPegi();
1813 
1814     QString fzpPath = saveFzp();   // needs a document somewhere to set up connectors--not part of the undo stack
1815     ModelPart * modelPart = new ModelPart(m_fzpDocument, fzpPath, ModelPart::Part);
1816 
1817     long newID = ItemBase::getNextID();
1818 	ViewGeometry viewGeometry;
1819 	viewGeometry.setLoc(QPointF(0, 0));
1820 
1821 	QList<ItemBase *> itemBases;
1822     itemBases << m_iconGraphicsView->addItem(modelPart, m_iconGraphicsView->defaultViewLayerPlacement(modelPart), BaseCommand::SingleView, viewGeometry, newID, -1, NULL);
1823     itemBases <<  m_breadboardGraphicsView->addItem(modelPart, m_breadboardGraphicsView->defaultViewLayerPlacement(modelPart), BaseCommand::SingleView, viewGeometry, newID, -1, NULL);
1824     itemBases <<  m_schematicGraphicsView->addItem(modelPart, m_schematicGraphicsView->defaultViewLayerPlacement(modelPart), BaseCommand::SingleView, viewGeometry, newID, -1, NULL);
1825     itemBases <<  m_pcbGraphicsView->addItem(modelPart, m_pcbGraphicsView->defaultViewLayerPlacement(modelPart), BaseCommand::SingleView, viewGeometry, newID, -1, NULL);
1826 
1827 	foreach (ItemBase * itemBase, itemBases) {
1828 		ViewThing * viewThing = m_viewThings.value(itemBase->viewID());
1829 		viewThing->itemBase = itemBase;
1830         viewThing->referenceFile = getSvgReferenceFile(itemBase->filename());
1831 	}
1832 
1833 	m_metadataView->initMetadata(m_fzpDocument);
1834 
1835 	QList<QWidget *> widgets;
1836 	widgets << m_metadataView << m_peToolView << m_connectorsView;
1837 	foreach (QWidget * widget, widgets) {
1838 		QList<QLineEdit *> lineEdits = widget->findChildren<QLineEdit *>();
1839 		foreach (QLineEdit * lineEdit, lineEdits) {
1840 			lineEdit->installEventFilter(this);
1841 		}
1842 		QList<QTextEdit *> textEdits = widget->findChildren<QTextEdit *>();
1843 		foreach (QTextEdit * textEdit, textEdits) {
1844 			textEdit->installEventFilter(this);
1845 		}
1846 	}
1847 
1848 	if (m_currentGraphicsView) {
1849 		showing(m_currentGraphicsView);
1850 	}
1851 
1852 	foreach (ViewThing * viewThing, m_viewThings.values()) {
1853 		initSvgTree(viewThing->sketchWidget, viewThing->itemBase, *viewThing->document);
1854 	}
1855 
1856     initConnectors(true);
1857 	m_connectorsView->setSMD(modelPart->flippedSMD());
1858 
1859     foreach (ViewThing * viewThing, m_viewThings.values()) {
1860         // TODO: may have to revisit this and move all pegi items
1861         //viewThing->itemBase->setMoveLock(true);
1862 		//viewThing->itemBase->setItemIsSelectable(false);
1863 		viewThing->itemBase->setAcceptsMousePressLegEvent(false);
1864         viewThing->itemBase->setSwappable(false);
1865         viewThing->sketchWidget->hideConnectors(true);
1866         viewThing->everZoomed = false;
1867    }
1868 
1869     switchedConnector(m_peToolView->currentConnectorIndex());
1870 
1871     // processEventBlocker might be enough?
1872     QTimer::singleShot(10, this, SLOT(initZoom()));
1873 
1874 	CursorMaster::instance()->removeCursor(this);
1875 
1876 }
1877 
busModeChanged(bool state)1878 void PEMainWindow::busModeChanged(bool state) {
1879 	if (m_currentGraphicsView == NULL) return;
1880 
1881 	if (!state) {
1882         foreach (ViewThing * viewThing, m_viewThings.values()) {
1883             viewThing->busMode = false;
1884             viewThing->sketchWidget->hideConnectors(true);
1885         }
1886 
1887 		deleteBuses();
1888 
1889         reload(false);
1890 		return;
1891 	}
1892 
1893 	QList<PEGraphicsItem *> pegiList = getPegiList(m_currentGraphicsView);
1894 
1895 	reload(false);
1896 
1897 	QDomElement root = m_fzpDocument.documentElement();
1898 	QDomElement connectors = root.firstChildElement("connectors");
1899 
1900     foreach (ViewThing * viewThing, m_viewThings.values()) {
1901 	    // items on pegiList no longer exist after reload so get them now
1902 	    pegiList = getPegiList(viewThing->sketchWidget);
1903 	    viewThing->sketchWidget->hideConnectors(true);
1904 	    foreach (PEGraphicsItem * pegi, pegiList) {
1905 		    pegi->setVisible(false);
1906 	    }
1907 
1908 	    // show connectorItems that have connectors assigned
1909 	    QStringList connectorIDs;
1910 	    QDomElement connector = connectors.firstChildElement("connector");
1911 	    while (!connector.isNull()) {
1912 		    QDomElement p = ViewLayer::getConnectorPElement(connector, viewThing->sketchWidget->viewID());
1913 		    QString id = p.attribute("svgId");
1914 		    foreach (PEGraphicsItem * pegi, pegiList) {
1915 			    QDomElement pegiElement = pegi->element();
1916 			    if (pegiElement.attribute("id").compare(id) == 0) {
1917 				    connectorIDs.append(connector.attribute("id"));
1918 				    break;
1919 			    }
1920 		    }
1921 		    connector = connector.nextSiblingElement("connector");
1922 	    }
1923 
1924 	    if (connectorIDs.count() > 0) {
1925 		    viewThing->itemBase->showConnectors(connectorIDs);
1926 	    }
1927     }
1928 
1929 	displayBuses();
1930 	m_peToolView->enableConnectorChanges(false, false, m_connectorList.count() > 0, false);
1931 }
1932 
pickModeChanged(bool state)1933 void PEMainWindow::pickModeChanged(bool state) {
1934 	if (m_currentGraphicsView == NULL) return;
1935 
1936 	// never actually get state == false;
1937 	m_inPickMode = state;
1938 	if (m_inPickMode) {
1939 		QApplication::setOverrideCursor(Qt::PointingHandCursor);
1940 		foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) {
1941 			PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
1942 			if (pegi) pegi->setPickAppearance(true);
1943 		}
1944 		qApp->installEventFilter(this);
1945 	}
1946 }
1947 
pegiTerminalPointMoved(PEGraphicsItem * pegi,QPointF p)1948 void PEMainWindow::pegiTerminalPointMoved(PEGraphicsItem * pegi, QPointF p)
1949 {
1950     // called while terminal point is being dragged, no need for an undo operation
1951 
1952     Q_UNUSED(pegi);
1953     m_peToolView->setTerminalPointCoords(p);
1954 }
1955 
pegiTerminalPointChanged(PEGraphicsItem * pegi,QPointF before,QPointF after)1956 void PEMainWindow::pegiTerminalPointChanged(PEGraphicsItem * pegi, QPointF before, QPointF after)
1957 {
1958     // called at the end of terminal point dragging
1959 
1960 	terminalPointChangedAux(pegi, before, after);
1961 }
1962 
pegiMousePressed(PEGraphicsItem * pegi,bool & ignore)1963 void PEMainWindow::pegiMousePressed(PEGraphicsItem * pegi, bool & ignore)
1964 {
1965     ignore = false;
1966 
1967 	if (m_useNextPick) {
1968 		m_useNextPick = false;
1969 		relocateConnector(pegi);
1970 		return;
1971 	}
1972 
1973     ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID());
1974     if (pegi->itemBase() != viewThing->itemBase) {
1975         ignore = true;
1976         return;
1977     }
1978 
1979 	QString id = pegi->element().attribute("id");
1980 	if (id.isEmpty()) return;
1981 
1982 	QDomElement current = m_connectorList.at(m_peToolView->currentConnectorIndex());
1983 	if (current.attribute("id").compare(id) == 0) {
1984 		// already there
1985 		return;
1986 	}
1987 
1988 	// if a connector has been clicked, make it the current connector
1989 	QDomElement root = m_fzpDocument.documentElement();
1990 	QDomElement connectors = root.firstChildElement("connectors");
1991 	QDomElement connector = connectors.firstChildElement("connector");
1992 	while (!connector.isNull()) {
1993 		QDomElement p = ViewLayer::getConnectorPElement(connector, m_currentGraphicsView->viewID());
1994 		if (p.attribute("svgId") == id || p.attribute("terminalId") == id) {
1995 			m_peToolView->setCurrentConnector(connector);
1996 			return;
1997 		}
1998 		connector = connector.nextSiblingElement("connector");
1999 	}
2000 }
2001 
pegiMouseReleased(PEGraphicsItem *)2002 void PEMainWindow::pegiMouseReleased(PEGraphicsItem *) {
2003 }
2004 
relocateConnector(PEGraphicsItem * pegi)2005 void PEMainWindow::relocateConnector(PEGraphicsItem * pegi)
2006 {
2007     QString newGorn = pegi->element().attribute("gorn");
2008     QDomDocument * svgDoc = m_viewThings.value(m_currentGraphicsView->viewID())->document;
2009     QDomElement svgRoot = svgDoc->documentElement();
2010     QDomElement newGornElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", newGorn);
2011     if (newGornElement.isNull()) {
2012         return;
2013     }
2014 
2015 	QDomElement fzpRoot = m_fzpDocument.documentElement();
2016 	QDomElement connectors = fzpRoot.firstChildElement("connectors");
2017 	QDomElement currentConnectorElement = m_connectorList.at(m_peToolView->currentConnectorIndex());
2018     QString svgID, terminalID;
2019     if (!ViewLayer::getConnectorSvgIDs(currentConnectorElement, m_currentGraphicsView->viewID(), svgID, terminalID)) {
2020         return;
2021     }
2022 
2023     QDomElement oldGornElement = TextUtils::findElementWithAttribute(svgRoot, "id", svgID);
2024     QString oldGorn = oldGornElement.attribute("gorn");
2025     QString oldGornTerminal;
2026     if (!terminalID.isEmpty()) {
2027         QDomElement element = TextUtils::findElementWithAttribute(svgRoot, "id", terminalID);
2028         oldGornTerminal = element.attribute("gorn");
2029     }
2030 
2031     if (newGornElement.attribute("gorn").compare(oldGornElement.attribute("gorn")) == 0) {
2032         // no change
2033         return;
2034     }
2035 
2036     RelocateConnectorSvgCommand * rcsc = new RelocateConnectorSvgCommand(this, m_currentGraphicsView, svgID, terminalID, oldGorn, oldGornTerminal, newGorn, "", NULL);
2037     rcsc->setText(tr("Relocate connector %1").arg(currentConnectorElement.attribute("name")));
2038     m_undoStack->waitPush(rcsc, SketchWidget::PropChangeDelay);
2039 }
2040 
relocateConnectorSvg(SketchWidget * sketchWidget,const QString & svgID,const QString & terminalID,const QString & oldGorn,const QString & oldGornTerminal,const QString & newGorn,const QString & newGornTerminal,int changeDirection)2041 void PEMainWindow::relocateConnectorSvg(SketchWidget * sketchWidget, const QString & svgID, const QString & terminalID,
2042                 const QString & oldGorn, const QString & oldGornTerminal, const QString & newGorn, const QString & newGornTerminal,
2043                 int changeDirection)
2044 {
2045     ViewLayer::ViewID viewID = sketchWidget->viewID();
2046     ViewThing * viewThing = m_viewThings.value(viewID);
2047     QDomDocument * svgDoc = viewThing->document;
2048     QDomElement svgRoot = svgDoc->documentElement();
2049 
2050     QDomElement oldGornElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", oldGorn);
2051     QDomElement oldGornTerminalElement;
2052     if (!oldGornTerminal.isEmpty()) {
2053         oldGornTerminalElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", oldGornTerminal);
2054     }
2055     QDomElement newGornElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", newGorn);
2056     QDomElement newGornTerminalElement;
2057     if (!newGornTerminal.isEmpty()) {
2058         newGornTerminalElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", newGornTerminal);
2059     }
2060 
2061     if (!oldGornElement.isNull()) {
2062         oldGornElement.removeAttribute("id");
2063         oldGornElement.removeAttribute("oldid");            // remove oldid so removeGorn() call later doesn't restore anything by accident
2064     }
2065     if (!oldGornTerminalElement.isNull()) {
2066         oldGornTerminalElement.removeAttribute("id");
2067         oldGornTerminalElement.removeAttribute("oldid");
2068     }
2069     if (!newGornElement.isNull()) {
2070         newGornElement.setAttribute("id", svgID);
2071         newGornElement.removeAttribute("oldid");
2072     }
2073     if (!newGornTerminalElement.isNull()) {
2074         newGornTerminalElement.setAttribute("id", terminalID);
2075         newGornTerminalElement.removeAttribute("oldid");
2076     }
2077 
2078 	QDomElement fzpRoot = m_fzpDocument.documentElement();
2079 	QDomElement connectors = fzpRoot.firstChildElement("connectors");
2080     QDomElement connector = connectors.firstChildElement("connector");
2081     while (!connector.isNull()) {
2082         QString cSvgID, cTerminalID;
2083         if (ViewLayer::getConnectorSvgIDs(connector, viewID, cSvgID, cTerminalID)) {
2084             if (cSvgID == svgID) break;
2085         }
2086         connector = connector.nextSiblingElement("connector");
2087     }
2088     if (connector.isNull()) return;
2089 
2090     QDomElement p = ViewLayer::getConnectorPElement(connector, viewID);
2091     if (terminalID.isEmpty()) {
2092         p.removeAttribute("terminalId");
2093     }
2094     else {
2095         p.setAttribute("terminalId", terminalID);
2096     }
2097 
2098     // update svg in case there is a subsequent call to reload
2099 	QString newPath = m_userPartsFolderSvgPath + makeSvgPath2(sketchWidget);
2100 	QString svg = TextUtils::svgNSOnly(svgDoc->toString());
2101     writeXml(newPath, removeGorn(svg), true);
2102     setImageAttribute(fzpRoot, newPath, viewID);
2103 
2104     foreach (QGraphicsItem * item, sketchWidget->scene()->items()) {
2105         PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
2106         if (pegi == NULL) continue;
2107 
2108         QDomElement element = pegi->element();
2109         if (element.attribute("gorn").compare(newGorn) == 0) {
2110             pegi->setHighlighted(true);
2111 			pegi->showMarquee(true);
2112 			pegi->showTerminalPoint(true);
2113             switchedConnector(m_peToolView->currentConnectorIndex(), sketchWidget);
2114 
2115         }
2116         else if (element.attribute("gorn").compare(oldGorn) == 0) {
2117 			pegi->showTerminalPoint(false);
2118             pegi->showMarquee(false);
2119         }
2120     }
2121 
2122 	updateAssignedConnectors();
2123     updateChangeCount(sketchWidget, changeDirection);
2124 }
2125 
save()2126 bool PEMainWindow::save() {
2127     if (!canSave()) {
2128         return saveAs(false);
2129     }
2130 
2131     return saveAs(true);
2132 }
2133 
saveAs()2134 bool PEMainWindow::saveAs() {
2135     return saveAs(false);
2136 }
2137 
saveAs(bool overWrite)2138 bool PEMainWindow::saveAs(bool overWrite)
2139 {
2140     bool ok = false;
2141     QString prefix = QInputDialog::getText(
2142 		    this,
2143 		    tr("Filename prefix"),
2144 		    tr("Please enter a prefix to help you identify the part files. The names will have the form 'PREFIX_%1'. (It is not necessary to change the prefix, since a unique suffix is always added.)").arg(m_guid),
2145 		    QLineEdit::Normal,
2146 		    m_prefix,
2147 		    &ok
2148 	    );
2149     if (!ok || prefix.isEmpty()) return false;
2150 
2151     if (prefix != m_prefix) overWrite = false;
2152     m_prefix = prefix;
2153 
2154     QDomElement fzpRoot = m_fzpDocument.documentElement();
2155 
2156     QList<MainWindow *> affectedWindows;
2157     QList<MainWindow *> allWindows;
2158     if (overWrite) {
2159         foreach (QWidget *widget, QApplication::topLevelWidgets()) {
2160             MainWindow *mainWindow = qobject_cast<MainWindow *>(widget);
2161 		    if (mainWindow == NULL) continue;
2162 
2163 		    if (qobject_cast<PEMainWindow *>(mainWindow) != NULL) continue;
2164 
2165 			allWindows.append(mainWindow);
2166             if (mainWindow->usesPart(m_originalModuleID)) {
2167                 affectedWindows.append(mainWindow);
2168             }
2169 	    }
2170 
2171         if (affectedWindows.count() > 0 && !m_gaveSaveWarning) {
2172 	        QMessageBox messageBox(this);
2173 	        messageBox.setWindowTitle(tr("Sketch Change Warning"));
2174             QString message;
2175             if (affectedWindows.count() == 1) {
2176                 message = tr("The open sketch '%1' uses the part you are editing. ").arg(affectedWindows.first()->windowTitle());
2177                 message += tr("Saving this part will make a change to the sketch that cannot be undone.");
2178             }
2179             else {
2180                 message =  tr("The open sketches ");
2181                 for (int i = 0; i < affectedWindows.count() - 1; i++) {
2182                     message += tr("'%1', ").arg(affectedWindows.at(i)->windowTitle());
2183                 }
2184                 message += tr("and '%1' ").arg(affectedWindows.last()->windowTitle());
2185                 message += tr("Saving this part will make a change to these sketches that cannot be undone.");
2186 
2187             }
2188             message += tr("\n\nGo ahead and save?");
2189 
2190 	        messageBox.setText(message);
2191 	        messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
2192 	        messageBox.setDefaultButton(QMessageBox::Cancel);
2193 	        messageBox.setIcon(QMessageBox::Warning);
2194 	        messageBox.setWindowModality(Qt::WindowModal);
2195 	        messageBox.setButtonText(QMessageBox::Ok, tr("Save"));
2196 	        messageBox.setButtonText(QMessageBox::Cancel, tr("Cancel"));
2197 	        QMessageBox::StandardButton answer = (QMessageBox::StandardButton) messageBox.exec();
2198 
2199 	        if (answer != QMessageBox::Ok) {
2200 		        return false;
2201 	        }
2202 
2203             m_gaveSaveWarning = true;
2204         }
2205     }
2206 
2207     QDomElement views = fzpRoot.firstChildElement("views");
2208 
2209     QHash<ViewLayer::ViewID, QString> svgPaths;
2210 
2211     foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) {
2212 		ViewThing * viewThing = m_viewThings.value(viewID);
2213         QDomElement view = views.firstChildElement(ViewLayer::viewIDXmlName(viewID));
2214         QDomElement layers = view.firstChildElement("layers");
2215 
2216         QString currentSvgPath = layers.attribute("image");
2217         svgPaths.insert(viewID, currentSvgPath);
2218 
2219 		QMultiHash<ViewLayer::ViewLayerID, QString> extraSvg;
2220         QDomDocument writeDoc = viewThing->document->cloneNode(true).toDocument();
2221         QDomElement svgRoot = writeDoc.documentElement();
2222         double svgWidth, svgHeight, vbWidth, vbHeight;
2223         bool svgOK = TextUtils::getSvgSizes(writeDoc, svgWidth, svgHeight, vbWidth, vbHeight);
2224         if (svgOK) {
2225             QHash<QString, QString> svgHash;
2226             foreach (QGraphicsItem * item, viewThing->sketchWidget->scene()->items()) {
2227                 ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
2228                 if (itemBase == NULL) continue;
2229 
2230                 if (itemBase->layerKinChief() == viewThing->itemBase) continue;
2231 
2232                 double factor;
2233 			    QString string = itemBase->layerKinChief()->retrieveSvg(itemBase->viewLayerID(), svgHash, false, vbWidth / svgWidth, factor);
2234 			    if (!string.isEmpty()) {
2235                     QPointF d = itemBase->pos() - viewThing->itemBase->pos();
2236                     double dx = d.x() * (vbWidth / svgWidth) / GraphicsUtils::SVGDPI;
2237                     double dy = d.y() * (vbWidth / svgWidth) / GraphicsUtils::SVGDPI;
2238                     string = QString("<g transform='translate(%1,%2)'>\n%3\n</g>\n").arg(dx).arg(dy).arg(string);
2239 				    extraSvg.insert(itemBase->viewLayerID(), string);
2240 			    }
2241             }
2242         }
2243 
2244 
2245         // deal with copper1 and copper0:
2246         //      find innermost
2247         //      don't save twice
2248         QDomElement copperChild;
2249         LayerList sketchLayers = viewThing->sketchWidget->viewLayers().keys();
2250         if (sketchLayers.contains(ViewLayer::Copper0) && sketchLayers.contains(ViewLayer::Copper1)) {
2251 		    QDomElement copper0 = TextUtils::findElementWithAttribute(svgRoot, "id", "copper0");
2252 		    QDomElement copper1 = TextUtils::findElementWithAttribute(svgRoot, "id", "copper1");
2253 		    if (!copper0.isNull() && !copper1.isNull()) {
2254 			    QDomElement parent0 = copper0.parentNode().toElement();
2255 			    QDomElement parent1 = copper1.parentNode().toElement();
2256 			    if (parent0.attribute("id") == "copper1") {
2257                     copperChild = copper0;
2258                 }
2259 			    else if (parent1.attribute("id") == "copper0") {
2260                     copperChild = copper1;
2261                 }
2262             }
2263             if (!copperChild.isNull()) {
2264                 if (extraSvg.uniqueKeys().contains(ViewLayer::Copper0)) {
2265                     extraSvg.remove(ViewLayer::Copper1);
2266                 }
2267             }
2268         }
2269 
2270         foreach (ViewLayer::ViewLayerID viewLayerID, extraSvg.uniqueKeys()) {
2271             QStringList svgList = extraSvg.values(viewLayerID);
2272             QDomElement svgViewElement = (viewLayerID == ViewLayer::Copper0 && !copperChild.isNull())
2273                         ? copperChild
2274                         : TextUtils::findElementWithAttribute(svgRoot, "id", ViewLayer::viewLayerXmlNameFromID(viewLayerID));
2275             if (svgViewElement.isNull()) {
2276                 svgViewElement = svgRoot;
2277             }
2278 
2279             foreach (QString svg, svgList) {
2280                 QDomDocument doc;
2281                 doc.setContent(svg, true);
2282                 QDomElement root = doc.documentElement();
2283                 foreach (ViewLayer::ViewLayerID vlid, sketchLayers) {
2284                     removeID(root, ViewLayer::viewLayerXmlNameFromID(vlid));
2285                 }
2286                 svgViewElement.appendChild(doc.documentElement());
2287             }
2288         }
2289 
2290         QString svg = writeDoc.toString();
2291         insertDesc(viewThing->referenceFile, svg);
2292 
2293         QString svgPath = makeSvgPath2(viewThing->sketchWidget);
2294 		setImageAttribute(layers, svgPath);
2295 
2296         QString actualPath = m_userPartsFolderSvgPath + svgPath;
2297         bool result = writeXml(actualPath, removeGorn(svg), false);
2298         if (!result) {
2299             // TODO: warn user
2300         }
2301     }
2302 
2303     QDir dir(m_userPartsFolderPath);
2304 	QString suffix = QString("%1_%2_%3").arg(m_prefix).arg(m_guid).arg(m_fileIndex++);
2305     QString fzpPath = dir.absoluteFilePath(QString("%1.fzp").arg(suffix));
2306 
2307     if (overWrite) {
2308         fzpPath = m_originalFzpPath;
2309     }
2310 	else {
2311 		fzpRoot.setAttribute("moduleId", suffix);
2312 		QString family = m_metadataView->family();
2313 		QString variant = m_metadataView->variant();
2314 		QHash<QString, QString> variants = m_referenceModel->allPropValues(family, "variant");
2315 		QStringList values = variants.values(variant);
2316 		if (values.count() > 0) {
2317 			QString newVariant = makeNewVariant(family);
2318 			QDomElement properties = fzpRoot.firstChildElement("properties");
2319 			replaceProperty("variant", newVariant, properties);
2320 			m_metadataView->resetProperty("variant", newVariant);
2321 		}
2322 	}
2323 
2324     bool result = writeXml(fzpPath, m_fzpDocument.toString(), false);
2325 
2326 	if (!overWrite) {
2327 		m_originalFzpPath = fzpPath;
2328 		m_canSave = true;
2329 		m_originalModuleID = fzpRoot.attribute("moduleId");
2330 	}
2331 
2332     // restore the set of working svg files
2333     foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) {
2334         QString svgPath = svgPaths.value(viewID);
2335         if (svgPath.isEmpty()) continue;
2336 
2337         QDomElement view = views.firstChildElement(ViewLayer::viewIDXmlName(viewID));
2338         QDomElement layers = view.firstChildElement("layers");
2339 		setImageAttribute(layers, svgPath);
2340     }
2341 
2342     ModelPart * modelPart = m_referenceModel->retrieveModelPart(m_originalModuleID);
2343     if (modelPart == NULL) {
2344 	    modelPart = m_referenceModel->loadPart(fzpPath, true);
2345         emit addToMyPartsSignal(modelPart);
2346 	}
2347     else {
2348         m_referenceModel->reloadPart(fzpPath, m_originalModuleID);
2349         WaitPushUndoStack undoStack;
2350         QUndoCommand * parentCommand = new QUndoCommand;
2351         foreach (MainWindow * mainWindow, affectedWindows) {
2352             mainWindow->updateParts(m_originalModuleID, parentCommand);
2353         }
2354         foreach (MainWindow * mainWindow, allWindows) {
2355             mainWindow->updatePartsBin(m_originalModuleID);
2356         }
2357         undoStack.push(parentCommand);
2358     }
2359 
2360 	m_autosaveNeeded = false;
2361     m_undoStack->setClean();
2362 
2363     return result;
2364 }
2365 
2366 
updateChangeCount(SketchWidget * sketchWidget,int changeDirection)2367 void PEMainWindow::updateChangeCount(SketchWidget * sketchWidget, int changeDirection) {
2368 	ViewThing * viewThing = m_viewThings.value(sketchWidget->viewID());
2369     viewThing->svgChangeCount += changeDirection;
2370 }
2371 
findConnectorItem()2372 PEGraphicsItem * PEMainWindow::findConnectorItem()
2373 {
2374     if (m_currentGraphicsView == NULL) return NULL;
2375 
2376     QDomElement connector = m_connectorList.at(m_peToolView->currentConnectorIndex());
2377     if (connector.isNull()) return NULL;
2378 
2379 	QString svgID, terminalID;
2380     bool ok = ViewLayer::getConnectorSvgIDs(connector, m_currentGraphicsView->viewID(), svgID, terminalID);
2381     if (!ok) return NULL;
2382 
2383     QList<PEGraphicsItem *> pegiList = getPegiList(m_currentGraphicsView);
2384     foreach (PEGraphicsItem * pegi, pegiList) {
2385         if (pegi->element().attribute("id") == svgID) return pegi;
2386     }
2387 
2388     return NULL;
2389 }
2390 
terminalPointChanged(const QString & how)2391 void PEMainWindow::terminalPointChanged(const QString & how) {
2392     PEGraphicsItem * pegi = findConnectorItem();
2393     if (pegi == NULL) return;
2394 
2395     QRectF r = pegi->rect();
2396     QPointF p = r.center();
2397     if (how == "center") {
2398     }
2399     else if (how == "N") {
2400         p.setY(0);
2401     }
2402     else if (how == "E") {
2403         p.setX(r.width());
2404     }
2405     else if (how == "S") {
2406         p.setY(r.height());
2407     }
2408     else if (how == "W") {
2409         p.setX(0);
2410     }
2411     terminalPointChangedAux(pegi, pegi->terminalPoint(), p);
2412 
2413     // TODO: UndoCommand which changes fzp xml and svg xml
2414 }
2415 
terminalPointChanged(const QString & coord,double value)2416 void PEMainWindow::terminalPointChanged(const QString & coord, double value)
2417 {
2418     PEGraphicsItem * pegi = findConnectorItem();
2419     if (pegi == NULL) return;
2420 
2421     QPointF p = pegi->terminalPoint();
2422     if (coord == "x") {
2423         p.setX(qMax(0.0, qMin(value, pegi->rect().width())));
2424     }
2425     else {
2426         p.setY(qMax(0.0, qMin(value, pegi->rect().height())));
2427     }
2428 
2429     terminalPointChangedAux(pegi, pegi->terminalPoint(), p);
2430 }
2431 
terminalPointChangedAux(PEGraphicsItem * pegi,QPointF before,QPointF after)2432 void PEMainWindow::terminalPointChangedAux(PEGraphicsItem * pegi, QPointF before, QPointF after)
2433 {
2434     if (pegi->pendingTerminalPoint() == after) {
2435         return;
2436     }
2437 
2438     pegi->setPendingTerminalPoint(after);
2439 
2440     QDomElement currentConnectorElement = m_connectorList.at(m_peToolView->currentConnectorIndex());
2441 
2442     MoveTerminalPointCommand * mtpc = new MoveTerminalPointCommand(this, this->m_currentGraphicsView, currentConnectorElement.attribute("id"), pegi->rect().size(), before, after, NULL);
2443     mtpc->setText(tr("Move terminal point"));
2444     m_undoStack->waitPush(mtpc, SketchWidget::PropChangeDelay);
2445 }
2446 
getSpinAmount(double & amount)2447 void PEMainWindow::getSpinAmount(double & amount) {
2448     double zoom = m_currentGraphicsView->currentZoom() / 100;
2449     if (zoom == 0) {
2450         amount = 1;
2451         return;
2452     }
2453 
2454     amount = qMin(1.0, 1.0 / zoom);
2455     if (QApplication::keyboardModifiers() & Qt::ShiftModifier) {
2456         amount *= 10;
2457     }
2458 }
2459 
moveTerminalPoint(SketchWidget * sketchWidget,const QString & connectorID,QSizeF size,QPointF p,int changeDirection)2460 void PEMainWindow::moveTerminalPoint(SketchWidget * sketchWidget, const QString & connectorID, QSizeF size, QPointF p, int changeDirection)
2461 {
2462     // TODO: assumes these IDs are unique--need to check if they are not
2463 
2464     QPointF center(size.width() / 2, size.height() / 2);
2465     bool centered = qAbs(center.x() - p.x()) < .05 && qAbs(center.y() - p.y()) < .05;
2466 
2467     QDomElement fzpRoot = m_fzpDocument.documentElement();
2468     QDomElement connectorElement = TextUtils::findElementWithAttribute(fzpRoot, "id", connectorID);
2469     if (connectorElement.isNull()) {
2470         DebugDialog::debug(QString("missing connector %1").arg(connectorID));
2471         return;
2472     }
2473 
2474     QDomElement pElement = ViewLayer::getConnectorPElement(connectorElement, sketchWidget->viewID());
2475     QString svgID = pElement.attribute("svgId");
2476     if (svgID.isEmpty()) {
2477         DebugDialog::debug(QString("Can't find svgId for connector %1").arg(connectorID));
2478         return;
2479     }
2480 
2481     PEGraphicsItem * connectorPegi = NULL;
2482 	QList<PEGraphicsItem *> pegiList = getPegiList(sketchWidget);
2483     foreach (PEGraphicsItem * pegi, pegiList) {
2484         QDomElement pegiElement = pegi->element();
2485         if (pegiElement.attribute("id").compare(svgID) == 0) {
2486             connectorPegi = pegi;
2487         }
2488     }
2489     if (connectorPegi == NULL) return;
2490 
2491     if (centered) {
2492         pElement.removeAttribute("terminalId");
2493         // no need to change SVG in this case
2494     }
2495     else {
2496 		ViewThing * viewThing = m_viewThings.value(sketchWidget->viewID());
2497         QDomDocument * svgDoc = viewThing->document;
2498         QDomElement svgRoot = svgDoc->documentElement();
2499         QDomElement svgConnectorElement = TextUtils::findElementWithAttribute(svgRoot, "id", svgID);
2500         if (svgConnectorElement.isNull()) {
2501             DebugDialog::debug(QString("Unable to find svg connector element %1").arg(svgID));
2502             return;
2503         }
2504 
2505         QString terminalID = pElement.attribute("terminalId");
2506         if (terminalID.isEmpty()) {
2507             terminalID = svgID;
2508             if (terminalID.endsWith("pin") || terminalID.endsWith("pad")) {
2509                 terminalID.chop(3);
2510             }
2511             terminalID += "terminal";
2512             pElement.setAttribute("terminalId", terminalID);
2513         }
2514 
2515         FSvgRenderer renderer;
2516         renderer.loadSvg(svgDoc->toByteArray(), "", false);
2517         QRectF svgBounds = renderer.boundsOnElement(svgID);
2518         double cx = p.x () * svgBounds.width() / size.width();
2519         double cy = p.y() * svgBounds.height() / size.height();
2520         double dx = svgBounds.width() / 1000;
2521         double dy = svgBounds.height() / 1000;
2522 
2523         QDomElement terminalElement = TextUtils::findElementWithAttribute(svgRoot, "id", terminalID);
2524         if (terminalElement.isNull()) {
2525             terminalElement = svgDoc->createElement("rect");
2526         }
2527         else if (terminalElement.tagName() != "rect" || terminalElement.attribute("fill") != "none" || terminalElement.attribute("stroke") != "none") {
2528             terminalElement.setAttribute("id", "");
2529             terminalElement = svgDoc->createElement("rect");
2530         }
2531         terminalElement.setAttribute("id", terminalID);
2532         terminalElement.setAttribute("oldid", terminalID);
2533         terminalElement.setAttribute("stroke", "none");
2534         terminalElement.setAttribute("fill", "none");
2535         terminalElement.setAttribute("stroke-width", "0");
2536         terminalElement.setAttribute("x", QString::number(svgBounds.left() + cx - dx));
2537         terminalElement.setAttribute("y", QString::number(svgBounds.top() + cy - dy));
2538         terminalElement.setAttribute("width", QString::number(dx * 2));
2539         terminalElement.setAttribute("height", QString::number(dy * 2));
2540         if (terminalElement.attribute("gorn").isEmpty()) {
2541             QString gorn = svgConnectorElement.attribute("gorn");
2542             int ix = gorn.lastIndexOf(".");
2543             if (ix > 0) {
2544                 gorn.truncate(ix);
2545             }
2546             gorn = QString("%1.gen%2").arg(gorn).arg(FakeGornSiblingNumber++);
2547             terminalElement.setAttribute("gorn", gorn);
2548         }
2549 
2550         svgConnectorElement.parentNode().insertAfter(terminalElement, svgConnectorElement);
2551 
2552         double oldZ = connectorPegi->zValue() + 1;
2553         foreach (PEGraphicsItem * pegi, pegiList) {
2554             QDomElement pegiElement = pegi->element();
2555             if (pegiElement.attribute("id").compare(terminalID) == 0) {
2556                 DebugDialog::debug("old pegi location", pegi->pos());
2557                 oldZ = pegi->zValue();
2558                 pegiList.removeOne(pegi);
2559                 delete pegi;
2560                 break;
2561             }
2562         }
2563 
2564         // update svg in case there is a subsequent call to reload
2565 	    QString newPath = m_userPartsFolderSvgPath + makeSvgPath2(sketchWidget);
2566 	    QString svg = TextUtils::svgNSOnly(svgDoc->toString());
2567         writeXml(newPath, removeGorn(svg), true);
2568         setImageAttribute(fzpRoot, newPath, sketchWidget->viewID());
2569 
2570         double invdx = dx * size.width() / svgBounds.width();
2571         double invdy = dy * size.height() / svgBounds.height();
2572         QPointF topLeft = connectorPegi->offset() + p - QPointF(invdx, invdy);
2573         PEGraphicsItem * pegi = makePegi(QSizeF(invdx * 2, invdy * 2), topLeft, viewThing->itemBase, terminalElement, oldZ);
2574         DebugDialog::debug("new pegi location", pegi->pos());
2575         updateChangeCount(sketchWidget, changeDirection);
2576     }
2577 
2578     connectorPegi->setTerminalPoint(p);
2579     connectorPegi->update();
2580     m_peToolView->setTerminalPointCoords(p);
2581 }
2582 
showInOS(QWidget * parent,const QString & pathIn)2583 void PEMainWindow::showInOS(QWidget *parent, const QString &pathIn)
2584 {
2585     Q_UNUSED(parent);
2586     FolderUtils::showInFolder(pathIn);
2587     QClipboard *clipboard = QApplication::clipboard();
2588 	if (clipboard != NULL) {
2589 		clipboard->setText(pathIn);
2590 	}
2591 }
2592 
showInOS()2593 void PEMainWindow::showInOS() {
2594     if (m_currentGraphicsView == NULL) return;
2595 
2596 	ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID());
2597     showInOS(this, viewThing->itemBase->filename());
2598 }
2599 
makePegi(QSizeF size,QPointF topLeft,ItemBase * itemBase,QDomElement & element,double z)2600 PEGraphicsItem * PEMainWindow::makePegi(QSizeF size, QPointF topLeft, ItemBase * itemBase, QDomElement & element, double z)
2601 {
2602     PEGraphicsItem * pegiItem = new PEGraphicsItem(0, 0, size.width(), size.height(), itemBase);
2603 	pegiItem->showTerminalPoint(false);
2604     pegiItem->setPos(itemBase->pos() + topLeft);
2605     pegiItem->setZValue(z);
2606     itemBase->scene()->addItem(pegiItem);
2607     pegiItem->setElement(element);
2608     pegiItem->setOffset(topLeft);
2609     connect(pegiItem, SIGNAL(highlightSignal(PEGraphicsItem *)), this, SLOT(highlightSlot(PEGraphicsItem *)));
2610     connect(pegiItem, SIGNAL(mouseReleasedSignal(PEGraphicsItem *)), this, SLOT(pegiMouseReleased(PEGraphicsItem *)));
2611     connect(pegiItem, SIGNAL(mousePressedSignal(PEGraphicsItem *, bool &)), this, SLOT(pegiMousePressed(PEGraphicsItem *, bool &)), Qt::DirectConnection);
2612     connect(pegiItem, SIGNAL(terminalPointMoved(PEGraphicsItem *, QPointF)), this, SLOT(pegiTerminalPointMoved(PEGraphicsItem *, QPointF)));
2613     connect(pegiItem, SIGNAL(terminalPointChanged(PEGraphicsItem *, QPointF, QPointF)), this, SLOT(pegiTerminalPointChanged(PEGraphicsItem *, QPointF, QPointF)));
2614     return pegiItem;
2615 }
2616 
getPixelBounds(FSvgRenderer & renderer,QDomElement & element)2617 QRectF PEMainWindow::getPixelBounds(FSvgRenderer & renderer, QDomElement & element)
2618 {
2619 	QSizeF defaultSizeF = renderer.defaultSizeF();
2620 	QRectF viewBox = renderer.viewBoxF();
2621 
2622     QString id = element.attribute("id");
2623     QRectF r = renderer.boundsOnElement(id);
2624     QMatrix matrix = renderer.matrixForElement(id);
2625     QString oldid = element.attribute("oldid");
2626     if (!oldid.isEmpty()) {
2627         element.setAttribute("id", oldid);
2628         element.removeAttribute("oldid");
2629     }
2630     QRectF bounds = matrix.mapRect(r);
2631 	bounds.setRect(bounds.x() * defaultSizeF.width() / viewBox.width(),
2632 						bounds.y() * defaultSizeF.height() / viewBox.height(),
2633 						bounds.width() * defaultSizeF.width() / viewBox.width(),
2634 						bounds.height() * defaultSizeF.height() / viewBox.height());
2635     return bounds;
2636 }
2637 
canSave()2638 bool PEMainWindow::canSave() {
2639 
2640     return m_canSave;
2641 }
2642 
tabWidget_currentChanged(int index)2643 void PEMainWindow::tabWidget_currentChanged(int index) {
2644     MainWindow::tabWidget_currentChanged(index);
2645 
2646     if (m_peToolView == NULL) return;
2647 
2648     switchedConnector(m_peToolView->currentConnectorIndex());
2649 
2650     bool enabled = index < IconViewIndex;
2651 	m_peSvgView->setChildrenVisible(enabled);
2652 	m_peToolView->setChildrenVisible(enabled);
2653 
2654     if (m_currentGraphicsView == NULL) {
2655         // update title when switching to connector and metadata view
2656         setTitle();
2657     }
2658     else {
2659     }
2660 
2661 	updateAssignedConnectors();
2662 	updateActiveLayerButtons();
2663 }
2664 
backupSketch()2665 void PEMainWindow::backupSketch()
2666 {
2667 }
2668 
removedConnector(const QDomElement & element)2669 void PEMainWindow::removedConnector(const QDomElement & element)
2670 {
2671     QList<QDomElement> connectors;
2672     connectors.append(element);
2673     removedConnectorsAux(connectors);
2674 }
2675 
removedConnectors(QList<ConnectorMetadata * > & cmdList)2676 void PEMainWindow::removedConnectors(QList<ConnectorMetadata *> & cmdList)
2677 {
2678     QList<QDomElement> connectors;
2679 
2680     foreach (ConnectorMetadata * cmd, cmdList) {
2681         int index;
2682         QDomElement connector = findConnector(cmd->connectorID, index);
2683         if (connector.isNull()) return;
2684 
2685         cmd->index = index;
2686         connectors.append(connector);
2687     }
2688 
2689     removedConnectorsAux(connectors);
2690 }
2691 
removedConnectorsAux(QList<QDomElement> & connectors)2692 void PEMainWindow::removedConnectorsAux(QList<QDomElement> & connectors)
2693 {
2694     QString originalPath = saveFzp();
2695 
2696     foreach (QDomElement connector, connectors) {
2697         if (m_removedConnector.isEmpty()) {
2698             QTextStream stream(&m_removedConnector);
2699             connector.save(stream, 0);
2700         }
2701         connector.parentNode().removeChild(connector);
2702     }
2703 
2704     QString newPath = saveFzp();
2705 
2706     ChangeFzpCommand * cfc = new ChangeFzpCommand(this, originalPath, newPath, NULL);
2707     QString message;
2708     if (connectors.count() == 1) {
2709         message = tr("Remove connector");
2710     }
2711     else {
2712         message = tr("Remove %1 connectors").arg(connectors.count());
2713     }
2714     cfc->setText(message);
2715     m_undoStack->waitPush(cfc, SketchWidget::PropChangeDelay);
2716 }
2717 
restoreFzp(const QString & fzpPath)2718 void PEMainWindow::restoreFzp(const QString & fzpPath)
2719 {
2720     if (!loadFzp(fzpPath)) return;
2721 
2722     reload(false);
2723 }
2724 
setBeforeClosingText(const QString & filename,QMessageBox & messageBox)2725 void PEMainWindow::setBeforeClosingText(const QString & filename, QMessageBox & messageBox)
2726 {
2727     Q_UNUSED(filename);
2728 
2729     QString partTitle = getPartTitle();
2730     messageBox.setWindowTitle(tr("Save \"%1\"").arg(partTitle));
2731     messageBox.setText(tr("Do you want to save the changes you made in the part \"%1\"?").arg(partTitle));
2732     messageBox.setInformativeText(tr("Your changes will be lost if you don't save them."));
2733 }
2734 
getPartTitle()2735 QString PEMainWindow::getPartTitle() {
2736     QString partTitle = tr("untitled part");
2737     QDomElement root = m_fzpDocument.documentElement();
2738     QDomElement title = root.firstChildElement("title");
2739     QString candidate = title.text();
2740     if (!candidate.isEmpty()) return candidate;
2741 
2742     if (m_viewThings.count() > 0) {
2743 		ViewThing * viewThing = m_viewThings.values().at(0);
2744 		if (viewThing->itemBase) {
2745 			candidate = viewThing->itemBase->title();
2746 			if (!candidate.isEmpty()) return candidate;
2747 		}
2748     }
2749 
2750     return partTitle;
2751 }
2752 
killPegi()2753 void PEMainWindow::killPegi() {
2754     foreach (ViewThing * viewThing, m_viewThings.values()) {
2755         if (viewThing->sketchWidget == NULL) continue;
2756 
2757         foreach (QGraphicsItem * item, viewThing->sketchWidget->scene()->items()) {
2758             PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
2759             if (pegi) delete pegi;
2760         }
2761     }
2762 }
2763 
loadFzp(const QString & path)2764 bool PEMainWindow::loadFzp(const QString & path) {
2765     QFile file(path);
2766 	QString errorStr;
2767 	int errorLine;
2768 	int errorColumn;
2769 	bool result = m_fzpDocument.setContent(&file, &errorStr, &errorLine, &errorColumn);
2770 	if (!result) {
2771         QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to load fzp from %1").arg(path));
2772 		return false;
2773 	}
2774 
2775     return true;
2776 }
2777 
connectorCountChanged(int newCount)2778 void PEMainWindow::connectorCountChanged(int newCount) {
2779     QList<QDomElement> connectorList;
2780     QDomElement root = m_fzpDocument.documentElement();
2781     QDomElement connectors = root.firstChildElement("connectors");
2782     QDomElement connector = connectors.firstChildElement("connector");
2783     while (!connector.isNull()) {
2784         connectorList.append(connector);
2785         connector = connector.nextSiblingElement();
2786     }
2787 
2788     if (newCount == connectorList.count()) return;
2789 
2790     if (newCount < connectorList.count()) {
2791         qSort(connectorList.begin(), connectorList.end(), byID);
2792         QList<QDomElement> toDelete;
2793         for (int i = newCount; i < connectorList.count(); i++) {
2794             toDelete.append(connectorList.at(i));
2795         }
2796 
2797         removedConnectorsAux(toDelete);
2798         return;
2799     }
2800 
2801     // add connectors
2802     int id = 0;
2803     foreach (QDomElement connector, connectorList) {
2804     	int ix = IntegerFinder.indexIn(connector.attribute("id"));
2805         if (ix >= 0) {
2806             int candidate = IntegerFinder.cap(0).toInt();
2807             if (candidate > id) id = candidate;
2808         }
2809         // sometimes id = 0 but name = 1, and we are now using name = id
2810         ix = IntegerFinder.indexIn(connector.attribute("name"));
2811         if (ix >= 0) {
2812             int candidate = IntegerFinder.cap(0).toInt();
2813             if (candidate > id) id = candidate;
2814         }
2815     }
2816 
2817     QString originalPath = saveFzp();
2818 
2819 	QDomElement connectorModel;
2820     QDomDocument tempDoc;
2821     if (connectorList.count() > 0) connectorModel = connectorList.at(0);
2822     else {
2823         tempDoc.setContent(m_removedConnector);
2824         connectorModel = tempDoc.documentElement();
2825         if (connectorModel.isNull()) {
2826             QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to create new connector--you may have to start over."));
2827 		    return;
2828         }
2829     }
2830     for (int i = connectorList.count(); i < newCount; i++) {
2831         id++;
2832         QDomElement element = connectorModel.cloneNode(true).toElement();
2833         connectors.appendChild(element);
2834         QString newName = QString("pin %1").arg(id);
2835         element.setAttribute("name", newName);
2836 		QString cid = QString("connector%1").arg(id);
2837         element.setAttribute("id", cid);
2838 		QString svgid = cid + "pin";
2839 		QDomNodeList nodeList = element.elementsByTagName("p");
2840 		for (int n = 0; n < nodeList.count(); n++) {
2841 			QDomElement p = nodeList.at(n).toElement();
2842 			p.removeAttribute("terminalId");
2843 			p.removeAttribute("legId");
2844 			p.setAttribute("svgId", svgid);
2845 		}
2846         QDomElement description = element.firstChildElement("description");
2847         if (description.isNull()) {
2848             description = m_fzpDocument.createElement("description");
2849             element.appendChild(description);
2850         }
2851         TextUtils::replaceChildText(description, newName);
2852     }
2853 
2854     QString newPath = saveFzp();
2855 
2856     ChangeFzpCommand * cfc = new ChangeFzpCommand(this, originalPath, newPath, NULL);
2857     QString message;
2858     if (newCount - connectorList.count() == 1) {
2859         message = tr("Add connector");
2860     }
2861     else {
2862         message = tr("Add %1 connectors").arg(newCount - connectorList.count());
2863     }
2864     cfc->setText(message);
2865     m_undoStack->waitPush(cfc, SketchWidget::PropChangeDelay);
2866 }
2867 
editsModuleID(const QString & moduleID)2868 bool PEMainWindow::editsModuleID(const QString & moduleID) {
2869     // only to detect whether a user tries to open the parts editor on the same part twice
2870     return (m_originalModuleID.compare(moduleID) == 0);
2871 }
2872 
2873 /*
2874 QString PEMainWindow::getFzpReferenceFile() {
2875     QString referenceFile = m_fzpDocument.documentElement().attribute(ReferenceFileString);
2876     if (!referenceFile.isEmpty()) referenceFile += "_";
2877     return referenceFile;
2878 }
2879 */
2880 
getSvgReferenceFile(const QString & filename)2881 QString PEMainWindow::getSvgReferenceFile(const QString & filename) {
2882     QFileInfo info(filename);
2883     QString referenceFile = info.fileName();
2884 
2885     QFile file(filename);
2886     if (!file.open(QFile::ReadOnly)) return referenceFile;
2887 
2888     QString svg = file.readAll();
2889     if (!svg.contains(ReferenceFileString)) return referenceFile;
2890 
2891     QXmlStreamReader xml(svg.toUtf8());
2892 	xml.setNamespaceProcessing(false);
2893     bool inDesc = false;
2894 	while (!xml.atEnd()) {
2895         switch (xml.readNext()) {
2896             case QXmlStreamReader::StartElement:
2897                 {
2898                     QString name = xml.name().toString();
2899 			        if (inDesc && name.compare(ReferenceFileString) == 0) {
2900 				        QString candidate = xml.readElementText().trimmed();
2901 				        if (candidate.isEmpty()) return referenceFile;
2902                         return candidate;
2903 			        }
2904 			        if (name.compare("desc") == 0) {
2905 				        inDesc = true;
2906 			        }
2907                 }
2908 			    break;
2909             case QXmlStreamReader::EndElement:
2910                 {
2911                     QString name = xml.name().toString();
2912 			        if (name.compare("desc") == 0) {
2913 				        inDesc = false;
2914 			        }
2915                 }
2916 			    break;
2917 
2918 		    default:
2919 			    break;
2920 		}
2921 	}
2922 
2923     return referenceFile;
2924 }
2925 
makeDesc(const QString & referenceFile)2926 QString PEMainWindow::makeDesc(const QString & referenceFile)
2927 {
2928     return QString("\n<desc><%2>%1</%2></desc>\n").arg(referenceFile).arg(ReferenceFileString);
2929 }
2930 
updateWindowMenu()2931 void PEMainWindow::updateWindowMenu() {
2932 }
2933 
updateRaiseWindowAction()2934 void PEMainWindow::updateRaiseWindowAction() {
2935     QString title = tr("Fritzing (New) Parts Editor");
2936     QString partTitle = getPartTitle();
2937 	QString actionText = QString("%1: %2").arg(title).arg(partTitle);
2938 	m_raiseWindowAct->setText(actionText);
2939 	m_raiseWindowAct->setToolTip(actionText);
2940 	m_raiseWindowAct->setStatusTip("raise \""+actionText+"\" window");
2941 }
2942 
writeXml(const QString & path,const QString & xml,bool temp)2943 bool PEMainWindow::writeXml(const QString & path, const QString & xml, bool temp)
2944 {
2945 	bool result = TextUtils::writeUtf8(path, TextUtils::svgNSOnly(xml));
2946 	if (result) {
2947 		if (temp) m_filesToDelete.append(path);
2948 		else m_filesToDelete.removeAll(path);
2949 	}
2950 
2951 	return result;
2952 }
2953 
displayBuses()2954 void PEMainWindow::displayBuses() {
2955 	deleteBuses();
2956 
2957 	QDomElement root = m_fzpDocument.documentElement();
2958 	QDomElement buses = root.firstChildElement("buses");
2959 	QDomElement bus = buses.firstChildElement("bus");
2960 	while (!bus.isNull()) {
2961 		QDomElement nodeMember = bus.firstChildElement("nodeMember");
2962 		QSet<QString> connectorIDs;
2963 		while (!nodeMember.isNull()) {
2964 			QString connectorID = nodeMember.attribute("connectorId");
2965 			if (!connectorID.isEmpty()) {
2966 				connectorIDs.insert(connectorID);
2967 			}
2968 			nodeMember = nodeMember.nextSiblingElement("nodeMember");
2969 		}
2970 
2971 		foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) {
2972 			ViewThing * viewThing = m_viewThings.value(viewID);
2973 			QList<ConnectorItem *> connectorItems;
2974 			foreach (QString connectorID, connectorIDs) {
2975 				ConnectorItem * connectorItem = viewThing->itemBase->findConnectorItemWithSharedID(connectorID, viewThing->itemBase->viewLayerPlacement());
2976 				if (connectorItem) connectorItems.append(connectorItem);
2977 			}
2978 			for (int i = 0; i < connectorItems.count() - 1; i++) {
2979 				ConnectorItem * c1 = connectorItems.at(i);
2980 				ConnectorItem * c2 = connectorItems.at(i + 1);
2981 				Wire * wire = viewThing->sketchWidget->makeOneRatsnestWire(c1, c2, false, QColor(0, 255, 0), true);
2982 				wire->setZValue(RatZ);
2983 			}
2984 		}
2985 
2986 		bus = bus.nextSiblingElement("bus");
2987 	}
2988 }
2989 
updateWireMenu()2990 void PEMainWindow::updateWireMenu() {
2991 	// assumes update wire menu is only called when right-clicking a wire
2992 	// and that wire is cached by the menu in Wire::mousePressEvent
2993 
2994 	Wire * wire = m_activeWire;
2995 	m_activeWire = NULL;
2996 
2997 	m_deleteBusConnectionAct->setWire(wire);
2998 	m_deleteBusConnectionAct->setEnabled(true);
2999 }
3000 
deleteBusConnection()3001 void PEMainWindow::deleteBusConnection() {
3002 	WireAction * wireAction = qobject_cast<WireAction *>(sender());
3003 	if (wireAction == NULL) return;
3004 
3005 	Wire * wire = wireAction->wire();
3006 	if (wire == NULL) return;
3007 
3008 	QList<Wire *> wires;
3009 	QList<ConnectorItem *> ends;
3010 	wire->collectChained(wires, ends);
3011 	if (ends.count() != 2) return;
3012 
3013 	QString id0 = ends.at(0)->connectorSharedID();
3014 	QString id1 = ends.at(1)->connectorSharedID();
3015 
3016 	QDomElement root = m_fzpDocument.documentElement();
3017 	QDomElement buses = root.firstChildElement("buses");
3018 	QDomElement bus = buses.firstChildElement("bus");
3019 	QDomElement nodeMember0;
3020 	QDomElement nodeMember1;
3021 	while (!bus.isNull()) {
3022 		nodeMember0 = TextUtils::findElementWithAttribute(bus, "connectorId", id0);
3023 		nodeMember1 = TextUtils::findElementWithAttribute(bus, "connectorId", id1);
3024 		if (!nodeMember0.isNull() && !nodeMember1.isNull()) break;
3025 
3026 		bus = bus.nextSiblingElement("bus");
3027 	}
3028 
3029 	QString busID = bus.attribute("id");
3030 
3031 	if (nodeMember0.isNull() || nodeMember1.isNull()) {
3032 		QMessageBox::critical(NULL, tr("Parts Editor"), tr("Internal connections are very messed up."));
3033 		return;
3034 	}
3035 
3036 	QUndoCommand * parentCommand = new QUndoCommand();
3037 	QStringList names;
3038 	names << ends.at(0)->connectorSharedName() << ends.at(1)->connectorSharedName() ;
3039 	new RemoveBusConnectorCommand(this, busID, id0, false, parentCommand);
3040 	new RemoveBusConnectorCommand(this, busID, id1, false, parentCommand);
3041 	if (ends.at(0)->connectedToItems().count() > 1) {
3042 		// restore it
3043 		names.removeAt(0);
3044 		new RemoveBusConnectorCommand(this, busID, id0, true, parentCommand);
3045 	}
3046 	if (ends.at(1)->connectedToItems().count() > 1) {
3047 		// restore it
3048 		new RemoveBusConnectorCommand(this, busID, id1, true, parentCommand);
3049 	}
3050 
3051 	parentCommand->setText(tr("Remove internal connection from '%1'").arg(names.at(0)));
3052 	m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay);
3053 }
3054 
newWireSlot(Wire * wire)3055 void PEMainWindow::newWireSlot(Wire * wire) {
3056 	wire->setDisplayBendpointCursor(false);
3057 	disconnect(wire, 0, m_viewThings.value(wire->viewID())->sketchWidget, 0);
3058 	connect(wire, SIGNAL(wireChangedSignal(Wire*, const QLineF & , const QLineF & , QPointF, QPointF, ConnectorItem *, ConnectorItem *)	),
3059 			this, SLOT(wireChangedSlot(Wire*, const QLineF & , const QLineF & , QPointF, QPointF, ConnectorItem *, ConnectorItem *)),
3060 			Qt::DirectConnection);		// DirectConnection means call the slot directly like a subroutine, without waiting for a thread or queue
3061 }
3062 
wireChangedSlot(Wire * wire,const QLineF &,const QLineF &,QPointF,QPointF,ConnectorItem * fromOnWire,ConnectorItem * to)3063 void PEMainWindow::wireChangedSlot(Wire* wire, const QLineF &, const QLineF &, QPointF, QPointF, ConnectorItem * fromOnWire, ConnectorItem * to) {
3064 	wire->deleteLater();
3065 
3066 	if (to == NULL) return;
3067 
3068 	ConnectorItem * from = wire->otherConnector(fromOnWire)->firstConnectedToIsh();
3069 	if (from == NULL) return;
3070 
3071 	QDomElement root = m_fzpDocument.documentElement();
3072 	QDomElement buses = root.firstChildElement("buses");
3073 	if (buses.isNull()) {
3074 		buses = m_fzpDocument.createElement("buses");
3075 		root.appendChild(buses);
3076 	}
3077 
3078 
3079 	QDomElement fromBus = findNodeMemberBus(buses, from->connectorSharedID());
3080 	QString fromBusID = fromBus.attribute("id");
3081 	QDomElement toBus = findNodeMemberBus(buses, to->connectorSharedID());
3082 	QString toBusID = toBus.attribute("id");
3083 
3084 	QString busID = fromBusID.isEmpty() ? toBusID : fromBusID;
3085 	if (busID.isEmpty()) {
3086 		int theMax = std::numeric_limits<int>::max();
3087 		for (int ix = 1; ix < theMax; ix++) {
3088 			QString candidate = QString("internal%1").arg(ix);
3089 			QDomElement busElement = findBus(buses, candidate);
3090 			if (busElement.isNull()) {
3091 				busID = candidate;
3092 				break;
3093 			}
3094 		}
3095 	}
3096 
3097 	QUndoCommand * parentCommand = new QUndoCommand(tr("Add internal connection from '%1' to '%2'").arg(from->connectorSharedName()).arg(to->connectorSharedName()));
3098 	if (!fromBusID.isEmpty()) {
3099 		// changing the bus for this nodeMember
3100 		new RemoveBusConnectorCommand(this, fromBusID, from->connectorSharedID(), false, parentCommand);
3101 	}
3102 	if (!toBusID.isEmpty()) {
3103 		// changing the bus for this nodeMember
3104 		new RemoveBusConnectorCommand(this, toBusID, to->connectorSharedID(), false, parentCommand);
3105 	}
3106 	new RemoveBusConnectorCommand(this, busID, from->connectorSharedID(), true, parentCommand);
3107 	new RemoveBusConnectorCommand(this, busID, to->connectorSharedID(), true, parentCommand);
3108 	m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay);
3109 }
3110 
findBus(const QDomElement & buses,const QString & id)3111 QDomElement PEMainWindow::findBus(const QDomElement & buses, const QString & id)
3112 {
3113 	QDomElement busElement = buses.firstChildElement("bus");
3114 	while (!busElement.isNull()) {
3115 		if (busElement.attribute("id").compare(id) == 0) {
3116 			return busElement;
3117 		}
3118 		busElement = busElement.nextSiblingElement("bus");
3119 	}
3120 
3121 	return QDomElement();
3122 }
3123 
findNodeMemberBus(const QDomElement & buses,const QString & connectorID)3124 QDomElement PEMainWindow::findNodeMemberBus(const QDomElement & buses, const QString & connectorID)
3125 {
3126 	QDomElement bus = buses.firstChildElement("bus");
3127 	while (!bus.isNull()) {
3128 		QDomElement nodeMember = bus.firstChildElement("nodeMember");
3129 		while (!nodeMember.isNull()) {
3130 			if (nodeMember.attribute("connectorId").compare(connectorID) == 0) {
3131 				return bus;
3132 			}
3133 			nodeMember = nodeMember.nextSiblingElement("nodeMember");
3134 		}
3135 		bus = bus.nextSiblingElement("bus");
3136 	}
3137 
3138 	return QDomElement();
3139 }
3140 
addBusConnector(const QString & busID,const QString & connectorID)3141 void PEMainWindow::addBusConnector(const QString & busID, const QString & connectorID)
3142 {
3143 	// called from command object
3144 	removeBusConnector(busID, connectorID, false);			// keep the dom very clean
3145 
3146 	QDomElement root = m_fzpDocument.documentElement();
3147 	QDomElement buses = root.firstChildElement("buses");
3148 	if (buses.isNull()) {
3149 		m_fzpDocument.createElement("buses");
3150 		root.appendChild(buses);
3151 	}
3152 
3153 	QDomElement theBusElement = findBus(buses, busID);
3154 	if (theBusElement.isNull()) {
3155 		theBusElement = m_fzpDocument.createElement("bus");
3156 		theBusElement.setAttribute("id", busID);
3157 		buses.appendChild(theBusElement);
3158 	}
3159 
3160 	QDomElement nodeMember = m_fzpDocument.createElement("nodeMember");
3161 	nodeMember.setAttribute("connectorId", connectorID);
3162 	theBusElement.appendChild(nodeMember);
3163 	displayBuses();
3164 }
3165 
removeBusConnector(const QString & busID,const QString & connectorID,bool display)3166 void PEMainWindow::removeBusConnector(const QString & busID, const QString & connectorID, bool display)
3167 {
3168 	// called from command object
3169 	// for the sake of cleaning, deletes all matching nodeMembers so be careful about the order of deletion and addition within the same parentCommand
3170 	Q_UNUSED(busID);
3171 
3172 	QDomElement root = m_fzpDocument.documentElement();
3173 	QDomElement buses = root.firstChildElement("buses");
3174 	QDomElement bus = buses.firstChildElement("bus");
3175 	QList<QDomElement> toDelete;
3176 	while (!bus.isNull()) {
3177 		QDomElement nodeMember = bus.firstChildElement("nodeMember");
3178 		while (!nodeMember.isNull()) {
3179 			if (nodeMember.attribute("connectorId").compare(connectorID) == 0) {
3180 				toDelete.append(nodeMember);
3181 			}
3182 			nodeMember = nodeMember.nextSiblingElement("nodeMember");
3183 		}
3184 		bus = bus.nextSiblingElement("bus");
3185 	}
3186 
3187 	foreach (QDomElement element, toDelete) {
3188 		element.parentNode().removeChild(element);
3189 	}
3190 
3191 	if (display) displayBuses();
3192 }
3193 
replaceProperty(const QString & key,const QString & value,QDomElement & properties)3194 void PEMainWindow::replaceProperty(const QString & key, const QString & value, QDomElement & properties)
3195 {
3196     QDomElement prop = properties.firstChildElement("property");
3197     while (!prop.isNull()) {
3198         QString name = prop.attribute("name");
3199         if (name.compare(key, Qt::CaseInsensitive) == 0) {
3200             TextUtils::replaceChildText(prop, value);
3201             return;
3202         }
3203 
3204         prop = prop.nextSiblingElement("property");
3205     }
3206 
3207 	prop = m_fzpDocument.createElement("property");
3208     properties.appendChild(prop);
3209     prop.setAttribute("name", key);
3210     TextUtils::replaceChildText(prop, value);
3211 }
3212 
createTabWidget()3213 QWidget * PEMainWindow::createTabWidget() {
3214     QTabWidget * tabWidget = new QTabWidget(this);
3215     tabWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
3216     tabWidget->setObjectName("pe_tabs");
3217 	return tabWidget;
3218 }
3219 
addTab(QWidget * widget,const QString & label)3220 void PEMainWindow::addTab(QWidget * widget, const QString & label) {
3221 	qobject_cast<QTabWidget *>(m_tabWidget)->addTab(widget, label);
3222     this -> setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
3223 }
3224 
currentTabIndex()3225 int PEMainWindow::currentTabIndex() {
3226 	return qobject_cast<QTabWidget *>(m_tabWidget)->currentIndex();
3227 }
3228 
setCurrentTabIndex(int index)3229 void PEMainWindow::setCurrentTabIndex(int index) {
3230 	qobject_cast<QTabWidget *>(m_tabWidget)->setCurrentIndex(index);
3231 }
3232 
currentTabWidget()3233 QWidget * PEMainWindow::currentTabWidget() {
3234 	return qobject_cast<QTabWidget *>(m_tabWidget)->currentWidget();
3235 }
3236 
event(QEvent * e)3237 bool PEMainWindow::event(QEvent * e) {
3238 	if (e->type() == QEvent::Close) {
3239 		//qDebug() << "event close";
3240 		//if (m_inFocusWidgets.count() > 0) {
3241 		//	e->ignore();
3242 		//	qDebug() << "bail in focus";
3243 		//	return true;
3244 		//}
3245 	}
3246 
3247 	return MainWindow::event(e);
3248 }
3249 
eventFilter(QObject * object,QEvent * event)3250 bool PEMainWindow::eventFilter(QObject *object, QEvent *event)
3251 {
3252 	if (m_inPickMode) {
3253 		switch (event->type()) {
3254 			case QEvent::MouseButtonPress:
3255 				clearPickMode();
3256 				{
3257 					QMouseEvent * mouseEvent = static_cast<QMouseEvent *>(event);
3258 					m_useNextPick = (mouseEvent->button() == Qt::LeftButton);
3259 				}
3260 				QTimer::singleShot(1, this, SLOT(resetNextPick()));
3261 				break;
3262 
3263 			case QEvent::ApplicationActivate:
3264 			case QEvent::ApplicationDeactivate:
3265 			case QEvent::WindowActivate:
3266 			case QEvent::WindowDeactivate:
3267 			case QEvent::NonClientAreaMouseButtonPress:
3268 				clearPickMode();
3269 				break;
3270 
3271 			case QEvent::KeyPress:
3272 			{
3273 				QKeyEvent * kevent = static_cast<QKeyEvent *>(event);
3274 				if (kevent->key() == Qt::Key_Escape) {
3275 					clearPickMode();
3276 					return true;
3277 				}
3278 			}
3279 
3280 			default:
3281 				break;
3282 		}
3283 
3284 		return false;
3285 	}
3286 
3287 	//qDebug() << "event" << event->type();
3288     if (event->type() == QEvent::FocusIn) {
3289         QLineEdit * lineEdit = qobject_cast<QLineEdit *>(object);
3290 		if (lineEdit != NULL) {
3291 			if (lineEdit->window() == this) {
3292 				qDebug() << "inc focus";
3293 				m_inFocusWidgets << lineEdit;
3294 			}
3295 		}
3296 		else {
3297 			QTextEdit * textEdit = qobject_cast<QTextEdit *>(object);
3298 			if (textEdit != NULL && textEdit->window() == this) {
3299 				qDebug() << "inc focus";
3300 				m_inFocusWidgets << textEdit;
3301 			}
3302 		}
3303     }
3304     if (event->type() == QEvent::FocusOut) {
3305         QLineEdit * lineEdit = qobject_cast<QLineEdit *>(object);
3306 		if (lineEdit != NULL) {
3307 			if (lineEdit->window() == this) {
3308 				qDebug() << "dec focus";
3309 				m_inFocusWidgets.removeOne(lineEdit);
3310 			}
3311 		}
3312 		else {
3313 			QTextEdit * textEdit = qobject_cast<QTextEdit *>(object);
3314 			if (textEdit != NULL && textEdit->window() == this) {
3315 				qDebug() << "inc focus";
3316 				m_inFocusWidgets.removeOne(textEdit);
3317 			}
3318 		}
3319     }
3320     return false;
3321 }
3322 
closeLater()3323 void PEMainWindow::closeLater()
3324 {
3325 	close();
3326 }
3327 
resetNextPick()3328 void PEMainWindow::resetNextPick() {
3329 	m_useNextPick = false;
3330 }
3331 
clearPickMode()3332 void PEMainWindow::clearPickMode() {
3333 	qApp->removeEventFilter(this);
3334 	m_useNextPick = m_inPickMode = false;
3335 	QApplication::restoreOverrideCursor();
3336 	if (m_currentGraphicsView) {
3337 		foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) {
3338 			PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
3339 			if (pegi) pegi->setPickAppearance(false);
3340 		}
3341 	}
3342 }
3343 
getPegiList(SketchWidget * sketchWidget)3344 QList<PEGraphicsItem *> PEMainWindow::getPegiList(SketchWidget * sketchWidget) {
3345    // DebugDialog::debug("-----------------------------");
3346 
3347     QList<PEGraphicsItem *> pegiList;
3348     foreach (QGraphicsItem * item, sketchWidget->scene()->items()) {
3349         PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
3350         if (pegi == NULL) continue;
3351 
3352 		pegiList.append(pegi);
3353         /*
3354         if (pegi->showingTerminalPoint() || pegi->showingMarquee() || !pegi->element().attribute("id").isEmpty()) {
3355             DebugDialog::debug(QString("pegi m:%1 t:%2 %3")
3356                 .arg(pegi->showingMarquee())
3357                 .arg(pegi->showingTerminalPoint())
3358                 .arg(pegi->element().attribute("id")));
3359         }
3360         */
3361     }
3362 
3363     //DebugDialog::debug("-----------------------------");
3364 
3365 	return pegiList;
3366 }
3367 
deleteBuses()3368 void PEMainWindow::deleteBuses() {
3369 	QList<Wire *> toDelete;
3370 	foreach (ViewThing * viewThing, m_viewThings.values()) {
3371 		foreach (QGraphicsItem * item, viewThing->sketchWidget->scene()->items()) {
3372 			Wire * wire = dynamic_cast<Wire *>(item);
3373 			if (wire == NULL) continue;
3374 
3375 			toDelete << wire;
3376 		}
3377 	}
3378 
3379 	foreach (Wire * wire, toDelete) {
3380 		delete wire;
3381 	}
3382 }
3383 
connectorsTypeChanged(Connector::ConnectorType ct)3384 void PEMainWindow::connectorsTypeChanged(Connector::ConnectorType ct)
3385 {
3386 	QUndoCommand * parentCommand = NULL;
3387 
3388 	QDomElement root = m_fzpDocument.documentElement();
3389 	QDomElement connectors = root.firstChildElement("connectors");
3390 	QDomElement connector = connectors.firstChildElement("connector");
3391 	while (!connector.isNull()) {
3392 		ConnectorMetadata oldCmd;
3393 		fillInMetadata(connector, oldCmd);
3394 		if (oldCmd.connectorType != ct) {
3395 			if (parentCommand == NULL) {
3396 				parentCommand = new QUndoCommand(tr("Change all connectors to %1").arg(Connector::connectorNameFromType(ct)));
3397 			}
3398 			ConnectorMetadata cmd = oldCmd;
3399 			cmd.connectorType = ct;
3400 			new ChangeConnectorMetadataCommand(this, &oldCmd, &cmd, parentCommand);
3401 		}
3402 
3403 		connector = connector.nextSiblingElement("connector");
3404 	}
3405 
3406 	if (parentCommand) {
3407 		m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay);
3408 	}
3409 }
3410 
fillInMetadata(const QDomElement & connector,ConnectorMetadata & cmd)3411 void PEMainWindow::fillInMetadata(const QDomElement & connector, ConnectorMetadata & cmd)
3412 {
3413     cmd.connectorID = connector.attribute("id");
3414     cmd.connectorType = Connector::connectorTypeFromName(connector.attribute("type"));
3415     cmd.connectorName = connector.attribute("name");
3416     QDomElement description = connector.firstChildElement("description");
3417     cmd.connectorDescription = description.text();
3418 }
3419 
smdChanged(const QString & after)3420 void PEMainWindow::smdChanged(const QString & after) {
3421 	QString before;
3422 	bool toSMD = true;
3423 	if (after.compare("smd", Qt::CaseInsensitive) != 0) {
3424 		before = "smd";
3425 		toSMD = false;
3426 	}
3427 
3428 	ViewThing * viewThing = m_viewThings.value(ViewLayer::PCBView);
3429 	ItemBase * itemBase = viewThing->itemBase;
3430 	if (itemBase == NULL) return;
3431 
3432 	QFile file(itemBase->filename());
3433 	QDomDocument svgDoc;
3434 	svgDoc.setContent(&file);
3435 	QDomElement svgRoot = svgDoc.documentElement();
3436 	QDomElement svgCopper0 = TextUtils::findElementWithAttribute(svgRoot, "id", "copper0");
3437 	QDomElement svgCopper1 = TextUtils::findElementWithAttribute(svgRoot, "id", "copper1");
3438 	if (svgCopper0.isNull() && svgCopper1.isNull()) {
3439 		QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to parse '%1'").arg(itemBase->filename()));
3440 		return;
3441 	}
3442 
3443 	if (toSMD) {
3444 		if (svgCopper0.isNull() && !svgCopper1.isNull()) {
3445 			// everything is fine
3446 		}
3447 		else if (svgCopper1.isNull()) {
3448 			// just needs a swap
3449 			svgCopper0.setAttribute("id", "copper1");
3450 		}
3451 		else {
3452 			svgCopper0.removeAttribute("id");
3453 		}
3454 	}
3455 	else {
3456 		if (!svgCopper0.isNull() && !svgCopper1.isNull()) {
3457 			// everything is fine
3458 		}
3459 		else if (svgCopper1.isNull()) {
3460 			svgCopper1 = m_pcbDocument.createElement("g");
3461 			svgCopper1.setAttribute("id", "copper1");
3462 			svgRoot.appendChild(svgCopper1);
3463 			QDomElement child = svgCopper0.firstChildElement();
3464 			while (!child.isNull()) {
3465 				QDomElement next = child.nextSiblingElement();
3466 				svgCopper1.appendChild(child);
3467 				child = next;
3468 			}
3469 			svgCopper0.appendChild(svgCopper1);
3470 		}
3471 		else {
3472 			svgCopper0 = m_pcbDocument.createElement("g");
3473 			svgCopper0.setAttribute("id", "copper0");
3474 			svgRoot.appendChild(svgCopper0);
3475 			QDomElement child = svgCopper1.firstChildElement();
3476 			while (!child.isNull()) {
3477 				QDomElement next = child.nextSiblingElement();
3478 				svgCopper0.appendChild(child);
3479 				child = next;
3480 			}
3481 			svgCopper1.appendChild(svgCopper0);
3482 		}
3483 	}
3484 
3485 	QString newPath = m_userPartsFolderSvgPath + makeSvgPath2(m_pcbGraphicsView);
3486 	QString svg = TextUtils::svgNSOnly(svgDoc.toString());
3487     writeXml(newPath, removeGorn(svg), true);
3488 
3489 	ChangeSMDCommand * csc = new ChangeSMDCommand(this, before, after, itemBase->filename(), newPath, NULL);
3490 	csc->setText(tr("Change to %1").arg(after));
3491 	m_undoStack->waitPush(csc, SketchWidget::PropChangeDelay);
3492 }
3493 
changeSMD(const QString & after,const QString & filename,int changeDirection)3494 void PEMainWindow::changeSMD(const QString & after, const QString & filename, int changeDirection)
3495 {
3496 	QDomElement root = m_fzpDocument.documentElement();
3497 	QDomElement views = root.firstChildElement("views");
3498 	QDomElement pcbView = views.firstChildElement("pcbView");
3499 	QDomElement layers = pcbView.firstChildElement("layers");
3500 	QDomElement copper0 = TextUtils::findElementWithAttribute(layers, "layerId", "copper0");
3501 	QDomElement copper1 = TextUtils::findElementWithAttribute(layers, "layerId", "copper1");
3502 	bool toSMD = true;
3503 	if (after.compare("smd", Qt::CaseInsensitive) == 0) {
3504 		if (!copper0.isNull()) {
3505 			copper0.parentNode().removeChild(copper0);
3506 		}
3507 	}
3508 	else {
3509 		toSMD = false;
3510 		if (copper0.isNull()) {
3511 			copper0 = m_fzpDocument.createElement("layer");
3512 			copper0.setAttribute("layerId", "copper0");
3513 			layers.appendChild(copper0);
3514 		}
3515 	}
3516 	if (copper1.isNull()) {
3517 		copper1 = m_fzpDocument.createElement("layer");
3518 		copper1.setAttribute("layerId", "copper1");
3519 		layers.appendChild(copper1);
3520 	}
3521 
3522 	QDomElement connectors = root.firstChildElement("connectors");
3523 	QDomElement connector = connectors.firstChildElement("connector");
3524 	while (!connector.isNull()) {
3525 		setConnectorSMD(toSMD, connector);
3526 		connector = connector.nextSiblingElement("connector");
3527 	}
3528 
3529 	changeSvg(m_pcbGraphicsView, filename, changeDirection);
3530 }
3531 
setConnectorSMD(bool toSMD,QDomElement & connector)3532 void PEMainWindow::setConnectorSMD(bool toSMD, QDomElement & connector) {
3533 	QDomElement views = connector.firstChildElement("views");
3534 	QDomElement pcbView = views.firstChildElement("pcbView");
3535 	QDomElement copper0 = TextUtils::findElementWithAttribute(pcbView, "layer", "copper0");
3536 	QDomElement copper1 = TextUtils::findElementWithAttribute(pcbView, "layer", "copper1");
3537 	if (copper0.isNull() && copper1.isNull()) {
3538 		// SVG is seriously messed up
3539 		DebugDialog::debug("setting SMD is very broken");
3540 		return;
3541 	}
3542 
3543 	if (toSMD) {
3544 		if (copper0.isNull() && !copper1.isNull()) {
3545 			// already correct
3546 			return;
3547 		}
3548 		if (copper1.isNull()) {
3549 			// swap it to copper1
3550 			copper0.setAttribute("layer", "copper1");
3551 			return;
3552 		}
3553 		// remove the extra layer
3554 		copper0.parentNode().removeChild(copper0);
3555 		return;
3556 	}
3557 
3558 	if (!copper0.isNull() && !copper1.isNull()) {
3559 		// already correct
3560 		return;
3561 	}
3562 
3563 	if (copper1.isNull()) {
3564 		copper1 = copper0.cloneNode(true).toElement();
3565 		copper0.parentNode().appendChild(copper1);
3566 		copper1.setAttribute("layer", "copper1");
3567 		return;
3568 	}
3569 
3570 	copper0 = copper1.cloneNode(true).toElement();
3571 	copper1.parentNode().appendChild(copper0);
3572 	copper0.setAttribute("layer", "copper0");
3573 }
3574 
3575 
reuseBreadboard()3576 void PEMainWindow::reuseBreadboard()
3577 {
3578 	reuseImage(ViewLayer::BreadboardView);
3579 }
3580 
reuseSchematic()3581 void PEMainWindow::reuseSchematic()
3582 {
3583 	reuseImage(ViewLayer::SchematicView);
3584 }
3585 
reusePCB()3586 void PEMainWindow::reusePCB()
3587 {
3588 	reuseImage(ViewLayer::PCBView);
3589 }
3590 
reuseImage(ViewLayer::ViewID viewID)3591 void PEMainWindow::reuseImage(ViewLayer::ViewID viewID) {
3592 	if (m_currentGraphicsView == NULL) return;
3593 
3594 	ViewThing * afterViewThing = m_viewThings.value(viewID);
3595 	if (afterViewThing->itemBase == NULL) return;
3596 
3597 	QString afterFilename = afterViewThing->itemBase->filename();
3598 
3599 	ViewThing * beforeViewThing = m_viewThings.value(m_currentGraphicsView->viewID());
3600 
3601 	ChangeSvgCommand * csc = new ChangeSvgCommand(this, m_currentGraphicsView, beforeViewThing->itemBase->filename(), afterFilename, NULL);
3602     QFileInfo info(afterFilename);
3603     csc->setText(QString("Load '%1'").arg(info.fileName()));
3604     m_undoStack->waitPush(csc, SketchWidget::PropChangeDelay);
3605 }
3606 
updateFileMenu()3607 void PEMainWindow::updateFileMenu() {
3608 	MainWindow::updateFileMenu();
3609 
3610     m_saveAct->setEnabled(canSave());
3611 
3612 	/*
3613 	QHash<ViewLayer::ViewID, bool> enabled;
3614 	enabled.insert(ViewLayer::BreadboardView, true);
3615 	enabled.insert(ViewLayer::SchematicView, true);
3616 	enabled.insert(ViewLayer::PCBView, true);
3617 	bool enableAll = true;
3618 	if (m_currentGraphicsView == NULL) {
3619 		enableAll = false;
3620 	}
3621 	else {
3622 		ViewLayer::ViewID viewID = m_currentGraphicsView->viewID();
3623 		enabled.insert(viewID, false);
3624 	}
3625 
3626 	m_reuseBreadboardAct->setEnabled(enableAll && enabled.value(ViewLayer::BreadboardView));
3627 	m_reuseSchematicAct->setEnabled(enableAll && enabled.value(ViewLayer::SchematicView));
3628 	m_reusePCBAct->setEnabled(enableAll && enabled.value(ViewLayer::PCBView));
3629 	*/
3630 
3631 	bool enabled = m_currentGraphicsView != NULL && m_currentGraphicsView->viewID() == ViewLayer::IconView;
3632 	m_reuseBreadboardAct->setEnabled(enabled);
3633 	m_reuseSchematicAct->setEnabled(enabled);
3634 	m_reusePCBAct->setEnabled(enabled);
3635 }
3636 
setImageAttribute(QDomElement & fzpRoot,const QString & svgPath,ViewLayer::ViewID viewID)3637 void PEMainWindow::setImageAttribute(QDomElement & fzpRoot, const QString & svgPath, ViewLayer::ViewID viewID)
3638 {
3639     QDomElement views = fzpRoot.firstChildElement("views");
3640     QDomElement view = views.firstChildElement(ViewLayer::viewIDXmlName(viewID));
3641     QDomElement layers = view.firstChildElement("layers");
3642     QFileInfo info(svgPath);
3643     QDir dir = info.absoluteDir();
3644     QString shortName = dir.dirName() + "/" + info.fileName();
3645 	setImageAttribute(layers, shortName);
3646 }
3647 
setImageAttribute(QDomElement & layers,const QString & svgPath)3648 void PEMainWindow::setImageAttribute(QDomElement & layers, const QString & svgPath)
3649 {
3650 	layers.setAttribute("image", svgPath);
3651 	QDomElement layer = layers.firstChildElement("layer");
3652 	if (!layer.isNull()) return;
3653 
3654 	layer = m_fzpDocument.createElement("layer");
3655 	layers.appendChild(layer);
3656 	layer.setAttribute("layerId", ViewLayer::viewLayerXmlNameFromID(ViewLayer::UnknownLayer));
3657 }
3658 
updateLayerMenu(bool resetLayout)3659 void PEMainWindow::updateLayerMenu(bool resetLayout) {
3660 	MainWindow::updateLayerMenu(resetLayout);
3661 
3662 	bool enabled = false;
3663 	if (m_currentGraphicsView != NULL) {
3664 		switch (m_currentGraphicsView->viewID()) {
3665 			case ViewLayer::BreadboardView:
3666 			case ViewLayer::SchematicView:
3667 			case ViewLayer::PCBView:
3668 				enabled = true;
3669 			default:
3670 				break;
3671 		}
3672 	}
3673 
3674 	m_hideOtherViewsAct->setEnabled(enabled);
3675 }
3676 
hideOtherViews()3677 void PEMainWindow::hideOtherViews() {
3678 	if (m_currentGraphicsView == NULL) return;
3679 
3680 	ViewLayer::ViewID afterViewID = m_currentGraphicsView->viewID();
3681 	ItemBase * afterItemBase = m_viewThings.value(afterViewID)->itemBase;
3682 	if (afterItemBase == NULL) return;
3683 	QString afterFilename = afterItemBase->filename();
3684 
3685 	QList<ViewLayer::ViewID> viewIDList;
3686 	viewIDList << ViewLayer::BreadboardView << ViewLayer::SchematicView << ViewLayer::PCBView;
3687 	viewIDList.removeOne(afterViewID);
3688 
3689 	QString originalPath = saveFzp();
3690 
3691 	QString afterViewName = ViewLayer::viewIDXmlName(afterViewID);
3692 	QStringList beforeViewNames;
3693 	foreach (ViewLayer::ViewID viewID, viewIDList) {
3694 		beforeViewNames << ViewLayer::viewIDXmlName(viewID);
3695 	}
3696 
3697 	QDomElement root = m_fzpDocument.documentElement();
3698 	QDomElement connectors = root.firstChildElement("connectors");
3699 	QDomElement connector = connectors.firstChildElement("connector");
3700 	while (!connector.isNull()) {
3701 		QDomElement views = connector.firstChildElement("views");
3702 		QDomElement afterView = views.firstChildElement(afterViewName);
3703 
3704 		foreach (QString name, beforeViewNames) {
3705 			QDomElement toRemove = views.firstChildElement(name);
3706 			if (!toRemove.isNull()) {
3707 				toRemove.parentNode().removeChild(toRemove);
3708 			}
3709 			QDomElement toReplace = afterView.cloneNode(true).toElement();
3710 			toReplace.setTagName(name);
3711 			views.appendChild(toReplace);
3712 		}
3713 
3714 		connector = connector.nextSiblingElement("connector");
3715 	}
3716 
3717 	QDomElement views = root.firstChildElement("views");
3718 	QDomElement afterView = views.firstChildElement(afterViewName);
3719 	foreach (QString name, beforeViewNames) {
3720 		QDomElement toRemove = views.firstChildElement(name);
3721 		if (!toRemove.isNull()) {
3722 			toRemove.parentNode().removeChild(toRemove);
3723 		}
3724 		QDomElement toReplace = afterView.cloneNode(true).toElement();
3725 		toReplace.setTagName(name);
3726 		views.appendChild(toReplace);
3727 	}
3728 
3729     QString newPath = saveFzp();
3730 	ChangeFzpCommand * cfc = new ChangeFzpCommand(this, originalPath, newPath, NULL);
3731 	cfc->setText(tr("Make only %1 view visible").arg(m_currentGraphicsView->viewName()));
3732 	m_undoStack->waitPush(cfc, SketchWidget::PropChangeDelay);
3733 }
3734 
makeNewVariant(const QString & family)3735 QString PEMainWindow::makeNewVariant(const QString & family)
3736 {
3737 	QStringList variants = m_referenceModel->propValues(family, "variant", true);
3738 	int theMax = std::numeric_limits<int>::max();
3739 	QString candidate;
3740 	for (int i = 1; i < theMax; i++) {
3741 		candidate = QString("variant %1").arg(i);
3742 		if (!variants.contains(candidate, Qt::CaseInsensitive)) break;
3743 	}
3744 
3745 	return candidate;
3746 }
3747 
updateAssignedConnectors()3748 void PEMainWindow::updateAssignedConnectors() {
3749 	if (m_currentGraphicsView == NULL) return;
3750 
3751 	QDomDocument * doc = m_viewThings.value(m_currentGraphicsView->viewID())->document;
3752 	if (doc) m_peToolView->showAssignedConnectors(doc, m_currentGraphicsView->viewID());
3753 }
3754 
connectorWarning()3755 void PEMainWindow::connectorWarning() {
3756 	QHash<ViewLayer::ViewID, int> unassigned;
3757 	foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) {
3758 		unassigned.insert(viewID, 0);
3759 	}
3760 	int unassignedTotal = 0;
3761 
3762 	QDomElement fzpRoot = m_fzpDocument.documentElement();
3763 	QDomElement connectors = fzpRoot.firstChildElement("connectors");
3764 	foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) {
3765 		if (viewID == ViewLayer::IconView) continue;
3766 
3767 		QDomDocument * svgDoc = m_viewThings.value(viewID)->document;
3768 		QDomElement svgRoot = svgDoc->documentElement();
3769 		QDomElement connector = connectors.firstChildElement("connector");
3770 		while (!connector.isNull()) {
3771 			QString svgID, terminalID;
3772 			if (ViewLayer::getConnectorSvgIDs(connector, viewID, svgID, terminalID)) {
3773 				QDomElement element = TextUtils::findElementWithAttribute(svgRoot, "id", svgID);
3774 				if (element.isNull()) {
3775 					unassigned.insert(viewID, 1 + unassigned.value(viewID));
3776 					unassignedTotal++;
3777 				}
3778 			}
3779 			else {
3780 				unassigned.insert(viewID, 1 + unassigned.value(viewID));
3781 				unassignedTotal++;
3782 			}
3783 
3784 			connector = connector.nextSiblingElement("connector");
3785 		}
3786 	}
3787 
3788 	if (unassignedTotal > 0) {
3789 		int viewCount = 0;
3790 		foreach (ViewLayer::ViewID viewID, unassigned.keys()) {
3791 			if (unassigned.value(viewID) > 0) viewCount++;
3792 		}
3793 		QMessageBox::warning(NULL, tr("Parts Editor"),
3794 			tr("This part has %n unassigned connectors ", "", unassignedTotal) +
3795 			tr("across %n views. ", "", viewCount) +
3796 			tr("Until all connectors are assigned to SVG elements, the part will not work correctly. ") +
3797 			tr("Exiting the Parts Editor now is fine, as long as you remember to finish the assignments later.")
3798 		);
3799 	}
3800 
3801 }
3802 
showing(SketchWidget * sketchWidget)3803 void PEMainWindow::showing(SketchWidget * sketchWidget) {
3804 	ViewThing * viewThing = m_viewThings.value(sketchWidget->viewID());
3805 	if (viewThing->firstTime) {
3806 		viewThing->firstTime = false;
3807 		QPointF offset = viewThing->sketchWidget->alignOneToGrid(viewThing->itemBase);
3808 		if (offset.x() != 0 || offset.y() != 0) {
3809 			QList<PEGraphicsItem *> pegiList = getPegiList(sketchWidget);
3810 			foreach (PEGraphicsItem * pegi, pegiList) {
3811 				pegi->setPos(pegi->pos() + offset);
3812 				pegi->setOffset(pegi->offset() + offset);
3813 			}
3814 		}
3815 	}
3816 }
3817 
anyMarquee()3818 bool PEMainWindow::anyMarquee() {
3819     if (m_currentGraphicsView == NULL) return false;
3820 
3821     QList<PEGraphicsItem *> pegiList = getPegiList(m_currentGraphicsView);
3822     foreach (PEGraphicsItem * pegi, pegiList) {
3823         if (pegi->showingMarquee()) {
3824             return true;
3825         }
3826     }
3827 
3828     return false;
3829 }
3830 
anyVisible()3831 bool PEMainWindow::anyVisible() {
3832     if (m_currentGraphicsView == NULL) return false;
3833 
3834     foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) {
3835         PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
3836         if (pegi == NULL) continue;
3837 
3838 		return pegi->isVisible();
3839     }
3840 
3841     return false;
3842 }
3843 
changeReferenceFile(ViewLayer::ViewID viewID,const QString referenceFile)3844 void PEMainWindow::changeReferenceFile(ViewLayer::ViewID viewID, const QString referenceFile)
3845 {
3846     ViewThing * viewThing = m_viewThings.value(viewID);
3847     if (viewThing == NULL) {
3848         // shouldn't happen
3849         DebugDialog::debug(QString("missing view thing for %1").arg(viewID));
3850         return;
3851     }
3852 
3853     viewThing->referenceFile = referenceFile;
3854 }
3855 
insertDesc(const QString & referenceFile,QString & svg)3856 void PEMainWindow::insertDesc(const QString & referenceFile, QString & svg) {
3857     if (svg.contains(ReferenceFileString)) return;
3858 
3859     int ix = svg.indexOf("<svg");
3860     if (ix >= 0) {
3861         int jx = svg.indexOf(">", ix);
3862         if (jx > ix) {
3863             svg.insert(jx + 1, makeDesc(referenceFile));
3864         }
3865     }
3866 }
3867 
itemAddedSlot(ModelPart *,ItemBase * itemBase,ViewLayer::ViewLayerPlacement,const ViewGeometry &,long id,SketchWidget *)3868 void PEMainWindow::itemAddedSlot(ModelPart *, ItemBase * itemBase, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long id, SketchWidget *) {
3869     Q_UNUSED(id);
3870 
3871     if (itemBase == NULL) return;
3872     if (itemBase->viewID() != m_currentGraphicsView->viewID()) return;
3873 
3874     QDomElement element;
3875     double z = 0;
3876     foreach (PEGraphicsItem * pegi, getPegiList(m_currentGraphicsView)) {
3877         if (pegi->zValue() > z) z = pegi->zValue();
3878     }
3879 
3880     QRectF bounds = itemBase->boundingRect();
3881     makePegi(bounds.size(), QPointF(0, 0), itemBase, element, z + 1);
3882 }
3883 
itemMovedSlot(ItemBase * itemBase)3884 void PEMainWindow::itemMovedSlot(ItemBase * itemBase) {
3885     if (itemBase == NULL) return;
3886     if (itemBase->viewID() != m_currentGraphicsView->viewID()) return;
3887 
3888     foreach (PEGraphicsItem * pegi, getPegiList(m_currentGraphicsView)) {
3889         if (pegi->itemBase() == itemBase) {
3890             pegi->setPos(itemBase->pos() + pegi->offset());
3891         }
3892     }
3893 
3894 }
3895 
resizedSlot(ItemBase * itemBase)3896 void PEMainWindow::resizedSlot(ItemBase * itemBase) {
3897     if (itemBase == NULL) return;
3898     if (itemBase->viewID() != m_currentGraphicsView->viewID()) return;
3899 
3900     foreach (PEGraphicsItem * pegi, getPegiList(m_currentGraphicsView)) {
3901         if (pegi->itemBase() == itemBase) {
3902             pegi->setPos(itemBase->pos() + pegi->offset());
3903             QRectF bounds = itemBase->boundingRect();
3904             pegi->setRect(bounds);
3905         }
3906     }
3907 }
3908 
clickedItemCandidateSlot(QGraphicsItem * item,bool & ok)3909 void PEMainWindow::clickedItemCandidateSlot(QGraphicsItem * item, bool & ok) {
3910     PEGraphicsItem * pegi = dynamic_cast<PEGraphicsItem *>(item);
3911     if (pegi == NULL) {
3912         ok = true;
3913         return;
3914     }
3915 
3916     ok = pegi->showingMarquee();
3917 }
3918 
initProgrammingWidget()3919 void PEMainWindow::initProgrammingWidget() {
3920 }
3921 
initWelcomeView()3922 void PEMainWindow::initWelcomeView() {
3923 }
3924 
setInitialView()3925 void PEMainWindow::setInitialView() {
3926     	// do this the first time, since the current_changed signal wasn't sent
3927 	int tab = 0;
3928 	tabWidget_currentChanged(tab+1);
3929 	tabWidget_currentChanged(tab);
3930 }
3931 
updateExportMenu()3932 void PEMainWindow::updateExportMenu() {
3933     foreach (QAction * action, m_exportMenu->actions()) {
3934         action->setEnabled(false);
3935     }
3936 }
3937 
convertToTenth()3938 void PEMainWindow::convertToTenth() {
3939     if (m_currentGraphicsView == NULL) return;
3940     if (m_currentGraphicsView->viewID() != ViewLayer::SchematicView) return;
3941 
3942     QString originalFzpPath = saveFzp();
3943     QString newFzpPath = saveFzp();
3944 
3945 	ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID());
3946     QString originalSvgPath = viewThing->itemBase->filename();
3947     QString newSvgPath = m_userPartsFolderSvgPath + makeSvgPath2(m_currentGraphicsView);
3948     QFile::copy(originalSvgPath, newSvgPath);
3949 
3950     S2S s2s(false);
3951     connect(&s2s, SIGNAL(messageSignal(const QString &)), this, SLOT(s2sMessageSlot(const QString &)));
3952     bool result = s2s.onefzp(newFzpPath, newSvgPath);
3953 
3954     if (!result) return;          // if conversion fails
3955 
3956     QUndoCommand * parentCommand = new QUndoCommand("Convert Schematic");
3957     new ChangeFzpCommand(this, originalFzpPath, newFzpPath, parentCommand);
3958     new ChangeSvgCommand(this, m_currentGraphicsView, originalSvgPath, newSvgPath, parentCommand);
3959     m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay);
3960 }
3961 
s2sMessageSlot(const QString & message)3962 void PEMainWindow::s2sMessageSlot(const QString & message) {
3963     QMessageBox::information(this, "Schematic Conversion", message);
3964 }
3965 
updateEditMenu()3966 void PEMainWindow::updateEditMenu() {
3967     MainWindow::updateEditMenu();
3968     m_convertToTenthAct->setEnabled(m_currentGraphicsView != NULL && m_currentGraphicsView->viewID() == ViewLayer::SchematicView);
3969 }
3970 
3971