~ubuntu-branches/ubuntu/trusty/fritzing/trusty-proposed

« back to all changes in this revision

Viewing changes to src/mainwindow_export.cpp

  • Committer: Package Import Robot
  • Author(s): Enrique Hernández Bello
  • Date: 2012-11-11 21:38:56 UTC
  • mfrom: (1.1.5)
  • Revision ID: package-import@ubuntu.com-20121111213856-0825ywdrtdcshl91
Tags: 0.7.10b-1
* New upstream version. Closes: #661495, #692998
* Removed useless patches.
* Removed SetupAPI.lib from sourceless files.
* Skip dfsg tarball creation if there are no sourceless files.
* Added libqt4-sql-sqlite to dependencies. Thanks to Tom Hummel <tom@bluespice.org>.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*******************************************************************
2
 
skw
3
 
Part of the Fritzing project - http://fritzing.org
4
 
Copyright (c) 2007-2012 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: 6265 $:
22
 
$Author: cohen@irascible.com $:
23
 
$Date: 2012-08-08 04:07:28 +0200 (Wed, 08 Aug 2012) $
24
 
 
25
 
********************************************************************/
26
 
 
27
 
#include <QtGui>
28
 
#include <QSvgGenerator>
29
 
#include <QColor>
30
 
#include <QImageWriter>
31
 
 
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"
70
 
 
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";
80
 
 
81
 
static QHash<QString, QPrinter::OutputFormat> filePrintFormats;
82
 
static QHash<QString, QImage::Format> fileExportFormats;
83
 
static QHash<QString, QString> fileExtFormats;
84
 
 
85
 
static QRegExp AaCc("[aAcC]");
86
 
static QRegExp LabelNumber("([^\\d]+)(.*)");
87
 
 
88
 
static const double InchesPerMeter = 39.3700787;
89
 
 
90
 
////////////////////////////////////////////////////////
91
 
 
92
 
bool sortPartList(ItemBase * b1, ItemBase * b2) {
93
 
    bool result = b1->instanceTitle().toLower() < b2->instanceTitle().toLower();
94
 
 
95
 
    int ix1 = LabelNumber.indexIn(b1->instanceTitle());
96
 
    if (ix1 < 0) return result;
97
 
 
98
 
    QString label1 = LabelNumber.cap(1);
99
 
    QString number1 = LabelNumber.cap(2);
100
 
 
101
 
    int ix2 = LabelNumber.indexIn(b2->instanceTitle());
102
 
    if (ix2 < 0) return result;
103
 
 
104
 
    QString label2 = LabelNumber.cap(1);
105
 
    QString number2 = LabelNumber.cap(2);
106
 
    if (label2.compare(label1, Qt::CaseInsensitive) != 0) return result;
107
 
 
108
 
    bool ok;
109
 
    double d1 = number1.toDouble(&ok);
110
 
    if (!ok) return result;
111
 
 
112
 
    double d2 = number2.toDouble(&ok);
113
 
    if (!ok) return result;
114
 
 
115
 
    return d1 < d2;
116
 
}
117
 
 
118
 
/////////////////////////////////////////////////////////
119
 
 
120
 
void MainWindow::initNames()
121
 
{
122
 
        OtherKnownExtensions << jpgActionType << psActionType << pdfActionType << pngActionType << svgActionType << bomActionType << netlistActionType;
123
 
 
124
 
        filePrintFormats[pdfActionType] = QPrinter::PdfFormat;
125
 
        filePrintFormats[psActionType] = QPrinter::PostScriptFormat;
126
 
 
127
 
        fileExportFormats[pngActionType] = QImage::Format_ARGB32;
128
 
        fileExportFormats[jpgActionType] = QImage::Format_RGB32;
129
 
 
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)");
136
 
 
137
 
        QSettings settings;
138
 
        AutosaveEnabled = settings.value("autosaveEnabled", QString("%1").arg(AutosaveEnabled)).toBool();
139
 
        AutosaveTimeoutMinutes = settings.value("autosavePeriod", QString("%1").arg(AutosaveTimeoutMinutes)).toInt();
140
 
}
141
 
 
142
 
void MainWindow::print() {
143
 
        #ifndef QT_NO_PRINTER
144
 
                QPrinter printer(QPrinter::HighResolution);
145
 
 
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);
151
 
                } else {
152
 
                        return;
153
 
                }
154
 
        #endif
155
 
}
156
 
 
157
 
void MainWindow::exportEtchable() {
158
 
        if (sender() == NULL) return;
159
 
 
160
 
    bool wantSvg = sender()->property("svg").toBool();
161
 
        exportEtchable(!wantSvg, wantSvg);
162
 
}
163
 
 
164
 
 
165
 
void MainWindow::exportEtchable(bool wantPDF, bool wantSVG)
166
 
{
167
 
    int boardCount;
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."));
172
 
        return;
173
 
    }
174
 
    if (board == NULL) {
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."));
177
 
        return;
178
 
    }
179
 
 
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;
193
 
        }
194
 
 
195
 
        QString path = defaultSaveFolder();
196
 
        QString extFmt = (wantPDF) ? fileExtFormats.value(pdfActionType) : fileExtFormats.value(svgActionType);
197
 
        QString fileExt = extFmt;
198
 
 
199
 
        QString suffix = (wantPDF) ? pdfActionType : svgActionType;
200
 
    QString prefix = "";
201
 
    if (boardCount > 1) {
202
 
        prefix = QString("%1_%2_").arg(board->instanceTitle()).arg(board->id());
203
 
    }
