src/document.cc

changeset 606
3dd6f343ec06
parent 604
01bdac75994a
child 607
353e418f161a
equal deleted inserted replaced
605:2983f7c7e7c9 606:3dd6f343ec06
47 47
48 // ============================================================================= 48 // =============================================================================
49 // ----------------------------------------------------------------------------- 49 // -----------------------------------------------------------------------------
50 namespace LDPaths 50 namespace LDPaths
51 { 51 {
52 static str pathError; 52 static QString pathError;
53 53
54 struct 54 struct
55 { 55 {
56 str LDConfigPath; 56 QString LDConfigPath;
57 str partsPath, primsPath; 57 QString partsPath, primsPath;
58 } pathInfo; 58 } pathInfo;
59 59
60 void initPaths() 60 void initPaths()
61 { 61 {
62 if (!tryConfigure (io_ldpath)) 62 if (!tryConfigure (io_ldpath))
68 68
69 io_ldpath = dlg.filename(); 69 io_ldpath = dlg.filename();
70 } 70 }
71 } 71 }
72 72
73 bool tryConfigure (str path) 73 bool tryConfigure (QString path)
74 { 74 {
75 QDir dir; 75 QDir dir;
76 76
77 if (!dir.cd (path)) 77 if (!dir.cd (path))
78 { 78 {
95 95
96 return true; 96 return true;
97 } 97 }
98 98
99 // Accessors 99 // Accessors
100 str getError() 100 QString getError()
101 { 101 {
102 return pathError; 102 return pathError;
103 } 103 }
104 104
105 str ldconfig() 105 QString ldconfig()
106 { 106 {
107 return pathInfo.LDConfigPath; 107 return pathInfo.LDConfigPath;
108 } 108 }
109 109
110 str prims() 110 QString prims()
111 { 111 {
112 return pathInfo.primsPath; 112 return pathInfo.primsPath;
113 } 113 }
114 114
115 str parts() 115 QString parts()
116 { 116 {
117 return pathInfo.partsPath; 117 return pathInfo.partsPath;
118 } 118 }
119 } 119 }
120 120
180 log ("Closed %1", getName()); 180 log ("Closed %1", getName());
181 } 181 }
182 182
183 // ============================================================================= 183 // =============================================================================
184 // ----------------------------------------------------------------------------- 184 // -----------------------------------------------------------------------------
185 LDDocument* findDocument (str name) 185 LDDocument* findDocument (QString name)
186 { 186 {
187 for (LDDocument * file : g_loadedFiles) 187 for (LDDocument * file : g_loadedFiles)
188 if (!file->getName().isEmpty() && file->getName() == name) 188 if (!file->getName().isEmpty() && file->getName() == name)
189 return file; 189 return file;
190 190
191 return null; 191 return null;
192 } 192 }
193 193
194 // ============================================================================= 194 // =============================================================================
195 // ----------------------------------------------------------------------------- 195 // -----------------------------------------------------------------------------
196 str dirname (str path) 196 QString dirname (QString path)
197 { 197 {
198 long lastpos = path.lastIndexOf (DIRSLASH); 198 long lastpos = path.lastIndexOf (DIRSLASH);
199 199
200 if (lastpos > 0) 200 if (lastpos > 0)
201 return path.left (lastpos); 201 return path.left (lastpos);
208 return ""; 208 return "";
209 } 209 }
210 210
211 // ============================================================================= 211 // =============================================================================
212 // ----------------------------------------------------------------------------- 212 // -----------------------------------------------------------------------------
213 str basename (str path) 213 QString basename (QString path)
214 { 214 {
215 long lastpos = path.lastIndexOf (DIRSLASH); 215 long lastpos = path.lastIndexOf (DIRSLASH);
216 216
217 if (lastpos != -1) 217 if (lastpos != -1)
218 return path.mid (lastpos + 1); 218 return path.mid (lastpos + 1);
220 return path; 220 return path;
221 } 221 }
222 222
223 // ============================================================================= 223 // =============================================================================
224 // ----------------------------------------------------------------------------- 224 // -----------------------------------------------------------------------------
225 File* openLDrawFile (str relpath, bool subdirs) 225 File* openLDrawFile (QString relpath, bool subdirs)
226 { 226 {
227 log ("Opening %1...\n", relpath); 227 log ("Opening %1...\n", relpath);
228 File* f = new File; 228 File* f = new File;
229 str fullPath; 229 QString fullPath;
230 230
231 // LDraw models use Windows-style path separators. If we're not on Windows, 231 // LDraw models use Windows-style path separators. If we're not on Windows,
232 // replace the path separator now before opening any files. Qt expects 232 // replace the path separator now before opening any files. Qt expects
233 // forward-slashes as directory separators. 233 // forward-slashes as directory separators.
234 #ifndef WIN32 234 #ifndef WIN32
235 relpath.replace ("\\", "/"); 235 relpath.replace ("\\", "/");
236 #endif // WIN32 236 #endif // WIN32
237 237
238 // Try find it relative to other currently open documents. We want a file 238 // Try find it relative to other currently open documents. We want a file
239 // in the immediate vicinity of a current model to override stock LDraw stuff. 239 // in the immediate vicinity of a current model to override stock LDraw stuff.
240 str reltop = basename (dirname (relpath)); 240 QString reltop = basename (dirname (relpath));
241 for (LDDocument* doc : g_loadedFiles) 241 for (LDDocument* doc : g_loadedFiles)
242 { 242 {
243 if (doc->getFullPath().isEmpty()) 243 if (doc->getFullPath().isEmpty())
244 continue; 244 continue;
245 245
246 str partpath = fmt ("%1/%2", dirname (doc->getFullPath()), relpath); 246 QString partpath = fmt ("%1/%2", dirname (doc->getFullPath()), relpath);
247 247
248 if (f->open (partpath, File::Read)) 248 if (f->open (partpath, File::Read))
249 { 249 {
250 // ensure we don't mix subfiles and 48-primitives with non-subfiles and non-48 250 // ensure we don't mix subfiles and 48-primitives with non-subfiles and non-48
251 str proptop = basename (dirname (partpath)); 251 QString proptop = basename (dirname (partpath));
252 252
253 bool bogus = false; 253 bool bogus = false;
254 254
255 for (str s : g_specialSubdirectories) 255 for (QString s : g_specialSubdirectories)
256 { 256 {
257 if ((proptop == s && reltop != s) || (reltop == s && proptop != s)) 257 if ((proptop == s && reltop != s) || (reltop == s && proptop != s))
258 { 258 {
259 bogus = true; 259 bogus = true;
260 break; 260 break;
277 277
278 if (subdirs) 278 if (subdirs)
279 { 279 {
280 // Look in sub-directories: parts and p. Also look in net_downloadpath, since that's 280 // Look in sub-directories: parts and p. Also look in net_downloadpath, since that's
281 // where we download parts from the PT to. 281 // where we download parts from the PT to.
282 for (const str& topdir : initlist<str> ({ io_ldpath, net_downloadpath })) 282 for (const QString& topdir : initlist<QString> ({ io_ldpath, net_downloadpath }))
283 { 283 {
284 for (const str& subdir : initlist<str> ({ "parts", "p" })) 284 for (const QString& subdir : initlist<QString> ({ "parts", "p" }))
285 { 285 {
286 fullPath = fmt ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relpath); 286 fullPath = fmt ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relpath);
287 287
288 if (f->open (fullPath, File::Read)) 288 if (f->open (fullPath, File::Read))
289 return f; 289 return f;
347 // Parse up to 300 lines per iteration 347 // Parse up to 300 lines per iteration
348 int max = i + 300; 348 int max = i + 300;
349 349
350 for (; i < max && i < (int) getLines().size(); ++i) 350 for (; i < max && i < (int) getLines().size(); ++i)
351 { 351 {
352 str line = getLines()[i]; 352 QString line = getLines()[i];
353 353
354 // Trim the trailing newline 354 // Trim the trailing newline
355 QChar c; 355 QChar c;
356 356
357 while (!line.isEmpty() && ((c = line[line.length() - 1]) == '\n' || c == '\r')) 357 while (!line.isEmpty() && ((c = line[line.length() - 1]) == '\n' || c == '\r'))
416 416
417 // ============================================================================= 417 // =============================================================================
418 // ----------------------------------------------------------------------------- 418 // -----------------------------------------------------------------------------
419 QList<LDObject*> loadFileContents (File* f, int* numWarnings, bool* ok) 419 QList<LDObject*> loadFileContents (File* f, int* numWarnings, bool* ok)
420 { 420 {
421 QList<str> lines; 421 QList<QString> lines;
422 QList<LDObject*> objs; 422 QList<LDObject*> objs;
423 423
424 if (numWarnings) 424 if (numWarnings)
425 *numWarnings = 0; 425 *numWarnings = 0;
426 426
427 // Read in the lines 427 // Read in the lines
428 for (str line : *f) 428 for (QString line : *f)
429 lines << line; 429 lines << line;
430 430
431 LDFileLoader* loader = new LDFileLoader; 431 LDFileLoader* loader = new LDFileLoader;
432 loader->setWarnings (numWarnings); 432 loader->setWarnings (numWarnings);
433 loader->setLines (lines); 433 loader->setLines (lines);
449 return objs; 449 return objs;
450 } 450 }
451 451
452 // ============================================================================= 452 // =============================================================================
453 // ----------------------------------------------------------------------------- 453 // -----------------------------------------------------------------------------
454 LDDocument* openDocument (str path, bool search) 454 LDDocument* openDocument (QString path, bool search)
455 { 455 {
456 // Convert the file name to lowercase since some parts contain uppercase 456 // Convert the file name to lowercase since some parts contain uppercase
457 // file names. I'll assume here that the library will always use lowercase 457 // file names. I'll assume here that the library will always use lowercase
458 // file names for the actual parts.. 458 // file names for the actual parts..
459 File* f; 459 File* f;
516 setlocale (LC_ALL, "C"); 516 setlocale (LC_ALL, "C");
517 517
518 // If we have unsaved changes, warn and give the option of saving. 518 // If we have unsaved changes, warn and give the option of saving.
519 if (hasUnsavedChanges()) 519 if (hasUnsavedChanges())
520 { 520 {
521 str message = fmt (tr ("There are unsaved changes to %1. Should it be saved?"), 521 QString message = fmt (tr ("There are unsaved changes to %1. Should it be saved?"),
522 (getName().length() > 0) ? getName() : tr ("<anonymous>")); 522 (getName().length() > 0) ? getName() : tr ("<anonymous>"));
523 523
524 int button = msgbox::question (g_win, tr ("Unsaved Changes"), message, 524 int button = msgbox::question (g_win, tr ("Unsaved Changes"), message,
525 (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel); 525 (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel);
526 526
529 case msgbox::Yes: 529 case msgbox::Yes:
530 { 530 {
531 // If we don't have a file path yet, we have to ask the user for one. 531 // If we don't have a file path yet, we have to ask the user for one.
532 if (getName().length() == 0) 532 if (getName().length() == 0)
533 { 533 {
534 str newpath = QFileDialog::getSaveFileName (g_win, tr ("Save As"), 534 QString newpath = QFileDialog::getSaveFileName (g_win, tr ("Save As"),
535 getCurrentDocument()->getName(), tr ("LDraw files (*.dat *.ldr)")); 535 getCurrentDocument()->getName(), tr ("LDraw files (*.dat *.ldr)"));
536 536
537 if (newpath.length() == 0) 537 if (newpath.length() == 0)
538 return false; 538 return false;
539 539
592 g_win->updateActions(); 592 g_win->updateActions();
593 } 593 }
594 594
595 // ============================================================================= 595 // =============================================================================
596 // ----------------------------------------------------------------------------- 596 // -----------------------------------------------------------------------------
597 void addRecentFile (str path) 597 void addRecentFile (QString path)
598 { 598 {
599 auto& rfiles = io_recentfiles; 599 auto& rfiles = io_recentfiles;
600 int idx = rfiles.indexOf (path); 600 int idx = rfiles.indexOf (path);
601 601
602 // If this file already is in the list, pop it out. 602 // If this file already is in the list, pop it out.
621 } 621 }
622 622
623 // ============================================================================= 623 // =============================================================================
624 // Open an LDraw file and set it as the main model 624 // Open an LDraw file and set it as the main model
625 // ----------------------------------------------------------------------------- 625 // -----------------------------------------------------------------------------
626 void openMainFile (str path) 626 void openMainFile (QString path)
627 { 627 {
628 g_loadingMainFile = true; 628 g_loadingMainFile = true;
629 LDDocument* file = openDocument (path, false); 629 LDDocument* file = openDocument (path, false);
630 630
631 if (!file) 631 if (!file)
660 g_loadingMainFile = false; 660 g_loadingMainFile = false;
661 } 661 }
662 662
663 // ============================================================================= 663 // =============================================================================
664 // ----------------------------------------------------------------------------- 664 // -----------------------------------------------------------------------------
665 bool LDDocument::save (str savepath) 665 bool LDDocument::save (QString savepath)
666 { 666 {
667 if (!savepath.length()) 667 if (!savepath.length())
668 savepath = getName(); 668 savepath = getName();
669 669
670 File f (savepath, File::Write); 670 File f (savepath, File::Write);
681 { 681 {
682 fpathComment = static_cast<LDComment*> (first); 682 fpathComment = static_cast<LDComment*> (first);
683 683
684 if (fpathComment->text.left (6) == "Name: ") 684 if (fpathComment->text.left (6) == "Name: ")
685 { 685 {
686 str newname = shortenName (savepath); 686 QString newname = shortenName (savepath);
687 fpathComment->text = fmt ("Name: %1", newname); 687 fpathComment->text = fmt ("Name: %1", newname);
688 g_win->buildObjList(); 688 g_win->buildObjList();
689 } 689 }
690 } 690 }
691 691
709 709
710 // ============================================================================= 710 // =============================================================================
711 // ----------------------------------------------------------------------------- 711 // -----------------------------------------------------------------------------
712 class LDParseError : public std::exception 712 class LDParseError : public std::exception
713 { 713 {
714 PROPERTY (private, str, Error, STR_OPS, STOCK_WRITE) 714 PROPERTY (private, QString, Error, STR_OPS, STOCK_WRITE)
715 PROPERTY (private, str, Line, STR_OPS, STOCK_WRITE) 715 PROPERTY (private, QString, Line, STR_OPS, STOCK_WRITE)
716 716
717 public: 717 public:
718 LDParseError (str line, str a) : m_Error (a), m_Line (line) {} 718 LDParseError (QString line, QString a) : m_Error (a), m_Line (line) {}
719 719
720 const char* what() const throw() 720 const char* what() const throw()
721 { 721 {
722 return getError().toLocal8Bit().constData(); 722 return getError().toLocal8Bit().constData();
723 } 723 }
724 }; 724 };
725 725
726 // ============================================================================= 726 // =============================================================================
727 // ----------------------------------------------------------------------------- 727 // -----------------------------------------------------------------------------
728 void checkTokenCount (str line, const QStringList& tokens, int num) 728 void checkTokenCount (QString line, const QStringList& tokens, int num)
729 { 729 {
730 if (tokens.size() != num) 730 if (tokens.size() != num)
731 throw LDParseError (line, fmt ("Bad amount of tokens, expected %1, got %2", num, tokens.size())); 731 throw LDParseError (line, fmt ("Bad amount of tokens, expected %1, got %2", num, tokens.size()));
732 } 732 }
733 733
734 // ============================================================================= 734 // =============================================================================
735 // ----------------------------------------------------------------------------- 735 // -----------------------------------------------------------------------------
736 void checkTokenNumbers (str line, const QStringList& tokens, int min, int max) 736 void checkTokenNumbers (QString line, const QStringList& tokens, int min, int max)
737 { 737 {
738 bool ok; 738 bool ok;
739 739
740 // Check scientific notation, e.g. 7.99361e-15 740 // Check scientific notation, e.g. 7.99361e-15
741 QRegExp scient ("\\-?[0-9]+\\.[0-9]+e\\-[0-9]+"); 741 QRegExp scient ("\\-?[0-9]+\\.[0-9]+e\\-[0-9]+");
764 // ============================================================================= 764 // =============================================================================
765 // This is the LDraw code parser function. It takes in a string containing LDraw 765 // This is the LDraw code parser function. It takes in a string containing LDraw
766 // code and returns the object parsed from it. parseLine never returns null, 766 // code and returns the object parsed from it. parseLine never returns null,
767 // the object will be LDError if it could not be parsed properly. 767 // the object will be LDError if it could not be parsed properly.
768 // ----------------------------------------------------------------------------- 768 // -----------------------------------------------------------------------------
769 LDObject* parseLine (str line) 769 LDObject* parseLine (QString line)
770 { 770 {
771 try 771 try
772 { 772 {
773 QStringList tokens = line.split (" ", str::SkipEmptyParts); 773 QStringList tokens = line.split (" ", QString::SkipEmptyParts);
774 774
775 if (tokens.size() <= 0) 775 if (tokens.size() <= 0)
776 { 776 {
777 // Line was empty, or only consisted of whitespace 777 // Line was empty, or only consisted of whitespace
778 return new LDEmpty; 778 return new LDEmpty;
786 switch (num) 786 switch (num)
787 { 787 {
788 case 0: 788 case 0:
789 { 789 {
790 // Comment 790 // Comment
791 str comm = line.mid (line.indexOf ("0") + 1).simplified(); 791 QString comm = line.mid (line.indexOf ("0") + 1).simplified();
792 792
793 // Handle BFC statements 793 // Handle BFC statements
794 if (tokens.size() > 2 && tokens[1] == "BFC") 794 if (tokens.size() > 2 && tokens[1] == "BFC")
795 { 795 {
796 for (int i = 0; i < LDBFC::NumStatements; ++i) 796 for (int i = 0; i < LDBFC::NumStatements; ++i)
800 // MLCAD is notorious for stuffing these statements in parts it 800 // MLCAD is notorious for stuffing these statements in parts it
801 // creates. The above block only handles valid statements, so we 801 // creates. The above block only handles valid statements, so we
802 // need to handle MLCAD-style invertnext, clip and noclip separately. 802 // need to handle MLCAD-style invertnext, clip and noclip separately.
803 struct 803 struct
804 { 804 {
805 str a; 805 QString a;
806 LDBFC::Type b; 806 LDBFC::Type b;
807 } BFCData[] = 807 } BFCData[] =
808 { 808 {
809 { "INVERTNEXT", LDBFC::InvertNext }, 809 { "INVERTNEXT", LDBFC::InvertNext },
810 { "NOCLIP", LDBFC::NoClip }, 810 { "NOCLIP", LDBFC::NoClip },
952 } 952 }
953 } 953 }
954 954
955 // ============================================================================= 955 // =============================================================================
956 // ----------------------------------------------------------------------------- 956 // -----------------------------------------------------------------------------
957 LDDocument* getDocument (str filename) 957 LDDocument* getDocument (QString filename)
958 { 958 {
959 // Try find the file in the list of loaded files 959 // Try find the file in the list of loaded files
960 LDDocument* doc = findDocument (filename); 960 LDDocument* doc = findDocument (filename);
961 961
962 // If it's not loaded, try open it 962 // If it's not loaded, try open it
1072 assert (idx >= 0 && idx < m_Objects.size()); 1072 assert (idx >= 0 && idx < m_Objects.size());
1073 1073
1074 // Mark this change to history 1074 // Mark this change to history
1075 if (!m_History->isIgnoring()) 1075 if (!m_History->isIgnoring())
1076 { 1076 {
1077 str oldcode = getObject (idx)->raw(); 1077 QString oldcode = getObject (idx)->raw();
1078 str newcode = obj->raw(); 1078 QString newcode = obj->raw();
1079 *m_History << new EditHistory (idx, oldcode, newcode); 1079 *m_History << new EditHistory (idx, oldcode, newcode);
1080 } 1080 }
1081 1081
1082 m_Objects[idx]->unselect(); 1082 m_Objects[idx]->unselect();
1083 m_Objects[idx]->setFile (null); 1083 m_Objects[idx]->setFile (null);
1119 return !isImplicit() && getHistory()->getPosition() != getSavePosition(); 1119 return !isImplicit() && getHistory()->getPosition() != getSavePosition();
1120 } 1120 }
1121 1121
1122 // ============================================================================= 1122 // =============================================================================
1123 // ----------------------------------------------------------------------------- 1123 // -----------------------------------------------------------------------------
1124 str LDDocument::getDisplayName() 1124 QString LDDocument::getDisplayName()
1125 { 1125 {
1126 if (!getName().isEmpty()) 1126 if (!getName().isEmpty())
1127 return getName(); 1127 return getName();
1128 1128
1129 if (!getDefaultName().isEmpty()) 1129 if (!getDefaultName().isEmpty())
1340 addToHistory (new SwapHistory (one->getID(), other->getID())); 1340 addToHistory (new SwapHistory (one->getID(), other->getID()));
1341 } 1341 }
1342 1342
1343 // ============================================================================= 1343 // =============================================================================
1344 // ----------------------------------------------------------------------------- 1344 // -----------------------------------------------------------------------------
1345 str LDDocument::shortenName (str a) // [static] 1345 QString LDDocument::shortenName (QString a) // [static]
1346 { 1346 {
1347 str shortname = basename (a); 1347 QString shortname = basename (a);
1348 str topdirname = basename (dirname (a)); 1348 QString topdirname = basename (dirname (a));
1349 1349
1350 if (g_specialSubdirectories.contains (topdirname)) 1350 if (g_specialSubdirectories.contains (topdirname))
1351 shortname.prepend (topdirname + "\\"); 1351 shortname.prepend (topdirname + "\\");
1352 1352
1353 return shortname; 1353 return shortname;

mercurial