40 EXTERN_CFGENTRY (Bool, UseLogoStuds) |
40 EXTERN_CFGENTRY (Bool, UseLogoStuds) |
41 |
41 |
42 static bool g_loadingMainFile = false; |
42 static bool g_loadingMainFile = false; |
43 static const int g_maxRecentFiles = 10; |
43 static const int g_maxRecentFiles = 10; |
44 static bool g_aborted = false; |
44 static bool g_aborted = false; |
45 static LDDocumentPtr g_logoedStud; |
45 static LDDocument* g_logoedStud; |
46 static LDDocumentPtr g_logoedStud2; |
46 static LDDocument* g_logoedStud2; |
47 static QList<LDDocumentWeakPtr> g_allDocuments; |
47 static QList<LDDocument*> g_allDocuments; |
48 static QList<LDDocumentPtr> g_explicitDocuments; |
48 static QList<LDDocument*> g_explicitDocuments; |
49 static LDDocumentPtr g_currentDocument; |
49 static LDDocument* g_currentDocument; |
50 static bool g_loadingLogoedStuds = false; |
50 static bool g_loadingLogoedStuds = false; |
51 |
51 |
52 const QStringList g_specialSubdirectories ({ "s", "48", "8" }); |
52 const QStringList g_specialSubdirectories ({ "s", "48", "8" }); |
53 |
53 |
54 // ============================================================================= |
54 // ============================================================================= |
124 } |
124 } |
125 } |
125 } |
126 |
126 |
127 // ============================================================================= |
127 // ============================================================================= |
128 // |
128 // |
129 LDDocument::LDDocument (LDDocumentPtr* selfptr) : |
129 LDDocument::LDDocument (LDDocument** selfptr) : |
130 m_isImplicit (true), |
130 m_isImplicit (true), |
131 m_flags (0), |
131 m_flags (0), |
132 m_verticesOutdated (true), |
132 m_verticesOutdated (true), |
133 m_needVertexMerge (true), |
133 m_needVertexMerge (true), |
134 m_gldata (new LDGLData) |
134 m_gldata (new LDGLData) |
135 { |
135 { |
136 *selfptr = LDDocumentPtr (this); |
136 *selfptr = LDDocument* (this); |
137 setSelf (*selfptr); |
137 setSelf (*selfptr); |
138 setSavePosition (-1); |
138 setSavePosition (-1); |
139 setTabIndex (-1); |
139 setTabIndex (-1); |
140 setHistory (new History); |
140 setHistory (new History); |
141 history()->setDocument (*selfptr); |
141 history()->setDocument (*selfptr); |
143 g_allDocuments << *selfptr; |
143 g_allDocuments << *selfptr; |
144 } |
144 } |
145 |
145 |
146 // ============================================================================= |
146 // ============================================================================= |
147 // |
147 // |
148 LDDocumentPtr LDDocument::createNew() |
148 LDDocument* LDDocument::createNew() |
149 { |
149 { |
150 LDDocumentPtr ptr; |
150 LDDocument* ptr; |
151 new LDDocument (&ptr); |
151 new LDDocument (&ptr); |
152 return ptr; |
152 return ptr; |
153 } |
153 } |
154 |
154 |
155 // ============================================================================= |
155 // ============================================================================= |
205 } |
205 } |
206 } |
206 } |
207 |
207 |
208 // ============================================================================= |
208 // ============================================================================= |
209 // |
209 // |
210 QList<LDDocumentPtr> const& LDDocument::explicitDocuments() |
210 QList<LDDocument*> const& LDDocument::explicitDocuments() |
211 { |
211 { |
212 return g_explicitDocuments; |
212 return g_explicitDocuments; |
213 } |
213 } |
214 |
214 |
215 // ============================================================================= |
215 // ============================================================================= |
216 // |
216 // |
217 LDDocumentPtr FindDocument (QString name) |
217 LDDocument* FindDocument (QString name) |
218 { |
218 { |
219 for (LDDocumentWeakPtr weakfile : g_allDocuments) |
219 for (LDDocument* file : g_allDocuments) |
220 { |
220 { |
221 if (weakfile == null) |
221 if (file == null) |
222 continue; |
222 continue; |
223 |
223 |
224 LDDocumentPtr file (weakfile); |
224 LDDocument* file (file); |
225 |
225 |
226 if (Eq (name, file->name(), file->defaultName())) |
226 if (Eq (name, file->name(), file->defaultName())) |
227 return file; |
227 return file; |
228 } |
228 } |
229 |
229 |
230 return LDDocumentPtr(); |
230 return nullptr; |
231 } |
231 } |
232 |
232 |
233 // ============================================================================= |
233 // ============================================================================= |
234 // |
234 // |
235 QString Dirname (QString path) |
235 QString Dirname (QString path) |
274 |
274 |
275 // Try find it relative to other currently open documents. We want a file |
275 // Try find it relative to other currently open documents. We want a file |
276 // in the immediate vicinity of a current model to override stock LDraw stuff. |
276 // in the immediate vicinity of a current model to override stock LDraw stuff. |
277 QString reltop = Basename (Dirname (relpath)); |
277 QString reltop = Basename (Dirname (relpath)); |
278 |
278 |
279 for (LDDocumentWeakPtr doc : g_allDocuments) |
279 for (LDDocument* doc : g_allDocuments) |
280 { |
280 { |
281 if (doc == null) |
281 if (doc == null) |
282 continue; |
282 continue; |
283 |
283 |
284 QString partpath = format ("%1/%2", Dirname (doc->fullPath()), relpath); |
284 QString partpath = format ("%1/%2", Dirname (doc->fullPath()), relpath); |
510 return objs; |
510 return objs; |
511 } |
511 } |
512 |
512 |
513 // ============================================================================= |
513 // ============================================================================= |
514 // |
514 // |
515 LDDocumentPtr OpenDocument (QString path, bool search, bool implicit, LDDocumentPtr fileToOverride) |
515 LDDocument* OpenDocument (QString path, bool search, bool implicit, LDDocument* fileToOverride) |
516 { |
516 { |
517 // Convert the file name to lowercase when searching because some parts contain subfile |
517 // Convert the file name to lowercase when searching because some parts contain subfile |
518 // subfile references with uppercase file names. I'll assume here that the library will always |
518 // subfile references with uppercase file names. I'll assume here that the library will always |
519 // use lowercase file names for the part files. |
519 // use lowercase file names for the part files. |
520 QFile* fp; |
520 QFile* fp; |
530 fullpath = path; |
530 fullpath = path; |
531 |
531 |
532 if (not fp->open (QIODevice::ReadOnly)) |
532 if (not fp->open (QIODevice::ReadOnly)) |
533 { |
533 { |
534 delete fp; |
534 delete fp; |
535 return LDDocumentPtr(); |
535 return nullptr; |
536 } |
536 } |
537 } |
537 } |
538 |
538 |
539 if (not fp) |
539 if (not fp) |
540 return LDDocumentPtr(); |
540 return nullptr; |
541 |
541 |
542 LDDocumentPtr load = (fileToOverride != null ? fileToOverride : LDDocument::createNew()); |
542 LDDocument* load = (fileToOverride != null ? fileToOverride : LDDocument::createNew()); |
543 load->setImplicit (implicit); |
543 load->setImplicit (implicit); |
544 load->setFullPath (fullpath); |
544 load->setFullPath (fullpath); |
545 load->setName (LDDocument::shortenName (load->fullPath())); |
545 load->setName (LDDocument::shortenName (load->fullPath())); |
546 |
546 |
547 // Loading the file shouldn't count as actual edits to the document. |
547 // Loading the file shouldn't count as actual edits to the document. |
631 |
631 |
632 // ============================================================================= |
632 // ============================================================================= |
633 // |
633 // |
634 void CloseAllDocuments() |
634 void CloseAllDocuments() |
635 { |
635 { |
636 for (LDDocumentPtr file : g_explicitDocuments) |
636 for (LDDocument* file : g_explicitDocuments) |
637 file->dismiss(); |
637 file->dismiss(); |
638 } |
638 } |
639 |
639 |
640 // ============================================================================= |
640 // ============================================================================= |
641 // |
641 // |
642 void newFile() |
642 void newFile() |
643 { |
643 { |
644 // Create a new anonymous file and set it to our current |
644 // Create a new anonymous file and set it to our current |
645 LDDocumentPtr f = LDDocument::createNew(); |
645 LDDocument* f = LDDocument::createNew(); |
646 f->setName (""); |
646 f->setName (""); |
647 f->setImplicit (false); |
647 f->setImplicit (false); |
648 LDDocument::setCurrent (f); |
648 LDDocument::setCurrent (f); |
649 LDDocument::closeInitialFile(); |
649 LDDocument::closeInitialFile(); |
650 g_win->R()->setDocument (f); |
650 g_win->R()->setDocument (f); |
683 // Open an LDraw file and set it as the main model |
683 // Open an LDraw file and set it as the main model |
684 // ============================================================================= |
684 // ============================================================================= |
685 void OpenMainModel (QString path) |
685 void OpenMainModel (QString path) |
686 { |
686 { |
687 // If there's already a file with the same name, this file must replace it. |
687 // If there's already a file with the same name, this file must replace it. |
688 LDDocumentPtr documentToReplace; |
688 LDDocument* documentToReplace; |
689 LDDocumentPtr file; |
689 LDDocument* file; |
690 QString shortName = LDDocument::shortenName (path); |
690 QString shortName = LDDocument::shortenName (path); |
691 |
691 |
692 for (LDDocumentWeakPtr doc : g_allDocuments) |
692 for (LDDocument* doc : g_allDocuments) |
693 { |
693 { |
694 if (doc != null and doc->name() == shortName) |
694 if (doc != null and doc->name() == shortName) |
695 { |
695 { |
696 documentToReplace = doc; |
696 documentToReplace = doc; |
697 break; |
697 break; |
786 // If the second object in the list holds the file name, update that now. |
786 // If the second object in the list holds the file name, update that now. |
787 LDObject* nameObject = getObject (1); |
787 LDObject* nameObject = getObject (1); |
788 |
788 |
789 if (nameObject != null and nameObject->type() == OBJ_Comment) |
789 if (nameObject != null and nameObject->type() == OBJ_Comment) |
790 { |
790 { |
791 LDCommentPtr nameComment = static_cast<LDComment*> (nameObject); |
791 LDComment* nameComment = static_cast<LDComment*> (nameObject); |
792 |
792 |
793 if (nameComment->text().left (6) == "Name: ") |
793 if (nameComment->text().left (6) == "Name: ") |
794 { |
794 { |
795 QString newname = shortenName (path); |
795 QString newname = shortenName (path); |
796 nameComment->setText (format ("Name: %1", newname)); |
796 nameComment->setText (format ("Name: %1", newname)); |
962 { |
962 { |
963 // Vertex (0 !LDFORGE VERTEX) |
963 // Vertex (0 !LDFORGE VERTEX) |
964 CheckTokenCount (tokens, 7); |
964 CheckTokenCount (tokens, 7); |
965 CheckTokenNumbers (tokens, 3, 6); |
965 CheckTokenNumbers (tokens, 3, 6); |
966 |
966 |
967 LDVertexPtr obj = LDSpawn<LDVertex>(); |
967 LDVertex* obj = LDSpawn<LDVertex>(); |
968 obj->setColor (LDColor::fromIndex (StringToNumber (tokens[3]))); |
968 obj->setColor (LDColor::fromIndex (StringToNumber (tokens[3]))); |
969 obj->pos.apply ([&](Axis ax, double& value) |
969 obj->pos.apply ([&](Axis ax, double& value) |
970 { value = tokens[4 + ax].toDouble(); }); |
970 { value = tokens[4 + ax].toDouble(); }); |
971 return obj; |
971 return obj; |
972 } |
972 } |
973 elif (tokens[2] == "OVERLAY") |
973 elif (tokens[2] == "OVERLAY") |
974 { |
974 { |
975 CheckTokenCount (tokens, 9); |
975 CheckTokenCount (tokens, 9); |
976 CheckTokenNumbers (tokens, 5, 8); |
976 CheckTokenNumbers (tokens, 5, 8); |
977 |
977 |
978 LDOverlayPtr obj = LDSpawn<LDOverlay>(); |
978 LDOverlay* obj = LDSpawn<LDOverlay>(); |
979 obj->setFileName (tokens[3]); |
979 obj->setFileName (tokens[3]); |
980 obj->setCamera (tokens[4].toLong()); |
980 obj->setCamera (tokens[4].toLong()); |
981 obj->setX (tokens[5].toLong()); |
981 obj->setX (tokens[5].toLong()); |
982 obj->setY (tokens[6].toLong()); |
982 obj->setY (tokens[6].toLong()); |
983 obj->setWidth (tokens[7].toLong()); |
983 obj->setWidth (tokens[7].toLong()); |
1000 |
1000 |
1001 // Try open the file. Disable g_loadingMainFile temporarily since we're |
1001 // Try open the file. Disable g_loadingMainFile temporarily since we're |
1002 // not loading the main file now, but the subfile in question. |
1002 // not loading the main file now, but the subfile in question. |
1003 bool tmp = g_loadingMainFile; |
1003 bool tmp = g_loadingMainFile; |
1004 g_loadingMainFile = false; |
1004 g_loadingMainFile = false; |
1005 LDDocumentPtr load = GetDocument (tokens[14]); |
1005 LDDocument* load = GetDocument (tokens[14]); |
1006 g_loadingMainFile = tmp; |
1006 g_loadingMainFile = tmp; |
1007 |
1007 |
1008 // If we cannot open the file, mark it an error. Note we cannot use LDParseError |
1008 // If we cannot open the file, mark it an error. Note we cannot use LDParseError |
1009 // here because the error object needs the document reference. |
1009 // here because the error object needs the document reference. |
1010 if (not load) |
1010 if (not load) |
1011 { |
1011 { |
1012 LDErrorPtr obj = LDSpawn<LDError> (line, format ("Could not open %1", tokens[14])); |
1012 LDError* obj = LDSpawn<LDError> (line, format ("Could not open %1", tokens[14])); |
1013 obj->setFileReferenced (tokens[14]); |
1013 obj->setFileReferenced (tokens[14]); |
1014 return obj; |
1014 return obj; |
1015 } |
1015 } |
1016 |
1016 |
1017 LDSubfilePtr obj = LDSpawn<LDSubfile>(); |
1017 LDSubfile* obj = LDSpawn<LDSubfile>(); |
1018 obj->setColor (LDColor::fromIndex (StringToNumber (tokens[1]))); |
1018 obj->setColor (LDColor::fromIndex (StringToNumber (tokens[1]))); |
1019 obj->setPosition (ParseVertex (tokens, 2)); // 2 - 4 |
1019 obj->setPosition (ParseVertex (tokens, 2)); // 2 - 4 |
1020 |
1020 |
1021 Matrix transform; |
1021 Matrix transform; |
1022 |
1022 |
1032 { |
1032 { |
1033 CheckTokenCount (tokens, 8); |
1033 CheckTokenCount (tokens, 8); |
1034 CheckTokenNumbers (tokens, 1, 7); |
1034 CheckTokenNumbers (tokens, 1, 7); |
1035 |
1035 |
1036 // Line |
1036 // Line |
1037 LDLinePtr obj (LDSpawn<LDLine>()); |
1037 LDLine* obj (LDSpawn<LDLine>()); |
1038 obj->setColor (LDColor::fromIndex (StringToNumber (tokens[1]))); |
1038 obj->setColor (LDColor::fromIndex (StringToNumber (tokens[1]))); |
1039 |
1039 |
1040 for (int i = 0; i < 2; ++i) |
1040 for (int i = 0; i < 2; ++i) |
1041 obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 7 |
1041 obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 7 |
1042 |
1042 |
1047 { |
1047 { |
1048 CheckTokenCount (tokens, 11); |
1048 CheckTokenCount (tokens, 11); |
1049 CheckTokenNumbers (tokens, 1, 10); |
1049 CheckTokenNumbers (tokens, 1, 10); |
1050 |
1050 |
1051 // Triangle |
1051 // Triangle |
1052 LDTrianglePtr obj (LDSpawn<LDTriangle>()); |
1052 LDTriangle* obj (LDSpawn<LDTriangle>()); |
1053 obj->setColor (LDColor::fromIndex (StringToNumber (tokens[1]))); |
1053 obj->setColor (LDColor::fromIndex (StringToNumber (tokens[1]))); |
1054 |
1054 |
1055 for (int i = 0; i < 3; ++i) |
1055 for (int i = 0; i < 3; ++i) |
1056 obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 10 |
1056 obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 10 |
1057 |
1057 |
1091 } |
1091 } |
1092 } |
1092 } |
1093 |
1093 |
1094 // ============================================================================= |
1094 // ============================================================================= |
1095 // |
1095 // |
1096 LDDocumentPtr GetDocument (QString filename) |
1096 LDDocument* GetDocument (QString filename) |
1097 { |
1097 { |
1098 // Try find the file in the list of loaded files |
1098 // Try find the file in the list of loaded files |
1099 LDDocumentPtr doc = FindDocument (filename); |
1099 LDDocument* doc = FindDocument (filename); |
1100 |
1100 |
1101 // If it's not loaded, try open it |
1101 // If it's not loaded, try open it |
1102 if (not doc) |
1102 if (not doc) |
1103 doc = OpenDocument (filename, true, true); |
1103 doc = OpenDocument (filename, true, true); |
1104 |
1104 |
1114 // Go through all objects in the current file and reload the subfiles |
1114 // Go through all objects in the current file and reload the subfiles |
1115 for (LDObject* obj : objects()) |
1115 for (LDObject* obj : objects()) |
1116 { |
1116 { |
1117 if (obj->type() == OBJ_Subfile) |
1117 if (obj->type() == OBJ_Subfile) |
1118 { |
1118 { |
1119 LDSubfilePtr ref = static_cast<LDSubfile*> (obj); |
1119 LDSubfile* ref = static_cast<LDSubfile*> (obj); |
1120 LDDocumentPtr fileInfo = GetDocument (ref->fileInfo()->name()); |
1120 LDDocument* fileInfo = GetDocument (ref->fileInfo()->name()); |
1121 |
1121 |
1122 if (fileInfo != null) |
1122 if (fileInfo != null) |
1123 { |
1123 { |
1124 ref->setFileInfo (fileInfo); |
1124 ref->setFileInfo (fileInfo); |
1125 } |
1125 } |
1209 history()->add (new DelHistory (idx, obj)); |
1209 history()->add (new DelHistory (idx, obj)); |
1210 m_objectVertices.remove (obj); |
1210 m_objectVertices.remove (obj); |
1211 } |
1211 } |
1212 |
1212 |
1213 m_objects.removeAt (idx); |
1213 m_objects.removeAt (idx); |
1214 obj->setDocument (LDDocumentPtr()); |
1214 obj->setDocument (nullptr); |
1215 } |
1215 } |
1216 |
1216 |
1217 // ============================================================================= |
1217 // ============================================================================= |
1218 // |
1218 // |
1219 bool IsSafeToCloseAll() |
1219 bool IsSafeToCloseAll() |
1220 { |
1220 { |
1221 for (LDDocumentPtr f : LDDocument::explicitDocuments()) |
1221 for (LDDocument* f : LDDocument::explicitDocuments()) |
1222 { |
1222 { |
1223 if (not f->isSafeToClose()) |
1223 if (not f->isSafeToClose()) |
1224 return false; |
1224 return false; |
1225 } |
1225 } |
1226 |
1226 |
1241 *m_history << new EditHistory (idx, oldcode, newcode); |
1241 *m_history << new EditHistory (idx, oldcode, newcode); |
1242 } |
1242 } |
1243 |
1243 |
1244 m_objectVertices.remove (m_objects[idx]); |
1244 m_objectVertices.remove (m_objects[idx]); |
1245 m_objects[idx]->deselect(); |
1245 m_objects[idx]->deselect(); |
1246 m_objects[idx]->setDocument (LDDocumentPtr()); |
1246 m_objects[idx]->setDocument (nullptr); |
1247 obj->setDocument (this); |
1247 obj->setDocument (this); |
1248 addKnownVertices (obj); |
1248 addKnownVertices (obj); |
1249 g_win->R()->compileObject (obj); |
1249 g_win->R()->compileObject (obj); |
1250 m_objects[idx] = obj; |
1250 m_objects[idx] = obj; |
1251 needVertexMerge(); |
1251 needVertexMerge(); |
1394 return objs; |
1394 return objs; |
1395 } |
1395 } |
1396 |
1396 |
1397 // ============================================================================= |
1397 // ============================================================================= |
1398 // |
1398 // |
1399 LDDocumentPtr LDDocument::current() |
1399 LDDocument* LDDocument::current() |
1400 { |
1400 { |
1401 return g_currentDocument; |
1401 return g_currentDocument; |
1402 } |
1402 } |
1403 |
1403 |
1404 // ============================================================================= |
1404 // ============================================================================= |
1405 // Sets the given file as the current one on display. At some point in time this |
1405 // Sets the given file as the current one on display. At some point in time this |
1406 // was an operation completely unheard of. ;) |
1406 // was an operation completely unheard of. ;) |
1407 // |
1407 // |
1408 // TODO: f can be temporarily null. This probably should not be the case. |
1408 // TODO: f can be temporarily null. This probably should not be the case. |
1409 // ============================================================================= |
1409 // ============================================================================= |
1410 void LDDocument::setCurrent (LDDocumentPtr f) |
1410 void LDDocument::setCurrent (LDDocument* f) |
1411 { |
1411 { |
1412 // Implicit files were loaded for caching purposes and must never be set |
1412 // Implicit files were loaded for caching purposes and must never be set |
1413 // current. |
1413 // current. |
1414 if (f != null and f->isImplicit()) |
1414 if (f != null and f->isImplicit()) |
1415 return; |
1415 return; |
1444 if (g_explicitDocuments.size() == 2 and |
1444 if (g_explicitDocuments.size() == 2 and |
1445 g_explicitDocuments[0]->name().isEmpty() and |
1445 g_explicitDocuments[0]->name().isEmpty() and |
1446 not g_explicitDocuments[1]->name().isEmpty() and |
1446 not g_explicitDocuments[1]->name().isEmpty() and |
1447 not g_explicitDocuments[0]->hasUnsavedChanges()) |
1447 not g_explicitDocuments[0]->hasUnsavedChanges()) |
1448 { |
1448 { |
1449 LDDocumentPtr filetoclose = g_explicitDocuments.first(); |
1449 LDDocument* filetoclose = g_explicitDocuments.first(); |
1450 filetoclose->dismiss(); |
1450 filetoclose->dismiss(); |
1451 } |
1451 } |
1452 } |
1452 } |
1453 |
1453 |
1454 // ============================================================================= |
1454 // ============================================================================= |