204
 
 
205
 
        QString exportDir = QFileDialog::getExistingDirectory(this, tr("Choose a folder for exporting"),
206
 
                                                                                                defaultSaveFolder(),
207
 
                                                                                                QFileDialog::ShowDirsOnly
208
 
                                                                                                | QFileDialog::DontResolveSymlinks);
209
 
        if (exportDir.isEmpty()) return;
210
 
 
211
 
        FolderUtils::setOpenSaveFolder(exportDir);
212
 
        FileProgressDialog * fileProgressDialog = exportProgress();
213
 
 
214
 
    QRectF r = board->sceneBoundingRect();
215
 
    QSizeF boardImageSize(r.width(), r.height());
216
 
  
217
 
        QStringList fileNames;
218
 
 
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));
226
 
        }
227
 
        fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_silk_top%1", suffix));
228
 
 
229
 
        QString maskTop, maskBottom;
230
 
        QList<ItemBase *> copperLogoItems, holes;
231
 
        for (int ix = 0; ix < fileNames.count(); ix++) {
232
 
                bool doMask = false;
233
 
                bool doSilk = false;
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;
239
 
                }
240
 
                else if (fileName.contains("mask_bottom")) {
241
 
                        doMask = true;
242
 
                        viewLayerIDs << ViewLayer::Copper0;
243
 
            doPaste = fileName.contains("paste");
244
 
                }
245
 
                else if (fileName.contains("copper_top")) {
246
 
                        viewLayerIDs << ViewLayer::GroundPlane1 << ViewLayer::Copper1 << ViewLayer::Copper1Trace;
247
 
                }
248
 
                else if (fileName.contains("mask_top")) {
249
 
                        viewLayerIDs << ViewLayer::Copper1;
250
 
                        doMask = true;
251
 
            doPaste = fileName.contains("paste");
252
 
                }
253
 
                else if (fileName.contains("silk_top")) {
254
 
                        viewLayerIDs << ViewLayer::Silkscreen1 << ViewLayer::Silkscreen1Label;
255
 
                        doSilk = true;
256
 
                }
257
 
 
258
 
                if (doMask) {
259
 
                        m_pcbGraphicsView->hideCopperLogoItems(copperLogoItems);
260
 
                }
261
 
                if (doPaste) {
262
 
                        m_pcbGraphicsView->hideHoles(holes);
263
 
                }
264
 
 
265
 
                if (wantSVG) {
266
 
                        bool empty;
267
 
                    QSizeF imageSize;
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);
274
 
                }
275
 
                else {
276
 
            QString svg;
277
 
            QList<bool> flips;
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();
285
 
 
286
 
                if (svg.isEmpty()) {
287
 
                                bool empty;
288
 
                    QSizeF imageSize;
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);
291
 
                }
292
 
                        
293
 
                QString merged = mergeBoardSvg(svg, board, res, flip, viewLayerIDs);
294
 
                        
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);
301
 
 
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);
305
 
 
306
 
                            QPainter painter;
307
 
                            if (painter.begin(&printer))
308
 
                            {
309
 
                                    svgRenderer.render(&painter, target);
310
 
                            }
311
 
 
312
 
                            painter.end();
313
 
            }
314
 
                }
315
 
                if (doMask) {
316
 
                        m_pcbGraphicsView->restoreCopperLogoItems(copperLogoItems);
317
 
                }
318
 
                if (doPaste) {
319
 
                        m_pcbGraphicsView->restoreCopperLogoItems(holes);
320
 
                }
321
 
 
322
 
        }
323
 
 
324
 
        m_statusBar->showMessage(tr("Sketch exported"), 2000);
325
 
        delete fileProgressDialog;
326
 
 
327
 
/*
328
 
 
329
 
        int width = m_pcbGraphicsView->width();
330
 
        if (m_pcbGraphicsView->verticalScrollBar()->isVisible()) {
331
 
                width -= m_pcbGraphicsView->verticalScrollBar()->width();
332
 
        }
333
 
        int height = m_pcbGraphicsView->height();
334
 
        if (m_pcbGraphicsView->horizontalScrollBar()->isVisible()) {
335
 
                height -= m_pcbGraphicsView->horizontalScrollBar()->height();
336
 
        }
337
 
 
338
 
        double trueWidth = width / m_printerScale;
339
 
        double trueHeight = height / m_printerScale;
340
 
 
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);
346
 
        QPainter painter;
347
 
 
348
 
        QColor color;
349
 
        color = m_pcbGraphicsView->background();
350
 
        m_pcbGraphicsView->setBackground(QColor::fromRgb(255,255,255,255));
351
 
 
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);
357
 
 
358
 
        painter.begin(&image);
359
 
        m_pcbGraphicsView->render(&painter);
360
 
        painter.end();
361
 
 
362
 
 
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);
368
 
        svgPainter.end();
369
 
 
370
 
 
371
 
        m_pcbGraphicsView->hideConnectors(false);
372
 
        m_pcbGraphicsView->setBackground(color);
373
 
        m_pcbGraphicsView->restoreLayerVisibility();
374
 
        // TODO: restore the selection
375
 
 
376
 
        QRgb black = 0;
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);
382
 
                        }
383
 
                }
384
 
        }
385
 
 
386
 
        bool result = image.save(fileName);
387
 
        if (!result) {
388
 
                QMessageBox::warning(this, tr("Fritzing"), tr("Unable to save %1").arg(fileName) );
389
 
        }
390
 
 
391
 
*/
392
 
 
393
 
}
394
 
 
395
 
