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: 6999 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-28 14:14:07 +0200 (So, 28. Apr 2013) $
24 
25 ********************************************************************/
26 
27 #include <QSvgGenerator>
28 #include <QColor>
29 #include <QImageWriter>
30 #include <QInputDialog>
31 #include <QApplication>
32 #include <QMenuBar>
33 #include <QClipboard>
34 #include <QDebug>
35 #include <QSettings>
36 #include <QDesktopServices>
37 #include <QMimeData>
38 
39 #include "mainwindow.h"
40 #include "../debugdialog.h"
41 #include "../waitpushundostack.h"
42 #include "../partseditor/pemainwindow.h"
43 #include "../help/aboutbox.h"
44 #include "../autoroute/mazerouter/mazerouter.h"
45 #include "../autoroute/autorouteprogressdialog.h"
46 #include "../autoroute/drc.h"
47 #include "../items/virtualwire.h"
48 #include "../items/resizableboard.h"
49 #include "../items/jumperitem.h"
50 #include "../items/via.h"
51 #include "../fsvgrenderer.h"
52 #include "../items/note.h"
53 #include "../eagle/fritzing2eagle.h"
54 #include "../sketch/breadboardsketchwidget.h"
55 #include "../sketch/schematicsketchwidget.h"
56 #include "../sketch/pcbsketchwidget.h"
57 #include "../partsbinpalette/binmanager/binmanager.h"
58 #include "../utils/expandinglabel.h"
59 #include "../infoview/htmlinfoview.h"
60 #include "../utils/bendpointaction.h"
61 #include "../sketch/fgraphicsscene.h"
62 #include "../utils/fmessagebox.h"
63 #include "../utils/fileprogressdialog.h"
64 #include "../svg/svgfilesplitter.h"
65 #include "../version/version.h"
66 #include "../svg/groundplanegenerator.h"
67 #include "../help/tipsandtricks.h"
68 #include "../dialogs/setcolordialog.h"
69 #include "../utils/folderutils.h"
70 #include "../utils/graphicsutils.h"
71 #include "../utils/textutils.h"
72 #include "../connectors/ercdata.h"
73 #include "../items/moduleidnames.h"
74 #include "../utils/zoomslider.h"
75 #include "../dock/layerpalette.h"
76 #include "../program/programwindow.h"
77 #include "../utils/autoclosemessagebox.h"
78 #include "../processeventblocker.h"
79 #include "../sketchtoolbutton.h"
80 #include "../help/firsttimehelpdialog.h"
81 
82 ////////////////////////////////////////////////////////
83 
84 // help struct to create the example menu from a xml file
85 struct SketchDescriptor {
SketchDescriptorSketchDescriptor86 	SketchDescriptor(const QString &_id, const QString &_name, const QString &_src, QAction * _action) {
87 		id = _id;
88 		name = _name;
89 		src = _src;
90 		action = _action;
91 	}
92 
93 	QString id;
94 	QString name;
95 	QString src;
96 	QAction * action;
97 };
98 
sortSketchDescriptors(SketchDescriptor * s1,SketchDescriptor * s2)99 bool sortSketchDescriptors(SketchDescriptor * s1, SketchDescriptor * s2){
100     return s1->name.toLower() < s2->name.toLower();
101 }
102 
getBestLanguageChild(const QString & localeName,const QDomElement & parent)103 QDomElement getBestLanguageChild(const QString & localeName, const QDomElement & parent)
104 {
105     QDomElement language = parent.firstChildElement("language");
106     QDomElement backupLang;
107     while (!language.isNull()) {
108         if (language.attribute("country") == "en") backupLang = language;
109         if (localeName.endsWith(language.attribute("country"))) {
110             return language;
111         }
112         language = language.nextSiblingElement("language");
113     }
114     return backupLang;
115 }
116 
117 ////////////////////////////////////////////////////////
118 
GridSizeThing(const QString & vName,const QString & sName,double defaultSize,const QString & gsText)119 GridSizeThing::GridSizeThing(const QString & vName, const QString & sName, double defaultSize, const QString & gsText)
120 {
121 	defaultGridSize = defaultSize;
122 	viewName = vName;
123 	shortName = sName;
124     gridSizeText = gsText;
125 }
126 
GridSizeDialog(GridSizeThing * gridSizeThing)127 GridSizeDialog::GridSizeDialog(GridSizeThing * gridSizeThing) : QDialog() {
128     m_gridSizeThing = gridSizeThing;
129 }
130 
gridSizeThing()131 GridSizeThing * GridSizeDialog::gridSizeThing() {
132     return m_gridSizeThing;
133 }
134 
135 
136 /////////////////////////////////////////////////////////
137 
closeIfEmptySketch(MainWindow * mw)138 void MainWindow::closeIfEmptySketch(MainWindow* mw) {
139 	int cascFactorX; int cascFactorY;
140 	// close empty sketch window if user opens from a file
141 	if (FolderUtils::isEmptyFileName(mw->m_fwFilename, untitledFileName()) && mw->undoStackIsEmpty()) {
142 		QTimer::singleShot(0, mw, SLOT(close()) );
143 		cascFactorX = 0;
144 		cascFactorY = 0;
145 	} else {
146 		cascFactorX = CascadeFactorX;
147 		cascFactorY = CascadeFactorY;
148 	}
149 	mw->move(x()+cascFactorX,y()+cascFactorY);
150 	mw->show();
151 }
152 
mainLoad()153 void MainWindow::mainLoad() {
154 	QString path;
155 	// if it's the first time load is called use Documents folder
156 	if(m_firstOpen){
157 		path = defaultSaveFolder();
158 		m_firstOpen = false;
159 	}
160 	else {
161 		path = "";
162 	}
163 
164 	QString fileName = FolderUtils::getOpenFileName(
165 			this,
166 			tr("Select a Fritzing File to Open"),
167 			path,
168 			tr("Fritzing Files (*%1 *%2 *%3 *%4 *%5);;Fritzing (*%1);;Fritzing Shareable (*%2);;Fritzing Part (*%3);;Fritzing Bin (*%4);;Fritzing Shareable Bin (*%5)")
169                 .arg(FritzingSketchExtension)
170                 .arg(FritzingBundleExtension)
171                 .arg(FritzingBundledPartExtension)
172                 .arg(FritzingBinExtension)
173                 .arg(FritzingBundledBinExtension)
174 		);
175 
176     if (fileName.isEmpty()) return;
177 
178     if (fileName.endsWith(FritzingBundledPartExtension)) {
179         m_binManager->importPartToMineBin(fileName);
180         return;
181     }
182 
183     if (fileName.endsWith(FritzingBinExtension) || fileName.endsWith(FritzingBundledBinExtension)) {
184         m_binManager->openBinIn(fileName, false);
185         return;
186     }
187 
188     mainLoadAux(fileName);
189 }
190 
mainLoadAux(const QString & fileName)191 void MainWindow::mainLoadAux(const QString & fileName)
192 {
193 	if (fileName.isNull()) return;
194 
195     if (fileName.endsWith(".txt")) {
196         QFileInfo info(fileName);
197         QFile file(fileName);
198         if (file.open(QFile::ReadOnly)) {
199             QTextStream stream(&file);
200             while (!stream.atEnd()) {
201                 QString line = stream.readLine().trimmed();
202                 foreach (QString ext, fritzingExtensions()) {
203                     if (line.endsWith(ext)) {
204                         QFileInfo lineInfo(line);
205                         if (lineInfo.exists()) {
206                             mainLoadAux(line);
207                         }
208                         else {
209                             QFileInfo lineInfo(info.absoluteDir().absoluteFilePath(line));
210                             if (lineInfo.exists()) {
211                                 mainLoadAux(lineInfo.absoluteFilePath());
212                             }
213                         }
214                         break;
215                     }
216                 }
217             }
218         }
219         return;
220     }
221 
222     if (!fileName.endsWith(FritzingSketchExtension) && !fileName.endsWith(FritzingBundleExtension)) {
223         loadWhich(fileName, false, false, true, "");
224         return;
225     }
226 
227 	if (alreadyOpen(fileName)) return;
228 
229 	QFile file(fileName);
230 	if (!file.exists()) {
231        FMessageBox::warning(this, tr("Fritzing"),
232                              tr("Cannot find file %1.")
233                              .arg(fileName));
234 
235 
236 		return;
237 	}
238 
239 
240 
241     if (!file.open(QFile::ReadOnly | QFile::Text)) {
242         FMessageBox::warning(this, tr("Fritzing"),
243                              tr("Cannot read file  1 %1:\n%2.")
244                              .arg(fileName)
245                              .arg(file.errorString()));
246         return;
247     }
248 
249     file.close();
250 
251     MainWindow* mw = newMainWindow(m_referenceModel, fileName, true, true, -1);
252 	mw->loadWhich(fileName, true, true, true, "");
253     mw->clearFileProgressDialog();
254 	closeIfEmptySketch(mw);
255 }
256 
revert()257 void MainWindow::revert() {
258 	QMessageBox::StandardButton answer = QMessageBox::question(
259             this,
260             tr("Revert?"),
261             tr("This operation can not be undone--you will lose all of your changes."
262                 "\n\nGo ahead and revert?"),
263             QMessageBox::Yes | QMessageBox::No,
264             QMessageBox::Yes
265     );
266     // TODO: make button texts translatable
267     if (answer != QMessageBox::Yes) {
268         return;
269     }
270 
271     revertAux();
272 }
273 
revertAux()274 MainWindow * MainWindow::revertAux()
275 {
276     MainWindow* mw = newMainWindow( m_referenceModel, fileName(), true, true, this->currentTabIndex());
277     mw->setGeometry(this->geometry());
278 
279     QFileInfo info(fileName());
280     if (info.exists() || !FolderUtils::isEmptyFileName(this->m_fwFilename, untitledFileName())) {
281 	    mw->loadWhich(fileName(), true, true, true, "");
282     }
283     else {
284 	    mw->addDefaultParts();
285         mw->show();
286 	    mw->hideTempPartsBin();
287     }
288 
289     mw->clearFileProgressDialog();
290 
291     // TODO: restore zoom, scroll, etc. for each view
292     mw->setCurrentTabIndex(currentTabIndex());
293 
294     this->setCloseSilently(true);
295     this->close();
296 
297     return mw;
298 }
299 
loadWhich(const QString & fileName,bool setAsLastOpened,bool addToRecent,bool checkObsolete,const QString & displayName)300 bool MainWindow::loadWhich(const QString & fileName, bool setAsLastOpened, bool addToRecent, bool checkObsolete, const QString & displayName)
301 {
302 	if (!QFileInfo(fileName).exists()) {
303 		FMessageBox::warning(NULL, tr("Fritzing"), tr("File '%1' not found").arg(fileName));
304 		return false;
305 	}
306 
307 	bool result = false;
308     if (fileName.endsWith(FritzingSketchExtension)) {
309 		QFileInfo info(fileName);
310 		FMessageBox messageBox(NULL);
311 		messageBox.setWindowTitle(tr("the .fz file format is obsolete"));
312 		messageBox.setText(tr("The .fz file format has been deprecated.\n\nWould you like to convert '%1' to the .fzz format now or open it read-only?\n").arg(info.fileName()));
313 		messageBox.setInformativeText(tr("The conversion process will not modify '%1'.").arg(info.fileName()));
314 		messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
315 		messageBox.setDefaultButton(QMessageBox::Yes);
316 		messageBox.setIcon(QMessageBox::Question);
317 		messageBox.setWindowModality(Qt::WindowModal);
318 		messageBox.setButtonText(QMessageBox::Yes, tr("Convert"));
319 		messageBox.setButtonText(QMessageBox::No, tr("Read-only"));
320 		messageBox.setButtonText(QMessageBox::Cancel, tr("Cancel"));
321 		QMessageBox::StandardButton answer = (QMessageBox::StandardButton) messageBox.exec();
322 
323 		if (answer == QMessageBox::Cancel) return false;
324 
325 		QString bundledFileName;
326 		if (answer == QMessageBox::Yes) {
327 			QString fileExt;
328 			bundledFileName = FolderUtils::getSaveFileName(this, tr("Please specify an .fzz file name to save '%1' to").arg(info.fileName()), fileName + "z", tr("Fritzing (*%1)").arg(FritzingBundleExtension), &fileExt);
329 			if (bundledFileName.isEmpty()) return false;
330 		}
331 
332     	mainLoad(fileName, displayName, checkObsolete);
333 		result = true;
334 
335 		QFile file(fileName);
336 		QDir dest(m_fzzFolder);
337 		FolderUtils::slamCopy(file, dest.absoluteFilePath(info.fileName()));			// copy the .fz file directly
338 
339 		if (answer == QMessageBox::Yes) {
340 			saveAsShareable(bundledFileName, false);					// false to prevent saving a bundle inside the bundle
341 			setCurrentFile(bundledFileName, addToRecent, setAsLastOpened);
342 		}
343 		else {
344 			this->setReadOnly(true);
345 			setCurrentFile(fileName, false, false);
346 		}
347     }
348 	else if(fileName.endsWith(FritzingBundleExtension)) {
349     	loadBundledSketch(fileName, addToRecent, setAsLastOpened, checkObsolete);
350 		result = true;
351     }
352 	else if (
353     		fileName.endsWith(FritzingBinExtension)
354     		|| fileName.endsWith(FritzingBundledBinExtension)
355     	) {
356 		m_binManager->load(fileName);
357 		result = true;
358 	}
359 	else if (fileName.endsWith(FritzingPartExtension)) {
360 		notYetImplemented(tr("directly loading parts"));
361 	}
362 	else if (fileName.endsWith(FritzingBundledPartExtension)) {
363 		loadBundledPart(fileName, true);
364 		result = true;
365 	}
366 
367 	if (result) {
368 		this->show();
369 	}
370 
371 	return result;
372 }
373 
mainLoad(const QString & fileName,const QString & displayName,bool checkObsolete)374 void MainWindow::mainLoad(const QString & fileName, const QString & displayName, bool checkObsolete) {
375 
376 	if (m_fileProgressDialog) {
377 		m_fileProgressDialog->setMaximum(200);
378 		m_fileProgressDialog->setValue(102);
379 	}
380 	this->show();
381 	ProcessEventBlocker::processEvents();
382 
383 	QString displayName2 = displayName;
384 	if (displayName.isEmpty()) {
385 		QFileInfo fileInfo(fileName);
386 		displayName2 = fileInfo.fileName();
387 	}
388 
389 	if (m_fileProgressDialog) {
390 		m_fileProgressDialog->setMessage(tr("loading %1 (model)").arg(displayName2));
391 		m_fileProgressDialog->setValue(110);
392 	}
393 	ProcessEventBlocker::processEvents();
394 
395 
396 	QList<ModelPart *> modelParts;
397 
398 	connect(m_sketchModel, SIGNAL(loadedViews(ModelBase *, QDomElement &)),
399 				this, SLOT(loadedViewsSlot(ModelBase *, QDomElement &)), Qt::DirectConnection);
400 	connect(m_sketchModel, SIGNAL(loadedRoot(const QString &, ModelBase *, QDomElement &)),
401 				this, SLOT(loadedRootSlot(const QString &, ModelBase *, QDomElement &)), Qt::DirectConnection);
402 	connect(m_sketchModel, SIGNAL(obsoleteSMDOrientationSignal()),
403 				this, SLOT(obsoleteSMDOrientationSlot()), Qt::DirectConnection);
404 	connect(m_sketchModel, SIGNAL(oldSchematicsSignal(const QString &, bool &)),
405 				this, SLOT(oldSchematicsSlot(const QString &, bool &)), Qt::DirectConnection);
406     m_obsoleteSMDOrientation = false;
407 
408 	m_sketchModel->loadFromFile(fileName, m_referenceModel, modelParts, true);
409 
410 	//DebugDialog::debug("core loaded");
411 	disconnect(m_sketchModel, SIGNAL(loadedViews(ModelBase *, QDomElement &)),
412 				this, SLOT(loadedViewsSlot(ModelBase *, QDomElement &)));
413 	disconnect(m_sketchModel, SIGNAL(loadedRoot(const QString &, ModelBase *, QDomElement &)),
414 				this, SLOT(loadedRootSlot(const QString &, ModelBase *, QDomElement &)));
415 	disconnect(m_sketchModel, SIGNAL(obsoleteSMDOrientationSignal()),
416 				this, SLOT(obsoleteSMDOrientationSlot()));
417 
418 	ProcessEventBlocker::processEvents();
419 	if (m_fileProgressDialog) {
420 		m_fileProgressDialog->setValue(155);
421 		m_fileProgressDialog->setMessage(tr("loading %1 (breadboard)").arg(displayName2));
422 	}
423 
424 	QList<long> newIDs;
425 	m_breadboardGraphicsView->loadFromModelParts(modelParts, BaseCommand::SingleView, NULL, false, NULL, false, newIDs);
426 
427 	ProcessEventBlocker::processEvents();
428 	if (m_fileProgressDialog) {
429 		m_fileProgressDialog->setValue(170);
430 		m_fileProgressDialog->setMessage(tr("loading %1 (pcb)").arg(displayName2));
431 	}
432 
433 	newIDs.clear();
434 	m_pcbGraphicsView->loadFromModelParts(modelParts, BaseCommand::SingleView, NULL, false, NULL, false, newIDs);
435 
436 
437 	ProcessEventBlocker::processEvents();
438 	if (m_fileProgressDialog) {
439 		m_fileProgressDialog->setValue(185);
440 		m_fileProgressDialog->setMessage(tr("loading %1 (schematic)").arg(displayName2));
441 	}
442 
443 	newIDs.clear();
444     m_schematicGraphicsView->setConvertSchematic(m_convertedSchematic);
445     m_schematicGraphicsView->setOldSchematic(this->m_useOldSchematic);
446 	m_schematicGraphicsView->loadFromModelParts(modelParts, BaseCommand::SingleView, NULL, false, NULL, false, newIDs);
447     m_schematicGraphicsView->setConvertSchematic(false);
448 
449 	ProcessEventBlocker::processEvents();
450 	if (m_fileProgressDialog) {
451 		m_fileProgressDialog->setValue(198);
452 	}
453 
454     if (m_obsoleteSMDOrientation) {
455         QSet<ItemBase *> toConvert;
456         foreach (QGraphicsItem * item, m_pcbGraphicsView->items()) {
457             ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
458             if (itemBase == NULL) continue;
459 
460             itemBase = itemBase->layerKinChief();
461             if (itemBase->modelPart()->flippedSMD() && itemBase->viewLayerPlacement() == ViewLayer::NewBottom) {
462                 toConvert.insert(itemBase);
463             }
464         }
465 
466         QList<ConnectorItem *> already;
467         foreach (ItemBase * itemBase, toConvert) {
468             PaletteItem * paletteItem = qobject_cast<PaletteItem *>(itemBase);
469             if (paletteItem == NULL) continue;          // shouldn't happen
470 
471             paletteItem->rotateItem(180, true);
472         }
473     }
474 
475     if (m_programView) {
476         QFileInfo fileInfo(m_fwFilename);
477         m_programView->linkFiles(m_linkedProgramFiles, fileInfo.absoluteDir().absolutePath());
478     }
479 
480     if (!m_useOldSchematic && checkObsolete) {
481         if (m_pcbGraphicsView) {
482             QList<ItemBase *> items = m_pcbGraphicsView->selectAllObsolete();
483 	        if (items.count() > 0) {
484                 checkSwapObsolete(items, true);
485             }
486         }
487     }
488 
489     initZoom();
490 
491 }
492 
copy()493 void MainWindow::copy() {
494 	if (m_currentGraphicsView == NULL) return;
495 	m_currentGraphicsView->copy();
496 }
497 
cut()498 void MainWindow::cut() {
499 	if (m_currentGraphicsView == NULL) return;
500 	m_currentGraphicsView->cut();
501 }
502 
pasteInPlace()503 void MainWindow::pasteInPlace() {
504 	pasteAux(true);
505 }
506 
paste()507 void MainWindow::paste() {
508 	pasteAux(false);
509 }
510 
pasteAux(bool pasteInPlace)511 void MainWindow::pasteAux(bool pasteInPlace)
512 {
513 	if (m_currentGraphicsView == NULL) return;
514 
515 	QClipboard *clipboard = QApplication::clipboard();
516 	if (clipboard == NULL) {
517 		// shouldn't happen
518 		return;
519 	}
520 
521 	const QMimeData* mimeData = clipboard->mimeData(QClipboard::Clipboard);
522 	if (mimeData == NULL) return;
523 
524    	if (!mimeData->hasFormat("application/x-dnditemsdata")) return;
525 
526     QByteArray itemData = mimeData->data("application/x-dnditemsdata");
527 	QList<ModelPart *> modelParts;
528 	QHash<QString, QRectF> boundingRects;
529 	if (m_sketchModel->paste(m_referenceModel, itemData, modelParts, boundingRects, false)) {
530 		QUndoCommand * parentCommand = new QUndoCommand("Paste");
531 
532         QList<SketchWidget *> sketchWidgets;
533         sketchWidgets << m_breadboardGraphicsView << m_schematicGraphicsView << m_pcbGraphicsView;
534         sketchWidgets.removeOne(m_currentGraphicsView);
535         sketchWidgets.prepend(m_currentGraphicsView);
536 
537 		QList<long> newIDs;
538         foreach (SketchWidget * sketchWidget, sketchWidgets) {
539             newIDs.clear();
540             QRectF r;
541 		    QRectF boundingRect = boundingRects.value(sketchWidget->viewName(), r);
542 		    sketchWidget->loadFromModelParts(modelParts, BaseCommand::SingleView, parentCommand, true, pasteInPlace ? &r : &boundingRect, false, newIDs);
543         }
544 
545 		foreach (long id, newIDs) {
546 			new IncLabelTextCommand(m_breadboardGraphicsView, id, parentCommand);
547 		}
548 
549         m_breadboardGraphicsView->setPasting(true);
550         m_pcbGraphicsView->setPasting(true);
551         m_schematicGraphicsView->setPasting(true);
552 		m_undoStack->push(parentCommand);
553         m_breadboardGraphicsView->setPasting(false);
554         m_pcbGraphicsView->setPasting(false);
555         m_schematicGraphicsView->setPasting(false);
556 	}
557 
558     m_currentGraphicsView->updateInfoView();
559 }
560 
duplicate()561 void MainWindow::duplicate() {
562 	if (m_currentGraphicsView == NULL) return;
563 
564 	m_currentGraphicsView->copy();
565 	paste();
566 
567 	//m_currentGraphicsView->duplicate();
568 }
569 
doDelete()570 void MainWindow::doDelete() {
571 	//DebugDialog::debug(QString("invoking do delete") );
572 
573 	if (m_currentGraphicsView != NULL) {
574 		m_currentGraphicsView->deleteSelected(retrieveWire(), false);
575 	}
576 }
577 
doDeleteMinus()578 void MainWindow::doDeleteMinus() {
579 	if (m_currentGraphicsView != NULL) {
580 		m_currentGraphicsView->deleteSelected(retrieveWire(), true);
581 	}
582 }
583 
selectAll()584 void MainWindow::selectAll() {
585 	if (m_currentGraphicsView != NULL) {
586 		m_currentGraphicsView->selectDeselectAllCommand(true);
587 	}
588 }
589 
deselect()590 void MainWindow::deselect() {
591 	if (m_currentGraphicsView != NULL) {
592 		m_currentGraphicsView->selectDeselectAllCommand(false);
593 	}
594 }
595 
about()596 void MainWindow::about()
597 {
598 	AboutBox::showAbout();
599 }
600 
tipsAndTricks()601 void MainWindow::tipsAndTricks()
602 {
603 	TipsAndTricks::showTipsAndTricks();
604 }
605 
firstTimeHelp()606 void MainWindow::firstTimeHelp()
607 {
608     if (m_currentGraphicsView == NULL) return;
609 
610     FirstTimeHelpDialog::setViewID(m_currentGraphicsView->viewID());
611     FirstTimeHelpDialog::showFirstTimeHelp();
612 }
613 
createActions()614 void MainWindow::createActions()
615 {
616     createRaiseWindowActions();
617 
618     createFileMenuActions();
619     createEditMenuActions();
620     createPartMenuActions();
621     createViewMenuActions(true);
622     createWindowMenuActions();
623     createHelpMenuActions();
624 	createTraceMenuActions();
625 }
626 
createRaiseWindowActions()627 void MainWindow::createRaiseWindowActions() {
628 	m_raiseWindowAct = new QAction(m_fwFilename, this);
629 	m_raiseWindowAct->setCheckable(true);
630 	connect( m_raiseWindowAct, SIGNAL(triggered()), this, SLOT(raiseAndActivate()));
631 	updateRaiseWindowAction();
632 }
633 
createFileMenuActions()634 void MainWindow::createFileMenuActions() {
635 	m_newAct = new QAction(tr("New"), this);
636 	m_newAct->setShortcut(tr("Ctrl+N"));
637 	m_newAct->setStatusTip(tr("Create a new sketch"));
638 	connect(m_newAct, SIGNAL(triggered()), this, SLOT(createNewSketch()));
639 
640 	m_openAct = new QAction(tr("&Open..."), this);
641 	m_openAct->setShortcut(tr("Ctrl+O"));
642 	m_openAct->setStatusTip(tr("Open a Fritzing sketch (.fzz, .fz), or load a Fritzing part (.fzpz), or a Fritzing parts bin (.fzb, .fzbz)"));
643 	connect(m_openAct, SIGNAL(triggered()), this, SLOT(mainLoad()));
644 
645 	m_revertAct = new QAction(tr("Revert"), this);
646 	m_revertAct->setStatusTip(tr("Reload the sketch"));
647 	connect(m_revertAct, SIGNAL(triggered()), this, SLOT(revert()));
648 
649 	createOpenRecentMenu();
650 	createOpenExampleMenu();
651 	createCloseAction();
652 	createExportActions();
653 	createOrderFabAct();
654 
655 	QString name;
656 	QString path;
657 	QStringList args;
658 	if (externalProcess(name, path, args)) {
659 		m_launchExternalProcessAct = new QAction(name, this);
660 		m_launchExternalProcessAct->setStatusTip(tr("Shell launch %1").arg(path));
661 		connect(m_launchExternalProcessAct, SIGNAL(triggered()), this, SLOT(launchExternalProcess()));
662 	}
663 
664 #ifndef QT_NO_DEBUG
665     m_exceptionAct = new QAction(tr("throw test exception"), this);
666     m_exceptionAct->setStatusTip(tr("throw a fake exception to see what happens"));
667     connect(m_exceptionAct, SIGNAL(triggered()), this, SLOT(throwFakeException()));
668 #endif
669 
670 	m_quitAct = new QAction(tr("&Quit"), this);
671 	m_quitAct->setShortcut(tr("Ctrl+Q"));
672 	m_quitAct->setStatusTip(tr("Quit the application"));
673 	connect(m_quitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows2()));
674 	m_quitAct->setMenuRole(QAction::QuitRole);
675 
676 }
677 
createOpenExampleMenu()678 void MainWindow::createOpenExampleMenu() {
679 	m_openExampleMenu = new QMenu(tr("&Open Example"), this);
680 	QString folderPath = FolderUtils::getApplicationSubFolderPath("sketches")+"/";
681 	populateMenuFromXMLFile(m_openExampleMenu, m_openExampleActions, folderPath, "index.xml");
682 }
683 
populateMenuFromXMLFile(QMenu * parentMenu,QStringList & actionsTracker,const QString & folderPath,const QString & indexFileName)684 void MainWindow::populateMenuFromXMLFile(QMenu *parentMenu, QStringList &actionsTracker, const QString &folderPath, const QString &indexFileName)
685 {
686 	QDomDocument dom;
687 	QFile file(folderPath+indexFileName);
688 	dom.setContent(&file);
689 
690 	QDomElement domElem = dom.documentElement();
691 	QDomElement indexDomElem = domElem.firstChildElement("sketches");
692 	QDomElement taxonomyDomElem = domElem.firstChildElement("categories");
693 
694     QLocale locale;
695     QString localeName = locale.name().toLower();     // get default translation, a string of the form "language_country" where country is a two-letter code.
696 
697 
698 	QHash<QString, struct SketchDescriptor *> index = indexAvailableElements(indexDomElem, folderPath, actionsTracker, localeName);
699 	QList<SketchDescriptor *> sketchDescriptors(index.values());
700 	qSort(sketchDescriptors.begin(), sketchDescriptors.end(), sortSketchDescriptors);
701 
702 	if (sketchDescriptors.size() > 0) {
703 		// set up the "all" category
704 		QDomElement all = dom.createElement("category");
705 		taxonomyDomElem.appendChild(all);
706         QDomElement language = dom.createElement("language");
707         language.setAttribute("name", tr("All"));
708         all.appendChild(language);
709 		foreach (SketchDescriptor * sketchDescriptor, sketchDescriptors) {
710 			QDomElement sketch = dom.createElement("sketch");
711 			sketch.setAttribute("id", sketchDescriptor->id);
712 			all.appendChild(sketch);
713 		}
714 	}
715 	populateMenuWithIndex(index, parentMenu, taxonomyDomElem, localeName);
716 	foreach (SketchDescriptor * sketchDescriptor, index.values()) {
717 		delete sketchDescriptor;
718 	}
719 }
720 
indexAvailableElements(QDomElement & domElem,const QString & srcPrefix,QStringList & actionsTracker,const QString & localeName)721 QHash<QString, struct SketchDescriptor *> MainWindow::indexAvailableElements(QDomElement &domElem, const QString &srcPrefix, QStringList & actionsTracker, const QString & localeName) {
722 	QHash<QString, struct SketchDescriptor *> retval;
723 	QDomElement sketch = domElem.firstChildElement("sketch");
724 
725     // TODO: eventually need to deal with language/country differences like pt_br vs. pt_pt
726 
727 	while(!sketch.isNull()) {
728 		const QString id = sketch.attribute("id");
729         QDomElement bestLang = getBestLanguageChild(localeName, sketch);
730 		QString name = bestLang.attribute("name");
731 		QString srcAux = bestLang.attribute("src");
732 		// if it's an absolute path, don't prefix it
733 		const QString src = QFileInfo(srcAux).exists()? srcAux: srcPrefix+srcAux;
734 		if(QFileInfo(src).exists()) {
735 			actionsTracker << name;
736 			QAction * action = new QAction(name, this);
737 			action->setData(src);
738 			connect(action,SIGNAL(triggered()),this,SLOT(openRecentOrExampleFile()));
739 			retval[id] = new SketchDescriptor(id,name,src, action);
740 		}
741 		sketch = sketch.nextSiblingElement("sketch");
742 	}
743 	return retval;
744 }
745 
populateMenuWithIndex(const QHash<QString,struct SketchDescriptor * > & index,QMenu * parentMenu,QDomElement & domElem,const QString & localeName)746 void MainWindow::populateMenuWithIndex(const QHash<QString, struct SketchDescriptor *>  &index, QMenu * parentMenu, QDomElement &domElem, const QString & localeName) {
747 	// note: the <sketch> element here is not the same as the <sketch> element in indexAvailableElements()
748 	QDomElement e = domElem.firstChildElement();
749 	while(!e.isNull()) {
750 		if (e.nodeName() == "sketch") {
751 			QString id = e.attribute("id");
752 			if (!id.isEmpty()) {
753 				if(index[id]) {
754 					SketchDescriptor * sketchDescriptor = index[id];
755 					parentMenu->addAction(sketchDescriptor->action);
756 				}
757 				else
758 				{
759 					qWarning() << tr("MainWindow::populateMenuWithIndex: couldn't load example with id='%1'").arg(id);
760 				}
761 			}
762 		}
763 		else if (e.nodeName() == "category") {
764             QDomElement bestLang = getBestLanguageChild(localeName, e);
765 			QString name = bestLang.attribute("name");
766 			QMenu * currMenu = new QMenu(name, parentMenu);
767 			parentMenu->addMenu(currMenu);
768 			populateMenuWithIndex(index, currMenu, e, localeName);
769 		}
770 		else if (e.nodeName() == "separator") {
771 			parentMenu->addSeparator();
772 		}
773 		else if (e.nodeName() == "url") {
774             QDomElement bestLang = getBestLanguageChild(localeName, e);
775 			QAction * action = new QAction(bestLang.attribute("name"), this);
776 			action->setData(bestLang.attribute("href"));
777 			connect(action, SIGNAL(triggered()), this, SLOT(openURL()));
778 			parentMenu->addAction(action);
779 		}
780 		e = e.nextSiblingElement();
781 	}
782 }
783 
populateMenuFromFolderContent(QMenu * parentMenu,const QString & path)784 void MainWindow::populateMenuFromFolderContent(QMenu * parentMenu, const QString &path) {
785 	QDir *currDir = new QDir(path);
786 	QStringList content = currDir->entryList(QDir::AllEntries | QDir::NoDotAndDotDot);
787 	if(content.size() > 0) {
788 		for(int i=0; i < content.size(); i++) {
789 			QString currFile = content.at(i);
790 			QString currFilePath = currDir->absoluteFilePath(currFile);
791 			if(QFileInfo(currFilePath).isDir()) {
792 				QMenu * currMenu = new QMenu(currFile, parentMenu);
793 				parentMenu->addMenu(currMenu);
794 				populateMenuFromFolderContent(currMenu, currFilePath);
795 			} else {
796 				QString actionText = QFileInfo(currFilePath).completeBaseName();
797 				m_openExampleActions << actionText;
798 				QAction * currAction = new QAction(actionText, this);
799 				currAction->setData(currFilePath);
800 				connect(currAction,SIGNAL(triggered()),this,SLOT(openRecentOrExampleFile()));
801 				parentMenu->addAction(currAction);
802 			}
803 		}
804 	} else {
805 		parentMenu->setEnabled(false);
806 	}
807 	delete currDir;
808 }
809 
createOpenRecentMenu()810 void MainWindow::createOpenRecentMenu() {
811 	m_openRecentFileMenu = new QMenu(tr("&Open Recent Files"), this);
812 
813 	for (int i = 0; i < MaxRecentFiles; ++i) {
814 		m_openRecentFileActs[i] = new QAction(this);
815 		m_openRecentFileActs[i]->setVisible(false);
816 		connect(m_openRecentFileActs[i], SIGNAL(triggered()),this, SLOT(openRecentOrExampleFile()));
817 	}
818 
819 
820     for (int i = 0; i < MaxRecentFiles; ++i) {
821     	m_openRecentFileMenu->addAction(m_openRecentFileActs[i]);
822     }
823     updateRecentFileActions();
824 }
825 
updateFileMenu()826 void MainWindow::updateFileMenu() {
827     m_printAct->setEnabled(m_currentGraphicsView != NULL || m_currentWidget->contentView() == m_programView);
828 
829 	updateRecentFileActions();
830 	m_orderFabAct->setEnabled(true);
831 
832     m_revertAct->setEnabled(m_undoStack->canUndo());
833 
834 
835 }
836 
updateRecentFileActions()837 void MainWindow::updateRecentFileActions() {
838 	QSettings settings;
839 	QStringList files = settings.value("recentFileList").toStringList();
840 	int ix = 0;
841 	for (int i = 0; i < files.size(); ++i) {
842 		QFileInfo finfo(files[i]);
843 		if (!finfo.exists()) continue;
844 
845 		QString text = tr("&%1 %2").arg(ix + 1).arg(finfo.fileName());
846 		m_openRecentFileActs[ix]->setText(text);
847 		m_openRecentFileActs[ix]->setData(files[i]);
848 		m_openRecentFileActs[ix]->setVisible(true);
849 		m_openRecentFileActs[ix]->setStatusTip(files[i]);
850 
851 		if (++ix >= (int) MaxRecentFiles) {
852 			break;
853 		}
854 	}
855 
856 	for (int j = ix; j < MaxRecentFiles; ++j) {
857 		m_openRecentFileActs[j]->setVisible(false);
858 	}
859 
860 	m_openRecentFileMenu->setEnabled(ix > 0);
861 }
862 
createEditMenuActions()863 void MainWindow::createEditMenuActions() {
864 	m_undoAct = m_undoGroup->createUndoAction(this, tr("Undo"));
865 	m_undoAct->setShortcuts(QKeySequence::Undo);
866 	m_undoAct->setText(tr("Undo"));
867 
868 	m_redoAct = m_undoGroup->createRedoAction(this, tr("Redo"));
869 	m_redoAct->setShortcuts(QKeySequence::Redo);
870 	m_redoAct->setText(tr("Redo"));
871 
872 	m_cutAct = new QAction(tr("&Cut"), this);
873     m_cutAct->setShortcut(QKeySequence::Cut);
874 	m_cutAct->setStatusTip(tr("Cut selection"));
875 	connect(m_cutAct, SIGNAL(triggered()), this, SLOT(cut()));
876 
877 	m_copyAct = new QAction(tr("&Copy"), this);
878     m_copyAct->setShortcut(QKeySequence::Copy);
879 	m_copyAct->setStatusTip(tr("Copy selection"));
880 	connect(m_copyAct, SIGNAL(triggered()), this, SLOT(copy()));
881 
882 	m_pasteAct = new QAction(tr("&Paste"), this);
883     m_pasteAct->setShortcut(QKeySequence::Paste);
884 	m_pasteAct->setStatusTip(tr("Paste clipboard contents"));
885 	connect(m_pasteAct, SIGNAL(triggered()), this, SLOT(paste()));
886 
887 	m_pasteInPlaceAct = new QAction(tr("Paste in Place"), this);
888     m_pasteInPlaceAct->setShortcut(tr("Ctrl+Shift+V"));
889 	m_pasteInPlaceAct->setStatusTip(tr("Paste clipboard contents in place"));
890 	connect(m_pasteInPlaceAct, SIGNAL(triggered()), this, SLOT(pasteInPlace()));
891 
892 	m_duplicateAct = new QAction(tr("&Duplicate"), this);
893 	m_duplicateAct->setShortcut(tr("Ctrl+D"));
894 	m_duplicateAct->setStatusTip(tr("Duplicate selection"));
895 	connect(m_duplicateAct, SIGNAL(triggered()), this, SLOT(duplicate()));
896 
897 	m_deleteAct = new QAction(tr("&Delete"), this);
898 	m_deleteAct->setStatusTip(tr("Delete selection"));
899 	connect(m_deleteAct, SIGNAL(triggered()), this, SLOT(doDelete()));
900 	#ifdef Q_OS_MAC
901 		m_deleteAct->setShortcut(Qt::Key_Backspace);
902 	#else
903 		m_deleteAct->setShortcut(QKeySequence::Delete);
904 	#endif
905 
906     m_deleteMinusAct = new QAction(tr("Delete Minus"), this);
907 	m_deleteMinusAct->setStatusTip(tr("Delete selection without attached wires"));
908 	connect(m_deleteMinusAct, SIGNAL(triggered()), this, SLOT(doDeleteMinus()));
909     #ifdef Q_OS_MAC
910         m_deleteMinusAct->setShortcut(Qt::Key_Backspace | Qt::AltModifier);
911     #endif
912 
913 	m_deleteWireAct = new WireAction(m_deleteAct);
914 	m_deleteWireAct->setText(tr("&Delete Wire"));
915 	connect(m_deleteWireAct, SIGNAL(triggered()), this, SLOT(doDelete()));
916 
917 	m_deleteWireMinusAct = new WireAction(m_deleteMinusAct);
918 	m_deleteWireMinusAct->setText(tr("Delete Wire up to bendpoints"));
919 	connect(m_deleteWireMinusAct, SIGNAL(triggered()), this, SLOT(doDeleteMinus()));
920 
921 	m_selectAllAct = new QAction(tr("&Select All"), this);
922     m_selectAllAct->setShortcut(QKeySequence::SelectAll);
923 	m_selectAllAct->setStatusTip(tr("Select all elements"));
924 	connect(m_selectAllAct, SIGNAL(triggered()), this, SLOT(selectAll()));
925 
926 	m_deselectAct = new QAction(tr("&Deselect"), this);
927 	m_deselectAct->setStatusTip(tr("Deselect"));
928 	connect(m_deselectAct, SIGNAL(triggered()), this, SLOT(deselect()));
929 
930 	m_addNoteAct = new QAction(tr("Add Note"), this);
931 	m_addNoteAct->setStatusTip(tr("Add a note"));
932 	connect(m_addNoteAct, SIGNAL(triggered()), this, SLOT(addNote()));
933 
934 	m_preferencesAct = new QAction(tr("&Preferences..."), this);
935 	m_preferencesAct->setStatusTip(tr("Show the application's about box"));
936 	m_preferencesAct->setMenuRole(QAction::PreferencesRole);						// make sure this is added to the correct menu on mac
937 	connect(m_preferencesAct, SIGNAL(triggered()), QApplication::instance(), SLOT(preferences()));
938 }
939 
createPartMenuActions()940 void MainWindow::createPartMenuActions() {
941 	m_openInPartsEditorNewAct = new QAction(tr("Edit (new parts editor)"), this);
942 	m_openInPartsEditorNewAct->setStatusTip(tr("Open the new parts editor on an existing part"));
943 	connect(m_openInPartsEditorNewAct, SIGNAL(triggered()), this, SLOT(openInPartsEditorNew()));
944 
945 	m_disconnectAllAct = new QAction(tr("Disconnect All Wires"), this);
946 	m_disconnectAllAct->setStatusTip(tr("Disconnect all wires connected to this connector"));
947 	connect(m_disconnectAllAct, SIGNAL(triggered()), this, SLOT(disconnectAll()));
948 
949 #ifndef QT_NO_DEBUG
950 	m_infoViewOnHoverAction = new QAction(tr("Update InfoView on hover"), this);
951 	m_infoViewOnHoverAction->setCheckable(true);
952 	bool infoViewOnHover = true;
953 	m_infoViewOnHoverAction->setChecked(infoViewOnHover);
954 	setInfoViewOnHover(infoViewOnHover);
955 	connect(m_infoViewOnHoverAction, SIGNAL(toggled(bool)), this, SLOT(setInfoViewOnHover(bool)));
956 
957 	m_exportNormalizedSvgAction = new QAction(tr("Export Normalized SVG"), this);
958 	m_exportNormalizedSvgAction->setStatusTip(tr("Export 1000 dpi SVG of this part in this view"));
959 	connect(m_exportNormalizedSvgAction, SIGNAL(triggered()), this, SLOT(exportNormalizedSVG()));
960 
961 	m_exportNormalizedFlattenedSvgAction = new QAction(tr("Export Normalized Flattened SVG"), this);
962 	m_exportNormalizedFlattenedSvgAction->setStatusTip(tr("Export 1000 dpi Flattened SVG of this part in this view"));
963 	connect(m_exportNormalizedFlattenedSvgAction, SIGNAL(triggered()), this, SLOT(exportNormalizedFlattenedSVG()));
964 
965 	m_dumpAllPartsAction = new QAction(tr("Dump all parts"), this);
966 	m_dumpAllPartsAction->setStatusTip(tr("Debug dump all parts in this view"));
967 	connect(m_dumpAllPartsAction, SIGNAL(triggered()), this, SLOT(dumpAllParts()));
968 
969 	m_testConnectorsAction = new QAction(tr("Test Connectors"), this);
970 	m_testConnectorsAction->setStatusTip(tr("Connect all connectors to a single test part"));
971 	connect(m_testConnectorsAction, SIGNAL(triggered()), this, SLOT(testConnectors()));
972 
973 #endif
974 
975 
976 	m_rotate45cwAct = new QAction(tr("Rotate 45\x00B0 Clockwise"), this);
977 	m_rotate45cwAct->setStatusTip(tr("Rotate current selection 45 degrees clockwise"));
978 	connect(m_rotate45cwAct, SIGNAL(triggered()), this, SLOT(rotate45cw()));
979 
980 	m_rotate90cwAct = new QAction(tr("Rotate 90\x00B0 Clockwise"), this);
981 	m_rotate90cwAct->setStatusTip(tr("Rotate the selected parts by 90 degrees clockwise"));
982 	connect(m_rotate90cwAct, SIGNAL(triggered()), this, SLOT(rotate90cw()));
983 
984 	m_rotate180Act = new QAction(tr("Rotate 180\x00B0"), this);
985 	m_rotate180Act->setStatusTip(tr("Rotate the selected parts by 180 degrees"));
986 	connect(m_rotate180Act, SIGNAL(triggered()), this, SLOT(rotate180()));
987 
988 	m_rotate90ccwAct = new QAction(tr("Rotate 90\x00B0 Counter Clockwise"), this);
989 	m_rotate90ccwAct->setStatusTip(tr("Rotate current selection 90 degrees counter clockwise"));
990 	connect(m_rotate90ccwAct, SIGNAL(triggered()), this, SLOT(rotate90ccw()));
991 
992 	m_rotate45ccwAct = new QAction(tr("Rotate 45\x00B0 Counter Clockwise"), this);
993 	m_rotate45ccwAct->setStatusTip(tr("Rotate current selection 45 degrees counter clockwise"));
994 	connect(m_rotate45ccwAct, SIGNAL(triggered()), this, SLOT(rotate45ccw()));
995 
996 	m_flipHorizontalAct = new QAction(tr("&Flip Horizontal"), this);
997 	m_flipHorizontalAct->setStatusTip(tr("Flip current selection horizontally"));
998 	connect(m_flipHorizontalAct, SIGNAL(triggered()), this, SLOT(flipHorizontal()));
999 
1000 	m_flipVerticalAct = new QAction(tr("&Flip Vertical"), this);
1001 	m_flipVerticalAct->setStatusTip(tr("Flip current selection vertically"));
1002 	connect(m_flipVerticalAct, SIGNAL(triggered()), this, SLOT(flipVertical()));
1003 
1004 	m_bringToFrontAct = new QAction(tr("Bring to Front"), this);
1005 	m_bringToFrontAct->setShortcut(tr("Shift+Ctrl+]"));
1006     m_bringToFrontAct->setStatusTip(tr("Bring selected object(s) to front of their layer"));
1007     connect(m_bringToFrontAct, SIGNAL(triggered()), this, SLOT(bringToFront()));
1008 	m_bringToFrontWireAct = new WireAction(m_bringToFrontAct);
1009     connect(m_bringToFrontWireAct, SIGNAL(triggered()), this, SLOT(bringToFront()));
1010 
1011 	m_bringForwardAct = new QAction(tr("Bring Forward"), this);
1012 	m_bringForwardAct->setShortcut(tr("Ctrl+]"));
1013     m_bringForwardAct->setStatusTip(tr("Bring selected object(s) forward in their layer"));
1014     connect(m_bringForwardAct, SIGNAL(triggered()), this, SLOT(bringForward()));
1015 	m_bringForwardWireAct = new WireAction(m_bringForwardAct);
1016     connect(m_bringForwardWireAct, SIGNAL(triggered()), this, SLOT(bringForward()));
1017 
1018 	m_sendBackwardAct = new QAction(tr("Send Backward"), this);
1019 	m_sendBackwardAct->setShortcut(tr("Ctrl+["));
1020     m_sendBackwardAct->setStatusTip(tr("Send selected object(s) back in their layer"));
1021     connect(m_sendBackwardAct, SIGNAL(triggered()), this, SLOT(sendBackward()));
1022 	m_sendBackwardWireAct = new WireAction(m_sendBackwardAct);
1023     connect(m_sendBackwardWireAct, SIGNAL(triggered()), this, SLOT(sendBackward()));
1024 
1025 	m_sendToBackAct = new QAction(tr("Send to Back"), this);
1026 	m_sendToBackAct->setShortcut(tr("Shift+Ctrl+["));
1027     m_sendToBackAct->setStatusTip(tr("Send selected object(s) to the back of their layer"));
1028     connect(m_sendToBackAct, SIGNAL(triggered()), this, SLOT(sendToBack()));
1029 	m_sendToBackWireAct = new WireAction(m_sendToBackAct);
1030     connect(m_sendToBackWireAct, SIGNAL(triggered()), this, SLOT(sendToBack()));
1031 
1032 	m_alignLeftAct = new QAction(tr("Align Left"), this);
1033     m_alignLeftAct->setStatusTip(tr("Align selected items at the left"));
1034     connect(m_alignLeftAct, SIGNAL(triggered()), this, SLOT(alignLeft()));
1035 
1036 	m_alignHorizontalCenterAct = new QAction(tr("Align Horizontal Center"), this);
1037     m_alignHorizontalCenterAct->setStatusTip(tr("Align selected items at the horizontal center"));
1038     connect(m_alignHorizontalCenterAct, SIGNAL(triggered()), this, SLOT(alignHorizontalCenter()));
1039 
1040     m_alignRightAct = new QAction(tr("Align Right"), this);
1041     m_alignRightAct->setStatusTip(tr("Align selected items at the right"));
1042     connect(m_alignRightAct, SIGNAL(triggered()), this, SLOT(alignRight()));
1043 
1044     m_alignTopAct = new QAction(tr("Align Top"), this);
1045     m_alignTopAct->setStatusTip(tr("Align selected items at the top"));
1046     connect(m_alignTopAct, SIGNAL(triggered()), this, SLOT(alignTop()));
1047 
1048 	m_alignVerticalCenterAct = new QAction(tr("Align Vertical Center"), this);
1049     m_alignVerticalCenterAct->setStatusTip(tr("Align selected items at the vertical center"));
1050     connect(m_alignVerticalCenterAct, SIGNAL(triggered()), this, SLOT(alignVerticalCenter()));
1051 
1052 	m_alignBottomAct = new QAction(tr("Align Bottom"), this);
1053     m_alignBottomAct->setStatusTip(tr("Align selected items at the bottom"));
1054     connect(m_alignBottomAct, SIGNAL(triggered()), this, SLOT(alignBottom()));
1055 
1056 	m_moveLockAct = new QAction(tr("Lock Part"), this);
1057     m_moveLockAct->setStatusTip(tr("Prevent a part from being moved"));
1058 	m_moveLockAct->setCheckable(true);
1059     connect(m_moveLockAct, SIGNAL(triggered()), this, SLOT(moveLock()));
1060 
1061 	m_stickyAct = new QAction(tr("Sticky"), this);
1062     m_stickyAct->setStatusTip(tr("If a \"sticky\" part is moved, parts on top of it are also moved"));
1063 	m_stickyAct->setCheckable(true);
1064     connect(m_stickyAct, SIGNAL(triggered()), this, SLOT(setSticky()));
1065 
1066 	m_selectMoveLockAct = new QAction(tr("Select All Locked Parts"), this);
1067     m_selectMoveLockAct->setStatusTip(tr("Select all parts that can't be moved"));
1068     connect(m_selectMoveLockAct, SIGNAL(triggered()), this, SLOT(selectMoveLock()));
1069 
1070 	m_showPartLabelAct = new QAction(tr("&Show part label"), this);
1071 	m_showPartLabelAct->setStatusTip(tr("Show/hide the label for the selected parts"));
1072 	connect(m_showPartLabelAct, SIGNAL(triggered()), this, SLOT(showPartLabels()));
1073 
1074 	m_saveBundledPart = new QAction(tr("&Export..."), this);
1075 	m_saveBundledPart->setStatusTip(tr("Export selected part"));
1076 	connect(m_saveBundledPart, SIGNAL(triggered()), this, SLOT(saveBundledPart()));
1077 
1078 	m_addBendpointAct = new BendpointAction(tr("Add Bendpoint"), this);
1079 	m_addBendpointAct->setStatusTip(tr("Add a bendpoint to the selected wire"));
1080 	connect(m_addBendpointAct, SIGNAL(triggered()), this, SLOT(addBendpoint()));
1081 
1082 	m_convertToViaAct = new BendpointAction(tr("Convert Bendpoint to Via"), this);
1083 	m_convertToViaAct->setStatusTip(tr("Convert the bendpoint to a via"));
1084 	connect(m_convertToViaAct, SIGNAL(triggered()), this, SLOT(convertToVia()));
1085 
1086 	m_convertToBendpointAct = new QAction(tr("Convert Via to Bendpoint"), this);
1087 	m_convertToBendpointAct->setStatusTip(tr("Convert the via to a bendpoint"));
1088 	connect(m_convertToBendpointAct, SIGNAL(triggered()), this, SLOT(convertToBendpoint()));
1089 
1090 	m_flattenCurveAct = new BendpointAction(tr("Straighten Curve"), this);
1091 	m_flattenCurveAct->setStatusTip(tr("Straighten the curve of the selected wire"));
1092 	connect(m_flattenCurveAct, SIGNAL(triggered()), this, SLOT(flattenCurve()));
1093 
1094     m_selectAllObsoleteAct = new QAction(tr("Select outdated parts"), this);
1095     m_selectAllObsoleteAct->setStatusTip(tr("Select outdated parts"));
1096 	connect(m_selectAllObsoleteAct, SIGNAL(triggered()), this, SLOT(selectAllObsolete()));
1097 
1098     m_swapObsoleteAct = new QAction(tr("Update selected parts"), this);
1099     m_swapObsoleteAct->setStatusTip(tr("Update selected parts"));
1100 	connect(m_swapObsoleteAct, SIGNAL(triggered()), this, SLOT(swapObsolete()));
1101 
1102     m_findPartInSketchAct = new QAction(tr("Find part in sketch..."), this);
1103     m_findPartInSketchAct->setStatusTip(tr("Search for parts in a sketch by matching text"));
1104 	connect(m_findPartInSketchAct, SIGNAL(triggered()), this, SLOT(findPartInSketch()));
1105 
1106     m_openProgramWindowAct = new QAction(tr("Open programming window"), this);
1107     m_openProgramWindowAct->setStatusTip(tr("Open microcontroller programming window"));
1108 	connect(m_openProgramWindowAct, SIGNAL(triggered()), this, SLOT(openProgramWindow()));
1109 
1110 	m_hidePartSilkscreenAct = new QAction(tr("Hide part silkscreen"), this);
1111 	m_hidePartSilkscreenAct->setStatusTip(tr("Hide/show the silkscreen layer for only this part"));
1112 	connect(m_hidePartSilkscreenAct, SIGNAL(triggered()), this, SLOT(hidePartSilkscreen()));
1113 
1114 
1115 }
1116 
createViewMenuActions(bool showWelcome)1117 void MainWindow::createViewMenuActions(bool showWelcome) {
1118 	m_zoomInAct = new QAction(tr("&Zoom In"), this);
1119 	m_zoomInAct->setShortcut(tr("Ctrl++"));
1120 	m_zoomInAct->setStatusTip(tr("Zoom in"));
1121 	connect(m_zoomInAct, SIGNAL(triggered()), this, SLOT(zoomIn()));
1122 
1123 	// instead of creating a filter to grab the shortcut, let's create a new action
1124 	// and append it to the window
1125 	m_zoomInShortcut = new QAction(this);
1126 	m_zoomInShortcut->setShortcut(tr("Ctrl+="));
1127 	connect(m_zoomInShortcut, SIGNAL(triggered()), this, SLOT(zoomIn()));
1128 	this->addAction(m_zoomInShortcut);
1129 
1130 	m_zoomOutAct = new QAction(tr("&Zoom Out"), this);
1131 	m_zoomOutAct->setShortcut(tr("Ctrl+-"));
1132 	m_zoomOutAct->setStatusTip(tr("Zoom out"));
1133 	connect(m_zoomOutAct, SIGNAL(triggered()), this, SLOT(zoomOut()));
1134 
1135 	m_fitInWindowAct = new QAction(tr("&Fit in Window"), this);
1136 	m_fitInWindowAct->setShortcut(tr("Ctrl+0"));
1137 	m_fitInWindowAct->setStatusTip(tr("Fit in window"));
1138 	connect(m_fitInWindowAct, SIGNAL(triggered()), this, SLOT(fitInWindow()));
1139 
1140 	m_actualSizeAct = new QAction(tr("&Actual Size"), this);
1141 	m_actualSizeAct->setStatusTip(tr("Actual (real world physical) size"));
1142 	connect(m_actualSizeAct, SIGNAL(triggered()), this, SLOT(actualSize()));
1143 
1144 	m_100PercentSizeAct = new QAction(tr("100% Size"), this);
1145 	m_100PercentSizeAct->setShortcut(tr("Shift+Ctrl+0"));
1146 	m_100PercentSizeAct->setStatusTip(tr("100% (pixel) size"));
1147 	connect(m_100PercentSizeAct, SIGNAL(triggered()), this, SLOT(hundredPercentSize()));
1148 
1149 	m_alignToGridAct = new QAction(tr("Align to Grid"), this);
1150 	m_alignToGridAct->setStatusTip(tr("Align items to grid when dragging"));
1151 	m_alignToGridAct->setCheckable(true);
1152 	connect(m_alignToGridAct, SIGNAL(triggered()), this, SLOT(alignToGrid()));
1153 
1154 	m_showGridAct = new QAction(tr("Show Grid"), this);
1155 	m_showGridAct->setStatusTip(tr("Show the grid"));
1156 	m_showGridAct->setCheckable(true);
1157 	connect(m_showGridAct, SIGNAL(triggered()), this, SLOT(showGrid()));
1158 
1159 	m_setGridSizeAct = new QAction(tr("Set Grid Size..."), this);
1160 	m_setGridSizeAct->setStatusTip(tr("Set the size of the grid in this view"));
1161 	connect(m_setGridSizeAct, SIGNAL(triggered()), this, SLOT(setGridSize()));
1162 
1163 	m_setBackgroundColorAct = new QAction(tr("Set Background Color..."), this);
1164 	m_setBackgroundColorAct->setStatusTip(tr("Set the background color of this view"));
1165 	connect(m_setBackgroundColorAct, SIGNAL(triggered()), this, SLOT(setBackgroundColor()));
1166 
1167 	QStringList controls;
1168 	controls << tr("Ctrl+1") << tr("Ctrl+2") << tr("Ctrl+3") << tr("Ctrl+4") << tr("Ctrl+5");
1169 	int controlIndex = 0;
1170 	if (showWelcome) {
1171 		m_showWelcomeAct = new QAction(tr("&Show Welcome"), this);
1172 		m_showWelcomeAct->setShortcut(controls.at(controlIndex++));
1173 		m_showWelcomeAct->setStatusTip(tr("Show the welcome view"));
1174         m_showWelcomeAct->setCheckable(true);
1175         connect(m_showWelcomeAct, SIGNAL(triggered()), this, SLOT(showWelcomeView()));
1176 	}
1177 
1178 	m_showBreadboardAct = new QAction(tr("&Show Breadboard"), this);
1179 	m_showBreadboardAct->setShortcut(controls.at(controlIndex++));
1180 	m_showBreadboardAct->setStatusTip(tr("Show the breadboard view"));
1181     m_showBreadboardAct->setCheckable(true);
1182 	connect(m_showBreadboardAct, SIGNAL(triggered()), this, SLOT(showBreadboardView()));
1183 
1184 	m_showSchematicAct = new QAction(tr("&Show Schematic"), this);
1185 	m_showSchematicAct->setShortcut(controls.at(controlIndex++));
1186 	m_showSchematicAct->setStatusTip(tr("Show the schematic view"));
1187     m_showSchematicAct->setCheckable(true);
1188     connect(m_showSchematicAct, SIGNAL(triggered()), this, SLOT(showSchematicView()));
1189 
1190 	m_showPCBAct = new QAction(tr("&Show PCB"), this);
1191 	m_showPCBAct->setShortcut(controls.at(controlIndex++));
1192 	m_showPCBAct->setStatusTip(tr("Show the PCB view"));
1193     m_showPCBAct->setCheckable(true);
1194     connect(m_showPCBAct, SIGNAL(triggered()), this, SLOT(showPCBView()));
1195 
1196     if (m_programView) {
1197 	    m_showProgramAct = new QAction(tr("Show Code"), this);
1198 	    m_showProgramAct->setShortcut(controls.at(controlIndex++));
1199 	    m_showProgramAct->setStatusTip(tr("Show the code (programming) view"));
1200         m_showProgramAct->setCheckable(true);
1201 	    connect(m_showProgramAct, SIGNAL(triggered()), this, SLOT(showProgramView()));
1202         QList<QAction *> viewMenuActions;
1203 		if (m_welcomeView) viewMenuActions << m_showWelcomeAct;
1204         viewMenuActions << m_showBreadboardAct << m_showSchematicAct << m_showPCBAct << m_showProgramAct;
1205         m_programView->createViewMenuActions(viewMenuActions);
1206     }
1207 
1208 	m_showPartsBinIconViewAct = new QAction(tr("Show Parts Bin Icon View"), this);
1209     m_showPartsBinIconViewAct->setStatusTip(tr("Display the parts bin in an icon view"));
1210     m_showPartsBinIconViewAct->setCheckable(true);
1211 	connect(m_showPartsBinIconViewAct, SIGNAL(triggered()), this, SLOT(showPartsBinIconView()));
1212 
1213 	m_showPartsBinListViewAct = new QAction(tr("Show Parts Bin List View"), this);
1214 	m_showPartsBinListViewAct->setStatusTip(tr("Display the parts bin in a list view"));
1215     m_showPartsBinListViewAct->setCheckable(true);
1216     connect(m_showPartsBinListViewAct, SIGNAL(triggered()), this, SLOT(showPartsBinListView()));
1217 
1218 	m_showAllLayersAct = new QAction(tr("&Show All Layers"), this);
1219 	m_showAllLayersAct->setStatusTip(tr("Show all the available layers for the current view"));
1220 	connect(m_showAllLayersAct, SIGNAL(triggered()), this, SLOT(showAllLayers()));
1221 
1222 	m_hideAllLayersAct = new QAction(tr("&Hide All Layers"), this);
1223 	m_hideAllLayersAct->setStatusTip(tr("Hide all the layers of the current view"));
1224 	connect(m_hideAllLayersAct, SIGNAL(triggered()), this, SLOT(hideAllLayers()));
1225 
1226 }
1227 
createWindowMenuActions()1228 void MainWindow::createWindowMenuActions() {
1229 	m_minimizeAct = new QAction(tr("&Minimize"), this);
1230 	m_minimizeAct->setShortcut(tr("Ctrl+M"));
1231 	m_minimizeAct->setStatusTip(tr("Minimize current window"));
1232 	connect(m_minimizeAct, SIGNAL(triggered(bool)), this, SLOT(minimize()));
1233 
1234 	/*
1235 	m_toggleToolbarAct = new QAction(tr("&Toolbar"), this);
1236 	m_toggleToolbarAct->setShortcut(tr("Shift+Ctrl+T"));
1237 	m_toggleToolbarAct->setCheckable(true);
1238 	m_toggleToolbarAct->setChecked(true);
1239 	m_toggleToolbarAct->setStatusTip(tr("Toggle Toolbar visibility"));
1240 	connect(m_toggleToolbarAct, SIGNAL(triggered(bool)), this, SLOT(toggleToolbar(bool)));
1241 	*/
1242 
1243     m_toggleDebuggerOutputAct = new QAction(tr("Debugger Output"), this);
1244     m_toggleDebuggerOutputAct->setCheckable(true);
1245    	connect(m_toggleDebuggerOutputAct, SIGNAL(triggered(bool)), this, SLOT(toggleDebuggerOutput(bool)));
1246 }
1247 
createHelpMenuActions()1248 void MainWindow::createHelpMenuActions() {
1249 	m_openHelpAct = new QAction(tr("Online Tutorials"), this);
1250 	m_openHelpAct->setShortcut(tr("Ctrl+?"));
1251 	m_openHelpAct->setStatusTip(tr("Open Fritzing help"));
1252 	connect(m_openHelpAct, SIGNAL(triggered(bool)), this, SLOT(openHelp()));
1253 
1254 	m_openDonateAct = new QAction(tr("Donate to Fritzing"), this);
1255 	m_openDonateAct->setStatusTip(tr("Open Fritzing donation web page"));
1256 	connect(m_openDonateAct, SIGNAL(triggered(bool)), this, SLOT(openDonate()));
1257 
1258 	m_examplesAct = new QAction(tr("Online Projects Gallery"), this);
1259 	m_examplesAct->setStatusTip(tr("Open Fritzing examples"));
1260 	connect(m_examplesAct, SIGNAL(triggered(bool)), this, SLOT(openExamples()));
1261 
1262 	m_partsRefAct = new QAction(tr("Online Parts Reference"), this);
1263 	m_partsRefAct->setStatusTip(tr("Open Parts Reference"));
1264 	connect(m_partsRefAct, SIGNAL(triggered(bool)), this, SLOT(openPartsReference()));
1265 
1266 	/*m_visitFritzingDotOrgAct = new QAction(tr("Visit fritzing.org"), this);
1267 	m_visitFritzingDotOrgAct->setStatusTip(tr("www.fritzing.org"));
1268 	connect(m_visitFritzingDotOrgAct, SIGNAL(triggered(bool)), this, SLOT(visitFritzingDotOrg()));*/
1269 
1270 	m_checkForUpdatesAct = new QAction(tr("Check for updates..."), this);
1271 	m_checkForUpdatesAct->setStatusTip(tr("Check whether a newer version of Fritzing is available for download"));
1272 	connect(m_checkForUpdatesAct, SIGNAL(triggered()), QApplication::instance(), SLOT(checkForUpdates()));
1273 
1274 	m_aboutAct = new QAction(tr("&About"), this);
1275 	m_aboutAct->setStatusTip(tr("Show the application's about box"));
1276 	connect(m_aboutAct, SIGNAL(triggered()), this, SLOT(about()));
1277 	m_aboutAct->setMenuRole(QAction::AboutRole);
1278 
1279 	m_tipsAndTricksAct = new QAction(tr("Tips, Tricks and Shortcuts"), this);
1280 	m_tipsAndTricksAct->setStatusTip(tr("Display some handy Fritzing tips and tricks"));
1281 	connect(m_tipsAndTricksAct, SIGNAL(triggered()), this, SLOT(tipsAndTricks()));
1282 
1283 	m_firstTimeHelpAct = new QAction(tr("First Time Help"), this);
1284 	m_firstTimeHelpAct->setStatusTip(tr("Display First Time Help"));
1285 	connect(m_firstTimeHelpAct, SIGNAL(triggered()), this, SLOT(firstTimeHelp()));
1286 
1287 	m_aboutQtAct = new QAction(tr("&About Qt"), this);
1288 	m_aboutQtAct->setStatusTip(tr("Show Qt's about box"));
1289 	connect(m_aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
1290 
1291 	m_reportBugAct = new QAction(tr("Report a bug..."), this);
1292 	m_reportBugAct->setStatusTip(tr("Report a but you've found in Fritzing"));
1293 	connect(m_reportBugAct, SIGNAL(triggered()), this, SLOT(reportBug()));
1294 
1295 	m_enableDebugAct = new QAction(tr("Enable debugging log"), this);
1296 	m_enableDebugAct->setStatusTip(tr("Report a but you've found in Fritzing"));
1297 	m_enableDebugAct->setCheckable(true);
1298 	m_enableDebugAct->setChecked(DebugDialog::enabled());
1299 	connect(m_enableDebugAct, SIGNAL(triggered()), this, SLOT(enableDebug()));
1300 
1301 	m_importFilesFromPrevInstallAct = new QAction(tr("&Import parts and bins from old version..."), this);
1302 	m_importFilesFromPrevInstallAct->setStatusTip(tr("Import parts and bins from previous installation"));
1303 	connect(m_importFilesFromPrevInstallAct, SIGNAL(triggered()), this, SLOT(importFilesFromPrevInstall()));
1304 
1305 	m_partsEditorHelpAct = new QAction(tr("Parts Editor Help"), this);
1306 	m_partsEditorHelpAct->setStatusTip(tr("Display Parts Editor help in a browser"));
1307 	connect(m_partsEditorHelpAct, SIGNAL(triggered(bool)), this, SLOT(partsEditorHelp()));
1308 
1309 
1310 }
1311 
createMenus()1312 void MainWindow::createMenus()
1313 {
1314     createFileMenu();
1315     createEditMenu();
1316     createPartMenu();
1317     createViewMenu();
1318     m_programView->initMenus(menuBar());
1319     createWindowMenu();
1320     createTraceMenus();
1321     createHelpMenu();
1322 }
1323 
createRotateSubmenu(QMenu * parentMenu)1324 void MainWindow::createRotateSubmenu(QMenu * parentMenu) {
1325     QMenu *rotateMenu = parentMenu->addMenu(tr("Rotate"));
1326     rotateMenu->addAction(m_rotate45cwAct);
1327     rotateMenu->addAction(m_rotate90cwAct);
1328     rotateMenu->addAction(m_rotate180Act);
1329     rotateMenu->addAction(m_rotate90ccwAct);
1330     rotateMenu->addAction(m_rotate45ccwAct);
1331 }
1332 
createZOrderSubmenu(QMenu * parentMenu)1333 void MainWindow::createZOrderSubmenu(QMenu * parentMenu) {
1334     QMenu *zOrderMenu = parentMenu->addMenu(tr("Raise and Lower"));
1335     zOrderMenu->addAction(m_bringToFrontAct);
1336     zOrderMenu->addAction(m_bringForwardAct);
1337     zOrderMenu->addAction(m_sendBackwardAct);
1338     zOrderMenu->addAction(m_sendToBackAct);
1339 }
1340 
createZOrderWireSubmenu(QMenu * parentMenu)1341 void MainWindow::createZOrderWireSubmenu(QMenu * parentMenu) {
1342     QMenu *zOrderWireMenu = parentMenu->addMenu(tr("Raise and Lower"));
1343     zOrderWireMenu->addAction(m_bringToFrontWireAct);
1344     zOrderWireMenu->addAction(m_bringForwardWireAct);
1345     zOrderWireMenu->addAction(m_sendBackwardWireAct);
1346     zOrderWireMenu->addAction(m_sendToBackWireAct);
1347 }
1348 
createAlignSubmenu(QMenu * parentMenu)1349 void MainWindow::createAlignSubmenu(QMenu * parentMenu) {
1350     QMenu *alignMenu = parentMenu->addMenu(tr("Align"));
1351     alignMenu->addAction(m_alignLeftAct);
1352     alignMenu->addAction(m_alignHorizontalCenterAct);
1353     alignMenu->addAction(m_alignRightAct);
1354     alignMenu->addAction(m_alignTopAct);
1355     alignMenu->addAction(m_alignVerticalCenterAct);
1356     alignMenu->addAction(m_alignBottomAct);
1357 }
1358 
createAddToBinSubmenu(QMenu * parentMenu)1359 void MainWindow::createAddToBinSubmenu(QMenu * parentMenu) {
1360     QMenu *addToBinMenu = parentMenu->addMenu(tr("&Add to bin..."));
1361     addToBinMenu->setStatusTip(tr("Add selected part to bin"));
1362     QList<QAction*> acts = m_binManager->openedBinsActions(selectedModuleID());
1363     addToBinMenu->addActions(acts);
1364 }
1365 
createFileMenu()1366 void MainWindow::createFileMenu() {
1367     m_fileMenu = menuBar()->addMenu(tr("&File"));
1368     m_fileMenu->addAction(m_newAct);
1369     m_fileMenu->addAction(m_openAct);
1370     m_fileMenu->addAction(m_revertAct);
1371     m_fileMenu->addMenu(m_openRecentFileMenu);
1372     m_fileMenu->addMenu(m_openExampleMenu);
1373 
1374     m_fileMenu->addSeparator();
1375     m_fileMenu->addAction(m_closeAct);
1376     m_fileMenu->addAction(m_saveAct);
1377     m_fileMenu->addAction(m_saveAsAct);
1378     m_fileMenu->addAction(m_shareOnlineAct);
1379 
1380 	if (m_orderFabEnabled) {
1381 		m_fileMenu->addAction(m_orderFabAct);
1382 	}
1383 
1384     m_fileMenu->addSeparator();
1385 	m_exportMenu = m_fileMenu->addMenu(tr("&Export"));
1386     connect(m_exportMenu, SIGNAL(aboutToShow()), this, SLOT(updateExportMenu()));
1387     //m_fileMenu->addAction(m_pageSetupAct);
1388     m_fileMenu->addAction(m_printAct);
1389 
1390 	QString name;
1391 	QString path;
1392 	QStringList args;
1393 	if (externalProcess(name, path, args)) {
1394 		m_fileMenu->addSeparator();
1395 		m_fileMenu->addAction(m_launchExternalProcessAct);
1396 	}
1397 
1398 #ifndef QT_NO_DEBUG
1399     m_fileMenu->addSeparator();
1400     m_fileMenu->addAction(m_exceptionAct);
1401 #endif
1402 
1403 	m_fileMenu->addSeparator();
1404 	m_fileMenu->addAction(m_quitAct);
1405     connect(m_fileMenu, SIGNAL(aboutToShow()), this, SLOT(updateFileMenu()));
1406 
1407 	populateExportMenu();
1408 
1409 	m_exportMenu->addAction(m_exportBomAct);
1410 	m_exportMenu->addAction(m_exportNetlistAct);
1411 	m_exportMenu->addAction(m_exportSpiceNetlistAct);
1412 
1413 	//m_exportMenu->addAction(m_exportEagleAct);
1414 }
1415 
populateExportMenu()1416 void MainWindow::populateExportMenu() {
1417 	QMenu * imageMenu = m_exportMenu->addMenu(tr("as Image"));
1418 	imageMenu->addAction(m_exportPngAct);
1419 	imageMenu->addAction(m_exportJpgAct);
1420 	imageMenu->addSeparator();
1421 	imageMenu->addAction(m_exportSvgAct);
1422 	imageMenu->addAction(m_exportPdfAct);
1423 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
1424 	imageMenu->addAction(m_exportPsAct);
1425 #endif
1426 
1427 	QMenu * productionMenu = m_exportMenu->addMenu(tr("for Production"));
1428 	productionMenu->addAction(m_exportEtchablePdfAct);
1429 	productionMenu->addAction(m_exportEtchableSvgAct);
1430 	productionMenu->addSeparator();
1431 	productionMenu->addAction(m_exportGerberAct);
1432 }
1433 
1434 
createEditMenu()1435 void MainWindow::createEditMenu()
1436 {
1437     m_editMenu = menuBar()->addMenu(tr("&Edit"));
1438     m_editMenu->addAction(m_undoAct);
1439     m_editMenu->addAction(m_redoAct);
1440     m_editMenu->addSeparator();
1441     m_editMenu->addAction(m_cutAct);
1442     m_editMenu->addAction(m_copyAct);
1443     m_editMenu->addAction(m_pasteAct);
1444     m_editMenu->addAction(m_pasteInPlaceAct);
1445     m_editMenu->addAction(m_duplicateAct);
1446     m_editMenu->addAction(m_deleteAct);
1447     m_editMenu->addAction(m_deleteMinusAct);
1448     m_editMenu->addSeparator();
1449     m_editMenu->addAction(m_selectAllAct);
1450     m_editMenu->addAction(m_deselectAct);
1451     m_editMenu->addSeparator();
1452     m_editMenu->addAction(m_addNoteAct);
1453     m_editMenu->addSeparator();
1454     m_editMenu->addAction(m_preferencesAct);
1455     updateEditMenu();
1456     connect(m_editMenu, SIGNAL(aboutToShow()), this, SLOT(updateEditMenu()));
1457 }
1458 
createPartMenu()1459 void MainWindow::createPartMenu() {
1460     m_partMenu = menuBar()->addMenu(tr("&Part"));
1461     connect(m_partMenu, SIGNAL(aboutToShow()), this, SLOT(updatePartMenu()));
1462 
1463 	m_partMenu->addAction(m_openInPartsEditorNewAct);
1464 
1465     m_partMenu->addSeparator();
1466     m_partMenu->addAction(m_saveBundledPart);
1467 
1468 	m_partMenu->addSeparator();
1469 	m_partMenu->addAction(m_flipHorizontalAct);
1470     m_partMenu->addAction(m_flipVerticalAct);
1471     createRotateSubmenu(m_partMenu);
1472     createZOrderSubmenu(m_partMenu);
1473     createZOrderWireSubmenu(m_partMenu);
1474     createAlignSubmenu(m_partMenu);
1475 	m_partMenu->addAction(m_moveLockAct);
1476 	m_partMenu->addAction(m_stickyAct);
1477 	m_partMenu->addAction(m_selectMoveLockAct);
1478 
1479 	m_partMenu->addSeparator();
1480     createAddToBinSubmenu(m_partMenu);
1481 	m_partMenu->addAction(m_showPartLabelAct);
1482 	m_partMenu->addSeparator();
1483 	m_partMenu->addAction(m_selectAllObsoleteAct);
1484 	m_partMenu->addAction(m_swapObsoleteAct);
1485 
1486 	m_partMenu->addSeparator();
1487 	m_partMenu->addAction(m_findPartInSketchAct);
1488 
1489 
1490 #ifndef QT_NO_DEBUG
1491     m_partMenu->addAction(m_dumpAllPartsAction);
1492 #endif
1493 
1494 }
1495 
createViewMenu()1496 void MainWindow::createViewMenu()
1497 {
1498     m_viewMenu = menuBar()->addMenu(tr("&View"));
1499     m_viewMenu->addAction(m_zoomInAct);
1500     m_viewMenu->addAction(m_zoomOutAct);
1501     m_viewMenu->addAction(m_fitInWindowAct);
1502     m_viewMenu->addAction(m_actualSizeAct);
1503     m_viewMenu->addAction(m_100PercentSizeAct);
1504 	m_viewMenu->addSeparator();
1505 
1506     m_viewMenu->addAction(m_alignToGridAct);
1507     m_viewMenu->addAction(m_showGridAct);
1508     m_viewMenu->addAction(m_setGridSizeAct);
1509     m_viewMenu->addAction(m_setBackgroundColorAct);
1510 	m_viewMenu->addSeparator();
1511 
1512 	if (m_welcomeView) m_viewMenu->addAction(m_showWelcomeAct);
1513     m_viewMenu->addAction(m_showBreadboardAct);
1514     m_viewMenu->addAction(m_showSchematicAct);
1515     m_viewMenu->addAction(m_showPCBAct);
1516     if (m_programView) m_viewMenu->addAction(m_showProgramAct);
1517     m_viewMenu->addSeparator();
1518     if (m_binManager) {
1519         m_viewMenu->addAction(m_showPartsBinIconViewAct);
1520         m_viewMenu->addAction(m_showPartsBinListViewAct);
1521         m_viewMenu->addSeparator();
1522     }
1523     connect(m_viewMenu, SIGNAL(aboutToShow()), this, SLOT(updateLayerMenu()));
1524     m_numFixedActionsInViewMenu = m_viewMenu->actions().size();
1525 }
1526 
createWindowMenu()1527 void MainWindow::createWindowMenu() {
1528     m_windowMenu = menuBar()->addMenu(tr("&Window"));
1529 	m_windowMenu->addAction(m_minimizeAct);
1530 	m_windowMenu->addSeparator();
1531 	//m_windowMenu->addAction(m_toggleToolbarAct);
1532 	updateWindowMenu();
1533 	connect(m_windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowMenu()));
1534 }
1535 
createTraceMenus()1536 void MainWindow::createTraceMenus()
1537 {
1538 	m_pcbTraceMenu = menuBar()->addMenu(tr("&Routing"));
1539 	m_pcbTraceMenu->addAction(m_newAutorouteAct);
1540 	m_pcbTraceMenu->addAction(m_newDesignRulesCheckAct);
1541 	m_pcbTraceMenu->addAction(m_autorouterSettingsAct);
1542 	m_pcbTraceMenu->addAction(m_fabQuoteAct);
1543 
1544 	QMenu * groundFillMenu = m_pcbTraceMenu->addMenu(tr("Ground Fill"));
1545 
1546 	groundFillMenu->addAction(m_copperFillAct);
1547 	groundFillMenu->addAction(m_groundFillAct);
1548 	groundFillMenu->addAction(m_removeGroundFillAct);
1549 	groundFillMenu->addAction(m_setGroundFillSeedsAct);
1550 	groundFillMenu->addAction(m_clearGroundFillSeedsAct);
1551 	groundFillMenu->addAction(m_setGroundFillKeepoutAct);
1552 	//m_pcbTraceMenu->addAction(m_updateRoutingStatusAct);
1553 	m_pcbTraceMenu->addSeparator();
1554 
1555 	m_pcbTraceMenu->addAction(m_viewFromBelowToggleAct);
1556 	m_pcbTraceMenu->addAction(m_activeLayerBothAct);
1557 	m_pcbTraceMenu->addAction(m_activeLayerBottomAct);
1558 	m_pcbTraceMenu->addAction(m_activeLayerTopAct);
1559 	m_pcbTraceMenu->addSeparator();
1560 
1561 	m_pcbTraceMenu->addAction(m_changeTraceLayerAct);
1562 	m_pcbTraceMenu->addAction(m_excludeFromAutorouteAct);
1563 	m_pcbTraceMenu->addSeparator();
1564 
1565 	m_pcbTraceMenu->addAction(m_showUnroutedAct);
1566 	m_pcbTraceMenu->addAction(m_selectAllTracesAct);
1567 	m_pcbTraceMenu->addAction(m_selectAllExcludedTracesAct);
1568 	m_pcbTraceMenu->addAction(m_selectAllIncludedTracesAct);
1569 	m_pcbTraceMenu->addAction(m_selectAllJumperItemsAct);
1570 	m_pcbTraceMenu->addAction(m_selectAllViasAct);
1571 	m_pcbTraceMenu->addAction(m_selectAllCopperFillAct);
1572 
1573     //m_pcbTraceMenu->addSeparator();
1574 	//m_pcbTraceMenu->addAction(m_checkLoadedTracesAct);
1575 
1576     m_schematicTraceMenu = menuBar()->addMenu(tr("&Routing"));
1577 	m_schematicTraceMenu->addAction(m_newAutorouteAct);
1578 	m_schematicTraceMenu->addAction(m_excludeFromAutorouteAct);
1579 	m_schematicTraceMenu->addAction(m_showUnroutedAct);
1580 	m_schematicTraceMenu->addAction(m_selectAllTracesAct);
1581 	m_schematicTraceMenu->addAction(m_selectAllExcludedTracesAct);
1582 	m_schematicTraceMenu->addAction(m_selectAllIncludedTracesAct);
1583 	//m_schematicTraceMenu->addAction(m_updateRoutingStatusAct);
1584 
1585 #ifndef QT_NO_DEBUG
1586 	m_schematicTraceMenu->addAction(m_tidyWiresAct);
1587 #endif
1588 
1589 	m_breadboardTraceMenu = menuBar()->addMenu(tr("&Routing"));
1590 	m_breadboardTraceMenu->addAction(m_showUnroutedAct);
1591 	m_breadboardTraceMenu->addAction(m_selectAllWiresAct);
1592 
1593 	updateTraceMenu();
1594 	connect(m_pcbTraceMenu, SIGNAL(aboutToShow()), this, SLOT(updateTraceMenu()));
1595 	connect(m_schematicTraceMenu, SIGNAL(aboutToShow()), this, SLOT(updateTraceMenu()));
1596 	connect(m_breadboardTraceMenu, SIGNAL(aboutToShow()), this, SLOT(updateTraceMenu()));
1597 
1598     menuBar()->addSeparator();
1599 }
1600 
createHelpMenu()1601 void MainWindow::createHelpMenu()
1602 {
1603     m_helpMenu = menuBar()->addMenu(tr("&Help"));
1604     m_helpMenu->addAction(m_openHelpAct);
1605     m_helpMenu->addAction(m_examplesAct);
1606     m_helpMenu->addAction(m_partsRefAct);
1607 	m_helpMenu->addSeparator();
1608     m_helpMenu->addAction(m_partsEditorHelpAct);
1609 	m_helpMenu->addSeparator();
1610 	m_helpMenu->addAction(m_checkForUpdatesAct);
1611 	m_helpMenu->addAction(m_importFilesFromPrevInstallAct);
1612 	m_helpMenu->addSeparator();
1613 	m_helpMenu->addAction(m_reportBugAct);
1614 	m_helpMenu->addAction(m_enableDebugAct);
1615 	m_helpMenu->addSeparator();
1616 	m_helpMenu->addAction(m_aboutAct);
1617     m_helpMenu->addAction(m_openDonateAct);
1618 	m_helpMenu->addAction(m_tipsAndTricksAct);
1619 	m_helpMenu->addAction(m_firstTimeHelpAct);
1620 #ifndef QT_NO_DEBUG
1621 	m_helpMenu->addAction(m_aboutQtAct);
1622 #endif
1623 }
1624 
1625 
updateLayerMenu(bool resetLayout)1626 void MainWindow::updateLayerMenu(bool resetLayout) {
1627     if (m_viewMenu == NULL) return;
1628     if (m_showAllLayersAct == NULL) return;
1629 
1630     QList<QAction *> actions;
1631     actions << m_zoomInAct << m_zoomOutAct << m_zoomInShortcut << m_fitInWindowAct << m_actualSizeAct <<
1632         m_100PercentSizeAct << m_alignToGridAct << m_showGridAct << m_setGridSizeAct << m_setBackgroundColorAct;
1633 
1634     bool enabled = (m_currentGraphicsView != NULL);
1635     foreach (QAction * action, actions) action->setEnabled(enabled);
1636 
1637 	actions.clear();
1638 
1639     if (m_showPartsBinIconViewAct) {
1640 	    if (m_binManager) {
1641 		    m_showPartsBinIconViewAct->setEnabled(true);
1642 		    m_showPartsBinListViewAct->setEnabled(true);
1643 		    actions << m_showPartsBinIconViewAct << m_showPartsBinListViewAct;
1644 		    setActionsIcons(m_binManager->currentViewIsIconView() ? 0 : 1, actions);
1645 	    }
1646 	    else {
1647 		    m_showPartsBinIconViewAct->setEnabled(false);
1648 		    m_showPartsBinListViewAct->setEnabled(false);
1649 	    }
1650     }
1651 
1652 	removeActionsStartingAt(m_viewMenu, m_numFixedActionsInViewMenu);
1653     if (m_showAllLayersAct) m_viewMenu->addAction(m_showAllLayersAct);
1654     if (m_hideAllLayersAct) m_viewMenu->addAction(m_hideAllLayersAct);
1655 
1656     m_hideAllLayersAct->setEnabled(false);
1657 	m_showAllLayersAct->setEnabled(false);
1658 
1659     if (m_currentGraphicsView == NULL) {
1660         return;
1661     }
1662 
1663 	m_alignToGridAct->setChecked(m_currentGraphicsView->alignedToGrid());
1664 	m_showGridAct->setChecked(m_currentGraphicsView->showingGrid());
1665 
1666 	LayerHash viewLayers = m_currentGraphicsView->viewLayers();
1667 	LayerList keys = viewLayers.keys();
1668 
1669 	// make sure they're in ascending order when inserting into the menu
1670 	qSort(keys.begin(), keys.end());
1671 
1672 	foreach (ViewLayer::ViewLayerID key, keys) {
1673 		ViewLayer * viewLayer = viewLayers.value(key);
1674 		//DebugDialog::debug(QString("Layer: %1 is %2").arg(viewLayer->action()->text()).arg(viewLayer->action()->isEnabled()));
1675     	if (viewLayer != NULL) {
1676 			if (viewLayer->parentLayer()) continue;
1677 			m_viewMenu->addAction(viewLayer->action());
1678 			disconnect(viewLayer->action(), SIGNAL(triggered()), this, SLOT(updateLayerMenu()));
1679 			connect(viewLayer->action(), SIGNAL(triggered()), this, SLOT(updateLayerMenu()));
1680 		}
1681 	}
1682 
1683 
1684 	if (keys.count() <= 0) return;
1685 
1686 	ViewLayer *prev = viewLayers.value(keys[0]);
1687 	if (prev == NULL) {
1688 		// jrc: I think prev == NULL is actually a side effect from an earlier bug
1689 		// but I haven't figured out the cause yet
1690 		// at any rate, when this bug occurs, keys[0] is some big negative number that looks like an
1691 		// uninitialized or scrambled layerID
1692 		DebugDialog::debug(QString("updateAllLayersActions keys[0] failed %1").arg(keys[0]) );
1693 		return;
1694 	}
1695 
1696 	bool sameState = prev->action()->isChecked();
1697 	bool checked = prev->action()->isChecked();
1698 	//DebugDialog::debug(QString("Layer: %1 is %2").arg(prev->action()->text()).arg(prev->action()->isChecked()));
1699 	for (int i = 1; i < keys.count(); i++) {
1700 		ViewLayer *viewLayer = viewLayers.value(keys[i]);
1701 		//DebugDialog::debug(QString("Layer: %1 is %2").arg(viewLayer->action()->text()).arg(viewLayer->action()->isChecked()));
1702 		if (viewLayer != NULL) {
1703 			if (prev != NULL && prev->action()->isChecked() != viewLayer->action()->isChecked() ) {
1704 				// if the actions aren't all checked or unchecked I don't bother about the "checked" variable
1705 				sameState = false;
1706 				break;
1707 			}
1708 			else {
1709 				sameState = true;
1710 				checked = viewLayer->action()->isChecked();
1711 			}
1712 			prev = viewLayer;
1713 		}
1714 	}
1715 
1716 	//DebugDialog::debug(QString("sameState: %1").arg(sameState));
1717 	//DebugDialog::debug(QString("checked: %1").arg(checked));
1718 	if (sameState) {
1719 		if(checked) {
1720 			m_hideAllLayersAct->setEnabled(true);
1721 		}
1722 		else {
1723 			m_showAllLayersAct->setEnabled(true);
1724 		}
1725 	}
1726 	else {
1727 		m_showAllLayersAct->setEnabled(true);
1728 		m_hideAllLayersAct->setEnabled(true);
1729 	}
1730 
1731 	if (resetLayout) {
1732 		m_layerPalette->resetLayout(viewLayers, keys);
1733 	}
1734 	m_layerPalette->updateLayerPalette(viewLayers, keys);
1735 }
1736 
updateWireMenu()1737 void MainWindow::updateWireMenu() {
1738 	// assumes update wire menu is only called when right-clicking a wire
1739 	// and that wire is cached by the menu in Wire::mousePressEvent
1740 
1741 	Wire * wire = m_activeWire;
1742 	m_activeWire = NULL;
1743 
1744 	if (wire) {
1745 		enableAddBendpointAct(wire);
1746 	}
1747 
1748 	bool enableAll = true;
1749 	bool deleteOK = false;
1750 	bool createTraceOK = false;
1751 	bool excludeOK = false;
1752 	bool enableZOK = true;
1753 	bool gotRat = false;
1754 	bool ctlOK = false;
1755 
1756 	if (wire != NULL) {
1757 
1758 		if (wire->getRatsnest()) {
1759 			QList<ConnectorItem *> ends;
1760 			Wire * jt = wire->findTraced(m_currentGraphicsView->getTraceFlag(), ends);
1761 			createTraceOK = (jt == NULL) || (!jt->getTrace());
1762 			deleteOK = true;
1763 			gotRat = true;
1764 			enableZOK = false;
1765 		}
1766 		else if (wire->getTrace()) {
1767 			deleteOK = true;
1768 			excludeOK = true;
1769 			m_excludeFromAutorouteWireAct->setChecked(!wire->getAutoroutable());
1770 			if (m_currentGraphicsView == m_pcbGraphicsView && m_currentGraphicsView->boardLayers() > 1) {
1771 				if (wire->canSwitchLayers()) {
1772                     if (ViewLayer::topLayers().contains(wire->viewLayerID())) {
1773                         m_changeTraceLayerWireAct->setText(tr("Move to bottom layer"));
1774                     }
1775                     else {
1776                         m_changeTraceLayerWireAct->setText(tr("Move to top layer"));
1777                     }
1778 					ctlOK = true;
1779 				}
1780 			}
1781 
1782 		}
1783 		else {
1784 			deleteOK = true;
1785 		}
1786 	}
1787 
1788 
1789     QMenu* wireColorMenu = (m_currentGraphicsView == m_breadboardGraphicsView ? m_breadboardWireColorMenu : m_schematicWireColorMenu);
1790 	if (wire) {
1791 		wireColorMenu->setEnabled(true);
1792         QString colorString = wire->colorString();
1793         //DebugDialog::debug("wire colorstring " + colorString);
1794 		foreach (QAction * action, wireColorMenu->actions()) {
1795 			QString colorName = action->data().toString();
1796             //DebugDialog::debug("colorname " + colorName);
1797 			action->setChecked(colorName.compare(colorString) == 0);
1798 		}
1799 	}
1800 	else {
1801 		wireColorMenu->setEnabled(false);
1802 	}
1803 
1804 	m_bringToFrontWireAct->setWire(wire);
1805 	m_bringForwardWireAct->setWire(wire);
1806 	m_sendBackwardWireAct->setWire(wire);
1807 	m_sendToBackWireAct->setWire(wire);
1808 	m_createTraceWireAct->setWire(wire);
1809 	m_createWireWireAct->setWire(wire);
1810 	m_deleteWireAct->setWire(wire);
1811 	m_deleteWireMinusAct->setWire(wire);
1812 	m_excludeFromAutorouteWireAct->setWire(wire);
1813 	m_changeTraceLayerWireAct->setWire(wire);
1814 
1815 	m_bringToFrontWireAct->setEnabled(enableZOK);
1816 	m_bringForwardWireAct->setEnabled(enableZOK);
1817 	m_sendBackwardWireAct->setEnabled(enableZOK);
1818 	m_sendToBackWireAct->setEnabled(enableZOK);
1819 	m_createTraceWireAct->setEnabled(enableAll && createTraceOK);
1820 	m_createWireWireAct->setEnabled(enableAll && createTraceOK);
1821 	m_deleteWireAct->setEnabled(enableAll && deleteOK);
1822 	m_deleteWireMinusAct->setEnabled(enableAll && deleteOK && !gotRat);
1823 	m_excludeFromAutorouteWireAct->setEnabled(enableAll && excludeOK);
1824 	m_changeTraceLayerAct->setEnabled(ctlOK);
1825 	m_changeTraceLayerWireAct->setEnabled(ctlOK);
1826 
1827 	if (gotRat) {
1828 		m_deleteWireAct->setText(tr("Delete Ratsnest Line"));
1829 	}
1830 	else {
1831 		m_deleteWireAct->setText(tr("Delete Wire"));
1832 	}
1833 }
1834 
updatePartMenu()1835 void MainWindow::updatePartMenu() {
1836 	if (m_partMenu == NULL) return;
1837 
1838     if (m_currentGraphicsView == NULL) {
1839         foreach (QAction * action, m_partMenu->actions()) {
1840             action->setEnabled(false);
1841         }
1842         return;
1843     }
1844 
1845 	ItemCount itemCount = m_currentGraphicsView->calcItemCount();
1846 
1847 	bool enable = true;
1848 	bool zenable = true;
1849 
1850 	if (itemCount.selCount <= 0) {
1851 		zenable = enable = false;
1852 	}
1853 	else {
1854 		if (itemCount.itemsCount == itemCount.selCount) {
1855 			// if all items are selected
1856 			// z-reordering is a no-op
1857 			zenable = false;
1858 		}
1859 	}
1860 
1861     m_alignLeftAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1);
1862     m_alignRightAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1);
1863     m_alignTopAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1);
1864     m_alignBottomAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1);
1865     m_alignVerticalCenterAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1);
1866     m_alignHorizontalCenterAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1);
1867 
1868 	//DebugDialog::debug(QString("enable layer actions %1")upat.arg(enable));
1869 	m_bringToFrontAct->setEnabled(zenable);
1870 	m_bringForwardAct->setEnabled(zenable);
1871 	m_sendBackwardAct->setEnabled(zenable);
1872 	m_sendToBackAct->setEnabled(zenable);
1873 
1874 	m_moveLockAct->setEnabled(itemCount.selCount > 0 && itemCount.selCount > itemCount.wireCount);
1875 	m_moveLockAct->setChecked(itemCount.moveLockCount > 0);
1876 	m_selectMoveLockAct->setEnabled(true);
1877 
1878 	m_showPartLabelAct->setEnabled((itemCount.hasLabelCount > 0) && enable);
1879 	m_showPartLabelAct->setText(itemCount.visLabelCount == itemCount.hasLabelCount ? tr("Hide part label") : tr("Show part label"));
1880     m_showPartLabelAct->setData(itemCount.visLabelCount != itemCount.hasLabelCount);
1881 
1882 	bool renable = (itemCount.selRotatable > 0);
1883 	bool renable45 = (itemCount.sel45Rotatable > 0);
1884 
1885 	//DebugDialog::debug(QString("enable rotate (2) %1").arg(enable));
1886 
1887 	m_rotate90cwAct->setEnabled(renable && enable);
1888 	m_rotate180Act->setEnabled(renable && enable);
1889 	m_rotate90ccwAct->setEnabled(renable && enable);
1890 	m_rotate45ccwAct->setEnabled(renable && renable45 && enable);
1891 	m_rotate45cwAct->setEnabled(renable && renable45 && enable);
1892 
1893 	m_flipHorizontalAct->setEnabled(enable && (itemCount.selHFlipable > 0) && (m_currentGraphicsView != m_pcbGraphicsView));
1894 	m_flipVerticalAct->setEnabled(enable && (itemCount.selVFlipable > 0) && (m_currentGraphicsView != m_pcbGraphicsView));
1895 
1896 	updateItemMenu();
1897 	updateEditMenu();
1898 
1899 
1900     bool ctbpVisible = false;
1901     bool ctbpEnabled = false;
1902 	if (itemCount.selCount == 1) {
1903         ItemBase * itemBase = dynamic_cast<ItemBase *>(m_currentGraphicsView->scene()->selectedItems()[0]);
1904 		enableAddBendpointAct(itemBase);
1905 
1906         Via * via = qobject_cast<Via *>(itemBase->layerKinChief());
1907         if (via) {
1908             ctbpVisible = true;
1909             int count = 0;
1910             QList<ConnectorItem *> viaConnectorItems;
1911             viaConnectorItems << via->connectorItem();
1912             if (via->connectorItem()->getCrossLayerConnectorItem()) {
1913                 viaConnectorItems << via->connectorItem()->getCrossLayerConnectorItem();
1914             }
1915 
1916             foreach (ConnectorItem * viaConnectorItem, viaConnectorItems) {
1917                 foreach (ConnectorItem * connectorItem, viaConnectorItem->connectedToItems()) {
1918                     Wire * wire = qobject_cast<Wire *>(connectorItem->attachedTo());
1919                     if (wire == NULL) continue;
1920                     if (wire->getRatsnest()) continue;
1921 
1922                     if (wire->isTraceType(m_currentGraphicsView->getTraceFlag())) {
1923                         count++;
1924                         if (count > 1) {
1925                             ctbpEnabled = true;
1926                             break;
1927                         }
1928                     }
1929                 }
1930                 if (count > 1) break;
1931             }
1932         }
1933 
1934 
1935 		m_openInPartsEditorNewAct->setEnabled(itemBase->canEditPart());
1936         m_stickyAct->setVisible(itemBase->isBaseSticky());
1937 	    m_stickyAct->setEnabled(true);
1938 	    m_stickyAct->setChecked(itemBase->isBaseSticky() && itemBase->isLocalSticky());
1939 
1940         QList<ItemBase *> itemBases;
1941         itemBases.append(itemBase);
1942         itemBases.append(itemBase->layerKinChief()->layerKin());
1943         bool hpsa = false;
1944         foreach (ItemBase * lkpi, itemBases) {
1945             if (lkpi->viewLayerID() == ViewLayer::Silkscreen1 || lkpi->viewLayerID() == ViewLayer::Silkscreen0) {
1946                 hpsa = true;
1947                 m_hidePartSilkscreenAct->setText(lkpi->layerHidden() ? tr("Show part silkscreen") : tr("Hide part silkscreen"));
1948                 break;
1949             }
1950         }
1951 
1952         m_hidePartSilkscreenAct->setEnabled(hpsa);
1953 	}
1954     else {
1955         m_hidePartSilkscreenAct->setEnabled(false);
1956         m_stickyAct->setVisible(false);
1957 		m_openInPartsEditorNewAct->setEnabled(false);
1958     }
1959     m_convertToBendpointAct->setEnabled(ctbpEnabled);
1960     m_convertToBendpointAct->setVisible(ctbpVisible);
1961     m_convertToBendpointSeparator->setVisible(ctbpVisible);
1962 
1963 	// TODO: only enable if there is an obsolete part in the sketch
1964 	m_selectAllObsoleteAct->setEnabled(true);
1965 	m_swapObsoleteAct->setEnabled(itemCount.obsoleteCount > 0);
1966     m_findPartInSketchAct->setEnabled(m_currentGraphicsView != NULL);
1967 	m_openProgramWindowAct->setEnabled(true);
1968 }
1969 
updateTransformationActions()1970 void MainWindow::updateTransformationActions() {
1971     // update buttons in sketch toolbar at bottom
1972 
1973 	if (m_currentGraphicsView == NULL) return;
1974     if (m_rotate90cwAct == NULL) return;
1975 
1976 	ItemCount itemCount = m_currentGraphicsView->calcItemCount();
1977 	bool enable = (itemCount.selRotatable > 0);
1978     bool renable = (itemCount.sel45Rotatable > 0);
1979 
1980 	//DebugDialog::debug(QString("enable rotate (1) %1").arg(enable));
1981 
1982 	m_rotate90cwAct->setEnabled(enable);
1983 	m_rotate180Act->setEnabled(enable);
1984 	m_rotate90ccwAct->setEnabled(enable);
1985 	m_rotate45ccwAct->setEnabled(enable && renable);
1986 	m_rotate45cwAct->setEnabled(enable && renable);
1987 	foreach(SketchToolButton* rotateButton, m_rotateButtons) {
1988 		rotateButton->setEnabled(enable);
1989 	}
1990 
1991 	m_flipHorizontalAct->setEnabled((itemCount.selHFlipable > 0) && (m_currentGraphicsView != m_pcbGraphicsView));
1992 	m_flipVerticalAct->setEnabled((itemCount.selVFlipable > 0) && (m_currentGraphicsView != m_pcbGraphicsView));
1993 
1994 	enable = m_flipHorizontalAct->isEnabled() || m_flipVerticalAct->isEnabled();
1995 	foreach(SketchToolButton* flipButton, m_flipButtons) {
1996 		flipButton->setEnabled(enable);
1997 	}
1998 }
1999 
updateItemMenu()2000 void MainWindow::updateItemMenu() {
2001 	if (m_currentGraphicsView == NULL) return;
2002 
2003 	ConnectorItem * activeConnectorItem = m_activeConnectorItem;
2004 	m_activeConnectorItem = NULL;
2005 
2006 	QList<QGraphicsItem *> items = m_currentGraphicsView->scene()->selectedItems();
2007 
2008 	int selCount = 0;
2009 	ItemBase * itemBase = NULL;
2010 	foreach(QGraphicsItem * item, items) {
2011 		ItemBase * ib = ItemBase::extractTopLevelItemBase(item);
2012 		if (ib == NULL) continue;
2013 
2014 		selCount++;
2015 		if (selCount == 1) itemBase = ib;
2016 		else if (selCount > 1) break;
2017 	}
2018 
2019 	PaletteItem *selected = qobject_cast<PaletteItem *>(itemBase);
2020 	bool enabled = (selCount == 1) && (selected != NULL);
2021 
2022 	m_saveBundledPart->setEnabled(enabled && !selected->modelPart()->isCore());
2023 
2024 
2025 	// can't open wire in parts editor
2026 	enabled &= selected != NULL && itemBase != NULL && itemBase->canEditPart();
2027 
2028 	m_disconnectAllAct->setEnabled(enabled && m_currentGraphicsView->canDisconnectAll() && (itemBase->rightClickedConnector() != NULL));
2029 
2030 	bool gfsEnabled = false;
2031 	if (activeConnectorItem) {
2032 		if (activeConnectorItem->attachedToItemType() != ModelPart::CopperFill) {
2033 			gfsEnabled = true;
2034 			m_setOneGroundFillSeedAct->setChecked(activeConnectorItem->isGroundFillSeed());
2035 		}
2036 	}
2037 	m_setOneGroundFillSeedAct->setEnabled(gfsEnabled);
2038 	m_setOneGroundFillSeedAct->setConnectorItem(activeConnectorItem);
2039 }
2040 
updateEditMenu()2041 void MainWindow::updateEditMenu() {
2042 	if (m_currentGraphicsView == NULL) {
2043         foreach (QAction * action, m_editMenu->actions()) {
2044             action->setEnabled(action == m_preferencesAct);
2045         }
2046         return;
2047     }
2048 
2049     foreach (QAction * action, m_editMenu->actions()) {
2050         action->setEnabled(true);
2051     }
2052 
2053 	QClipboard *clipboard = QApplication::clipboard();
2054 	m_pasteAct->setEnabled(false);
2055 	m_pasteInPlaceAct->setEnabled(false);
2056 	if (clipboard != NULL) {
2057 		const QMimeData *mimeData = clipboard->mimeData(QClipboard::Clipboard);
2058 		if (mimeData != NULL) {
2059 			if (mimeData->hasFormat("application/x-dnditemsdata")) {
2060 				m_pasteAct->setEnabled(true);
2061 				m_pasteInPlaceAct->setEnabled(true);
2062 				//DebugDialog::debug(QString("paste enabled: true"));
2063 			}
2064 		}
2065 	}
2066 
2067 	const QList<QGraphicsItem *> items =  m_currentGraphicsView->scene()->selectedItems();
2068 	bool copyActsEnabled = false;
2069 	bool deleteActsEnabled = false;
2070 	foreach (QGraphicsItem * item, items) {
2071 		if (m_currentGraphicsView->canDeleteItem(item, items.count())) {
2072 			deleteActsEnabled = true;
2073 		}
2074 		if (m_currentGraphicsView->canCopyItem(item, items.count())) {
2075 			copyActsEnabled = true;
2076 		}
2077 	}
2078 
2079 	//DebugDialog::debug(QString("enable cut/copy/duplicate/delete %1 %2 %3").arg(copyActsEnabled).arg(deleteActsEnabled).arg(m_currentWidget->viewID()) );
2080 	m_deleteAct->setEnabled(deleteActsEnabled);
2081 	m_deleteMinusAct->setEnabled(deleteActsEnabled);
2082 	m_deleteAct->setText(tr("Delete"));
2083 	m_cutAct->setEnabled(deleteActsEnabled && copyActsEnabled);
2084 	m_copyAct->setEnabled(copyActsEnabled);
2085 	m_duplicateAct->setEnabled(copyActsEnabled);
2086 }
2087 
updateTraceMenu()2088 void MainWindow::updateTraceMenu() {
2089     if (m_pcbTraceMenu == NULL) return;
2090 
2091 	bool tEnabled = false;
2092 	bool twEnabled = false;
2093 	bool ctlEnabled = false;
2094 	bool arEnabled = false;
2095 
2096     TraceMenuThing traceMenuThing;
2097 
2098 	if (m_currentGraphicsView != NULL) {
2099 		QList<QGraphicsItem *> items = m_currentGraphicsView->scene()->items();
2100 		foreach (QGraphicsItem * item, items) {
2101 			Wire * wire = dynamic_cast<Wire *>(item);
2102 			if (wire == NULL) {
2103 				if (m_currentGraphicsView == m_pcbGraphicsView) {
2104                     updatePCBTraceMenu(item, traceMenuThing);
2105                 }
2106                 continue;
2107 			}
2108 
2109 			if (!wire->isEverVisible()) continue;
2110 			if (wire->getRatsnest()) {
2111 				//rEnabled = true;
2112 				//if (wire->isSelected()) {
2113 					//ctEnabled = true;
2114 				//}
2115 			}
2116 			else if (wire->isTraceType(m_currentGraphicsView->getTraceFlag())) {
2117 				arEnabled = true;
2118 				tEnabled = true;
2119 				twEnabled = true;
2120 				if (wire->isSelected()) {
2121 					traceMenuThing.exEnabled = true;
2122 					if (wire->getAutoroutable()) {
2123 						traceMenuThing.exChecked = false;
2124 					}
2125 				}
2126 				if (m_currentGraphicsView == m_pcbGraphicsView && m_currentGraphicsView->boardLayers() > 1) {
2127 					if (wire->canSwitchLayers()) {
2128 						ctlEnabled = true;
2129 					}
2130 				}
2131 			}
2132 		}
2133 	}
2134 
2135 	if (!arEnabled) {
2136 		if (m_currentGraphicsView != NULL) {
2137 			arEnabled = m_currentGraphicsView->hasAnyNets();
2138 		}
2139 	}
2140 
2141     bool anyOrNo = (traceMenuThing.boardCount >= 1 || m_currentGraphicsView != m_pcbGraphicsView);
2142 
2143 	m_excludeFromAutorouteAct->setEnabled(traceMenuThing.exEnabled);
2144 	m_excludeFromAutorouteAct->setChecked(traceMenuThing.exChecked);
2145 	m_changeTraceLayerAct->setEnabled(ctlEnabled);
2146 	m_orderFabAct->setEnabled(traceMenuThing.boardCount > 0);
2147 	m_showUnroutedAct->setEnabled(true);
2148 	m_selectAllTracesAct->setEnabled(tEnabled && anyOrNo);
2149 	m_selectAllWiresAct->setEnabled(tEnabled && anyOrNo);
2150 	m_selectAllCopperFillAct->setEnabled(traceMenuThing.gfrEnabled && traceMenuThing.boardCount >= 1);
2151 	m_selectAllExcludedTracesAct->setEnabled(tEnabled && anyOrNo);
2152 	m_selectAllIncludedTracesAct->setEnabled(tEnabled && anyOrNo);
2153 	m_selectAllJumperItemsAct->setEnabled(traceMenuThing.jiEnabled && traceMenuThing.boardCount >= 1);
2154 	m_selectAllViasAct->setEnabled(traceMenuThing.viaEnabled && traceMenuThing.boardCount >= 1);
2155 	m_tidyWiresAct->setEnabled(twEnabled);
2156 
2157     QString sides;
2158     if (m_pcbGraphicsView->layerIsActive(ViewLayer::Copper0) && m_pcbGraphicsView->layerIsActive(ViewLayer::Copper1)) {
2159         sides = tr("top and bottom");
2160     }
2161     else if (m_pcbGraphicsView->layerIsActive(ViewLayer::Copper0)) {
2162         sides = tr("bottom");
2163     }
2164     else sides = tr("top");
2165 
2166     QString groundFillString = tr("Ground Fill (%1)").arg(sides);
2167     QString copperFillString = tr("Copper Fill (%1)").arg(sides);
2168 
2169 	m_groundFillAct->setEnabled(traceMenuThing.boardCount >= 1);
2170     m_groundFillAct->setText(groundFillString);
2171 	m_copperFillAct->setEnabled(traceMenuThing.boardCount >= 1);
2172 	m_copperFillAct->setText(copperFillString);
2173 	m_removeGroundFillAct->setEnabled(traceMenuThing.gfrEnabled && traceMenuThing.boardCount >= 1);
2174 
2175 	// TODO: set and clear enabler logic
2176 	m_setGroundFillSeedsAct->setEnabled(traceMenuThing.gfsEnabled && traceMenuThing.boardCount >= 1);
2177 	m_clearGroundFillSeedsAct->setEnabled(traceMenuThing.gfsEnabled && traceMenuThing.boardCount >= 1);
2178 
2179 	m_newDesignRulesCheckAct->setEnabled(traceMenuThing.boardCount >= 1);
2180 	m_checkLoadedTracesAct->setEnabled(true);
2181 	m_autorouterSettingsAct->setEnabled(m_currentGraphicsView == m_pcbGraphicsView);
2182 	m_updateRoutingStatusAct->setEnabled(true);
2183 
2184 
2185 	m_fabQuoteAct->setEnabled(m_currentGraphicsView == m_pcbGraphicsView);
2186 }
2187 
2188 
updatePCBTraceMenu(QGraphicsItem * item,TraceMenuThing & traceMenuThing)2189 void MainWindow::updatePCBTraceMenu(QGraphicsItem * item, TraceMenuThing & traceMenuThing)
2190 {
2191 	ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
2192 	if (itemBase == NULL) return;
2193 	if (!itemBase->isEverVisible()) return;
2194 
2195 	if (!traceMenuThing.gfsEnabled) {
2196 		traceMenuThing.gfsEnabled = itemBase->itemType() != ModelPart::CopperFill && itemBase->hasConnectors();
2197 	}
2198 
2199     if (Board::isBoard(itemBase)) {
2200         traceMenuThing.boardCount++;
2201         if (itemBase->isSelected()) traceMenuThing.boardSelectedCount++;
2202     }
2203 
2204 	switch (itemBase->itemType()) {
2205 		case ModelPart::Jumper:
2206 			traceMenuThing.jiEnabled = true;
2207 			if (itemBase->isSelected()) {
2208 				traceMenuThing.exEnabled = true;
2209 				if (qobject_cast<JumperItem *>(itemBase->layerKinChief())->getAutoroutable()) {
2210 					traceMenuThing.exChecked = false;
2211 				}
2212 			}
2213 			break;
2214 		case ModelPart::Via:
2215 			traceMenuThing.viaEnabled = true;
2216 			if (itemBase->isSelected()) {
2217 				traceMenuThing.exEnabled = true;
2218 				if (qobject_cast<Via *>(itemBase->layerKinChief())->getAutoroutable()) {
2219 					traceMenuThing.exChecked = false;
2220 				}
2221 			}
2222 			break;
2223 		case ModelPart::CopperFill:
2224 			traceMenuThing.gfrEnabled = true;
2225 		default:
2226 			break;
2227 	}
2228 }
2229 
zoomIn()2230 void MainWindow::zoomIn() {
2231 	m_zoomSlider->zoomIn();
2232 }
2233 
zoomOut()2234 void MainWindow::zoomOut() {
2235 	m_zoomSlider->zoomOut();
2236 }
2237 
fitInWindow()2238 void MainWindow::fitInWindow() {
2239 	if (m_currentGraphicsView == NULL) return;
2240 
2241 	double newZoom = m_currentGraphicsView->fitInWindow();
2242 	m_zoomSlider->setValue(newZoom);
2243 }
2244 
hundredPercentSize()2245 void MainWindow::hundredPercentSize() {
2246 	m_currentGraphicsView->absoluteZoom(100);
2247 	m_zoomSlider->setValue(100);
2248 }
2249 
actualSize()2250 void MainWindow::actualSize() {
2251 	QMessageBox::information(this, tr("Actual Size"),
2252 				tr("It doesn't seem to be possible to automatically determine the actual physical size of the monitor, so "
2253 				"'actual size' as currently implemented is only a guess. "
2254 				"Your best bet would be to drag out a ruler part, then place a real (physical) ruler on top and zoom until they match up."
2255 				));
2256 
2257 
2258 	int dpi = this->physicalDpiX();
2259 	int l = this->logicalDpiX();
2260 
2261 	DebugDialog::debug(QString("actual size %1 %2").arg(dpi).arg(l));
2262 
2263 	// remember the parameter to the next two functions is a percent
2264 	m_currentGraphicsView->absoluteZoom(dpi * 100.0 / GraphicsUtils::SVGDPI);
2265 	m_zoomSlider->setValue(dpi * 100.0 / GraphicsUtils::SVGDPI);
2266 }
2267 
showWelcomeView()2268 void MainWindow::showWelcomeView() {
2269 	setCurrentTabIndex(0);
2270 }
2271 
showBreadboardView()2272 void MainWindow::showBreadboardView() {
2273 	int ix = (m_welcomeView == NULL) ? 0 : 1;
2274 	setCurrentTabIndex(ix);
2275 }
2276 
showSchematicView()2277 void MainWindow::showSchematicView() {
2278 	int ix = (m_welcomeView == NULL) ? 1 : 2;
2279 	setCurrentTabIndex(ix);
2280 }
2281 
showPCBView()2282 void MainWindow::showPCBView() {
2283 	int ix = (m_welcomeView == NULL) ? 2 : 3;
2284 	setCurrentTabIndex(ix);
2285 }
2286 
showProgramView()2287 void MainWindow::showProgramView() {
2288 	int ix = (m_welcomeView == NULL) ? 3 : 4;
2289 	setCurrentTabIndex(ix);
2290 }
2291 
setCurrentView(ViewLayer::ViewID viewID)2292 void MainWindow::setCurrentView(ViewLayer::ViewID viewID)
2293 {
2294     if (viewID == ViewLayer::BreadboardView) showBreadboardView();
2295     else if (viewID == ViewLayer::SchematicView) showSchematicView();
2296     else if (viewID == ViewLayer::PCBView) showPCBView();
2297 }
2298 
showPartsBinIconView()2299 void MainWindow::showPartsBinIconView() {
2300 	if (m_binManager) m_binManager->toIconView();
2301 }
2302 
showPartsBinListView()2303 void MainWindow::showPartsBinListView() {
2304 	if (m_binManager) m_binManager->toListView();
2305 }
2306 
openHelp()2307 void MainWindow::openHelp() {
2308 	QDesktopServices::openUrl(QString("http://fritzing.org/learning/"));
2309 }
2310 
openDonate()2311 void MainWindow::openDonate() {
2312 	QDesktopServices::openUrl(QString("http://fritzing.org/shop/donations/"));
2313 }
2314 
openExamples()2315 void MainWindow::openExamples() {
2316 	QDesktopServices::openUrl(QString("http://fritzing.org/projects/"));
2317 }
2318 
openPartsReference()2319 void MainWindow::openPartsReference() {
2320 	QDesktopServices::openUrl(QString("http://fritzing.org/parts/"));
2321 }
2322 
visitFritzingDotOrg()2323 void MainWindow::visitFritzingDotOrg() {
2324 	 QDesktopServices::openUrl(QString("http://www.fritzing.org"));
2325 }
2326 
reportBug()2327 void MainWindow::reportBug() {
2328      QDesktopServices::openUrl(QString("https://github.com/fritzing/fritzing-app/issues"));
2329 }
2330 
partsEditorHelp()2331 void MainWindow::partsEditorHelp() {
2332 	QDir * dir = FolderUtils::getApplicationSubFolder("help");
2333 	QString path = dir->absoluteFilePath("parts_editor_help.html");
2334 	if (QFileInfo(path).exists()) {
2335 		QDesktopServices::openUrl(QString("file:///%1").arg(path));
2336 	}
2337 }
2338 
2339 
2340 
enableDebug()2341 void MainWindow::enableDebug() {
2342 	DebugDialog::setEnabled(m_enableDebugAct->isChecked());
2343 	if (!m_windowMenu->actions().contains(m_toggleDebuggerOutputAct)) {
2344 	    m_windowMenu->insertSeparator(m_windowMenuSeparator);
2345 		m_windowMenu->insertAction(m_windowMenuSeparator, m_toggleDebuggerOutputAct);
2346 		toggleDebuggerOutput(true);
2347 	}
2348 }
2349 
2350 
openNewPartsEditor(PaletteItem * paletteItem)2351 void MainWindow::openNewPartsEditor(PaletteItem * paletteItem)
2352 {
2353     foreach (QWidget *widget, QApplication::topLevelWidgets()) {
2354         PEMainWindow * peMainWindow = qobject_cast<PEMainWindow *>(widget);
2355 		if (peMainWindow == NULL) continue;
2356 
2357         if (peMainWindow->editsModuleID(paletteItem->moduleID())) {
2358             if (peMainWindow->isMinimized()) peMainWindow->showNormal();
2359             else peMainWindow->show();
2360 	        peMainWindow->raise();
2361             return;
2362         }
2363     }
2364 
2365     PEMainWindow * peMainWindow = new PEMainWindow(m_referenceModel, NULL);
2366     peMainWindow->init(m_referenceModel, false);
2367    if (peMainWindow->setInitialItem(paletteItem)) {
2368 	    peMainWindow->show();
2369 	    peMainWindow->raise();
2370         connect(peMainWindow, SIGNAL(addToMyPartsSignal(ModelPart *)), this, SLOT(addToMyParts(ModelPart *)));
2371    }
2372    else {
2373        delete peMainWindow;
2374    }
2375 }
2376 
getPartsEditorNewAnd(ItemBase * fromItem)2377 void MainWindow::getPartsEditorNewAnd(ItemBase * fromItem)
2378 {
2379     PaletteItem * paletteItem = qobject_cast<PaletteItem *>(fromItem);
2380     if (paletteItem == NULL) return;
2381 
2382     openNewPartsEditor(paletteItem);
2383 }
2384 
openInPartsEditorNew()2385 void MainWindow::openInPartsEditorNew() {
2386 	if (m_currentGraphicsView == NULL) return;
2387 
2388 	PaletteItem *selectedPart = m_currentGraphicsView->getSelectedPart();
2389 	openNewPartsEditor(selectedPart);
2390 }
2391 
createNewSketch()2392 void MainWindow::createNewSketch() {
2393     MainWindow* mw = newMainWindow(m_referenceModel, "", true, true, -1);
2394     mw->move(x()+CascadeFactorX,y()+CascadeFactorY);
2395 	ProcessEventBlocker::processEvents();
2396 
2397 	mw->addDefaultParts();
2398     mw->show();
2399 	mw->hideTempPartsBin();
2400 
2401     QSettings settings;
2402     settings.remove("lastOpenSketch");
2403     mw->clearFileProgressDialog();
2404 }
2405 
minimize()2406 void MainWindow::minimize() {
2407 	this->showMinimized();
2408 }
2409 
toggleToolbar(bool toggle)2410 void MainWindow::toggleToolbar(bool toggle) {
2411 	Q_UNUSED(toggle);
2412 	/*if(toggle) {
2413 		this->m_fileToolBar->show();
2414 		this->m_editToolBar->show();
2415 	} else {
2416 		this->m_fileToolBar->hide();
2417 		this->m_editToolBar->hide();
2418 	}*/
2419 }
2420 
togglePartLibrary(bool toggle)2421 void MainWindow::togglePartLibrary(bool toggle) {
2422 	if(toggle) {
2423 		m_binManager->show();
2424 	} else {
2425 		m_binManager->hide();
2426 	}
2427 }
2428 
toggleInfo(bool toggle)2429 void MainWindow::toggleInfo(bool toggle) {
2430 	if(toggle) {
2431 		((QDockWidget*)m_infoView->parent())->show();
2432 	} else {
2433 		((QDockWidget*)m_infoView->parent())->hide();
2434 	}
2435 }
2436 
toggleUndoHistory(bool toggle)2437 void MainWindow::toggleUndoHistory(bool toggle) {
2438 	if(toggle) {
2439 		((QDockWidget*)m_undoView->parent())->show();
2440 	} else {
2441 		((QDockWidget*)m_undoView->parent())->hide();
2442 	}
2443 }
2444 
toggleDebuggerOutput(bool toggle)2445 void MainWindow::toggleDebuggerOutput(bool toggle) {
2446 	if (toggle) {
2447 		DebugDialog::showDebug();
2448 	}
2449 	else
2450 	{
2451 	}
2452 }
2453 
updateWindowMenu()2454 void MainWindow::updateWindowMenu() {
2455 	m_toggleDebuggerOutputAct->setChecked(DebugDialog::visible());
2456 	foreach (QWidget * widget, QApplication::topLevelWidgets()) {
2457 		MainWindow * mainWindow = qobject_cast<MainWindow *>(widget);
2458 		if (mainWindow == NULL) continue;
2459 
2460 		QAction *action = mainWindow->raiseWindowAction();
2461         if (action != NULL) {
2462 		    action->setChecked(action == m_raiseWindowAct);
2463 		    m_windowMenu->addAction(action);
2464         }
2465 	}
2466 }
2467 
pageSetup()2468 void MainWindow::pageSetup() {
2469 	notYetImplemented(tr("Page Setup"));
2470 }
2471 
notYetImplemented(QString action)2472 void MainWindow::notYetImplemented(QString action) {
2473 	QMessageBox::warning(this, tr("Fritzing"),
2474 				tr("Sorry, \"%1\" has not been implemented yet").arg(action));
2475 }
2476 
rotateIncCW()2477 void MainWindow::rotateIncCW() {
2478 	if (m_currentGraphicsView == NULL) return;
2479 
2480 	if (m_rotate45cwAct->isEnabled()) {
2481 		rotate45cw();
2482 	}
2483 	else if (m_rotate90cwAct->isEnabled()) {
2484 		rotate90cw();
2485 	}
2486 }
2487 
rotateIncCWRubberBand()2488 void MainWindow::rotateIncCWRubberBand() {
2489 	if (m_currentGraphicsView == NULL) return;
2490 
2491 	if (m_rotate45cwAct->isEnabled()) {
2492 		m_currentGraphicsView->rotateX(45, true, NULL);
2493 	}
2494 	else if (m_rotate90cwAct->isEnabled()) {
2495 		m_currentGraphicsView->rotateX(90, true, NULL);
2496 	}
2497 }
2498 
rotateIncCCW()2499 void MainWindow::rotateIncCCW() {
2500 	if (m_currentGraphicsView == NULL) return;
2501 
2502 	if (m_rotate45ccwAct->isEnabled()) {
2503 		m_currentGraphicsView->rotateX(315, true, NULL);
2504 	}
2505 	else if (m_rotate90ccwAct->isEnabled()) {
2506 		m_currentGraphicsView->rotateX(270, true, NULL);
2507 	}
2508 }
2509 
rotateIncCCWRubberBand()2510 void MainWindow::rotateIncCCWRubberBand() {
2511 	if (m_currentGraphicsView == NULL) return;
2512 
2513 	if (m_rotate45ccwAct->isEnabled()) {
2514 		m_currentGraphicsView->rotateX(315, true, NULL);
2515 	}
2516 	else if (m_rotate90ccwAct->isEnabled()) {
2517 		m_currentGraphicsView->rotateX(270, true, NULL);
2518 	}
2519 }
2520 
rotate90cw()2521 void MainWindow::rotate90cw() {
2522 	if (m_currentGraphicsView == NULL) return;
2523 
2524 	m_currentGraphicsView->rotateX(90, false, NULL);
2525 }
2526 
rotate90ccw()2527 void MainWindow::rotate90ccw() {
2528 	if (m_currentGraphicsView == NULL) return;
2529 
2530 	m_currentGraphicsView->rotateX(270, false, NULL);
2531 }
2532 
rotate45ccw()2533 void MainWindow::rotate45ccw() {
2534 	if (m_currentGraphicsView == NULL) return;
2535 
2536 	m_currentGraphicsView->rotateX(315, false, NULL);
2537 }
2538 
rotate45cw()2539 void MainWindow::rotate45cw() {
2540 	if (m_currentGraphicsView == NULL) return;
2541 
2542 	m_currentGraphicsView->rotateX(45, false, NULL);
2543 }
2544 
rotate180()2545 void MainWindow::rotate180() {
2546 	if (m_currentGraphicsView == NULL) return;
2547 
2548 	m_currentGraphicsView->rotateX(180, false, NULL);
2549 }
2550 
flipHorizontal()2551 void MainWindow::flipHorizontal() {
2552 	m_currentGraphicsView->flipX(Qt::Horizontal, false);
2553 }
2554 
flipVertical()2555 void MainWindow::flipVertical() {
2556 	m_currentGraphicsView->flipX(Qt::Vertical, false);
2557 }
2558 
sendToBack()2559 void MainWindow::sendToBack() {
2560 	if (m_currentGraphicsView == NULL) return;
2561 
2562 	m_currentGraphicsView->sendToBack();
2563 }
2564 
sendBackward()2565 void MainWindow::sendBackward() {
2566 	if (m_currentGraphicsView == NULL) return;
2567 
2568 	m_currentGraphicsView->sendBackward();
2569 }
2570 
bringForward()2571 void MainWindow::bringForward() {
2572 	if (m_currentGraphicsView == NULL) return;
2573 
2574 	m_currentGraphicsView->bringForward();
2575 }
2576 
bringToFront()2577 void MainWindow::bringToFront() {
2578 	if (m_currentGraphicsView == NULL) return;
2579 
2580 	m_currentGraphicsView->bringToFront();
2581 }
2582 
alignLeft()2583 void MainWindow::alignLeft() {
2584 	if (m_currentGraphicsView == NULL) return;
2585 
2586 	m_currentGraphicsView->alignItems(Qt::AlignLeft);
2587 }
2588 
alignVerticalCenter()2589 void MainWindow::alignVerticalCenter() {
2590 	if (m_currentGraphicsView == NULL) return;
2591 
2592 	m_currentGraphicsView->alignItems(Qt::AlignVCenter);
2593 }
2594 
alignRight()2595 void MainWindow::alignRight() {
2596 	if (m_currentGraphicsView == NULL) return;
2597 
2598 	m_currentGraphicsView->alignItems(Qt::AlignRight);
2599 }
2600 
alignTop()2601 void MainWindow::alignTop() {
2602 	if (m_currentGraphicsView == NULL) return;
2603 
2604 	m_currentGraphicsView->alignItems(Qt::AlignTop);
2605 }
2606 
alignHorizontalCenter()2607 void MainWindow::alignHorizontalCenter() {
2608 	if (m_currentGraphicsView == NULL) return;
2609 
2610 	m_currentGraphicsView->alignItems(Qt::AlignHCenter);
2611 }
2612 
alignBottom()2613 void MainWindow::alignBottom() {
2614 	if (m_currentGraphicsView == NULL) return;
2615 
2616 	m_currentGraphicsView->alignItems(Qt::AlignBottom);
2617 }
2618 
showAllLayers()2619 void MainWindow::showAllLayers() {
2620 	if (m_currentGraphicsView == NULL) return;
2621 
2622 	m_currentGraphicsView->setAllLayersVisible(true);
2623 	updateLayerMenu();
2624 }
2625 
hideAllLayers()2626 void MainWindow::hideAllLayers() {
2627 	if (m_currentGraphicsView == NULL) return;
2628 
2629 	m_currentGraphicsView->setAllLayersVisible(false);
2630 	updateLayerMenu();
2631 }
2632 
openURL()2633 void MainWindow::openURL() {
2634 	QAction *action = qobject_cast<QAction *>(sender());
2635 	if (action == NULL) return;
2636 
2637 	QString href = action->data().toString();
2638 	if (href.isEmpty()) return;
2639 
2640 	QDesktopServices::openUrl(href);
2641 }
2642 
openRecentOrExampleFile()2643 void MainWindow::openRecentOrExampleFile() {
2644 	QAction *action = qobject_cast<QAction *>(sender());
2645 	if (action) {
2646 		openRecentOrExampleFile(action->data().toString(), action->text());
2647 	}
2648 }
2649 
openRecentOrExampleFile(const QString & filename,const QString & actionText)2650 void MainWindow::openRecentOrExampleFile(const QString & filename, const QString & actionText) {
2651 	if (alreadyOpen(filename)) {
2652 		return;
2653 	}
2654 
2655 	if (!QFileInfo(filename).exists()) {
2656 		QMessageBox::warning(NULL, tr("Fritzing"), tr("File '%1' not found").arg(filename));
2657 		return;
2658 	}
2659 
2660 	MainWindow* mw = newMainWindow(m_referenceModel, filename, true, true, -1);
2661 	bool readOnly = m_openExampleActions.contains(actionText);
2662 	mw->setReadOnly(readOnly);
2663 	mw->loadWhich(filename, !readOnly,!readOnly,!readOnly, "");
2664 	mw->clearFileProgressDialog();
2665 	closeIfEmptySketch(mw);
2666 }
2667 
removeActionsStartingAt(QMenu * menu,int start)2668 void MainWindow::removeActionsStartingAt(QMenu * menu, int start) {
2669 	QList<QAction*> actions = menu->actions();
2670 
2671 	if(start == 0) {
2672 		menu->clear();
2673 	} else {
2674 		for(int i=start; i < actions.size(); i++) {
2675 			menu->removeAction(actions.at(i));
2676 		}
2677 	}
2678 }
2679 
hideShowProgramMenu()2680 void MainWindow::hideShowProgramMenu() {
2681     if (m_currentWidget == NULL) return;
2682 
2683     bool show = m_programView == NULL || m_currentWidget->contentView() != m_programView;
2684     //if (m_fileMenu) m_fileMenu->menuAction()->setVisible(show);
2685     if (m_viewMenu) {
2686         m_viewMenu->menuAction()->setVisible(show);
2687         m_viewMenu->setEnabled(show);
2688     }
2689     if (m_partMenu) {
2690         m_partMenu->menuAction()->setVisible(show);
2691         m_partMenu->setEnabled(show);
2692     }
2693     if (m_editMenu) {
2694         m_editMenu->menuAction()->setVisible(show);
2695         m_editMenu->setEnabled(show);
2696         m_copyAct->setEnabled(show);
2697         m_cutAct->setEnabled(show);
2698         m_selectAllAct->setEnabled(show);
2699         m_undoAct->setEnabled(show);
2700         m_redoAct->setEnabled(show);
2701     }
2702     if (m_programView) m_programView->showMenus(!show);
2703 }
2704 
hideShowTraceMenu()2705 void MainWindow::hideShowTraceMenu() {
2706     if (m_pcbTraceMenu) m_pcbTraceMenu->menuAction()->setVisible(m_currentGraphicsView == m_pcbGraphicsView);
2707 	if (m_schematicTraceMenu) m_schematicTraceMenu->menuAction()->setVisible(m_currentGraphicsView == m_schematicGraphicsView);
2708 	if (m_breadboardTraceMenu) m_breadboardTraceMenu->menuAction()->setVisible(m_currentGraphicsView == m_breadboardGraphicsView);
2709 }
2710 
createTraceMenuActions()2711 void MainWindow::createTraceMenuActions() {
2712 	m_newAutorouteAct = new QAction(tr("Autoroute"), this);
2713 	m_newAutorouteAct->setStatusTip(tr("Autoroute connections..."));
2714 	m_newAutorouteAct->setShortcut(tr("Shift+Ctrl+A"));
2715 	connect(m_newAutorouteAct, SIGNAL(triggered()), this, SLOT(newAutoroute()));
2716 
2717 	createOrderFabAct();
2718 	createActiveLayerActions();
2719 
2720 	QAction * traceAct = new QAction(tr("&Create trace from ratsnest"), this);
2721 	traceAct->setStatusTip(tr("Create a trace from the ratsnest line"));
2722 	m_createTraceWireAct = new WireAction(traceAct);
2723 	connect(m_createTraceWireAct, SIGNAL(triggered()), this, SLOT(createTrace()));
2724 	traceAct = new QAction(tr("&Create wire from ratsnest"), this);
2725 	traceAct->setStatusTip(tr("Create a wire from the ratsnest line"));
2726 	m_createWireWireAct = new WireAction(traceAct);
2727 	connect(m_createWireWireAct, SIGNAL(triggered()), this, SLOT(createTrace()));
2728 
2729 	m_excludeFromAutorouteAct = new QAction(tr("Do not autoroute"), this);
2730 	m_excludeFromAutorouteAct->setStatusTip(tr("When autorouting, do not rip up this trace wire, via, or jumper item"));
2731 	connect(m_excludeFromAutorouteAct, SIGNAL(triggered()), this, SLOT(excludeFromAutoroute()));
2732 	m_excludeFromAutorouteAct->setCheckable(true);
2733 	m_excludeFromAutorouteWireAct = new WireAction(m_excludeFromAutorouteAct);
2734 	connect(m_excludeFromAutorouteWireAct, SIGNAL(triggered()), this, SLOT(excludeFromAutoroute()));
2735 
2736     m_changeTraceLayerAct = new QAction(tr("Move to other side of the board"), this);
2737 	m_changeTraceLayerAct->setStatusTip(tr("Move selected traces to the other side of the board (note: the 'first' trace will be moved and the rest will follow to the same side)"));
2738 	connect(m_changeTraceLayerAct, SIGNAL(triggered()), this, SLOT(changeTraceLayer()));
2739 	m_changeTraceLayerWireAct = new WireAction(m_changeTraceLayerAct);
2740 	connect(m_changeTraceLayerWireAct, SIGNAL(triggered()), this, SLOT(changeTraceLayer()));
2741 
2742     m_showUnroutedAct = new QAction(tr("Show unrouted"), this);
2743 	m_showUnroutedAct->setStatusTip(tr("Highlight all unrouted connectors"));
2744 	connect(m_showUnroutedAct, SIGNAL(triggered()), this, SLOT(showUnrouted()));
2745 
2746 	m_selectAllTracesAct = new QAction(tr("Select All Traces"), this);
2747 	m_selectAllTracesAct->setStatusTip(tr("Select all trace wires"));
2748 	connect(m_selectAllTracesAct, SIGNAL(triggered()), this, SLOT(selectAllTraces()));
2749 
2750 	m_selectAllWiresAct = new QAction(tr("Select All Wires"), this);
2751 	m_selectAllWiresAct->setStatusTip(tr("Select all wires"));
2752 	connect(m_selectAllWiresAct, SIGNAL(triggered()), this, SLOT(selectAllTraces()));
2753 
2754 	m_selectAllCopperFillAct = new QAction(tr("Select All CopperFill"), this);
2755 	m_selectAllCopperFillAct->setStatusTip(tr("Select all copper fill items"));
2756 	connect(m_selectAllCopperFillAct, SIGNAL(triggered()), this, SLOT(selectAllCopperFill()));
2757 
2758 	m_updateRoutingStatusAct = new QAction(tr("Force Update Routing Status and Ratsnests"), this);
2759 	m_updateRoutingStatusAct->setStatusTip(tr("Recalculate routing status and ratsnest wires (in case the auto-update isn't working correctly)"));
2760 	connect(m_updateRoutingStatusAct, SIGNAL(triggered()), this, SLOT(updateRoutingStatus()));
2761 
2762 	m_selectAllExcludedTracesAct = new QAction(tr("Select All \"Don't Autoroute\" Traces"), this);
2763 	m_selectAllExcludedTracesAct->setStatusTip(tr("Select all trace wires excluded from autorouting"));
2764 	connect(m_selectAllExcludedTracesAct, SIGNAL(triggered()), this, SLOT(selectAllExcludedTraces()));
2765 
2766 	m_selectAllIncludedTracesAct = new QAction(tr("Select All Autoroutable Traces"), this);
2767 	m_selectAllIncludedTracesAct->setStatusTip(tr("Select all trace wires that can be changed during autorouting"));
2768 	connect(m_selectAllIncludedTracesAct, SIGNAL(triggered()), this, SLOT(selectAllIncludedTraces()));
2769 
2770 	m_selectAllJumperItemsAct = new QAction(tr("Select All Jumpers"), this);
2771 	m_selectAllJumperItemsAct->setStatusTip(tr("Select all jumper item parts"));
2772 	connect(m_selectAllJumperItemsAct, SIGNAL(triggered()), this, SLOT(selectAllJumperItems()));
2773 
2774 	m_selectAllViasAct = new QAction(tr("Select All Vias"), this);
2775 	m_selectAllViasAct->setStatusTip(tr("Select all via parts"));
2776 	connect(m_selectAllViasAct, SIGNAL(triggered()), this, SLOT(selectAllVias()));
2777 
2778 	m_tidyWiresAct = new QAction(tr("Tidy Wires"), this);
2779 	m_tidyWiresAct->setStatusTip(tr("Tidy selected wires"));
2780 	connect(m_tidyWiresAct, SIGNAL(triggered()), this, SLOT(tidyWires()));
2781 
2782 	m_groundFillAct = new QAction(tr("Ground Fill"), this);
2783 	m_groundFillAct->setStatusTip(tr("Fill empty regions of the copper layer--fill will include all traces connected to a GROUND"));
2784 	connect(m_groundFillAct, SIGNAL(triggered()), this, SLOT(groundFill()));
2785 
2786 	m_copperFillAct = new QAction(tr("Copper Fill"), this);
2787 	m_copperFillAct->setStatusTip(tr("Fill empty regions of the copper layer--not including traces connected to a GROUND"));
2788 	connect(m_copperFillAct, SIGNAL(triggered()), this, SLOT(copperFill()));
2789 
2790 	m_removeGroundFillAct = new QAction(tr("Remove Copper Fill"), this);
2791 	m_removeGroundFillAct->setStatusTip(tr("Remove the copper fill"));
2792 	connect(m_removeGroundFillAct, SIGNAL(triggered()), this, SLOT(removeGroundFill()));
2793 
2794 	m_setGroundFillSeedsAct = new QAction(tr("Choose Ground Fill Seed(s)..."), this);
2795 	m_setGroundFillSeedsAct->setStatusTip(tr("Fill empty regions of the copper layer--fill will include all traces connected to the seeds"));
2796 	connect(m_setGroundFillSeedsAct, SIGNAL(triggered()), this, SLOT(setGroundFillSeeds()));
2797 
2798 	m_setOneGroundFillSeedAct = new ConnectorItemAction(tr("Set Ground Fill Seed"), this);
2799 	m_setOneGroundFillSeedAct->setStatusTip(tr("Treat this connector and its connections as a 'ground' during ground fill."));
2800 	m_setOneGroundFillSeedAct->setCheckable(true);
2801 	connect(m_setOneGroundFillSeedAct, SIGNAL(triggered()), this, SLOT(setOneGroundFillSeed()));
2802 
2803 	m_clearGroundFillSeedsAct = new ConnectorItemAction(tr("Clear Ground Fill Seeds"), this);
2804 	m_clearGroundFillSeedsAct->setStatusTip(tr("Clear ground fill seeds--enable copper fill only."));
2805 	connect(m_clearGroundFillSeedsAct, SIGNAL(triggered()), this, SLOT(clearGroundFillSeeds()));
2806 
2807 	m_setGroundFillKeepoutAct = new QAction(tr("Set Ground Fill Keepout..."), this);
2808 	m_setGroundFillKeepoutAct->setStatusTip(tr("Set the minimum distance between ground fill and traces or connectors"));
2809 	connect(m_setGroundFillKeepoutAct, SIGNAL(triggered()), this, SLOT(setGroundFillKeepout()));
2810 
2811 
2812 
2813 	m_newDesignRulesCheckAct = new QAction(tr("Design Rules Check (DRC)"), this);
2814 	m_newDesignRulesCheckAct->setStatusTip(tr("Highlights any parts that are too close together for safe board production"));
2815 	m_newDesignRulesCheckAct->setShortcut(tr("Shift+Ctrl+D"));
2816 	connect(m_newDesignRulesCheckAct, SIGNAL(triggered()), this, SLOT(newDesignRulesCheck()));
2817 
2818 	m_checkLoadedTracesAct = new QAction(tr("Check Loaded Traces"), this);
2819 	m_checkLoadedTracesAct->setStatusTip(tr("Select any traces where the screen location doesn't match the actual location. Only needed for sketches autorouted with version 0.7.10 or earlier"));
2820 	connect(m_checkLoadedTracesAct, SIGNAL(triggered()), this, SLOT(checkLoadedTraces()));
2821 
2822 	m_autorouterSettingsAct = new QAction(tr("Autorouter/DRC settings..."), this);
2823 	m_autorouterSettingsAct->setStatusTip(tr("Set autorouting parameters including keepout..."));
2824 	connect(m_autorouterSettingsAct, SIGNAL(triggered()), this, SLOT(autorouterSettings()));
2825 
2826 	m_fabQuoteAct = new QAction(tr("Fritzing Fab Quote..."), this);
2827 	m_fabQuoteAct->setStatusTip(tr("How much would it could to produce a PCB from this sketch with Fritzing Fab"));
2828 	connect(m_fabQuoteAct, SIGNAL(triggered()), this, SLOT(fabQuote()));
2829 
2830 }
2831 
createActiveLayerActions()2832 void MainWindow::createActiveLayerActions() {
2833 
2834 	m_viewFromBelowToggleAct = new QAction(tr("View from below"), this);
2835 	m_viewFromBelowToggleAct->setStatusTip(tr("View the PCB from the bottom layers upwards"));
2836     m_viewFromBelowToggleAct->setCheckable(true);
2837     m_viewFromBelowToggleAct->setChecked(false);
2838 	connect(m_viewFromBelowToggleAct, SIGNAL(triggered()), this, SLOT(setViewFromBelowToggle()));
2839 
2840 	m_viewFromBelowAct = new QAction(tr("View from below"), this);
2841     m_viewFromBelowAct->setStatusTip(tr("View the PCB from the bottom layers upwards"));
2842     m_viewFromBelowAct->setCheckable(true);
2843 	connect(m_viewFromBelowAct, SIGNAL(triggered()), this, SLOT(setViewFromBelow()));
2844 
2845 	m_viewFromAboveAct = new QAction(tr("View from above"), this);
2846 	m_viewFromAboveAct->setStatusTip(tr("View the PCB from the top layers downwards"));
2847     m_viewFromAboveAct->setCheckable(true);
2848 	connect(m_viewFromAboveAct, SIGNAL(triggered()), this, SLOT(setViewFromAbove()));
2849 
2850 	m_activeLayerBothAct = new QAction(tr("Set both copper layers clickable"), this);
2851 	m_activeLayerBothAct->setStatusTip(tr("Set both copper layers clickable"));
2852     m_activeLayerBothAct->setShortcut(tr("Shift+Ctrl+3"));
2853     m_activeLayerBothAct->setCheckable(true);
2854     connect(m_activeLayerBothAct, SIGNAL(triggered()), this, SLOT(activeLayerBoth()));
2855 
2856 	m_activeLayerTopAct = new QAction(tr("Set copper top layer clickable"), this);
2857 	m_activeLayerTopAct->setStatusTip(tr("Set copper top layer clickable"));
2858     m_activeLayerTopAct->setShortcut(tr("Shift+Ctrl+2"));
2859     m_activeLayerTopAct->setCheckable(true);
2860     connect(m_activeLayerTopAct, SIGNAL(triggered()), this, SLOT(activeLayerTop()));
2861 
2862 	m_activeLayerBottomAct = new QAction(tr("Set copper bottom layer clickable"), this);
2863 	m_activeLayerBottomAct->setStatusTip(tr("Set copper bottom layer clickable"));
2864     m_activeLayerBottomAct->setShortcut(tr("Shift+Ctrl+1"));
2865     m_activeLayerBottomAct->setCheckable(true);
2866     connect(m_activeLayerBottomAct, SIGNAL(triggered()), this, SLOT(activeLayerBottom()));
2867 }
2868 
activeLayerBoth()2869 void MainWindow::activeLayerBoth() {
2870 	PCBSketchWidget * pcbSketchWidget = qobject_cast<PCBSketchWidget *>(m_currentGraphicsView);
2871 	if (pcbSketchWidget == NULL) return;
2872 
2873 	pcbSketchWidget->setLayerActive(ViewLayer::Copper1, true);
2874 	pcbSketchWidget->setLayerActive(ViewLayer::Copper0, true);
2875 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, true);
2876 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, true);
2877 	AutoCloseMessageBox::showMessage(this, tr("Copper Top and Copper Bottom layers are both active"));
2878 	updateActiveLayerButtons();
2879 }
2880 
activeLayerTop()2881 void MainWindow::activeLayerTop() {
2882 	PCBSketchWidget * pcbSketchWidget = qobject_cast<PCBSketchWidget *>(m_currentGraphicsView);
2883 	if (pcbSketchWidget == NULL) return;
2884 
2885 	pcbSketchWidget->setLayerActive(ViewLayer::Copper1, true);
2886 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, true);
2887 	pcbSketchWidget->setLayerActive(ViewLayer::Copper0, false);
2888 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, false);
2889 	AutoCloseMessageBox::showMessage(this, tr("Copper Top layer is active"));
2890 	updateActiveLayerButtons();
2891 }
2892 
activeLayerBottom()2893 void MainWindow::activeLayerBottom() {
2894 	PCBSketchWidget * pcbSketchWidget = qobject_cast<PCBSketchWidget *>(m_currentGraphicsView);
2895 	if (pcbSketchWidget == NULL) return;
2896 
2897 	pcbSketchWidget->setLayerActive(ViewLayer::Copper1, false);
2898 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, false);
2899 	pcbSketchWidget->setLayerActive(ViewLayer::Copper0, true);
2900 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, true);
2901 	AutoCloseMessageBox::showMessage(this, tr("Copper Bottom layer is active"));
2902 	updateActiveLayerButtons();
2903 }
2904 
toggleActiveLayer()2905 void MainWindow::toggleActiveLayer()
2906 {
2907 	PCBSketchWidget * pcbSketchWidget = qobject_cast<PCBSketchWidget *>(m_currentGraphicsView);
2908 	if (pcbSketchWidget == NULL) return;
2909 
2910 	int index = activeLayerIndex();
2911 	switch (index) {
2912 		case 0:
2913 			activeLayerBottom();
2914 			return;
2915 		case 1:
2916 			activeLayerTop();
2917 			return;
2918 		case 2:
2919 			activeLayerBoth();
2920 			return;
2921 		default:
2922 			return;
2923 	}
2924 }
2925 
2926 
createOrderFabAct()2927 void MainWindow::createOrderFabAct() {
2928 	if (m_orderFabAct != NULL) return;
2929 
2930 	m_orderFabAct = new QAction(tr("Order a PCB..."), this);
2931 	m_orderFabAct->setStatusTip(tr("Order a PCB created from your sketch--from fabulous Fritzing Fab"));
2932 	connect(m_orderFabAct, SIGNAL(triggered()), this, SLOT(orderFab()));
2933 }
2934 
2935 
newAutoroute()2936 void MainWindow::newAutoroute() {
2937 	PCBSketchWidget * pcbSketchWidget = qobject_cast<PCBSketchWidget *>(m_currentGraphicsView);
2938 	if (pcbSketchWidget == NULL) return;
2939 
2940     ItemBase * board = NULL;
2941     if (pcbSketchWidget->autorouteTypePCB()) {
2942         int boardCount;
2943 		board = pcbSketchWidget->findSelectedBoard(boardCount);
2944         if (boardCount == 0) {
2945             QMessageBox::critical(this, tr("Fritzing"),
2946                        tr("Your sketch does not have a board yet!  Please add a PCB in order to use the autorouter."));
2947             return;
2948         }
2949         if (board == NULL) {
2950             QMessageBox::critical(this, tr("Fritzing"),
2951                        tr("Please select the board you want to autoroute. The autorouter can only handle one board at a time."));
2952             return;
2953         }
2954 	}
2955 
2956     dynamic_cast<SketchAreaWidget *>(pcbSketchWidget->parent())->routingStatusLabel()->setText(tr("Autorouting..."));
2957 
2958 	bool copper0Active = pcbSketchWidget->layerIsActive(ViewLayer::Copper0);
2959 	bool copper1Active = pcbSketchWidget->layerIsActive(ViewLayer::Copper1);
2960 
2961 	AutorouteProgressDialog progress(tr("Autorouting Progress..."), true, true, true, true, pcbSketchWidget, this);
2962 	progress.setModal(true);
2963 	progress.show();
2964 	QRect pr = progress.frameGeometry();
2965 	QRect wr = this->frameGeometry();
2966 	progress.move(wr.right() - pr.width(), pr.top());
2967 
2968 
2969 	pcbSketchWidget->scene()->clearSelection();
2970 	pcbSketchWidget->setIgnoreSelectionChangeEvents(true);
2971 	Autorouter * autorouter = NULL;
2972     autorouter = new MazeRouter(pcbSketchWidget, board, true);
2973 
2974 	connect(autorouter, SIGNAL(wantTopVisible()), this, SLOT(activeLayerTop()), Qt::DirectConnection);
2975 	connect(autorouter, SIGNAL(wantBottomVisible()), this, SLOT(activeLayerBottom()), Qt::DirectConnection);
2976 	connect(autorouter, SIGNAL(wantBothVisible()), this, SLOT(activeLayerBoth()), Qt::DirectConnection);
2977 
2978 	connect(&progress, SIGNAL(cancel()), autorouter, SLOT(cancel()), Qt::DirectConnection);
2979 	connect(&progress, SIGNAL(skip()), autorouter, SLOT(cancelTrace()), Qt::DirectConnection);
2980 	connect(&progress, SIGNAL(stop()), autorouter, SLOT(stopTracing()), Qt::DirectConnection);
2981 	connect(&progress, SIGNAL(best()), autorouter, SLOT(useBest()), Qt::DirectConnection);
2982 	connect(&progress, SIGNAL(spinChange(int)), autorouter, SLOT(setMaxCycles(int)), Qt::DirectConnection);
2983 
2984 	connect(autorouter, SIGNAL(setMaximumProgress(int)), &progress, SLOT(setMaximum(int)), Qt::DirectConnection);
2985 	connect(autorouter, SIGNAL(setProgressValue(int)), &progress, SLOT(setValue(int)), Qt::DirectConnection);
2986 	connect(autorouter, SIGNAL(setProgressMessage(const QString &)), &progress, SLOT(setMessage(const QString &)));
2987 	connect(autorouter, SIGNAL(setProgressMessage2(const QString &)), &progress, SLOT(setMessage2(const QString &)));
2988 	connect(autorouter, SIGNAL(setCycleMessage(const QString &)), &progress, SLOT(setSpinLabel(const QString &)));
2989 	connect(autorouter, SIGNAL(setCycleCount(int)), &progress, SLOT(setSpinValue(int)));
2990 	connect(autorouter, SIGNAL(disableButtons()), &progress, SLOT(disableButtons()));
2991 
2992 	ProcessEventBlocker::processEvents();
2993 	ProcessEventBlocker::block();
2994 
2995 	autorouter->start();
2996 	pcbSketchWidget->setIgnoreSelectionChangeEvents(false);
2997 
2998 	delete autorouter;
2999 
3000 	pcbSketchWidget->setLayerActive(ViewLayer::Copper1, copper1Active);
3001 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, copper1Active);
3002 	pcbSketchWidget->setLayerActive(ViewLayer::Copper0, copper0Active);
3003 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, copper0Active);
3004 	updateActiveLayerButtons();
3005 
3006 	ProcessEventBlocker::unblock();
3007 }
3008 
createTrace()3009 void MainWindow::createTrace() {
3010 	m_currentGraphicsView->createTrace(retrieveWire(), true);
3011 }
3012 
excludeFromAutoroute()3013 void MainWindow::excludeFromAutoroute() {
3014 	Wire * wire = retrieveWire();
3015 	PCBSketchWidget * pcbSketchWidget = qobject_cast<PCBSketchWidget *>(m_currentGraphicsView);
3016 	if (pcbSketchWidget == NULL) return;
3017 
3018 	pcbSketchWidget->excludeFromAutoroute(wire == NULL ? m_excludeFromAutorouteAct->isChecked() : m_excludeFromAutorouteWireAct->isChecked());
3019 }
3020 
selectAllTraces()3021 void MainWindow::selectAllTraces()
3022 {
3023 	m_currentGraphicsView->selectAllWires(m_currentGraphicsView->getTraceFlag());
3024 }
3025 
updateRoutingStatus()3026 void MainWindow::updateRoutingStatus() {
3027 	RoutingStatus routingStatus;
3028 	routingStatus.zero();
3029 	m_currentGraphicsView->updateRoutingStatus(NULL, routingStatus, true);
3030 }
3031 
selectAllExcludedTraces()3032 void MainWindow::selectAllExcludedTraces() {
3033 	PCBSketchWidget * pcbSketchWidget = qobject_cast<PCBSketchWidget *>(m_currentGraphicsView);
3034 	if (pcbSketchWidget == NULL) return;
3035 
3036     pcbSketchWidget->selectAllExcludedTraces();
3037 }
3038 
selectAllIncludedTraces()3039 void MainWindow::selectAllIncludedTraces() {
3040 	PCBSketchWidget * pcbSketchWidget = qobject_cast<PCBSketchWidget *>(m_currentGraphicsView);
3041 	if (pcbSketchWidget == NULL) return;
3042 
3043     pcbSketchWidget->selectAllIncludedTraces();
3044 }
3045 
selectAllJumperItems()3046 void MainWindow::selectAllJumperItems() {
3047 	m_pcbGraphicsView->selectAllItemType(ModelPart::Jumper, tr("jumpers"));
3048 }
3049 
selectAllCopperFill()3050 void MainWindow::selectAllCopperFill() {
3051 	m_pcbGraphicsView->selectAllItemType(ModelPart::CopperFill, tr("copperfill"));
3052 }
3053 
selectAllVias()3054 void MainWindow::selectAllVias() {
3055 	m_pcbGraphicsView->selectAllItemType(ModelPart::Via, tr("vias"));
3056 }
3057 
notClosableForAWhile()3058 void MainWindow::notClosableForAWhile() {
3059 	m_dontClose = true;
3060 
3061 	QTimer::singleShot(500, this, SLOT(ensureClosable()));
3062 }
3063 
ensureClosable()3064 void MainWindow::ensureClosable() {
3065 	m_dontClose = false;
3066 }
3067 
showPartLabels()3068 void MainWindow::showPartLabels() {
3069 	if (m_currentGraphicsView == NULL) return;
3070 
3071 	m_currentGraphicsView->showPartLabels(m_showPartLabelAct->data().toBool());
3072 }
3073 
addNote()3074 void MainWindow::addNote() {
3075 	if (m_currentGraphicsView == NULL) return;
3076 
3077 	ViewGeometry vg;
3078 	vg.setRect(0, 0, Note::initialMinWidth, Note::initialMinHeight);
3079 	QPointF tl = m_currentGraphicsView->mapToScene(QPoint(0, 0));
3080 	QSizeF vpSize = m_currentGraphicsView->viewport()->size();
3081 	tl.setX(tl.x() + ((vpSize.width() - Note::initialMinWidth) / 2.0));
3082 	tl.setY(tl.y() + ((vpSize.height() - Note::initialMinHeight) / 2.0));
3083 	vg.setLoc(tl);
3084 
3085 	QUndoCommand * parentCommand = new QUndoCommand(tr("Add Note"));
3086 	m_currentGraphicsView->stackSelectionState(false, parentCommand);
3087 	m_currentGraphicsView->scene()->clearSelection();
3088 	new AddItemCommand(m_currentGraphicsView, BaseCommand::SingleView, ModuleIDNames::NoteModuleIDName, m_currentGraphicsView->defaultViewLayerPlacement(NULL), vg, ItemBase::getNextID(), false, -1, parentCommand);
3089 	m_undoStack->push(parentCommand);
3090 }
3091 
alreadyOpen(const QString & fileName)3092 bool MainWindow::alreadyOpen(const QString & fileName) {
3093     foreach (QWidget * widget, QApplication::topLevelWidgets()) {
3094         MainWindow * mainWindow = qobject_cast<MainWindow *>(widget);
3095         if (mainWindow == NULL) continue;
3096 
3097 		// don't load two copies of the same file
3098 		if (mainWindow->fileName().compare(fileName) == 0) {
3099 			mainWindow->raise();
3100 			return true;
3101 		}
3102     }
3103 
3104 	return false;
3105 }
3106 
enableAddBendpointAct(QGraphicsItem * graphicsItem)3107 void MainWindow::enableAddBendpointAct(QGraphicsItem * graphicsItem) {
3108 	m_addBendpointAct->setEnabled(false);
3109 	m_convertToViaAct->setEnabled(false);
3110 	m_flattenCurveAct->setEnabled(false);
3111 
3112 	Wire * wire = dynamic_cast<Wire *>(graphicsItem);
3113 	if (wire == NULL) return;
3114 	if (wire->getRatsnest()) return;
3115 
3116 	m_flattenCurveAct->setEnabled(wire->isCurved());
3117 
3118 	BendpointAction * bendpointAction = qobject_cast<BendpointAction *>(m_addBendpointAct);
3119 	BendpointAction * convertToViaAction = qobject_cast<BendpointAction *>(m_convertToViaAct);
3120 	FGraphicsScene * scene = qobject_cast<FGraphicsScene *>(graphicsItem->scene());
3121 	if (scene != NULL) {
3122 		bendpointAction->setLastLocation(scene->lastContextMenuPos());
3123 		convertToViaAction->setLastLocation(scene->lastContextMenuPos());
3124 	}
3125 
3126 	bool enabled = false;
3127     bool ctvEnabled = false;
3128 	if (m_currentGraphicsView->lastHoverEnterConnectorItem()) {
3129 		bendpointAction->setText(tr("Remove Bendpoint"));
3130 		bendpointAction->setLastHoverEnterConnectorItem(m_currentGraphicsView->lastHoverEnterConnectorItem());
3131 		bendpointAction->setLastHoverEnterItem(NULL);
3132 		convertToViaAction->setLastHoverEnterConnectorItem(m_currentGraphicsView->lastHoverEnterConnectorItem());
3133 		convertToViaAction->setLastHoverEnterItem(NULL);
3134 		ctvEnabled = enabled = true;
3135 	}
3136 	else if (m_currentGraphicsView->lastHoverEnterItem()) {
3137 		bendpointAction->setText(tr("Add Bendpoint"));
3138 		bendpointAction->setLastHoverEnterItem(m_currentGraphicsView->lastHoverEnterItem());
3139 		bendpointAction->setLastHoverEnterConnectorItem(NULL);
3140 		convertToViaAction->setLastHoverEnterItem(NULL);
3141 		convertToViaAction->setLastHoverEnterConnectorItem(NULL);
3142 		enabled = true;
3143 	}
3144 	else {
3145 		bendpointAction->setLastHoverEnterItem(NULL);
3146 		bendpointAction->setLastHoverEnterConnectorItem(NULL);
3147 		convertToViaAction->setLastHoverEnterItem(NULL);
3148 		convertToViaAction->setLastHoverEnterConnectorItem(NULL);
3149 	}
3150 
3151 	m_addBendpointAct->setEnabled(enabled);
3152 	m_convertToViaAct->setEnabled(ctvEnabled && (m_currentGraphicsView == m_pcbGraphicsView));
3153 }
3154 
addBendpoint()3155 void MainWindow::addBendpoint()
3156 {
3157 	BendpointAction * bendpointAction = qobject_cast<BendpointAction *>(m_addBendpointAct);
3158 
3159 	m_currentGraphicsView->addBendpoint(bendpointAction->lastHoverEnterItem(),
3160 										bendpointAction->lastHoverEnterConnectorItem(),
3161 										bendpointAction->lastLocation());
3162 }
3163 
convertToVia()3164 void MainWindow::convertToVia()
3165 {
3166 	BendpointAction * bendpointAction = qobject_cast<BendpointAction *>(m_convertToViaAct);
3167 
3168 	m_pcbGraphicsView->convertToVia(bendpointAction->lastHoverEnterConnectorItem());
3169 }
3170 
convertToBendpoint()3171 void MainWindow::convertToBendpoint()
3172 {
3173     m_pcbGraphicsView->convertToBendpoint();
3174 }
3175 
flattenCurve()3176 void MainWindow::flattenCurve()
3177 {
3178 	BendpointAction * bendpointAction = qobject_cast<BendpointAction *>(m_addBendpointAct);
3179 
3180 	m_currentGraphicsView->flattenCurve(bendpointAction->lastHoverEnterItem(),
3181 										bendpointAction->lastHoverEnterConnectorItem(),
3182 										bendpointAction->lastLocation());
3183 }
3184 
3185 
importFilesFromPrevInstall()3186 void MainWindow::importFilesFromPrevInstall() {
3187 	QString prevInstallPath = QFileDialog::getExistingDirectory(
3188 			this,
3189 			tr("Please choose the previous Fritzing folder..."),
3190             QDir::homePath(),
3191             QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
3192 	if(prevInstallPath.isNull()) return;
3193 	if(!QFileInfo(prevInstallPath+"/parts").exists()) {
3194 		FMessageBox::critical(
3195 			this, QObject::tr("Fritzing"),
3196 			tr("The folder \"%1\" isn't a Fritzing installation folder").arg(prevInstallPath));
3197 		return;
3198 	}
3199 
3200 	QString userDataPath = FolderUtils::getUserDataStorePath();
3201 
3202 	// replicate dirs
3203 	QStringList foldersToCopy = FolderUtils::getUserDataStoreFolders();
3204 	foreach(QString folder, foldersToCopy) {
3205         FolderUtils::replicateDir(QDir(prevInstallPath+folder), QDir(userDataPath+folder));
3206 	}
3207 
3208 	// cleanup old bins
3209 	QDir dataStoreBins(userDataPath);
3210 	dataStoreBins.cd("bins");
3211 	QStringList binsToRemove;
3212 	binsToRemove
3213 		<< "allParts.fzb" << "artreenoBin.fzb"
3214 		<< "E6SetBin.fzb" << "pin_headers.fzb";
3215 	foreach(QString binToRemove, binsToRemove) {
3216 		dataStoreBins.remove(binToRemove);
3217 	}
3218 
3219 	// make sure to add the old my_parts.fzp to the folder
3220 	QString myPartsBinRelPath = "/bins/my_parts.fzb";
3221 	QFile myOldPartsBinFile(prevInstallPath+myPartsBinRelPath);
3222 	if(myOldPartsBinFile.exists()) {
3223 		QDateTime now = QDateTime::currentDateTime();
3224 		QString newNamePostfix = QString("__imported_on__%1.fzb").arg(now.toString("yyyy-MM-dd_hh-mm-ss"));
3225 		FolderUtils::slamCopy(myOldPartsBinFile, userDataPath+myPartsBinRelPath.replace(".fzb",newNamePostfix));
3226 	}
3227 
3228 	FMessageBox::information(
3229 		this, QObject::tr("Fritzing"),
3230 		tr("You will have to restart Fritzing in order to use the imported parts"));
3231 }
3232 
tidyWires()3233 void MainWindow::tidyWires() {
3234 	m_currentGraphicsView->tidyWires();
3235 }
3236 
copperFill()3237 void MainWindow::copperFill() {
3238 	groundFillAux2(false);
3239 }
3240 
groundFill()3241 void MainWindow::groundFill()
3242 {
3243     groundFillAux2(true);
3244 }
3245 
groundFillAux2(bool fillGroundTraces)3246 void MainWindow::groundFillAux2(bool fillGroundTraces) {
3247 
3248     if (m_pcbGraphicsView->layerIsActive(ViewLayer::Copper0) && m_pcbGraphicsView->layerIsActive(ViewLayer::Copper1)) {
3249 	    groundFillAux(fillGroundTraces, ViewLayer::UnknownLayer);
3250     }
3251     else if (m_pcbGraphicsView->layerIsActive(ViewLayer::Copper0)) {
3252         groundFillAux(fillGroundTraces, ViewLayer::GroundPlane0);
3253     }
3254     else {
3255         groundFillAux(fillGroundTraces, ViewLayer::GroundPlane1);
3256     }
3257 }
3258 
copperFill(ViewLayer::ViewLayerID viewLayerID)3259 void MainWindow::copperFill(ViewLayer::ViewLayerID viewLayerID) {
3260 	groundFillAux(false, viewLayerID);
3261 }
3262 
groundFill(ViewLayer::ViewLayerID viewLayerID)3263 void MainWindow::groundFill(ViewLayer::ViewLayerID viewLayerID)
3264 {
3265 	groundFillAux(true, viewLayerID);
3266 }
3267 
groundFillAux(bool fillGroundTraces,ViewLayer::ViewLayerID viewLayerID)3268 void MainWindow::groundFillAux(bool fillGroundTraces, ViewLayer::ViewLayerID viewLayerID)
3269 {
3270 	// TODO:
3271 	//		what about leftover temp files from crashes?
3272 	//		clear ground plane when anything changes
3273 	//		some polygons can be combined
3274 	//		remove old ground plane modules from paletteModel and database
3275 
3276 	if (m_pcbGraphicsView == NULL) return;
3277 
3278     int boardCount;
3279     ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount);
3280     if (boardCount == 0) {
3281         QMessageBox::critical(this, tr("Fritzing"),
3282                    tr("Your sketch does not have a board yet!  Please add a PCB in order to use ground or copper fill."));
3283         return;
3284     }
3285     if (board == NULL) {
3286         QMessageBox::critical(this, tr("Fritzing"),
3287                    tr("Please select a PCB--copper fill only works for one board at a time."));
3288         return;
3289     }
3290 
3291 
3292     FileProgressDialog fileProgress(tr("Generating %1 fill...").arg(fillGroundTraces ? tr("ground") : tr("copper")), 0, this);
3293     fileProgress.setIndeterminate();
3294 	QUndoCommand * parentCommand = new QUndoCommand(fillGroundTraces ? tr("Ground Fill") : tr("Copper Fill"));
3295     m_pcbGraphicsView->blockUI(true);
3296     removeGroundFill(viewLayerID, parentCommand);
3297 	if (m_pcbGraphicsView->groundFill(fillGroundTraces, viewLayerID, parentCommand)) {
3298 		m_undoStack->push(parentCommand);
3299 	}
3300 	else {
3301 		delete parentCommand;
3302 	}
3303     m_pcbGraphicsView->blockUI(false);
3304 }
3305 
removeGroundFill()3306 void MainWindow::removeGroundFill() {
3307     removeGroundFill(ViewLayer::UnknownLayer, NULL);
3308 }
3309 
removeGroundFill(ViewLayer::ViewLayerID viewLayerID,QUndoCommand * parentCommand)3310 void MainWindow::removeGroundFill(ViewLayer::ViewLayerID viewLayerID, QUndoCommand * parentCommand) {
3311 	QSet<ItemBase *> toDelete;
3312     int boardCount;
3313     ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount);
3314     if (boardCount == 0) {
3315         QMessageBox::critical(this, tr("Fritzing"),
3316                    tr("Your sketch does not have a board yet!  Please add a PCB in order to remove copper fill."));
3317         return;
3318     }
3319     if (board == NULL) {
3320         QMessageBox::critical(this, tr("Fritzing"),
3321                    tr("Please select a PCB--ground fill operations only work on a one board at a time."));
3322         return;
3323     }
3324 
3325 	foreach (QGraphicsItem * item, m_pcbGraphicsView->scene()->collidingItems(board)) {
3326 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
3327 		if (itemBase == NULL) continue;
3328         if (itemBase->moveLock()) continue;
3329         if (!isGroundFill(itemBase)) continue;
3330 
3331         if (viewLayerID != ViewLayer::UnknownLayer) {
3332             if (itemBase->viewLayerID() != viewLayerID) continue;
3333         }
3334 
3335 		toDelete.insert(itemBase->layerKinChief());
3336 	}
3337 
3338     if (toDelete.count() == 0) return;
3339 
3340 
3341     bool push = (parentCommand == NULL);
3342 
3343     if (push) {
3344 	    parentCommand = new QUndoCommand(tr("Remove copper fill"));
3345     }
3346 
3347 	new CleanUpWiresCommand(m_pcbGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand);
3348 	new CleanUpRatsnestsCommand(m_pcbGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand);
3349 
3350     m_pcbGraphicsView->deleteMiddle(toDelete, parentCommand);
3351 	foreach (ItemBase * itemBase, toDelete) {
3352         itemBase->saveGeometry();
3353 		m_pcbGraphicsView->makeDeleteItemCommand(itemBase, BaseCommand::CrossView, parentCommand);
3354 	}
3355 
3356 	new CleanUpRatsnestsCommand(m_pcbGraphicsView, CleanUpWiresCommand::RedoOnly, parentCommand);
3357 	new CleanUpWiresCommand(m_pcbGraphicsView, CleanUpWiresCommand::RedoOnly, parentCommand);
3358 
3359     if (push) {
3360 	    m_undoStack->push(parentCommand);
3361     }
3362     else {
3363         foreach (ItemBase * itemBase, toDelete) {
3364             // move them out of the way because they are about to be deleted anyhow
3365             itemBase->setPos(itemBase->pos() + board->sceneBoundingRect().bottomRight() + QPointF(10000, 10000));
3366         }
3367     }
3368 }
3369 
isGroundFill(ItemBase * itemBase)3370 bool MainWindow::isGroundFill(ItemBase * itemBase) {
3371 	return (itemBase->itemType() == ModelPart::CopperFill);
3372 }
3373 
3374 
breadboardItemMenu()3375 QMenu *MainWindow::breadboardItemMenu() {
3376 	QMenu *menu = new QMenu(QObject::tr("Part"), this);
3377     createRotateSubmenu(menu);
3378 	return viewItemMenuAux(menu);
3379 }
3380 
schematicItemMenu()3381 QMenu *MainWindow::schematicItemMenu() {
3382 	QMenu *menu = new QMenu(QObject::tr("Part"), this);
3383     createRotateSubmenu(menu);
3384 	menu->addAction(m_flipHorizontalAct);
3385 	menu->addAction(m_flipVerticalAct);
3386 	return viewItemMenuAux(menu);
3387 }
3388 
pcbItemMenu()3389 QMenu *MainWindow::pcbItemMenu() {
3390 	QMenu *menu = new QMenu(QObject::tr("Part"), this);
3391     createRotateSubmenu(menu);
3392 	menu = viewItemMenuAux(menu);
3393     menu->addAction(m_hidePartSilkscreenAct);
3394 	menu->addSeparator();
3395     menu->addAction(m_convertToBendpointAct);
3396 	m_convertToBendpointSeparator = menu->addSeparator();
3397 	menu->addAction(m_setOneGroundFillSeedAct);
3398 	menu->addAction(m_clearGroundFillSeedsAct);
3399 	return menu;
3400 }
3401 
breadboardWireMenu()3402 QMenu *MainWindow::breadboardWireMenu() {
3403 	QMenu *menu = new QMenu(QObject::tr("Wire"), this);
3404     createZOrderWireSubmenu(menu);
3405 	menu->addSeparator();
3406 	m_breadboardWireColorMenu = menu->addMenu(tr("&Wire Color"));
3407 	foreach(QString colorName, Wire::colorNames) {
3408 		QString colorValue = Wire::colorTrans.value(colorName);
3409 		QAction * action = new QAction(colorName, this);
3410 		m_breadboardWireColorMenu->addAction(action);
3411 		action->setData(colorValue);
3412 		action->setCheckable(true);
3413         action->setChecked(false);
3414 		connect(action, SIGNAL(triggered(bool)), this, SLOT(changeWireColor(bool)));
3415 	}
3416 	menu->addAction(m_createWireWireAct);
3417 	menu->addSeparator();
3418 	menu->addAction(m_deleteWireAct);
3419 	menu->addAction(m_deleteWireMinusAct);
3420 	menu->addSeparator();
3421 	menu->addAction(m_addBendpointAct);
3422 	menu->addAction(m_flattenCurveAct);
3423 
3424 #ifndef QT_NO_DEBUG
3425 	menu->addSeparator();
3426 	menu->addAction(m_infoViewOnHoverAction);
3427 #endif
3428 
3429     connect( menu, SIGNAL(aboutToShow()), this, SLOT(updateWireMenu()));
3430 
3431 	return menu;
3432 }
3433 
pcbWireMenu()3434 QMenu *MainWindow::pcbWireMenu() {
3435     QMenu *menu = new QMenu(QObject::tr("Wire"), this);
3436     createZOrderWireSubmenu(menu);
3437 	menu->addSeparator();
3438 	menu->addAction(m_changeTraceLayerWireAct);
3439 	menu->addAction(m_createTraceWireAct);
3440 	menu->addAction(m_excludeFromAutorouteWireAct);
3441 	menu->addSeparator();
3442 	menu->addAction(m_deleteWireAct);
3443 	menu->addAction(m_deleteWireMinusAct);
3444 	menu->addSeparator();
3445 	menu->addAction(m_addBendpointAct);
3446 	menu->addAction(m_convertToViaAct);
3447 	menu->addAction(m_flattenCurveAct);
3448 
3449 #ifndef QT_NO_DEBUG
3450 	menu->addSeparator();
3451 	menu->addAction(m_infoViewOnHoverAction);
3452 #endif
3453 
3454     connect(menu, SIGNAL(aboutToShow()), this, SLOT(updateWireMenu()));
3455 
3456 	return menu;
3457 }
3458 
schematicWireMenu()3459 QMenu *MainWindow::schematicWireMenu() {
3460     QMenu *menu = new QMenu(QObject::tr("Wire"), this);
3461     createZOrderWireSubmenu(menu);
3462 	menu->addSeparator();
3463 	m_schematicWireColorMenu = menu->addMenu(tr("&Wire Color"));
3464 	foreach(QString colorName, Wire::colorNames) {
3465 		QString colorValue = Wire::colorTrans.value(colorName);
3466         if (colorValue == "white") continue;
3467 		QAction * action = new QAction(colorName, this);
3468 		m_schematicWireColorMenu->addAction(action);
3469 		action->setData(colorValue);
3470 		action->setCheckable(true);
3471 		connect(action, SIGNAL(triggered(bool)), this, SLOT(changeWireColor(bool)));
3472 	}
3473 	menu->addAction(m_createTraceWireAct);
3474 	menu->addAction(m_excludeFromAutorouteWireAct);
3475 	menu->addSeparator();
3476 	menu->addAction(m_deleteWireAct);
3477 	menu->addAction(m_deleteWireMinusAct);
3478 	menu->addSeparator();
3479 	menu->addAction(m_addBendpointAct);
3480 #ifndef QT_NO_DEBUG
3481 	menu->addSeparator();
3482 	menu->addAction(m_infoViewOnHoverAction);
3483 #endif
3484 
3485     connect( menu, SIGNAL(aboutToShow()), this, SLOT(updateWireMenu()));
3486 
3487 	return menu;
3488 }
3489 
viewItemMenuAux(QMenu * menu)3490 QMenu *MainWindow::viewItemMenuAux(QMenu* menu) {
3491     createZOrderWireSubmenu(menu);
3492 	menu->addAction(m_moveLockAct);
3493 	menu->addAction(m_stickyAct);
3494 	menu->addSeparator();
3495 	menu->addAction(m_copyAct);
3496 	menu->addAction(m_duplicateAct);
3497 	menu->addAction(m_deleteAct);
3498 	menu->addAction(m_deleteMinusAct);
3499 #ifndef QT_NO_DEBUG
3500 	menu->addSeparator();
3501 	menu->addAction(m_disconnectAllAct);
3502 #endif
3503 	menu->addSeparator();
3504 	menu->addAction(m_openInPartsEditorNewAct);
3505     createAddToBinSubmenu(menu);
3506 	menu->addSeparator();
3507 	menu->addAction(m_showPartLabelAct);
3508 #ifndef QT_NO_DEBUG
3509 	menu->addSeparator();
3510 	menu->addAction(m_infoViewOnHoverAction);
3511 	menu->addAction(m_exportNormalizedSvgAction);
3512 	menu->addAction(m_exportNormalizedFlattenedSvgAction);
3513 	menu->addAction(m_testConnectorsAction);
3514 #endif
3515 
3516     connect(
3517     	menu,
3518     	SIGNAL(aboutToShow()),
3519     	this,
3520     	SLOT(updatePartMenu())
3521     );
3522 
3523     return menu;
3524 }
3525 
changeWireColor(bool checked)3526 void MainWindow::changeWireColor(bool checked) {
3527 	if (checked == false) {
3528 		// choosing the same color again (assuming this action can only apply to a single wire at a time)
3529 		return;
3530 	}
3531 
3532 	QAction * action = qobject_cast<QAction *>(sender());
3533 	if (action == NULL) return;
3534 
3535 	QString colorName = action->data().toString();
3536 	if (colorName.isEmpty()) return;
3537 
3538 	m_currentGraphicsView->changeWireColor(colorName);
3539 }
3540 
startSaveInstancesSlot(const QString & fileName,ModelPart *,QXmlStreamWriter & streamWriter)3541 void MainWindow::startSaveInstancesSlot(const QString & fileName, ModelPart *, QXmlStreamWriter & streamWriter) {
3542 	Q_UNUSED(fileName);
3543 
3544 	if (m_backingUp) {
3545 		streamWriter.writeTextElement("originalFileName", m_fwFilename);
3546 	}
3547 
3548     if (m_pcbGraphicsView) {
3549         QList<ItemBase *> boards = m_pcbGraphicsView->findBoard();
3550         if (boards.count()) {
3551             streamWriter.writeStartElement("boards");
3552             foreach (ItemBase * board, boards) {
3553                 QRectF r = board->sceneBoundingRect();
3554                 double w = 2.54 * r.width() / GraphicsUtils::SVGDPI;
3555                 double h = 2.54 * r.height() / GraphicsUtils::SVGDPI;
3556                 streamWriter.writeStartElement("board");
3557                 streamWriter.writeAttribute("moduleId", QString("%1").arg(board->moduleID()));
3558                 streamWriter.writeAttribute("title", QString("%1").arg(board->title()));
3559                 streamWriter.writeAttribute("instance", QString("%1").arg(board->instanceTitle()));
3560                 streamWriter.writeAttribute("width", QString("%1cm").arg(w));
3561                 streamWriter.writeAttribute("height", QString("%1cm").arg(h));
3562                 streamWriter.writeEndElement();
3563             }
3564             streamWriter.writeEndElement();
3565         }
3566     }
3567 
3568 	if (m_linkedProgramFiles.count() > 0) {
3569 		streamWriter.writeStartElement("programs");
3570 		QSettings settings;
3571 		streamWriter.writeAttribute("pid", settings.value("pid").toString());
3572 		foreach (LinkedFile * linkedFile, m_linkedProgramFiles) {
3573 			streamWriter.writeStartElement("program");
3574             streamWriter.writeAttribute("language", linkedFile->platform);
3575 			streamWriter.writeCharacters(linkedFile->linkedFilename);
3576 			streamWriter.writeEndElement();
3577 		}
3578 		streamWriter.writeEndElement();
3579 	}
3580 
3581 	streamWriter.writeStartElement("views");
3582 	QList<SketchWidget *> views;
3583 	views << m_breadboardGraphicsView << m_schematicGraphicsView << m_pcbGraphicsView;
3584 	foreach  (SketchWidget * sketchWidget, views) {
3585 		streamWriter.writeStartElement("view");
3586 		streamWriter.writeAttribute("name", ViewLayer::viewIDXmlName(sketchWidget->viewID()));
3587 		streamWriter.writeAttribute("backgroundColor", sketchWidget->background().name());
3588 		streamWriter.writeAttribute("gridSize", sketchWidget->gridSizeText());
3589 		streamWriter.writeAttribute("showGrid", sketchWidget->showingGrid() ? "1" : "0");
3590 		streamWriter.writeAttribute("alignToGrid", sketchWidget->alignedToGrid() ? "1" : "0");
3591 		streamWriter.writeAttribute("viewFromBelow", sketchWidget->viewFromBelow() ? "1" : "0");
3592         QHash<QString, QString> autorouterSettings = sketchWidget->getAutorouterSettings();
3593         foreach (QString key, autorouterSettings.keys()) {
3594 		    streamWriter.writeAttribute(key, autorouterSettings.value(key));
3595         }
3596 		streamWriter.writeEndElement();
3597 	}
3598 	streamWriter.writeEndElement();
3599 }
3600 
obsoleteSMDOrientationSlot()3601 void MainWindow::obsoleteSMDOrientationSlot() {
3602     m_obsoleteSMDOrientation = true;
3603 }
3604 
oldSchematicsSlot(const QString & filename,bool & useOldSchematics)3605 void MainWindow::oldSchematicsSlot(const QString &filename, bool & useOldSchematics) {
3606     useOldSchematics = m_convertedSchematic = m_useOldSchematic = false;
3607     if (m_noSchematicConversion) return;
3608 
3609     if (m_readOnly) {
3610         useOldSchematics = m_useOldSchematic = true;
3611         return;
3612     }
3613 
3614     QMessageBox::StandardButton answer = oldSchematicMessage(filename);
3615 	if (answer == QMessageBox::No) {
3616         useOldSchematics = m_useOldSchematic = true;
3617         this->setReadOnly(true);
3618 	}
3619     else {
3620         m_convertedSchematic = true;
3621     }
3622 }
3623 
oldSchematicMessage(const QString & filename)3624 QMessageBox::StandardButton MainWindow::oldSchematicMessage(const QString & filename)
3625 {
3626     QFileInfo info(filename);
3627     FMessageBox messageBox(NULL);
3628 	messageBox.setWindowTitle(tr("Schematic view update"));
3629 	messageBox.setText(tr("There is a new graphics standard for schematic-view part images, beginning with version 0.8.6.\n\n") +
3630                         tr("Would you like to convert '%1' to the new standard now or open the file read-only?\n").arg(info.fileName())
3631                         );
3632 	messageBox.setInformativeText("<ul><li>" +
3633                                     tr("The conversion process will not modify '%1', until you save the file. ").arg(info.fileName()) +
3634                                     + "</li><li>" +
3635                                     tr("You will have to rearrange parts and connections in schematic view, as the sizes of most part images will have changed. Consider using the Autorouter to clean up traces. ") +
3636                                     + "</li><li>" +
3637                                     tr("Note that any custom parts will not be converted. A tool for converting 'rectangular' schematic images is available in the Parts Editor.") +
3638                                     + "</li></ul>"
3639                                  );
3640 	messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
3641 	messageBox.setDefaultButton(QMessageBox::Yes);
3642 	messageBox.setIcon(QMessageBox::Question);
3643 	messageBox.setWindowModality(Qt::WindowModal);
3644 	messageBox.setButtonText(QMessageBox::Yes, tr("Convert"));
3645 	messageBox.setButtonText(QMessageBox::No, tr("Read-only"));
3646 	return (QMessageBox::StandardButton) messageBox.exec();
3647 }
3648 
3649 
loadedRootSlot(const QString & fname,ModelBase *,QDomElement & root)3650 void MainWindow::loadedRootSlot(const QString & fname, ModelBase *, QDomElement & root) {
3651 	if (root.isNull()) return;
3652 
3653 	QDomElement programs = root.firstChildElement("programs");
3654 	if (programs.isNull()) return;
3655 
3656 	QString thatPid = programs.attribute("pid");
3657 	QSettings settings;
3658 	QString thisPid = settings.value("pid").toString();
3659 	bool sameMachine = thatPid.isEmpty() || (!thisPid.isEmpty() && (thatPid.compare(thisPid) == 0));
3660 	QFileInfo fileInfo(fname);
3661 	QDir dir = fileInfo.absoluteDir();
3662 
3663 	QDomElement program = programs.firstChildElement("program");
3664 	while (!program.isNull()) {
3665 		bool obsolete = false;
3666 		bool inBundle = false;
3667 		QString text;
3668 		TextUtils::findText(program, text);
3669 		if (!text.isEmpty()) {
3670             QString platform = program.attribute("language");
3671 			QString path;
3672 			if (thatPid.isEmpty()) {
3673 				// pre 0.7.0 relative path
3674 				QFileInfo newFileInfo(text);
3675 				dir.cd(newFileInfo.dir().path());
3676 				path = dir.absoluteFilePath(newFileInfo.fileName());
3677 				obsolete = true;
3678 			}
3679 			else {
3680 				path = text;
3681 			}
3682 
3683 			LinkedFile * linkedFile = new LinkedFile;
3684 			QFileInfo info(path);
3685 			if (!(sameMachine && info.exists())) {
3686 				inBundle = true;
3687 				path = dir.absoluteFilePath(info.fileName());
3688 			}
3689 			linkedFile->linkedFilename = path;
3690             linkedFile->platform = platform;
3691 			linkedFile->fileFlags = LinkedFile::NoFlag;
3692 			if (sameMachine) linkedFile->fileFlags |= LinkedFile::SameMachineFlag;
3693 			if (obsolete) linkedFile->fileFlags |= LinkedFile::ObsoleteFlag;
3694 			if (inBundle) linkedFile->fileFlags |= LinkedFile::InBundleFlag;
3695 			if (this->m_readOnly) linkedFile->fileFlags |= LinkedFile::ReadOnlyFlag;
3696 
3697 			m_linkedProgramFiles.append(linkedFile);
3698 		}
3699 		program = program.nextSiblingElement("program");
3700 	}
3701 
3702 }
3703 
loadedViewsSlot(ModelBase *,QDomElement & views)3704 void MainWindow::loadedViewsSlot(ModelBase *, QDomElement & views) {
3705 	if (views.isNull()) return;
3706 
3707 	QDomElement view = views.firstChildElement("view");
3708 	while (!view.isNull()) {
3709 		QString name = view.attribute("name");
3710 		ViewLayer::ViewID viewID = ViewLayer::idFromXmlName(name);
3711         SketchWidget * sketchWidget = NULL;
3712 		switch (viewID) {
3713 			case ViewLayer::BreadboardView:
3714 				sketchWidget = m_breadboardGraphicsView;
3715 				break;
3716 			case ViewLayer::SchematicView:
3717 				sketchWidget = m_schematicGraphicsView;
3718 				break;
3719 			case ViewLayer::PCBView:
3720 				sketchWidget = m_pcbGraphicsView;
3721 				break;
3722 			default:
3723 				break;
3724 		}
3725 
3726         if (sketchWidget) {
3727 		    QString colorName = view.attribute("backgroundColor", "");
3728             QString gridSizeText = view.attribute("gridSize", "");
3729             QString alignToGridText = view.attribute("alignToGrid", "");
3730             QString showGridText = view.attribute("showGrid", "");
3731             QString viewFromBelowText = view.attribute("viewFromBelow", "");
3732 
3733             QHash<QString, QString> autorouterSettings;
3734             QDomNamedNodeMap map = view.attributes();
3735             for (int m = 0; m < map.count(); m++) {
3736 			    QDomNode node = map.item(m);
3737 			    autorouterSettings.insert(node.nodeName(), node.nodeValue());
3738             }
3739             sketchWidget->setAutorouterSettings(autorouterSettings);
3740 
3741 		    QColor color;
3742 		    color.setNamedColor(colorName);
3743 
3744             bool redraw = false;
3745             if (color.isValid()) {
3746                 sketchWidget->setBackground(color);
3747                 redraw = true;
3748             }
3749             if (!alignToGridText.isEmpty()) {
3750                 sketchWidget->alignToGrid(alignToGridText.compare("1") == 0);
3751             }
3752             if (!showGridText.isEmpty()) {
3753                 sketchWidget->showGrid(showGridText.compare("1") == 0);
3754                 redraw = 1;
3755             }
3756             if (!gridSizeText.isEmpty()) {
3757                 sketchWidget->setGridSize(gridSizeText);
3758                 redraw = true;
3759             }
3760 
3761             if (!viewFromBelowText.isEmpty()) {
3762                 sketchWidget->setViewFromBelow(viewFromBelowText.compare("1") == 0);
3763                 redraw = 1;
3764             }
3765 
3766             if (redraw) sketchWidget->invalidateScene();
3767         }
3768 
3769 		view = view.nextSiblingElement("view");
3770 	}
3771 }
3772 
disconnectAll()3773 void MainWindow::disconnectAll() {
3774 	m_currentGraphicsView->disconnectAll();
3775 }
3776 
externalProcess(QString & name,QString & path,QStringList & args)3777 bool MainWindow::externalProcess(QString & name, QString & path, QStringList & args) {
3778 	emit externalProcessSignal(name, path, args);
3779 
3780 	if (path.isEmpty()) return false;
3781 
3782 	if (name.isEmpty()) {
3783 		name = tr("Launch %1...").arg(path);
3784 	}
3785 
3786 	return true;
3787 }
3788 
launchExternalProcess()3789 void MainWindow::launchExternalProcess() {
3790 	QString name;
3791 	QString path;
3792 	QStringList args;
3793 	if (!externalProcess(name, path, args)) return;
3794 
3795 	args.append("-sketch");
3796 	args.append(fileName());
3797 	m_externalProcessOutput.clear();
3798 
3799 	QFileInfo f = QFileInfo(path);
3800 	QProcess * process = new QProcess(this);
3801 	process->setWorkingDirectory(f.dir().absolutePath());
3802 	process->setProcessChannelMode(QProcess::MergedChannels);
3803 	process->setReadChannel(QProcess::StandardOutput);
3804 
3805 	connect(process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
3806 	connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
3807 	connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(processReadyRead()));
3808 	connect(process, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged(QProcess::ProcessState)));
3809 
3810 	process->start(path, args);
3811 }
3812 
3813 
processError(QProcess::ProcessError processError)3814 void MainWindow::processError(QProcess::ProcessError processError) {
3815 	DebugDialog::debug(QString("process error %1").arg(processError));
3816 }
3817 
processFinished(int exitCode,QProcess::ExitStatus exitStatus)3818 void MainWindow::processFinished(int exitCode, QProcess::ExitStatus exitStatus) {
3819 	DebugDialog::debug(QString("process finished %1 %2").arg(exitCode).arg(exitStatus));
3820 
3821 	QString name, path;
3822 	QStringList args;
3823 	externalProcess(name, path, args);
3824 	QMessageBox::information(this, name, QString(m_externalProcessOutput));
3825 
3826 	sender()->deleteLater();
3827 }
3828 
processReadyRead()3829 void MainWindow::processReadyRead() {
3830 	QByteArray byteArray = qobject_cast<QProcess *>(sender())->readAllStandardOutput();
3831 	m_externalProcessOutput.append(byteArray);
3832 
3833 	DebugDialog::debug(byteArray.data());
3834 }
3835 
processStateChanged(QProcess::ProcessState newState)3836 void MainWindow::processStateChanged(QProcess::ProcessState newState) {
3837 	switch(newState) {
3838 		case QProcess::Running:
3839 			DebugDialog::debug(QString("process running"));
3840 			break;
3841 		case QProcess::Starting:
3842 			DebugDialog::debug(QString("process starting"));
3843 			break;
3844 		case QProcess::NotRunning:
3845 			DebugDialog::debug(QString("process not running"));
3846 			break;
3847 	}
3848 }
3849 
shareOnline()3850 void MainWindow::shareOnline() {
3851 	QDesktopServices::openUrl(QString("http://fritzing.org/projects/create/"));
3852 }
3853 
3854 
selectAllObsolete()3855 void MainWindow::selectAllObsolete() {
3856 	selectAllObsolete(true);
3857 }
3858 
selectAllObsolete(bool displayFeedback)3859 QList<ItemBase *> MainWindow::selectAllObsolete(bool displayFeedback) {
3860 	QList<ItemBase *> items = m_pcbGraphicsView->selectAllObsolete();
3861 	if (!displayFeedback) return items;
3862 
3863 	if (items.count() <= 0) {
3864         QMessageBox::information(this, tr("Fritzing"), tr("No outdated parts found.\nAll your parts are up-to-date.") );
3865     }
3866 	else {
3867         checkSwapObsolete(items, false);
3868 	}
3869 
3870     return items;
3871 }
3872 
checkSwapObsolete(QList<ItemBase * > & items,bool includeUpdateLaterMessage)3873 void MainWindow::checkSwapObsolete(QList<ItemBase *> & items, bool includeUpdateLaterMessage) {
3874     QString msg = includeUpdateLaterMessage ? tr("\n\nNote: if you want to update later, there are options under the 'Part' menu for dealing with outdated parts individually. ") : "";
3875 
3876     QMessageBox::StandardButton answer = FMessageBox::question(
3877             this,
3878             tr("Outdated parts"),
3879             tr("There are %n outdated part(s) in this sketch. ", "", items.count()) +
3880             tr("We strongly recommend that you update these %n parts  to the latest version. ", "", items.count()) +
3881             tr("This may result in changes to your sketch, as parts or connectors may be shifted. ") +
3882             msg +
3883             tr("\n\nDo you want to update now?"),
3884             QMessageBox::Yes | QMessageBox::No,
3885             QMessageBox::Yes
3886     );
3887     // TODO: make button texts translatable
3888     if (answer == QMessageBox::Yes) {
3889         swapObsolete(true, items);
3890     }
3891 }
3892 
3893 
3894 
findReplacedby(ModelPart * originalModelPart)3895 ModelPart * MainWindow::findReplacedby(ModelPart * originalModelPart) {
3896 	ModelPart * newModelPart = originalModelPart;
3897 	while (true) {
3898 		QString newModuleID = newModelPart->replacedby();
3899 		if (newModuleID.isEmpty()) {
3900 			return ((newModelPart == originalModelPart) ? NULL : newModelPart);
3901 		}
3902 
3903 		ModelPart * tempModelPart = this->m_referenceModel->retrieveModelPart(newModuleID);
3904 		if (tempModelPart == NULL) {
3905 			// something's screwy
3906 			return NULL;
3907 		}
3908 
3909 		newModelPart = tempModelPart;
3910 	}
3911 }
3912 
swapObsolete()3913 void MainWindow::swapObsolete() {
3914     QList<ItemBase *> items;
3915 	swapObsolete(true, items);
3916 }
3917 
swapObsolete(bool displayFeedback,QList<ItemBase * > & items)3918 void MainWindow::swapObsolete(bool displayFeedback, QList<ItemBase *> & items) {
3919 	QSet<ItemBase *> itemBases;
3920 
3921     if (items.count() == 0) {
3922 	    foreach (QGraphicsItem * item, m_pcbGraphicsView->scene()->selectedItems()) {
3923 		    ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
3924 		    if (itemBase == NULL) continue;
3925 		    if (!itemBase->isObsolete()) continue;
3926 
3927 		    itemBase = itemBase->layerKinChief();
3928 		    itemBases.insert(itemBase);
3929 	    }
3930 
3931 	    if (itemBases.count() <= 0) return;
3932     }
3933     else {
3934         foreach (ItemBase * itemBase, items) itemBases.insert(itemBase);
3935     }
3936 
3937 	QUndoCommand* parentCommand = new QUndoCommand();
3938     int count = 0;
3939     QMap<QString, QString> propsMap;
3940 	foreach (ItemBase * itemBase, itemBases) {
3941 		ModelPart * newModelPart = findReplacedby(itemBase->modelPart());
3942 		if (newModelPart == NULL) {
3943 			FMessageBox::information(
3944 				this,
3945 				tr("Sorry!"),
3946 				tr( "unable to find replacement for %1.\n").arg(itemBase->title())
3947 			);
3948 			continue;
3949 		}
3950 
3951 		count++;
3952 		long newID = swapSelectedAuxAux(itemBase, newModelPart->moduleID(), itemBase->viewLayerPlacement(), propsMap, parentCommand);
3953 		if (itemBase->modelPart()) {
3954 			// special case for swapping old resistors.
3955 			QString resistance = itemBase->modelPart()->properties().value("resistance", "");
3956 			if (!resistance.isEmpty()) {
3957 				QChar r = resistance.at(resistance.length() - 1);
3958 				ushort ohm = r.unicode();
3959 				if (ohm == 8486) {
3960 					// ends with the ohm symbol
3961 					resistance.chop(1);
3962 				}
3963 			}
3964 			QString footprint = itemBase->modelPart()->properties().value("footprint", "");
3965 			if (!resistance.isEmpty() && !footprint.isEmpty()) {
3966 				new SetResistanceCommand(m_currentGraphicsView, newID, resistance, resistance, footprint, footprint, parentCommand);
3967 			}
3968 
3969 			// special case for swapping LEDs
3970 			if (newModelPart->moduleID().contains(ModuleIDNames::ColorLEDModuleIDName)) {
3971 				QString oldColor = itemBase->modelPart()->properties().value("color");
3972 				QString newColor;
3973 				if (oldColor.contains("red", Qt::CaseInsensitive)) {
3974 					newColor = "Red (633nm)";
3975 				}
3976 				else if (oldColor.contains("blue", Qt::CaseInsensitive)) {
3977 					newColor = "Blue (430nm)";
3978 				}
3979 				else if (oldColor.contains("yellow", Qt::CaseInsensitive)) {
3980 					newColor = "Yellow (585nm)";
3981 				}
3982 				else if (oldColor.contains("green", Qt::CaseInsensitive)) {
3983 					newColor = "Green (555nm)";
3984 				}
3985 				else if (oldColor.contains("white", Qt::CaseInsensitive)) {
3986 					newColor = "White (4500K)";
3987 				}
3988 
3989 				if (newColor.length() > 0) {
3990 					new SetPropCommand(m_currentGraphicsView, newID, "color", newColor, newColor, true, parentCommand);
3991 				}
3992 			}
3993 
3994 		}
3995 	}
3996 
3997 
3998 	if (count == 0) {
3999 		delete parentCommand;
4000 	}
4001 	else {
4002         parentCommand->setText(tr("Update %1 part(s)", "").arg(count));
4003 		m_undoStack->push(parentCommand);
4004 	}
4005 
4006 	if (displayFeedback) {
4007 		QMessageBox::information(this, tr("Fritzing"), tr("Successfully updated %1 part(s).\n"
4008                                                           "Please check all views for potential side-effects.").arg(count) );
4009 	}
4010 	DebugDialog::debug(QString("updated %1 obsolete in %2").arg(count).arg(m_fwFilename));
4011 }
4012 
throwFakeException()4013 void MainWindow::throwFakeException() {
4014     throw "fake exception";
4015 }
4016 
alignToGrid()4017 void MainWindow::alignToGrid() {
4018 	if (m_currentGraphicsView == NULL) return;
4019 
4020 	m_currentGraphicsView->alignToGrid(m_alignToGridAct->isChecked());
4021     setWindowModified(true);
4022 }
4023 
showGrid()4024 void MainWindow::showGrid() {
4025 	if (m_currentGraphicsView == NULL) return;
4026 
4027 	m_currentGraphicsView->showGrid(m_showGridAct->isChecked());
4028     setWindowModified(true);
4029 }
4030 
setGridSize()4031 void MainWindow::setGridSize()
4032 {
4033     GridSizeThing gridSizeThing(m_currentGraphicsView->viewName(),
4034                                 m_currentGraphicsView->getShortName(),
4035                                 m_currentGraphicsView->defaultGridSizeInches(),
4036                                 m_currentGraphicsView->gridSizeText());
4037 
4038     GridSizeDialog dialog(&gridSizeThing);
4039     dialog.setWindowTitle(QObject::tr("Set Grid Size"));
4040 
4041 	QVBoxLayout * vLayout = new QVBoxLayout(&dialog);
4042 
4043     vLayout->addWidget(createGridSizeForm(&gridSizeThing));
4044 
4045     QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
4046 	buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
4047 	buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
4048 
4049     connect(buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
4050     connect(buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
4051 
4052 	vLayout->addWidget(buttonBox);
4053 
4054     int result = dialog.exec();
4055 
4056     if (result == QDialog::Accepted) {
4057 	    QString units = (gridSizeThing.inButton->isChecked() ? "in" : "mm");
4058         m_currentGraphicsView->setGridSize(gridSizeThing.lineEdit->text() + units);
4059         setWindowModified(true);
4060     }
4061 }
4062 
createGridSizeForm(GridSizeThing * gridSizeThing)4063 QWidget * MainWindow::createGridSizeForm(GridSizeThing * gridSizeThing)
4064 {
4065     this->setObjectName("gridSizeDia");
4066 	QGroupBox * over = new QGroupBox("", this);
4067 
4068 	QVBoxLayout * vLayout = new QVBoxLayout();
4069 
4070 	QLabel * explain = new QLabel(tr("Set the grid size for %1.").arg(gridSizeThing->viewName));
4071 	vLayout->addWidget(explain);
4072 
4073 	QGroupBox * groupBox = new QGroupBox(this);
4074 
4075 	QHBoxLayout * hLayout = new QHBoxLayout();
4076 
4077 	QLabel * label = new QLabel(tr("Grid Size:"));
4078 	hLayout->addWidget(label);
4079 
4080 	gridSizeThing->lineEdit = new QLineEdit();
4081 
4082     gridSizeThing->lineEdit->setFixedWidth(55);
4083 
4084 	gridSizeThing->validator = new QDoubleValidator(gridSizeThing->lineEdit);
4085     gridSizeThing->validator->setRange(0.001, 1.0, 4);
4086 	gridSizeThing->validator->setNotation(QDoubleValidator::StandardNotation);
4087     gridSizeThing->validator->setLocale(QLocale::C);
4088 	gridSizeThing->lineEdit->setValidator(gridSizeThing->validator);
4089 
4090 	hLayout->addWidget(gridSizeThing->lineEdit);
4091 
4092 	gridSizeThing->inButton = new QRadioButton(tr("in"), this);
4093 	hLayout->addWidget(gridSizeThing->inButton);
4094 
4095 	gridSizeThing->mmButton = new QRadioButton(tr("mm"), this);
4096 	hLayout->addWidget(gridSizeThing->mmButton);
4097 
4098 	groupBox->setLayout(hLayout);
4099 
4100 	vLayout->addWidget(groupBox);
4101 	vLayout->addSpacing(5);
4102 
4103 	QPushButton * pushButton = new QPushButton(this);
4104 	pushButton->setText(tr("Restore Default"));
4105     pushButton->setMaximumWidth(150);
4106 	vLayout->addWidget(pushButton);
4107 	vLayout->addSpacing(10);
4108 
4109 	over->setLayout(vLayout);
4110 
4111 	if (gridSizeThing->gridSizeText.length() <= 2) {
4112 		gridSizeThing->inButton->setChecked(true);
4113 		gridSizeThing->lineEdit->setText(QString::number(gridSizeThing->defaultGridSize));
4114 	}
4115 	else {
4116 		if (gridSizeThing->gridSizeText.endsWith("mm")) {
4117 			gridSizeThing->mmButton->setChecked(true);
4118 			gridSizeThing->validator->setTop(25.4);
4119 		}
4120 		else {
4121 			gridSizeThing->inButton->setChecked(true);
4122 		}
4123         QString szString = gridSizeThing->gridSizeText;
4124 		szString.chop(2);
4125 		gridSizeThing->lineEdit->setText(szString);
4126 	}
4127 
4128 	connect(gridSizeThing->inButton, SIGNAL(clicked(bool)), this, SLOT(gridUnits(bool)));
4129 	connect(pushButton, SIGNAL(clicked()), this, SLOT(restoreDefaultGrid()));
4130 	connect(gridSizeThing->mmButton, SIGNAL(clicked(bool)), this, SLOT(gridUnits(bool)));
4131 
4132 	return over;
4133 }
4134 
4135 
openProgramWindow()4136 void MainWindow::openProgramWindow() {
4137 	if (m_programWindow) {
4138 		m_programWindow->setVisible(true);
4139 		m_programWindow->raise();
4140 		return;
4141 	}
4142 
4143 	m_programWindow = new ProgramWindow();
4144     connect(m_programWindow, SIGNAL(linkToProgramFile(const QString &, Platform *, bool, bool)),
4145             this, SLOT(linkToProgramFile(const QString &, Platform *, bool, bool)));
4146 	connect(m_programWindow, SIGNAL(changeActivationSignal(bool, QWidget *)), qApp, SLOT(changeActivation(bool, QWidget *)), Qt::DirectConnection);
4147 	connect(m_programWindow, SIGNAL(destroyed(QObject *)), qApp, SLOT(topLevelWidgetDestroyed(QObject *)));
4148 
4149 	QFileInfo fileInfo(m_fwFilename);
4150 	m_programWindow->setup();
4151 	m_programWindow->linkFiles(m_linkedProgramFiles, fileInfo.absoluteDir().absolutePath());
4152 	m_programWindow->setVisible(true);
4153 }
4154 
linkToProgramFile(const QString & filename,Platform * platform,bool addLink,bool strong)4155 void MainWindow::linkToProgramFile(const QString & filename, Platform * platform, bool addLink, bool strong) {
4156 #ifdef Q_OS_WIN
4157 	Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive;
4158 #else
4159 	Qt::CaseSensitivity sensitivity = Qt::CaseSensitive;
4160 #endif
4161 
4162 	if (addLink && strong) {
4163 		bool gotOne = false;
4164 		foreach (LinkedFile * linkedFile, m_linkedProgramFiles) {
4165 			if (linkedFile->linkedFilename.compare(filename, sensitivity) == 0) {
4166                 if (linkedFile->platform != platform->getName()) {
4167                     linkedFile->platform = platform->getName();
4168 					this->setWindowModified(true);
4169 				}
4170 				gotOne = true;
4171 				break;
4172 			}
4173 		}
4174 		if (!gotOne) {
4175 			LinkedFile * linkedFile = new LinkedFile;
4176 			linkedFile->linkedFilename = filename;
4177             linkedFile->platform = platform->getName();
4178 			m_linkedProgramFiles.append(linkedFile);
4179 			this->setWindowModified(true);
4180 		}
4181 		return;
4182 	}
4183 	else {
4184 		for (int i = 0; i < m_linkedProgramFiles.count(); i++) {
4185 			LinkedFile * linkedFile = m_linkedProgramFiles.at(i);
4186 			if (linkedFile->linkedFilename.compare(filename, sensitivity) == 0) {
4187 				if (strong) {
4188 					m_linkedProgramFiles.removeAt(i);
4189 					this->setWindowModified(true);
4190 				}
4191 				else {
4192                     if (linkedFile->platform != platform->getName()) {
4193                         linkedFile->platform = platform->getName();
4194 						this->setWindowModified(true);
4195                     }
4196 				}
4197 				return;
4198 			}
4199 		}
4200 	}
4201 }
4202 
newDesignRulesCheck()4203 QStringList MainWindow::newDesignRulesCheck()
4204 {
4205     return newDesignRulesCheck(true);
4206 }
4207 
newDesignRulesCheck(bool showOkMessage)4208 QStringList MainWindow::newDesignRulesCheck(bool showOkMessage)
4209 {
4210     QStringList results;
4211 
4212 	if (m_currentGraphicsView == NULL) return results;
4213 
4214 	PCBSketchWidget * pcbSketchWidget = qobject_cast<PCBSketchWidget *>(m_currentGraphicsView);
4215 	if (pcbSketchWidget == NULL) return results;
4216 
4217     ItemBase * board = NULL;
4218     if (pcbSketchWidget->autorouteTypePCB()) {
4219         int boardCount;
4220 		board = pcbSketchWidget->findSelectedBoard(boardCount);
4221         if (boardCount == 0) {
4222             QString message = tr("Your sketch does not have a board yet! DRC only works with a PCB.");
4223             results << message;
4224             FMessageBox::critical(this, tr("Fritzing"), message);
4225             return results;
4226         }
4227         if (board == NULL) {
4228             QString message = tr("Please select a PCB. DRC only works on one board at a time.");
4229             results << message;
4230             FMessageBox::critical(this, tr("Fritzing"), message);
4231             return results;
4232         }
4233 	}
4234 
4235 	bool copper0Active = pcbSketchWidget->layerIsActive(ViewLayer::Copper0);
4236 	bool copper1Active = pcbSketchWidget->layerIsActive(ViewLayer::Copper1);
4237 
4238 	AutorouteProgressDialog progress(tr("DRC Progress..."), true, false, false, false, pcbSketchWidget, this);
4239 	progress.setModal(true);
4240 	progress.show();
4241 	QRect pr = progress.frameGeometry();
4242 	QRect wr = pcbSketchWidget->frameGeometry();
4243     QPoint p = pcbSketchWidget->mapTo(this, wr.topRight());
4244 	progress.move(p.x() - pr.width(), pr.top());
4245 
4246 	DRC drc(pcbSketchWidget, board);
4247 
4248 	connect(&drc, SIGNAL(wantTopVisible()), this, SLOT(activeLayerTop()), Qt::DirectConnection);
4249 	connect(&drc, SIGNAL(wantBottomVisible()), this, SLOT(activeLayerBottom()), Qt::DirectConnection);
4250 	connect(&drc, SIGNAL(wantBothVisible()), this, SLOT(activeLayerBoth()), Qt::DirectConnection);
4251 
4252 	connect(&progress, SIGNAL(cancel()), &drc, SLOT(cancel()), Qt::DirectConnection);
4253 
4254 	connect(&drc, SIGNAL(setMaximumProgress(int)), &progress, SLOT(setMaximum(int)), Qt::DirectConnection);
4255 	connect(&drc, SIGNAL(setProgressValue(int)), &progress, SLOT(setValue(int)), Qt::DirectConnection);
4256 	connect(&drc, SIGNAL(setProgressMessage(const QString &)), &progress, SLOT(setMessage(const QString &)));
4257 	connect(&drc, SIGNAL(hideProgress()), &progress, SLOT(close()));
4258 
4259 	ProcessEventBlocker::processEvents();
4260 	ProcessEventBlocker::block();
4261 	results = drc.start(showOkMessage, pcbSketchWidget->getKeepout() * 1000 / GraphicsUtils::SVGDPI);     // pixels to mils
4262 	ProcessEventBlocker::unblock();
4263 
4264 	pcbSketchWidget->setLayerActive(ViewLayer::Copper1, copper1Active);
4265 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, copper1Active);
4266 	pcbSketchWidget->setLayerActive(ViewLayer::Copper0, copper0Active);
4267 	pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, copper0Active);
4268 	updateActiveLayerButtons();
4269     return results;
4270 }
4271 
changeTraceLayer()4272 void MainWindow::changeTraceLayer() {
4273 	if (m_currentGraphicsView == NULL) return;
4274 	if (m_currentGraphicsView != m_pcbGraphicsView) return;
4275 
4276 	Wire * wire = retrieveWire();
4277 	m_pcbGraphicsView->changeTraceLayer(wire, false, NULL);
4278 }
4279 
retrieveWire()4280 Wire * MainWindow::retrieveWire() {
4281 	WireAction * wireAction = qobject_cast<WireAction *>(sender());
4282 	if (wireAction == NULL) return NULL;
4283 
4284 	return wireAction->wire();
4285 }
4286 
retrieveConnectorItem()4287 ConnectorItem * MainWindow::retrieveConnectorItem() {
4288 	ConnectorItemAction * connectorItemAction = qobject_cast<ConnectorItemAction *>(sender());
4289 	if (connectorItemAction == NULL) return NULL;
4290 
4291 	return connectorItemAction->connectorItem();
4292 }
4293 
setSticky()4294 void MainWindow::setSticky()
4295 {
4296     QList<QGraphicsItem *> items = m_currentGraphicsView->scene()->selectedItems();
4297     if (items.count() < 1) return;
4298 
4299     ItemBase * itemBase = dynamic_cast<ItemBase *>(items.at(0));
4300     if (itemBase == NULL) return;
4301 
4302     if (!itemBase->isBaseSticky()) return;
4303 
4304     itemBase->setLocalSticky(!itemBase->isLocalSticky());
4305 }
4306 
moveLock()4307 void MainWindow::moveLock()
4308 {
4309 	bool moveLock = true;
4310 
4311 	foreach (QGraphicsItem  * item, m_currentGraphicsView->scene()->selectedItems()) {
4312 		ItemBase * itemBase = ItemBase::extractTopLevelItemBase(item);
4313 		if (itemBase == NULL) continue;
4314 		if (itemBase->itemType() == ModelPart::Wire) continue;
4315 
4316 		if (itemBase->moveLock()) {
4317 			moveLock = false;
4318 			break;
4319 		}
4320 	}
4321 
4322 	ItemBase * viewedItem = m_infoView->currentItem();
4323 	foreach (QGraphicsItem  * item, m_currentGraphicsView->scene()->selectedItems()) {
4324 		ItemBase * itemBase = ItemBase::extractTopLevelItemBase(item);
4325 		if (itemBase == NULL) continue;
4326 		if (itemBase->itemType() == ModelPart::Wire) continue;
4327 
4328 		itemBase->setMoveLock(moveLock);
4329 		if (viewedItem && viewedItem->layerKinChief() == itemBase->layerKinChief()) {
4330 			m_currentGraphicsView->viewItemInfo(itemBase);
4331 		}
4332 	}
4333 }
4334 
selectMoveLock()4335 void MainWindow::selectMoveLock()
4336 {
4337 	m_currentGraphicsView->selectAllMoveLock();
4338 }
4339 
autorouterSettings()4340 void MainWindow::autorouterSettings() {
4341 	if (m_currentGraphicsView != m_pcbGraphicsView) return;
4342 
4343 	m_pcbGraphicsView->autorouterSettings();
4344 }
4345 
orderFab()4346 void MainWindow::orderFab()
4347 {
4348 	QDesktopServices::openUrl(QString("http://fab.fritzing.org/"));
4349 }
4350 
setGroundFillSeeds()4351 void MainWindow::setGroundFillSeeds() {
4352     int boardCount;
4353     ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount);
4354     if (boardCount == 0) {
4355         QMessageBox::critical(this, tr("Fritzing"),
4356                    tr("Your sketch does not have a board yet! Please add a PCB in order to use copper fill operations."));
4357         return;
4358     }
4359     if (board == NULL) {
4360         QMessageBox::critical(this, tr("Fritzing"),
4361                    tr("Please select a PCB. Copper fill operations only work on one board at a time."));
4362         return;
4363     }
4364 
4365 	m_pcbGraphicsView->setGroundFillSeeds();
4366 }
4367 
clearGroundFillSeeds()4368 void MainWindow::clearGroundFillSeeds() {
4369     int boardCount;
4370     ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount);
4371     if (boardCount == 0) {
4372         QMessageBox::critical(this, tr("Fritzing"),
4373                    tr("Your sketch does not have a board yet! Please add a PCB in order to use copper fill operations."));
4374         return;
4375     }
4376     if (board == NULL) {
4377         QMessageBox::critical(this, tr("Fritzing"),
4378                    tr("Please select a PCB. Copper fill operations only work on one board at a time."));
4379         return;
4380     }
4381 
4382     m_pcbGraphicsView->clearGroundFillSeeds();
4383 }
4384 
setOneGroundFillSeed()4385 void MainWindow::setOneGroundFillSeed() {
4386 	ConnectorItemAction * action = qobject_cast<ConnectorItemAction *>(sender());
4387 	if (action == NULL) return;
4388 
4389 	ConnectorItem * connectorItem = action->connectorItem();
4390 	if (connectorItem == NULL) return;
4391 
4392 	GroundFillSeedCommand * command = new GroundFillSeedCommand(m_pcbGraphicsView, NULL);
4393 	command->addItem(connectorItem->attachedToID(), connectorItem->connectorSharedID(), action->isChecked());
4394 
4395 	m_undoStack->push(command);
4396 }
4397 
gridUnits(bool checked)4398 void MainWindow::gridUnits(bool checked) {
4399     QWidget * widget = qobject_cast<QWidget *>(sender());
4400     if (widget == NULL) return;
4401 
4402     GridSizeDialog * dialog = qobject_cast<GridSizeDialog *>(widget->window());
4403     if (dialog == NULL) return;
4404 
4405 	GridSizeThing * gridSizeThing = dialog->gridSizeThing();
4406 
4407 	QString units;
4408 	if (sender() == gridSizeThing->inButton) {
4409 		units = (checked) ? "in" : "mm";
4410 	}
4411 	else {
4412 		units = (checked) ? "mm" : "in";
4413 	}
4414 
4415 	if (units.startsWith("mm")) {
4416 		gridSizeThing->validator->setTop(25.4);
4417 		gridSizeThing->lineEdit->setText(QString::number(gridSizeThing->lineEdit->text().toDouble() * 25.4));
4418 	}
4419 	else {
4420 		gridSizeThing->validator->setTop(1.0);
4421 		gridSizeThing->lineEdit->setText(QString::number(gridSizeThing->lineEdit->text().toDouble() / 25.4));
4422 	}
4423 
4424 }
4425 
restoreDefaultGrid()4426 void MainWindow::restoreDefaultGrid() {
4427     QWidget * widget = qobject_cast<QWidget *>(sender());
4428     if (widget == NULL) return;
4429 
4430     GridSizeDialog * dialog = qobject_cast<GridSizeDialog *>(widget->window());
4431     if (dialog == NULL) return;
4432 
4433 	GridSizeThing * gridSizeThing = dialog->gridSizeThing();
4434 
4435 	gridSizeThing->inButton->setChecked(true);
4436 	gridSizeThing->mmButton->setChecked(false);
4437 	gridSizeThing->lineEdit->setText(QString::number(gridSizeThing->defaultGridSize));
4438 }
4439 
setBackgroundColor()4440 void MainWindow::setBackgroundColor()
4441 {
4442 	QColor cc = m_currentGraphicsView->background();
4443 	QColor scc = m_currentGraphicsView->standardBackground();
4444 
4445 	SetColorDialog setColorDialog(tr("%1 background").arg(m_currentGraphicsView->viewName()), cc, scc, true, this);
4446 	int result = setColorDialog.exec();
4447 	if (result == QDialog::Rejected) return;
4448 
4449 	QColor newColor = setColorDialog.selectedColor();
4450     m_currentGraphicsView->setBackgroundColor(newColor, setColorDialog.isPrefsColor());
4451     setWindowModified(true);
4452 }
4453 
checkLoadedTraces()4454 void MainWindow::checkLoadedTraces() {
4455     if (m_pcbGraphicsView) m_pcbGraphicsView->checkLoadedTraces();
4456 }
4457 
showUnrouted()4458 void MainWindow::showUnrouted()
4459 {
4460     m_currentGraphicsView->showUnrouted();
4461 }
4462 
hidePartSilkscreen()4463 void MainWindow::hidePartSilkscreen()
4464 {
4465     m_pcbGraphicsView->hidePartSilkscreen();
4466 }
4467 
fabQuote()4468 void MainWindow::fabQuote() {
4469     if (m_pcbGraphicsView) m_pcbGraphicsView->fabQuote();
4470 }
4471 
findPartInSketch()4472 void MainWindow::findPartInSketch() {
4473     static QString lastSearchText;
4474 
4475 	if (m_currentGraphicsView == NULL) return;
4476 
4477     bool ok;
4478     QString text = QInputDialog::getText(this, tr("Enter Text"),
4479                                           tr("Text will match part label, description, title, etc. Enter text to search for:"),
4480                                           QLineEdit::Normal, lastSearchText, &ok);
4481     if (!ok || text.isEmpty()) return;
4482 
4483     lastSearchText = text;
4484     QSet<ItemBase *> itemBases;
4485     foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) {
4486         ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
4487         if (itemBase == NULL) continue;
4488 
4489         itemBases.insert(itemBase->layerKinChief());
4490     }
4491 
4492 	QStringList strings;
4493     strings << text;
4494     QList<ItemBase *> matched;
4495     foreach (ItemBase * itemBase, itemBases) {
4496 
4497 #ifndef QT_NO_DEBUG
4498         if (QString::number(itemBase->id()).contains(text)) {
4499             matched << itemBase;
4500             continue;
4501         }
4502 #endif
4503 
4504         if (itemBase->instanceTitle().contains(text, Qt::CaseInsensitive)) {
4505             matched << itemBase;
4506             continue;
4507         }
4508 
4509 	    QList<ModelPart *> modelParts;
4510         m_referenceModel->search(itemBase->modelPart(), strings, modelParts, true);
4511         if (modelParts.count() > 0) {
4512             matched << itemBase;
4513         }
4514     }
4515 
4516     if (matched.count() == 0) {
4517 	    QMessageBox::information(this, tr("Search"), tr("No parts matched search term '%1'.").arg(text));
4518         return;
4519     }
4520 
4521     m_currentGraphicsView->selectItems(matched);
4522 }
4523 
setGroundFillKeepout()4524 void MainWindow::setGroundFillKeepout() {
4525     if (m_pcbGraphicsView != NULL) m_pcbGraphicsView->setGroundFillKeepout();
4526 }
4527 
setViewFromBelowToggle()4528 void MainWindow::setViewFromBelowToggle() {
4529     if (m_pcbGraphicsView != NULL) {
4530         m_pcbGraphicsView->setViewFromBelow(m_viewFromBelowToggleAct->isChecked());
4531         updateActiveLayerButtons();
4532     }
4533 }
4534 
setViewFromBelow()4535 void MainWindow::setViewFromBelow() {
4536     if (m_pcbGraphicsView != NULL) {
4537         m_pcbGraphicsView->setViewFromBelow(true);
4538         updateActiveLayerButtons();
4539     }
4540 }
4541 
setViewFromAbove()4542 void MainWindow::setViewFromAbove() {
4543     if (m_pcbGraphicsView != NULL) {
4544         m_pcbGraphicsView->setViewFromBelow(false);
4545         updateActiveLayerButtons();
4546     }
4547 }
4548 
updateExportMenu()4549 void MainWindow::updateExportMenu() {
4550     bool enabled = m_currentGraphicsView != NULL;
4551     foreach (QAction * action, m_exportMenu->actions()) {
4552         action->setEnabled(enabled);
4553     }
4554 }
4555 
testConnectors()4556 void MainWindow::testConnectors() {
4557     if (m_currentGraphicsView == NULL) return;
4558 
4559     m_currentGraphicsView->testConnectors();
4560 }
4561