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.old
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: 6963 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-14 10:13:52 +0200 (So, 14. Apr 2013) $
24
25 ********************************************************************/
26
27 #include "modelbase.h"
28 #include "../debugdialog.h"
29 #include "../items/partfactory.h"
30 #include "../items/moduleidnames.h"
31 #include "../utils/textutils.h"
32 #include "../utils/folderutils.h"
33 #include "../utils/fmessagebox.h"
34 #include "../version/version.h"
35 #include "../viewgeometry.h"
36
37 #include <QMessageBox>
38
39 QList<QString> ModelBase::CoreList;
40
41 /////////////////////////////////////////////////
42
ModelBase(bool makeRoot)43 ModelBase::ModelBase( bool makeRoot )
44 {
45 m_useOldSchematics = false;
46 m_reportMissingModules = true;
47 m_referenceModel = NULL;
48 m_root = NULL;
49 if (makeRoot) {
50 m_root = new ModelPart();
51 m_root->setModelPartShared(new ModelPartSharedRoot());
52 }
53 }
54
~ModelBase()55 ModelBase::~ModelBase() {
56 if (m_root) {
57 ModelPartShared * modelPartShared = m_root->modelPartShared();
58 if (modelPartShared) {
59 m_root->setModelPartShared(NULL);
60 delete modelPartShared;
61 }
62 delete m_root;
63 }
64 }
65
root()66 ModelPart * ModelBase::root() {
67 return m_root;
68 }
69
retrieveModelPart(const QString &)70 ModelPart * ModelBase::retrieveModelPart(const QString & /* moduleID */) {
71 return NULL;
72 }
73
74 // loads a model from an fz file--assumes a reference model exists with all parts
loadFromFile(const QString & fileName,ModelBase * referenceModel,QList<ModelPart * > & modelParts,bool checkViews)75 bool ModelBase::loadFromFile(const QString & fileName, ModelBase * referenceModel, QList<ModelPart *> & modelParts, bool checkViews) {
76 m_referenceModel = referenceModel;
77
78 QFile file(fileName);
79 if (!file.open(QFile::ReadOnly | QFile::Text)) {
80 FMessageBox::warning(NULL, QObject::tr("Fritzing"),
81 QObject::tr("Cannot read file %1:\n%2.")
82 .arg(fileName)
83 .arg(file.errorString()));
84 return false;
85 }
86
87 QString errorStr;
88 int errorLine;
89 int errorColumn;
90 QDomDocument domDocument;
91
92 if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
93 FMessageBox::information(NULL, QObject::tr("Fritzing"),
94 QObject::tr("Parse error (1) at line %1, column %2:\n%3\n%4")
95 .arg(errorLine)
96 .arg(errorColumn)
97 .arg(errorStr)
98 .arg(fileName));
99 return false;
100 }
101
102 QDomElement root = domDocument.documentElement();
103 if (root.isNull()) {
104 FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("The file %1 is not a Fritzing file (2).").arg(fileName));
105 return false;
106 }
107
108 emit loadedRoot(fileName, this, root);
109
110 if (root.tagName() != "module") {
111 FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("The file %1 is not a Fritzing file (4).").arg(fileName));
112 return false;
113 }
114
115 bool checkForOldSchematics = true;
116 bool checkForRats = true;
117 bool checkForTraces = true;
118 bool checkForMysteryParts = true;
119 bool checkForObsoleteSMDOrientation = true;
120 m_fritzingVersion = root.attribute("fritzingVersion");
121 if (m_fritzingVersion.length() > 0) {
122 // with version 0.4.3 ratsnests in fz files are obsolete
123 VersionThing versionThingRats;
124 versionThingRats.majorVersion = 0;
125 versionThingRats.minorVersion = 4;
126 versionThingRats.minorSubVersion = 2;
127 versionThingRats.releaseModifier = "";
128 VersionThing versionThingFz;
129 Version::toVersionThing(m_fritzingVersion,versionThingFz);
130 checkForRats = !Version::greaterThan(versionThingRats, versionThingFz);
131 // with version 0.6.5 traces are copied to all views
132 versionThingRats.minorVersion = 6;
133 versionThingRats.minorSubVersion = 4;
134 checkForTraces = !Version::greaterThan(versionThingRats, versionThingFz);
135 // with version 0.7.6 mystery part spacing implementation changes
136 versionThingRats.minorVersion = 7;
137 versionThingRats.minorSubVersion = 5;
138 checkForMysteryParts = !Version::greaterThan(versionThingRats, versionThingFz);
139 // with version 0.8.0 flipSMD is horizontal
140 versionThingRats.minorVersion = 7;
141 versionThingRats.minorSubVersion = 13;
142 checkForObsoleteSMDOrientation = !Version::greaterThan(versionThingRats, versionThingFz);
143 // with version 0.8.6 we get a new schematic template
144 versionThingRats.minorVersion = 8;
145 versionThingRats.minorSubVersion = 5;
146 checkForOldSchematics = !Version::greaterThan(versionThingRats, versionThingFz);
147 }
148
149 ModelPartSharedRoot * modelPartSharedRoot = this->rootModelPartShared();
150
151 QDomElement title = root.firstChildElement("title");
152 if (!title.isNull()) {
153 if (modelPartSharedRoot) {
154 modelPartSharedRoot->setTitle(title.text());
155 }
156 }
157
158 // ensures changeBinIcon() is not available
159 // this may be a bug?
160 QString iconFilename = root.attribute("icon");
161 if (iconFilename.isEmpty()) {
162 iconFilename = title.text() + ".png";
163 }
164
165 if (!iconFilename.isEmpty()) {
166 if (modelPartSharedRoot) {
167 modelPartSharedRoot->setIcon(iconFilename);
168 }
169 }
170
171 QString searchTerm = root.attribute("search");
172 if (!searchTerm.isEmpty() && modelPartSharedRoot != NULL) {
173 modelPartSharedRoot->setSearchTerm(searchTerm);
174 }
175
176 QDomElement views = root.firstChildElement("views");
177 emit loadedViews(this, views);
178
179 QDomElement instances = root.firstChildElement("instances");
180 if (instances.isNull()) {
181 FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("The file %1 is not a Fritzing file (3).").arg(fileName));
182 return false;
183 }
184
185 // delete any aready-existing model parts
186 for (int i = m_root->children().count() - 1; i >= 0; i--) {
187 QObject* child = m_root->children()[i];
188 child->setParent(NULL);
189 delete child;
190 }
191
192 emit loadingInstances(this, instances);
193
194 if (checkForRats) {
195 QDomElement instance = instances.firstChildElement("instance");
196 while (!instance.isNull()) {
197 QDomElement nextInstance = instance.nextSiblingElement("instance");
198 if (isRatsnest(instance)) {
199 instances.removeChild(instance);
200 }
201
202 instance = nextInstance;
203 }
204 }
205
206 if (checkForTraces) {
207 QDomElement instance = instances.firstChildElement("instance");
208 while (!instance.isNull()) {
209 checkTraces(instance);
210 instance = instance.nextSiblingElement("instance");
211 }
212 }
213
214 if (checkForMysteryParts) {
215 QDomElement instance = instances.firstChildElement("instance");
216 while (!instance.isNull()) {
217 checkMystery(instance);
218 instance = instance.nextSiblingElement("instance");
219 }
220 }
221
222 if (checkForObsoleteSMDOrientation) {
223 QDomElement instance = instances.firstChildElement("instance");
224 while (!instance.isNull()) {
225 if (checkObsoleteOrientation(instance)) {
226 emit obsoleteSMDOrientationSignal();
227 break;
228 }
229 instance = instance.nextSiblingElement("instance");
230 }
231 }
232
233 m_useOldSchematics = false;
234 if (checkForOldSchematics) {
235 QDomElement instance = instances.firstChildElement("instance");
236 while (!instance.isNull()) {
237 if (checkOldSchematics(instance)) {
238 emit oldSchematicsSignal(fileName, m_useOldSchematics);
239 break;
240 }
241 instance = instance.nextSiblingElement("instance");
242 }
243 }
244
245 bool result = loadInstances(domDocument, instances, modelParts, checkViews);
246 emit loadedInstances(this, instances);
247 return result;
248 }
249
fixObsoleteModuleID(QDomDocument & domDocument,QDomElement & instance,QString & moduleIDRef)250 ModelPart * ModelBase::fixObsoleteModuleID(QDomDocument & domDocument, QDomElement & instance, QString & moduleIDRef) {
251 return PartFactory::fixObsoleteModuleID(domDocument, instance, moduleIDRef, m_referenceModel);
252 }
253
loadInstances(QDomDocument & domDocument,QDomElement & instances,QList<ModelPart * > & modelParts,bool checkViews)254 bool ModelBase::loadInstances(QDomDocument & domDocument, QDomElement & instances, QList<ModelPart *> & modelParts, bool checkViews)
255 {
256 QHash<QString, QString> missingModules;
257 QDomElement instance = instances.firstChildElement("instance");
258 ModelPart* modelPart = NULL;
259 while (!instance.isNull()) {
260 emit loadingInstance(this, instance);
261
262 if (checkViews) {
263 QDomElement views = instance.firstChildElement("views");
264 QDomElement view = views.firstChildElement();
265 if (views.isNull() || view.isNull()) {
266 // do not load a part with no views
267 //QString text;
268 //QTextStream stream(&text);
269 //instance.save(stream, 0);
270 //DebugDialog::debug(text);
271 instance = instance.nextSiblingElement("instance");
272 continue;
273 }
274 }
275
276 // for now assume all parts are in the palette
277 QString moduleIDRef = instance.attribute("moduleIdRef");
278
279 //DebugDialog::debug("loading " + moduleIDRef);
280 if (moduleIDRef.compare(ModuleIDNames::SpacerModuleIDName) == 0) {
281 ModelPart * mp = new ModelPart(ModelPart::Space);
282 mp->setInstanceText(instance.attribute("path"));
283 mp->setParent(m_root);
284 mp->modelPartShared()->setModuleID(ModuleIDNames::SpacerModuleIDName);
285 mp->modelPartShared()->setPath(instance.attribute("path"));
286 modelParts.append(mp);
287 instance = instance.nextSiblingElement("instance");
288 continue;
289 }
290
291 bool generated = false;
292 modelPart = m_referenceModel->retrieveModelPart(moduleIDRef);
293 if (modelPart == NULL) {
294 DebugDialog::debug(QString("module id %1 not found in database").arg(moduleIDRef));
295 modelPart = fixObsoleteModuleID(domDocument, instance, moduleIDRef);
296 if (modelPart == NULL) {
297 modelPart = genFZP(moduleIDRef, m_referenceModel);
298 if (modelPart != NULL) {
299 instance.setAttribute("moduleIdRef", modelPart->moduleID());
300 moduleIDRef = modelPart->moduleID();
301 generated = true;
302 }
303 if (modelPart == NULL) {
304 missingModules.insert(moduleIDRef, instance.attribute("path"));
305 instance = instance.nextSiblingElement("instance");
306 continue;
307 }
308 }
309 }
310
311 if (modelPart->isCore() && m_useOldSchematics) {
312 modelPart = createOldSchematicPart(modelPart, moduleIDRef);
313 }
314
315 modelPart->setInBin(true);
316 modelPart = addModelPart(m_root, modelPart);
317 modelPart->setInstanceDomElement(instance);
318 modelParts.append(modelPart);
319
320 // TODO Mariano: i think this is not the way
321 QString instanceTitle = instance.firstChildElement("title").text();
322 if(!instanceTitle.isNull() && !instanceTitle.isEmpty()) {
323 modelPart->setInstanceTitle(instanceTitle, false);
324 }
325
326 QDomElement localConnectors = instance.firstChildElement("localConnectors");
327 QDomElement localConnector = localConnectors.firstChildElement("localConnector");
328 while (!localConnector.isNull()) {
329 modelPart->setConnectorLocalName(localConnector.attribute("id"), localConnector.attribute("name"));
330 localConnector = localConnector.nextSiblingElement("localConnector");
331 }
332
333 QString instanceText = instance.firstChildElement("text").text();
334 if(!instanceText.isNull() && !instanceText.isEmpty()) {
335 modelPart->setInstanceText(instanceText);
336 }
337
338 bool ok;
339 long index = instance.attribute("modelIndex").toLong(&ok);
340 if (ok) {
341 // set the index so we can find the same model part later, as we continue loading
342 modelPart->setModelIndex(index);
343 }
344
345 // note: this QDomNamedNodeMap loop is obsolete, but leaving it here so that old sketches don't get broken (jc, 22 Oct 2009)
346 QDomNamedNodeMap map = instance.attributes();
347 for (int m = 0; m < map.count(); m++) {
348 QDomNode node = map.item(m);
349 QString nodeName = node.nodeName();
350
351 if (nodeName.isEmpty()) continue;
352 if (nodeName.compare("moduleIdRef") == 0) continue;
353 if (nodeName.compare("modelIndex") == 0) continue;
354 if (nodeName.compare("originalModelIndex") == 0) continue;
355 if (nodeName.compare("path") == 0) continue;
356
357 modelPart->setLocalProp(nodeName, node.nodeValue());
358 }
359
360 // "property" loop replaces previous QDomNamedNodeMap loop (jc, 22 Oct 2009)
361 QDomElement prop = instance.firstChildElement("property");
362 while(!prop.isNull()) {
363 QString name = prop.attribute("name");
364 if (!name.isEmpty()) {
365 QString value = prop.attribute("value");
366 if (!value.isEmpty()) {
367 modelPart->setLocalProp(name, value);
368 }
369 }
370
371 prop = prop.nextSiblingElement("property");
372 }
373
374 instance = instance.nextSiblingElement("instance");
375 }
376
377 if (m_reportMissingModules && missingModules.count() > 0) {
378 QString unableToFind = QString("<html><body><b>%1</b><br/><table style='border-spacing: 0px 12px;'>")
379 .arg(tr("Unable to find the following %n part(s):", "", missingModules.count()));
380 foreach (QString key, missingModules.keys()) {
381 unableToFind += QString("<tr><td>'%1'</td><td><b>%2</b></td><td>'%3'</td></tr>")
382 .arg(key).arg(tr("at")).arg(missingModules.value(key, ""));
383 }
384 unableToFind += "</table></body></html>";
385 FMessageBox::warning(NULL, QObject::tr("Fritzing"), unableToFind);
386 }
387
388
389 return true;
390 }
391
addModelPart(ModelPart * parent,ModelPart * copyChild)392 ModelPart * ModelBase::addModelPart(ModelPart * parent, ModelPart * copyChild) {
393
394 //if (copyChild->moduleID() == "df9d072afa2b594ac67b60b4153ff57b_29") {
395 // DebugDialog::debug("alive in here");
396 //}
397
398 ModelPart * modelPart = new ModelPart();
399 modelPart->copyNew(copyChild);
400 modelPart->setParent(parent);
401 modelPart->initConnectors();
402 modelPart->flipSMDAnd();
403 return modelPart;
404 }
405
addPart(QString newPartPath,bool addToReference)406 ModelPart * ModelBase::addPart(QString newPartPath, bool addToReference) {
407 Q_UNUSED(newPartPath);
408 Q_UNUSED(addToReference);
409 throw "ModelBase::addPart should not be invoked";
410 return NULL;
411 }
412
addPart(QString newPartPath,bool addToReference,bool updateIdAlreadyExists)413 ModelPart * ModelBase::addPart(QString newPartPath, bool addToReference, bool updateIdAlreadyExists)
414 {
415 Q_UNUSED(updateIdAlreadyExists);
416 Q_UNUSED(newPartPath);
417 Q_UNUSED(addToReference);
418 throw "ModelBase::addPart should not be invoked";
419 return NULL;
420 }
421
422 // TODO Mariano: this function should never get called. Make pure virtual
addPart(ModelPart * modelPart,bool update)423 bool ModelBase::addPart(ModelPart * modelPart, bool update) {
424 Q_UNUSED(modelPart);
425 Q_UNUSED(update);
426 throw "ModelBase::addPart should not be invoked";
427 return false;
428 }
429
430
save(const QString & fileName,bool asPart)431 void ModelBase::save(const QString & fileName, bool asPart) {
432 QFileInfo info(fileName);
433 QDir dir = info.absoluteDir();
434
435 QString temp = dir.absoluteFilePath("temp.xml");
436 QFile file1(temp);
437 if (!file1.open(QFile::WriteOnly | QFile::Text)) {
438 FMessageBox::warning(NULL, QObject::tr("Fritzing"),
439 QObject::tr("Cannot write file temp:\n%1\n%2\n%3.")
440 .arg(temp)
441 .arg(fileName)
442 .arg(file1.errorString())
443 );
444 return;
445 }
446
447 QXmlStreamWriter streamWriter(&file1);
448 save(fileName, streamWriter, asPart);
449 file1.close();
450 QFile original(fileName);
451 if(original.exists() && !original.remove()) {
452 file1.remove();
453 FMessageBox::warning(
454 NULL,
455 tr("File save failed!"),
456 tr("Couldn't overwrite file '%1'.\nReason: %2 (errcode %3)")
457 .arg(fileName)
458 .arg(original.errorString())
459 .arg(original.error())
460 );
461 return;
462 }
463 file1.rename(fileName);
464 }
465
save(const QString & fileName,QXmlStreamWriter & streamWriter,bool asPart)466 void ModelBase::save(const QString & fileName, QXmlStreamWriter & streamWriter, bool asPart) {
467 streamWriter.setAutoFormatting(true);
468 if(asPart) {
469 m_root->saveAsPart(streamWriter, true);
470 } else {
471 m_root->saveInstances(fileName, streamWriter, true);
472 }
473 }
474
paste(ModelBase * referenceModel,QByteArray & data,QList<ModelPart * > & modelParts,QHash<QString,QRectF> & boundingRects,bool preserveIndex)475 bool ModelBase::paste(ModelBase * referenceModel, QByteArray & data, QList<ModelPart *> & modelParts, QHash<QString, QRectF> & boundingRects, bool preserveIndex)
476 {
477 m_referenceModel = referenceModel;
478
479 QDomDocument domDocument;
480 QString errorStr;
481 int errorLine;
482 int errorColumn;
483 bool result = domDocument.setContent(data, &errorStr, &errorLine, &errorColumn);
484 if (!result) return false;
485
486 QDomElement module = domDocument.documentElement();
487 if (module.isNull()) {
488 return false;
489 }
490
491 QDomElement boundingRectsElement = module.firstChildElement("boundingRects");
492 if (!boundingRectsElement.isNull()) {
493 QDomElement boundingRect = boundingRectsElement.firstChildElement("boundingRect");
494 while (!boundingRect.isNull()) {
495 QString name = boundingRect.attribute("name");
496 QString rect = boundingRect.attribute("rect");
497 QRectF br;
498 if (!rect.isEmpty()) {
499 QStringList s = rect.split(" ");
500 if (s.count() == 4) {
501 QRectF r(s[0].toDouble(), s[1].toDouble(), s[2].toDouble(), s[3].toDouble());
502 br = r;
503 }
504 }
505 boundingRects.insert(name, br);
506 boundingRect = boundingRect.nextSiblingElement("boundingRect");
507 }
508 }
509
510 QDomElement instances = module.firstChildElement("instances");
511 if (instances.isNull()) {
512 return false;
513 }
514
515 if (!preserveIndex) {
516 // need to map modelIndexes from copied parts to new modelIndexes
517 QHash<long, long> oldToNew;
518 QDomElement instance = instances.firstChildElement("instance");
519 while (!instance.isNull()) {
520 long oldModelIndex = instance.attribute("modelIndex").toLong();
521 oldToNew.insert(oldModelIndex, ModelPart::nextIndex());
522 instance = instance.nextSiblingElement("instance");
523 }
524 renewModelIndexes(instances, "instance", oldToNew);
525 }
526
527 //QFile file("test.xml");
528 //file.open(QFile::WriteOnly);
529 //file.write(domDocument.toByteArray());
530 //file.close();
531
532 return loadInstances(domDocument, instances, modelParts, true);
533 }
534
renewModelIndexes(QDomElement & parentElement,const QString & childName,QHash<long,long> & oldToNew)535 void ModelBase::renewModelIndexes(QDomElement & parentElement, const QString & childName, QHash<long, long> & oldToNew)
536 {
537 QDomElement instance = parentElement.firstChildElement(childName);
538 while (!instance.isNull()) {
539 long oldModelIndex = instance.attribute("modelIndex").toLong();
540 instance.setAttribute("modelIndex", QString::number(oldToNew.value(oldModelIndex)));
541 QDomElement views = instance.firstChildElement("views");
542 if (!views.isNull()) {
543 QDomElement view = views.firstChildElement();
544 while (!view.isNull()) {
545 bool ok;
546 int superpart = view.attribute("superpart").toLong(&ok);
547 if (ok) {
548 view.setAttribute("superpart", QString::number(oldToNew.value(superpart)));
549 }
550 QDomElement connectors = view.firstChildElement("connectors");
551 if (!connectors.isNull()) {
552 QDomElement connector = connectors.firstChildElement("connector");
553 while (!connector.isNull()) {
554 QDomElement connects = connector.firstChildElement("connects");
555 if (!connects.isNull()) {
556 QDomElement connect = connects.firstChildElement("connect");
557 while (!connect.isNull()) {
558 bool ok;
559 oldModelIndex = connect.attribute("modelIndex").toLong(&ok);
560 if (ok) {
561 long newModelIndex = oldToNew.value(oldModelIndex, -1);
562 if (newModelIndex != -1) {
563 connect.setAttribute("modelIndex", QString::number(newModelIndex));
564 }
565 else {
566 //DebugDialog::debug(QString("keep old model index %1").arg(oldModelIndex));
567 }
568 }
569 connect = connect.nextSiblingElement("connect");
570 }
571 }
572 connector = connector.nextSiblingElement("connector");
573 }
574 }
575
576 view = view.nextSiblingElement();
577 }
578 }
579
580 instance = instance.nextSiblingElement(childName);
581 }
582 }
583
setReportMissingModules(bool b)584 void ModelBase::setReportMissingModules(bool b) {
585 m_reportMissingModules = b;
586 }
587
genFZP(const QString & moduleID,ModelBase * referenceModel)588 ModelPart * ModelBase::genFZP(const QString & moduleID, ModelBase * referenceModel) {
589 QString path = PartFactory::getFzpFilename(moduleID);
590 if (path.isEmpty()) return NULL;
591
592 ModelPart* mp = referenceModel->addPart(path, true, true);
593 if (mp) mp->setCore(true);
594 return mp;
595 }
596
rootModelPartShared()597 ModelPartSharedRoot * ModelBase::rootModelPartShared() {
598 if (m_root == NULL) return NULL;
599
600 return m_root->modelPartSharedRoot();
601 }
602
isRatsnest(QDomElement & instance)603 bool ModelBase::isRatsnest(QDomElement & instance) {
604 QString moduleIDRef = instance.attribute("moduleIdRef");
605 if (moduleIDRef.compare(ModuleIDNames::WireModuleIDName) != 0) return false;
606
607 QDomElement views = instance.firstChildElement("views");
608 if (views.isNull()) return false;
609
610 QDomElement view = views.firstChildElement();
611 while (!view.isNull()) {
612 QDomElement geometry = view.firstChildElement("geometry");
613 if (!geometry.isNull()) {
614 int flags = geometry.attribute("wireFlags").toInt();
615 if (flags & ViewGeometry::RatsnestFlag) {
616 return true;
617 }
618 if (flags & ViewGeometry::ObsoleteJumperFlag) {
619 return true;
620 }
621 }
622
623 view = view.nextSiblingElement();
624 }
625
626 return false;
627 }
628
629
checkOldSchematics(QDomElement & instance)630 bool ModelBase::checkOldSchematics(QDomElement & instance)
631 {
632 if (instance.attribute("moduleIdRef").compare(ModuleIDNames::WireModuleIDName) != 0) {
633 return false;
634 }
635
636 QDomElement views = instance.firstChildElement("views");
637 QDomElement schematicView = views.firstChildElement("schematicView");
638 QDomElement geometry = schematicView.firstChildElement("geometry");
639 if (geometry.isNull()) return false;
640
641 int flags = geometry.attribute("wireFlags", "0").toInt();
642 return (flags & ViewGeometry::SchematicTraceFlag) != 0;
643 }
644
645
checkObsoleteOrientation(QDomElement & instance)646 bool ModelBase::checkObsoleteOrientation(QDomElement & instance)
647 {
648 QString flippedSMD = instance.attribute("flippedSMD", "");
649 if (flippedSMD != "true") return false;
650
651 QDomElement views = instance.firstChildElement("views");
652 QDomElement pcbView = views.firstChildElement("pcbView");
653 return (pcbView.attribute("layer", "") == "copper0");
654 }
655
checkTraces(QDomElement & instance)656 void ModelBase::checkTraces(QDomElement & instance) {
657 QString moduleIDRef = instance.attribute("moduleIdRef");
658 if (moduleIDRef.compare(ModuleIDNames::WireModuleIDName) != 0) return;
659
660 QDomElement views = instance.firstChildElement("views");
661 if (views.isNull()) return;
662
663 QDomElement bbView = views.firstChildElement("breadboardView");
664 QDomElement schView = views.firstChildElement("schematicView");
665 QDomElement pcbView = views.firstChildElement("pcbView");
666
667 if (!bbView.isNull() && !schView.isNull() && !pcbView.isNull()) {
668 // if it's a breadboard wire; just make sure flag is correct
669
670 QList<QDomElement> elements;
671 elements << bbView << schView << pcbView;
672 foreach (QDomElement element, elements) {
673 QDomElement geometry = element.firstChildElement("geometry");
674 if (!geometry.isNull()) {
675 int flags = geometry.attribute("wireFlags").toInt();
676 if (flags & ViewGeometry::PCBTraceFlag) return; // not a breadboard wire, bail out
677 if (flags & ViewGeometry::SchematicTraceFlag) return; // not a breadboard wire, bail out
678
679 if ((flags & ViewGeometry::NormalFlag) == 0) {
680 flags |= ViewGeometry::NormalFlag;
681 geometry.setAttribute("wireFlags", QString::number(flags));
682 }
683 }
684 }
685
686
687 return;
688 }
689
690 if (!bbView.isNull()) {
691 QDomElement geometry = bbView.firstChildElement("geometry");
692 if (!geometry.isNull()) {
693 int flags = geometry.attribute("wireFlags").toInt();
694 if ((flags & ViewGeometry::NormalFlag) == 0) {
695 flags |= ViewGeometry::NormalFlag;
696 geometry.setAttribute("wireFlags", QString::number(flags));
697 }
698 }
699 schView = bbView.cloneNode(true).toElement();
700 pcbView = bbView.cloneNode(true).toElement();
701 schView.setTagName("schematicView");
702 pcbView.setTagName("pcbView");
703 views.appendChild(pcbView);
704 views.appendChild(schView);
705 return;
706 }
707
708 if (!schView.isNull()) {
709 QDomElement geometry = schView.firstChildElement("geometry");
710 if (!geometry.isNull()) {
711 int flags = geometry.attribute("wireFlags").toInt();
712 if (flags & ViewGeometry::PCBTraceFlag) {
713 flags ^= ViewGeometry::PCBTraceFlag;
714 flags |= ViewGeometry::SchematicTraceFlag;
715 geometry.setAttribute("wireFlags", QString::number(flags));
716 }
717 }
718 pcbView = schView.cloneNode(true).toElement();
719 bbView = schView.cloneNode(true).toElement();
720 pcbView.setTagName("pcbView");
721 bbView.setTagName("breadboardView");
722 views.appendChild(bbView);
723 views.appendChild(pcbView);
724 return;
725 }
726
727 if (!pcbView.isNull()) {
728 schView = pcbView.cloneNode(true).toElement();
729 bbView = pcbView.cloneNode(true).toElement();
730 schView.setTagName("schematicView");
731 bbView.setTagName("breadboardView");
732 views.appendChild(bbView);
733 views.appendChild(schView);
734 return;
735 }
736
737 QDomElement iconView = views.firstChildElement("iconView");
738 if (!iconView.isNull()) return;
739
740 QString string;
741 QTextStream stream(&string);
742 instance.save(stream, 0);
743 stream.flush();
744 DebugDialog::debug(QString("no wire view elements in fz file %1").arg(string));
745 }
746
fritzingVersion()747 const QString & ModelBase::fritzingVersion() {
748 return m_fritzingVersion;
749 }
750
setReferenceModel(ModelBase * modelBase)751 void ModelBase::setReferenceModel(ModelBase * modelBase) {
752 m_referenceModel = modelBase;
753 }
754
checkMystery(QDomElement & instance)755 void ModelBase::checkMystery(QDomElement & instance)
756 {
757 QString moduleIDRef = instance.attribute("moduleIdRef");
758 bool mystery = false;
759 bool sip = false;
760 bool dip = false;
761 if (moduleIDRef.contains("mystery", Qt::CaseInsensitive)) mystery = true;
762 else if (moduleIDRef.contains("sip", Qt::CaseInsensitive)) sip = true;
763 else if (moduleIDRef.contains("dip", Qt::CaseInsensitive)) dip = true;
764 else return;
765
766 QString spacing;
767 int pins = TextUtils::getPinsAndSpacing(moduleIDRef, spacing);
768
769 QDomElement prop = instance.firstChildElement("property");
770 while (!prop.isNull()) {
771 if (prop.attribute("name", "").compare("spacing") == 0) {
772 QString trueSpacing = prop.attribute("value", "");
773 if (trueSpacing.isEmpty()) trueSpacing = "300mil";
774
775 if (moduleIDRef.contains(spacing)) {
776 moduleIDRef.replace(spacing, trueSpacing);
777 instance.setAttribute("moduleIdRef", moduleIDRef);
778 return;
779 }
780
781 // if we're here, it's a single sided mystery part.
782 moduleIDRef = QString("mystery_part_sip_%1_100mil").arg(pins);
783 instance.setAttribute("moduleIdRef", moduleIDRef);
784 return;
785 }
786 prop = prop.nextSiblingElement("property");
787 }
788 }
789
onCoreList(const QString & moduleID)790 bool ModelBase::onCoreList(const QString & moduleID) {
791 // CoreList contains db entries that are (presumably) overridden by an fzp in the parts folder
792 return CoreList.contains(moduleID);
793
794 }
795
createOldSchematicPart(ModelPart * modelPart,QString & moduleIDRef)796 ModelPart * ModelBase::createOldSchematicPart(ModelPart * modelPart, QString & moduleIDRef) {
797 QString schematicFilename = modelPart->imageFileName(ViewLayer::SchematicView, ViewLayer::Schematic);
798 if (!schematicFilename.startsWith("schematic")) {
799 schematicFilename = modelPart->imageFileName(ViewLayer::SchematicView, ViewLayer::SchematicFrame);
800 if (!schematicFilename.startsWith("schematic")) {
801 return modelPart;
802 }
803 }
804
805 DebugDialog::debug("schematic " + schematicFilename);
806 QString oldModuleIDRef = PartFactory::OldSchematicPrefix + moduleIDRef;
807 ModelPart * oldModelPart = m_referenceModel->retrieveModelPart(oldModuleIDRef); // cached after the first time it's created
808 if (oldModelPart) {
809 moduleIDRef = oldModuleIDRef;
810 return oldModelPart;
811 }
812
813 int ix = schematicFilename.indexOf("/");
814 schematicFilename.insert(ix + 1, PartFactory::OldSchematicPrefix);
815 QString oldSvgPath = FolderUtils::getApplicationSubFolderPath("parts") + "/svg/obsolete/"+ schematicFilename;
816 oldModelPart = createOldSchematicPartAux(modelPart, oldModuleIDRef, schematicFilename, oldSvgPath);
817 if (oldModelPart) {
818 moduleIDRef = oldModuleIDRef;
819 return oldModelPart;
820 }
821
822 oldSvgPath = ":resources/parts/svg/obsolete/"+ schematicFilename;
823 oldModelPart = createOldSchematicPartAux(modelPart, oldModuleIDRef, schematicFilename, oldSvgPath);
824 if (oldModelPart) {
825 moduleIDRef = oldModuleIDRef;
826 return oldModelPart;
827 }
828
829
830 // see whether it's a generated part
831 oldSvgPath = PartFactory::getSvgFilename(schematicFilename);
832 if (!oldSvgPath.isEmpty()) {
833 oldModelPart = createOldSchematicPartAux(modelPart, oldModuleIDRef, schematicFilename, oldSvgPath);
834 if (oldModelPart) {
835 moduleIDRef = oldModuleIDRef;
836 return oldModelPart;
837 }
838 }
839
840 return modelPart;
841 }
842
createOldSchematicPartAux(ModelPart * modelPart,const QString & oldModuleIDRef,const QString & oldSchematicFileName,const QString & oldSvgPath)843 ModelPart * ModelBase::createOldSchematicPartAux(ModelPart * modelPart, const QString & oldModuleIDRef, const QString & oldSchematicFileName, const QString & oldSvgPath)
844 {
845 if (!QFile::exists(oldSvgPath)) return NULL;
846
847 // create oldModelPart, set up the new image file name, add it to refmodel
848 QFile newFzp(modelPart->path());
849 QDomDocument oldDoc;
850 bool ok = oldDoc.setContent(&newFzp);
851 if (!ok) {
852 // this shouldn't happen
853 return NULL;
854 }
855
856 QDomElement root = oldDoc.documentElement();
857 root.setAttribute("moduleId", oldModuleIDRef);
858 QDomElement views = root.firstChildElement("views");
859 QDomElement schematicView = views.firstChildElement("schematicView");
860 QDomElement layers = schematicView.firstChildElement("layers");
861 if (layers.isNull()) {
862 // this shouldn't happen
863 return NULL;
864 }
865
866 layers.setAttribute("image", oldSchematicFileName);
867
868 QString oldFzpPath = PartFactory::fzpPath() + oldModuleIDRef + ".fzp";
869 if (!TextUtils::writeUtf8(oldFzpPath, oldDoc.toString())) {
870 // this shouldn't happen
871 return NULL;
872 }
873
874 ModelPart * oldModelPart = m_referenceModel->addPart(oldFzpPath, true, true);
875 oldModelPart->setCore(modelPart->isCore());
876 oldModelPart->setContrib(modelPart->isContrib());
877 return oldModelPart;
878 }
879
880