QString MainWindow::mergeBoardSvg(QString & svg, ItemBase * board, int res, bool flip, LayerList & viewLayerIDs) {
396
 
        QString boardSvg = getBoardSvg(board, res, viewLayerIDs);
397
 
 
398
 
    LayerList outlineLayerIDs = ViewLayer::outlineLayers();
399
 
        QSizeF imageSize;
400
 
        bool empty;
401
 
 
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");
405
 
 
406
 
    if (!boardSvg.isEmpty() && !outlineSvg.isEmpty()) {
407
 
        boardSvg = TextUtils::mergeSvg(boardSvg, outlineSvg, "", false);
408
 
    }
409
 
    else if (boardSvg.isEmpty()) {
410
 
        boardSvg = outlineSvg;
411
 
    }
412
 
        
413
 
        return TextUtils::convertExtendedChars(TextUtils::mergeSvg(boardSvg, svg, "", flip));
414
 
}
415
 
 
416
 
QString MainWindow::getBoardSvg(ItemBase * board, int res,  LayerList & viewLayerIDs) {
417
 
        if (board == NULL) return ___emptyString___;
418
 
 
419
 
    board = board->layerKinChief();
420
 
    QList<ItemBase *> boardLayers;
421
 
    boardLayers << board;
422
 
    foreach (ItemBase * lk, board->layerKin()) {
423
 
        boardLayers << lk;
424
 
    }
425
 
 
426
 
    bool gotOne = false;
427
 
    foreach (ItemBase * boardLayer, boardLayers) {
428
 
        if (viewLayerIDs.contains(boardLayer->viewLayerID())) {
429
 
            gotOne = true;
430
 
            break;
431
 
        }
432
 
    }
433
 
 
434
 
    if (!gotOne) return "";
435
 
 
436
 
        m_pcbGraphicsView->setIgnoreSelectionChangeEvents(true);
437
 
 
438
 
        QList<QGraphicsItem *> items = m_pcbGraphicsView->scene()->selectedItems();
439
 
        foreach (QGraphicsItem * item, items) {
440
 
                item->setSelected(false);
441
 
        }
442
 
        board->setSelected(true);
443
 
 
444
 
        bool empty;
445
 
    QSizeF imageSize;
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);
450
 
        }
451
 
 
452
 
        m_pcbGraphicsView->setIgnoreSelectionChangeEvents(false);
453
 
 
454
 
        return svg;
455
 
}
456
 
 
457
 
 
458
 
void MainWindow::doExport() {
459
 
        QAction * action = qobject_cast<QAction *>(sender());
460
 
        if (action == NULL) return;
461
 
 
462
 
        QString actionType = action->data().toString();
463
 
        QString path = defaultSaveFolder();
464
 
 
465
 
        if (actionType.compare(eagleActionType) == 0) {
466
 
                exportToEagle();
467
 
                return;
468
 
        }
469
 
 
470
 
        if (actionType.compare(gerberActionType) == 0) {
471
 
                exportToGerber();
472
 
                return;
473
 
        }
474
 
 
475
 
        if (actionType.compare(bomActionType) == 0) {
476
 
                exportBOM();
477
 
                return;
478
 
        }
479
 
 
480
 
        if (actionType.compare(netlistActionType) == 0) {
481
 
                exportNetlist();
482
 
                return;
483
 
        }
484
 
 
485
 
        if (actionType.compare(svgActionType) == 0) {
486
 
                exportSvg(GraphicsUtils::IllustratorDPI, false, false);
487
 
                return;
488
 
        }
489
 
 
490
 
        #ifndef QT_NO_PRINTER
491
 
                QString fileExt;
492
 
                QString extFmt = fileExtFormats.value(actionType);
493
 
                DebugDialog::debug(QString("file export string %1").arg(extFmt));
494
 
                QString fileName = FolderUtils::getSaveFileName(this,
495
 
                        tr("Export..."),
496
 
                        path+"/"+constructFileName("", actionType),
497
 
                        extFmt,
498
 
                        &fileExt
499
 
                );
500
 
 
501
 
                if (fileName.isEmpty()) {
502
 
                        return; //Cancel pressed
503
 
                } else {
504
 
                        FileProgressDialog * fileProgressDialog = exportProgress();
505
 
                        DebugDialog::debug(fileExt+" selected to export");
506
 
                        if(!alreadyHasExtension(fileName, actionType)) {
507
 
                                fileName += actionType;
508
 
                        }
509
 
 
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);
517
 
                        } else { // PNG...
518
 
                                DebugDialog::debug(QString("format: %1 %2").arg(fileExt).arg(fileExportFormats[actionType]));
519
 
                                exportAux(fileName,fileExportFormats[actionType], true);
520
 
                        }
521
 
                        delete fileProgressDialog;
522
 
 
523
 
                }
524
 
        #endif
525
 
}
526
 
 
527
 
void MainWindow::exportAux(QString fileName, QImage::Format format, bool removeBackground) 
528
 
