src/ldDocument.cpp

changeset 978
4603d8fd063e
parent 971
c00f9665a9f8
child 979
880d3fe9ac7c
equal deleted inserted replaced
977:dc3ceb65cda7 978:4603d8fd063e
34 34
35 ConfigOption (QStringList RecentFiles) 35 ConfigOption (QStringList RecentFiles)
36 ConfigOption (bool TryDownloadMissingFiles = false) 36 ConfigOption (bool TryDownloadMissingFiles = false)
37 37
38 static bool g_loadingMainFile = false; 38 static bool g_loadingMainFile = false;
39 static const int g_maxRecentFiles = 10; 39 enum { MAX_RECENT_FILES = 10 };
40 static bool g_aborted = false; 40 static bool g_aborted = false;
41 static LDDocument* g_logoedStud; 41 static LDDocument* g_logoedStud;
42 static LDDocument* g_logoedStud2; 42 static LDDocument* g_logoedStud2;
43 static QList<LDDocument*> g_allDocuments;
44 static QList<LDDocument*> g_explicitDocuments;
45 static LDDocument* g_currentDocument;
46 static bool g_loadingLogoedStuds = false; 43 static bool g_loadingLogoedStuds = false;
47 44
48 const QStringList g_specialSubdirectories ({ "s", "48", "8" }); 45 const QStringList g_specialSubdirectories ({ "s", "48", "8" });
49 46
50 // ============================================================================= 47 // =============================================================================
51 // 48 //
52 LDDocument::LDDocument() : 49 LDDocument::LDDocument (QObject* parent) :
50 QObject (parent),
51 HierarchyElement (parent),
53 m_history (new History), 52 m_history (new History),
54 m_isImplicit (true), 53 m_isImplicit (true),
55 m_flags (0), 54 m_flags (0),
56 m_verticesOutdated (true), 55 m_verticesOutdated (true),
57 m_needVertexMerge (true), 56 m_needVertexMerge (true),
59 { 58 {
60 setSavePosition (-1); 59 setSavePosition (-1);
61 setTabIndex (-1); 60 setTabIndex (-1);
62 m_history->setDocument (this); 61 m_history->setDocument (this);
63 m_needsReCache = true; 62 m_needsReCache = true;
64 g_allDocuments << this;
65 }
66
67 // =============================================================================
68 //
69 LDDocument* LDDocument::createNew()
70 {
71 return new LDDocument();
72 } 63 }
73 64
74 // ============================================================================= 65 // =============================================================================
75 // 66 //
76 LDDocument::~LDDocument() 67 LDDocument::~LDDocument()
77 { 68 {
78 g_allDocuments.removeOne (this);
79 m_flags |= DOCF_IsBeingDestroyed; 69 m_flags |= DOCF_IsBeingDestroyed;
80 delete m_history; 70 delete m_history;
81 delete m_gldata; 71 delete m_gldata;
82 } 72 }
83 73
89 { 79 {
90 m_isImplicit = a; 80 m_isImplicit = a;
91 81
92 if (a == false) 82 if (a == false)
93 { 83 {
94 g_explicitDocuments << this;
95 print ("Opened %1", name()); 84 print ("Opened %1", name());
96 85
97 // Implicit files are not compiled by the GL renderer. Now that this 86 // Implicit files are not compiled by the GL renderer. Now that this
98 // part is no longer implicit, it needs to be compiled. 87 // part is no longer implicit, it needs to be compiled.
99 if (g_win != null) 88 m_window->R()->compiler()->compileDocument (this);
100 g_win->R()->compiler()->compileDocument (this);
101 } 89 }
102 else 90 else
103 { 91 {
104 g_explicitDocuments.removeOne (this);
105 print ("Closed %1", name()); 92 print ("Closed %1", name());
106 } 93 }
107 94
108 if (g_win != null) 95 m_window->updateDocumentList();
109 g_win->updateDocumentList(); 96
110 97 // If the current document just became implicit (i.e. user closed it), we need to get a new one to show.
111 // If the current document just became implicit (e.g. it was 'closed'), 98 if (currentDocument() == this and a)
112 // we need to get a new current document. 99 m_window->currentDocumentClosed();
113 if (current() == this and isImplicit()) 100 }
114 {
115 if (explicitDocuments().isEmpty())
116 newFile();
117 else
118 setCurrent (explicitDocuments().first());
119 }
120 }
121 }
122
123 // =============================================================================
124 //
125 QList<LDDocument*> const& LDDocument::explicitDocuments()
126 {
127 return g_explicitDocuments;
128 } 101 }
129 102
130 // ============================================================================= 103 // =============================================================================
131 // 104 //
132 LDDocument* FindDocument (QString name) 105 LDDocument* FindDocument (QString name)
133 { 106 {
134 for (LDDocument* file : g_allDocuments) 107 for (LDDocument* document : g_win->allDocuments())
135 { 108 {
136 if (file == null) 109 if (isOneOf (name, document->name(), document->defaultName()))
137 continue; 110 return document;
138
139 if (isOneOf (name, file->name(), file->defaultName()))
140 return file;
141 } 111 }
142 112
143 return nullptr; 113 return nullptr;
144 } 114 }
145 115
176 // 146 //
177 static QString FindDocumentPath (QString relpath, bool subdirs) 147 static QString FindDocumentPath (QString relpath, bool subdirs)
178 { 148 {
179 QString fullPath; 149 QString fullPath;
180 150
181 // LDraw models use Windows-style path separators. If we're not on Windows, 151 // LDraw models use backslashes as path separators. Replace those into forward slashes for Qt.
182 // replace the path separator now before opening any files. Qt expects
183 // forward-slashes as directory separators.
184 #ifndef WIN32
185 relpath.replace ("\\", "/"); 152 relpath.replace ("\\", "/");
186 #endif // WIN32 153
187 154 // Try find it relative to other currently open documents. We want a file in the immediate vicinity of a current
188 // Try find it relative to other currently open documents. We want a file 155 // part model to override stock LDraw stuff.
189 // in the immediate vicinity of a current model to override stock LDraw stuff.
190 QString reltop = Basename (Dirname (relpath)); 156 QString reltop = Basename (Dirname (relpath));
191 157
192 for (LDDocument* doc : g_allDocuments) 158 for (LDDocument* doc : g_win->allDocuments())
193 { 159 {
194 if (doc == null)
195 continue;
196
197 QString partpath = format ("%1/%2", Dirname (doc->fullPath()), relpath); 160 QString partpath = format ("%1/%2", Dirname (doc->fullPath()), relpath);
198 QFile f (partpath); 161 QFile f (partpath);
199 162
200 if (f.exists()) 163 if (f.exists())
201 { 164 {
227 if (QFile::exists (fullPath)) 190 if (QFile::exists (fullPath))
228 return fullPath; 191 return fullPath;
229 192
230 if (subdirs) 193 if (subdirs)
231 { 194 {
232 // Look in sub-directories: parts and p. Also look in net_downloadpath, since that's 195 // Look in sub-directories: parts and p. Also look in the download path, since that's where we download parts
233 // where we download parts from the PT to. 196 // from the PT to.
234 QStringList dirs = { g_win->configBag()->lDrawPath(), g_win->configBag()->downloadFilePath() }; 197 QStringList dirs = { g_win->configBag()->lDrawPath(), g_win->configBag()->downloadFilePath() };
235 for (const QString& topdir : dirs) 198 for (const QString& topdir : dirs)
236 { 199 {
237 for (const QString& subdir : QStringList ({ "parts", "p" })) 200 for (const QString& subdir : QStringList ({ "parts", "p" }))
238 { 201 {
451 } 414 }
452 415
453 if (not fp) 416 if (not fp)
454 return nullptr; 417 return nullptr;
455 418
456 LDDocument* load = (fileToOverride != null ? fileToOverride : LDDocument::createNew()); 419 LDDocument* load = (fileToOverride != null ? fileToOverride : g_win->newDocument (implicit));
457 load->setImplicit (implicit);
458 load->setFullPath (fullpath); 420 load->setFullPath (fullpath);
459 load->setName (LDDocument::shortenName (load->fullPath())); 421 load->setName (LDDocument::shortenName (load->fullPath()));
460 422
461 // Loading the file shouldn't count as actual edits to the document. 423 // Loading the file shouldn't count as actual edits to the document.
462 load->history()->setIgnoring (true); 424 load->history()->setIgnoring (true);
475 437
476 load->addObjects (objs); 438 load->addObjects (objs);
477 439
478 if (g_loadingMainFile) 440 if (g_loadingMainFile)
479 { 441 {
480 LDDocument::setCurrent (load); 442 g_win->changeDocument (load);
481 g_win->R()->setDocument (load); 443 g_win->R()->setDocument (load);
482 print (QObject::tr ("File %1 parsed successfully (%2 errors)."), path, numWarnings); 444 print (QObject::tr ("File %1 parsed successfully (%2 errors)."), path, numWarnings);
483 } 445 }
484 446
485 load->history()->setIgnoring (false); 447 load->history()->setIgnoring (false);
494 setlocale (LC_ALL, "C"); 456 setlocale (LC_ALL, "C");
495 457
496 // If we have unsaved changes, warn and give the option of saving. 458 // If we have unsaved changes, warn and give the option of saving.
497 if (hasUnsavedChanges()) 459 if (hasUnsavedChanges())
498 { 460 {
499 QString message = format (QObject::tr ("There are unsaved changes to %1. Should it be saved?"), 461 QString message = format (tr ("There are unsaved changes to %1. Should it be saved?"), getDisplayName());
500 getDisplayName()); 462
501 463 int button = msgbox::question (m_window, QObject::tr ("Unsaved Changes"), message,
502 int button = msgbox::question (g_win, QObject::tr ("Unsaved Changes"), message,
503 (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel); 464 (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel);
504 465
505 switch (button) 466 switch (button)
506 { 467 {
507 case msgbox::Yes: 468 case msgbox::Yes:
508 { 469 {
509 // If we don't have a file path yet, we have to ask the user for one. 470 // If we don't have a file path yet, we have to ask the user for one.
510 if (name().length() == 0) 471 if (name().length() == 0)
511 { 472 {
512 QString newpath = QFileDialog::getSaveFileName (g_win, QObject::tr ("Save As"), 473 QString newpath = QFileDialog::getSaveFileName (m_window, QObject::tr ("Save As"),
513 CurrentDocument()->name(), QObject::tr ("LDraw files (*.dat *.ldr)")); 474 name(), QObject::tr ("LDraw files (*.dat *.ldr)"));
514 475
515 if (newpath.length() == 0) 476 if (newpath.length() == 0)
516 return false; 477 return false;
517 478
518 setName (newpath); 479 setName (newpath);
521 if (not save()) 482 if (not save())
522 { 483 {
523 message = format (QObject::tr ("Failed to save %1 (%2)\nDo you still want to close?"), 484 message = format (QObject::tr ("Failed to save %1 (%2)\nDo you still want to close?"),
524 name(), strerror (errno)); 485 name(), strerror (errno));
525 486
526 if (msgbox::critical (g_win, QObject::tr ("Save Failure"), message, 487 if (msgbox::critical (m_window, QObject::tr ("Save Failure"), message,
527 (msgbox::Yes | msgbox::No), msgbox::No) == msgbox::No) 488 (msgbox::Yes | msgbox::No), msgbox::No) == msgbox::No)
528 { 489 {
529 return false; 490 return false;
530 } 491 }
531 } 492 }
545 506
546 // ============================================================================= 507 // =============================================================================
547 // 508 //
548 void CloseAllDocuments() 509 void CloseAllDocuments()
549 { 510 {
550 for (LDDocument* file : g_explicitDocuments) 511 for (LDDocument* file : g_win->allDocuments())
551 file->dismiss(); 512 file->dismiss();
552 }
553
554 // =============================================================================
555 //
556 void newFile()
557 {
558 // Create a new anonymous file and set it to our current
559 LDDocument* f = LDDocument::createNew();
560 f->setName ("");
561 f->setImplicit (false);
562 LDDocument::setCurrent (f);
563 LDDocument::closeInitialFile();
564 g_win->R()->setDocument (f);
565 g_win->doFullRefresh();
566 g_win->updateTitle();
567 g_win->updateActions();
568 } 513 }
569 514
570 // ============================================================================= 515 // =============================================================================
571 // 516 //
572 void AddRecentFile (QString path) 517 void AddRecentFile (QString path)
582 527
583 recentFiles.removeAt (idx); 528 recentFiles.removeAt (idx);
584 } 529 }
585 530
586 // If there's too many recent files, drop one out. 531 // If there's too many recent files, drop one out.
587 while (recentFiles.size() > (g_maxRecentFiles - 1)) 532 while (recentFiles.size() > (MAX_RECENT_FILES - 1))
588 recentFiles.removeAt (0); 533 recentFiles.removeAt (0);
589 534
590 // Add the file 535 // Add the file
591 recentFiles << path; 536 recentFiles << path;
592 g_win->configBag()->setRecentFiles (recentFiles); 537 g_win->configBag()->setRecentFiles (recentFiles);
602 // If there's already a file with the same name, this file must replace it. 547 // If there's already a file with the same name, this file must replace it.
603 LDDocument* documentToReplace = nullptr; 548 LDDocument* documentToReplace = nullptr;
604 LDDocument* file = nullptr; 549 LDDocument* file = nullptr;
605 QString shortName = LDDocument::shortenName (path); 550 QString shortName = LDDocument::shortenName (path);
606 551
607 for (LDDocument* doc : g_allDocuments) 552 for (LDDocument* doc : g_win->allDocuments())
608 { 553 {
609 if (doc != null and doc->name() == shortName) 554 if (doc->name() == shortName)
610 { 555 {
611 documentToReplace = doc; 556 documentToReplace = doc;
612 break; 557 break;
613 } 558 }
614 } 559 }
642 g_loadingMainFile = false; 587 g_loadingMainFile = false;
643 return; 588 return;
644 } 589 }
645 590
646 file->setImplicit (false); 591 file->setImplicit (false);
647 592 g_win->closeInitialDocument();
648 // If we have an anonymous, unchanged file open as the only open file 593 g_win->changeDocument (file);
649 // (aside of the one we just opened), close it now.
650 LDDocument::closeInitialFile();
651
652 // Rebuild the object tree view now.
653 LDDocument::setCurrent (file);
654 g_win->doFullRefresh(); 594 g_win->doFullRefresh();
655
656 // Add it to the recent files list.
657 AddRecentFile (path); 595 AddRecentFile (path);
658 g_loadingMainFile = false; 596 g_loadingMainFile = false;
659 597
660 // If there were problems loading subfile references, try see if we can find these 598 // If there were problems loading subfile references, try see if we can find these
661 // files on the parts tracker. 599 // files on the parts tracker.
707 645
708 if (nameComment->text().left (6) == "Name: ") 646 if (nameComment->text().left (6) == "Name: ")
709 { 647 {
710 QString newname = shortenName (path); 648 QString newname = shortenName (path);
711 nameComment->setText (format ("Name: %1", newname)); 649 nameComment->setText (format ("Name: %1", newname));
712 g_win->buildObjList(); 650 m_window->buildObjList();
713 } 651 }
714 } 652 }
715 653
716 QByteArray data; 654 QByteArray data;
717 655
738 676
739 // We have successfully saved, update the save position now. 677 // We have successfully saved, update the save position now.
740 setSavePosition (history()->position()); 678 setSavePosition (history()->position());
741 setFullPath (path); 679 setFullPath (path);
742 setName (shortenName (path)); 680 setName (shortenName (path));
743 681 m_window->updateDocumentListItem (this);
744 g_win->updateDocumentListItem (this); 682 m_window->updateTitle();
745 g_win->updateTitle();
746 return true; 683 return true;
747 } 684 }
748 685
749 // ============================================================================= 686 // =============================================================================
750 // 687 //
1039 obj->replace (ParseLine (static_cast<LDError*> (obj)->contents())); 976 obj->replace (ParseLine (static_cast<LDError*> (obj)->contents()));
1040 } 977 }
1041 978
1042 m_needsReCache = true; 979 m_needsReCache = true;
1043 980
1044 if (this == CurrentDocument()) 981 if (this == m_window->currentDocument())
1045 g_win->buildObjList(); 982 m_window->buildObjList();
1046 } 983 }
1047 984
1048 // ============================================================================= 985 // =============================================================================
1049 // 986 //
1050 int LDDocument::addObject (LDObject* obj) 987 int LDDocument::addObject (LDObject* obj)
1051 { 988 {
1052 history()->add (new AddHistory (objects().size(), obj)); 989 history()->add (new AddHistory (objects().size(), obj));
1053 m_objects << obj; 990 m_objects << obj;
1054 addKnownVertices (obj); 991 addKnownVertices (obj);
1055 obj->setDocument (this); 992 obj->setDocument (this);
1056 g_win->R()->compileObject (obj); 993 m_window->R()->compileObject (obj);
1057 return getObjectCount() - 1; 994 return getObjectCount() - 1;
1058 } 995 }
1059 996
1060 // ============================================================================= 997 // =============================================================================
1061 // 998 //
1073 void LDDocument::insertObj (int pos, LDObject* obj) 1010 void LDDocument::insertObj (int pos, LDObject* obj)
1074 { 1011 {
1075 history()->add (new AddHistory (pos, obj)); 1012 history()->add (new AddHistory (pos, obj));
1076 m_objects.insert (pos, obj); 1013 m_objects.insert (pos, obj);
1077 obj->setDocument (this); 1014 obj->setDocument (this);
1078 g_win->R()->compileObject (obj); 1015 m_window->R()->compileObject (obj);
1079 1016
1080 1017
1081 #ifdef DEBUG 1018 #ifdef DEBUG
1082 if (not isImplicit()) 1019 if (not isImplicit())
1083 dprint ("Inserted object #%1 (%2) at %3\n", obj->id(), obj->typeName(), pos); 1020 dprint ("Inserted object #%1 (%2) at %3\n", obj->id(), obj->typeName(), pos);
1122 1059
1123 // ============================================================================= 1060 // =============================================================================
1124 // 1061 //
1125 bool IsSafeToCloseAll() 1062 bool IsSafeToCloseAll()
1126 { 1063 {
1127 for (LDDocument* f : LDDocument::explicitDocuments()) 1064 for (LDDocument* f : g_win->allDocuments())
1128 { 1065 {
1129 if (not f->isSafeToClose()) 1066 if (not f->isSafeToClose())
1130 return false; 1067 return false;
1131 } 1068 }
1132 1069
1151 m_objectVertices.remove (m_objects[idx]); 1088 m_objectVertices.remove (m_objects[idx]);
1152 m_objects[idx]->deselect(); 1089 m_objects[idx]->deselect();
1153 m_objects[idx]->setDocument (nullptr); 1090 m_objects[idx]->setDocument (nullptr);
1154 obj->setDocument (this); 1091 obj->setDocument (this);
1155 addKnownVertices (obj); 1092 addKnownVertices (obj);
1156 g_win->R()->compileObject (obj); 1093 m_window->R()->compileObject (obj);
1157 m_objects[idx] = obj; 1094 m_objects[idx] = obj;
1158 needVertexMerge(); 1095 needVertexMerge();
1159 } 1096 }
1160 1097
1161 // ============================================================================= 1098 // =============================================================================
1266 LDObjectList LDDocument::inlineContents (bool deep, bool renderinline) 1203 LDObjectList LDDocument::inlineContents (bool deep, bool renderinline)
1267 { 1204 {
1268 // Possibly substitute with logoed studs: 1205 // Possibly substitute with logoed studs:
1269 // stud.dat -> stud-logo.dat 1206 // stud.dat -> stud-logo.dat
1270 // stud2.dat -> stud-logo2.dat 1207 // stud2.dat -> stud-logo2.dat
1271 if (g_win->configBag()->useLogoStuds() and renderinline) 1208 if (m_config->useLogoStuds() and renderinline)
1272 { 1209 {
1273 // Ensure logoed studs are loaded first 1210 // Ensure logoed studs are loaded first
1274 LoadLogoStuds(); 1211 LoadLogoStuds();
1275 1212
1276 if (name() == "stud.dat" and g_logoedStud != null) 1213 if (name() == "stud.dat" and g_logoedStud != null)
1297 else 1234 else
1298 objs << obj->createCopy(); 1235 objs << obj->createCopy();
1299 } 1236 }
1300 1237
1301 return objs; 1238 return objs;
1302 }
1303
1304 // =============================================================================
1305 //
1306 LDDocument* LDDocument::current()
1307 {
1308 return g_currentDocument;
1309 }
1310
1311 // =============================================================================
1312 // Sets the given file as the current one on display. At some point in time this
1313 // was an operation completely unheard of. ;)
1314 //
1315 // TODO: f can be temporarily null. This probably should not be the case.
1316 // =============================================================================
1317 void LDDocument::setCurrent (LDDocument* f)
1318 {
1319 // Implicit files were loaded for caching purposes and must never be set
1320 // current.
1321 if (f != null and f->isImplicit())
1322 return;
1323
1324 g_currentDocument = f;
1325
1326 if (g_win and f)
1327 {
1328 // A ton of stuff needs to be updated
1329 g_win->updateDocumentListItem (f);
1330 g_win->buildObjList();
1331 g_win->updateTitle();
1332 g_win->R()->setDocument (f);
1333 g_win->R()->compiler()->needMerge();
1334 print ("Changed file to %1", f->getDisplayName());
1335 }
1336 }
1337
1338 // =============================================================================
1339 //
1340 int LDDocument::countExplicitFiles()
1341 {
1342 return g_explicitDocuments.size();
1343 }
1344
1345 // =============================================================================
1346 // This little beauty closes the initial file that was open at first when opening
1347 // a new file over it.
1348 // =============================================================================
1349 void LDDocument::closeInitialFile()
1350 {
1351 if (g_explicitDocuments.size() == 2 and
1352 g_explicitDocuments[0]->name().isEmpty() and
1353 not g_explicitDocuments[1]->name().isEmpty() and
1354 not g_explicitDocuments[0]->hasUnsavedChanges())
1355 {
1356 LDDocument* filetoclose = g_explicitDocuments.first();
1357 filetoclose->dismiss();
1358 }
1359 } 1239 }
1360 1240
1361 // ============================================================================= 1241 // =============================================================================
1362 // 1242 //
1363 void LoadLogoStuds() 1243 void LoadLogoStuds()
1377 void LDDocument::addToSelection (LDObject* obj) // [protected] 1257 void LDDocument::addToSelection (LDObject* obj) // [protected]
1378 { 1258 {
1379 if (not obj->isSelected() and obj->document() == this) 1259 if (not obj->isSelected() and obj->document() == this)
1380 { 1260 {
1381 m_sel << obj; 1261 m_sel << obj;
1382 g_win->R()->compileObject (obj); 1262 m_window->R()->compileObject (obj);
1383 obj->setSelected (true); 1263 obj->setSelected (true);
1384 } 1264 }
1385 } 1265 }
1386 1266
1387 // ============================================================================= 1267 // =============================================================================
1389 void LDDocument::removeFromSelection (LDObject* obj) // [protected] 1269 void LDDocument::removeFromSelection (LDObject* obj) // [protected]
1390 { 1270 {
1391 if (obj->isSelected() and obj->document() == this) 1271 if (obj->isSelected() and obj->document() == this)
1392 { 1272 {
1393 m_sel.removeOne (obj); 1273 m_sel.removeOne (obj);
1394 g_win->R()->compileObject (obj); 1274 m_window->R()->compileObject (obj);
1395 obj->setSelected (false); 1275 obj->setSelected (false);
1396 } 1276 }
1397 } 1277 }
1398 1278
1399 // ============================================================================= 1279 // =============================================================================
1400 // 1280 //
1401 void LDDocument::clearSelection() 1281 void LDDocument::clearSelection()
1402 { 1282 {
1403 for (LDObject* obj : m_sel) 1283 for (LDObject* obj : m_sel)
1404 { 1284 {
1405 g_win->R()->compileObject (obj); 1285 m_window->R()->compileObject (obj);
1406 obj->setSelected (false); 1286 obj->setSelected (false);
1407 } 1287 }
1408 1288
1409 m_sel.clear(); 1289 m_sel.clear();
1410 } 1290 }

mercurial