1 /*******************************************************************
2
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 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: 6417 $:
22 $Author: cohen@irascible.com $:
23 $Date: 2012-09-14 23:34:09 +0200 (Fr, 14. Sep 2012) $
24
25 ********************************************************************/
26
27 #include <QMessageBox>
28 #include <QFileDialog>
29 #include <QtDebug>
30 #include <QSvgGenerator>
31 #include <QGraphicsProxyWidget>
32 #include <QVarLengthArray>
33 #include <QDialogButtonBox>
34 #include <QFormLayout>
35 #include <QComboBox>
36 #include <QLabel>
37 #include <QPushButton>
38 #include <QScrollBar>
39
40 #include "partseditorview.h"
41 #include "partseditorconnectoritem.h"
42 #include "fixfontsdialog.h"
43 #include "zoomcontrols.h"
44 #include "kicadmoduledialog.h"
45 #include "../items/layerkinpaletteitem.h"
46 #include "../items/partfactory.h"
47 #include "../layerattributes.h"
48 #include "../mainwindow/fritzingwindow.h"
49 #include "../fsvgrenderer.h"
50 #include "../debugdialog.h"
51 #include "../utils/folderutils.h"
52 #include "../utils/textutils.h"
53 #include "../utils/graphicsutils.h"
54 #include "../utils/ratsnestcolors.h"
55 #include "../svg/gedaelement2svg.h"
56 #include "../svg/kicadmodule2svg.h"
57 #include "../svg/kicadschematic2svg.h"
58 #include "../connectors/connectorshared.h"
59
60
61 int PartsEditorView::ConnDefaultWidth = 5;
62 int PartsEditorView::ConnDefaultHeight = ConnDefaultWidth;
63
PartsEditorView(ViewLayer::ViewIdentifier viewId,QDir tempDir,bool showingTerminalPoints,QGraphicsProxyWidget * startItem,QWidget * parent,int size,bool deleteModelPartOnClearScene,ItemBase * fromItem)64 PartsEditorView::PartsEditorView(
65 ViewLayer::ViewIdentifier viewId, QDir tempDir,
66 bool showingTerminalPoints, QGraphicsProxyWidget *startItem,
67 QWidget *parent, int size, bool deleteModelPartOnClearScene,
68 ItemBase * fromItem)
69 : SketchWidget(viewId, parent, size, size)
70 {
71 m_alignToGrid = m_showGrid = false;
72 m_viewItem = NULL;
73 m_item = NULL;
74 m_connsLayerID = ViewLayer::UnknownLayer;
75 m_svgLoaded = false;
76 m_deleteModelPartOnSceneClear = deleteModelPartOnClearScene;
77 m_tempFolder = tempDir;
78 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
79 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
80 setDefaultBackground();
81
82
83
84 //spec
85 m_svgFilePath = new SvgAndPartFilePath;
86 m_startItem = startItem;
87 if(m_startItem) {
88 addFixedToCenterItem(startItem);
89 ensureFixedToCenterItems();
90
91 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(ensureFixedItemsPositions()));
92 connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(ensureFixedItemsPositions()));
93
94 connect(this, SIGNAL(resizeSignal()), this, SLOT(ensureFixedItemsPositions()));
95 connect(this, SIGNAL(wheelSignal()), this, SLOT(ensureFixedItemsPositions()));
96
97
98 // TODO: do we still need this?
99 QTimer::singleShot(400, this, SLOT(ensureFixedItemsPositions()));
100 }
101 addDefaultLayers(fromItem);
102
103
104 // conns
105 m_showingTerminalPoints = showingTerminalPoints;
106 m_lastSelectedConnId = "";
107
108 setDragMode(QGraphicsView::ScrollHandDrag);
109
110 setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
111
112 m_terminalPointsTimer = new QTimer(this);
113 connect(
114 m_terminalPointsTimer,SIGNAL(timeout()),
115 this,SLOT(recoverTerminalPointsState())
116 );
117 m_showingTerminalPointsBackup = m_showingTerminalPoints;
118
119 m_fitItemInViewTimer = new QTimer(this);
120 m_fitItemInViewTimer->setSingleShot(true);
121 m_fitItemInViewTimer->setInterval(200);
122 connect(m_fitItemInViewTimer,SIGNAL(timeout()),this,SLOT(fitCenterAndDeselect()));
123 }
124
~PartsEditorView()125 PartsEditorView::~PartsEditorView() {
126 if (m_startItem) delete m_startItem;
127 delete m_svgFilePath;
128 clearScene();
129 }
130
addDefaultLayers(ItemBase * fromItem)131 void PartsEditorView::addDefaultLayers(ItemBase * fromItem) {
132 switch( m_viewIdentifier ) {
133 case ViewLayer::BreadboardView:
134 addBreadboardViewLayers();
135 break;
136 case ViewLayer::SchematicView:
137 addSchematicViewLayers();
138 break;
139 case ViewLayer::PCBView:
140 addPcbViewLayers();
141 if (fromItem && fromItem->modelPart()->flippedSMD()) {
142 DebugDialog::debug("editing an SMD part");
143 setViewLayerIDs(ViewLayer::Silkscreen1, ViewLayer::Copper1Trace, ViewLayer::Copper1, ViewLayer::PcbRuler, ViewLayer::PcbNote);
144 this->m_viewLayers.remove(ViewLayer::Copper0);
145 this->m_viewLayers.remove(ViewLayer::Silkscreen0);
146 this->m_viewLayers.remove(ViewLayer::Copper0Trace);
147 }
148 break;
149 default:
150 break;
151 }
152 }
153
addItemInPartsEditor(ModelPart * modelPart,SvgAndPartFilePath * svgFilePath)154 void PartsEditorView::addItemInPartsEditor(ModelPart * modelPart, SvgAndPartFilePath * svgFilePath) {
155 if (modelPart == NULL) {
156 throw "PartsEditorView::addItemInPartsEditor no model part";
157 }
158 clearScene();
159
160 m_item = newPartsEditorPaletteItem(modelPart, svgFilePath);
161 this->addItem(modelPart, defaultViewLayerSpec(), BaseCommand::CrossView, m_item->getViewGeometry(), m_item->id(), -1, NULL, m_item);
162
163 fitCenterAndDeselect();
164
165 setItemProperties();
166
167 /*foreach(QWidget* w, m_fixedWidgets) {
168 QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget();
169 proxy->setWidget(w);
170
171 addFixedToBottomRightItem(proxy);
172 }*/
173
174 emit connectorsFoundSignal(this->m_viewIdentifier,m_item->connectors());
175 }
176
addItemAux(ModelPart * modelPart,ViewLayer::ViewLayerSpec viewLayerSpec,const ViewGeometry &,long id,PaletteItem * paletteItemAux,bool doConnectors,ViewLayer::ViewIdentifier,bool temporary)177 ItemBase * PartsEditorView::addItemAux(ModelPart * modelPart, ViewLayer::ViewLayerSpec viewLayerSpec, const ViewGeometry &, long id, PaletteItem * paletteItemAux, bool doConnectors, ViewLayer::ViewIdentifier, bool temporary) {
178 Q_UNUSED(id);
179 Q_UNUSED(temporary);
180
181 if(paletteItemAux == NULL) {
182 paletteItemAux = newPartsEditorPaletteItem(modelPart);
183 }
184 PartsEditorPaletteItem *paletteItem = dynamic_cast<PartsEditorPaletteItem*>(paletteItemAux);
185 if (paletteItem == NULL) {
186 throw "PartsEditorView::addItemAux paletteItem not found";
187 }
188
189 if(paletteItem) {
190 modelPart->initConnectors(); // is a no-op if connectors already in place
191 QString layerFileName = getLayerFileName(modelPart);
192 if(!layerFileName.isEmpty()) {
193 if(paletteItem->createSvgPath(modelPart->path(), layerFileName)) {
194 paletteItem->createSvgFile(paletteItem->svgFilePath()->absolutePath());
195 ViewLayer::ViewLayerID viewLayerID =
196 ViewLayer::viewLayerIDFromXmlString(
197 findConnectorsLayerId(paletteItem->svgDom())
198 );
199 if(viewLayerID == ViewLayer::UnknownLayer) {
200 viewLayerID = getViewLayerID(modelPart, m_viewIdentifier, viewLayerSpec);
201 }
202 addDefaultLayers(NULL);
203 if (m_viewItem != NULL) {
204 QHash<QString, QString> svgHash;
205 QString svg = "";
206 foreach (ViewLayer * vl, m_viewLayers.values()) {
207 svg += m_viewItem->retrieveSvg(vl->viewLayerID(), svgHash, false, GraphicsUtils::StandardFritzingDPI);
208 }
209 if (!svg.isEmpty()) {
210 QSizeF size = m_viewItem->size();
211 svg = TextUtils::makeSVGHeader(GraphicsUtils::SVGDPI, GraphicsUtils::StandardFritzingDPI, size.width(), size.height()) + svg + "</svg>";
212 paletteItem->setItemSVG(svg);
213 }
214 }
215
216 QString error;
217 if (paletteItem->renderImage(modelPart, m_viewIdentifier, m_viewLayers, viewLayerID, doConnectors, error)) {
218 addToScene(paletteItemAux, paletteItemAux->viewLayerID());
219 // layers are not needed on the parts editor (so far)
220 return paletteItemAux;
221 }
222 }
223 }
224 }
225 return NULL;
226 }
227
fitCenterAndDeselect()228 void PartsEditorView::fitCenterAndDeselect() {
229 if(m_item) {
230 m_item->setSelected(false);
231 m_item->setHidden(false);
232 m_item->setInactive(false);
233
234 fitInView(m_item, Qt::KeepAspectRatio);
235
236 QRectF viewRect = rect();
237 QRectF itemsRect = scene()->itemsBoundingRect();
238
239 double wRelation = viewRect.width() / itemsRect.width();
240 double hRelation = viewRect.height() / itemsRect.height();
241
242 if(wRelation < hRelation) {
243 m_scaleValue = (wRelation * 100);
244 } else {
245 m_scaleValue = (hRelation * 100);
246 }
247
248 emit zoomChanged(m_scaleValue);
249 }
250 }
251
setDefaultBackground()252 void PartsEditorView::setDefaultBackground() {
253 QString bgColor = " PartsEditorView {background-color: rgb(%1,%2,%3);} ";
254 QColor c = standardBackground();
255 if (c.isValid()) {
256 setStyleSheet(styleSheet()+bgColor.arg(c.red()).arg(c.green()).arg(c.blue()));
257 }
258 }
259
clearScene()260 void PartsEditorView::clearScene() {
261 if(m_item) {
262 deleteItem(m_item, m_deleteModelPartOnSceneClear, true, false);
263
264 scene()->clear();
265 m_item = NULL;
266 }
267 }
268
removeConnectors()269 void PartsEditorView::removeConnectors() {
270 QList<PartsEditorConnectorItem*> list;
271 for (int i = m_item->childItems().count()-1; i >= 0; i--) {
272 PartsEditorConnectorItem * connectorItem = dynamic_cast<PartsEditorConnectorItem *>(m_item->childItems()[i]);
273 if (connectorItem == NULL) continue;
274
275 list << connectorItem;
276 }
277
278 for(int i=0; i < list.size(); i++) {
279 list[i]->removeFromModel();
280 delete list[i];
281 }
282 }
283
createFakeModelPart(SvgAndPartFilePath * svgpath)284 ModelPart *PartsEditorView::createFakeModelPart(SvgAndPartFilePath *svgpath) {
285 const QHash<QString,ConnectorTerminalSvgIdPair> connIds = getConnectorsSvgIds(svgpath->absolutePath());
286 const QStringList layers = getLayers(svgpath->absolutePath());
287
288 QString path = svgpath->relativePath().isEmpty() ? svgpath->absolutePath() : svgpath->relativePath();
289 ModelPart * mp = createFakeModelPart(connIds, layers, path);
290
291 return mp;
292 }
293
createFakeModelPart(const QHash<QString,ConnectorTerminalSvgIdPair> & conns,const QStringList & layers,const QString & svgFilePath)294 ModelPart *PartsEditorView::createFakeModelPart(const QHash<QString,ConnectorTerminalSvgIdPair> &conns, const QStringList &layers, const QString &svgFilePath) {
295 QDomDocument *domDoc = new QDomDocument();
296 QString errorStr;
297 int errorLine;
298 int errorColumn;
299 QString fakeFzFile =
300 QString("<module><views>\n")+
301 QString("<%1><layers image='%2' >\n").arg(ViewLayer::viewIdentifierXmlName(m_viewIdentifier)).arg(svgFilePath);
302 foreach(QString layer, layers) { fakeFzFile +=
303 QString(" <layer layerId='%1' />\n").arg(layer);
304 }
305 fakeFzFile +=
306 QString("</layers></%1>\n").arg(ViewLayer::viewIdentifierXmlName(m_viewIdentifier))+
307 QString("</views><connectors>\n");
308
309 QStringList defaultLayers = defaultLayerAsStringlist();
310
311 foreach(QString id, conns.keys()) {
312 QString terminalAttr = conns[id].terminalId.isEmpty() ? "" : QString("terminalId='%1'").arg(conns[id].terminalId);
313 QString name = conns[id].connectorName.isEmpty() ? "" : QString("name='%1'").arg(conns[id].connectorName);
314 fakeFzFile += QString("<connector id='%1' %2><views>\n").arg(id).arg(name) +
315 QString("<%1>\n").arg(ViewLayer::viewIdentifierXmlName(m_viewIdentifier));
316 foreach (QString layer, defaultLayers) {
317 if (layers.contains(layer)) {
318 fakeFzFile += QString("<p layer='%1' svgId='%2' %3/>\n")
319 .arg(layer)
320 .arg(conns[id].connectorId)
321 .arg(terminalAttr);
322 }
323 }
324 fakeFzFile += QString("</%1>\n").arg(ViewLayer::viewIdentifierXmlName(m_viewIdentifier))+
325 QString("</views></connector>\n");
326 }
327 fakeFzFile += QString("</connectors></module>\n");
328
329 QString path = m_tempFolder.absolutePath()+"/"+FolderUtils::getRandText()+".fz";
330 TextUtils::writeUtf8(path, fakeFzFile);
331
332 domDoc->setContent(fakeFzFile, &errorStr, &errorLine, &errorColumn);
333
334 ModelPart *retval = m_sketchModel->root();
335 retval->modelPartShared()->setDomDocument(domDoc);
336 retval->modelPartShared()->resetConnectorsInitialization();
337 retval->modelPartShared()->setPath(path);
338 retval->initConnectors(true /*redo connectors*/);
339
340 return retval;
341 }
342
getConnectorsSvgIds(const QString & path)343 const QHash<QString,ConnectorTerminalSvgIdPair> PartsEditorView::getConnectorsSvgIds(const QString &path) {
344 QDomDocument dom ;
345 QFile file(path);
346 dom.setContent(&file);
347
348 QDomElement docElem = dom.documentElement();
349 getConnectorsSvgIdsAux(docElem);
350
351 return m_svgIds;
352 }
353
getConnectorsSvgIdsAux(QDomElement & docElem)354 void PartsEditorView::getConnectorsSvgIdsAux(QDomElement &docElem) {
355 QDomElement e = docElem.firstChildElement();
356 while(!e.isNull()) {
357 QString id = e.attribute("id");
358 if(id.startsWith("connector") && id.endsWith("terminal")) {
359 QString conn = id.left(id.lastIndexOf(QRegExp("\\d"))+1);
360 ConnectorTerminalSvgIdPair pair = m_svgIds.contains(conn) ? m_svgIds[conn] : ConnectorTerminalSvgIdPair();
361 pair.terminalId = id;
362 m_svgIds[conn] = pair;
363 }
364 else if(id.startsWith("connector") /*&& id.endsWith("pin") */ ) {
365 QString conn = id.left(id.lastIndexOf(QRegExp("\\d"))+1);
366 ConnectorTerminalSvgIdPair pair = m_svgIds.contains(conn) ? m_svgIds[conn] : ConnectorTerminalSvgIdPair();
367 pair.connectorId = id;
368 pair.connectorName = e.attribute("connectorname");
369 m_svgIds[conn] = pair;
370 }
371 if(e.hasChildNodes()) {
372 getConnectorsSvgIdsAux(e);
373 }
374 e = e.nextSiblingElement();
375 }
376 }
377
getLayers(const QString & path)378 const QStringList PartsEditorView::getLayers(const QString &path) {
379 if(m_viewIdentifier == ViewLayer::IconView) { // defaulting layer to icon for iconview
380 return defaultLayerAsStringlist();
381 } else {
382 QDomDocument dom;
383 QFile file(path);
384 dom.setContent(&file);
385 return getLayers(&dom);
386 }
387 }
388
getLayers(const QDomDocument * dom,bool fakeDefaultIfNone)389 const QStringList PartsEditorView::getLayers(const QDomDocument *dom, bool fakeDefaultIfNone) {
390 QStringList retval;
391
392 QDomNodeList nodeList = dom->elementsByTagName("g");
393 for (uint i = 0; i < nodeList.length(); i++) {
394 QDomElement e = nodeList.item(i).toElement();
395 QString id = e.attribute("id");
396 if (id.isEmpty()) continue;
397 if (ViewLayer::viewLayerIDFromXmlString(id) == ViewLayer::UnknownLayer) continue;
398
399 retval << id;
400 }
401
402 if(fakeDefaultIfNone && retval.isEmpty()) {
403 retval << ViewLayer::viewIdentifierNaturalName(m_viewIdentifier);
404 }
405
406 return retval;
407 }
408
newPartsEditorPaletteItem(ModelPart * modelPart)409 PartsEditorPaletteItem *PartsEditorView::newPartsEditorPaletteItem(ModelPart *modelPart) {
410 return new PartsEditorConnectorsPaletteItem(this, modelPart, m_viewIdentifier);
411 }
412
newPartsEditorPaletteItem(ModelPart * modelPart,SvgAndPartFilePath * path)413 PartsEditorPaletteItem *PartsEditorView::newPartsEditorPaletteItem(ModelPart * modelPart, SvgAndPartFilePath *path) {
414 return new PartsEditorConnectorsPaletteItem(this, modelPart, m_viewIdentifier, path);
415 }
416
tempFolder()417 QDir PartsEditorView::tempFolder() {
418 return m_tempFolder;
419 }
420
getOrCreateViewFolderInTemp()421 QString PartsEditorView::getOrCreateViewFolderInTemp() {
422 QString viewFolder = ViewLayer::viewIdentifierNaturalName(m_viewIdentifier);
423
424 if(!QFileInfo(m_tempFolder.absolutePath()+"/"+viewFolder).exists()) {
425 bool mkResult = m_tempFolder.mkpath(m_tempFolder.absolutePath()+"/"+viewFolder);
426 if (!mkResult) {
427 throw "PartsEditorView::getOrCreateViewFolderInTemp failed";
428 }
429 }
430
431 return viewFolder;
432 }
433
isEmpty()434 bool PartsEditorView::isEmpty() {
435 return m_item == NULL;
436 }
437
ensureFilePath(const QString & filePath)438 bool PartsEditorView::ensureFilePath(const QString &filePath) {
439 QString svgFolder = FolderUtils::getUserDataStorePath("parts")+"/svg";
440
441 Qt::CaseSensitivity cs = Qt::CaseSensitive;
442 #ifdef Q_WS_WIN
443 cs = Qt::CaseInsensitive;
444 #endif
445 if(!filePath.contains(svgFolder, cs)) {
446 // This has to be here in order of all this, to work in release mode
447 m_tempFolder.mkpath(QFileInfo(filePath).absoluteDir().path());
448 }
449 return true;
450 }
451
connectorsLayerId()452 ViewLayer::ViewLayerID PartsEditorView::connectorsLayerId() {
453 //Q_ASSERT(m_item);
454 findConnectorsLayerId();
455 return m_connsLayerID;
456 }
457
terminalIdForConnector(const QString & connId)458 QString PartsEditorView::terminalIdForConnector(const QString &connId) {
459 //Q_ASSERT(m_item)
460
461 if (m_item == NULL) return "";
462
463 QString result = "";
464 QDomElement elem = m_item->svgDom()->documentElement();
465 if(terminalIdForConnectorIdAux(result, connId, elem, true)) {
466 return result;
467 } else {
468 return "";
469 }
470 }
471
terminalIdForConnectorIdAux(QString & result,const QString & connId,QDomElement & docElem,bool wantTerminal)472 bool PartsEditorView::terminalIdForConnectorIdAux(QString &result, const QString &connId, QDomElement &docElem, bool wantTerminal) {
473 QDomElement e = docElem.firstChildElement();
474 while(!e.isNull()) {
475 QString id = e.attribute("id");
476 if(id.startsWith(connId) && ((wantTerminal && id.endsWith("terminal")) || (!wantTerminal && !id.endsWith("terminal")))) {
477 // the id is the one from the previous iteration
478 result = id;
479 return true;
480 } else if(e.hasChildNodes()) {
481 // potencial solution, if the next iteration returns true
482 if(terminalIdForConnectorIdAux(result, connId, e, wantTerminal)) {
483 return true;
484 }
485 }
486 e = e.nextSiblingElement();
487 }
488 return false;
489 }
490
findConnectorsLayerId()491 void PartsEditorView::findConnectorsLayerId() {
492 if(m_connsLayerID == ViewLayer::UnknownLayer) {
493 if (m_item != NULL) {
494 m_connsLayerID = ViewLayer::viewLayerIDFromXmlString(
495 findConnectorsLayerId(m_item->svgDom())
496 );
497 }
498 if(m_connsLayerID == ViewLayer::UnknownLayer) {
499 m_connsLayerID = SketchWidget::defaultConnectorLayer(m_viewIdentifier);
500 }
501 }
502 }
503
findConnectorsLayerIds(QDomDocument * svgDom)504 QStringList PartsEditorView::findConnectorsLayerIds(QDomDocument *svgDom) {
505 QStringList result;
506 QDomElement docElem = svgDom->documentElement();
507 findConnectorsLayerIdsAux(result, docElem);
508 if (result.count() > 0) return result;
509
510 return defaultLayerAsStringlist();
511 }
512
findConnectorsLayerIdsAux(QStringList & result,QDomElement & docElem)513 void PartsEditorView::findConnectorsLayerIdsAux(QStringList &result, QDomElement &docElem) {
514 QDomElement e = docElem.firstChildElement();
515 while(!e.isNull()) {
516 QString id = e.attribute("id");
517 if (id.startsWith("connector")) {
518 QDomElement parent = e.parentNode().toElement();
519 QString id = parent.attribute("id");
520 if (!id.isEmpty() && (ViewLayer::viewLayerIDFromXmlString(id) != ViewLayer::UnknownLayer)) {
521 result << id;
522 }
523 }
524 else if(e.hasChildNodes()) {
525 findConnectorsLayerIdsAux(result, e);
526 }
527 e = e.nextSiblingElement();
528 }
529 }
530
findConnectorsLayerId(QDomDocument * svgDom)531 QString PartsEditorView::findConnectorsLayerId(QDomDocument *svgDom) {
532 QString result;
533 QStringList layers;
534 QDomElement docElem = svgDom->documentElement();
535 if(findConnectorsLayerIdAux(result, docElem, layers)) {
536 if(ViewLayer::viewLayerIDFromXmlString(result) == ViewLayer::UnknownLayer) {
537 foreach(QString layer, layers) {
538 ViewLayer::ViewLayerID vlid = ViewLayer::viewLayerIDFromXmlString(layer);
539 if(m_viewLayers.keys().contains(vlid)) {
540 result = layer;
541 }
542 }
543 }
544 return result;
545 } else {
546 return defaultLayerAsStringlist().at(0);
547 }
548 }
549
findConnectorsLayerIdAux(QString & result,QDomElement & docElem,QStringList & prevLayers)550 bool PartsEditorView::findConnectorsLayerIdAux(QString &result, QDomElement &docElem, QStringList &prevLayers) {
551 QDomElement e = docElem.firstChildElement();
552 while(!e.isNull()) {
553 QString id = e.attribute("id");
554 if(id.startsWith("connector")) {
555 // the id is the one from the previous iteration
556 return true;
557 } else if(e.hasChildNodes()) {
558 // potencial solution, if the next iteration returns true
559 result = id;
560 prevLayers << id;
561 if(findConnectorsLayerIdAux(result, e, prevLayers)) {
562 return true;
563 }
564 }
565 e = e.nextSiblingElement();
566 }
567 return false;
568 }
569
getLayerFileName(ModelPart * modelPart)570 QString PartsEditorView::getLayerFileName(ModelPart * modelPart) {
571 return modelPart->imageFileName(m_viewIdentifier);
572 }
573
574
575 // specs
copySvgFileToDestiny(const QString & partFileName)576 void PartsEditorView::copySvgFileToDestiny(const QString &partFileName) {
577 Qt::CaseSensitivity cs = Qt::CaseSensitive;
578 #ifdef Q_WS_WIN
579 cs = Qt::CaseInsensitive;
580 #endif
581
582 // if the svg file is in the temp folder, then copy it to destiny
583 if(m_svgFilePath->absolutePath().startsWith(m_tempFolder.absolutePath(),cs)) {
584 QString origFile = svgFilePath();
585 setFriendlierSvgFileName(partFileName);
586 QString destFile = FolderUtils::getUserDataStorePath("parts")+"/svg/user/"+m_svgFilePath->relativePath();
587
588 ensureFilePath(origFile);
589 QFile tempFile(origFile);
590 DebugDialog::debug(QString("copying from %1 to %2")
591 .arg(origFile)
592 .arg(destFile));
593 tempFile.copy(destFile);
594 tempFile.close();
595
596 // update the item info, to point to this file
597 m_svgFilePath->setAbsolutePath(destFile);
598 }
599 }
600
loadFile()601 void PartsEditorView::loadFile() {
602 QStringList extras;
603 extras.append("");
604 extras.append("");
605 QString imageFiles;
606 if (m_viewIdentifier == ViewLayer::PCBView) {
607 imageFiles = tr("Image & Footprint Files (%1 %2 %3 %4 %5);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3);;gEDA Footprint Files (%4);;Kicad Module Files (%5)"); //
608 extras[0] = "*.fp";
609 extras[1] = "*.mod";
610 }
611 else {
612 imageFiles = tr("Image Files (%1 %2 %3);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3)%4%5");
613 }
614
615 if (m_viewIdentifier == ViewLayer::SchematicView) {
616 extras[0] = "*.lib";
617 imageFiles = tr("Image & Footprint Files (%1 %2 %3 %4);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3);;Kicad Schematic Files (%4)%5"); //
618 }
619
620 QString origPath = FolderUtils::getOpenFileName(this,
621 tr("Open Image"),
622 m_originalSvgFilePath.isEmpty() ? FolderUtils::openSaveFolder() /* FolderUtils::getUserDataStorePath("parts")+"/parts/svg/" */ : m_originalSvgFilePath,
623 imageFiles.arg("*.svg").arg("*.jpg *.jpeg").arg("*.png").arg(extras[0]).arg(extras[1])
624 );
625
626 if(origPath.isEmpty()) {
627 return; // Cancel pressed
628 }
629
630 if(!origPath.endsWith(".svg")) {
631 try {
632 origPath = createSvgFromImage(origPath);
633 }
634 catch (const QString & msg) {
635 QMessageBox::warning(
636 NULL,
637 tr("Conversion problem"),
638 tr("Unable to load image file: \n%1").arg(msg)
639 );
640 return;
641 }
642 }
643 if(!origPath.isEmpty()) {
644 if(m_startItem) {
645 m_fixedToCenterItems.removeAll(m_startItem);
646 delete m_startItem;
647 m_startItem = NULL;
648 }
649 m_viewItem = NULL; // loading a new file, so m_viewItem is obsolete
650 loadSvgFile(origPath);
651 }
652 }
653
updateModelPart(const QString & origPath)654 void PartsEditorView::updateModelPart(const QString& origPath) {
655 m_undoStack->push(new QUndoCommand("Dummy parts editor command"));
656
657 setSvgFilePath(origPath);
658 copyToTempAndRenameIfNecessary(m_svgFilePath);
659 m_item->setSvgFilePath(m_svgFilePath);
660
661 ModelPart *mp = createFakeModelPart(m_svgFilePath);
662 m_item->setModelPart(mp);
663 }
664
loadSvgFile(const QString & origPath)665 void PartsEditorView::loadSvgFile(const QString& origPath) {
666 // back to an empty state
667 m_drawnConns.clear();
668 m_removedConnIds.clear();
669 m_connsLayerID = ViewLayer::UnknownLayer;
670 m_svgIds.clear();
671
672 m_svgLoaded = true;
673
674 bool canceled = false;
675 beforeSVGLoading(origPath, canceled);
676
677 if(!canceled) {
678 m_undoStack->push(new QUndoCommand("Dummy parts editor command"));
679 setSvgFilePath(origPath);
680
681 // TODO: this code reuses the current modelpart and replaces its connectors,
682 // it would be better to delete the modelpart and create a new one
683 // however, one would have to tidy up the various objects that rely on pointers
684 // to the original modelpart and its connectors
685 ModelPart * mp = createFakeModelPart(m_svgFilePath);
686 loadSvgFile(mp);
687 }
688 }
689
beforeSVGLoading(const QString & filename,bool & canceled)690 void PartsEditorView::beforeSVGLoading(const QString &filename, bool &canceled) {
691 QFile file(filename);
692 if(!file.open(QIODevice::ReadOnly )) {
693 QMessageBox::warning(
694 NULL,
695 tr("Couldn't open svg file"),
696 tr(
697 "The file couldn't be opened. If this file defines its dimensions \n"
698 "in non-real-world units (e.g. pixels), then they won't be translated \n"
699 "into real life ones.\n"
700 "Malformed font-family definitions won't be fixed either.")
701 );
702 return;
703 }
704
705 QString fileContent(file.readAll());
706 bool fileHasChanged = (m_viewIdentifier == ViewLayer::IconView) ? false : TextUtils::fixPixelDimensionsIn(fileContent);
707 fileHasChanged |= TextUtils::cleanSodipodi(fileContent);
708 fileHasChanged |= TextUtils::fixViewboxOrigin(fileContent);
709 fileHasChanged |= TextUtils::tspanRemove(fileContent);
710 fileHasChanged |= fixFonts(fileContent,filename,canceled);
711
712 if(fileHasChanged) {
713 file.close();
714 if(!TextUtils::writeUtf8(filename, fileContent)) {
715 QMessageBox::warning(
716 NULL,
717 tr("Couldn't write into file"),
718 tr(
719 "This file needs to be fixed to fit fritzing needs, but it couldn't\n"
720 "be written.\n"
721 "Fritzing is not compatible with this kind of svg files. Please \n"
722 "check your permissions, and try again.\n\n"
723
724 "More information at http://fritzing.org/using-svg-images-new-parts/"
725 )
726 );
727 }
728 }
729
730 }
731
fixFonts(QString & fileContent,const QString & filename,bool & canceled)732 bool PartsEditorView::fixFonts(QString &fileContent, const QString &filename, bool &canceled) {
733 bool changed = removeFontFamilySingleQuotes(fileContent, filename);
734 changed |= fixUnavailableFontFamilies(fileContent, filename, canceled);
735
736 return changed;
737 }
738
removeFontFamilySingleQuotes(QString & fileContent,const QString & filename)739 bool PartsEditorView::removeFontFamilySingleQuotes(QString &fileContent, const QString &filename) {
740 QString pattern = "font-family=\"('.*')\"";
741 QSet<QString> wrongFontFamilies = TextUtils::getRegexpCaptures(pattern,fileContent);
742
743 foreach(QString ff, wrongFontFamilies) {
744 QString wrongFF = ff;
745 QString fixedFF = ff.remove('\'');
746 fileContent.replace(wrongFF,fixedFF);
747 DebugDialog::debug(
748 QString("removing font-family single quotes: \"%1\" to \"%2\" in file '%3'")
749 .arg(wrongFF).arg(fixedFF).arg(filename)
750 );
751 }
752
753 return wrongFontFamilies.size() > 0;
754 }
755
fixUnavailableFontFamilies(QString & fileContent,const QString & filename,bool & canceled)756 bool PartsEditorView::fixUnavailableFontFamilies(QString &fileContent, const QString &filename, bool &canceled) {
757 QSet<QString> definedFFs;
758 definedFFs.unite(getAttrFontFamilies(fileContent));
759 definedFFs.unite(getFontFamiliesInsideStyleTag(fileContent));
760
761 FixedFontsHash fixedFonts = FixFontsDialog::fixFonts(this,definedFFs,canceled);
762
763 if(!canceled) {
764 foreach(QString oldF, fixedFonts.keys()) {
765 QString newF = fixedFonts[oldF];
766 fileContent.replace(oldF,newF);
767 DebugDialog::debug(
768 QString("replacing font-family: \"%1\" to \"%2\" in file '%3'")
769 .arg(oldF).arg(newF).arg(filename)
770 );
771 }
772 }
773
774 return !canceled && fixedFonts.size() > 0;
775 }
776
getAttrFontFamilies(const QString & fileContent)777 QSet<QString> PartsEditorView::getAttrFontFamilies(const QString &fileContent) {
778 /*
779 * font-family defined as attr example:
780
781 <text xmlns="http://www.w3.org/2000/svg" font-family="DroidSans"
782 id="text2732" transform="matrix(1 0 0 1 32.2012 236.969)"
783 font-size="9.9771" >A0</text>
784
785 */
786
787 QString pattern = "font-family\\s*=\\s*\"(.|[^\"]*)\\s*\"";
788 return TextUtils::getRegexpCaptures(pattern,fileContent);
789 }
790
getFontFamiliesInsideStyleTag(const QString & fileContent)791 QSet<QString> PartsEditorView::getFontFamiliesInsideStyleTag(const QString &fileContent) {
792 /*
793 * regexp: font-family\s*:\s*(.|[^;"]*).*"
794 * font-family defined in a style attr example:
795
796 style="font-size:9;-inkscape-font-specification:Droid Sans;font-family:Droid Sans;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
797
798 style="font-size:144px;font-style:normal;font-weight:normal;line-height:100%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans" x="18.000002"
799
800 */
801
802 QString pattern = "font-family\\s*:\\s*(.|[^;\"]*).*\"";
803 return TextUtils::getRegexpCaptures(pattern,fileContent);
804 }
805
loadSvgFile(ModelPart * modelPart)806 void PartsEditorView::loadSvgFile(ModelPart * modelPart) {
807 addItemInPartsEditor(modelPart, m_svgFilePath);
808 copyToTempAndRenameIfNecessary(m_svgFilePath);
809 m_item->setSvgFilePath(m_svgFilePath);
810 }
811
loadFromModel(PaletteModel * paletteModel,ModelPart * modelPart)812 void PartsEditorView::loadFromModel(PaletteModel *paletteModel, ModelPart * modelPart) {
813 clearScene();
814
815 ViewGeometry viewGeometry;
816 this->setPaletteModel(paletteModel);
817 m_item = (PartsEditorPaletteItem*) addItemAux(modelPart, defaultViewLayerSpec(), viewGeometry, ItemBase::getNextID(), NULL, true, m_viewIdentifier, true);
818
819 fitCenterAndDeselect();
820
821 setItemProperties();
822
823 if(m_item) {
824 if(m_startItem) {
825 m_fixedToCenterItems.removeAll(m_startItem);
826 delete m_startItem;
827 m_startItem = NULL;
828 }
829
830
831 SvgAndPartFilePath *sp = m_item->svgFilePath();
832
833 copyToTempAndRenameIfNecessary(sp);
834 m_item->setSvgFilePath(m_svgFilePath);
835 }
836 }
837
copyToTempAndRenameIfNecessary(SvgAndPartFilePath * filePathOrig)838 void PartsEditorView::copyToTempAndRenameIfNecessary(SvgAndPartFilePath *filePathOrig) {
839 m_originalSvgFilePath = filePathOrig->absolutePath();
840 QString userSvgFolderPath = FolderUtils::getUserDataStorePath("parts")+"/svg";
841 QString coreSvgFolderPath = FolderUtils::getApplicationSubFolderPath("parts")+"/svg";
842 QString pfSvgFolderPath = PartFactory::folderPath()+"/svg";
843
844 if(!(filePathOrig->absolutePath().startsWith(userSvgFolderPath)
845 || filePathOrig->absolutePath().startsWith(coreSvgFolderPath)
846 || filePathOrig->absolutePath().startsWith(pfSvgFolderPath))
847 )
848 { // it's outside the parts folder
849 DebugDialog::debug(QString("copying from %1").arg(m_originalSvgFilePath));
850 QString viewFolder = ViewLayer::viewIdentifierNaturalName(m_viewIdentifier);
851
852 if(!QFileInfo(m_tempFolder.path()+"/"+viewFolder).exists()
853 && !m_tempFolder.mkdir(viewFolder)) return;
854 if(!m_tempFolder.cd(viewFolder)) return;
855
856 QString destFilePath = FolderUtils::getRandText()+".svg";
857 DebugDialog::debug(QString("dest file: %1").arg(m_tempFolder.absolutePath()+"/"+destFilePath));
858
859 ensureFilePath(m_tempFolder.absolutePath()+"/"+destFilePath);
860
861 QFile tempFile(m_originalSvgFilePath);
862 tempFile.copy(m_tempFolder.absolutePath()+"/"+destFilePath);
863 tempFile.close();
864
865 if(!m_tempFolder.cd("..")) return; // out of view folder
866
867 m_svgFilePath->setRelativePath(viewFolder+"/"+destFilePath);
868 m_svgFilePath->setAbsolutePath(m_tempFolder.absolutePath()+"/"+m_svgFilePath->relativePath());
869
870 } else {
871 QString relPathAux = filePathOrig->relativePath();
872 m_svgFilePath->setAbsolutePath(m_originalSvgFilePath);
873 if (relPathAux.count("/") > 2) {
874 throw "PartsEditorView::copyToTempAndRenameIfNecessary bad path";
875 }
876
877 if(relPathAux.count("/") == 2) { // this means that core/user/contrib is still in the file name
878 m_svgFilePath->setRelativePath(
879 relPathAux.right(// remove user/core/contrib
880 relPathAux.size() -
881 relPathAux.indexOf("/") - 1
882 )
883 );
884 } else { //otherwise, just leave it as it is
885 m_svgFilePath->setRelativePath(relPathAux);
886 }
887 }
888 }
889
setSvgFilePath(const QString & filePath)890 void PartsEditorView::setSvgFilePath(const QString &filePath) {
891 ensureFilePath(filePath);
892 m_originalSvgFilePath = filePath;
893
894 QString userSvgFolder = FolderUtils::getUserDataStorePath("parts")+"/svg";
895 QString coreSvgFolder = FolderUtils::getApplicationSubFolderPath("parts")+"/svg";
896 QString pfSvgFolder = PartFactory::folderPath()+"/svg";
897
898 QString tempFolder = m_tempFolder.absolutePath();
899
900 QString relative;
901 Qt::CaseSensitivity cs = Qt::CaseSensitive;
902 QString filePathAux = filePath;
903
904 #ifdef Q_WS_WIN
905 // seems to be necessary for Windows: getUserDataStorePath() returns a string starting with "c:"
906 // but the file dialog returns a string beginning with "C:"
907 cs = Qt::CaseInsensitive;
908 #endif
909 if(filePath.contains(userSvgFolder, cs) || filePath.contains(coreSvgFolder, cs) || filePath.contains(pfSvgFolder, cs)) {
910 int ix = filePath.indexOf("svg");
911 // is core/user file
912 relative = filePathAux.remove(0, ix + 4);
913 //Mariano: I don't like this folder thing anymore
914 relative = relative.mid(filePathAux.indexOf("/")+1); // remove core/user/contrib
915 } else {
916 // generated jpeg/png or file outside fritzing folder
917 relative = "";
918 }
919
920 if (m_svgFilePath) delete m_svgFilePath;
921 m_svgFilePath = new SvgAndPartFilePath(filePath,relative);
922 }
923
924
svgFilePath()925 const QString PartsEditorView::svgFilePath() {
926 return m_svgFilePath->absolutePath();
927 }
928
svgFileSplit()929 const SvgAndPartFilePath& PartsEditorView::svgFileSplit() {
930 return *m_svgFilePath;
931 }
932
createSvgFromImage(const QString & origFilePath)933 QString PartsEditorView::createSvgFromImage(const QString &origFilePath) {
934 QString viewFolder = getOrCreateViewFolderInTemp();
935
936 QString newFilePath = m_tempFolder.absolutePath()+"/"+viewFolder+"/"+FolderUtils::getRandText()+".svg";
937 ensureFilePath(newFilePath);
938
939 if (origFilePath.endsWith(".fp")) {
940 // this is a geda footprint file
941 GedaElement2Svg geda;
942 QString svg = geda.convert(origFilePath, false);
943 return saveSvg(svg, newFilePath);
944 }
945
946 if (origFilePath.endsWith(".lib")) {
947 // Kicad schematic library file
948 QStringList defs = KicadSchematic2Svg::listDefs(origFilePath);
949 if (defs.count() == 0) {
950 throw tr("no schematics found in %1").arg(origFilePath);
951 }
952
953 QString def;
954 if (defs.count() > 1) {
955 KicadModuleDialog kmd(tr("schematic part"), origFilePath, defs, this);
956 int result = kmd.exec();
957 if (result != QDialog::Accepted) {
958 return "";
959 }
960
961 def = kmd.selectedModule();
962 }
963 else {
964 def = defs.at(0);
965 }
966
967 KicadSchematic2Svg kicad;
968 QString svg = kicad.convert(origFilePath, def);
969 return saveSvg(svg, newFilePath);
970 }
971
972 if (origFilePath.endsWith(".mod")) {
973 // Kicad footprint (Module) library file
974 QStringList modules = KicadModule2Svg::listModules(origFilePath);
975 if (modules.count() == 0) {
976 throw tr("no footprints found in %1").arg(origFilePath);
977 }
978
979 QString module;
980 if (modules.count() > 1) {
981 KicadModuleDialog kmd("footprint", origFilePath, modules, this);
982 int result = kmd.exec();
983 if (result != QDialog::Accepted) {
984 return "";
985 }
986
987 module = kmd.selectedModule();
988 }
989 else {
990 module = modules.at(0);
991 }
992
993 KicadModule2Svg kicad;
994 QString svg = kicad.convert(origFilePath, module, false);
995 return saveSvg(svg, newFilePath);
996 }
997
998 // deal with png, jpg, etc.:
999
1000
1001 /* %1=witdh in mm
1002 * %2=height in mm
1003 * %3=width in local coords
1004 * %4=height in local coords
1005 * %5=binary data
1006 */
1007 /* QString svgTemplate =
1008 "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
1009 " <svg width='%1mm' height='%2mm' viewBox='0 0 %3 %4' xmlns='http://www.w3.org/2000/svg'\n"
1010 " xmlns:xlink='http://www.w3.org/1999/xlink' version='1.2' baseProfile='tiny'>\n"
1011 " <g fill='none' stroke='black' vector-effect='non-scaling-stroke' stroke-width='1'\n"
1012 " fill-rule='evenodd' stroke-linecap='square' stroke-linejoin='bevel' >\n"
1013 " <image x='0' y='0' width='%3' height='%4'\n"
1014 " xlink:href='data:image/png;base64,%5' />\n"
1015 " </g>\n"
1016 " </svg>";
1017
1018 QPixmap pixmap(origFilePath);
1019 QByteArray bytes;
1020 QBuffer buffer(&bytes);
1021 buffer.open(QIODevice::WriteOnly);
1022 pixmap.save(&buffer,"png"); // writes pixmap into bytes in PNG format
1023
1024 QString svgDom = svgTemplate
1025 .arg(pixmap.widthMM()).arg(pixmap.heightMM())
1026 .arg(pixmap.width()).arg(pixmap.height())
1027 .arg(QString("data:image/png;base64,%2").arg(QString(bytes.toBase64())));
1028
1029 QFile destFile(newFilePath);
1030 if(!destFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
1031 QMessageBox::information(NULL, "", "file not created");
1032 if(!destFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
1033 QMessageBox::information(NULL, "", "file not created 2");
1034 }
1035 }
1036 QTextStream out(&destFile);
1037 out << svgDom;
1038 destFile.close();
1039 qDebug() << newFilePath;
1040 bool existsResult = QFileInfo(newFilePath).exists();
1041 Q_ASSERT(existsResult);
1042 */
1043
1044 QImage img(origFilePath);
1045 QSvgGenerator svgGenerator;
1046 svgGenerator.setResolution(90);
1047 svgGenerator.setFileName(newFilePath);
1048 QSize sz = img.size();
1049 svgGenerator.setSize(sz);
1050 svgGenerator.setViewBox(QRect(0, 0, sz.width(), sz.height()));
1051 QPainter svgPainter(&svgGenerator);
1052 svgPainter.drawImage(QPoint(0,0), img);
1053 svgPainter.end();
1054
1055 return newFilePath;
1056 }
1057
setFriendlierSvgFileName(const QString & partFileName)1058 QString PartsEditorView::setFriendlierSvgFileName(const QString &partFileName) {
1059 QString aux = partFileName;
1060 aux = aux
1061 .remove(FritzingPartExtension)
1062 .replace(" ","_");
1063 if(aux.length()>40) aux.truncate(40);
1064 aux+=QString("__%1__%2.svg")
1065 .arg(ViewLayer::viewIdentifierNaturalName(m_viewIdentifier))
1066 .arg(FolderUtils::getRandText());
1067 int slashIdx = m_svgFilePath->relativePath().indexOf("/");
1068 QString relpath = m_svgFilePath->relativePath();
1069 QString relpath2 = relpath;
1070 QString abspath = m_svgFilePath->absolutePath();
1071 QString viewFolder = relpath.remove(slashIdx,relpath.size()-slashIdx+1);
1072 m_svgFilePath->setAbsolutePath(abspath.remove(relpath2)+viewFolder+"/"+aux);
1073 m_svgFilePath->setRelativePath(viewFolder+"/"+aux);
1074 return aux;
1075 }
1076
1077
1078 // conns
wheelEvent(QWheelEvent * event)1079 void PartsEditorView::wheelEvent(QWheelEvent* event) {
1080 if(m_showingTerminalPoints) {
1081 if(!m_terminalPointsTimer->isActive()) {
1082 m_showingTerminalPointsBackup = m_showingTerminalPoints;
1083 showTerminalPoints(false);
1084 m_terminalPointsTimer->start(50);
1085 }
1086 } else if(m_terminalPointsTimer->isActive()) {
1087 m_terminalPointsTimer->stop();
1088 m_terminalPointsTimer->start(50);
1089 }
1090 SketchWidget::wheelEvent(event);
1091 }
1092
mousePressEvent(QMouseEvent * event)1093 void PartsEditorView::mousePressEvent(QMouseEvent *event) {
1094 SketchWidget::mousePressEvent(event);
1095 }
1096
mouseMoveEvent(QMouseEvent * event)1097 void PartsEditorView::mouseMoveEvent(QMouseEvent *event) {
1098 QGraphicsView::mouseMoveEvent(event);
1099 }
1100
mouseReleaseEvent(QMouseEvent * event)1101 void PartsEditorView::mouseReleaseEvent(QMouseEvent *event) {
1102 SketchWidget::mouseReleaseEvent(event);
1103 }
1104
resizeEvent(QResizeEvent * event)1105 void PartsEditorView::resizeEvent(QResizeEvent * event) {
1106 SketchWidget::resizeEvent(event);
1107 if(m_fitItemInViewTimer->isActive()) {
1108 m_fitItemInViewTimer->stop();
1109 }
1110 m_fitItemInViewTimer->start();
1111
1112 }
1113
drawConector(Connector * conn,bool showTerminalPoint)1114 void PartsEditorView::drawConector(Connector *conn, bool showTerminalPoint) {
1115 QSize size(ConnDefaultWidth,ConnDefaultHeight);
1116 createConnector(conn,size,showTerminalPoint);
1117 }
1118
createConnector(Connector * conn,const QSize & connSize,bool showTerminalPoint)1119 void PartsEditorView::createConnector(Connector *conn, const QSize &connSize, bool showTerminalPoint) {
1120 QString connId = conn->connectorSharedID();
1121
1122 QRectF bounds = m_item
1123 ? QRectF(m_item->boundingRect().center(),connSize)
1124 : QRectF(scene()->itemsBoundingRect().center(),connSize);
1125 PartsEditorConnectorsConnectorItem *connItem = new PartsEditorConnectorsConnectorItem(conn, m_item, m_showingTerminalPoints, bounds);
1126 m_drawnConns[connId] = connItem;
1127 connItem->setShowTerminalPoint(showTerminalPoint);
1128
1129 m_undoStack->push(new QUndoCommand(
1130 QString("connector '%1' added to %2 view")
1131 .arg(connId).arg(ViewLayer::viewIdentifierName(m_viewIdentifier))
1132 ));
1133 }
1134
removeConnector(const QString & connId)1135 void PartsEditorView::removeConnector(const QString &connId) {
1136 ConnectorItem *connToRemove = NULL;
1137 foreach(QGraphicsItem *item, items()) {
1138 ConnectorItem *connItem = dynamic_cast<ConnectorItem*>(item);
1139 if(connItem && connItem->connector()->connectorSharedID() == connId) {
1140 connToRemove = connItem;
1141 break;
1142 }
1143 }
1144
1145 if(connToRemove) {
1146 scene()->removeItem(connToRemove);
1147 scene()->update();
1148 m_undoStack->push(new QUndoCommand(
1149 QString("connector '%1' removed from %2 view")
1150 .arg(connId).arg(ViewLayer::viewIdentifierName(m_viewIdentifier))
1151 ));
1152
1153 PartsEditorConnectorsConnectorItem *connToRemoveAux = dynamic_cast<PartsEditorConnectorsConnectorItem*>(connToRemove);
1154 m_drawnConns.remove(connToRemoveAux->connectorSharedID());
1155 m_removedConnIds << connId;
1156 }
1157 }
1158
setItemProperties()1159 void PartsEditorView::setItemProperties() {
1160 if(m_item) {
1161 m_item->setFlag(QGraphicsItem::ItemIsSelectable, false);
1162 m_item->setFlag(QGraphicsItem::ItemIsMovable, false);
1163 m_item->setFlag(QGraphicsItem::ItemClipsToShape, true);
1164 //m_item->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
1165 myItem()->highlightConnectors(m_lastSelectedConnId);
1166
1167 double size = 500; // just make sure the user get enough space to play
1168 setSceneRect(0,0,size,size);
1169
1170
1171 m_item->setPos((size-m_item->size().width())/2,(size-m_item->size().height())/2);
1172 centerOn(m_item);
1173
1174 }
1175 //ensureFixedToBottomRight(m_zoomControls);
1176 }
1177
informConnectorSelection(const QString & connId)1178 void PartsEditorView::informConnectorSelection(const QString &connId) {
1179 if(m_item) {
1180 m_lastSelectedConnId = connId;
1181 myItem()->highlightConnectors(connId);
1182 }
1183 }
1184
informConnectorSelectionFromView(const QString & connId)1185 void PartsEditorView::informConnectorSelectionFromView(const QString &connId) {
1186 informConnectorSelection(connId);
1187 emit connectorSelected(connId);
1188 }
1189
setMismatching(ViewLayer::ViewIdentifier viewId,const QString & id,bool mismatching)1190 void PartsEditorView::setMismatching(ViewLayer::ViewIdentifier viewId, const QString &id, bool mismatching) {
1191 if(m_item && viewId == m_viewIdentifier) {
1192 for (int i = 0; i < m_item->childItems().count(); i++) {
1193 PartsEditorConnectorsConnectorItem * connectorItem
1194 = dynamic_cast<PartsEditorConnectorsConnectorItem *>(m_item->childItems()[i]);
1195 if(connectorItem == NULL) continue;
1196
1197 if(connectorItem->connector()->connectorSharedID() == id) {
1198 connectorItem->setMismatching(mismatching);
1199 }
1200 }
1201 }
1202 }
1203
aboutToSave(bool fakeDefaultIfNone)1204 void PartsEditorView::aboutToSave(bool fakeDefaultIfNone) {
1205 if(m_item) {
1206 FSvgRenderer renderer;
1207 QByteArray bytes = renderer.loadSvg(m_item->flatSvgFilePath());
1208 if (!bytes.isEmpty()) {
1209 QRectF svgViewBox = renderer.viewBoxF();
1210 QSizeF sceneViewBox = renderer.defaultSizeF();
1211 QDomDocument *svgDom = m_item->svgDom();
1212
1213 // this may change the layers defined in the file, so
1214 // let's get the connectorsLayer after it
1215 bool somethingChanged = addDefaultLayerIfNotInSvg(svgDom, fakeDefaultIfNone);
1216
1217 QDomElement elem = svgDom->documentElement();
1218
1219 somethingChanged |= removeConnectorsIfNeeded(elem);
1220 QStringList connectorsLayerIds = findConnectorsLayerIds(svgDom);
1221 foreach (QString connectorsLayerId, connectorsLayerIds) {
1222 somethingChanged |= updateTerminalPoints(svgDom, sceneViewBox, svgViewBox, connectorsLayerId);
1223 somethingChanged |= addConnectorsIfNeeded(svgDom, sceneViewBox, svgViewBox, connectorsLayerId);
1224 }
1225 somethingChanged |= (m_viewItem != NULL);
1226
1227 if(somethingChanged) {
1228 QString viewFolder = getOrCreateViewFolderInTemp();
1229
1230 QString tempFile = m_tempFolder.absolutePath()+"/"+viewFolder+"/"+FolderUtils::getRandText()+".svg";
1231
1232 ensureFilePath(tempFile);
1233
1234 if(!TextUtils::writeUtf8(tempFile, TextUtils::removeXMLEntities(svgDom->toString()))) {
1235 /*QMessageBox::information(NULL,"",
1236 QString("Couldn't open file for update, after drawing connectors: '%1'")
1237 .arg(tempFile)
1238 );*/
1239 DebugDialog::debug(QString("Couldn't open file for update, after drawing connectors: '%1'").arg(tempFile));
1240 }
1241 }
1242 } else {
1243 DebugDialog::debug("updating part view svg file: could not load file "+m_item->flatSvgFilePath());
1244 }
1245 }
1246 }
1247
addConnectorsIfNeeded(QDomDocument * svgDom,const QSizeF & sceneViewBox,const QRectF & svgViewBox,const QString & connectorsLayerId)1248 bool PartsEditorView::addConnectorsIfNeeded(QDomDocument *svgDom, const QSizeF &sceneViewBox, const QRectF &svgViewBox, const QString &connectorsLayerId) {
1249 bool changed = false;
1250 if(!m_drawnConns.isEmpty()) {
1251 QRectF bounds;
1252 QString connId;
1253
1254 foreach(PartsEditorConnectorsConnectorItem* drawnConn, m_drawnConns.values()) {
1255 bounds = drawnConn->mappedRect();
1256 connId = drawnConn->connector()->connectorSharedID();
1257
1258 QRectF svgRect = mapFromSceneToSvg(bounds,sceneViewBox,svgViewBox);
1259 QString svgId = svgIdForConnector(drawnConn->connector(), connId);
1260 addRectToSvg(svgDom,svgId,svgRect, connectorsLayerId);
1261 }
1262 changed = true;
1263 }
1264
1265 return changed;
1266 }
1267
1268
addDefaultLayerIfNotInSvg(QDomDocument * svgDom,bool fakeDefaultIfNone)1269 bool PartsEditorView::addDefaultLayerIfNotInSvg(QDomDocument *svgDom, bool fakeDefaultIfNone)
1270 {
1271 QStringList defaultLayers = defaultLayerAsStringlist();
1272 QStringList layers = getLayers(svgDom, fakeDefaultIfNone);
1273 foreach (QString defaultLayer, defaultLayers) {
1274 if (layers.contains(defaultLayer)) return false;
1275 }
1276
1277 // jrc 4/7/2010: not sure if making a new top level layer is correct
1278 // since it could swallow other layers
1279
1280 QDomElement docElem = svgDom->documentElement();
1281
1282 QDomElement newTopLevel = svgDom->createElement("g");
1283 newTopLevel.setAttribute("id", defaultLayers.at(0));
1284
1285 // place the child in a aux list, cause the
1286 // qdomnodelist takes care of the references
1287 QList<QDomNode> children;
1288 for(QDomNode child=docElem.firstChild(); !child.isNull(); child=child.nextSibling()) {
1289 children << child;
1290 }
1291
1292 foreach(QDomNode child, children) {
1293 newTopLevel.appendChild(child);
1294 }
1295
1296 docElem.appendChild(newTopLevel);
1297
1298 return true;
1299 }
1300
defaultLayers()1301 LayerList PartsEditorView::defaultLayers() {
1302 LayerList layers;
1303 switch( m_viewIdentifier ) {
1304 case ViewLayer::IconView:
1305 layers << ViewLayer::Icon;
1306 break;
1307 case ViewLayer::BreadboardView:
1308 layers << ViewLayer::Breadboard;
1309 break;
1310 case ViewLayer::SchematicView:
1311 layers << ViewLayer::Schematic;
1312 break;
1313 case ViewLayer::PCBView:
1314 layers << ViewLayer::Copper0 << ViewLayer::Copper1;
1315 break;
1316 default:
1317 layers << ViewLayer::UnknownLayer;
1318 break;
1319 }
1320 return layers;
1321 }
1322
defaultLayerAsStringlist()1323 QStringList PartsEditorView::defaultLayerAsStringlist() {
1324 QStringList layers;
1325 foreach (ViewLayer::ViewLayerID viewLayerID, defaultLayers()) {
1326 layers << ViewLayer::viewLayerXmlNameFromID(viewLayerID);
1327 }
1328
1329 return layers;
1330 }
1331
svgIdForConnector(const QString & connId)1332 QString PartsEditorView::svgIdForConnector(const QString &connId) {
1333 //Q_ASSERT(m_item)
1334
1335 if (m_item == NULL) return connId;
1336
1337
1338 QString result = "";
1339 QDomElement elem = m_item->svgDom()->documentElement();
1340 if(terminalIdForConnectorIdAux(result, connId, elem, false)) {
1341 return result;
1342 }
1343
1344 /*
1345 foreach(Connector* conn, m_item->connectors()) {
1346 QString svgId = svgIdForConnector(conn, connId);
1347 if(connId != svgId) {
1348 return svgId;
1349 }
1350 }
1351
1352 */
1353
1354 return connId;
1355 }
1356
svgIdForConnector(Connector * conn,const QString & connId)1357 QString PartsEditorView::svgIdForConnector(Connector* conn, const QString &connId) {
1358 if (conn->connectorShared() && conn->connectorSharedID() == connId) {
1359 foreach(SvgIdLayer *sil, conn->connectorShared()->pins().values(m_viewIdentifier)) {
1360 return sil->m_svgId;
1361 }
1362 }
1363 return connId;
1364 }
1365
updateTerminalPoints(QDomDocument * svgDom,const QSizeF & sceneViewBox,const QRectF & svgViewBox,const QString & connectorsLayerId)1366 bool PartsEditorView::updateTerminalPoints(QDomDocument *svgDom, const QSizeF &sceneViewBox, const QRectF &svgViewBox, const QString &connectorsLayerId) {
1367 QList<PartsEditorConnectorsConnectorItem*> connsWithNewTPs;
1368 QStringList tpIdsToRemove;
1369 foreach(QGraphicsItem *item, items()) {
1370 PartsEditorConnectorsConnectorItem *citem = dynamic_cast<PartsEditorConnectorsConnectorItem*>(item);
1371 if(citem) {
1372 TerminalPointItem *tp = citem->terminalPointItem();
1373 if (!tp) {
1374 citem->setShowTerminalPoint(citem->isShowingTerminalPoint());
1375 tp = citem->terminalPointItem();
1376 }
1377
1378 QString connId = citem->connector()->connectorSharedID();
1379 QString terminalId = connId+"terminal";
1380
1381 if(tp && !tp->isInTheCenter()) {
1382 if(tp->hasBeenMoved() || citem->hasBeenMoved()) {
1383 connsWithNewTPs << citem;
1384 tpIdsToRemove << terminalId;
1385 //DebugDialog::debug("<<<< MOVED! removing terminal "+terminalId+" in view: "+ViewLayer::viewIdentifierName(m_viewIdentifier));
1386 updateSvgIdLayer(connId, terminalId, connectorsLayerId);
1387 }
1388 } else {
1389 //DebugDialog::debug("<<<< removing terminal "+terminalId+" in view: "+ViewLayer::viewIdentifierName(m_viewIdentifier));
1390 tpIdsToRemove << terminalId;
1391 emit removeTerminalPoint(connId, m_viewIdentifier);
1392 }
1393 }
1394 }
1395 QDomElement elem = svgDom->documentElement();
1396 removeTerminalPoints(tpIdsToRemove,elem);
1397 addNewTerminalPoints(connsWithNewTPs, svgDom, sceneViewBox, svgViewBox, connectorsLayerId);
1398 return !tpIdsToRemove.isEmpty();
1399 }
1400
updateSvgIdLayer(const QString & connId,const QString & terminalId,const QString & connectorsLayerId)1401 void PartsEditorView::updateSvgIdLayer(const QString &connId, const QString &terminalId, const QString &connectorsLayerId) {
1402 ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString(connectorsLayerId);
1403 foreach(Connector *conn, m_item->connectors()) {
1404 foreach(SvgIdLayer *sil, conn->connectorShared()->pins().values(m_viewIdentifier)) {
1405 if(conn->connectorSharedID() == connId) {
1406 if (sil->m_svgViewLayerID == viewLayerID) {
1407 sil->m_terminalId = terminalId;
1408 return;
1409 }
1410 }
1411 }
1412
1413 foreach(SvgIdLayer *sil, conn->connectorShared()->pins().values(m_viewIdentifier)) {
1414 if(conn->connectorSharedID() == connId) {
1415 sil->m_terminalId = terminalId;
1416
1417 if(viewLayerID != ViewLayer::UnknownLayer) {
1418 sil->m_svgViewLayerID = viewLayerID;
1419 }
1420 }
1421 }
1422 }
1423 }
1424
removeTerminalPoints(const QStringList & tpIdsToRemove,QDomElement & docElem)1425 void PartsEditorView::removeTerminalPoints(const QStringList &tpIdsToRemove, QDomElement &docElem) {
1426 QDomElement e = docElem.firstChildElement();
1427 while(!e.isNull()) {
1428 bool doRemove = false;
1429 QString id = e.attribute("id");
1430 if(tpIdsToRemove.contains(id)) {
1431 doRemove = true;
1432 } else if(e.hasChildNodes()) {
1433 removeTerminalPoints(tpIdsToRemove,e);
1434 }
1435 QDomElement e2;
1436 if(doRemove) {
1437 e2 = e;
1438 }
1439 e = e.nextSiblingElement();
1440 if(doRemove) {
1441 e2.removeAttribute("id");
1442 }
1443 }
1444 }
1445
addNewTerminalPoints(const QList<PartsEditorConnectorsConnectorItem * > & connsWithNewTPs,QDomDocument * svgDom,const QSizeF & sceneViewBox,const QRectF & svgViewBox,const QString & connectorsLayerId)1446 void PartsEditorView::addNewTerminalPoints(
1447 const QList<PartsEditorConnectorsConnectorItem*> &connsWithNewTPs, QDomDocument *svgDom,
1448 const QSizeF &sceneViewBox, const QRectF &svgViewBox, const QString &connectorsLayerId
1449 ) {
1450 foreach(PartsEditorConnectorsConnectorItem* citem, connsWithNewTPs) {
1451 QString connId = citem->connector()->connectorSharedID();
1452 TerminalPointItem *tp = citem->terminalPointItem();
1453 if (tp == NULL) {
1454 throw "PartsEditorView::addNewTerminalPoints tp missing";
1455 }
1456
1457 if(tp) {
1458 QRectF tpointRect(tp->mappedPoint(), QPointF(0,0));
1459 QRectF svgTpRect = mapFromSceneToSvg(tpointRect,sceneViewBox,svgViewBox);
1460
1461 double halfTPSize = 0.001; // a tiny rectangle
1462 svgTpRect.setSize(QSizeF(halfTPSize*2,halfTPSize*2));
1463
1464 addRectToSvg(svgDom,connId+"terminal",svgTpRect, connectorsLayerId);
1465 } else {
1466 qWarning() << tr(
1467 "Parts Editor: couldn't save terminal "
1468 "point for connector %1 in %2 view")
1469 .arg(citem->connector()->connectorSharedID())
1470 .arg(ViewLayer::viewIdentifierNaturalName(m_viewIdentifier));
1471 }
1472 }
1473 }
1474
removeConnectorsIfNeeded(QDomElement & docElem)1475 bool PartsEditorView::removeConnectorsIfNeeded(QDomElement &docElem) {
1476 if(!m_removedConnIds.isEmpty()) {
1477 //Q_ASSERT(docElem.tagName() == "svg");
1478
1479 QDomElement e = docElem.firstChildElement();
1480 while(!e.isNull()) {
1481 QString id = e.attribute("id");
1482 if(isSupposedToBeRemoved(id)) {
1483 e.removeAttribute("id");
1484 } else if(e.hasChildNodes()) {
1485 removeConnectorsIfNeeded(e);
1486 }
1487 e = e.nextSiblingElement();
1488 }
1489 return true;
1490 }
1491 return false;
1492 }
1493
mapFromSceneToSvg(const QRectF & itemRect,const QSizeF & sceneViewBox,const QRectF & svgViewBox)1494 QRectF PartsEditorView::mapFromSceneToSvg(const QRectF &itemRect, const QSizeF &sceneViewBox, const QRectF &svgViewBox) {
1495 double relationW = svgViewBox.width() / sceneViewBox.width();
1496 double relationH = svgViewBox.height() / sceneViewBox.height();
1497
1498 double x = itemRect.x() * relationW;
1499 double y = itemRect.y() * relationH;
1500 double width = itemRect.width() * relationW;
1501 double height = itemRect.height() * relationH;
1502
1503 return QRectF(x,y,width,height);
1504 }
1505
addRectToSvg(QDomDocument * svgDom,const QString & id,const QRectF & rect,const QString & connectorsLayerId)1506 bool PartsEditorView::addRectToSvg(QDomDocument* svgDom, const QString &id, const QRectF &rect, const QString &connectorsLayerId) {
1507 QDomElement connElem = svgDom->createElement("rect");
1508 connElem.setAttribute("id",id);
1509 connElem.setAttribute("x",rect.x());
1510 connElem.setAttribute("y",rect.y());
1511 connElem.setAttribute("width",rect.width());
1512 connElem.setAttribute("height",rect.height());
1513 connElem.setAttribute("fill","none");
1514
1515 if(connectorsLayerId.isEmpty()) {
1516 svgDom->firstChildElement("svg").appendChild(connElem);
1517 return true;
1518 } else {
1519 QDomElement docElem = svgDom->documentElement();
1520 return addRectToSvgAux(docElem, connectorsLayerId, connElem);
1521 }
1522 }
1523
addRectToSvgAux(QDomElement & docElem,const QString & connectorsLayerId,QDomElement & rectElem)1524 bool PartsEditorView::addRectToSvgAux(QDomElement &docElem, const QString &connectorsLayerId, QDomElement &rectElem) {
1525 QDomElement e = docElem.firstChildElement();
1526 while(!e.isNull()) {
1527 QString id = e.attribute("id");
1528 if(id == connectorsLayerId) {
1529 e.appendChild(rectElem);
1530 return true;
1531 } else if(e.hasChildNodes()) {
1532 if(addRectToSvgAux(e, connectorsLayerId, rectElem)) {
1533 return true;
1534 }
1535 }
1536 e = e.nextSiblingElement();
1537 }
1538 return false;
1539 }
1540
1541
isSupposedToBeRemoved(const QString & id)1542 bool PartsEditorView::isSupposedToBeRemoved(const QString& id) {
1543 if (id.isEmpty()) return false;
1544
1545 // TODO: m_removedConnIds should be svg ids and not connectorSharedIDs (from fzp)
1546
1547 foreach(QString toBeRemoved, m_removedConnIds) {
1548 if(id.startsWith(toBeRemoved)) {
1549 QString temp = id;
1550 temp = temp.remove(0, toBeRemoved.length());
1551 if (temp.length() == 0) return true;
1552
1553 // assumes svg id is always prefixDsuffix where D is some string of decimal digits
1554 // and prefixD matches toBeRemoved
1555 if (!temp.at(0).isDigit()) {
1556 return true;
1557 }
1558 }
1559 }
1560 return false;
1561 }
1562
myItem()1563 PartsEditorConnectorsPaletteItem *PartsEditorView::myItem() {
1564 return dynamic_cast<PartsEditorConnectorsPaletteItem*>(m_item.data());
1565 }
1566
showTerminalPoints(bool show)1567 void PartsEditorView::showTerminalPoints(bool show) {
1568 m_showingTerminalPoints = show;
1569 foreach(QGraphicsItem *item, items()) {
1570 PartsEditorConnectorsConnectorItem *connItem
1571 = dynamic_cast<PartsEditorConnectorsConnectorItem*>(item);
1572 if(connItem) {
1573 connItem->setShowTerminalPoint(show);
1574 }
1575 }
1576 scene()->update();
1577
1578 /*if(!m_showingTerminalPoints) {
1579 m_terminalPointsTimer->stop();
1580 }*/
1581 }
1582
showingTerminalPoints()1583 bool PartsEditorView::showingTerminalPoints() {
1584 return m_showingTerminalPoints;
1585 }
1586
inFileDefinedConnectorChanged(PartsEditorConnectorsConnectorItem * connItem)1587 void PartsEditorView::inFileDefinedConnectorChanged(PartsEditorConnectorsConnectorItem *connItem) {
1588 QString connId = connItem->connectorSharedID();
1589 m_drawnConns[connId] = connItem;
1590 if(!m_removedConnIds.contains(connId)) {
1591 m_removedConnIds << connId;
1592 }
1593 }
1594
1595
addFixedToBottomRight(QWidget * widget)1596 void PartsEditorView::addFixedToBottomRight(QWidget *widget) {
1597 m_fixedWidgets << widget;
1598 QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget();
1599 proxy->setWidget(widget);
1600
1601 addFixedToBottomRightItem(proxy);
1602 }
1603
imageLoaded()1604 bool PartsEditorView::imageLoaded() {
1605 return m_item != NULL;
1606 }
1607
drawBackground(QPainter * painter,const QRectF & rect)1608 void PartsEditorView::drawBackground(QPainter *painter, const QRectF &rect) {
1609 SketchWidget::drawBackground(painter,rect);
1610
1611 // 10mm spacing grid
1612 /*const int gridSize = 10*width()/widthMM();
1613
1614 QRectF itemRect = m_item->mapToScene(m_item->boundingRect()).boundingRect();
1615 painter->drawRect(itemRect);
1616 double itemTop = itemRect.top();
1617 double itemLeft = itemRect.left();
1618
1619 QVarLengthArray<QLineF, 100> lines;
1620
1621 for (double x = itemLeft; x < rect.right(); x += gridSize) {
1622 lines.append(QLineF(x, rect.top(), x, rect.bottom()));
1623 }
1624 for (double x = itemLeft-gridSize; x > rect.left(); x -= gridSize) {
1625 lines.append(QLineF(x, rect.top(), x, rect.bottom()));
1626 }
1627
1628 for (double y = itemTop; y < rect.bottom(); y += gridSize) {
1629 lines.append(QLineF(rect.left(), y, rect.right(), y));
1630 }
1631 for (double y = itemTop-gridSize; y > rect.top(); y -= gridSize) {
1632 lines.append(QLineF(rect.left(), y, rect.right(), y));
1633 }
1634
1635 painter->drawLines(lines.data(), lines.size());*/
1636 }
1637
recoverTerminalPointsState()1638 void PartsEditorView::recoverTerminalPointsState() {
1639 showTerminalPoints(m_showingTerminalPointsBackup);
1640 m_terminalPointsTimer->stop();
1641 }
1642
connsPosOrSizeChanged()1643 bool PartsEditorView::connsPosOrSizeChanged() {
1644 foreach(QGraphicsItem *item, items()) {
1645 PartsEditorConnectorsConnectorItem *citem =
1646 dynamic_cast<PartsEditorConnectorsConnectorItem*>(item);
1647 if(citem) {
1648 TerminalPointItem *tp = citem->terminalPointItem();
1649 if((tp && tp->hasBeenMoved()) || citem->hasBeenMoved() || citem->hasBeenResized()) {
1650 return true;
1651 }
1652 }
1653 }
1654 return false;
1655 }
1656
setViewItem(ItemBase * item)1657 void PartsEditorView::setViewItem(ItemBase * item) {
1658 m_viewItem = item;
1659 }
1660
1661
checkConnectorLayers(ViewLayer::ViewIdentifier viewIdentifier,const QString & connId,Connector * existingConnector,Connector * newConnector)1662 void PartsEditorView::checkConnectorLayers(ViewLayer::ViewIdentifier viewIdentifier, const QString & connId, Connector* existingConnector, Connector * newConnector)
1663 {
1664 if (m_viewIdentifier != viewIdentifier) return;
1665
1666 Q_UNUSED(connId);
1667 QList<SvgIdLayer *> newpins = newConnector->connectorShared()->pins().values(viewIdentifier);
1668 QList<SvgIdLayer *> oldpins = existingConnector->connectorShared()->pins().values(viewIdentifier);
1669
1670 LayerList layerList;
1671
1672 QList<SvgIdLayer *> changes;
1673 foreach (SvgIdLayer * newSvgIdLayer, newpins) {
1674 bool gotOne = false;
1675 layerList << newSvgIdLayer->m_svgViewLayerID;
1676
1677 foreach (SvgIdLayer * oldSvgIdLayer, oldpins) {
1678 if (newSvgIdLayer->m_svgViewLayerID == oldSvgIdLayer->m_svgViewLayerID) {
1679 gotOne = true;
1680 break;
1681 }
1682 }
1683 if (!gotOne) {
1684 changes << newSvgIdLayer;
1685 }
1686 }
1687
1688
1689 foreach (SvgIdLayer * svgIdLayer, changes) {
1690 SvgIdLayer * cpy = svgIdLayer->copyLayer();
1691 existingConnector->connectorShared()->insertPin(viewIdentifier, cpy);
1692 }
1693
1694 changes.clear();
1695 foreach (SvgIdLayer * oldSvgIdLayer, oldpins) {
1696 bool gotOne = false;
1697 foreach (SvgIdLayer * newSvgIdLayer, newpins) {
1698 if (newSvgIdLayer->m_svgViewLayerID == oldSvgIdLayer->m_svgViewLayerID) {
1699 gotOne = true;
1700 break;
1701 }
1702 }
1703 if (!gotOne) {
1704 changes << oldSvgIdLayer;
1705 }
1706 }
1707
1708 foreach (SvgIdLayer * svgIdLayer, changes) {
1709 existingConnector->connectorShared()->removePin(viewIdentifier, svgIdLayer);
1710 }
1711 }
1712
updatePinsInfo(QList<QPointer<ConnectorShared>> connsShared)1713 void PartsEditorView::updatePinsInfo(QList< QPointer<ConnectorShared> > connsShared) {
1714 if(!m_svgLoaded) return; // if the user has not changed the svg file, there's nothing to update
1715
1716 ViewLayer::ViewLayerID layerID = connectorsLayerId();
1717 QList<ConnectorShared *> notFound;
1718
1719 foreach(ConnectorShared* cs, connsShared) {
1720 QString connId = cs->id();
1721 SvgIdLayer* pinInfo = cs->fullPinInfo(m_viewIdentifier, layerID);
1722 if (pinInfo == NULL) {
1723 notFound << cs;
1724 }
1725 else if(!m_svgIds[connId].connectorId.isEmpty()) {
1726 pinInfo->m_svgId = m_svgIds[connId].connectorId;
1727 // terminal points are already updated (by the function updateTerminalPoints)
1728 // pinInfo->m_terminalId = m_svgIds[connId].terminalId;
1729 }
1730 }
1731
1732 if (notFound.length() == 0) return;
1733
1734 // not sure this is the right place to handle the change of connector layers...
1735
1736 LayerList alts = ViewLayer::findAlternativeLayers(layerID);
1737 if (alts.length() == 0) return;
1738
1739 foreach (ViewLayer::ViewLayerID vlid, alts) {
1740 QList<ConnectorShared*> found;
1741 foreach(ConnectorShared* cs, notFound) {
1742 QString connId = cs->id();
1743 SvgIdLayer* pinInfo = cs->fullPinInfo(m_viewIdentifier, vlid);
1744 if (pinInfo != NULL && !m_svgIds[connId].connectorId.isEmpty()) {
1745 pinInfo->m_svgId = m_svgIds[connId].connectorId;
1746 pinInfo->m_svgViewLayerID = layerID;
1747 found << cs;
1748 }
1749 }
1750
1751 foreach (ConnectorShared * cs, found) {
1752 notFound.removeOne(cs);
1753 }
1754 }
1755 }
1756
saveSvg(const QString & svg,const QString & newFilePath)1757 QString PartsEditorView::saveSvg(const QString & svg, const QString & newFilePath) {
1758 if (!TextUtils::writeUtf8(newFilePath, svg)) {
1759 throw tr("unable to open temp file %1").arg(newFilePath);
1760 }
1761 return newFilePath;
1762 }
1763
clearFixedItems()1764 void PartsEditorView::clearFixedItems() {
1765 m_fixedToBottomLeftItems.clear();
1766 m_fixedToBottomRightItems.clear();
1767 m_fixedToCenterItems.clear();
1768 m_fixedToTopLeftItems.clear();
1769 m_fixedToTopRightItems.clear();
1770 }
1771
1772
ensureFixedItemsPositions()1773 void PartsEditorView::ensureFixedItemsPositions() {
1774
1775 //DebugDialog::debug("ensure fixed items positions");
1776
1777 ensureFixedToBottomLeftItems();
1778 ensureFixedToCenterItems();
1779 ensureFixedToTopLeftItems();
1780 ensureFixedToTopRightItems();
1781 ensureFixedToBottomRightItems();
1782
1783 scene()->update(sceneRect());
1784 }
1785
addFixedToTopLeftItem(QGraphicsItem * item)1786 void PartsEditorView::addFixedToTopLeftItem(QGraphicsItem *item) {
1787 item->setFlag(QGraphicsItem::ItemIgnoresTransformations);
1788 if(!scene()->items().contains(item)) {
1789 scene()->addItem(item);
1790 }
1791 m_fixedToTopLeftItems << item;
1792 ensureFixedToTopLeft(item);
1793 }
1794
addFixedToTopRightItem(QGraphicsItem * item)1795 void PartsEditorView::addFixedToTopRightItem(QGraphicsItem *item) {
1796 item->setFlag(QGraphicsItem::ItemIgnoresTransformations);
1797 if(!scene()->items().contains(item)) {
1798 scene()->addItem(item);
1799 }
1800 m_fixedToTopRightItems << item;
1801 ensureFixedToTopRight(item);
1802 }
1803
addFixedToBottomLeftItem(QGraphicsItem * item)1804 void PartsEditorView::addFixedToBottomLeftItem(QGraphicsItem *item) {
1805 item->setFlag(QGraphicsItem::ItemIgnoresTransformations);
1806 if(!scene()->items().contains(item)) {
1807 scene()->addItem(item);
1808 }
1809 m_fixedToBottomLeftItems << item;
1810 ensureFixedToBottomLeft(item);
1811 }
1812
addFixedToBottomRightItem(QGraphicsItem * item)1813 void PartsEditorView::addFixedToBottomRightItem(QGraphicsItem *item) {
1814 item->setFlag(QGraphicsItem::ItemIgnoresTransformations);
1815 if(!scene()->items().contains(item)) {
1816 scene()->addItem(item);
1817 }
1818 m_fixedToBottomRightItems << item;
1819 ensureFixedToBottomRight(item);
1820 }
1821
addFixedToCenterItem(QGraphicsItem * item)1822 void PartsEditorView::addFixedToCenterItem(QGraphicsItem *item) {
1823 item->setFlag(QGraphicsItem::ItemIgnoresTransformations);
1824 if(!scene()->items().contains(item)) {
1825 scene()->addItem(item);
1826 }
1827 m_fixedToCenterItems << item;
1828 ensureFixedToCenter(item);
1829 }
1830
ensureFixedToTopLeftItems()1831 void PartsEditorView::ensureFixedToTopLeftItems() {
1832 if(isVisible()) {
1833 QList<QGraphicsItem*> toRemove;
1834
1835 foreach(QGraphicsItem* item, m_fixedToTopLeftItems) {
1836 if(scene()->items().contains(item)) {
1837 ensureFixedToTopLeft(item);
1838 } else {
1839 toRemove << item;
1840 }
1841 }
1842
1843 foreach(QGraphicsItem* item, toRemove) {
1844 m_fixedToTopLeftItems.removeAll(item);
1845 }
1846 }
1847 }
1848
ensureFixedToTopLeft(QGraphicsItem * item)1849 void PartsEditorView::ensureFixedToTopLeft(QGraphicsItem* item) {
1850 item->setPos(mapToScene(0,0));
1851 }
1852
ensureFixedToTopRightItems()1853 void PartsEditorView::ensureFixedToTopRightItems() {
1854 if(isVisible()) {
1855 QList<QGraphicsItem*> toRemove;
1856
1857 foreach(QGraphicsItem* item, m_fixedToTopRightItems) {
1858 if(scene()->items().contains(item)) {
1859 ensureFixedToTopRight(item);
1860 } else {
1861 toRemove << item;
1862 }
1863 }
1864
1865 foreach(QGraphicsItem* item, toRemove) {
1866 m_fixedToTopRightItems.removeAll(item);
1867 }
1868 }
1869 }
1870
ensureFixedToTopRight(QGraphicsItem * item)1871 void PartsEditorView::ensureFixedToTopRight(QGraphicsItem* item) {
1872 int x = (int) (width()-fixedItemWidth(item));
1873 int y = 0;
1874
1875 item->setPos(mapToScene(x,y));
1876 }
1877
ensureFixedToBottomLeftItems()1878 void PartsEditorView::ensureFixedToBottomLeftItems() {
1879 if(isVisible()) {
1880 QList<QGraphicsItem*> toRemove;
1881
1882 foreach(QGraphicsItem* item, m_fixedToBottomLeftItems) {
1883 if(scene()->items().contains(item)) {
1884 ensureFixedToBottomLeft(item);
1885 } else {
1886 toRemove << item;
1887 }
1888 }
1889
1890 foreach(QGraphicsItem* item, toRemove) {
1891 m_fixedToBottomLeftItems.removeAll(item);
1892 }
1893 }
1894 }
1895
ensureFixedToBottomLeft(QGraphicsItem * item)1896 void PartsEditorView::ensureFixedToBottomLeft(QGraphicsItem* item) {
1897 int x = 0;
1898 int y = (int) (height()-fixedItemHeight(item));
1899
1900 item->setPos(mapToScene(x,y));
1901 }
1902
ensureFixedToBottomRightItems()1903 void PartsEditorView::ensureFixedToBottomRightItems() {
1904 if(isVisible()) {
1905 QList<QGraphicsItem*> toRemove;
1906
1907 foreach(QGraphicsItem* item, m_fixedToBottomRightItems) {
1908 if(scene()->items().contains(item)) {
1909 ensureFixedToBottomRight(item);
1910 } else {
1911 toRemove << item;
1912 }
1913 }
1914
1915 foreach(QGraphicsItem* item, toRemove) {
1916 m_fixedToBottomRightItems.removeAll(item);
1917 }
1918 }
1919 }
1920
ensureFixedToBottomRight(QGraphicsItem * item)1921 void PartsEditorView::ensureFixedToBottomRight(QGraphicsItem* item) {
1922 int x = (int) (width()-fixedItemWidth(item));
1923 int y = (int) (height()-fixedItemHeight(item));
1924
1925 item->setPos(mapToScene(x,y));
1926 }
1927
ensureFixedToCenterItems()1928 void PartsEditorView::ensureFixedToCenterItems() {
1929 if(isVisible()) {
1930 QList<QGraphicsItem*> toRemove;
1931
1932 foreach(QGraphicsItem* item, m_fixedToCenterItems) {
1933 if(scene()->items().contains(item)) {
1934 ensureFixedToCenter(item);
1935 } else {
1936 toRemove << item;
1937 }
1938 }
1939
1940 foreach(QGraphicsItem* item, toRemove) {
1941 m_fixedToCenterItems.removeAll(item);
1942 }
1943 }
1944 }
1945
ensureFixedToCenter(QGraphicsItem * item)1946 void PartsEditorView::ensureFixedToCenter(QGraphicsItem* item) {
1947 double x = (width()-fixedItemWidth(item))/2;
1948 double y = (height()-fixedItemHeight(item))/2;
1949
1950 QPointF pos = mapToScene(x,y);
1951
1952 if(pos.x() < scene()->width() && pos.y() < scene()->height()) {
1953 item->setPos(pos);
1954 }
1955 }
1956
removeIfFixedPos(QGraphicsItem * item)1957 void PartsEditorView::removeIfFixedPos(QGraphicsItem *item) {
1958 m_fixedToBottomLeftItems.removeAll(item);
1959 m_fixedToBottomRightItems.removeAll(item);
1960 m_fixedToCenterItems.removeAll(item);
1961 m_fixedToTopLeftItems.removeAll(item);
1962 m_fixedToTopRightItems.removeAll(item);
1963 }
1964
1965
fixedItemWidth(QGraphicsItem * item)1966 double PartsEditorView::fixedItemWidth(QGraphicsItem* item) {
1967 QGraphicsProxyWidget* gWidget = dynamic_cast<QGraphicsProxyWidget*>(item);
1968 if(gWidget) {
1969 return gWidget->widget()->width();
1970 } else {
1971 return item->boundingRect().width();
1972 }
1973 }
1974
fixedItemHeight(QGraphicsItem * item)1975 double PartsEditorView::fixedItemHeight(QGraphicsItem* item) {
1976 QGraphicsProxyWidget* gWidget = dynamic_cast<QGraphicsProxyWidget*>(item);
1977 if(gWidget) {
1978 return gWidget->widget()->height();
1979 } else {
1980 return item->boundingRect().height();
1981 }
1982 }
1983
deleteItem(ItemBase * itemBase,bool deleteModelPart,bool doEmit,bool later)1984 void PartsEditorView::deleteItem(ItemBase * itemBase, bool deleteModelPart, bool doEmit, bool later)
1985 {
1986 removeIfFixedPos(itemBase);
1987 SketchWidget::deleteItem(itemBase, deleteModelPart, doEmit, later);
1988 }
1989
setPaletteModel(PaletteModel * paletteModel)1990 void PartsEditorView::setPaletteModel(PaletteModel * paletteModel)
1991 {
1992 m_paletteModel = paletteModel;
1993 }
1994