{
529
 
    double resMultiplier = 3;
530
 
 
531
 
        QRectF source = m_currentGraphicsView->scene()->itemsBoundingRect();
532
 
        QGraphicsItem * watermark = m_currentGraphicsView->addWatermark(":resources/images/watermark_fritzing_outline.svg");
533
 
        if (watermark) {
534
 
                watermark->setPos(source.right() - watermark->boundingRect().width(), source.bottom());
535
 
                source.adjust(0, 0, 0, watermark->boundingRect().height());
536
 
        }
537
 
 
538
 
        int width = source.width();
539
 
        int height = source.height();
540
 
 
541
 
        /*
542
 
        int width = m_currentGraphicsView->width();
543
 
        if (m_currentGraphicsView->verticalScrollBar()->isVisible()) {
544
 
                width -= m_currentGraphicsView->verticalScrollBar()->width();
545
 
        }
546
 
        int height = m_currentGraphicsView->height();
547
 
        if (m_currentGraphicsView->horizontalScrollBar()->isVisible()) {
548
 
                height -= m_currentGraphicsView->horizontalScrollBar()->height();
549
 
        }
550
 
        */
551
 
 
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);
556
 
        QPainter painter;
557
 
        QColor color;
558
 
        if (removeBackground) {
559
 
                color = m_currentGraphicsView->background();
560
 
                m_currentGraphicsView->setBackground(QColor::fromRgb(255,255,255,255));
561
 
        }
562
 
 
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);
567
 
        painter.end();
568
 
 
569
 
        if (removeBackground) {
570
 
                m_currentGraphicsView->setBackground(color);
571
 
        }
572
 
 
573
 
        if (watermark) {
574
 
                delete watermark;
575
 
        }
576
 
 
577
 
        QImageWriter imageWriter(fileName);
578
 
        if (imageWriter.supportsOption(QImageIOHandler::Description)) {
579
 
                imageWriter.setText("", TextUtils::CreatedWithFritzingString);
580
 
        }
581
 
    imageWriter.setQuality(100);
582
 
        bool result = imageWriter.write(image);
583
 
        if (!result) {
584
 
                QMessageBox::warning(this, tr("Fritzing"), tr("Unable to save %1").arg(fileName) );
585
 
        }
586
 
}
587
 
 
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) );
600
 
 
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);
605
 
        
606
 
        QRectF source = m_currentGraphicsView->scene()->itemsBoundingRect();
607
 
        QGraphicsItem * watermark = m_currentGraphicsView->addWatermark(":resources/images/watermark_fritzing_outline.svg");
608
 
        if (watermark) {
609
 
                watermark->setPos(source.right() - watermark->boundingRect().width(), source.bottom());
610
 
                source.adjust(0, 0, 0, watermark->boundingRect().height());
611
 
        }
612
 
 
613
 
        QRectF target(0, 0, source.width() * scale2, source.height() * scale2);
614
 
 
615
 
        if (!paginate) {
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);
619
 
        }
620
 
 
621
 
        QPainter painter;
622
 
        if (!painter.begin(&printer)) {
623
 
                if (watermark) {
624
 
                        delete watermark;
625
 
                }
626
 
                QMessageBox::warning(this, tr("Fritzing"), tr("Cannot print to %1").arg(printer.docName()));
627
 
                return;
628
 
        }
629
 
 
630
 
        QColor color;
631
 
        if(removeBackground) {
632
 
                color = m_currentGraphicsView->background();
633
 
                m_currentGraphicsView->setBackground(QColor::fromRgb(255,255,255,255));
634
 
        }
635
 
 
636
 
        QList<QGraphicsItem*> selItems = m_currentGraphicsView->scene()->selectedItems();
637
 
        foreach(QGraphicsItem *item, selItems) {
638
 
                item->setSelected(false);
639
 
        }
640
 
 
641
 
        if (paginate) {
642
 
                int xPages = qCeil(target.width() / printer.width());
643
 
                int yPages = qCeil(target.height() / printer.height());
644
 
                int lastPage = xPages * yPages;
645
 
 
646
 
                int xSourcePage = qFloor(printer.width() / scale2);
647
 
                int ySourcePage = qFloor(printer.height() / scale2);
648
 
 
649
 
                int page = 0;
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) {
660
 
                                        printer.newPage();
661
 
                                }
662
 
                        }
663
 
                }
664
 
        }
665
 
        else {
666
 
                m_currentGraphicsView->scene()->render(&painter, target, source, Qt::KeepAspectRatio);
667
 
        }
668
 
 
669
 
        foreach(QGraphicsItem *item, selItems) {
670
 
                item->setSelected(true);
671
 
        }
672
 
 
673
 
        if(removeBackground) {
674
 
                m_currentGraphicsView->setBackground(color);
675
 
        }
676
 
 
677
 
        if (watermark) {
678
 
                delete watermark;
679
 
        }
680
 
 
681
 
        DebugDialog::debug(QString("source w:%1 h:%2 target w:%5 h:%6 pres:%3 screenres:%4")
682
 
                .arg(source.width())
683
 
                .arg(source.height()).arg(res).arg(this->physicalDpiX())
684
 
                .arg(target.width()).arg(target.height()) );
685
 
 
686
 
        //#ifndef QT_NO_CONCURRENT
687
 
                //QProgressDialog dialog;
688
 
                //dialog.setLabelText(message);
689
 
        //
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)));
696
 
        //
697
 
                // Start the computation.
698
 
                //futureWatcher.setFuture(QtConcurrent::run(painter,&QPainter::end));
699
 
                //dialog.exec();
700
 
        //
701
 
                //futureWatcher.waitForFinished();
702
 
        //#endif
703
 
 
704
 
        //#ifdef QT_NO_CONCURRENT
705
 
                painter.end();
706
 
        //#endif
707
 
 
708
 
}
709
 
 
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.")
715
 
                             .arg(fileName)
