1
/*******************************************************************
3
Part of the Fritzing project - http://fritzing.org
4
Copyright (c) 2007-2012 Fachhochschule Potsdam - http://fh-potsdam.de
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.
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.
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/>.
19
********************************************************************
22
$Author: cohen@irascible.com $:
23
$Date: 2012-08-08 04:07:28 +0200 (Wed, 08 Aug 2012) $
25
********************************************************************/
28
#include <QSvgGenerator>
30
#include <QImageWriter>
32
#include "mainwindow.h"
33
#include "debugdialog.h"
34
#include "waitpushundostack.h"
35
#include "partseditor/partseditormainwindow.h"
36
#include "help/aboutbox.h"
37
#include "autoroute/cmrouter/cmrouter.h"
38
#include "autoroute/autorouteprogressdialog.h"
39
#include "items/virtualwire.h"
40
#include "items/jumperitem.h"
41
#include "items/via.h"
42
#include "fsvgrenderer.h"
43
#include "items/note.h"
44
#include "eagle/fritzing2eagle.h"
45
#include "sketch/breadboardsketchwidget.h"
46
#include "sketch/schematicsketchwidget.h"
47
#include "sketch/pcbsketchwidget.h"
48
#include "partsbinpalette/binmanager/binmanager.h"
49
#include "utils/expandinglabel.h"
50
#include "infoview/htmlinfoview.h"
51
#include "utils/bendpointaction.h"
52
#include "fgraphicsscene.h"
53
#include "utils/fileprogressdialog.h"
54
#include "svg/svgfilesplitter.h"
55
#include "version/version.h"
56
#include "help/tipsandtricks.h"
57
#include "dialogs/setcolordialog.h"
58
#include "utils/folderutils.h"
59
#include "utils/graphicsutils.h"
60
#include "utils/textutils.h"
61
#include "connectors/ercdata.h"
62
#include "items/moduleidnames.h"
63
#include "navigator/miniviewcontainer.h"
64
#include "utils/zoomslider.h"
65
#include "layerpalette.h"
66
#include "program/programwindow.h"
67
#include "utils/autoclosemessagebox.h"
68
#include "svg/gerbergenerator.h"
69
#include "processeventblocker.h"
71
static QString eagleActionType = ".eagle";
72
static QString gerberActionType = ".gerber";
73
static QString jpgActionType = ".jpg";
74
static QString psActionType = ".ps";
75
static QString pdfActionType = ".pdf";
76
static QString pngActionType = ".png";
77
static QString svgActionType = ".svg";
78
static QString bomActionType = ".html";
79
static QString netlistActionType = ".xml";
81
static QHash<QString, QPrinter::OutputFormat> filePrintFormats;
82
static QHash<QString, QImage::Format> fileExportFormats;
83
static QHash<QString, QString> fileExtFormats;
85
static QRegExp AaCc("[aAcC]");
86
static QRegExp LabelNumber("([^\\d]+)(.*)");
88
static const double InchesPerMeter = 39.3700787;
90
////////////////////////////////////////////////////////
92
bool sortPartList(ItemBase * b1, ItemBase * b2) {
93
bool result = b1->instanceTitle().toLower() < b2->instanceTitle().toLower();
95
int ix1 = LabelNumber.indexIn(b1->instanceTitle());
96
if (ix1 < 0) return result;
98
QString label1 = LabelNumber.cap(1);
99
QString number1 = LabelNumber.cap(2);
101
int ix2 = LabelNumber.indexIn(b2->instanceTitle());
102
if (ix2 < 0) return result;
104
QString label2 = LabelNumber.cap(1);
105
QString number2 = LabelNumber.cap(2);
106
if (label2.compare(label1, Qt::CaseInsensitive) != 0) return result;
109
double d1 = number1.toDouble(&ok);
110
if (!ok) return result;
112
double d2 = number2.toDouble(&ok);
113
if (!ok) return result;
118
/////////////////////////////////////////////////////////
120
void MainWindow::initNames()
122
OtherKnownExtensions << jpgActionType << psActionType << pdfActionType << pngActionType << svgActionType << bomActionType << netlistActionType;
124
filePrintFormats[pdfActionType] = QPrinter::PdfFormat;
125
filePrintFormats[psActionType] = QPrinter::PostScriptFormat;
127
fileExportFormats[pngActionType] = QImage::Format_ARGB32;
128
fileExportFormats[jpgActionType] = QImage::Format_RGB32;
130
fileExtFormats[pdfActionType] = tr("PDF (*.pdf)");
131
fileExtFormats[psActionType] = tr("PostScript (*.ps)");
132
fileExtFormats[pngActionType] = tr("PNG Image (*.png)");
133
fileExtFormats[jpgActionType] = tr("JPEG Image (*.jpg)");
134
fileExtFormats[svgActionType] = tr("SVG Image (*.svg)");
135
fileExtFormats[bomActionType] = tr("BoM Text File (*.html)");
138
AutosaveEnabled = settings.value("autosaveEnabled", QString("%1").arg(AutosaveEnabled)).toBool();
139
AutosaveTimeoutMinutes = settings.value("autosavePeriod", QString("%1").arg(AutosaveTimeoutMinutes)).toInt();
142
void MainWindow::print() {
143
#ifndef QT_NO_PRINTER
144
QPrinter printer(QPrinter::HighResolution);
146
QPrintDialog *printDialog = new QPrintDialog(&printer, this);
147
if (printDialog->exec() == QDialog::Accepted) {
148
m_statusBar->showMessage(tr("Printing..."));
149
printAux(printer, true, true);
150
m_statusBar->showMessage(tr("Ready"), 2000);
157
void MainWindow::exportEtchable() {
158
if (sender() == NULL) return;
160
bool wantSvg = sender()->property("svg").toBool();
161
exportEtchable(!wantSvg, wantSvg);
165
void MainWindow::exportEtchable(bool wantPDF, bool wantSVG)
168
ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount);
169
if (boardCount == 0) {
170
QMessageBox::critical(this, tr("Fritzing"),
171
tr("Your sketch does not have a board yet! Please add a PCB in order to export etchable."));
175
QMessageBox::critical(this, tr("Fritzing"),
176
tr("Etchable export can only handle one board at a time--please select the board you want to export."));
180
RoutingStatus routingStatus;
181
m_pcbGraphicsView->updateRoutingStatus(NULL, routingStatus, true);
182
if (routingStatus.m_connectorsLeftToRoute > 0) {
183
QMessageBox msgBox(this);
184
msgBox.setWindowModality(Qt::WindowModal);
185
msgBox.setText(tr("All traces have not yet been routed."));
186
msgBox.setInformativeText(tr("Do you want to proceed anyway?"));
187
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
188
msgBox.button(QMessageBox::Yes)->setText(tr("Proceed"));
189
msgBox.button(QMessageBox::No)->setText(tr("Cancel"));
190
msgBox.setDefaultButton(QMessageBox::Yes);
191
int ret = msgBox.exec();
192
if (ret != QMessageBox::Yes) return;
195
QString path = defaultSaveFolder();
196
QString extFmt = (wantPDF) ? fileExtFormats.value(pdfActionType) : fileExtFormats.value(svgActionType);
197
QString fileExt = extFmt;
199
QString suffix = (wantPDF) ? pdfActionType : svgActionType;
201
if (boardCount > 1) {
202
prefix = QString("%1_%2_").arg(board->instanceTitle()).arg(board->id());
205
QString exportDir = QFileDialog::getExistingDirectory(this, tr("Choose a folder for exporting"),
207
QFileDialog::ShowDirsOnly
208
| QFileDialog::DontResolveSymlinks);
209
if (exportDir.isEmpty()) return;
211
FolderUtils::setOpenSaveFolder(exportDir);
212
FileProgressDialog * fileProgressDialog = exportProgress();
214
QRectF r = board->sceneBoundingRect();
215
QSizeF boardImageSize(r.width(), r.height());
217
QStringList fileNames;
219
fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_copper_bottom%1", suffix));
220
fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_mask_bottom%1", suffix));
221
fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_paste_mask_bottom%1", suffix));
222
if (m_pcbGraphicsView->boardLayers() > 1) {
223
fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_copper_top%1", suffix));
224
fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_mask_top%1", suffix));
225
fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_paste_mask_top%1", suffix));
227
fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_silk_top%1", suffix));
229
QString maskTop, maskBottom;
230
QList<ItemBase *> copperLogoItems, holes;
231
for (int ix = 0; ix < fileNames.count(); ix++) {
234
bool doPaste = false;
235
QString fileName = fileNames[ix];
236
LayerList viewLayerIDs;
237
if (fileName.contains("copper_bottom")) {
238
viewLayerIDs << ViewLayer::GroundPlane0 << ViewLayer::Copper0 << ViewLayer::Copper0Trace;
240
else if (fileName.contains("mask_bottom")) {
242
viewLayerIDs << ViewLayer::Copper0;
243
doPaste = fileName.contains("paste");
245
else if (fileName.contains("copper_top")) {
246
viewLayerIDs << ViewLayer::GroundPlane1 << ViewLayer::Copper1 << ViewLayer::Copper1Trace;
248
else if (fileName.contains("mask_top")) {
249
viewLayerIDs << ViewLayer::Copper1;
251
doPaste = fileName.contains("paste");
253
else if (fileName.contains("silk_top")) {
254
viewLayerIDs << ViewLayer::Silkscreen1 << ViewLayer::Silkscreen1Label;
259
m_pcbGraphicsView->hideCopperLogoItems(copperLogoItems);
262
m_pcbGraphicsView->hideHoles(holes);
268
QString svg = m_pcbGraphicsView->renderToSVG(GraphicsUtils::SVGDPI, viewLayerIDs, true, imageSize, board, GraphicsUtils::IllustratorDPI, false, false, empty);
269
massageOutput(svg, doMask, doSilk, doPaste, maskTop, maskBottom, fileName, board, GraphicsUtils::IllustratorDPI, viewLayerIDs);
270
QString merged = mergeBoardSvg(svg, board, GraphicsUtils::IllustratorDPI, false, viewLayerIDs);
271
TextUtils::writeUtf8(fileName.arg(""), merged);
272
merged = mergeBoardSvg(svg, board, GraphicsUtils::IllustratorDPI, true, viewLayerIDs);
273
TextUtils::writeUtf8(fileName.arg("_mirror"), merged);
278
flips << false << true;
279
foreach (bool flip, flips) {
280
QString mirror = flip ? "_mirror" : "";
281
QPrinter printer(QPrinter::HighResolution);
282
printer.setOutputFormat(filePrintFormats[fileExt]);
283
printer.setOutputFileName(fileName.arg(mirror));
284
int res = printer.resolution();
289
svg = m_pcbGraphicsView->renderToSVG(GraphicsUtils::SVGDPI, viewLayerIDs, true, imageSize, board, res, false, false, empty);
290
massageOutput(svg, doMask, doSilk, doPaste, maskTop, maskBottom, fileName, board, res, viewLayerIDs);
293
QString merged = mergeBoardSvg(svg, board, res, flip, viewLayerIDs);
295
// now convert to pdf
296
QSvgRenderer svgRenderer;
297
svgRenderer.load(merged.toLatin1());
298
double trueWidth = boardImageSize.width() / GraphicsUtils::SVGDPI;
299
double trueHeight = boardImageSize.height() / GraphicsUtils::SVGDPI;
300
QRectF target(0, 0, trueWidth * res, trueHeight * res);
302
QSizeF psize((target.width() + printer.paperRect().width() - printer.width()) / res,
303
(target.height() + printer.paperRect().height() - printer.height()) / res);
304
printer.setPaperSize(psize, QPrinter::Inch);
307
if (painter.begin(&printer))
309
svgRenderer.render(&painter, target);
316
m_pcbGraphicsView->restoreCopperLogoItems(copperLogoItems);
319
m_pcbGraphicsView->restoreCopperLogoItems(holes);
324
m_statusBar->showMessage(tr("Sketch exported"), 2000);
325
delete fileProgressDialog;
329
int width = m_pcbGraphicsView->width();
330
if (m_pcbGraphicsView->verticalScrollBar()->isVisible()) {
331
width -= m_pcbGraphicsView->verticalScrollBar()->width();
333
int height = m_pcbGraphicsView->height();
334
if (m_pcbGraphicsView->horizontalScrollBar()->isVisible()) {
335
height -= m_pcbGraphicsView->horizontalScrollBar()->height();
338
double trueWidth = width / m_printerScale;
339
double trueHeight = height / m_printerScale;
341
// set everything to a 1200 dpi resolution
342
QSize imgSize(trueWidth * 1200, trueHeight * 1200);
343
QImage image(imgSize, QImage::Format_RGB32);
344
image.setDotsPerMeterX(1200 * GraphicsUtils::InchesPerMeter);
345
image.setDotsPerMeterY(1200 * GraphicsUtils::InchesPerMeter);
349
color = m_pcbGraphicsView->background();
350
m_pcbGraphicsView->setBackground(QColor::fromRgb(255,255,255,255));
352
m_pcbGraphicsView->scene()->clearSelection();
353
m_pcbGraphicsView->saveLayerVisibility();
354
m_pcbGraphicsView->setAllLayersVisible(false);
355
m_pcbGraphicsView->setLayerVisible(ViewLayer::Copper0, true);
356
m_pcbGraphicsView->hideConnectors(true);
358
painter.begin(&image);
359
m_pcbGraphicsView->render(&painter);
363
QSvgGenerator svgGenerator;
364
svgGenerator.setFileName("c:/fritzing2/testsvggenerator.svg");
365
svgGenerator.setSize(QSize(width * 8, height * 8));
366
QPainter svgPainter(&svgGenerator);
367
m_pcbGraphicsView->render(&svgPainter);
371
m_pcbGraphicsView->hideConnectors(false);
372
m_pcbGraphicsView->setBackground(color);
373
m_pcbGraphicsView->restoreLayerVisibility();
374
// TODO: restore the selection
377
for (int x = 0; x < imgSize.width(); x++) {
378
for (int y = 0; y < imgSize.height(); y++) {
379
QRgb p = image.pixel(x, y);
380
if (p != 0xffffffff) {
381
image.setPixel(x, y, black);
386
bool result = image.save(fileName);
388
QMessageBox::warning(this, tr("Fritzing"), tr("Unable to save %1").arg(fileName) );
395
QString MainWindow::mergeBoardSvg(QString & svg, ItemBase * board, int res, bool flip, LayerList & viewLayerIDs) {
396
QString boardSvg = getBoardSvg(board, res, viewLayerIDs);
398
LayerList outlineLayerIDs = ViewLayer::outlineLayers();
402
QString outlineSvg = m_pcbGraphicsView->renderToSVG(GraphicsUtils::SVGDPI, outlineLayerIDs, true, imageSize, board, res, false, false, empty);
403
outlineSvg = GerberGenerator::cleanOutline(outlineSvg);
404
outlineSvg = TextUtils::slamStrokeAndFill(outlineSvg, "black", "0.5", "none");
406
if (!boardSvg.isEmpty() && !outlineSvg.isEmpty()) {
407
boardSvg = TextUtils::mergeSvg(boardSvg, outlineSvg, "", false);
409
else if (boardSvg.isEmpty()) {
410
boardSvg = outlineSvg;
413
return TextUtils::convertExtendedChars(TextUtils::mergeSvg(boardSvg, svg, "", flip));
416
QString MainWindow::getBoardSvg(ItemBase * board, int res, LayerList & viewLayerIDs) {
417
if (board == NULL) return ___emptyString___;
419
board = board->layerKinChief();
420
QList<ItemBase *> boardLayers;
421
boardLayers << board;
422
foreach (ItemBase * lk, board->layerKin()) {
427
foreach (ItemBase * boardLayer, boardLayers) {
428
if (viewLayerIDs.contains(boardLayer->viewLayerID())) {
434
if (!gotOne) return "";
436
m_pcbGraphicsView->setIgnoreSelectionChangeEvents(true);
438
QList<QGraphicsItem *> items = m_pcbGraphicsView->scene()->selectedItems();
439
foreach (QGraphicsItem * item, items) {
440
item->setSelected(false);
442
board->setSelected(true);
446
QString svg = m_pcbGraphicsView->renderToSVG(GraphicsUtils::SVGDPI, viewLayerIDs, true, imageSize, board, res, true, false, empty);
447
board->setSelected(false);
448
foreach (QGraphicsItem * item, items) {
449
item->setSelected(true);
452
m_pcbGraphicsView->setIgnoreSelectionChangeEvents(false);
458
void MainWindow::doExport() {
459
QAction * action = qobject_cast<QAction *>(sender());
460
if (action == NULL) return;
462
QString actionType = action->data().toString();
463
QString path = defaultSaveFolder();
465
if (actionType.compare(eagleActionType) == 0) {
470
if (actionType.compare(gerberActionType) == 0) {
475
if (actionType.compare(bomActionType) == 0) {
480
if (actionType.compare(netlistActionType) == 0) {
485
if (actionType.compare(svgActionType) == 0) {
486
exportSvg(GraphicsUtils::IllustratorDPI, false, false);
490
#ifndef QT_NO_PRINTER
492
QString extFmt = fileExtFormats.value(actionType);
493
DebugDialog::debug(QString("file export string %1").arg(extFmt));
494
QString fileName = FolderUtils::getSaveFileName(this,
496
path+"/"+constructFileName("", actionType),
501
if (fileName.isEmpty()) {
502
return; //Cancel pressed
504
FileProgressDialog * fileProgressDialog = exportProgress();
505
DebugDialog::debug(fileExt+" selected to export");
506
if(!alreadyHasExtension(fileName, actionType)) {
507
fileName += actionType;
510
if(filePrintFormats.contains(actionType)) { // PDF or PS
511
QPrinter printer(QPrinter::HighResolution);
512
printer.setOutputFormat(filePrintFormats[actionType]);
513
printer.setOutputFileName(fileName);
514
m_statusBar->showMessage(tr("Exporting..."));
515
printAux(printer, true, false);
516
m_statusBar->showMessage(tr("Sketch exported"), 2000);
518
DebugDialog::debug(QString("format: %1 %2").arg(fileExt).arg(fileExportFormats[actionType]));
519
exportAux(fileName,fileExportFormats[actionType], true);
521
delete fileProgressDialog;
527
void MainWindow::exportAux(QString fileName, QImage::Format format, bool removeBackground)
529
double resMultiplier = 3;
531
QRectF source = m_currentGraphicsView->scene()->itemsBoundingRect();
532
QGraphicsItem * watermark = m_currentGraphicsView->addWatermark(":resources/images/watermark_fritzing_outline.svg");
534
watermark->setPos(source.right() - watermark->boundingRect().width(), source.bottom());
535
source.adjust(0, 0, 0, watermark->boundingRect().height());
538
int width = source.width();
539
int height = source.height();
542
int width = m_currentGraphicsView->width();
543
if (m_currentGraphicsView->verticalScrollBar()->isVisible()) {
544
width -= m_currentGraphicsView->verticalScrollBar()->width();
546
int height = m_currentGraphicsView->height();
547
if (m_currentGraphicsView->horizontalScrollBar()->isVisible()) {
548
height -= m_currentGraphicsView->horizontalScrollBar()->height();
552
QSize imgSize(width * resMultiplier, height * resMultiplier);
553
QImage image(imgSize,format);
554
image.setDotsPerMeterX(InchesPerMeter * GraphicsUtils::SVGDPI * resMultiplier);
555
image.setDotsPerMeterY(InchesPerMeter * GraphicsUtils::SVGDPI * resMultiplier);
558
if (removeBackground) {
559
color = m_currentGraphicsView->background();
560
m_currentGraphicsView->setBackground(QColor::fromRgb(255,255,255,255));
563
painter.begin(&image);
564
//m_currentGraphicsView->render(&painter);
565
QRectF target(0, 0, imgSize.width(), imgSize.height());
566
m_currentGraphicsView->scene()->render(&painter, target, source, Qt::KeepAspectRatio);
569
if (removeBackground) {
570
m_currentGraphicsView->setBackground(color);
577
QImageWriter imageWriter(fileName);
578
if (imageWriter.supportsOption(QImageIOHandler::Description)) {
579
imageWriter.setText("", TextUtils::CreatedWithFritzingString);
581
imageWriter.setQuality(100);
582
bool result = imageWriter.write(image);
584
QMessageBox::warning(this, tr("Fritzing"), tr("Unable to save %1").arg(fileName) );
588
void MainWindow::printAux(QPrinter &printer, bool removeBackground, bool paginate) {
589
int res = printer.resolution();
590
double scale2 = res / GraphicsUtils::SVGDPI;
591
DebugDialog::debug(QString("p.w:%1 p.h:%2 pager.w:%3 pager.h:%4 paperr.w:%5 paperr.h:%6 source.w:%7 source.h:%8")
592
.arg(printer.width())
593
.arg(printer.height())
594
.arg(printer.pageRect().width())
595
.arg(printer.pageRect().height())
596
.arg(printer.paperRect().width())
597
.arg(printer.paperRect().height())
598
.arg(printer.width() / scale2)
599
.arg(printer.height() / scale2) );
601
// oSceneStart oSceneEnd: shows only what's visible in the viewport, not the entire view
602
//QPointF oSceneStart = m_currentGraphicsView->mapToScene(QPoint(0,0));
603
//QPointF oSceneEnd = m_currentGraphicsView->mapToScene(QPoint(m_currentGraphicsView->viewport()->width(), m_currentGraphicsView->viewport()->height()));
604
//QRectF source(oSceneStart, oSceneEnd);
606
QRectF source = m_currentGraphicsView->scene()->itemsBoundingRect();
607
QGraphicsItem * watermark = m_currentGraphicsView->addWatermark(":resources/images/watermark_fritzing_outline.svg");
609
watermark->setPos(source.right() - watermark->boundingRect().width(), source.bottom());
610
source.adjust(0, 0, 0, watermark->boundingRect().height());
613
QRectF target(0, 0, source.width() * scale2, source.height() * scale2);
616
QSizeF psize((target.width() + printer.paperRect().width() - printer.width()) / res,
617
(target.height() + printer.paperRect().height() - printer.height()) / res);
618
printer.setPaperSize(psize, QPrinter::Inch);
622
if (!painter.begin(&printer)) {
626
QMessageBox::warning(this, tr("Fritzing"), tr("Cannot print to %1").arg(printer.docName()));
631
if(removeBackground) {
632
color = m_currentGraphicsView->background();
633
m_currentGraphicsView->setBackground(QColor::fromRgb(255,255,255,255));
636
QList<QGraphicsItem*> selItems = m_currentGraphicsView->scene()->selectedItems();
637
foreach(QGraphicsItem *item, selItems) {
638
item->setSelected(false);
642
int xPages = qCeil(target.width() / printer.width());
643
int yPages = qCeil(target.height() / printer.height());
644
int lastPage = xPages * yPages;
646
int xSourcePage = qFloor(printer.width() / scale2);
647
int ySourcePage = qFloor(printer.height() / scale2);
650
for (int iy = 0; iy < yPages; iy++) {
651
for (int ix = 0; ix < xPages; ix++) {
652
// render to printer:
653
QRectF pSource((ix * xSourcePage) + source.left(),
654
(iy * ySourcePage) + source.top(),
655
qMin(xSourcePage, (int) source.width() - (ix * xSourcePage)),
656
qMin(ySourcePage, (int) source.height() - (iy * ySourcePage)));
657
QRectF pTarget(0, 0, pSource.width() * scale2, pSource.height() * scale2);
658
m_currentGraphicsView->scene()->render(&painter, pTarget, pSource, Qt::KeepAspectRatio);
659
if (++page < lastPage) {
666
m_currentGraphicsView->scene()->render(&painter, target, source, Qt::KeepAspectRatio);
669
foreach(QGraphicsItem *item, selItems) {
670
item->setSelected(true);
673
if(removeBackground) {
674
m_currentGraphicsView->setBackground(color);
681
DebugDialog::debug(QString("source w:%1 h:%2 target w:%5 h:%6 pres:%3 screenres:%4")
683
.arg(source.height()).arg(res).arg(this->physicalDpiX())
684
.arg(target.width()).arg(target.height()) );
686
//#ifndef QT_NO_CONCURRENT
687
//QProgressDialog dialog;
688
//dialog.setLabelText(message);
690
// Create a QFutureWatcher and conncect signals and slots.
691
//QFutureWatcher<void> futureWatcher;
692
//QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset()));
693
//QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel()));
694
//QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int)));
695
//QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int)));
697
// Start the computation.
698
//futureWatcher.setFuture(QtConcurrent::run(painter,&QPainter::end));
701
//futureWatcher.waitForFinished();
704
//#ifdef QT_NO_CONCURRENT
710
bool MainWindow::saveAsAux(const QString & fileName) {
711
QFile file(fileName);
712
if (!file.open(QFile::WriteOnly | QFile::Text)) {
713
QMessageBox::warning(this, tr("Fritzing"),
714
tr("Cannot write file %1:\n%2.")
716
.arg(file.errorString()));
723
//FritzingWindow::saveAsAux(fileName);
725
saveAsAuxAux(fileName);
726
m_autosaveNeeded = false;
727
undoStackCleanChanged(true);
729
m_statusBar->showMessage(tr("Saved '%1'").arg(fileName), 2000);
730
setCurrentFile(fileName, true, true);
732
if(m_restarting && !m_fwFilename.isEmpty()) {
734
settings.setValue("lastOpenSketch",m_fwFilename);
737
// mark the stack clean so we update the window dirty flag
738
m_undoStack->setClean();
740
// slam it here in case we were modified due to m_linkedProgramFiles changes
741
setWindowModified(false);
743
m_saveAct->setEnabled(true);
748
void MainWindow::saveAsAuxAux(const QString & fileName) {
749
QApplication::setOverrideCursor(Qt::WaitCursor);
751
connectStartSave(true);
753
QDir dir(this->m_fzzFolder);
754
QStringList nameFilters("*" + FritzingSketchExtension);
755
QFileInfoList fileList = dir.entryInfoList(nameFilters, QDir::Files | QDir::NoSymLinks);
756
foreach (QFileInfo fileInfo, fileList) {
757
QFile file(fileInfo.absoluteFilePath());
761
QString fzName = dir.absoluteFilePath(QFileInfo(fileName).completeBaseName() + FritzingSketchExtension);
762
m_sketchModel->save(fzName, false);
764
saveAsShareable(fileName, false);
766
connectStartSave(false);
768
QApplication::restoreOverrideCursor();
772
void MainWindow::saveAsShareable(const QString & path, bool saveModel)
774
QString filename = path;
775
QHash<QString, ModelPart *> saveParts;
776
foreach (QGraphicsItem * item, m_pcbGraphicsView->scene()->items()) {
777
ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
778
if (itemBase == NULL) continue;
779
if (itemBase->modelPart()->isCore()) continue;
781
saveParts.insert(itemBase->moduleID(), itemBase->modelPart());
783
saveBundledNonAtomicEntity(filename, FritzingBundleExtension, this, saveParts.values(), false, m_fzzFolder, saveModel, true);
787
void MainWindow::saveBundledNonAtomicEntity(QString &filename, const QString &extension, Bundler *bundler, const QList<ModelPart*> &partsToSave, bool askForFilename, const QString & destFolderPath, bool saveModel, bool deleteLeftovers) {
791
QString path = defaultSaveFolder() + "/" + QFileInfo(filename).fileName()+"z";
792
QString bundledFileName = askForFilename
793
? FolderUtils::getSaveFileName(this, tr("Specify a file name"), path, tr("Fritzing (*%1)").arg(extension), &fileExt)
796
if (bundledFileName.isEmpty()) return; // Cancel pressed
798
FileProgressDialog progress("Saving...", 0, this);
800
if(!alreadyHasExtension(bundledFileName, extension)) {
801
bundledFileName += extension;
804
ProcessEventBlocker::processEvents();
808
if (destFolderPath.isEmpty()) {
809
destFolder = QDir::temp();
810
FolderUtils::createFolderAnCdIntoIt(destFolder, FolderUtils::getRandText());
811
dirToRemove = destFolder.path();
814
destFolder = QDir(destFolderPath);
817
QString aux = QFileInfo(bundledFileName).fileName();
818
QString destSketchPath = // remove the last "z" from the extension
819
destFolder.path()+"/"+aux.left(aux.size()-1);
820
DebugDialog::debug("saving entity temporarily to "+destSketchPath);
822
QStringList skipSuffixes;
824
if (extension.compare(FritzingBundleExtension) == 0) {
825
for (int i = 0; i < m_linkedProgramFiles.count(); i++) {
826
LinkedFile * linkedFile = m_linkedProgramFiles.at(i);
827
QFileInfo fileInfo(linkedFile->linkedFilename);
828
QFile file(linkedFile->linkedFilename);
829
file.copy(destFolder.absoluteFilePath(fileInfo.fileName()));
831
skipSuffixes << FritzingBinExtension << FritzingBundleExtension;
835
QString prevFileName = filename;
836
ProcessEventBlocker::processEvents();
837
bundler->saveAsAux(destSketchPath);
838
filename = prevFileName;
841
foreach(ModelPart* mp, partsToSave) {
842
names.append(saveBundledAux(mp, destFolder));
845
if (deleteLeftovers) {
846
QStringList nameFilters;
847
nameFilters << ("*" + FritzingPartExtension) << "*.svg";
848
QDir dir(destFolder);
849
QStringList fileList = dir.entryList(nameFilters, QDir::Files | QDir::NoSymLinks);
850
foreach (QString fileName, fileList) {
851
if (!names.contains(fileName)) {
852
QFile::remove(dir.absoluteFilePath(fileName));
857
QApplication::processEvents();
859
if(!FolderUtils::createZipAndSaveTo(destFolder, bundledFileName, skipSuffixes)) {
860
QMessageBox::warning(
863
tr("Unable to export %1 as shareable").arg(bundledFileName)
867
if (!dirToRemove.isEmpty()) {
868
FolderUtils::rmdir(dirToRemove);
873
void MainWindow::createExportActions() {
875
m_saveAct = new QAction(tr("&Save"), this);
876
m_saveAct->setShortcut(tr("Ctrl+S"));
877
m_saveAct->setStatusTip(tr("Save the current sketch"));
878
connect(m_saveAct, SIGNAL(triggered()), this, SLOT(save()));
880
m_saveAsAct = new QAction(tr("&Save As..."), this);
881
m_saveAsAct->setShortcut(tr("Shift+Ctrl+S"));
882
m_saveAsAct->setStatusTip(tr("Save the current sketch"));
883
connect(m_saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));
885
m_shareOnlineAct = new QAction(tr("Share online..."), this);
886
m_shareOnlineAct->setStatusTip(tr("Post a project to the Fritzing website"));
887
connect(m_shareOnlineAct, SIGNAL(triggered()), this, SLOT(shareOnline()));
889
m_exportJpgAct = new QAction(tr("JPG..."), this);
890
m_exportJpgAct->setData(jpgActionType);
891
m_exportJpgAct->setStatusTip(tr("Export the visible area of the current sketch as a JPG image"));
892
connect(m_exportJpgAct, SIGNAL(triggered()), this, SLOT(doExport()));
894
m_exportPngAct = new QAction(tr("PNG..."), this);
895
m_exportPngAct->setData(pngActionType);
896
m_exportPngAct->setStatusTip(tr("Export the visible area of the current sketch as a PNG image"));
897
connect(m_exportPngAct, SIGNAL(triggered()), this, SLOT(doExport()));
899
m_exportPsAct = new QAction(tr("PostScript..."), this);
900
m_exportPsAct->setData(psActionType);
901
m_exportPsAct->setStatusTip(tr("Export the visible area of the current sketch as a PostScript image"));
902
connect(m_exportPsAct, SIGNAL(triggered()), this, SLOT(doExport()));
904
m_exportPdfAct = new QAction(tr("PDF..."), this);
905
m_exportPdfAct->setData(pdfActionType);
906
m_exportPdfAct->setStatusTip(tr("Export the visible area of the current sketch as a PDF image"));
907
connect(m_exportPdfAct, SIGNAL(triggered()), this, SLOT(doExport()));
909
m_exportSvgAct = new QAction(tr("SVG..."), this);
910
m_exportSvgAct->setData(svgActionType);
911
m_exportSvgAct->setStatusTip(tr("Export the current sketch as an SVG image"));
912
connect(m_exportSvgAct, SIGNAL(triggered()), this, SLOT(doExport()));
914
m_exportBomAct = new QAction(tr("List of parts (&Bill of Materials)..."), this);
915
m_exportBomAct->setData(bomActionType);
916
m_exportBomAct->setStatusTip(tr("Save a Bill of Materials (BoM)/Shopping List as text"));
917
connect(m_exportBomAct, SIGNAL(triggered()), this, SLOT(doExport()));
919
m_exportNetlistAct = new QAction(tr("XML Netlist..."), this);
920
m_exportNetlistAct->setData(netlistActionType);
921
m_exportNetlistAct->setStatusTip(tr("Save a netlist in XML format"));
922
connect(m_exportNetlistAct, SIGNAL(triggered()), this, SLOT(doExport()));
924
m_exportEagleAct = new QAction(tr("Eagle..."), this);
925
m_exportEagleAct->setData(eagleActionType);
926
m_exportEagleAct->setStatusTip(tr("Export the current sketch to Eagle CAD"));
927
connect(m_exportEagleAct, SIGNAL(triggered()), this, SLOT(doExport()));
929
m_exportGerberAct = new QAction(tr("Extended Gerber (RS-274X)..."), this);
930
m_exportGerberAct->setData(gerberActionType);
931
m_exportGerberAct->setStatusTip(tr("Export the current sketch to Extended Gerber format (RS-274X) for professional PCB production"));
932
connect(m_exportGerberAct, SIGNAL(triggered()), this, SLOT(doExport()));
934
m_exportEtchablePdfAct = new QAction(tr("Etchable (PDF)..."), this);
935
m_exportEtchablePdfAct->setStatusTip(tr("Export the current sketch to PDF for DIY PCB production (photoresist)"));
936
m_exportEtchablePdfAct->setProperty("svg", false);
937
connect(m_exportEtchablePdfAct, SIGNAL(triggered()), this, SLOT(exportEtchable()));
939
m_exportEtchableSvgAct = new QAction(tr("Etchable (SVG)..."), this);
940
m_exportEtchableSvgAct->setStatusTip(tr("Export the current sketch to SVG for DIY PCB production (photoresist)"));
941
m_exportEtchableSvgAct->setProperty("svg", true);
942
connect(m_exportEtchableSvgAct, SIGNAL(triggered()), this, SLOT(exportEtchable()));
944
/*m_pageSetupAct = new QAction(tr("&Page Setup..."), this);
945
m_pageSetupAct->setShortcut(tr("Shift+Ctrl+P"));
946
m_pageSetupAct->setStatusTip(tr("Setup the current sketch page"));
947
connect(m_pageSetupAct, SIGNAL(triggered()), this, SLOT(pageSetup()));*/
949
m_printAct = new QAction(tr("&Print..."), this);
950
m_printAct->setShortcut(tr("Ctrl+P"));
951
m_printAct->setStatusTip(tr("Print the current view"));
952
connect(m_printAct, SIGNAL(triggered()), this, SLOT(print()));
956
void MainWindow::exportToEagle() {
959
tr("This will soon provide an export of your Fritzing sketch to the EAGLE layout "
960
"software. If you'd like to have more exports to your favourite EDA tool, please let "
961
"us know, or contribute.");
964
tr("The Eagle export module is very experimental. If anything breaks or behaves "
965
"strangely, please let us know.");
968
QMessageBox::information(this, tr("Fritzing"), text);
970
Fritzing2Eagle eagle = Fritzing2Eagle(m_pcbGraphicsView);
973
QList <ItemBase*> partList;
975
// bail out if something is wrong
976
// TODO: show an error in QMessageBox
977
if(m_currentWidget == NULL) {
981
m_pcbGraphicsView->collectParts(partList);
983
QString exportInfoString = tr("parts include:\n");
984
QString exportString = tr("GRID INCH 0.005\n");
987
for(int i=0; i < partList.size(); i++){
988
QString label = partList.at(i)->instanceTitle();
989
QString desc = partList.at(i)->title();
991
QHash<QString,QString> properties = partList.at(i)->modelPartShared()->properties();
992
QString package = properties["package"];
993
if (package == NULL) {
994
package = tr("*** package not specified ***");
997
exportInfoString += label + tr(" which is a ") + desc + tr(" in a ") + package + tr(" package.\n");
999
QMessageBox::information(this, tr("Fritzing"), exportInfoString);
1003
QFile fp( fileName );
1004
fp.open(QIODevice::WriteOnly);
1005
fp.write(bom.toUtf8(),bom.length());
1012
USE '/Applications/eclipse/eagle/lbr/fritzing.lbr';
1013
ADD RESISTOR@fritzing 'R_1' R0.000 (2.3055117 2.1307087);
1014
ADD LED@fritzing 'L_2' R0.000 (5.423622 2.425197);
1019
void MainWindow::exportSvg(double res, bool selectedItems, bool flatten) {
1020
QString path = defaultSaveFolder();
1022
QString fileName = FolderUtils::getSaveFileName(this,
1023
tr("Export SVG..."),
1024
path+"/"+constructFileName("", svgActionType),
1025
fileExtFormats[svgActionType],
1029
if (fileName.isEmpty()) return;
1031
FileProgressDialog * fileProgressDialog = exportProgress();
1032
LayerList viewLayerIDs;
1033
foreach (ViewLayer * viewLayer, m_currentGraphicsView->viewLayers()) {
1034
if (viewLayer == NULL) continue;
1035
if (!viewLayer->visible()) continue;
1037
viewLayerIDs << viewLayer->viewLayerID();
1042
QString svg = m_currentGraphicsView->renderToSVG(GraphicsUtils::SVGDPI, viewLayerIDs, false, imageSize, NULL, res, selectedItems, false, empty);
1043
if (svg.isEmpty()) {
1044
// tell the user something reasonable
1048
if (selectedItems == false && flatten == false) {
1049
exportSvgWatermark(svg, res);
1052
TextUtils::writeUtf8(fileName, TextUtils::convertExtendedChars(svg));
1053
delete fileProgressDialog;
1057
void MainWindow::exportSvgWatermark(QString & svg, double res)
1059
QFile file(":/resources/images/watermark_fritzing_outline.svg");
1060
if (!file.open(QFile::ReadOnly)) return;
1062
QString watermarkSvg = file.readAll();
1065
if (!watermarkSvg.contains("<svg")) return;
1067
QSizeF watermarkSize = TextUtils::parseForWidthAndHeight(watermarkSvg);
1068
QSizeF svgSize = TextUtils::parseForWidthAndHeight(svg);
1070
SvgFileSplitter splitter;
1071
bool result = splitter.splitString(watermarkSvg, "watermark");
1072
if (!result) return;
1074
result = splitter.normalize(res, "watermark", false);
1075
if (!result) return;
1077
QString transWatermark = splitter.shift((svgSize.width() - watermarkSize.width()) * res, svgSize.height() * res, "watermark", true);
1078
QString newSvg = TextUtils::makeSVGHeader(1, res, svgSize.width(), svgSize.height() + watermarkSize.height()) + transWatermark + "</svg>";
1079
svg = TextUtils::mergeSvg(newSvg, svg, "", false);
1082
void MainWindow::exportBOM() {
1084
// bail out if something is wrong
1085
// TODO: show an error in QMessageBox
1086
if (m_currentWidget == NULL) {
1090
QString bomTemplate;
1091
QFile file(":/resources/templates/bom.html");
1092
if (file.open(QFile::ReadOnly)) {
1093
bomTemplate = file.readAll();
1100
QString bomRowTemplate;
1101
QFile file2(":/resources/templates/bom_row.html");
1102
if (file2.open(QFile::ReadOnly)) {
1103
bomRowTemplate = file2.readAll();
1110
QList <ItemBase*> partList;
1111
QMap<QString, int> shoppingList;
1112
QHash<QString, ItemBase *> descrs;
1114
m_currentGraphicsView->collectParts(partList);
1116
qSort(partList.begin(), partList.end(), sortPartList);
1118
foreach (ItemBase * itemBase, partList) {
1119
if (itemBase->itemType() != ModelPart::Part) continue;
1121
QString desc = getBomProps(itemBase);
1123
if(!shoppingList.contains(desc)) {
1124
shoppingList.insert(desc, 1);
1125
descrs.insert(desc, itemBase);
1128
shoppingList[desc]++;
1133
QString assemblyString;
1134
foreach (ItemBase * itemBase, partList) {
1135
if (itemBase->itemType() != ModelPart::Part) continue;
1137
assemblyString += bomRowTemplate.arg(itemBase->instanceTitle()).arg(itemBase->title()).arg(getBomProps(itemBase));
1140
QString shoppingListString;
1141
QMapIterator<QString, int> it(shoppingList);
1142
while (it.hasNext()) {
1144
ItemBase * itemBase = descrs.value(it.key());
1146
// it.value() = count; it.key() = bomProps
1147
shoppingListString += bomRowTemplate.arg(it.value()).arg(itemBase->title()).arg(it.key());
1150
QString bom = bomTemplate
1151
.arg("Fritzing Bill of Materials")
1152
.arg(QFileInfo(m_fwFilename).fileName())
1154
.arg(QDateTime::currentDateTime().toString("dddd, MMMM d yyyy, hh:mm:ss"))
1155
.arg(assemblyString)
1156
.arg(shoppingListString)
1157
.arg(QString("%1.%2.%3").arg(Version::majorVersion()).arg(Version::minorVersion()).arg(Version::minorSubVersion()));
1160
QString path = defaultSaveFolder();
1163
QString extFmt = fileExtFormats.value(bomActionType);
1164
QString fname = path+"/"+constructFileName("bom", bomActionType);
1165
DebugDialog::debug(QString("fname %1\n%2").arg(fname).arg(extFmt));
1167
QString fileName = FolderUtils::getSaveFileName(this,
1168
tr("Export Bill of Materials (BoM)..."),
1174
if (fileName.isEmpty()) {
1175
return; //Cancel pressed
1178
FileProgressDialog * fileProgressDialog = exportProgress();
1179
DebugDialog::debug(fileExt+" selected to export");
1180
if(!alreadyHasExtension(fileName, bomActionType)) {
1181
fileName += bomActionType;
1184
if (!TextUtils::writeUtf8(fileName, bom)) {
1185
QMessageBox::warning(this, tr("Fritzing"), tr("Unable to save BOM file, but the text is on the clipboard."));
1188
QFileInfo info(fileName);
1189
if (info.exists()) {
1190
QDesktopServices::openUrl(QString("file:///%1").arg(fileName));
1193
QClipboard *clipboard = QApplication::clipboard();
1194
if (clipboard != NULL) {
1195
clipboard->setText(bom);
1197
delete fileProgressDialog;
1200
void MainWindow::exportNetlist() {
1201
QHash<ConnectorItem *, int> indexer;
1202
QList< QList<ConnectorItem *>* > netList;
1203
this->m_currentGraphicsView->collectAllNets(indexer, netList, true, m_currentGraphicsView->boardLayers() > 1);
1206
doc.setContent(QString("<?xml version='1.0' encoding='UTF-8'?>\n") + TextUtils::CreatedWithFritzingXmlComment);
1207
QDomElement netlist = doc.createElement("netlist");
1208
doc.appendChild(netlist);
1209
netlist.setAttribute("sketch", QFileInfo(m_fwFilename).fileName());
1210
netlist.setAttribute("date", QDateTime::currentDateTime().toString());
1212
// TODO: filter out 'ignore' connectors
1214
QList< QList<ConnectorItem *>* > deleteNets;
1215
foreach (QList<ConnectorItem *> * net, netList) {
1216
QList<ConnectorItem *> deleteItems;
1217
foreach (ConnectorItem * connectorItem, *net) {
1218
ErcData * ercData = connectorItem->connectorSharedErcData();
1219
if (ercData == NULL) continue;
1221
if (ercData->ignore() == ErcData::Always) {
1222
deleteItems.append(connectorItem);
1224
else if ((ercData->ignore() == ErcData::IfUnconnected) && (net->count() == 1)) {
1225
deleteItems.append(connectorItem);
1229
foreach (ConnectorItem * connectorItem, deleteItems) {
1230
net->removeOne(connectorItem);
1232
if (net->count() == 0) {
1233
deleteNets.append(net);
1237
foreach (QList<ConnectorItem *> * net, deleteNets) {
1238
netList.removeOne(net);
1241
foreach (QList<ConnectorItem *> * net, netList) {
1242
QDomElement netElement = doc.createElement("net");
1243
netlist.appendChild(netElement);
1244
foreach (ConnectorItem * connectorItem, *net) {
1245
QDomElement connector = doc.createElement("connector");
1246
netElement.appendChild(connector);
1247
connector.setAttribute("id", connectorItem->connectorSharedID());
1248
connector.setAttribute("name", connectorItem->connectorSharedName());
1249
QDomElement part = doc.createElement("part");
1250
connector.appendChild(part);
1251
ItemBase * itemBase = connectorItem->attachedTo();
1252
part.setAttribute("id", itemBase->id());
1253
part.setAttribute("label", itemBase->instanceTitle());
1254
part.setAttribute("title", itemBase->title());
1255
ErcData * ercData = connectorItem->connectorSharedErcData();
1256
if (ercData != NULL) {
1257
QDomElement erc = doc.createElement("erc");
1258
if (ercData->writeToElement(erc, doc)) {
1259
connector.appendChild(erc);
1265
foreach (QList<ConnectorItem *> * net, netList) {
1270
QString path = defaultSaveFolder();
1273
QString extFmt = fileExtFormats.value(netlistActionType);
1274
QString fname = path + "/" +constructFileName("netlist", netlistActionType);
1275
//DebugDialog::debug(QString("fname %1\n%2").arg(fname).arg(extFmt));
1277
QString fileName = FolderUtils::getSaveFileName(this,
1278
tr("Export Netlist..."),
1284
if (fileName.isEmpty()) {
1285
return; //Cancel pressed
1288
FileProgressDialog * fileProgressDialog = exportProgress();
1289
//DebugDialog::debug(fileExt + " selected to export");
1290
if(!alreadyHasExtension(fileName, netlistActionType)) {
1291
fileName += netlistActionType;
1294
QFile fp( fileName );
1295
fp.open(QIODevice::WriteOnly);
1296
fp.write(doc.toByteArray());
1299
QClipboard *clipboard = QApplication::clipboard();
1300
if (clipboard != NULL) {
1301
clipboard->setText(doc.toByteArray());
1303
delete fileProgressDialog;
1308
FileProgressDialog * MainWindow::exportProgress() {
1309
return (new FileProgressDialog("Exporting...", 0, this));
1312
void MainWindow::exportNormalizedSVG() {
1313
exportSvg(GraphicsUtils::StandardFritzingDPI, true, false);
1316
void MainWindow::exportNormalizedFlattenedSVG() {
1317
exportSvg(GraphicsUtils::StandardFritzingDPI, true, true);
1320
QString MainWindow::getBomProps(ItemBase * itemBase)
1322
if (itemBase == NULL) return "";
1325
QHash<QString, QString> properties = HtmlInfoView::getPartProperties(itemBase->modelPart(), itemBase, false, keys);
1327
foreach (QString key, keys) {
1328
if (key.compare("family") == 0) continue;
1330
QString value = properties.value(key);
1333
QWidget * resultWidget = NULL;
1334
QString resultKey, resultValue;
1335
itemBase->collectExtraInfo(&widget, properties.value("family"), key, value, false, resultKey, resultValue, resultWidget);
1336
if (resultValue.isEmpty()) continue;
1338
pString += ItemBase::translatePropertyName(resultKey) + " " + resultValue + "; ";
1341
if (pString.length() > 2) pString.chop(2);
1346
void MainWindow::exportToGerber() {
1348
//NOTE: this assumes just one board per sketch
1351
ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount);
1353
// barf an error if there's no board
1354
if (boardCount == 0) {
1355
QMessageBox::critical(this, tr("Fritzing"),
1356
tr("Your sketch does not have a board yet! Please add a PCB in order to export to Gerber."));
1359
if (board == NULL) {
1360
QMessageBox::critical(this, tr("Fritzing"),
1361
tr("Gerber export can only handle one board at a time--please select the board you want to export."));
1365
QString exportDir = QFileDialog::getExistingDirectory(this, tr("Choose a folder for exporting"),
1366
defaultSaveFolder(),
1367
QFileDialog::ShowDirsOnly
1368
| QFileDialog::DontResolveSymlinks);
1370
if (exportDir.isEmpty()) return;
1372
FileProgressDialog * fileProgressDialog = exportProgress();
1374
FolderUtils::setOpenSaveFolder(exportDir);
1375
m_pcbGraphicsView->saveLayerVisibility();
1376
m_pcbGraphicsView->setAllLayersVisible(true);
1378
QFileInfo info(m_fwFilename);
1379
QString prefix = info.completeBaseName();
1380
if (boardCount > 1) {
1381
prefix += QString("_%1_%2").arg(board->instanceTitle()).arg(board->id());
1383
GerberGenerator::exportToGerber(prefix, exportDir, board, m_pcbGraphicsView, true);
1384
m_pcbGraphicsView->restoreLayerVisibility();
1385
m_statusBar->showMessage(tr("Sketch exported to Gerber"), 2000);
1387
delete fileProgressDialog;
1390
void MainWindow::exportToGerber(const QString & exportDir) {
1391
GerberGenerator::exportToGerber(m_fwFilename, exportDir, NULL, m_pcbGraphicsView, false);
1394
void MainWindow::connectStartSave(bool doConnect) {
1397
connect(m_sketchModel->root(), SIGNAL(startSaveInstances(const QString &, ModelPart *, QXmlStreamWriter &)),
1398
this, SLOT(startSaveInstancesSlot(const QString &, ModelPart *, QXmlStreamWriter &)), Qt::DirectConnection);
1401
disconnect(m_sketchModel->root(), SIGNAL(startSaveInstances(const QString &, ModelPart *, QXmlStreamWriter &)),
1402
this, SLOT(startSaveInstancesSlot(const QString &, ModelPart *, QXmlStreamWriter &)));
1406
QString MainWindow::constructFileName(const QString & differentiator, const QString & suffix) {
1407
QString fn = QFileInfo(m_fwFilename).completeBaseName();
1408
fn += "_" + (differentiator.isEmpty() ? m_currentGraphicsView->getShortName() : differentiator);
1412
void MainWindow::massageOutput(QString & svg, bool doMask, bool doSilk, bool doPaste, QString & maskTop, QString & maskBottom, const QString & fileName, ItemBase * board, int dpi, const LayerList & viewLayerIDs)
1415
// must test doPaste first, since doMask will also be true
1416
svg = pcbView()->makePasteMask(svg, board, dpi, viewLayerIDs);
1419
QString use = (fileName.contains("bottom")) ? maskBottom : maskTop;
1420
use = TextUtils::expandAndFill(use, "white", GerberGenerator::MaskClearanceMils * 2 * dpi / 1000);
1421
svg = TextUtils::mergeSvg(svg, use, "", false);
1424
if (fileName.contains("bottom")) maskBottom = svg; else maskTop = svg;
1425
svg = TextUtils::expandAndFill(svg, "black", GerberGenerator::MaskClearanceMils * 2 * dpi / 1000);