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