716
 
                             .arg(file.errorString()));
717
 
        return false;
718
 
    }
719
 
 
720
 
    file.close();
721
 
 
722
 
    setReadOnly(false);
723
 
    //FritzingWindow::saveAsAux(fileName);
724
 
 
725
 
        saveAsAuxAux(fileName);
726
 
        m_autosaveNeeded = false;
727
 
        undoStackCleanChanged(true);
728
 
 
729
 
        m_statusBar->showMessage(tr("Saved '%1'").arg(fileName), 2000);
730
 
    setCurrentFile(fileName, true, true);
731
 
 
732
 
        if(m_restarting && !m_fwFilename.isEmpty()) {
733
 
                QSettings settings;
734
 
                settings.setValue("lastOpenSketch",m_fwFilename);
735
 
        }
736
 
 
737
 
   // mark the stack clean so we update the window dirty flag
738
 
    m_undoStack->setClean();
739
 
 
740
 
        // slam it here in case we were modified due to m_linkedProgramFiles changes
741
 
        setWindowModified(false);
742
 
 
743
 
    m_saveAct->setEnabled(true);
744
 
 
745
 
        return true;
746
 
}
747
 
 
748
 
void MainWindow::saveAsAuxAux(const QString & fileName) {
749
 
    QApplication::setOverrideCursor(Qt::WaitCursor);
750
 
 
751
 
        connectStartSave(true);
752
 
 
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());
758
 
                file.remove();
759
 
        }
760
 
 
761
 
        QString fzName = dir.absoluteFilePath(QFileInfo(fileName).completeBaseName() + FritzingSketchExtension); 
762
 
        m_sketchModel->save(fzName, false);
763
 
 
764
 
        saveAsShareable(fileName, false);
765
 
 
766
 
        connectStartSave(false);
767
 
 
768
 
        QApplication::restoreOverrideCursor();
769
 
}
770
 
 
771
 
 
772
 
void MainWindow::saveAsShareable(const QString & path, bool saveModel)
773
 
{
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;
780
 
        
781
 
                saveParts.insert(itemBase->moduleID(), itemBase->modelPart());
782
 
        }
783
 
        saveBundledNonAtomicEntity(filename, FritzingBundleExtension, this, saveParts.values(), false, m_fzzFolder, saveModel, true);
784
 
 
785
 
}
786
 
 
787
 
void MainWindow::saveBundledNonAtomicEntity(QString &filename, const QString &extension, Bundler *bundler, const QList<ModelPart*> &partsToSave, bool askForFilename, const QString & destFolderPath, bool saveModel, bool deleteLeftovers) {
788
 
        QStringList names;
789
 
 
790
 
        QString fileExt;
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)
794
 
                : filename;
795
 
 
796
 
        if (bundledFileName.isEmpty()) return; // Cancel pressed
797
 
 
798
 
    FileProgressDialog progress("Saving...", 0, this);
799
 
 
800
 
        if(!alreadyHasExtension(bundledFileName, extension)) {
801
 
                bundledFileName += extension;
802
 
        }
803
 
 
804
 
        ProcessEventBlocker::processEvents();
805
 
 
806
 
        QDir destFolder;
807
 
        QString dirToRemove;
808
 
        if (destFolderPath.isEmpty()) {
809
 
                destFolder = QDir::temp();
810
 
                FolderUtils::createFolderAnCdIntoIt(destFolder, FolderUtils::getRandText());
811
 
                dirToRemove = destFolder.path();
812
 
        }
813
 
        else {
814
 
                destFolder = QDir(destFolderPath);
815
 
        }
816
 
 
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);
821
 
 
822
 
    QStringList skipSuffixes;
823
 
 
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()));
830
 
                }
831
 
        skipSuffixes << FritzingBinExtension << FritzingBundleExtension;
832
 
        }
833
 
 
834
 
        if (saveModel) {
835
 
                QString prevFileName = filename;
836
 
                ProcessEventBlocker::processEvents();
837
 
                bundler->saveAsAux(destSketchPath);
838
 
                filename = prevFileName;
839
 
        }
840
 
 
841
 
        foreach(ModelPart* mp, partsToSave) {
842
 
                names.append(saveBundledAux(mp, destFolder));
843
 
        }
844
 
 
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));
853
 
                        }
854
 
                }
855
 
        }
856
 
 
857
 
        QApplication::processEvents();
858
 
 
859
 
        if(!FolderUtils::createZipAndSaveTo(destFolder, bundledFileName, skipSuffixes)) {
860
 
                QMessageBox::warning(
861
 
                        this,
862
 
                        tr("Fritzing"),
863
 
                        tr("Unable to export %1 as shareable").arg(bundledFileName)
864
 
                );
865
 
        }
866
 
 
867
 
        if (!dirToRemove.isEmpty()) {
868
 
                FolderUtils::rmdir(dirToRemove);
869
 
        }
870
 
}
871
 
 
872
 
 
873
 
void MainWindow::createExportActions() {
874
 
 
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()));
879
 
 
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()));
884
 
 
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()));
888
 
 
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()));
893
 
 
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()));
898
 
 
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()));
903
 
 
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()));
908
 
 
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()));
913
 
 
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()));
918
 
 
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()));
923
 
 
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()));
928
 
 
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()));
933
 
 
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()));
938
 
 
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()));
943
 
 
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()));*/
948
 
 
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()));
953
 
 
954
 
}
955
 
 
956
 
