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