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 
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Fritzing is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Fritzing.  If not, see <http://www.gnu.org/licenses/>.
19 
20 ********************************************************************
21 
22 $Revision: 6995 $:
23 $Author: irascibl@gmail.com $:
24 $Date: 2013-04-28 00:56:34 +0200 (So, 28. Apr 2013) $lo
25 
26 ********************************************************************/
27 
28 #include <QtXml>
29 #include <QList>
30 #include <QFileInfo>
31 #include <QStringList>
32 #include <QFileInfoList>
33 #include <QDir>
34 #include <QLabel>
35 #include <QTime>
36 #include <QSettings>
37 #include <QRegExp>
38 #include <QPaintDevice>
39 #include <QPixmap>
40 #include <QTimer>
41 #include <QStackedWidget>
42 #include <QXmlStreamReader>
43 #include <QShortcut>
44 #include <QStyle>
45 #include <QFontMetrics>
46 #include <QApplication>
47 
48 
49 #include "mainwindow.h"
50 #include "../debugdialog.h"
51 #include "../connectors/connector.h"
52 #include "../partsbinpalette/partsbinpalettewidget.h"
53 #include "fdockwidget.h"
54 #include "../infoview/htmlinfoview.h"
55 #include "../waitpushundostack.h"
56 #include "../layerattributes.h"
57 #include "../sketch/breadboardsketchwidget.h"
58 #include "../sketch/schematicsketchwidget.h"
59 #include "../sketch/pcbsketchwidget.h"
60 #include "../sketch/welcomeview.h"
61 #include "../svg/svgfilesplitter.h"
62 #include "../utils/folderutils.h"
63 #include "../utils/fmessagebox.h"
64 #include "../utils/lockmanager.h"
65 #include "../utils/textutils.h"
66 #include "../utils/graphicsutils.h"
67 #include "../items/mysterypart.h"
68 #include "../items/moduleidnames.h"
69 #include "../items/pinheader.h"
70 #include "../items/perfboard.h"
71 #include "../items/stripboard.h"
72 #include "../items/partfactory.h"
73 #include "../dock/layerpalette.h"
74 #include "../items/paletteitem.h"
75 #include "../items/virtualwire.h"
76 #include "../items/screwterminal.h"
77 #include "../items/dip.h"
78 #include "../processeventblocker.h"
79 #include "../sketchtoolbutton.h"
80 #include "../partsbinpalette/binmanager/binmanager.h"
81 #include "../fsvgrenderer.h"
82 #include "../utils/fsizegrip.h"
83 #include "../utils/expandinglabel.h"
84 #include "../utils/autoclosemessagebox.h"
85 #include "../utils/fileprogressdialog.h"
86 #include "../utils/clickablelabel.h"
87 #include "../items/resizableboard.h"
88 #include "../items/resistor.h"
89 #include "../items/logoitem.h"
90 #include "../utils/zoomslider.h"
91 #include "../partseditor/pemainwindow.h"
92 #include "../help/firsttimehelpdialog.h"
93 
FTabWidget(QWidget * parent)94 FTabWidget::FTabWidget(QWidget * parent) : QTabWidget(parent)
95 {
96     QTabBar * tabBar = new FTabBar;
97     tabBar->setObjectName("mainTabBar");
98     //connect(this, SIGNAL(currentChanged(int)), this, SLOT(tabIndexChanged(int)));
99     setTabBar(tabBar);
100 }
101 
102 //int FTabWidget::addTab(QWidget * page, const QIcon & icon, const QIcon & hoverIcon, const QIcon & inactiveIcon, const QString & label)
103 //{
104  //   // assumes tabs are not deleted or reordered
105 //    m_inactiveIcons << inactiveIcon;
106 //    m_hoverIcons << hoverIcon;
107 //    m_icons << icon;
108 //    return QTabWidget::addTab(page, icon, label);
109 //}
110 
111 //void FTabWidget::tabIndexChanged(int index) {
112 //    for (int i = 0; i < this->count(); ++i) {
113 //        if (i == index) setTabIcon(i, m_icons.at(i));
114 //        else setTabIcon(i, m_inactiveIcons.at(i));
115 //    }
116 //}
117 
FTabBar()118 FTabBar::FTabBar() : QTabBar()
119 {
120     m_firstTime = true;
121 }
122 
123 
paintEvent(QPaintEvent * event)124 void FTabBar::paintEvent(QPaintEvent * event) {
125     // this is a hack to left-align the tab text by adding spaces to the text
126     // center-alignment is hard-coded deep into the way the tab is drawn in qcommonstyle.cpp
127 
128     static int offset = 15;  // derived this empirically, no idea where it comes from
129 
130     if (m_firstTime) {
131         m_firstTime = false;
132 
133         // TODO: how to append spaces from the language direction
134 
135         for (int i = 0; i < this->count(); ++i) {
136             QStyleOptionTabV3 tab;
137             initStyleOption(&tab, 0);
138             //DebugDialog::debug(QString("state %1").arg(tab.state));
139             QString text = tabText(i);
140             int added = 0;
141             while (true) {
142                 QRect r = tab.fontMetrics.boundingRect(text);
143                 if (r.width() + iconSize().width() + offset >= tabRect(i).width()) {
144                     if (added) {
145                         text.chop(1);
146                         setTabText(i, text);
147                     }
148                     break;
149                 }
150 
151                 text += " ";
152                 added++;
153             }
154         }
155     }
156 
157     QTabBar::paintEvent(event);
158 
159     /*
160     return;
161 
162     // this code mostly lifted from QTabBar::paintEvent
163 
164     QStyleOptionTabBarBaseV2 optTabBase;
165     optTabBase.init(this);
166     optTabBase.shape = this->shape();
167     optTabBase.documentMode = this->documentMode();
168 
169     QStylePainter p(this);
170     int selected = this->currentIndex();
171 
172     for (int i = 0; i < this->count(); ++i)
173          optTabBase.tabBarRect |= tabRect(i);
174 
175     optTabBase.selectedTabRect = tabRect(selected);
176 
177     p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
178 
179     for (int i = 0; i < this->count(); ++i) {
180         if (i == selected) continue;
181 
182         QStyleOptionTabV3 tab;
183         initStyleOption(&tab, i);
184         drawTab(p, tab, i);
185     }
186 
187     // Draw the selected tab last to get it "on top"
188     if (selected >= 0) {
189         QStyleOptionTabV3 tab;
190         initStyleOption(&tab, selected);
191         drawTab(p, tab, selected);
192     }
193 
194 
195     */
196 
197 }
198 
199 /*
200 void FTabBar::drawTab(QStylePainter & p, QStyleOptionTabV3 & tabV3, int index)
201 {
202     //tabV3.iconSize = m_pixmaps.at(index).size();
203     p.drawControl(QStyle::CE_TabBarTabShape, tabV3);
204     //p.drawPixmap(tabV3.rect.left(), tabV3.rect.top(), m_pixmaps.at(index));
205 
206 
207     //QRect tr = tabV3.rect;
208 
209     //int alignment = Qt::AlignLeft | Qt::TextShowMnemonic;
210     //if (!this->style()->styleHint(QStyle::SH_UnderlineShortcut, &tabV3, this))
211     //    alignment |= Qt::TextHideMnemonic;
212 
213     //p.drawItemText(tr, alignment, tabV3.palette, tabV3.state & QStyle::State_Enabled, tabV3.text, QPalette::WindowText);
214 
215 
216     //p.drawControl(QStyle::CE_TabBarTabLabel, tabV3);
217 
218 }
219 */
220 
221 ///////////////////////////////////////////////
222 
223 struct MissingSvgInfo {
224     QString requestedPath;
225     QStringList connectorSvgIds;
226     ModelPart * modelPart;
227     bool equal;
228 };
229 
byConnectorCount(MissingSvgInfo & m1,MissingSvgInfo & m2)230 bool byConnectorCount(MissingSvgInfo & m1, MissingSvgInfo & m2)
231 {
232     if (m1.connectorSvgIds.count() == m2.connectorSvgIds.count() && m1.modelPart != m2.modelPart) {
233         m1.equal = m2.equal = true;
234     }
235 
236 	return (m1.connectorSvgIds.count() > m2.connectorSvgIds.count());
237 }
238 
239 ///////////////////////////////////////////////
240 
241 #define ZIP_PART QString("part.")
242 #define ZIP_SVG  QString("svg.")
243 
244 ///////////////////////////////////////////////
245 
246 // SwapTimer explained: http://code.google.com/p/fritzing/issues/detail?id=1431
247 
SwapTimer()248 SwapTimer::SwapTimer() : QTimer()
249 {
250 }
251 
setAll(const QString & family,const QString & prop,QMap<QString,QString> & propsMap,ItemBase * itemBase)252 void SwapTimer::setAll(const QString & family, const QString & prop, QMap<QString, QString> & propsMap, ItemBase * itemBase)
253 {
254 	m_family = family;
255 	m_prop = prop;
256 	m_propsMap = propsMap;
257 	m_itemBase = itemBase;
258 }
259 
family()260 const QString & SwapTimer::family()
261 {
262 	return  m_family;
263 }
264 
prop()265 const QString & SwapTimer::prop()
266 {
267 	return m_prop;
268 }
269 
propsMap()270 QMap<QString, QString> SwapTimer::propsMap()
271 {
272 	return m_propsMap;
273 }
274 
itemBase()275 ItemBase * SwapTimer::itemBase()
276 {
277 	return m_itemBase;
278 }
279 
280 ///////////////////////////////////////////////
281 
282 const QString MainWindow::UntitledSketchName = "Untitled Sketch";
283 int MainWindow::UntitledSketchIndex = 1;
284 int MainWindow::CascadeFactorX = 21;
285 int MainWindow::CascadeFactorY = 19;
286 
287 static const int MainWindowDefaultWidth = 840;
288 static const int MainWindowDefaultHeight = 600;
289 
290 int MainWindow::AutosaveTimeoutMinutes = 10;   // in minutes
291 bool MainWindow::AutosaveEnabled = true;
292 QString MainWindow::BackupFolder;
293 
294 QRegExp MainWindow::GuidMatcher = QRegExp("[A-Fa-f0-9]{32}");
295 
296 /////////////////////////////////////////////
297 
MainWindow(ReferenceModel * referenceModel,QWidget * parent)298 MainWindow::MainWindow(ReferenceModel *referenceModel, QWidget * parent) :
299     FritzingWindow(untitledFileName(), untitledFileCount(), fileExtension(), parent)
300 {
301     m_noSchematicConversion = m_useOldSchematic = m_convertedSchematic = false;
302     m_initialTab = 1;
303     m_rolloverQuoteDialog = NULL;
304 	setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
305 	setDockOptions(QMainWindow::AnimatedDocks);
306 	m_sizeGrip = new FSizeGrip(this);
307 
308 	m_topDock = NULL;
309 	m_bottomDock = NULL;
310 	m_dontKeepMargins = true;
311 
312     m_settingsPrefix = "main/";
313     m_showWelcomeAct = m_showProgramAct = m_raiseWindowAct = m_showPartsBinIconViewAct = m_showAllLayersAct = m_hideAllLayersAct = m_rotate90cwAct = m_showBreadboardAct = m_showSchematicAct = m_showPCBAct = NULL;
314     m_fileMenu = m_editMenu = m_partMenu = m_windowMenu = m_pcbTraceMenu = m_schematicTraceMenu = m_breadboardTraceMenu = m_viewMenu = NULL;
315     m_infoView = NULL;
316     m_addedToTemp = false;
317     setAcceptDrops(true);
318 	m_activeWire = NULL;
319 	m_activeConnectorItem = NULL;
320 	m_swapTimer.setInterval(30);
321 	m_swapTimer.setParent(this);
322 	m_swapTimer.setSingleShot(true);
323 	connect(&m_swapTimer, SIGNAL(timeout()), this, SLOT(swapSelectedTimeout()));
324 
325 	m_closeSilently = false;
326 	m_orderFabAct = NULL;
327 	m_viewFromButtonWidget = m_activeLayerButtonWidget = NULL;
328 	m_programView = m_programWindow = NULL;
329 	m_welcomeView = NULL;
330 	m_windowMenuSeparator = NULL;
331 	m_schematicWireColorMenu = m_breadboardWireColorMenu = NULL;
332 	m_checkForUpdatesAct = NULL;
333 	m_fileProgressDialog = NULL;
334 	m_currentGraphicsView = NULL;
335 	m_comboboxChanged = false;
336 
337     // Add a timer for autosaving
338 	m_backingUp = m_autosaveNeeded = false;
339     connect(&m_autosaveTimer, SIGNAL(timeout()), this, SLOT(backupSketch()));
340     m_autosaveTimer.start(AutosaveTimeoutMinutes * 60 * 1000);
341 
342     m_fireQuoteTimer.setSingleShot(true);
343     connect(&m_fireQuoteTimer, SIGNAL(timeout()), this, SLOT(fireQuote()));
344 
345 	resize(MainWindowDefaultWidth, MainWindowDefaultHeight);
346 
347 	m_backupFileNameAndPath = MainWindow::BackupFolder + "/" + TextUtils::getRandText() + FritzingSketchExtension;
348     // Connect the undoStack to our autosave stuff
349     connect(m_undoStack, SIGNAL(indexChanged(int)), this, SLOT(autosaveNeeded(int)));
350     connect(m_undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(undoStackCleanChanged(bool)));
351 
352 	// Create dot icons
353 	m_dotIcon = QIcon(":/resources/images/dot.png");
354 	m_emptyIcon = QIcon();
355 
356 	m_currentWidget = NULL;
357 	m_firstOpen = true;
358 
359 	m_statusBar = new QStatusBar(this);
360 	setStatusBar(m_statusBar);
361 	m_statusBar->setSizeGripEnabled(false);
362 
363 	QSettings settings;
364 	m_locationLabelUnits = settings.value("LocationInches", "in").toString();
365 
366 	// leave the m_orderFabEnabled check in case we turn off the fab button in the future
367 	m_orderFabEnabled = true; // settings.value(ORDERFABENABLED, QVariant(false)).toBool();
368 
369 	m_locationLabel = new ClickableLabel("", this);
370 	m_locationLabel->setObjectName("LocationLabel");
371 	connect(m_locationLabel, SIGNAL(clicked()), this, SLOT(locationLabelClicked()));
372     m_locationLabel->setCursor(Qt::PointingHandCursor);
373 	m_statusBar->addPermanentWidget(m_locationLabel);
374 
375 	m_zoomSlider = new ZoomSlider(ZoomableGraphicsView::MaxScaleValue, m_statusBar);
376 	connect(m_zoomSlider, SIGNAL(zoomChanged(double)), this, SLOT(updateViewZoom(double)));
377 	m_statusBar->addPermanentWidget(m_zoomSlider);
378 
379 	setAttribute(Qt::WA_DeleteOnClose, true);
380 
381 #ifdef Q_OS_MAC
382         //setAttribute(Qt::WA_QuitOnClose, false);					// restoring this temporarily (2008.12.19)
383 #endif
384     m_dontClose = m_closing = false;
385 
386 	m_referenceModel = referenceModel;
387 	m_sketchModel = new SketchModel(true);
388 
389 
390 	QShortcut * shortcut = new QShortcut(QKeySequence(tr("Ctrl+R", "Rotate Clockwise")), this);
391 	connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCW()));
392 	shortcut = new QShortcut(QKeySequence(tr("Alt+Ctrl+R", "Rotate Clockwise")), this);
393 	connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCWRubberBand()));
394 	shortcut = new QShortcut(QKeySequence(tr("Meta+Ctrl+R", "Rotate Clockwise")), this);
395 	connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCWRubberBand()));
396 
397 	shortcut = new QShortcut(QKeySequence(tr("Shift+Ctrl+R", "Rotate Counterclockwise")), this);
398 	connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCCW()));
399 	shortcut = new QShortcut(QKeySequence(tr("Alt+Shift+Ctrl+R", "Rotate Counterclockwise")), this);
400 	connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCCWRubberBand()));
401 	shortcut = new QShortcut(QKeySequence(tr("Meta+Shift+Ctrl+R", "Rotate Counterclockwise")), this);
402 	connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCCWRubberBand()));
403 
404 	shortcut = new QShortcut(QKeySequence(tr("Shift+Ctrl+Tab", "Toggle Active Layer")), this);
405 	connect(shortcut, SIGNAL(activated()), this, SLOT(toggleActiveLayer()));
406 
407 
408 	connect(this, SIGNAL(changeActivationSignal(bool, QWidget *)), qApp, SLOT(changeActivation(bool, QWidget *)), Qt::DirectConnection);
409 	connect(this, SIGNAL(destroyed(QObject *)), qApp, SLOT(topLevelWidgetDestroyed(QObject *)));
410 	connect(this, SIGNAL(externalProcessSignal(QString &, QString &, QStringList &)),
411 			qApp, SLOT(externalProcessSlot(QString &, QString &, QStringList &)),
412 			Qt::DirectConnection);
413 }
414 
createTabWidget()415 QWidget * MainWindow::createTabWidget() {
416 	//return new QStackedWidget(this);
417     FTabWidget * tabWidget = new FTabWidget(this);
418     tabWidget->setObjectName("sketch_tabs");
419     return tabWidget;
420 }
421 
addTab(QWidget * widget,const QString & label)422 void MainWindow::addTab(QWidget * widget, const QString & label) {
423 	//Q_UNUSED(label);
424 	//qobject_cast<QStackedWidget *>(m_tabWidget)->addWidget(widget);
425     qobject_cast<QTabWidget *>(m_tabWidget)->addTab(widget, label);
426 }
427 
428 
addTab(QWidget * widget,const QString & iconPath,const QString & label,bool withIcon)429 void MainWindow::addTab(QWidget * widget, const QString & iconPath, const QString & label, bool withIcon) {
430     if (!withIcon) {
431         addTab(widget, label);
432         return;
433     }
434 
435     FTabWidget * tabWidget = qobject_cast<FTabWidget *>(m_tabWidget);
436     if (tabWidget == NULL) {
437         addTab(widget, label);
438         return;
439     }
440 
441 	//Q_UNUSED(label);
442 	//qobject_cast<QStackedWidget *>(m_tabWidget)->addWidget(widget);
443     QIcon icon;
444     QPixmap pixmap(iconPath);
445     icon.addPixmap(pixmap, QIcon::Normal, QIcon::On);
446     QString inactivePath = iconPath;
447     inactivePath.replace("Active", "Inactive");
448     QPixmap inactivePixmap(inactivePath);
449     icon.addPixmap(inactivePixmap, QIcon::Normal, QIcon::Off);
450     QString hoverPath = iconPath;
451     hoverPath.replace("Active", "Hover");
452     QPixmap hoverPixmap(hoverPath);
453     icon.addPixmap(hoverPixmap, QIcon::Active, QIcon::On);
454 
455     tabWidget->addTab(widget, icon, label);
456 }
457 
currentTabIndex()458 int MainWindow::currentTabIndex() {
459 	return qobject_cast<QTabWidget *>(m_tabWidget)->currentIndex();
460 }
461 
setCurrentTabIndex(int index)462 void MainWindow::setCurrentTabIndex(int index) {
463 	qobject_cast<QTabWidget *>(m_tabWidget)->setCurrentIndex(index);
464 }
465 
currentTabWidget()466 QWidget * MainWindow::currentTabWidget() {
467 	return qobject_cast<QTabWidget *>(m_tabWidget)->currentWidget();
468 }
469 
init(ReferenceModel * referenceModel,bool lockFiles)470 void MainWindow::init(ReferenceModel *referenceModel, bool lockFiles) {
471 
472 	m_tabWidget = createTabWidget(); //   FTabWidget(this);
473 	setCentralWidget(m_tabWidget);
474 
475     m_referenceModel = referenceModel;
476     m_restarting = false;
477 
478 	if (m_fileProgressDialog) {
479 		m_fileProgressDialog->setValue(2);
480 	}
481 
482     initLockedFiles(lockFiles);
483 
484 
485 	initWelcomeView();
486     initSketchWidgets(true);
487     initProgrammingWidget();
488 
489     m_undoView = new QUndoView();
490     m_undoGroup = new QUndoGroup(this);
491     m_undoView->setGroup(m_undoGroup);
492     m_undoGroup->setActiveStack(m_undoStack);
493 
494     initDock();
495     initMenus();
496     moreInitDock();
497 
498 	createZoomOptions(m_breadboardWidget);
499 	createZoomOptions(m_schematicWidget);
500 	createZoomOptions(m_pcbWidget);
501 
502     m_breadboardWidget->setToolbarWidgets(getButtonsForView(ViewLayer::BreadboardView));
503     m_schematicWidget->setToolbarWidgets(getButtonsForView(ViewLayer::SchematicView));
504 	m_pcbWidget->setToolbarWidgets(getButtonsForView(ViewLayer::PCBView));
505 
506     initStyleSheet();
507 
508     m_breadboardGraphicsView->setItemMenu(breadboardItemMenu());
509     m_breadboardGraphicsView->setWireMenu(breadboardWireMenu());
510 
511     m_pcbGraphicsView->setWireMenu(pcbWireMenu());
512     m_pcbGraphicsView->setItemMenu(pcbItemMenu());
513 
514     m_schematicGraphicsView->setItemMenu(schematicItemMenu());
515     m_schematicGraphicsView->setWireMenu(schematicWireMenu());
516 
517     if (m_infoView) {
518         m_breadboardGraphicsView->setInfoView(m_infoView);
519         m_pcbGraphicsView->setInfoView(m_infoView);
520         m_schematicGraphicsView->setInfoView(m_infoView);
521     }
522 
523 	// make sure to set the connections after the views have been created
524 	connect(m_tabWidget, SIGNAL(currentChanged ( int )), this, SLOT(tabWidget_currentChanged( int )));
525 
526 	connectPairs();
527 
528     setInitialView();
529 
530 	this->installEventFilter(this);
531 
532 	if (m_fileProgressDialog) {
533 		m_fileProgressDialog->setValue(95);
534 	}
535 
536 	QSettings settings;
537 
538 	if(!settings.value(m_settingsPrefix + "state").isNull()) {
539 		restoreState(settings.value(m_settingsPrefix + "state").toByteArray());
540 		restoreGeometry(settings.value(m_settingsPrefix + "geometry").toByteArray());
541 	}
542 
543 	setMinimumSize(0,0);
544 	m_tabWidget->setMinimumWidth(500);
545 	m_tabWidget->setMinimumWidth(0);
546 
547 	connect(this, SIGNAL(readOnlyChanged(bool)), this, SLOT(applyReadOnlyChange(bool)));
548 
549 	m_setUpDockManagerTimer.setSingleShot(true);
550 	connect(&m_setUpDockManagerTimer, SIGNAL(timeout()), this, SLOT(keepMargins()));
551     m_setUpDockManagerTimer.start(1000);
552 
553 	if (m_fileProgressDialog) {
554 		m_fileProgressDialog->setValue(98);
555 	}
556 
557 }
558 
~MainWindow()559 MainWindow::~MainWindow()
560 {
561     // Delete backup of this sketch if one exists.
562     QFile::remove(m_backupFileNameAndPath);
563 
564 	delete m_sketchModel;
565 
566 	dontKeepMargins();
567 	m_setUpDockManagerTimer.stop();
568 
569 	foreach (LinkedFile * linkedFile, m_linkedProgramFiles) {
570 		delete linkedFile;
571 	}
572 	m_linkedProgramFiles.clear();
573 
574 	if (!m_fzzFolder.isEmpty()) {
575 		LockManager::releaseLockedFiles(m_fzzFolder, m_fzzFiles);
576 		FolderUtils::rmdir(m_fzzFolder);
577 	}
578 }
579 
initLockedFiles(bool lockFiles)580 void MainWindow::initLockedFiles(bool lockFiles) {
581 	LockManager::initLockedFiles("fzz", m_fzzFolder, m_fzzFiles, lockFiles ? LockManager::SlowTime : 0);
582 	if (lockFiles) {
583 		QFileInfoList backupList;
584 		LockManager::checkLockedFiles("fzz", backupList, m_fzzFiles, true, LockManager::SlowTime);
585 	}
586 }
587 
initSketchWidgets(bool withIcons)588 void MainWindow::initSketchWidgets(bool withIcons) {
589 	//DebugDialog::debug("init sketch widgets");
590 
591 	// all this belongs in viewLayer.xml
592 	m_breadboardGraphicsView = new BreadboardSketchWidget(ViewLayer::BreadboardView, this);
593 	initSketchWidget(m_breadboardGraphicsView);
594 	m_breadboardWidget = new SketchAreaWidget(m_breadboardGraphicsView,this);
595     addTab(m_breadboardWidget, ":/resources/images/icons/TabWidgetBreadboardActive_icon.png", tr("Breadboard"), withIcons);
596 
597 	if (m_fileProgressDialog) {
598 		m_fileProgressDialog->setValue(11);
599 	}
600 
601 	m_schematicGraphicsView = new SchematicSketchWidget(ViewLayer::SchematicView, this);
602 	initSketchWidget(m_schematicGraphicsView);
603 	m_schematicWidget = new SketchAreaWidget(m_schematicGraphicsView, this);
604     addTab(m_schematicWidget, ":/resources/images/icons/TabWidgetSchematicActive_icon.png", tr("Schematic"), withIcons);
605 
606 	if (m_fileProgressDialog) {
607 		m_fileProgressDialog->setValue(20);
608 	}
609 
610 	m_pcbGraphicsView = new PCBSketchWidget(ViewLayer::PCBView, this);
611 	initSketchWidget(m_pcbGraphicsView);
612 	m_pcbWidget = new SketchAreaWidget(m_pcbGraphicsView, this);
613     addTab(m_pcbWidget, ":/resources/images/icons/TabWidgetPcbActive_icon.png", tr("PCB"), withIcons);
614 
615 
616 	if (m_fileProgressDialog) {
617 		m_fileProgressDialog->setValue(29);
618 	}
619 }
620 
initMenus()621 void MainWindow::initMenus() {
622 	// This is the magic translation that changes all the shortcut text on the menu items
623 	// to the native language instead of "Ctrl", so the German menu items will now read "Strg"
624 	// You don't actually have to translate every menu item in the .ts file, you can just leave it as "Ctrl".
625 	QShortcut::tr("Ctrl", "for naming shortcut keys on menu items");
626 	QShortcut::tr("Alt", "for naming shortcut keys on menu items");
627 	QShortcut::tr("Shift", "for naming shortcut keys on menu items");
628 	QShortcut::tr("Meta", "for naming shortcut keys on menu items");
629 
630 	//DebugDialog::debug("create menus");
631 
632     createActions();
633     createMenus();
634 
635 	//DebugDialog::debug("create toolbars");
636 
637     createStatusBar();
638 
639 	//DebugDialog::debug("after creating status bar");
640 
641 	if (m_fileProgressDialog) {
642 		m_fileProgressDialog->setValue(91);
643 	}
644 }
645 
initSketchWidget(SketchWidget * sketchWidget)646 void MainWindow::initSketchWidget(SketchWidget * sketchWidget) {
647 	sketchWidget->setSketchModel(m_sketchModel);
648 	sketchWidget->setReferenceModel(m_referenceModel);
649 	sketchWidget->setUndoStack(m_undoStack);
650 	sketchWidget->setChainDrag(true);			// enable bend points
651 	sketchWidget->initGrid();
652 	sketchWidget->addViewLayers();
653 }
654 
connectPairs()655 void MainWindow::connectPairs() {
656 	connectPair(m_breadboardGraphicsView, m_schematicGraphicsView);
657 	connectPair(m_breadboardGraphicsView, m_pcbGraphicsView);
658 	connectPair(m_schematicGraphicsView, m_breadboardGraphicsView);
659 	connectPair(m_schematicGraphicsView, m_pcbGraphicsView);
660 	connectPair(m_pcbGraphicsView, m_breadboardGraphicsView);
661 	connectPair(m_pcbGraphicsView, m_schematicGraphicsView);
662 
663 	connect(m_pcbGraphicsView, SIGNAL(groundFillSignal()), this, SLOT(groundFill()));
664 	connect(m_pcbGraphicsView, SIGNAL(copperFillSignal()), this, SLOT(copperFill()));
665 
666     connect(m_pcbGraphicsView, SIGNAL(swapBoardImageSignal(SketchWidget *, ItemBase *, const QString &, const QString &, bool)),
667                 this, SLOT(swapBoardImageSlot(SketchWidget *, ItemBase *, const QString &, const QString &, bool)));
668 
669 
670 	connect(m_breadboardGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *)));
671 	connect(m_schematicGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *)));
672 	connect(m_pcbGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *)));
673 
674 	connect(m_breadboardGraphicsView, SIGNAL(setActiveConnectorItemSignal(ConnectorItem *)), this, SLOT(setActiveConnectorItem(ConnectorItem *)));
675 	connect(m_schematicGraphicsView, SIGNAL(setActiveConnectorItemSignal(ConnectorItem *)), this, SLOT(setActiveConnectorItem(ConnectorItem *)));
676 	connect(m_pcbGraphicsView, SIGNAL(setActiveConnectorItemSignal(ConnectorItem *)), this, SLOT(setActiveConnectorItem(ConnectorItem *)));
677 
678 	bool succeeded = connect(m_pcbGraphicsView, SIGNAL(routingStatusSignal(SketchWidget *, const RoutingStatus &)),
679 						this, SLOT(routingStatusSlot(SketchWidget *, const RoutingStatus &)));
680 	succeeded =  succeeded && connect(m_schematicGraphicsView, SIGNAL(routingStatusSignal(SketchWidget *, const RoutingStatus &)),
681 						this, SLOT(routingStatusSlot(SketchWidget *, const RoutingStatus &)));
682 	succeeded =  succeeded && connect(m_breadboardGraphicsView, SIGNAL(routingStatusSignal(SketchWidget *, const RoutingStatus &)),
683 						this, SLOT(routingStatusSlot(SketchWidget *, const RoutingStatus &)));
684 
685 	succeeded =  succeeded && connect(m_breadboardGraphicsView, SIGNAL(swapSignal(const QString &, const QString &, QMap<QString, QString> &, ItemBase *)),
686 						this, SLOT(swapSelectedDelay(const QString &, const QString &, QMap<QString, QString> &, ItemBase *)));
687 	succeeded =  succeeded && connect(m_schematicGraphicsView, SIGNAL(swapSignal(const QString &, const QString &, QMap<QString, QString> &, ItemBase *)),
688 						this, SLOT(swapSelectedDelay(const QString &, const QString &, QMap<QString, QString> &, ItemBase *)));
689 	succeeded =  succeeded && connect(m_pcbGraphicsView, SIGNAL(swapSignal(const QString &, const QString &, QMap<QString, QString> &, ItemBase *)),
690 						this, SLOT(swapSelectedDelay(const QString &, const QString &, QMap<QString, QString> &, ItemBase *)));
691 
692 	succeeded =  succeeded && connect(m_breadboardGraphicsView, SIGNAL(dropPasteSignal(SketchWidget *)),
693 						this, SLOT(dropPaste(SketchWidget *)));
694 	succeeded =  succeeded && connect(m_schematicGraphicsView, SIGNAL(dropPasteSignal(SketchWidget *)),
695 						this, SLOT(dropPaste(SketchWidget *)));
696 	succeeded =  succeeded && connect(m_pcbGraphicsView, SIGNAL(dropPasteSignal(SketchWidget *)),
697 						this, SLOT(dropPaste(SketchWidget *)));
698 
699 	succeeded =  succeeded && connect(m_pcbGraphicsView, SIGNAL(subSwapSignal(SketchWidget *, ItemBase *, const QString &, ViewLayer::ViewLayerPlacement, long &, QUndoCommand *)),
700 						this, SLOT(subSwapSlot(SketchWidget *, ItemBase *, const QString &, ViewLayer::ViewLayerPlacement, long &, QUndoCommand *)),
701 						Qt::DirectConnection);
702 
703 	succeeded =  succeeded && connect(m_pcbGraphicsView, SIGNAL(updateLayerMenuSignal()), this, SLOT(updateLayerMenuSlot()));
704 	succeeded =  succeeded && connect(m_pcbGraphicsView, SIGNAL(changeBoardLayersSignal(int, bool )), this, SLOT(changeBoardLayers(int, bool )));
705 
706 	succeeded =  succeeded && connect(m_pcbGraphicsView, SIGNAL(boardDeletedSignal()), this, SLOT(boardDeletedSlot()));
707 
708 	succeeded =  succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_breadboardGraphicsView, SLOT(spaceBarIsPressedSlot(bool)));
709 	succeeded =  succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_schematicGraphicsView, SLOT(spaceBarIsPressedSlot(bool)));
710 	succeeded =  succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_pcbGraphicsView, SLOT(spaceBarIsPressedSlot(bool)));
711 
712 	succeeded =  succeeded && connect(m_pcbGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double)));
713 	succeeded =  succeeded && connect(m_breadboardGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double)));
714 	succeeded =  succeeded && connect(m_schematicGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double)));
715 
716 	succeeded =  succeeded && connect(m_breadboardGraphicsView, SIGNAL(filenameIfSignal(QString &)), this, SLOT(filenameIfSlot(QString &)), Qt::DirectConnection);
717 	succeeded =  succeeded && connect(m_pcbGraphicsView, SIGNAL(filenameIfSignal(QString &)), this, SLOT(filenameIfSlot(QString &)), Qt::DirectConnection);
718 	succeeded =  succeeded && connect(m_schematicGraphicsView, SIGNAL(filenameIfSignal(QString &)), this, SLOT(filenameIfSlot(QString &)), Qt::DirectConnection);
719 
720 
721 
722     succeeded =  succeeded && connect(m_breadboardGraphicsView, SIGNAL(getDroppedItemViewLayerPlacementSignal(ModelPart *, ViewLayer::ViewLayerPlacement &)),
723                                         m_pcbGraphicsView, SLOT(getDroppedItemViewLayerPlacement(ModelPart *, ViewLayer::ViewLayerPlacement &)),
724                                         Qt::DirectConnection);
725     succeeded =  succeeded && connect(m_schematicGraphicsView, SIGNAL(getDroppedItemViewLayerPlacementSignal(ModelPart *, ViewLayer::ViewLayerPlacement &)),
726                                         m_pcbGraphicsView, SLOT(getDroppedItemViewLayerPlacement(ModelPart *, ViewLayer::ViewLayerPlacement &)),
727                                         Qt::DirectConnection);
728 
729 
730     if (!succeeded) {
731 		DebugDialog::debug("connectPair failed");
732 	}
733 }
734 
connectPair(SketchWidget * signaller,SketchWidget * slotter)735 void MainWindow::connectPair(SketchWidget * signaller, SketchWidget * slotter)
736 {
737 
738 	bool succeeded = connect(signaller, SIGNAL(itemAddedSignal(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long, SketchWidget *)),
739 							 slotter, SLOT(itemAddedSlot(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long, SketchWidget *)));
740 
741 	succeeded = succeeded && connect(signaller, SIGNAL(itemDeletedSignal(long)),
742 									 slotter, SLOT(itemDeletedSlot(long)),
743 									 Qt::DirectConnection);
744 
745 	succeeded = succeeded && connect(signaller, SIGNAL(clearSelectionSignal()),
746 									 slotter, SLOT(clearSelectionSlot()));
747 
748 	succeeded = succeeded && connect(signaller, SIGNAL(itemSelectedSignal(long, bool)),
749 									 slotter, SLOT(itemSelectedSlot(long, bool)));
750 	succeeded = succeeded && connect(signaller, SIGNAL(selectAllItemsSignal(bool, bool)),
751 									 slotter, SLOT(selectAllItems(bool, bool)));
752 	succeeded = succeeded && connect(signaller, SIGNAL(wireDisconnectedSignal(long, QString)),
753 									 slotter, SLOT(wireDisconnectedSlot(long,  QString)));
754 	succeeded = succeeded && connect(signaller, SIGNAL(wireConnectedSignal(long,  QString, long,  QString)),
755 									 slotter, SLOT(wireConnectedSlot(long, QString, long, QString)));
756 	succeeded = succeeded && connect(signaller, SIGNAL(changeConnectionSignal(long,  QString, long,  QString, ViewLayer::ViewLayerPlacement, bool, bool)),
757 									 slotter, SLOT(changeConnectionSlot(long, QString, long, QString, ViewLayer::ViewLayerPlacement, bool, bool)));
758 	succeeded = succeeded && connect(signaller, SIGNAL(copyBoundingRectsSignal(QHash<QString, QRectF> &)),
759 													   slotter, SLOT(copyBoundingRectsSlot(QHash<QString, QRectF> &)),
760 									 Qt::DirectConnection);
761 
762 	succeeded = succeeded && connect(signaller, SIGNAL(cleanUpWiresSignal(CleanUpWiresCommand *)),
763 									 slotter, SLOT(cleanUpWiresSlot(CleanUpWiresCommand *)) );
764 
765 	succeeded = succeeded && connect(signaller, SIGNAL(cleanupRatsnestsSignal(bool)),
766 									 slotter, SLOT(cleanupRatsnests(bool)) );
767 
768 	succeeded = succeeded && connect(signaller, SIGNAL(checkStickySignal(long, bool, bool, CheckStickyCommand *)),
769 									 slotter, SLOT(checkSticky(long, bool, bool, CheckStickyCommand *)) );
770 
771 	succeeded = succeeded && connect(signaller, SIGNAL(disconnectAllSignal(QList<ConnectorItem *>, QHash<ItemBase *, SketchWidget *> &, QUndoCommand *)),
772 									 slotter, SLOT(disconnectAllSlot(QList<ConnectorItem *>, QHash<ItemBase *, SketchWidget *> &, QUndoCommand *)),
773 									 Qt::DirectConnection);
774 	succeeded = succeeded && connect(signaller, SIGNAL(setResistanceSignal(long, QString, QString, bool)),
775 									 slotter, SLOT(setResistance(long, QString, QString, bool)));
776 	succeeded = succeeded && connect(signaller, SIGNAL(makeDeleteItemCommandPrepSignal(ItemBase *, bool , QUndoCommand * )),
777 									 slotter, SLOT(makeDeleteItemCommandPrepSlot(ItemBase * , bool , QUndoCommand * )));
778 	succeeded = succeeded && connect(signaller, SIGNAL(makeDeleteItemCommandFinalSignal(ItemBase *, bool , QUndoCommand * )),
779 									 slotter, SLOT(makeDeleteItemCommandFinalSlot(ItemBase * , bool , QUndoCommand * )));
780 
781 	succeeded = succeeded && connect(signaller, SIGNAL(setPropSignal(long,  const QString &,  const QString &, bool, bool)),
782 									 slotter, SLOT(setProp(long,  const QString &,  const QString &, bool, bool)));
783 
784 	succeeded = succeeded && connect(signaller, SIGNAL(setInstanceTitleSignal(long, const QString &, const QString &, bool, bool )),
785 									 slotter, SLOT(setInstanceTitle(long, const QString &, const QString &, bool, bool )));
786 
787 	succeeded = succeeded && connect(signaller, SIGNAL(setVoltageSignal(double, bool )),
788 									 slotter, SLOT(setVoltage(double, bool )));
789 
790 	succeeded = succeeded && connect(signaller, SIGNAL(showLabelFirstTimeSignal(long, bool, bool )),
791 									 slotter, SLOT(showLabelFirstTime(long, bool, bool )));
792 
793 	succeeded = succeeded && connect(signaller, SIGNAL(changeBoardLayersSignal(int, bool )),
794 									 slotter, SLOT(changeBoardLayers(int, bool )));
795 
796 	succeeded = succeeded && connect(signaller, SIGNAL(deleteTracesSignal(QSet<ItemBase *> &, QHash<ItemBase *, SketchWidget *> &, QList<long> &, bool, QUndoCommand *)),
797 									 slotter, SLOT(deleteTracesSlot(QSet<ItemBase *> &, QHash<ItemBase *, SketchWidget *> &, QList<long> &, bool, QUndoCommand *)),
798 									 Qt::DirectConnection);
799 
800 	succeeded = succeeded && connect(signaller, SIGNAL(ratsnestConnectSignal(long, const QString &, bool, bool)),
801 									 slotter, SLOT(ratsnestConnect(long, const QString &, bool, bool )),
802 									 Qt::DirectConnection);
803 
804 
805 	succeeded = succeeded && connect(signaller, SIGNAL(updatePartLabelInstanceTitleSignal(long)),
806 									 slotter, SLOT(updatePartLabelInstanceTitleSlot(long)));
807 
808 	succeeded = succeeded && connect(signaller, SIGNAL(changePinLabelsSignal(ItemBase *, bool)),
809 									 slotter, SLOT(changePinLabelsSlot(ItemBase *, bool)));
810 
811 	succeeded = succeeded && connect(signaller, SIGNAL(collectRatsnestSignal(QList<SketchWidget *> &)),
812 									 slotter, SLOT(collectRatsnestSlot(QList<SketchWidget *> &)),
813 									 Qt::DirectConnection);
814 
815 	succeeded = succeeded && connect(signaller, SIGNAL(removeRatsnestSignal(QList<struct ConnectorEdge *> &, QUndoCommand *)),
816 									 slotter, SLOT(removeRatsnestSlot(QList<struct ConnectorEdge *> &, QUndoCommand *)),
817 									 Qt::DirectConnection);
818 
819 	succeeded = succeeded && connect(signaller, SIGNAL(canConnectSignal(Wire *, ItemBase *, bool &)),
820 									 slotter, SLOT(canConnect(Wire *, ItemBase *, bool &)),
821 									 Qt::DirectConnection);
822 
823 	succeeded = succeeded && connect(signaller, SIGNAL(swapStartSignal(SwapThing &, bool)),
824 									 slotter, SLOT(swapStart(SwapThing &, bool)),
825 									 Qt::DirectConnection);
826 
827 	succeeded = succeeded && connect(signaller, SIGNAL(packItemsSignal(int, const QList<long> &, QUndoCommand *, bool)),
828 									 slotter, SLOT(packItems(int, const QList<long> &, QUndoCommand *, bool)));
829 
830 	succeeded = succeeded && connect(signaller, SIGNAL(addSubpartSignal(long, long, bool)), slotter, SLOT(addSubpart(long, long, bool)));
831 
832 	if (!succeeded) {
833 		DebugDialog::debug("connectPair failed");
834 	}
835 
836 }
837 
setCurrentFile(const QString & filename,bool addToRecent,bool setAsLastOpened)838 void MainWindow::setCurrentFile(const QString &filename, bool addToRecent, bool setAsLastOpened) {
839 	setFileName(filename);
840 
841 	if (setAsLastOpened) {
842 		QSettings settings;
843 		settings.setValue("lastOpenSketch",filename);
844 
845         m_useOldSchematic = false;
846 
847         if (m_convertedSchematic) {
848             m_convertedSchematic = false;
849             QString gridSize = QString("%1in").arg(m_schematicGraphicsView->defaultGridSizeInches());
850             m_schematicGraphicsView->setGridSize(gridSize);
851             setCurrentTabIndex(2);
852             m_schematicGraphicsView->resizeLabels();
853             m_schematicGraphicsView->resizeWires();
854             m_schematicGraphicsView->updateWires();
855             setWindowModified(true);
856         }
857         else {
858             QStringList files = settings.value("lastTabList").toStringList();
859             for (int ix = files.count() - 1; ix >= 0; ix--) {
860                 if (files[ix].mid(1) == filename) {
861                     bool ok;
862                     int lastTab = files[ix].left(1).toInt(&ok);
863                     if (ok) {
864                         setCurrentTabIndex(lastTab);
865                     }
866                 }
867             }
868         }
869 	}
870 
871 	updateRaiseWindowAction();
872 	setTitle();
873 
874 	if(addToRecent) {
875 		QSettings settings;
876 		QStringList files = settings.value("recentFileList").toStringList();
877 		files.removeAll(filename);
878 		files.prepend(filename);
879 		while (files.size() > MaxRecentFiles)
880 			files.removeLast();
881 
882 		settings.setValue("recentFileList", files);
883         QTimer::singleShot(1, this, SLOT(updateWelcomeViewRecentList()));
884 
885         // TODO: if lastTab file is not on recent list, remove it from the settings
886 	}
887 
888     foreach (QWidget *widget, QApplication::topLevelWidgets()) {
889         MainWindow *mainWin = qobject_cast<MainWindow *>(widget);
890         if (mainWin)
891             mainWin->updateRecentFileActions();
892     }
893 }
894 
895 
createZoomOptions(SketchAreaWidget * parent)896 void MainWindow::createZoomOptions(SketchAreaWidget* parent) {
897 
898     connect(parent->contentView(), SIGNAL(zoomChanged(double)), this, SLOT(updateZoomSlider(double)));
899     connect(parent->contentView(), SIGNAL(zoomOutOfRange(double)), this, SLOT(updateZoomOptionsNoMatterWhat(double)));
900 }
901 
createRoutingStatusLabel(SketchAreaWidget * parent)902 ExpandingLabel * MainWindow::createRoutingStatusLabel(SketchAreaWidget * parent) {
903 	ExpandingLabel * routingStatusLabel = new ExpandingLabel(m_pcbWidget);
904 
905 	connect(routingStatusLabel, SIGNAL(mousePressSignal(QMouseEvent*)), this, SLOT(routingStatusLabelMousePress(QMouseEvent*)));
906 	connect(routingStatusLabel, SIGNAL(mouseReleaseSignal(QMouseEvent*)), this, SLOT(routingStatusLabelMouseRelease(QMouseEvent*)));
907 
908 	routingStatusLabel->setTextInteractionFlags(Qt::NoTextInteraction);
909 	routingStatusLabel->setCursor(Qt::WhatsThisCursor);
910 	routingStatusLabel->viewport()->setCursor(Qt::WhatsThisCursor);
911 
912 	routingStatusLabel->setObjectName(SketchAreaWidget::RoutingStateLabelName);
913     routingStatusLabel->setToolTip(tr("Click to highlight unconnected parts"));
914 	parent->setRoutingStatusLabel(routingStatusLabel);
915 	RoutingStatus routingStatus;
916 	routingStatus.zero();
917 	routingStatusSlot(qobject_cast<SketchWidget *>(parent->contentView()), routingStatus);
918 	return routingStatusLabel;
919 }
920 
createRotateButton(SketchAreaWidget * parent)921 SketchToolButton *MainWindow::createRotateButton(SketchAreaWidget *parent) {
922 	QList<QAction*> rotateMenuActions;
923 	rotateMenuActions << m_rotate45ccwAct << m_rotate90ccwAct << m_rotate180Act << m_rotate90cwAct << m_rotate45cwAct;
924 	SketchToolButton * rotateButton = new SketchToolButton("Rotate",parent, rotateMenuActions);
925 	rotateButton->setDefaultAction(m_rotate90ccwAct);
926 	rotateButton->setText(tr("Rotate"));
927 
928 	m_rotateButtons << rotateButton;
929 	return rotateButton;
930 }
931 
createShareButton(SketchAreaWidget * parent)932 SketchToolButton *MainWindow::createShareButton(SketchAreaWidget *parent) {
933 	SketchToolButton *shareButton = new SketchToolButton("Share",parent, m_shareOnlineAct);
934 	shareButton->setText(tr("Share"));
935     shareButton->setObjectName("shareProjectButton");
936     shareButton->setEnabledIcon();					// seems to need this to display button icon first time
937     return shareButton;
938 }
939 
createFlipButton(SketchAreaWidget * parent)940 SketchToolButton *MainWindow::createFlipButton(SketchAreaWidget *parent) {
941 	QList<QAction*> flipMenuActions;
942 	flipMenuActions << m_flipHorizontalAct << m_flipVerticalAct;
943 	SketchToolButton *flipButton = new SketchToolButton("Flip",parent, flipMenuActions);
944 	flipButton->setText(tr("Flip"));
945 
946 	m_flipButtons << flipButton;
947 	return flipButton;
948 }
949 
createAutorouteButton(SketchAreaWidget * parent)950 SketchToolButton *MainWindow::createAutorouteButton(SketchAreaWidget *parent) {
951 	SketchToolButton *autorouteButton = new SketchToolButton("Autoroute",parent, m_newAutorouteAct);
952 	autorouteButton->setText(tr("Autoroute"));
953 	autorouteButton->setEnabledIcon();					// seems to need this to display button icon first time
954 
955 	return autorouteButton;
956 }
957 
createOrderFabButton(SketchAreaWidget * parent)958 SketchToolButton *MainWindow::createOrderFabButton(SketchAreaWidget *parent) {
959     SketchToolButton *orderFabButton = new SketchToolButton("Order",parent, m_orderFabAct);
960     orderFabButton->setText(tr("Fabricate"));
961     orderFabButton->setObjectName("orderFabButton");
962     orderFabButton->setEnabledIcon();// seems to need this to display button icon first time
963     connect(orderFabButton, SIGNAL(entered()), this, SLOT(orderFabHoverEnter()));
964     connect(orderFabButton, SIGNAL(left()), this, SLOT(orderFabHoverLeave()));
965 
966 	return orderFabButton;
967 }
968 
createActiveLayerButton(SketchAreaWidget * parent)969 QWidget *MainWindow::createActiveLayerButton(SketchAreaWidget *parent)
970 {
971 	QList<QAction *> actions;
972 	actions << m_activeLayerBothAct << m_activeLayerBottomAct << m_activeLayerTopAct;
973 
974 	m_activeLayerButtonWidget = new QStackedWidget;
975     // m_activeLayerButtonWidget->setMaximumWidth(90);
976     m_activeLayerButtonWidget->setObjectName("activeLayerButton");
977 
978 	SketchToolButton * button = new SketchToolButton("ActiveLayer", parent, actions);
979 	button->setDefaultAction(m_activeLayerBottomAct);
980 	button->setText(tr("Both Layers"));
981 	m_activeLayerButtonWidget->addWidget(button);
982 
983 	button = new SketchToolButton("ActiveLayerB", parent, actions);
984 	button->setDefaultAction(m_activeLayerTopAct);
985 	button->setText(tr("Bottom Layer"));
986 	m_activeLayerButtonWidget->addWidget(button);
987 
988 	button = new SketchToolButton("ActiveLayerT", parent, actions);
989 	button->setDefaultAction(m_activeLayerBothAct);
990 	button->setText(tr("Top Layer"));
991 	m_activeLayerButtonWidget->addWidget(button);
992 
993 	return m_activeLayerButtonWidget;
994 }
995 
createViewFromButton(SketchAreaWidget * parent)996 QWidget *MainWindow::createViewFromButton(SketchAreaWidget *parent)
997 {
998 	QList<QAction *> actions;
999 	actions << m_viewFromAboveAct << m_viewFromBelowAct;
1000 
1001 	m_viewFromButtonWidget = new QStackedWidget;
1002     // m_viewFromButtonWidget->setMaximumWidth(95);
1003     m_viewFromButtonWidget->setObjectName("viewFromButton");
1004 
1005 	SketchToolButton * button = new SketchToolButton("ViewFromT", parent, actions);
1006 	button->setDefaultAction(m_viewFromBelowAct);
1007 	button->setText(tr("View from Above"));
1008     button->setEnabledIcon();					// seems to need this to display button icon first time
1009 
1010 	m_viewFromButtonWidget->addWidget(button);
1011 
1012 	button = new SketchToolButton("ViewFromB", parent, actions);
1013 	button->setDefaultAction(m_viewFromAboveAct);
1014 	button->setText(tr("View from Below"));
1015     button->setEnabledIcon();					// seems to need this to display button icon first time
1016 	m_viewFromButtonWidget->addWidget(button);
1017 
1018 	return m_viewFromButtonWidget;
1019 }
1020 
createNoteButton(SketchAreaWidget * parent)1021 SketchToolButton *MainWindow::createNoteButton(SketchAreaWidget *parent) {
1022 	SketchToolButton *noteButton = new SketchToolButton("Notes",parent, m_addNoteAct);
1023     noteButton->setObjectName("noteButton");
1024 	noteButton->setText(tr("Add a note"));
1025 	noteButton->setEnabledIcon();					// seems to need this to display button icon first time
1026 	return noteButton;
1027 }
1028 
createExportEtchableButton(SketchAreaWidget * parent)1029 SketchToolButton *MainWindow::createExportEtchableButton(SketchAreaWidget *parent) {
1030 	QList<QAction*> actions;
1031 	actions << m_exportEtchablePdfAct << m_exportEtchableSvgAct << m_exportGerberAct;
1032 	SketchToolButton *exportEtchableButton = new SketchToolButton("Diy",parent, actions);
1033     exportEtchableButton->setObjectName("exportButton");
1034 	exportEtchableButton->setDefaultAction(m_exportEtchablePdfAct);
1035 	exportEtchableButton->setText(tr("Export for PCB"));
1036 	exportEtchableButton->setEnabledIcon();				// seems to need this to display button icon first time
1037 	return exportEtchableButton;
1038 }
1039 
createToolbarSpacer(SketchAreaWidget * parent)1040 QWidget *MainWindow::createToolbarSpacer(SketchAreaWidget *parent) {
1041     QFrame *toolbarSpacer = new QFrame(parent);
1042 	QHBoxLayout *spacerLayout = new QHBoxLayout(toolbarSpacer);
1043 	spacerLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding));
1044 
1045 	return toolbarSpacer;
1046 }
1047 
getButtonsForView(ViewLayer::ViewID viewId)1048 QList<QWidget*> MainWindow::getButtonsForView(ViewLayer::ViewID viewId) {
1049 	QList<QWidget*> retval;
1050 	SketchAreaWidget *parent;
1051 	switch(viewId) {
1052 		case ViewLayer::BreadboardView: parent = m_breadboardWidget; break;
1053 		case ViewLayer::SchematicView: parent = m_schematicWidget; break;
1054 		case ViewLayer::PCBView: parent = m_pcbWidget; break;
1055 		default: return retval;
1056 	}
1057 
1058 	switch(viewId) {
1059 		case ViewLayer::BreadboardView:
1060 		case ViewLayer::SchematicView:
1061 			retval << createNoteButton(parent);
1062 		default:
1063 			break;
1064 	}
1065 
1066 	retval << createRotateButton(parent);
1067 	switch (viewId) {
1068 		case ViewLayer::BreadboardView:
1069 			retval << createFlipButton(parent) << createRoutingStatusLabel(parent);
1070 			break;
1071 		case ViewLayer::SchematicView:
1072 			retval << createFlipButton(parent) <<  createAutorouteButton(parent) << createRoutingStatusLabel(parent);
1073 			break;
1074 		case ViewLayer::PCBView:
1075 			retval << createViewFromButton(parent)
1076 				<< createActiveLayerButton(parent)
1077 				<< createAutorouteButton(parent)
1078 				<< createExportEtchableButton(parent)
1079                 << createRoutingStatusLabel(parent)
1080                 ;
1081 
1082 			if (m_orderFabEnabled) {
1083                 m_orderFabButton = createOrderFabButton(parent);
1084 				retval << m_orderFabButton;
1085 			}
1086 
1087 
1088 			break;
1089 		default:
1090 			break;
1091 	}
1092 
1093     retval << createShareButton(parent);
1094 	return retval;
1095 }
1096 
updateZoomSlider(double zoom)1097 void MainWindow::updateZoomSlider(double zoom) {
1098 	m_zoomSlider->setValue(zoom);
1099 }
1100 
currentSketchArea()1101 SketchAreaWidget *MainWindow::currentSketchArea() {
1102     if (m_currentGraphicsView == NULL) return NULL;
1103 
1104 	return dynamic_cast<SketchAreaWidget*>(m_currentGraphicsView->parent());
1105 }
1106 
updateZoomOptionsNoMatterWhat(double zoom)1107 void MainWindow::updateZoomOptionsNoMatterWhat(double zoom) {
1108 	m_zoomSlider->setValue(zoom);
1109 }
1110 
updateViewZoom(double newZoom)1111 void MainWindow::updateViewZoom(double newZoom) {
1112 	m_comboboxChanged = true;
1113 	if(m_currentGraphicsView) m_currentGraphicsView->absoluteZoom(newZoom);
1114 }
1115 
1116 
createStatusBar()1117 void MainWindow::createStatusBar()
1118 {
1119     m_statusBar->showMessage(tr("Ready"));
1120 }
1121 
tabWidget_currentChanged(int index)1122 void MainWindow::tabWidget_currentChanged(int index) {
1123 	SketchAreaWidget * widgetParent = dynamic_cast<SketchAreaWidget *>(currentTabWidget());
1124 	if (widgetParent == NULL) return;
1125 
1126 	m_currentWidget = widgetParent;
1127 
1128 	if (m_locationLabel) {
1129 		m_locationLabel->setText("");
1130 	}
1131 
1132 	QStatusBar *sb = statusBar();
1133 	connect(sb, SIGNAL(messageChanged(const QString &)), this, SLOT(showStatusMessage(const QString &)));
1134 	widgetParent->addStatusBar(m_statusBar);
1135 	if(sb != m_statusBar) sb->hide();
1136 
1137 	if (m_breadboardGraphicsView) m_breadboardGraphicsView->setCurrent(false);
1138 	if (m_schematicGraphicsView) m_schematicGraphicsView->setCurrent(false);
1139 	if (m_pcbGraphicsView) m_pcbGraphicsView->setCurrent(false);
1140 
1141 	SketchWidget *widget = qobject_cast<SketchWidget *>(widgetParent->contentView());
1142 
1143 	if(m_currentGraphicsView) {
1144 		m_currentGraphicsView->saveZoom(m_zoomSlider->value());
1145 		disconnect(
1146 			m_currentGraphicsView,
1147 			SIGNAL(selectionChangedSignal()),
1148 			this,
1149 			SLOT(updateTransformationActions())
1150 		);
1151 	}
1152 	m_currentGraphicsView = widget;
1153 
1154     if (m_programView) {
1155         hideShowProgramMenu();
1156     }
1157 
1158 	hideShowTraceMenu();
1159     updateEditMenu();
1160 
1161     if (m_showBreadboardAct) {
1162 	    QList<QAction *> actions;
1163 		if (m_welcomeView) actions << m_showWelcomeAct;
1164 	    actions << m_showBreadboardAct << m_showSchematicAct << m_showPCBAct;
1165         if (m_programView) actions << m_showProgramAct;
1166 	    setActionsIcons(index, actions);
1167     }
1168 
1169 	if (widget == NULL) {
1170         m_firstTimeHelpAct->setEnabled(false);
1171         return;
1172     }
1173 
1174 	m_zoomSlider->setValue(m_currentGraphicsView->retrieveZoom());
1175     FirstTimeHelpDialog::setViewID(m_currentGraphicsView->viewID());
1176     m_firstTimeHelpAct->setEnabled(true);
1177 
1178 	connect(
1179 		m_currentGraphicsView,					// don't connect directly to the scene here, connect to the widget's signal
1180 		SIGNAL(selectionChangedSignal()),
1181 		this,
1182 		SLOT(updateTransformationActions())
1183 	);
1184 
1185 	updateActiveLayerButtons();
1186 
1187 	m_currentGraphicsView->setCurrent(true);
1188 
1189 	// !!!!!! hack alert  !!!!!!!
1190 	// this item update loop seems to deal with a qt update bug:
1191 	// if one view is visible and you change something in another view,
1192 	// the change might not appear when you switch views until you move the item in question
1193 	foreach(QGraphicsItem * item, m_currentGraphicsView->items()) {
1194 		item->update();
1195 	}
1196 
1197 	updateLayerMenu(true);
1198 	updateTraceMenu();
1199 	updateTransformationActions();
1200 
1201 	setTitle();
1202 
1203     if (m_infoView) {
1204 	    m_currentGraphicsView->updateInfoView();
1205     }
1206 
1207 	// update issue with 4.5.1?: is this still valid (4.6.x?)
1208 	m_currentGraphicsView->updateConnectors();
1209 
1210     QTimer::singleShot(10, this, SLOT(initZoom()));
1211 
1212 }
1213 
setActionsIcons(int index,QList<QAction * > & actions)1214 void MainWindow::setActionsIcons(int index, QList<QAction *> & actions) {
1215 	for (int i = 0; i < actions.count(); i++) {
1216         // DebugDialog::debug(QString("setting icon %1 %2").arg(i).arg(index == i));
1217         // setting the icons seems to be broken in Qt 5, so use checkMarks instead
1218         // note that we used dots instead of checkMarks originally because
1219         // we hoped it was clearer that the items were mutually exclusive
1220         // note that using QIcon() instead of m_emptyIcon does no good
1221         // (and we used the m_emptyIcon to preserve the space at the left)
1222         // actions[i]->setIcon(index == i ? m_dotIcon : m_emptyIcon);
1223         actions[i]->setChecked(index == i);
1224 	}
1225 }
1226 
closeEvent(QCloseEvent * event)1227 void MainWindow::closeEvent(QCloseEvent *event) {
1228 	if (m_dontClose) {
1229 		event->ignore();
1230 		return;
1231 	}
1232 
1233 	if (m_programWindow) {
1234 		m_programWindow->close();
1235 		if (m_programWindow->isVisible()) {
1236 			event->ignore();
1237 			return;
1238 		}
1239 	}
1240 
1241 	if (!m_closeSilently) {
1242 		bool whatWithAliens = whatToDoWithAlienFiles();
1243 		bool discard;
1244 		if(!beforeClosing(true, discard) || !whatWithAliens ||!m_binManager->beforeClosing()) {
1245 			event->ignore();
1246 			return;
1247 		}
1248 
1249 		if(whatWithAliens && m_binManager->hasAlienParts()) {
1250 			m_binManager->createIfMyPartsNotExists();
1251 		}
1252 	}
1253 
1254 	//DebugDialog::debug(QString("top level windows: %1").arg(QApplication::topLevelWidgets().size()));
1255 	/*
1256 	foreach (QWidget * widget, QApplication::topLevelWidgets()) {
1257 		QMenu * menu = qobject_cast<QMenu *>(widget);
1258 		if (menu != NULL) {
1259 			continue;				// QMenus are always top level widgets, even if they have parents...
1260 		}
1261 		DebugDialog::debug(QString("top level widget %1 %2 %3")
1262 			.arg(widget->metaObject()->className())
1263 			.arg(widget->windowTitle())
1264 			.arg(widget->toolTip())
1265 			);
1266 	}
1267 	*/
1268 
1269 	m_closing = true;
1270 
1271 	int count = 0;
1272 	foreach (QWidget *widget, QApplication::topLevelWidgets()) {
1273 		if (widget == this) continue;
1274 		if (qobject_cast<QMainWindow *>(widget) == NULL) continue;
1275 
1276 		count++;
1277 	}
1278 
1279 	if (count == 0) {
1280 		DebugDialog::closeDebug();
1281 	}
1282 
1283 	QSettings settings;
1284 	settings.setValue(m_settingsPrefix + "state",saveState());
1285 	settings.setValue(m_settingsPrefix + "geometry",saveGeometry());
1286 
1287     saveLastTabList();
1288 
1289 	QMainWindow::closeEvent(event);
1290 }
1291 
saveLastTabList()1292 void MainWindow::saveLastTabList() {
1293     QSettings settings;
1294     QStringList files = settings.value("lastTabList").toStringList();
1295     for (int ix = files.count() - 1; ix >= 0; ix--) {
1296         if (files[ix].mid(1) == m_fwFilename) files.removeAt(ix);
1297     }
1298     files.prepend(QString("%1%2").arg(this->currentTabIndex()).arg(m_fwFilename));
1299     while (files.size() > MaxRecentFiles)
1300         files.removeLast();
1301 
1302     settings.setValue("lastTabList", files);
1303 }
1304 
whatToDoWithAlienFiles()1305 bool MainWindow::whatToDoWithAlienFiles() {
1306 	if (m_alienFiles.size() > 0) {
1307         QString basename = QFileInfo(m_fwFilename).fileName();
1308 		QMessageBox::StandardButton reply;
1309 		reply = QMessageBox::question(this, tr("Save %1").arg(basename),
1310 						 m_alienPartsMsg
1311 						 .arg(basename),
1312 						 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
1313 		// TODO: translate button text
1314 		if (reply == QMessageBox::Yes) {
1315 			return true;
1316 		} else if (reply == QMessageBox::No) {
1317 			foreach(QString pathToRemove, m_alienFiles) {
1318 				QFile::remove(pathToRemove);
1319 			}
1320 			m_alienFiles.clear();
1321 			recoverBackupedFiles();
1322 
1323 			emit alienPartsDismissed();
1324 			return true;
1325 		}
1326 		else {
1327 			return false;
1328 		}
1329 	} else {
1330 		return true;
1331 	}
1332 }
1333 
acceptAlienFiles()1334 void MainWindow::acceptAlienFiles() {
1335 	m_alienFiles.clear();
1336 }
1337 
eventFilter(QObject * object,QEvent * event)1338 bool MainWindow::eventFilter(QObject *object, QEvent *event) {
1339 	if (object == this &&
1340 		(event->type() == QEvent::KeyPress
1341 		// || event->type() == QEvent::KeyRelease
1342 		|| event->type() == QEvent::ShortcutOverride))
1343 	{
1344 		//DebugDialog::debug(QString("event filter %1").arg(event->type()) );
1345 		updatePartMenu();
1346 		updateTraceMenu();
1347 
1348 		// On the mac, the first time the delete key is pressed, to be used as a shortcut for QAction m_deleteAct,
1349 		// for some reason, the enabling of the m_deleteAct in UpdateEditMenu doesn't "take" until the next time the event loop is processed
1350 		// Thereafter, the delete key works as it should.
1351 		// So this call to processEvents() makes sure m_deleteAct is enabled.
1352 		ProcessEventBlocker::processEvents();
1353 	}
1354 
1355 #if defined(Q_OS_MAC) && (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
1356 
1357     // Need to process Backspace on Mac to workaround bug in Qt5
1358     // See http://qt-project.org/forums/viewthread/36174
1359 
1360     if (event->type() == QEvent::KeyPress) {
1361         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
1362 
1363         if (keyEvent->key() == Qt::Key_Backspace) {
1364             Qt::KeyboardModifiers modifiers = keyEvent->modifiers();
1365             if (modifiers == Qt::NoModifier) {
1366                 doDelete();
1367                 return true;
1368             }
1369             if (modifiers == Qt::AltModifier) {
1370                 doDeleteMinus();
1371                 return true;
1372             }
1373         }
1374     }
1375 #endif
1376 
1377 	return QMainWindow::eventFilter(object, event);
1378 }
1379 
untitledFileName()1380 const QString MainWindow::untitledFileName() {
1381 	return UntitledSketchName;
1382 }
1383 
untitledFileCount()1384 int &MainWindow::untitledFileCount() {
1385 	return UntitledSketchIndex;
1386 }
1387 
fileExtension()1388 const QString MainWindow::fileExtension() {
1389 	return FritzingBundleExtension;
1390 }
1391 
defaultSaveFolder()1392 const QString MainWindow::defaultSaveFolder() {
1393 	return FolderUtils::openSaveFolder();
1394 }
1395 
undoStackIsEmpty()1396 bool MainWindow::undoStackIsEmpty() {
1397 	return m_undoStack->count() == 0;
1398 }
1399 
setInfoViewOnHover(bool infoViewOnHover)1400 void MainWindow::setInfoViewOnHover(bool infoViewOnHover) {
1401 	m_breadboardGraphicsView->setInfoViewOnHover(infoViewOnHover);
1402 	m_schematicGraphicsView->setInfoViewOnHover(infoViewOnHover);
1403 	m_pcbGraphicsView->setInfoViewOnHover(infoViewOnHover);
1404 
1405 	m_binManager->setInfoViewOnHover(infoViewOnHover);
1406 }
1407 
loadBundledSketch(const QString & fileName,bool addToRecent,bool setAsLastOpened,bool checkObsolete)1408 void MainWindow::loadBundledSketch(const QString &fileName, bool addToRecent, bool setAsLastOpened, bool checkObsolete) {
1409 
1410     QString error;
1411 	if(!FolderUtils::unzipTo(fileName, m_fzzFolder, error)) {
1412 		FMessageBox::warning(
1413 			this,
1414 			tr("Fritzing"),
1415 			tr("Unable to open '%1': %2").arg(fileName).arg(error)
1416 		);
1417 
1418 		return;
1419 	}
1420 
1421 	QDir dir(m_fzzFolder);
1422     QString binFileName = dir.absoluteFilePath(QFileInfo(BinManager::TempPartsBinTemplateLocation).fileName());
1423     m_binManager->setTempPartsBinLocation(binFileName);
1424     FolderUtils::copyBin(binFileName, BinManager::TempPartsBinTemplateLocation);
1425 
1426 	QStringList namefilters;
1427 	namefilters << "*"+FritzingSketchExtension;
1428 	QFileInfoList entryInfoList = dir.entryInfoList(namefilters);
1429 	if (entryInfoList.count() == 0) {
1430 		FMessageBox::warning(
1431 			this,
1432 			tr("Fritzing"),
1433 			tr("No Sketch found in '%1'").arg(fileName)
1434 		);
1435 
1436 		return;
1437 	}
1438 
1439 	QFileInfo sketchInfo = entryInfoList[0];
1440 
1441 	QString sketchName = dir.absoluteFilePath(sketchInfo.fileName());
1442 
1443     namefilters.clear();
1444     namefilters << "*" + FritzingPartExtension;
1445     entryInfoList = dir.entryInfoList(namefilters);
1446 
1447     namefilters.clear();
1448 	namefilters << "*.svg";
1449 	QFileInfoList svgEntryInfoList = dir.entryInfoList(namefilters);
1450 
1451     m_addedToTemp = false;
1452 
1453     QList<MissingSvgInfo> missing;
1454     QList<ModelPart *> missingModelParts;
1455 
1456     foreach (QFileInfo fzpInfo, entryInfoList) {
1457         QFile file(dir.absoluteFilePath(fzpInfo.fileName()));
1458         if (!file.open(QFile::ReadOnly)) {
1459             DebugDialog::debug(QString("unable to open %1: %2").arg(file.fileName()));
1460             continue;
1461         }
1462 
1463         // TODO: could be more efficient by using a streamreader
1464         QString fzp = file.readAll();
1465         file.close();
1466 
1467         QString moduleID = TextUtils::parseForModuleID(fzp);
1468         if (moduleID.isEmpty()) {
1469             DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
1470             DebugDialog::debug(QString("unable to find module id in %1: %2").arg(file.fileName()).arg(fzp));
1471             DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
1472             continue;
1473         }
1474 
1475         ModelPart * mp = m_referenceModel->retrieveModelPart(moduleID);
1476         if (mp == NULL) {
1477             QDomDocument doc;
1478             if (!doc.setContent(fzp)) {
1479                 DebugDialog::debug(QString("unable to parse fzp in %1: %2").arg(file.fileName()).arg(fzp));
1480                 continue;
1481             }
1482 
1483             mp = copyToPartsFolder(fzpInfo, false, PartFactory::folderPath(), "contrib");
1484             if (mp == NULL) {
1485                 DebugDialog::debug(QString("unable to create model part in %1: %2").arg(file.fileName()).arg(fzp));
1486                 continue;
1487             }
1488 
1489             QDomElement root = doc.documentElement();
1490 	        QDomElement views = root.firstChildElement("views");
1491 	        QDomElement view = views.firstChildElement();
1492             while (!view.isNull()) {
1493                 QDomElement layers = view.firstChildElement("layers");
1494                 QString path = layers.attribute("image", "");
1495                 if (!path.isEmpty()) {
1496                     bool copied = copySvg(path, svgEntryInfoList);
1497                     if (!copied) {
1498                         DebugDialog::debug(QString("missing svg %1").arg(path));
1499                         MissingSvgInfo msi;
1500                         msi.equal = false;
1501                         msi.modelPart = mp;
1502                         missingModelParts << mp;
1503                         msi.requestedPath = path;
1504                         ViewLayer::ViewID viewID = ViewLayer::idFromXmlName(view.tagName());
1505                         QDomElement connectors = root.firstChildElement("connectors");
1506                         QDomElement connector = connectors.firstChildElement("connector");
1507                         while (!connector.isNull()) {
1508                             QString id, terminalID;
1509                             ViewLayer::getConnectorSvgIDs(connector, viewID, id, terminalID);
1510                             if (!id.isEmpty()) {
1511                                 msi.connectorSvgIds.append(id);
1512                             }
1513                             connector = connector.nextSiblingElement("connector");
1514                         }
1515                         missing << msi;
1516                     }
1517                 }
1518                 view = view.nextSiblingElement();
1519             }
1520 
1521         	mp->setFzz(true);
1522         }
1523 
1524         if (!missingModelParts.contains(mp)) {
1525 		    m_binManager->addToTempPartsBin(mp);
1526             m_addedToTemp = true;
1527         }
1528     }
1529 
1530     qSort(missing.begin(), missing.end(), byConnectorCount);
1531     foreach (MissingSvgInfo msi, missing) {
1532         if (msi.equal) {
1533             // two or more parts have the same number of connectors--so we can't figure out how to assign them
1534             continue;
1535         }
1536 
1537         int slash = msi.requestedPath.indexOf("/");
1538         QString suffix = msi.requestedPath.mid(slash + 1);
1539         QString prefix = msi.requestedPath.left(slash);
1540         for (int jx = svgEntryInfoList.count() - 1; jx >= 0; jx--) {
1541             QFileInfo svgInfo = svgEntryInfoList.at(jx);
1542             if (!svgInfo.fileName().contains(prefix, Qt::CaseInsensitive)) continue;
1543 
1544             QFile svgfile(svgInfo.absoluteFilePath());
1545             QDomDocument svgDoc;
1546             if (!svgDoc.setContent(&svgfile)) continue;
1547 
1548             QList<QDomElement> elements;
1549             QDomElement root = svgDoc.documentElement();
1550             TextUtils::findElementsWithAttribute(root, "id", elements);
1551             if (elements.count() < msi.connectorSvgIds.count()) continue;
1552 
1553             QStringList ids;
1554             foreach (QDomElement element, elements) {
1555                 ids << element.attribute("id");
1556             }
1557 
1558             bool allGood = true;
1559             foreach (QString id, msi.connectorSvgIds) {
1560                 if (!ids.contains(id)) {
1561                     allGood = false;
1562                     break;
1563                 }
1564             }
1565 
1566             if (!allGood) continue;
1567 
1568             QString destPath = copyToSvgFolder(svgInfo, false, PartFactory::folderPath(), "contrib");   // copy file with original name
1569             if (!destPath.isEmpty()) {
1570                 QFileInfo destInfo(destPath);
1571                 QFile file(destPath);
1572                 DebugDialog::debug(QString("found missing %1").arg(destPath));
1573                 FolderUtils::slamCopy(file, destInfo.absoluteDir().absoluteFilePath(suffix));         // make another copy that has the name used in the fzp file
1574                 svgEntryInfoList.removeAt(jx);
1575                 break;
1576             }
1577         }
1578     }
1579 
1580     foreach (ModelPart * mp, missingModelParts) {
1581 		m_binManager->addToTempPartsBin(mp);
1582         m_addedToTemp = true;
1583     }
1584 
1585 	if (!m_addedToTemp) {
1586 		m_binManager->hideTempPartsBin();
1587 	}
1588 
1589 	// the bundled itself
1590 	this->mainLoad(sketchName, "", checkObsolete);
1591 	setCurrentFile(fileName, addToRecent, setAsLastOpened);
1592 }
1593 
copySvg(const QString & path,QFileInfoList & svgEntryInfoList)1594 bool MainWindow::copySvg(const QString & path, QFileInfoList & svgEntryInfoList)
1595 {
1596     int slash = path.indexOf("/");
1597     QString subpath = path.mid(slash + 1);
1598     bool gotOne = false;
1599     for (int jx = svgEntryInfoList.count() - 1; jx >= 0; jx--) {
1600         QFileInfo svgInfo = svgEntryInfoList.at(jx);
1601         if (svgInfo.fileName().contains(subpath)) {
1602             copyToSvgFolder(svgInfo, false, PartFactory::folderPath(), "contrib");
1603             svgEntryInfoList.removeAt(jx);
1604             // jrc 30 oct 2012: not sure why we can't just return at this point--can there be other matching files?
1605             gotOne = true;
1606         }
1607     }
1608 
1609     if (gotOne) return true;
1610 
1611     // deal with a bug in which all the svg files exist in the fzz but the fz file points to the wrong name
1612     // most of the time it's just a GUID difference
1613 
1614     DebugDialog::debug(QString("svg matching fz path %1 not found").arg(path));
1615     int guidix = GuidMatcher.lastIndexIn(subpath);
1616     if (guidix < 0) return false;
1617 
1618     QString originalGuid = GuidMatcher.cap(0);
1619     QString tryPath = subpath;
1620     tryPath.replace(guidix, originalGuid.length(), "%%%%");
1621     for (int jx = svgEntryInfoList.count() - 1; jx >= 0; jx--) {
1622         QFileInfo svgInfo = svgEntryInfoList.at(jx);
1623         QString tempPath = svgInfo.fileName();
1624         guidix = GuidMatcher.lastIndexIn(tempPath);
1625         if (guidix < 0) continue;
1626 
1627         tempPath.replace(guidix, GuidMatcher.cap(0).length(), "%%%%");
1628         if (!tempPath.contains(tryPath)) continue;
1629 
1630         QString destPath = copyToSvgFolder(svgInfo, false, PartFactory::folderPath(), "contrib");
1631         if (!destPath.isEmpty()) {
1632             QFile file(destPath);
1633             guidix = GuidMatcher.lastIndexIn(destPath);
1634             destPath.replace(guidix, GuidMatcher.cap(0).length(), originalGuid);
1635             FolderUtils::slamCopy(file, destPath);
1636             DebugDialog::debug(QString("found matching svg %1").arg(destPath));
1637             svgEntryInfoList.removeAt(jx);
1638             return true;
1639         }
1640     }
1641 
1642     return false;
1643 
1644 }
1645 
1646 
loadBundledNonAtomicEntity(const QString & fileName,Bundler * bundler,bool addToBin,bool dontAsk)1647 bool MainWindow::loadBundledNonAtomicEntity(const QString &fileName, Bundler* bundler, bool addToBin, bool dontAsk) {
1648 	QDir destFolder = QDir::temp();
1649 
1650 	FolderUtils::createFolderAnCdIntoIt(destFolder, TextUtils::getRandText());
1651 	QString unzipDirPath = destFolder.path();
1652 
1653     QString error;
1654 	if(!FolderUtils::unzipTo(fileName, unzipDirPath, error)) {
1655 		FMessageBox::warning(
1656 			this,
1657 			tr("Fritzing"),
1658 			tr("Unable to open shareable '%1': %2").arg(fileName).arg(error)
1659 		);
1660 
1661 		// gotta return now, or loadBundledSketchAux will crash
1662 		return false;
1663 	}
1664 
1665 	QDir unzipDir(unzipDirPath);
1666 
1667 	if (bundler->preloadBundledAux(unzipDir, dontAsk)) {
1668 		QList<ModelPart*> mps = moveToPartsFolder(unzipDir,this,addToBin,true,FolderUtils::getUserDataStorePath("parts"), "contrib", false);
1669 		// the bundled itself
1670 		bundler->loadBundledAux(unzipDir,mps);
1671 	}
1672 
1673 	FolderUtils::rmdir(unzipDirPath);
1674 
1675     return true;
1676 }
1677 
1678 /*
1679 void MainWindow::loadBundledPartFromWeb() {
1680 	QMainWindow *mw = new QMainWindow();
1681 	QString url = "http://localhost:8081/parts_gen/choose";
1682 	QWebView *view = new QWebView(mw);
1683 	mw->setCentralWidget(view);
1684 	view->setUrl(QUrl(url));
1685 	mw->show();
1686 	mw->raise();
1687 }
1688 */
1689 
loadBundledPart(const QString & fileName,bool addToBin)1690 ModelPart* MainWindow::loadBundledPart(const QString &fileName, bool addToBin) {
1691 	QDir destFolder = QDir::temp();
1692 
1693 	FolderUtils::createFolderAnCdIntoIt(destFolder, TextUtils::getRandText());
1694 	QString unzipDirPath = destFolder.path();
1695 
1696     QString error;
1697 	if(!FolderUtils::unzipTo(fileName, unzipDirPath, error)) {
1698 		FMessageBox::warning(
1699 			this,
1700 			tr("Fritzing"),
1701 			tr("Unable to open shareable part '%1': %2").arg(fileName).arg(error)
1702 		);
1703 		return NULL;
1704 	}
1705 
1706 	QDir unzipDir(unzipDirPath);
1707 
1708     QList<ModelPart*> mps;
1709     try {
1710 	    mps = moveToPartsFolder(unzipDir, this, addToBin, true, FolderUtils::getUserDataStorePath("parts"), "user", true);
1711     }
1712     catch (const QString & msg) {
1713 		FMessageBox::warning(
1714 			this,
1715 			tr("Fritzing"),
1716 			msg
1717 		);
1718         return NULL;
1719     }
1720 
1721 	if (mps.count() != 1) {
1722 		// if this fails, that means that the bundled was wrong
1723 		FMessageBox::warning(
1724 			this,
1725 			tr("Fritzing"),
1726 			tr("Unable to load part from '%1'").arg(fileName)
1727 		);
1728 		return NULL;
1729 	}
1730 
1731 	FolderUtils::rmdir(unzipDirPath);
1732 
1733 	return mps[0];
1734 }
1735 
saveBundledPart(const QString & moduleId)1736 void MainWindow::saveBundledPart(const QString &moduleId) {
1737 	QString modIdToExport;
1738 	ModelPart* mp;
1739 
1740 	if(moduleId.isEmpty()) {
1741 		if (m_currentGraphicsView == NULL) return;
1742 		PaletteItem *selectedPart = m_currentGraphicsView->getSelectedPart();
1743 		mp = selectedPart->modelPart();
1744 		modIdToExport = mp->moduleID();
1745 	} else {
1746 		modIdToExport = moduleId;
1747 		mp = m_referenceModel->retrieveModelPart(moduleId);
1748 	}
1749 	QString partTitle = mp->title();
1750 
1751 	QString fileExt;
1752 	QString path = defaultSaveFolder()+"/"+partTitle+FritzingBundledPartExtension;
1753 	QString bundledFileName = FolderUtils::getSaveFileName(
1754 			this,
1755 			tr("Specify a file name"),
1756 			path,
1757 			tr("Fritzing Part (*%1)").arg(FritzingBundledPartExtension),
1758 			&fileExt
1759 		  );
1760 
1761 	if (bundledFileName.isEmpty()) return; // Cancel pressed
1762 
1763 	if(!alreadyHasExtension(bundledFileName, FritzingBundledPartExtension)) {
1764 		bundledFileName += FritzingBundledPartExtension;
1765 	}
1766 
1767 	QDir destFolder = QDir::temp();
1768 
1769 	FolderUtils::createFolderAnCdIntoIt(destFolder, TextUtils::getRandText());
1770 	QString dirToRemove = destFolder.path();
1771 
1772 	QString aux = QFileInfo(bundledFileName).fileName();
1773 	QString destPartPath = // remove the last "z" from the extension
1774 			destFolder.path()+"/"+aux.left(aux.size()-1);
1775 	DebugDialog::debug("saving part temporarily to "+destPartPath);
1776 
1777 	//bool wasModified = isWindowModified();
1778 	//QString prevFileName = m_fileName;
1779 	//saveAsAux(destPartPath);
1780 	//m_fileName = prevFileName;
1781 	//setWindowModified(wasModified);
1782 	setTitle();
1783 
1784 	saveBundledAux(mp, destFolder);
1785 
1786     QStringList skipSuffixes;
1787 	if(!FolderUtils::createZipAndSaveTo(destFolder, bundledFileName, skipSuffixes)) {
1788 		FMessageBox::warning(
1789 			this,
1790 			tr("Fritzing"),
1791 			tr("Unable to export %1 to shareable sketch").arg(bundledFileName)
1792 		);
1793 	}
1794 
1795 	FolderUtils::rmdir(dirToRemove);
1796 }
1797 
saveBundledAux(ModelPart * mp,const QDir & destFolder)1798 QStringList MainWindow::saveBundledAux(ModelPart *mp, const QDir &destFolder) {
1799 	QStringList names;
1800 	QString partPath = mp->path();
1801 	QFile file(partPath);
1802 	QString fn = ZIP_PART + QFileInfo(partPath).fileName();
1803 	names << fn;
1804     FolderUtils::slamCopy(file, destFolder.path()+"/"+fn);
1805 
1806 	QList<ViewLayer::ViewID> viewIDs;
1807 	viewIDs << ViewLayer::IconView << ViewLayer::BreadboardView << ViewLayer::SchematicView << ViewLayer::PCBView;
1808 	foreach (ViewLayer::ViewID viewID, viewIDs) {
1809 		QString basename = mp->hasBaseNameFor(viewID);
1810 		if (basename.isEmpty()) continue;
1811 
1812 		QString filename = PartFactory::getSvgFilename(mp, basename, true, true);
1813 		if (filename.isEmpty()) continue;
1814 
1815 		QFile file(filename);
1816 		basename.replace("/", ".");
1817 		QString fn = ZIP_SVG + basename;
1818 		names << fn;
1819         FolderUtils::slamCopy(file, destFolder.path()+"/"+fn);
1820 	}
1821 
1822 	return names;
1823 }
1824 
moveToPartsFolder(QDir & unzipDir,MainWindow * mw,bool addToBin,bool addToAlien,const QString & prefixFolder,const QString & destFolder,bool importingSinglePart)1825 QList<ModelPart*> MainWindow::moveToPartsFolder(QDir &unzipDir, MainWindow* mw, bool addToBin, bool addToAlien, const QString & prefixFolder, const QString &destFolder, bool importingSinglePart) {
1826 	QStringList namefilters;
1827 	QList<ModelPart*> retval;
1828 
1829 	if (mw == NULL) {
1830 		throw tr("MainWindow::moveToPartsFolder mainwindow missing");
1831 	}
1832 
1833     namefilters.clear();
1834 	namefilters << ZIP_PART+"*";
1835     QList<QFileInfo> partEntryInfoList = unzipDir.entryInfoList(namefilters);
1836 
1837     if (importingSinglePart && partEntryInfoList.count() > 0) {
1838         // TODO use a stream reader
1839         QFile file(partEntryInfoList[0].absoluteFilePath());
1840         file.open(QFile::ReadOnly);
1841         QString fzp = file.readAll();
1842         file.close();
1843         QString moduleID = TextUtils::parseForModuleID(fzp);
1844         if (!moduleID.isEmpty() && m_referenceModel->retrieveModelPart(moduleID) != NULL) {
1845             throw tr("There is already a part with id '%1' loaded into Fritzing.").arg(moduleID);
1846         }
1847     }
1848 
1849 
1850 	namefilters.clear();
1851     namefilters << ZIP_SVG+"*";
1852 	foreach(QFileInfo file, unzipDir.entryInfoList(namefilters)) { // svg files
1853 		//DebugDialog::debug("unzip svg " + file.absoluteFilePath());
1854 		mw->copyToSvgFolder(file, addToAlien, prefixFolder, destFolder);
1855 	}
1856 
1857 
1858 	foreach(QFileInfo file, partEntryInfoList) { // part files
1859 		//DebugDialog::debug("unzip part " + file.absoluteFilePath());
1860         ModelPart * mp = mw->copyToPartsFolder(file, addToAlien, prefixFolder, destFolder);
1861 		retval << mp;
1862         if (addToBin) {
1863             // should only be here when adding single new part
1864 		    m_binManager->addToMyParts(mp);
1865 	    }
1866 	}
1867 
1868 
1869 
1870 	return retval;
1871 }
1872 
copyToSvgFolder(const QFileInfo & file,bool addToAlien,const QString & prefixFolder,const QString & destFolder)1873 QString MainWindow::copyToSvgFolder(const QFileInfo& file, bool addToAlien, const QString & prefixFolder, const QString &destFolder) {
1874 	QFile svgfile(file.filePath());
1875 	// let's make sure that we remove just the suffix
1876 	QString fileName = file.fileName().remove(QRegExp("^"+ZIP_SVG));
1877 	QString viewFolder = fileName.left(fileName.indexOf("."));
1878 	fileName.remove(0, viewFolder.length() + 1);
1879 
1880 	QString destFilePath =
1881 		prefixFolder+"/svg/"+destFolder+"/"+viewFolder+"/"+fileName;
1882 
1883 	backupExistingFileIfExists(destFilePath);
1884 	if(FolderUtils::slamCopy(svgfile, destFilePath)) {
1885 		if (addToAlien) {
1886 			m_alienFiles << destFilePath;
1887 		}
1888         return destFilePath;
1889 	}
1890 
1891     return "";
1892 }
1893 
copyToPartsFolder(const QFileInfo & file,bool addToAlien,const QString & prefixFolder,const QString & destFolder)1894 ModelPart* MainWindow::copyToPartsFolder(const QFileInfo& file, bool addToAlien, const QString & prefixFolder, const QString &destFolder) {
1895 	QFile partfile(file.filePath());
1896 	// let's make sure that we remove just the suffix
1897 	QString destFilePath =
1898 		prefixFolder+"/"+destFolder+"/"+file.fileName().remove(QRegExp("^"+ZIP_PART));
1899 
1900 	backupExistingFileIfExists(destFilePath);
1901 	if(FolderUtils::slamCopy(partfile, destFilePath)) {
1902 		if (addToAlien) {
1903 			m_alienFiles << destFilePath;
1904 			m_alienPartsMsg = tr("Do you want to keep the imported parts?");
1905 		}
1906 	}
1907 	ModelPart *mp = m_referenceModel->loadPart(destFilePath, true);
1908 	mp->setAlien(true);
1909 
1910 	return mp;
1911 }
1912 
binSaved(bool hasPartsFromBundled)1913 void MainWindow::binSaved(bool hasPartsFromBundled) {
1914 	if(hasPartsFromBundled) {
1915 		// the bin will need those parts, so just keep them
1916 		m_alienFiles.clear();
1917 		resetTempFolder();
1918 	}
1919 }
1920 
1921 #undef ZIP_PART
1922 #undef ZIP_SVG
1923 
1924 
backupExistingFileIfExists(const QString & destFilePath)1925 void MainWindow::backupExistingFileIfExists(const QString &destFilePath) {
1926 	if(QFileInfo(destFilePath).exists()) {
1927 		if(m_tempDir.path() == ".") {
1928 			m_tempDir = QDir::temp();
1929 			FolderUtils::createFolderAnCdIntoIt(m_tempDir, TextUtils::getRandText());
1930 			DebugDialog::debug("debug folder for overwritten files: "+m_tempDir.path());
1931 		}
1932 
1933 		QString fileBackupName = QFileInfo(destFilePath).fileName();
1934 		m_filesReplacedByAlienOnes << destFilePath;
1935 		QFile file(destFilePath);
1936 		bool alreadyExists = file.exists();
1937 		FolderUtils::slamCopy(file, m_tempDir.path()+"/"+fileBackupName);
1938 
1939 		if(alreadyExists) {
1940 			file.remove(destFilePath);
1941 		}
1942 	}
1943 }
1944 
recoverBackupedFiles()1945 void MainWindow::recoverBackupedFiles() {
1946 	foreach(QString originalFilePath, m_filesReplacedByAlienOnes) {
1947 		QFile file(m_tempDir.path()+"/"+QFileInfo(originalFilePath).fileName());
1948 		if(file.exists(originalFilePath)) {
1949 			file.remove();
1950 		}
1951 		FolderUtils::slamCopy(file, originalFilePath);
1952 	}
1953 	resetTempFolder();
1954 }
1955 
resetTempFolder()1956 void MainWindow::resetTempFolder() {
1957 	if(m_tempDir.path() != ".") {
1958 		FolderUtils::rmdir(m_tempDir);
1959 		m_tempDir = QDir::temp();
1960 	}
1961 	m_filesReplacedByAlienOnes.clear();
1962 }
1963 
routingStatusSlot(SketchWidget * sketchWidget,const RoutingStatus & routingStatus)1964 void MainWindow::routingStatusSlot(SketchWidget * sketchWidget, const RoutingStatus & routingStatus) {
1965 	m_routingStatus = routingStatus;
1966 	QString theText;
1967 	if (routingStatus.m_netCount == 0) {
1968 		theText = tr("No connections to route");
1969 	} else if (routingStatus.m_netCount == routingStatus.m_netRoutedCount) {
1970 		if (routingStatus.m_jumperItemCount == 0) {
1971 			theText = tr("Routing completed");
1972 		}
1973 		else {
1974 			theText = tr("Routing completed using %n jumper part(s)", "", routingStatus.m_jumperItemCount);
1975 		}
1976 	} else {
1977 		theText = tr("%1 of %2 nets routed - %n connector(s) still to be routed", "", routingStatus.m_connectorsLeftToRoute)
1978 			.arg(routingStatus.m_netRoutedCount)
1979 			.arg(routingStatus.m_netCount);
1980 	}
1981 
1982 	dynamic_cast<SketchAreaWidget *>(sketchWidget->parent())->routingStatusLabel()->setLabelText(theText);
1983 
1984 	updateTraceMenu();
1985 }
1986 
applyReadOnlyChange(bool isReadOnly)1987 void MainWindow::applyReadOnlyChange(bool isReadOnly) {
1988 	Q_UNUSED(isReadOnly);
1989 	//m_saveAct->setDisabled(isReadOnly);
1990 }
1991 
fritzingTitle()1992 const QString MainWindow::fritzingTitle() {
1993 	if (m_currentGraphicsView == NULL) {
1994 		return FritzingWindow::fritzingTitle();
1995 	}
1996 
1997 	QString fritzing = FritzingWindow::fritzingTitle();
1998 	return tr("%1 - [%2]").arg(fritzing).arg(m_currentGraphicsView->viewName());
1999 }
2000 
raiseWindowAction()2001 QAction *MainWindow::raiseWindowAction() {
2002 	return m_raiseWindowAct;
2003 }
2004 
raiseAndActivate()2005 void MainWindow::raiseAndActivate() {
2006 	if(isMinimized()) {
2007 		showNormal();
2008 	}
2009 	raise();
2010 	QTimer::singleShot(20, this, SLOT(activateWindowAux()));
2011 }
2012 
activateWindowAux()2013 void MainWindow::activateWindowAux() {
2014 	activateWindow();
2015 }
2016 
updateRaiseWindowAction()2017 void MainWindow::updateRaiseWindowAction() {
2018 	QString actionText;
2019 	QFileInfo fileInfo(m_fwFilename);
2020 	if(fileInfo.exists()) {
2021 		int lastSlashIdx = m_fwFilename.lastIndexOf("/");
2022 		int beforeLastSlashIdx = m_fwFilename.left(lastSlashIdx).lastIndexOf("/");
2023 		actionText = beforeLastSlashIdx > -1 && lastSlashIdx > -1 ? "..." : "";
2024 		actionText += m_fwFilename.right(m_fwFilename.size()-beforeLastSlashIdx-1);
2025 	} else {
2026 		actionText = m_fwFilename;
2027 	}
2028 	m_raiseWindowAct->setText(actionText);
2029 	m_raiseWindowAct->setToolTip(m_fwFilename);
2030 	m_raiseWindowAct->setStatusTip("raise \""+m_fwFilename+"\" window");
2031 }
2032 
sizeGrip()2033 QSizeGrip *MainWindow::sizeGrip() {
2034 	return m_sizeGrip;
2035 }
2036 
realStatusBar()2037 QStatusBar *MainWindow::realStatusBar() {
2038 	return m_statusBar;
2039 }
2040 
moveEvent(QMoveEvent * event)2041 void MainWindow::moveEvent(QMoveEvent * event) {
2042 	FritzingWindow::moveEvent(event);
2043 	emit mainWindowMoved(this);
2044 }
2045 
event(QEvent * e)2046 bool MainWindow::event(QEvent * e) {
2047 	switch (e->type()) {
2048 		case QEvent::WindowActivate:
2049 			emit changeActivationSignal(true, this);
2050 			break;
2051 		case QEvent::WindowDeactivate:
2052 			emit changeActivationSignal(false, this);
2053 			break;
2054 		default:
2055 			break;
2056 	}
2057 	return FritzingWindow::event(e);
2058 }
2059 
resizeEvent(QResizeEvent * event)2060 void MainWindow::resizeEvent(QResizeEvent * event) {
2061     if (m_sizeGrip) {
2062 	    m_sizeGrip->rearrange();
2063     }
2064 	FritzingWindow::resizeEvent(event);
2065 }
2066 
enableCheckUpdates(bool enabled)2067 void MainWindow::enableCheckUpdates(bool enabled)
2068 {
2069 	if (m_checkForUpdatesAct != NULL) {
2070 		m_checkForUpdatesAct->setEnabled(enabled);
2071 	}
2072 }
2073 
2074 
swapSelectedDelay(const QString & family,const QString & prop,QMap<QString,QString> & currPropsMap,ItemBase * itemBase)2075 void MainWindow::swapSelectedDelay(const QString & family, const QString & prop, QMap<QString, QString> & currPropsMap, ItemBase * itemBase)
2076 {
2077     //DebugDialog::debug("swap selected delay");
2078 	m_swapTimer.stop();
2079 	m_swapTimer.setAll(family, prop, currPropsMap, itemBase);
2080 	m_swapTimer.start();
2081 }
2082 
swapSelectedTimeout()2083 void MainWindow::swapSelectedTimeout()
2084 {
2085 	if (sender() == &m_swapTimer) {
2086 		QMap<QString, QString> map =  m_swapTimer.propsMap();
2087 		swapSelectedMap(m_swapTimer.family(), m_swapTimer.prop(), map, m_swapTimer.itemBase());
2088 	}
2089 }
2090 
swapSelectedMap(const QString & family,const QString & prop,QMap<QString,QString> & currPropsMap,ItemBase * itemBase)2091 void MainWindow::swapSelectedMap(const QString & family, const QString & prop, QMap<QString, QString> & currPropsMap, ItemBase * itemBase)
2092 {
2093 	if (itemBase == NULL) return;
2094 
2095 	QString generatedModuleID = currPropsMap.value("moduleID");
2096     bool logoPadBlocker = false;
2097 
2098 	if (generatedModuleID.isEmpty()) {
2099         if (prop.compare("layer") == 0) {
2100 	        if (family.compare("logo") == 0 || family.compare("pad") == 0 || family.contains("blocker", Qt::CaseInsensitive)) {
2101 		        QString value = Board::convertToXmlName(currPropsMap.value(prop));
2102 		        if (value.contains("copper1") && m_currentGraphicsView->boardLayers() == 1) {
2103 			        QMessageBox::warning(
2104 				        this,
2105 				        tr("No copper top layer"),
2106 				        tr("The copper top (copper 1) layer is not available on a one-sided board.  Please switch the board to double-sided or choose the copper bottom (copper 0) layer.")
2107 			        );
2108 			        return;
2109 		        }
2110                 // use the xml name
2111                 currPropsMap.insert(prop, value);
2112                 logoPadBlocker = true;
2113 	        }
2114             else if (itemBase->itemType() == ModelPart::Wire) {
2115                 // assume this option is disabled for a one-sided board, so we would not get here?
2116                 m_pcbGraphicsView->changeTraceLayer(itemBase, false, NULL);
2117                 return;
2118             }
2119         }
2120     }
2121 
2122 	if (generatedModuleID.isEmpty()) {
2123 		if (family.compare("Prototyping Board", Qt::CaseInsensitive) == 0) {
2124 			if (prop.compare("size", Qt::CaseInsensitive) == 0 || prop.compare("type", Qt::CaseInsensitive) == 0) {
2125 				QString size = currPropsMap.value("size");
2126 				QString type = currPropsMap.value("type");
2127 				if (type.compare("perfboard", Qt::CaseInsensitive) == 0) {
2128 					generatedModuleID = Perfboard::genModuleID(currPropsMap);
2129 				}
2130 				else if (type.compare("stripboard", Qt::CaseInsensitive) == 0) {
2131 					generatedModuleID = Stripboard::genModuleID(currPropsMap);
2132 				}
2133 			}
2134 		}
2135 	}
2136 
2137     bool swapLayer = false;
2138     ViewLayer::ViewLayerPlacement newViewLayerPlacement = ViewLayer::UnknownPlacement;
2139     if (prop.compare("layer") == 0 && !logoPadBlocker) {
2140         if (itemBase->modelPart()->flippedSMD() || itemBase->itemType() == ModelPart::Part) {
2141             ItemBase * viewItem = itemBase->modelPart()->viewItem(ViewLayer::PCBView);
2142             if (viewItem) {
2143                 ViewLayer::ViewLayerPlacement vlp = (currPropsMap.value(prop) == ItemBase::TranslatedPropertyNames.value("bottom") ? ViewLayer::NewBottom : ViewLayer::NewTop);
2144                 if (viewItem->viewLayerPlacement() != newViewLayerPlacement) {
2145                     swapLayer = true;
2146                     newViewLayerPlacement = vlp;
2147                 }
2148             }
2149         }
2150     }
2151 
2152 	if (!generatedModuleID.isEmpty()) {
2153 		ModelPart * modelPart = m_referenceModel->retrieveModelPart(generatedModuleID);
2154 		if (modelPart == NULL) {
2155 			if (!m_referenceModel->genFZP(generatedModuleID, m_referenceModel)) {
2156 				return;
2157 			}
2158 		}
2159 
2160 		swapSelectedAux(itemBase->layerKinChief(), generatedModuleID, swapLayer, newViewLayerPlacement, currPropsMap);
2161 		return;
2162 	}
2163 
2164     if (swapLayer) {
2165         swapSelectedAux(itemBase->layerKinChief(), itemBase->moduleID(), true, newViewLayerPlacement, currPropsMap);
2166         return;
2167     }
2168 
2169 	if ((prop.compare("package", Qt::CaseSensitive) != 0) && swapSpecial(prop, currPropsMap)) {
2170 		return;
2171 	}
2172 
2173 	foreach (QString key, currPropsMap.keys()) {
2174 		QString value = currPropsMap.value(key);
2175 		m_referenceModel->recordProperty(key, value);
2176 	}
2177 
2178 	QString moduleID = m_referenceModel->retrieveModuleIdWith(family, prop, true);
2179 	bool exactMatch = m_referenceModel->lastWasExactMatch();
2180 
2181 	if (moduleID.isEmpty()) {
2182 		QMessageBox::information(
2183 			this,
2184 			tr("Sorry!"),
2185 			tr(
2186 			 "No part with those characteristics.\n"
2187 			 "We're working to avoid this message, and only let you choose between properties that do exist")
2188 		);
2189 		return;
2190 	}
2191 
2192 
2193 
2194 	itemBase = itemBase->layerKinChief();
2195 
2196 	if(!exactMatch) {
2197 		AutoCloseMessageBox::showMessage(this, tr("No exactly matching part found; Fritzing chose the closest match."));
2198 	}
2199 
2200 	swapSelectedAux(itemBase, moduleID, false, ViewLayer::UnknownPlacement, currPropsMap);
2201 }
2202 
swapSpecial(const QString & theProp,QMap<QString,QString> & currPropsMap)2203 bool MainWindow::swapSpecial(const QString & theProp, QMap<QString, QString> & currPropsMap) {
2204 	ItemBase * itemBase = m_infoView->currentItem();
2205 	QString pinSpacing, resistance;
2206     int layers = 0;
2207 
2208 	foreach (QString key, currPropsMap.keys()) {
2209 		if (key.compare("layers", Qt::CaseInsensitive) == 0) {
2210 			if (!Board::isBoard(itemBase)) continue;
2211 
2212 			QString value = currPropsMap.value(key, "");
2213 			if (value.compare(Board::OneLayerTranslated) == 0) {
2214 				layers = 1;
2215 			}
2216 			else if (value.compare(Board::TwoLayersTranslated) == 0) {
2217 				layers = 2;
2218 			}
2219         }
2220 
2221 		if (key.compare("resistance", Qt::CaseInsensitive) == 0) {
2222 			resistance = currPropsMap.value(key);
2223 			continue;
2224 		}
2225 		if (key.compare("pin spacing", Qt::CaseInsensitive) == 0) {
2226 			pinSpacing = currPropsMap.value(key);
2227 			continue;
2228 		}
2229 	}
2230 
2231     if (layers != 0) {
2232         currPropsMap.insert("layers", QString::number(layers));
2233         if (theProp.compare("layers") == 0) {
2234             QString msg = (layers == 1) ? tr("Change to single layer pcb") : tr("Change to two layer pcb");
2235             swapLayers(itemBase, layers, msg, SketchWidget::PropChangeDelay);
2236             return true;
2237         }
2238 	}
2239 
2240 	if (!resistance.isEmpty() || !pinSpacing.isEmpty()) {
2241 		if (theProp.contains("band", Qt::CaseInsensitive)) {
2242 			// swap 4band for 5band or vice versa.
2243 			return false;
2244 		}
2245 
2246 		Resistor * resistor = qobject_cast<Resistor *>(itemBase);
2247 		if (resistor != NULL) {
2248 			m_currentGraphicsView->setResistance(resistance, pinSpacing);
2249 			return true;
2250 		}
2251 	}
2252 
2253 	return false;
2254 }
2255 
swapLayers(ItemBase * itemBase,int layers,const QString & msg,int delay)2256 void MainWindow::swapLayers(ItemBase * itemBase, int layers, const QString & msg, int delay) {
2257     QUndoCommand* parentCommand = new QUndoCommand(msg);
2258 	new CleanUpWiresCommand(m_breadboardGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand);
2259 	new CleanUpRatsnestsCommand(m_breadboardGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand);
2260     m_pcbGraphicsView->swapLayers(itemBase, layers, parentCommand);
2261 	// need to defer execution so the content of the info view doesn't change during an event that started in the info view
2262 	m_undoStack->waitPush(parentCommand, delay);
2263 }
2264 
swapSelectedAux(ItemBase * itemBase,const QString & moduleID,bool useViewLayerPlacement,ViewLayer::ViewLayerPlacement overrideViewLayerPlacement,QMap<QString,QString> & propsMap)2265 void MainWindow::swapSelectedAux(ItemBase * itemBase, const QString & moduleID, bool useViewLayerPlacement, ViewLayer::ViewLayerPlacement overrideViewLayerPlacement,  QMap<QString, QString> & propsMap) {
2266 
2267 	QUndoCommand* parentCommand = new QUndoCommand(tr("Swapped %1 with module %2").arg(itemBase->instanceTitle()).arg(moduleID));
2268 	new CleanUpWiresCommand(m_breadboardGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand);
2269 	new CleanUpRatsnestsCommand(m_breadboardGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand);
2270 
2271     ViewLayer::ViewLayerPlacement viewLayerPlacement = itemBase->viewLayerPlacement();
2272     ModelPart * modelPart = m_referenceModel->retrieveModelPart(moduleID);
2273     if (m_pcbGraphicsView->boardLayers() == 2) {
2274         if (modelPart->flippedSMD()) {
2275             //viewLayerPlacement = m_pcbGraphicsView->dropOnBottom() ? ViewLayer::NewBottom : ViewLayer::NewTop;
2276             if (useViewLayerPlacement) viewLayerPlacement = overrideViewLayerPlacement;
2277         }
2278         else if (modelPart->itemType() == ModelPart::Part) {
2279             //viewLayerPlacement = m_pcbGraphicsView->dropOnBottom() ? ViewLayer::NewBottom : ViewLayer::NewTop;
2280             if (useViewLayerPlacement) viewLayerPlacement = overrideViewLayerPlacement;
2281         }
2282     }
2283     else {
2284         if (modelPart->flippedSMD()) {
2285             viewLayerPlacement = ViewLayer::NewBottom;
2286         }
2287         else if (modelPart->itemType() == ModelPart::Part) {
2288             //viewLayerPlacement = m_pcbGraphicsView->dropOnBottom() ? ViewLayer::NewBottom : ViewLayer::NewTop;
2289             if (useViewLayerPlacement) viewLayerPlacement = overrideViewLayerPlacement;
2290         }
2291     }
2292 
2293 	swapSelectedAuxAux(itemBase, moduleID, viewLayerPlacement, propsMap, parentCommand);
2294 
2295 	// need to defer execution so the content of the info view doesn't change during an event that started in the info view
2296 	m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay);
2297 
2298 }
2299 
swapBoardImageSlot(SketchWidget * sketchWidget,ItemBase * itemBase,const QString & filename,const QString & moduleID,bool addName)2300 void MainWindow::swapBoardImageSlot(SketchWidget * sketchWidget, ItemBase * itemBase, const QString & filename, const QString & moduleID, bool addName) {
2301 
2302 	QUndoCommand* parentCommand = new QUndoCommand(tr("Change image to %2").arg(filename));
2303     QMap<QString, QString> propsMap;
2304 	long newID = swapSelectedAuxAux(itemBase, moduleID, itemBase->viewLayerPlacement(), propsMap, parentCommand);
2305 
2306     LoadLogoImageCommand * cmd = new LoadLogoImageCommand(sketchWidget, newID, "", QSizeF(0,0), filename, filename, addName, parentCommand);
2307     cmd->setRedoOnly();
2308 
2309 	// need to defer execution so the content of the info view doesn't change during an event that started in the info view
2310 	m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay);
2311 }
2312 
2313 
subSwapSlot(SketchWidget * sketchWidget,ItemBase * itemBase,const QString & newModuleID,ViewLayer::ViewLayerPlacement viewLayerPlacement,long & newID,QUndoCommand * parentCommand)2314 void MainWindow::subSwapSlot(SketchWidget * sketchWidget, ItemBase * itemBase, const QString & newModuleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, long & newID, QUndoCommand * parentCommand) {
2315 	Q_UNUSED(sketchWidget);
2316     QMap<QString, QString> propsMap;
2317 	newID = swapSelectedAuxAux(itemBase, newModuleID, viewLayerPlacement, propsMap, parentCommand);
2318 }
2319 
swapSelectedAuxAux(ItemBase * itemBase,const QString & moduleID,ViewLayer::ViewLayerPlacement viewLayerPlacement,QMap<QString,QString> & propsMap,QUndoCommand * parentCommand)2320 long MainWindow::swapSelectedAuxAux(ItemBase * itemBase, const QString & moduleID,  ViewLayer::ViewLayerPlacement viewLayerPlacement, QMap<QString, QString> & propsMap, QUndoCommand * parentCommand)
2321 {
2322 	long modelIndex = ModelPart::nextIndex();
2323 
2324 	QList<SketchWidget *> sketchWidgets;
2325 
2326     // master view must go last, since it creates the delete command, and possibly has all the local props
2327     switch (itemBase->viewID()) {
2328         case ViewLayer::SchematicView:
2329 		    sketchWidgets << m_pcbGraphicsView << m_breadboardGraphicsView << m_schematicGraphicsView;
2330             break;
2331         case ViewLayer::PCBView:
2332             sketchWidgets << m_schematicGraphicsView << m_breadboardGraphicsView << m_pcbGraphicsView;
2333             break;
2334         default:
2335             sketchWidgets << m_schematicGraphicsView << m_pcbGraphicsView << m_breadboardGraphicsView;
2336             break;
2337 	}
2338 
2339     SwapThing swapThing;
2340     swapThing.firstTime = true;
2341     swapThing.itemBase = itemBase;
2342     swapThing.newModelIndex = modelIndex;
2343     swapThing.newModuleID = moduleID;
2344     swapThing.viewLayerPlacement = viewLayerPlacement;
2345     swapThing.parentCommand = parentCommand;
2346     swapThing.propsMap = propsMap;
2347     swapThing.bbView = m_breadboardGraphicsView;
2348 
2349     long newID = 0;
2350     for (int i = 0; i < 3; i++) {
2351         long tempID = sketchWidgets[i]->setUpSwap(swapThing, i == 2);
2352         if (newID == 0 && tempID != 0) newID = tempID;
2353     }
2354 
2355 	// TODO:  z-order?
2356 
2357 	return newID;
2358 }
2359 
svgMissingLayer(const QString & layername,const QString & path)2360 void MainWindow::svgMissingLayer(const QString & layername, const QString & path) {
2361 	QMessageBox::warning(
2362 		this,
2363 		tr("Fritzing"),
2364 		tr("Svg %1 is missing a '%2' layer. "
2365 			"For more information on how to create a custom board shape, "
2366 			"see the tutorial at <a href='http://fritzing.org/learning/tutorials/designing-pcb/pcb-custom-shape/'>http://fritzing.org/learning/tutorials/designing-pcb/pcb-custom-shape/</a>.")
2367 		.arg(path)
2368 		.arg(layername)
2369 	);
2370 }
2371 
addDefaultParts()2372 void MainWindow::addDefaultParts() {
2373 	if (m_pcbGraphicsView == NULL) return;
2374 
2375 	m_pcbGraphicsView->addDefaultParts();
2376 	m_breadboardGraphicsView->addDefaultParts();
2377 	m_schematicGraphicsView->addDefaultParts();
2378 }
2379 
newMainWindow(ReferenceModel * referenceModel,const QString & displayPath,bool showProgress,bool lockFiles,int initialTab)2380 MainWindow * MainWindow::newMainWindow(ReferenceModel *referenceModel, const QString & displayPath, bool showProgress, bool lockFiles, int initialTab) {
2381     MainWindow * mw = new MainWindow(referenceModel, NULL);
2382 	if (showProgress) {
2383 		mw->showFileProgressDialog(displayPath);
2384 	}
2385 
2386     if (initialTab >= 0) mw->setInitialTab(initialTab);
2387     mw->init(referenceModel, lockFiles);
2388 
2389 	return mw;
2390 }
2391 
clearFileProgressDialog()2392 void  MainWindow::clearFileProgressDialog() {
2393 	if (m_fileProgressDialog) {
2394 		m_fileProgressDialog->close();
2395 		delete m_fileProgressDialog;
2396 		m_fileProgressDialog = NULL;
2397 	}
2398 }
2399 
setFileProgressPath(const QString & path)2400 void MainWindow::setFileProgressPath(const QString & path)
2401 {
2402 	if (m_fileProgressDialog) m_fileProgressDialog->setMessage(tr("loading %1").arg(path));
2403 }
2404 
fileProgressDialog()2405 FileProgressDialog * MainWindow::fileProgressDialog()
2406 {
2407 	return m_fileProgressDialog;
2408 }
2409 
showFileProgressDialog(const QString & path)2410 void MainWindow::showFileProgressDialog(const QString & path) {
2411     m_fileProgressDialog = new FileProgressDialog(tr("Loading..."), 200, this);
2412 	m_fileProgressDialog->setBinLoadingChunk(50);
2413 	if (!path.isEmpty()) {
2414 		setFileProgressPath(QFileInfo(path).fileName());
2415 	}
2416 	else {
2417 		setFileProgressPath(tr("new sketch"));
2418 	}
2419 }
2420 
selectedModuleID()2421 const QString &MainWindow::selectedModuleID() {
2422 	if(m_currentGraphicsView) {
2423 		return m_currentGraphicsView->selectedModuleID();
2424 	} else {
2425 		return ___emptyString___;
2426 	}
2427 }
2428 
redrawSketch()2429 void MainWindow::redrawSketch() {
2430     QList<ConnectorItem *> visited;
2431 	foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) {
2432 		item->update();
2433 		ConnectorItem * c = dynamic_cast<ConnectorItem *>(item);
2434 		if (c != NULL) {
2435 			c->restoreColor(visited);
2436 		}
2437 	}
2438 }
2439 
statusMessage(QString message,int timeout)2440 void MainWindow::statusMessage(QString message, int timeout) {
2441 	QStatusBar * sb = realStatusBar();
2442 	if (sb != NULL) {
2443 		sb->showMessage(message, timeout);
2444 	}
2445 }
2446 
dropPaste(SketchWidget * sketchWidget)2447 void MainWindow::dropPaste(SketchWidget * sketchWidget) {
2448 	Q_UNUSED(sketchWidget);
2449 	pasteInPlace();
2450 }
2451 
updateLayerMenuSlot()2452 void MainWindow::updateLayerMenuSlot() {
2453 	updateLayerMenu(true);
2454 }
2455 
save()2456 bool MainWindow::save() {
2457 	bool result = FritzingWindow::save();
2458 	if (result) {
2459 		QSettings settings;
2460 		settings.setValue("lastOpenSketch", m_fwFilename);
2461 	}
2462 	return result;
2463 }
2464 
saveAs()2465 bool MainWindow::saveAs() {
2466     bool convertSchematic = false;
2467     if (m_schematicGraphicsView != NULL && m_schematicGraphicsView->isOldSchematic()) {
2468 		QMessageBox::StandardButton reply = QMessageBox::question(this, tr("Schematic conversion"),
2469                     tr("Saving this sketch will convert it to the new schematic graphics standard. Go ahead and convert?"),
2470 					 QMessageBox::Yes | QMessageBox::No);
2471 		if (reply != QMessageBox::Yes) {
2472             return false;
2473         }
2474         convertSchematic = true;
2475     }
2476 
2477 	bool result = false;
2478 	if (m_fwFilename.endsWith(FritzingSketchExtension)) {
2479 		result = FritzingWindow::saveAs(m_fwFilename + 'z', false);
2480 	}
2481 	else {
2482 		result = FritzingWindow::saveAs();
2483 	}
2484 	if (result) {
2485 		QSettings settings;
2486 		settings.setValue("lastOpenSketch", m_fwFilename);
2487         if (convertSchematic) {
2488             MainWindow * mw = revertAux();
2489             QString gridSize = QString("%1in").arg(m_schematicGraphicsView->defaultGridSizeInches());
2490             mw->m_schematicGraphicsView->setGridSize(gridSize);
2491             mw->m_schematicGraphicsView->resizeLabels();
2492             mw->m_schematicGraphicsView->resizeWires();
2493             mw->m_schematicGraphicsView->updateWires();
2494             mw->save();
2495         }
2496 	}
2497 	return result;
2498 }
2499 
changeBoardLayers(int layers,bool doEmit)2500 void MainWindow::changeBoardLayers(int layers, bool doEmit) {
2501 	Q_UNUSED(doEmit);
2502 	Q_UNUSED(layers);
2503 	updateActiveLayerButtons();
2504 	if (m_currentGraphicsView) m_currentGraphicsView->updateConnectors();
2505 }
2506 
updateActiveLayerButtons()2507 void MainWindow::updateActiveLayerButtons() {
2508     if (m_activeLayerButtonWidget != NULL) {
2509 	    int index = activeLayerIndex();
2510 	    bool enabled = index >= 0;
2511 
2512 	    m_activeLayerButtonWidget->setCurrentIndex(index);
2513 	    m_activeLayerButtonWidget->setVisible(enabled);
2514 
2515 	    m_activeLayerBothAct->setEnabled(enabled);
2516 	    m_activeLayerBottomAct->setEnabled(enabled);
2517 	    m_activeLayerTopAct->setEnabled(enabled);
2518 
2519 	    QList<QAction *> actions;
2520 	    actions << m_activeLayerBothAct << m_activeLayerBottomAct << m_activeLayerTopAct;
2521 	    setActionsIcons(index, actions);
2522     }
2523 
2524     if (m_viewFromButtonWidget != NULL) {
2525         if (m_pcbGraphicsView) {
2526             bool viewFromBelow = m_pcbGraphicsView->viewFromBelow();
2527             int index = (viewFromBelow ? 1 : 0);
2528 	        m_viewFromButtonWidget->setCurrentIndex(index);
2529 	        m_viewFromButtonWidget->setVisible(true);
2530             m_viewFromBelowToggleAct->setChecked(viewFromBelow);
2531 
2532             m_viewFromBelowAct->setChecked(viewFromBelow);
2533             m_viewFromAboveAct->setChecked(viewFromBelow);
2534 
2535 	        m_viewFromBelowToggleAct->setEnabled(true);
2536 	        m_viewFromBelowAct->setEnabled(true);
2537 	        m_viewFromAboveAct->setEnabled(true);
2538         }
2539     }
2540 }
2541 
activeLayerIndex()2542 int MainWindow::activeLayerIndex()
2543 {
2544 	if (m_currentGraphicsView == NULL) return -1;
2545 
2546 	if (m_currentGraphicsView->boardLayers() == 2 || activeLayerWidgetAlwaysOn()) {
2547 		bool copper0Visible = m_currentGraphicsView->layerIsActive(ViewLayer::Copper0);
2548 		bool copper1Visible = m_currentGraphicsView->layerIsActive(ViewLayer::Copper1);
2549 		if (copper0Visible && copper1Visible) {
2550 			return 0;
2551 		}
2552 		else if (copper1Visible) {
2553 			return 2;
2554 		}
2555 		else if (copper0Visible) {
2556 			return 1;
2557 		}
2558 	}
2559 
2560 	return -1;
2561 }
2562 
activeLayerWidgetAlwaysOn()2563 bool MainWindow::activeLayerWidgetAlwaysOn() {
2564 	return false;
2565 }
2566 
2567 /**
2568  * A slot for saving a copy of the current sketch to a temp location.
2569  * This should be called every X minutes as well as just before certain
2570  * events, such as saves, part imports, file export/printing. This relies
2571  * on the m_autosaveNeeded variable and the undoStack being dirty for
2572  * an autosave to be attempted.
2573  */
backupSketch()2574 void  MainWindow::backupSketch() {
2575 	if (ProcessEventBlocker::isProcessing()) {
2576 		// don't want to autosave during autorouting, for example
2577 		return;
2578 	}
2579 
2580     if (m_autosaveNeeded && !m_undoStack->isClean()) {
2581         m_autosaveNeeded = false;			// clear this now in case the save takes a really long time
2582 
2583         DebugDialog::debug(QString("%1 autosaved as %2").arg(m_fwFilename).arg(m_backupFileNameAndPath));
2584         statusBar()->showMessage(tr("Backing up '%1'").arg(m_fwFilename), 2000);
2585 		ProcessEventBlocker::processEvents();
2586 		m_backingUp = true;
2587 		connectStartSave(true);
2588 		m_sketchModel->save(m_backupFileNameAndPath, false);
2589 		connectStartSave(false);
2590 		m_backingUp = false;
2591     }
2592 }
2593 
2594 /**
2595  * This function is used to trigger an autosave at the next autosave
2596  * timer event. It is connected to the QUndoStack::indexChanged(int)
2597  * signal so that any change to the undo stack triggers autosaves.
2598  * This function can be called independent of this signal and
2599  * still work properly.
2600  */
autosaveNeeded(int index)2601 void MainWindow::autosaveNeeded(int index) {
2602     Q_UNUSED(index);
2603     //DebugDialog::debug(QString("Triggering autosave"));
2604     m_autosaveNeeded = true;
2605 }
2606 
2607 /**
2608  * delete the backup file when the undostack is clean.
2609  */
undoStackCleanChanged(bool isClean)2610 void MainWindow::undoStackCleanChanged(bool isClean) {
2611     // DebugDialog::debug(QString("Clean status changed to %1").arg(isClean));
2612     if (isClean) {
2613         QFile::remove(m_backupFileNameAndPath);
2614     }
2615 }
2616 
setAutosavePeriod(int minutes)2617 void MainWindow::setAutosavePeriod(int minutes) {
2618 	setAutosave(minutes, AutosaveEnabled);
2619 }
2620 
setAutosaveEnabled(bool enabled)2621 void MainWindow::setAutosaveEnabled(bool enabled) {
2622 	setAutosave(AutosaveTimeoutMinutes, enabled);
2623 }
2624 
setAutosave(int minutes,bool enabled)2625 void MainWindow::setAutosave(int minutes, bool enabled) {
2626 	AutosaveTimeoutMinutes = minutes;
2627 	AutosaveEnabled = enabled;
2628 	foreach (QWidget * widget, QApplication::topLevelWidgets()) {
2629 		MainWindow * mainWindow = qobject_cast<MainWindow *>(widget);
2630 		if (mainWindow == NULL) continue;
2631 
2632 		mainWindow->m_autosaveTimer.stop();
2633 		if (qobject_cast<PEMainWindow *>(widget)) {
2634 			continue;
2635 		}
2636 
2637 		if (AutosaveEnabled) {
2638 			// is there a way to get the current timer offset so that all the timers aren't running in sync?
2639 			// or just add some random time...
2640 			mainWindow->m_autosaveTimer.start(AutosaveTimeoutMinutes * 60 * 1000);
2641 		}
2642 	}
2643 }
2644 
hasLinkedProgramFiles(const QString & filename,QStringList & linkedProgramFiles)2645 bool MainWindow::hasLinkedProgramFiles(const QString & filename, QStringList & linkedProgramFiles)
2646 {
2647 	QFile file(filename);
2648 	file.open(QFile::ReadOnly);
2649 	QXmlStreamReader xml(&file);
2650     xml.setNamespaceProcessing(false);
2651 
2652 	bool done = false;
2653 	while (!xml.atEnd()) {
2654         switch (xml.readNext()) {
2655         case QXmlStreamReader::StartElement:
2656 			if (xml.name().toString().compare("program") == 0) {
2657 				linkedProgramFiles.append(xml.readElementText());
2658 				break;
2659 			}
2660 			if (xml.name().toString().compare("views") == 0) {
2661 				done = true;
2662 				break;
2663 			}
2664 			if (xml.name().toString().compare("instances") == 0) {
2665 				done = true;
2666 				break;
2667 			}
2668 		default:
2669 			break;
2670 		}
2671 
2672 		if (done) break;
2673 	}
2674 
2675 	return linkedProgramFiles.count() > 0;
2676 }
2677 
getExtensionString()2678 QString MainWindow::getExtensionString() {
2679 	return tr("Fritzing (*%1)").arg(fileExtension());
2680 }
2681 
getExtensions()2682 QStringList MainWindow::getExtensions() {
2683 	QStringList extensions;
2684 	extensions.append(fileExtension());
2685 	return extensions;
2686 }
2687 
routingStatusLabelMousePress(QMouseEvent * event)2688 void MainWindow::routingStatusLabelMousePress(QMouseEvent* event) {
2689 	routingStatusLabelMouse(event, true);
2690 }
2691 
routingStatusLabelMouseRelease(QMouseEvent * event)2692 void MainWindow::routingStatusLabelMouseRelease(QMouseEvent* event) {
2693 	routingStatusLabelMouse(event, false);
2694 }
2695 
routingStatusLabelMouse(QMouseEvent *,bool show)2696 void MainWindow::routingStatusLabelMouse(QMouseEvent*, bool show) {
2697 	//if (show) DebugDialog::debug("-------");
2698 
2699     if (m_currentGraphicsView == NULL) return;
2700 
2701 	QSet<ConnectorItem *> toShow;
2702 	foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) {
2703 		VirtualWire * vw = dynamic_cast<VirtualWire *>(item);
2704 		if (vw == NULL) continue;
2705 
2706 		foreach (ConnectorItem * connectorItem, vw->connector0()->connectedToItems()) {
2707 			toShow.insert(connectorItem);
2708 		}
2709 		foreach (ConnectorItem * connectorItem, vw->connector1()->connectedToItems()) {
2710 			toShow.insert(connectorItem);
2711 		}
2712 	}
2713 	QList<ConnectorItem *> visited;
2714 	foreach (ConnectorItem * connectorItem, toShow) {
2715 		//if (show) {
2716 		//	DebugDialog::debug(QString("unrouted %1 %2 %3 %4")
2717 		//		.arg(connectorItem->attachedToInstanceTitle())
2718 		//		.arg(connectorItem->attachedToID())
2719 		//		.arg(connectorItem->attachedTo()->title())
2720 		//		.arg(connectorItem->connectorSharedName()));
2721 		//}
2722 
2723 		if (connectorItem->isActive() && connectorItem->isVisible() && !connectorItem->hidden() && !connectorItem->layerHidden()) {
2724 			connectorItem->showEqualPotential(show, visited);
2725 		}
2726 		else {
2727 			connectorItem = connectorItem->getCrossLayerConnectorItem();
2728 			if (connectorItem) connectorItem->showEqualPotential(show, visited);
2729 		}
2730 	}
2731 
2732     if (!show && toShow.count() == 0) {
2733             QMessageBox::information(this, tr("Unrouted connections"),
2734             tr("There are no unrouted connections in this view."));
2735     }
2736 }
2737 
setReportMissingModules(bool b)2738 void MainWindow::setReportMissingModules(bool b) {
2739 	if (m_sketchModel) {
2740 		m_sketchModel->setReportMissingModules(b);
2741 	}
2742 }
2743 
boardDeletedSlot()2744 void MainWindow::boardDeletedSlot()
2745 {
2746 	activeLayerBottom();
2747 }
2748 
cursorLocationSlot(double xinches,double yinches,double width,double height)2749 void MainWindow::cursorLocationSlot(double xinches, double yinches, double width, double height)
2750 {
2751 	if (m_locationLabel) {
2752 		QString units;
2753 		double x, y, w, h;
2754 		QHash<QString, int> precision;
2755 		precision["mm"] = 1;
2756 		precision["px"] = 0;
2757 		precision["in"] = 3;
2758 
2759 		m_locationLabel->setProperty("location", QSizeF(xinches, xinches));
2760 
2761 		if (m_locationLabelUnits.compare("mm") == 0) {
2762 			units = "mm";
2763 			x = xinches * 25.4;
2764 			y = yinches * 25.4;
2765 			w = width * 25.4;
2766 			h = height * 25.4;
2767 		}
2768 		else if (m_locationLabelUnits.compare("px") == 0) {
2769 			units = "px";
2770 			x = xinches * GraphicsUtils::SVGDPI;
2771 			y = yinches * GraphicsUtils::SVGDPI;
2772 			w = width * GraphicsUtils::SVGDPI;
2773 			h = height * GraphicsUtils::SVGDPI;
2774 		}
2775 		else {
2776 			units = "in";
2777 			x = xinches;
2778 			y = yinches;
2779 			w = width;
2780 			h = height;
2781 		}
2782 
2783 		if ( w*h == 0.0) {
2784 			m_locationLabel->setText(tr("(x,y)=(%1, %2) %3")
2785 				.arg(x, 0, 'f', precision[units])
2786 				.arg(y, 0, 'f', precision[units])
2787 				.arg(units) );
2788 		} else {
2789 			m_locationLabel->setText(tr("(x, y)=(%1, %2)\t(width, height)=(%3, %4) %5")
2790 				.arg(x, 0, 'f', precision[units])
2791 				.arg(y, 0, 'f', precision[units])
2792 				.arg(w, 0, 'f', precision[units])
2793 				.arg(h, 0, 'f', precision[units])
2794 				.arg(units) );
2795 		}
2796 	}
2797 }
2798 
locationLabelClicked()2799 void MainWindow::locationLabelClicked()
2800 {
2801 	if (m_locationLabelUnits.compare("mm") == 0) {
2802 		m_locationLabelUnits = "px";
2803 	}
2804 	else if (m_locationLabelUnits.compare("px") == 0) {
2805 		m_locationLabelUnits = "in";
2806 	}
2807 	else if (m_locationLabelUnits.compare("in") == 0) {
2808 		m_locationLabelUnits = "mm";
2809 	}
2810 	else {
2811 		m_locationLabelUnits = "in";
2812 	}
2813 
2814 	if (m_locationLabel) {
2815 		QVariant variant =  m_locationLabel->property("location");
2816 		if (variant.isValid()) {
2817 			QSizeF size = variant.toSizeF();
2818 			cursorLocationSlot(size.width(), size.height());
2819 		}
2820 		else {
2821 			cursorLocationSlot(0.0, 0.0);
2822 		}
2823 	}
2824 
2825 	QSettings settings;
2826 	settings.setValue("LocationInches", QVariant(m_locationLabelUnits));
2827 }
2828 
filenameIfSlot(QString & filename)2829 void MainWindow::filenameIfSlot(QString & filename)
2830 {
2831 	filename = QFileInfo(fileName()).fileName();
2832 }
2833 
sketchWidgets()2834 QList<SketchWidget *> MainWindow::sketchWidgets()
2835 {
2836 	QList<SketchWidget *> list;
2837 	list << m_breadboardGraphicsView << m_schematicGraphicsView << m_pcbGraphicsView;
2838 	return list;
2839 }
2840 
setCloseSilently(bool cs)2841 void MainWindow::setCloseSilently(bool cs)
2842 {
2843 	m_closeSilently = cs;
2844 }
2845 
pcbView()2846 PCBSketchWidget * MainWindow::pcbView() {
2847 	return m_pcbGraphicsView;
2848 }
2849 
noBackup()2850 void MainWindow::noBackup()
2851 {
2852 	m_autosaveTimer.stop();
2853 }
2854 
hideTempPartsBin()2855 void MainWindow::hideTempPartsBin() {
2856 	if (m_binManager) m_binManager->hideTempPartsBin();
2857 }
2858 
setActiveWire(Wire * wire)2859 void MainWindow::setActiveWire(Wire * wire) {
2860 	m_activeWire = wire;
2861 }
2862 
setActiveConnectorItem(ConnectorItem * connectorItem)2863 void MainWindow::setActiveConnectorItem(ConnectorItem * connectorItem) {
2864 	m_activeConnectorItem = connectorItem;
2865 }
2866 
fritzingVersion()2867 const QString & MainWindow::fritzingVersion() {
2868 	if (m_sketchModel) return m_sketchModel->fritzingVersion();
2869 
2870 	return ___emptyString___;
2871 }
2872 
2873 
dragEnterEvent(QDragEnterEvent * event)2874 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
2875 {
2876     const QMimeData* mimeData = event->mimeData();
2877 
2878     if (mimeData->hasUrls()) {
2879         QStringList pathList;
2880         QList<QUrl> urlList = mimeData->urls();
2881 
2882         // extract the local paths of the files
2883         for (int i = 0; i < urlList.size() && i < 32; ++i) {
2884             QString fn = urlList.at(i).toLocalFile();
2885             foreach (QString ext, fritzingExtensions()) {
2886                 if (fn.endsWith(ext)) {
2887                     event->acceptProposedAction();
2888                     return;
2889                 }
2890             }
2891 
2892             if (fn.endsWith("txt")) {
2893                 QFile file(fn);
2894                 if (file.open(QFile::ReadOnly)) {
2895                     QTextStream stream(&file);
2896                     while (!stream.atEnd()) {
2897                         QString line = stream.readLine().trimmed();
2898                         foreach (QString ext, fritzingExtensions()) {
2899                             if (line.endsWith(ext)) {
2900                                 event->acceptProposedAction();
2901                                 return;
2902                             }
2903                         }
2904                     }
2905                 }
2906             }
2907         }
2908     }
2909 
2910 }
2911 
dropEvent(QDropEvent * event)2912 void MainWindow::dropEvent(QDropEvent *event)
2913 {
2914     const QMimeData* mimeData = event->mimeData();
2915 
2916     if (mimeData->hasUrls()) {
2917         QStringList pathList;
2918         QList<QUrl> urlList = mimeData->urls();
2919 
2920         // extract the local paths of the files
2921         for (int i = 0; i < urlList.size() && i < 32; ++i) {
2922             mainLoadAux(urlList.at(i).toLocalFile());
2923         }
2924 
2925         return;
2926     }
2927 }
2928 
hasAnyAlien()2929 bool MainWindow::hasAnyAlien() {
2930     return m_addedToTemp;
2931 }
2932 
initStyleSheet()2933 void MainWindow::initStyleSheet()
2934 {
2935     QString suffix = getStyleSheetSuffix();
2936 	QFile styleSheet(QString(":/resources/styles/%1.qss").arg(suffix));
2937     if (!styleSheet.open(QIODevice::ReadOnly)) {
2938 		DebugDialog::debug(QString("Unable to open :/resources/styles/%1.qss").arg(suffix));
2939 	} else {
2940 		QString platformDependantStyle = "";
2941 		QString platformDependantStylePath;
2942 #ifdef Q_OS_LINUX
2943 		if(style()->metaObject()->className()==QString("OxygenStyle")) {
2944 			QFile oxygenStyleSheet(QString(":/resources/styles/linux-kde-oxygen-%1.qss").arg(suffix));
2945 			if(oxygenStyleSheet.open(QIODevice::ReadOnly)) {
2946 				platformDependantStyle += oxygenStyleSheet.readAll();
2947 			}
2948 		}
2949 		platformDependantStylePath = QString(":/resources/styles/linux-%1.qss").arg(suffix);
2950 #endif
2951 
2952 #ifdef Q_OS_MAC
2953 		platformDependantStylePath = QString(":/resources/styles/mac-%1.qss").arg(suffix);
2954 #endif
2955 
2956 #ifdef Q_OS_WIN
2957 		platformDependantStylePath = QString(":/resources/styles/win-%1.qss").arg(suffix);
2958 #endif
2959 
2960 		QFile platformDependantStyleSheet(platformDependantStylePath);
2961 		if(platformDependantStyleSheet.open(QIODevice::ReadOnly)) {
2962 			platformDependantStyle += platformDependantStyleSheet.readAll();
2963 		}
2964 		setStyleSheet(styleSheet.readAll()+platformDependantStyle);
2965 	}
2966 }
2967 
getStyleSheetSuffix()2968 QString MainWindow::getStyleSheetSuffix() {
2969     return "fritzing";
2970 }
2971 
addToMyParts(ModelPart * modelPart)2972 void MainWindow::addToMyParts(ModelPart * modelPart)
2973 {
2974     if (modelPart != NULL) m_binManager->addToMyParts(modelPart);
2975 }
2976 
anyUsePart(const QString & moduleID)2977 bool MainWindow::anyUsePart(const QString & moduleID) {
2978     foreach (QWidget *widget, QApplication::topLevelWidgets()) {
2979         MainWindow *mainWindow = qobject_cast<MainWindow *>(widget);
2980 		if (mainWindow == NULL) continue;
2981 
2982         if (mainWindow->usesPart(moduleID)) {
2983             return true;
2984         }
2985     }
2986 
2987     return false;
2988 }
2989 
usesPart(const QString & moduleID)2990 bool MainWindow::usesPart(const QString & moduleID) {
2991     if (m_currentGraphicsView == NULL) return false;
2992 
2993     foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) {
2994         ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
2995         if (itemBase != NULL && itemBase->moduleID().compare(moduleID) == 0) {
2996             return true;
2997         }
2998     }
2999 
3000     return false;
3001 }
3002 
updateParts(const QString & moduleID,QUndoCommand * parentCommand)3003 bool MainWindow::updateParts(const QString & moduleID, QUndoCommand * parentCommand) {
3004     if (m_currentGraphicsView == NULL) return false;
3005 
3006     QSet<ItemBase *> itemBases;
3007     foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) {
3008         ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
3009         if (itemBase == NULL) continue;
3010         if (itemBase->moduleID().compare(moduleID) != 0) continue;
3011 
3012         itemBases.insert(itemBase->layerKinChief());
3013     }
3014 
3015     QMap<QString, QString> propsMap;
3016     foreach (ItemBase * itemBase, itemBases) {
3017         swapSelectedAuxAux(itemBase, moduleID, itemBase->viewLayerPlacement(), propsMap, parentCommand);
3018     }
3019 
3020     return true;
3021 }
3022 
showStatusMessage(const QString & message)3023 void MainWindow::showStatusMessage(const QString & message)
3024 {
3025     if (sender() == m_statusBar) {
3026         return;
3027     }
3028 
3029     if (message == m_statusBar->currentMessage()) {
3030         return;
3031     }
3032 
3033     //DebugDialog::debug("show message " + message);
3034     m_statusBar->blockSignals(true);
3035     m_statusBar->showMessage(message);
3036     m_statusBar->blockSignals(false);
3037 }
3038 
updatePartsBin(const QString & moduleID)3039 void MainWindow::updatePartsBin(const QString & moduleID) {
3040 	m_binManager->reloadPart(moduleID);
3041 }
3042 
hasCustomBoardShape()3043 bool MainWindow::hasCustomBoardShape() {
3044 	if (m_pcbGraphicsView == NULL) return false;
3045 
3046 	return m_pcbGraphicsView->hasCustomBoardShape();
3047 }
3048 
selectPartsWithModuleID(ModelPart * modelPart)3049 void MainWindow::selectPartsWithModuleID(ModelPart * modelPart) {
3050     if (m_currentGraphicsView == NULL) return;
3051 
3052     m_currentGraphicsView->selectItemsWithModuleID(modelPart);
3053 }
3054 
addToSketch(QList<ModelPart * > & modelParts)3055 void MainWindow::addToSketch(QList<ModelPart *> & modelParts) {
3056     if (m_currentGraphicsView == NULL) return;
3057 
3058     m_currentGraphicsView->addToSketch(modelParts);
3059 }
3060 
initProgrammingWidget()3061 void MainWindow::initProgrammingWidget() {
3062     m_programView = new ProgramWindow(this);
3063 
3064     connect(m_programView, SIGNAL(linkToProgramFile(const QString &, Platform *, bool, bool)),
3065             this, SLOT(linkToProgramFile(const QString &, Platform *, bool, bool)));
3066 
3067 	m_programView->setup();
3068 
3069     SketchAreaWidget * sketchAreaWidget = new SketchAreaWidget(m_programView, this, false, true);
3070     addTab(sketchAreaWidget, ":/resources/images/icons/TabWidgetCodeActive_icon.png", tr("Code"), true);
3071 }
3072 
programmingWidget()3073 ProgramWindow *MainWindow::programmingWidget() {
3074     return m_programView;
3075 }
3076 
orderFabHoverEnter()3077 void MainWindow::orderFabHoverEnter() {
3078     m_fireQuoteTimer.stop();
3079     if (!QuoteDialog::quoteSucceeded()) return;
3080     if (m_rolloverQuoteDialog && m_rolloverQuoteDialog->isVisible()) return;
3081 
3082     m_fireQuoteTimer.setInterval(fireQuoteDelay());
3083     m_fireQuoteTimer.start();
3084 }
3085 
fireQuote()3086 void MainWindow::fireQuote() {
3087     m_fireQuoteTimer.stop();
3088     if (!QuoteDialog::quoteSucceeded()) return;
3089 
3090     m_rolloverQuoteDialog = m_pcbGraphicsView->quoteDialog(m_pcbWidget);
3091     if (m_rolloverQuoteDialog == NULL) return;
3092 
3093     //DebugDialog::debug("enter fab button");
3094     //QWidget * toolbar = m_pcbWidget->toolbar();
3095     //QRect r = toolbar->geometry();
3096     //QPointF p = toolbar->parentWidget()->mapToGlobal(r.topLeft());
3097 
3098 	Qt::WindowFlags flags = m_rolloverQuoteDialog->windowFlags();
3099     flags = Qt::Window | Qt::Dialog | Qt::FramelessWindowHint;
3100 	m_rolloverQuoteDialog->setWindowFlags(flags);
3101     m_rolloverQuoteDialog->show();
3102 
3103     // seems to require setting position after show()
3104 
3105     QRect b = m_orderFabButton->geometry();
3106     QPoint bt = m_orderFabButton->parentWidget()->mapToGlobal(b.topRight());
3107     QRect t = m_pcbWidget->toolbar()->geometry();
3108     QPoint gt = m_pcbWidget->toolbar()->parentWidget()->mapToGlobal(t.topLeft());
3109     QRect q = m_rolloverQuoteDialog->geometry();
3110 
3111     // I don't understand why--perhaps due to the windowFlags--but q is already in global coordinates
3112     q.moveBottom(gt.y() - 20);
3113     q.moveRight(bt.x());
3114     m_rolloverQuoteDialog->setGeometry(q);
3115 }
3116 
orderFabHoverLeave()3117 void MainWindow::orderFabHoverLeave() {
3118     m_fireQuoteTimer.stop();
3119     //DebugDialog::debug("leave fab button");
3120     if (m_rolloverQuoteDialog) {
3121         m_rolloverQuoteDialog->hide();
3122     }
3123 }
3124 
initWelcomeView()3125 void MainWindow::initWelcomeView() {
3126     m_welcomeView = new WelcomeView(this);
3127     m_welcomeView->setObjectName("WelcomeView");
3128     SketchAreaWidget * sketchAreaWidget = new SketchAreaWidget(m_welcomeView, this, false, false);
3129     addTab(sketchAreaWidget, ":/resources/images/icons/TabWidgetWelcomeActive_icon.png", tr("Welcome"), true);
3130 }
3131 
setInitialView()3132 void MainWindow::setInitialView() {
3133     	// do this the first time, since the current_changed signal wasn't sent
3134 	int tab = 0;
3135 
3136 	tabWidget_currentChanged(tab+1);
3137     tabWidget_currentChanged(tab);
3138 
3139     // default to breadboard view
3140     this->setCurrentTabIndex(m_initialTab);
3141 }
3142 
updateWelcomeViewRecentList(bool doEmit)3143 void MainWindow::updateWelcomeViewRecentList(bool doEmit) {
3144     if (m_welcomeView) {
3145         m_welcomeView->updateRecent();
3146         if (doEmit) {
3147              foreach (QWidget *widget, QApplication::topLevelWidgets()) {
3148                 MainWindow *mainWin = qobject_cast<MainWindow *>(widget);
3149                 if (mainWin && mainWin != this) {
3150                     mainWin->updateWelcomeViewRecentList(false);
3151                 }
3152             }
3153         }
3154     }
3155 }
3156 
initZoom()3157 void MainWindow::initZoom() {
3158     if (m_currentGraphicsView == NULL) return;
3159     if (m_currentGraphicsView->everZoomed()) return;
3160     if (!m_currentGraphicsView->isVisible()) return;
3161 
3162     bool parts = false;
3163     foreach (QGraphicsItem * item, m_currentGraphicsView->items()) {
3164         ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
3165         if (itemBase == NULL) continue;
3166         if (!itemBase->isEverVisible()) continue;
3167 
3168         parts = true;
3169         break;
3170     }
3171 
3172     if (parts) {
3173         m_currentGraphicsView->fitInWindow();
3174     }
3175 
3176     m_currentGraphicsView->setEverZoomed(true);
3177 }
3178 
fireQuoteDelay()3179 int MainWindow::fireQuoteDelay() {
3180     return m_fireQuoteDelay;
3181 }
3182 
setFireQuoteDelay(int delay)3183 void MainWindow::setFireQuoteDelay(int delay) {
3184     m_fireQuoteDelay = delay;
3185 }
3186 
noSchematicConversion()3187 void MainWindow::noSchematicConversion() {
3188     m_noSchematicConversion = true;
3189 }
3190 
setInitialTab(int tab)3191 void MainWindow::setInitialTab(int tab) {
3192     m_initialTab = tab;
3193 }
3194