void MainWindow::exportToEagle() {
957
 
 
958
 
        QString text =
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.");
962
 
/*
963
 
        QString text =
964
 
                tr("The Eagle export module is very experimental.  If anything breaks or behaves "
965
 
                "strangely, please let us know.");
966
 
*/
967
 
 
968
 
        QMessageBox::information(this, tr("Fritzing"), text);
969
 
 
970
 
        Fritzing2Eagle eagle = Fritzing2Eagle(m_pcbGraphicsView);
971
 
 
972
 
        /*
973
 
        QList <ItemBase*> partList;
974
 
 
975
 
        // bail out if something is wrong
976
 
        // TODO: show an error in QMessageBox
977
 
    if(m_currentWidget == NULL) {
978
 
                return;
979
 
        }
980
 
 
981
 
    m_pcbGraphicsView->collectParts(partList);
982
 
 
983
 
        QString exportInfoString = tr("parts include:\n");
984
 
        QString exportString = tr("GRID INCH 0.005\n");
985
 
 
986
 
 
987
 
        for(int i=0; i < partList.size(); i++){
988
 
                QString label = partList.at(i)->instanceTitle();
989
 
                QString desc = partList.at(i)->title();
990
 
 
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 ***");
995
 
                }
996
 
 
997
 
                exportInfoString += label + tr(" which is a ") + desc + tr(" in a ") + package + tr(" package.\n");
998
 
        }
999
 
        QMessageBox::information(this, tr("Fritzing"), exportInfoString);
1000
 
        */
1001
 
 
1002
 
        /*
1003
 
        QFile fp( fileName );
1004
 
        fp.open(QIODevice::WriteOnly);
1005
 
        fp.write(bom.toUtf8(),bom.length());
1006
 
        fp.close();
1007
 
        */
1008
 
 
1009
 
 
1010
 
        /*
1011
 
        GRID INCH 0.005
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);
1015
 
        GRID LAST;
1016
 
        */
1017
 
}
1018
 
 
1019
 
void MainWindow::exportSvg(double res, bool selectedItems, bool flatten) {
1020
 
        QString path = defaultSaveFolder();
1021
 
        QString fileExt;
1022
 
        QString fileName = FolderUtils::getSaveFileName(this,
1023
 
                tr("Export SVG..."),
1024
 
                path+"/"+constructFileName("", svgActionType),
1025
 
                fileExtFormats[svgActionType],
1026
 
                &fileExt
1027
 
                );
1028
 
 
1029
 
        if (fileName.isEmpty()) return;
1030
 
 
1031
 
        FileProgressDialog * fileProgressDialog = exportProgress();
1032
 
        LayerList viewLayerIDs;
1033
 
        foreach (ViewLayer * viewLayer, m_currentGraphicsView->viewLayers()) {
1034
 
                if (viewLayer == NULL) continue;
1035
 
                if (!viewLayer->visible()) continue;
1036
 
 
1037
 
                viewLayerIDs << viewLayer->viewLayerID();
1038
 
        }
1039
 
 
1040
 
        QSizeF imageSize;
1041
 
        bool empty;
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
1045
 
                return;
1046
 
        }
1047
 
 
1048
 
        if (selectedItems == false && flatten == false) {
1049
 
                exportSvgWatermark(svg, res);
1050
 
        }
1051
 
 
1052
 
    TextUtils::writeUtf8(fileName, TextUtils::convertExtendedChars(svg));
1053
 
        delete fileProgressDialog;
1054
 
 
1055
 
}
1056
 
 
1057
 
void MainWindow::exportSvgWatermark(QString & svg, double res)
1058
 
{
1059
 
        QFile file(":/resources/images/watermark_fritzing_outline.svg");
1060
 
        if (!file.open(QFile::ReadOnly)) return;
1061
 
 
1062
 
        QString watermarkSvg = file.readAll();
1063
 
        file.close();
1064
 
 
1065
 
        if (!watermarkSvg.contains("<svg")) return;
1066
 
 
1067
 
        QSizeF watermarkSize = TextUtils::parseForWidthAndHeight(watermarkSvg);
1068
 
        QSizeF svgSize = TextUtils::parseForWidthAndHeight(svg);
1069
 
 
1070
 
        SvgFileSplitter splitter;
1071
 
        bool result = splitter.splitString(watermarkSvg, "watermark");
1072
 
        if (!result) return;
1073
 
 
1074
 
        result = splitter.normalize(res, "watermark", false);
1075
 
        if (!result) return;
1076
 
 
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);
1080
 
}
1081
 
 
1082
 
void MainWindow::exportBOM() {
1083
 
 
1084
 
    // bail out if something is wrong
1085
 
    // TODO: show an error in QMessageBox
1086
 
        if (m_currentWidget == NULL) {
1087
 
        return;
1088
 
    }
1089
 
 
1090
 
        QString bomTemplate;
1091
 
        QFile file(":/resources/templates/bom.html");
1092
 
        if (file.open(QFile::ReadOnly)) {
1093
 
                bomTemplate = file.readAll();
1094
 
                file.close();
1095
 
        }
1096
 
        else {
1097
 
                return;
1098
 
        }
1099
 
 
1100
 
        QString bomRowTemplate;
1101
 
        QFile file2(":/resources/templates/bom_row.html");
1102
 
        if (file2.open(QFile::ReadOnly)) {
1103
 
                bomRowTemplate = file2.readAll();
1104
 
                file2.close();
1105
 
        }
1106
 
        else {
1107
 
                return;
1108
 
        }
1109
 
 
1110
 
    QList <ItemBase*> partList;
1111
 
    QMap<QString, int> shoppingList;
1112
 
        QHash<QString, ItemBase *> descrs;
1113
 
 
1114
 
        m_currentGraphicsView->collectParts(partList);
1115
 
 
1116
 
    qSort(partList.begin(), partList.end(), sortPartList);
1117
 
 
1118
 
        foreach (ItemBase * itemBase, partList) {
1119
 
                if (itemBase->itemType() != ModelPart::Part) continue;
1120
 
 
1121
 
        QString desc = getBomProps(itemBase);
1122
 
 
1123
 
        if(!shoppingList.contains(desc)) {
1124
 
            shoppingList.insert(desc, 1);
1125
 
                        descrs.insert(desc, itemBase);
1126
 
        }
1127
 
        else {
1128
 
            shoppingList[desc]++;
1129
 
        }
1130
 
    }
1131
 
 
1132
 
 
1133
 
        QString assemblyString;
1134
 
        foreach (ItemBase * itemBase, partList) {
1135
 
                if (itemBase->itemType() != ModelPart::Part) continue;
1136
 
 
1137
 
                assemblyString += bomRowTemplate.arg(itemBase->instanceTitle()).arg(itemBase->title()).arg(getBomProps(itemBase));
1138
 
    }
1139
 
 
1140
 
        QString shoppingListString;
1141
 
    QMapIterator<QString, int> it(shoppingList);
1142
 
    while (it.hasNext()) {
1143
 
        it.next();
1144
 
                ItemBase * itemBase = descrs.value(it.key());
1145
 
 
1146
 
                // it.value() = count; it.key() = bomProps
1147
 
                shoppingListString += bomRowTemplate.arg(it.value()).arg(itemBase->title()).arg(it.key());
1148
 
    }
1149
 
 
1150
 
        QString bom = bomTemplate
1151
 
                .arg("Fritzing Bill of Materials")
1152
 
                .arg(QFileInfo(m_fwFilename).fileName())
1153
 
                .arg(m_fwFilename)
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()));
1158
 
 
1159
 
 
1160
 
    QString path = defaultSaveFolder();
1161
 
 
1162
 
    QString fileExt;
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));
1166
 
 
1167
 
    QString fileName = FolderUtils::getSaveFileName(this,
1168
 
            tr("Export Bill of Materials (BoM)..."),
1169
 
            fname,
1170
 
            extFmt,
1171
 
            &fileExt
1172
 
    );
1173
 
 
1174
 
    if (fileName.isEmpty()) {
1175
 
                return; //Cancel pressed
1176
 
    }
1177
 
 
1178
 
        FileProgressDialog * fileProgressDialog = exportProgress();
1179
 
    DebugDialog::debug(fileExt+" selected to export");
1180
 
    if(!alreadyHasExtension(fileName, bomActionType)) {
1181
 
                fileName += bomActionType;
1182
 
    }
1183
 
 
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."));
1186
 
        }
1187
 
 
1188
 
    QFileInfo info(fileName);
1189
 
        if (info.exists()) {
1190
 
                QDesktopServices::openUrl(QString("file:///%1").arg(fileName));
1191
 
        }
1192
 
 
1193
 
        QClipboard *clipboard = QApplication::clipboard();
1194
 
        if (clipboard != NULL) {
1195
 
                clipboard->setText(bom);
1196
 
        }
1197
 
        delete fileProgressDialog;
1198
 
}
1199
 
 
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);
1204
 
 
1205
 
        QDomDocument doc;
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());
1211
 
 
1212
 
        // TODO: filter out 'ignore' connectors
1213
 
 
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;
1220
 
 
1221
 
                        if (ercData->ignore() == ErcData::Always) {
1222
 
                                deleteItems.append(connectorItem);
1223
 
                        }
1224
 
                        else if ((ercData->ignore() == ErcData::IfUnconnected) && (net->count() == 1)) {
1225
 
                                deleteItems.append(connectorItem);
1226
 
                        }
1227
 
                }
1228
 
 
1229
 
                foreach (ConnectorItem * connectorItem, deleteItems) {
1230
 
                        net->removeOne(connectorItem);
1231
 
                }
1232
 
                if (net->count() == 0) {
1233
 
                        deleteNets.append(net);
1234
 
                }
1235
 
        }
1236
 
 
1237
 
        foreach (QList<ConnectorItem *> * net, deleteNets) {
1238
 
                netList.removeOne(net);
1239
 
        }
1240
 
 
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);
1260
 
                                }
1261
 
                        }
1262
 
                }
1263
 
        }
1264
 
 
1265
 
        foreach (QList<ConnectorItem *> * net, netList) {
1266
 
                delete net;
1267
 
        }
1268
 
        netList.clear();
1269
 
 
1270
 
    QString path = defaultSaveFolder();
1271
 
 
1272
 
    QString fileExt;
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));
1276
 
 
1277
 
    QString fileName = FolderUtils::getSaveFileName(this,
1278
 
            tr("Export Netlist..."),
1279
 
            fname,
1280
 
            extFmt,
1281
 
            &fileExt
1282
 
    );
1283
 
 
1284
 
    if (fileName.isEmpty()) {
1285
 
                return; //Cancel pressed
1286
 
    }
1287
 
 
1288
 
        FileProgressDialog * fileProgressDialog = exportProgress();
1289
 
    //DebugDialog::debug(fileExt + " selected to export");
1290
 
    if(!alreadyHasExtension(fileName, netlistActionType)) {
1291
 
                fileName += netlistActionType;
1292
 
    }
1293
 
 
1294
 
    QFile fp( fileName );
1295
 
    fp.open(QIODevice::WriteOnly);
1296
 
    fp.write(doc.toByteArray());
1297
 
    fp.close();
1298
 
 
1299
 
        QClipboard *clipboard = QApplication::clipboard();
1300
 
        if (clipboard != NULL) {
1301
 
                clipboard->setText(doc.toByteArray());
1302
 
        }
1303
 
        delete fileProgressDialog;
1304
 
 
1305
 
 
1306
 
}
1307
 
 
1308
 
FileProgressDialog * MainWindow::exportProgress() {
1309
 
    return (new FileProgressDialog("Exporting...", 0, this));
1310
 
}
1311
 
 
1312
 
void MainWindow::exportNormalizedSVG() {
1313
 
        exportSvg(GraphicsUtils::StandardFritzingDPI, true, false);
1314
 
}
1315
 
 
1316
 
void MainWindow::exportNormalizedFlattenedSVG() {
1317
 
        exportSvg(GraphicsUtils::StandardFritzingDPI, true, true);
1318
 
}
1319
 
 
1320
 
QString MainWindow::getBomProps(ItemBase * itemBase)
1321
 
{
1322
 
        if (itemBase == NULL) return "";
1323
 
 
1324
 
        QStringList keys;
1325
 
        QHash<QString, QString> properties = HtmlInfoView::getPartProperties(itemBase->modelPart(), itemBase, false, keys);
1326
 
        QString pString;
1327
 
        foreach (QString key, keys) {
1328
 
                if (key.compare("family") == 0) continue;
1329
 
 
1330
 
                QString value = properties.value(key);
1331
 
 
1332
 
                QWidget widget;
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;
1337
 
 
1338
 
                pString += ItemBase::translatePropertyName(resultKey) + " " + resultValue + "; ";
1339
 
        }
1340
 
 
1341
 
        if (pString.length() > 2) pString.chop(2);
1342
 
 
1343
 
        return pString;
1344
 
}
1345
 
 
1346
 
void MainWindow::exportToGerber() {
1347
 
 
1348
 
    //NOTE: this assumes just one board per sketch
1349
 
 
1350
 
    int boardCount;
1351
 
        ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount);
1352
 
 
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."));
1357
 
        return;
1358
 
    }
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."));
1362
 
        return;
1363
 
    }
1364
 
 
1365
 
    QString exportDir = QFileDialog::getExistingDirectory(this, tr("Choose a folder for exporting"),
1366
 
                                             defaultSaveFolder(),
1367
 
                                             QFileDialog::ShowDirsOnly
1368
 
                                             | QFileDialog::DontResolveSymlinks);
1369
 
 
1370
 
        if (exportDir.isEmpty()) return;
1371
 
 
1372
 
        FileProgressDialog * fileProgressDialog = exportProgress();
1373
 
 
1374
 
        FolderUtils::setOpenSaveFolder(exportDir);
1375
 
        m_pcbGraphicsView->saveLayerVisibility();
1376
 
        m_pcbGraphicsView->setAllLayersVisible(true);
1377
 
 
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());
1382
 
    }
1383
 
        GerberGenerator::exportToGerber(prefix, exportDir, board, m_pcbGraphicsView, true);
1384
 
        m_pcbGraphicsView->restoreLayerVisibility();
1385
 
        m_statusBar->showMessage(tr("Sketch exported to Gerber"), 2000);
1386
 
 
1387
 
        delete fileProgressDialog;
1388
 
}
1389
 
 
1390
 
void MainWindow::exportToGerber(const QString & exportDir) {
1391
 
        GerberGenerator::exportToGerber(m_fwFilename, exportDir, NULL, m_pcbGraphicsView, false);
1392
 
}
1393
 
 
1394
 
void MainWindow::connectStartSave(bool doConnect) {
1395
 
 
1396
 
        if (doConnect) {
1397
 
                connect(m_sketchModel->root(), SIGNAL(startSaveInstances(const QString &, ModelPart *, QXmlStreamWriter &)),
1398
 
                                this, SLOT(startSaveInstancesSlot(const QString &, ModelPart *, QXmlStreamWriter &)), Qt::DirectConnection);
1399
 
        }
1400
 
        else {
1401
 
                disconnect(m_sketchModel->root(), SIGNAL(startSaveInstances(const QString &, ModelPart *, QXmlStreamWriter &)),
1402
 
                                this, SLOT(startSaveInstancesSlot(const QString &, ModelPart *, QXmlStreamWriter &)));
1403
 
        }
1404
 
}
1405
 
 
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);
1409
 
        return fn + suffix;
1410
 
}
1411
 
 
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)
1413
 
{
1414
 
        if (doPaste) {
1415
 
        // must test doPaste first, since doMask will also be true
1416
 
        svg = pcbView()->makePasteMask(svg, board, dpi, viewLayerIDs);
1417
 
    }
1418
 
        else if (doSilk) {
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);
1422
 
        }
1423
 
    else if (doMask) {
1424
 
                if (fileName.contains("bottom")) maskBottom = svg; else maskTop = svg;
1425
 
                svg = TextUtils::expandAndFill(svg, "black", GerberGenerator::MaskClearanceMils * 2 * dpi / 1000);
1426
 
        }
1427
 
}
1428