src/ldDocument.cc

changeset 739
152b33a6d51b
parent 737
e58bdb21cc54
child 744
bbe0e1834065
equal deleted inserted replaced
738:16b63398aa1f 739:152b33a6d51b
50 50
51 // ============================================================================= 51 // =============================================================================
52 // 52 //
53 namespace LDPaths 53 namespace LDPaths
54 { 54 {
55 static QString pathError; 55 static String pathError;
56 56
57 struct 57 struct
58 { 58 {
59 QString LDConfigPath; 59 String LDConfigPath;
60 QString partsPath, primsPath; 60 String partsPath, primsPath;
61 } pathInfo; 61 } pathInfo;
62 62
63 void initPaths() 63 void initPaths()
64 { 64 {
65 if (not tryConfigure (io_ldpath)) 65 if (not tryConfigure (io_ldpath))
71 71
72 io_ldpath = dlg.filename(); 72 io_ldpath = dlg.filename();
73 } 73 }
74 } 74 }
75 75
76 bool tryConfigure (QString path) 76 bool tryConfigure (String path)
77 { 77 {
78 QDir dir; 78 QDir dir;
79 79
80 if (not dir.cd (path)) 80 if (not dir.cd (path))
81 { 81 {
98 98
99 return true; 99 return true;
100 } 100 }
101 101
102 // Accessors 102 // Accessors
103 QString getError() 103 String getError()
104 { 104 {
105 return pathError; 105 return pathError;
106 } 106 }
107 107
108 QString ldconfig() 108 String ldconfig()
109 { 109 {
110 return pathInfo.LDConfigPath; 110 return pathInfo.LDConfigPath;
111 } 111 }
112 112
113 QString prims() 113 String prims()
114 { 114 {
115 return pathInfo.primsPath; 115 return pathInfo.primsPath;
116 } 116 }
117 117
118 QString parts() 118 String parts()
119 { 119 {
120 return pathInfo.partsPath; 120 return pathInfo.partsPath;
121 } 121 }
122 } 122 }
123 123
183 print ("Closed %1", name()); 183 print ("Closed %1", name());
184 } 184 }
185 185
186 // ============================================================================= 186 // =============================================================================
187 // 187 //
188 LDDocument* findDocument (QString name) 188 LDDocument* findDocument (String name)
189 { 189 {
190 for (LDDocument * file : g_loadedFiles) 190 for (LDDocument * file : g_loadedFiles)
191 if (not file->name().isEmpty() && file->name() == name) 191 if (not file->name().isEmpty() && file->name() == name)
192 return file; 192 return file;
193 193
194 return null; 194 return null;
195 } 195 }
196 196
197 // ============================================================================= 197 // =============================================================================
198 // 198 //
199 QString dirname (QString path) 199 String dirname (String path)
200 { 200 {
201 long lastpos = path.lastIndexOf (DIRSLASH); 201 long lastpos = path.lastIndexOf (DIRSLASH);
202 202
203 if (lastpos > 0) 203 if (lastpos > 0)
204 return path.left (lastpos); 204 return path.left (lastpos);
211 return ""; 211 return "";
212 } 212 }
213 213
214 // ============================================================================= 214 // =============================================================================
215 // 215 //
216 QString basename (QString path) 216 String basename (String path)
217 { 217 {
218 long lastpos = path.lastIndexOf (DIRSLASH); 218 long lastpos = path.lastIndexOf (DIRSLASH);
219 219
220 if (lastpos != -1) 220 if (lastpos != -1)
221 return path.mid (lastpos + 1); 221 return path.mid (lastpos + 1);
223 return path; 223 return path;
224 } 224 }
225 225
226 // ============================================================================= 226 // =============================================================================
227 // 227 //
228 static QString findLDrawFilePath (QString relpath, bool subdirs) 228 static String findLDrawFilePath (String relpath, bool subdirs)
229 { 229 {
230 QString fullPath; 230 String fullPath;
231 231
232 // LDraw models use Windows-style path separators. If we're not on Windows, 232 // LDraw models use Windows-style path separators. If we're not on Windows,
233 // replace the path separator now before opening any files. Qt expects 233 // replace the path separator now before opening any files. Qt expects
234 // forward-slashes as directory separators. 234 // forward-slashes as directory separators.
235 #ifndef WIN32 235 #ifndef WIN32
236 relpath.replace ("\\", "/"); 236 relpath.replace ("\\", "/");
237 #endif // WIN32 237 #endif // WIN32
238 238
239 // Try find it relative to other currently open documents. We want a file 239 // Try find it relative to other currently open documents. We want a file
240 // in the immediate vicinity of a current model to override stock LDraw stuff. 240 // in the immediate vicinity of a current model to override stock LDraw stuff.
241 QString reltop = basename (dirname (relpath)); 241 String reltop = basename (dirname (relpath));
242 242
243 for (LDDocument* doc : g_loadedFiles) 243 for (LDDocument* doc : g_loadedFiles)
244 { 244 {
245 if (doc->fullPath().isEmpty()) 245 if (doc->fullPath().isEmpty())
246 continue; 246 continue;
247 247
248 QString partpath = format ("%1/%2", dirname (doc->fullPath()), relpath); 248 String partpath = format ("%1/%2", dirname (doc->fullPath()), relpath);
249 QFile f (partpath); 249 QFile f (partpath);
250 250
251 if (f.exists()) 251 if (f.exists())
252 { 252 {
253 // ensure we don't mix subfiles and 48-primitives with non-subfiles and non-48 253 // ensure we don't mix subfiles and 48-primitives with non-subfiles and non-48
254 QString proptop = basename (dirname (partpath)); 254 String proptop = basename (dirname (partpath));
255 255
256 bool bogus = false; 256 bool bogus = false;
257 257
258 for (QString s : g_specialSubdirectories) 258 for (String s : g_specialSubdirectories)
259 { 259 {
260 if ((proptop == s && reltop != s) || (reltop == s && proptop != s)) 260 if ((proptop == s && reltop != s) || (reltop == s && proptop != s))
261 { 261 {
262 bogus = true; 262 bogus = true;
263 break; 263 break;
280 280
281 if (subdirs) 281 if (subdirs)
282 { 282 {
283 // Look in sub-directories: parts and p. Also look in net_downloadpath, since that's 283 // Look in sub-directories: parts and p. Also look in net_downloadpath, since that's
284 // where we download parts from the PT to. 284 // where we download parts from the PT to.
285 for (const QString& topdir : QList<QString> ({ io_ldpath, net_downloadpath })) 285 for (const String& topdir : QList<String> ({ io_ldpath, net_downloadpath }))
286 { 286 {
287 for (const QString& subdir : QList<QString> ({ "parts", "p" })) 287 for (const String& subdir : QList<String> ({ "parts", "p" }))
288 { 288 {
289 fullPath = format ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relpath); 289 fullPath = format ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relpath);
290 290
291 if (QFile::exists (fullPath)) 291 if (QFile::exists (fullPath))
292 return fullPath; 292 return fullPath;
296 296
297 // Did not find the file. 297 // Did not find the file.
298 return ""; 298 return "";
299 } 299 }
300 300
301 QFile* openLDrawFile (QString relpath, bool subdirs, QString* pathpointer) 301 QFile* openLDrawFile (String relpath, bool subdirs, String* pathpointer)
302 { 302 {
303 print ("Opening %1...\n", relpath); 303 print ("Opening %1...\n", relpath);
304 QString path = findLDrawFilePath (relpath, subdirs); 304 String path = findLDrawFilePath (relpath, subdirs);
305 305
306 if (pathpointer != null) 306 if (pathpointer != null)
307 *pathpointer = path; 307 *pathpointer = path;
308 308
309 if (path.isEmpty()) 309 if (path.isEmpty())
368 // Parse up to 300 lines per iteration 368 // Parse up to 300 lines per iteration
369 int max = i + 300; 369 int max = i + 300;
370 370
371 for (; i < max && i < (int) lines().size(); ++i) 371 for (; i < max && i < (int) lines().size(); ++i)
372 { 372 {
373 QString line = lines()[i]; 373 String line = lines()[i];
374 374
375 // Trim the trailing newline 375 // Trim the trailing newline
376 QChar c; 376 QChar c;
377 377
378 while (line.endsWith ("\n") || line.endsWith ("\r")) 378 while (line.endsWith ("\n") || line.endsWith ("\r"))
445 if (numWarnings) 445 if (numWarnings)
446 *numWarnings = 0; 446 *numWarnings = 0;
447 447
448 // Read in the lines 448 // Read in the lines
449 while (not fp->atEnd()) 449 while (not fp->atEnd())
450 lines << QString::fromUtf8 (fp->readLine()); 450 lines << String::fromUtf8 (fp->readLine());
451 451
452 LDFileLoader* loader = new LDFileLoader; 452 LDFileLoader* loader = new LDFileLoader;
453 loader->setWarnings (numWarnings); 453 loader->setWarnings (numWarnings);
454 loader->setLines (lines); 454 loader->setLines (lines);
455 loader->setOnForeground (g_loadingMainFile); 455 loader->setOnForeground (g_loadingMainFile);
470 return objs; 470 return objs;
471 } 471 }
472 472
473 // ============================================================================= 473 // =============================================================================
474 // 474 //
475 LDDocument* openDocument (QString path, bool search, bool implicit) 475 LDDocument* openDocument (String path, bool search, bool implicit)
476 { 476 {
477 // Convert the file name to lowercase since some parts contain uppercase 477 // Convert the file name to lowercase since some parts contain uppercase
478 // file names. I'll assume here that the library will always use lowercase 478 // file names. I'll assume here that the library will always use lowercase
479 // file names for the actual parts.. 479 // file names for the actual parts..
480 QFile* fp; 480 QFile* fp;
481 QString fullpath; 481 String fullpath;
482 482
483 if (search) 483 if (search)
484 fp = openLDrawFile (path.toLower(), true, &fullpath); 484 fp = openLDrawFile (path.toLower(), true, &fullpath);
485 else 485 else
486 { 486 {
541 setlocale (LC_ALL, "C"); 541 setlocale (LC_ALL, "C");
542 542
543 // If we have unsaved changes, warn and give the option of saving. 543 // If we have unsaved changes, warn and give the option of saving.
544 if (hasUnsavedChanges()) 544 if (hasUnsavedChanges())
545 { 545 {
546 QString message = format (tr ("There are unsaved changes to %1. Should it be saved?"), 546 String message = format (tr ("There are unsaved changes to %1. Should it be saved?"),
547 (name().length() > 0) ? name() : tr ("<anonymous>")); 547 (name().length() > 0) ? name() : tr ("<anonymous>"));
548 548
549 int button = msgbox::question (g_win, tr ("Unsaved Changes"), message, 549 int button = msgbox::question (g_win, tr ("Unsaved Changes"), message,
550 (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel); 550 (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel);
551 551
554 case msgbox::Yes: 554 case msgbox::Yes:
555 { 555 {
556 // If we don't have a file path yet, we have to ask the user for one. 556 // If we don't have a file path yet, we have to ask the user for one.
557 if (name().length() == 0) 557 if (name().length() == 0)
558 { 558 {
559 QString newpath = QFileDialog::getSaveFileName (g_win, tr ("Save As"), 559 String newpath = QFileDialog::getSaveFileName (g_win, tr ("Save As"),
560 getCurrentDocument()->name(), tr ("LDraw files (*.dat *.ldr)")); 560 getCurrentDocument()->name(), tr ("LDraw files (*.dat *.ldr)"));
561 561
562 if (newpath.length() == 0) 562 if (newpath.length() == 0)
563 return false; 563 return false;
564 564
617 g_win->updateActions(); 617 g_win->updateActions();
618 } 618 }
619 619
620 // ============================================================================= 620 // =============================================================================
621 // 621 //
622 void addRecentFile (QString path) 622 void addRecentFile (String path)
623 { 623 {
624 auto& rfiles = io_recentfiles; 624 auto& rfiles = io_recentfiles;
625 int idx = rfiles.indexOf (path); 625 int idx = rfiles.indexOf (path);
626 626
627 // If this file already is in the list, pop it out. 627 // If this file already is in the list, pop it out.
646 } 646 }
647 647
648 // ============================================================================= 648 // =============================================================================
649 // Open an LDraw file and set it as the main model 649 // Open an LDraw file and set it as the main model
650 // ============================================================================= 650 // =============================================================================
651 void openMainFile (QString path) 651 void openMainFile (String path)
652 { 652 {
653 g_loadingMainFile = true; 653 g_loadingMainFile = true;
654 654
655 // If there's already a file with the same name, this file must replace it. 655 // If there's already a file with the same name, this file must replace it.
656 LDDocument* documentToReplace = null; 656 LDDocument* documentToReplace = null;
657 QString shortName = LDDocument::shortenName (path); 657 String shortName = LDDocument::shortenName (path);
658 658
659 for (LDDocument* doc : g_loadedFiles) 659 for (LDDocument* doc : g_loadedFiles)
660 { 660 {
661 if (doc->name() == shortName) 661 if (doc->name() == shortName)
662 { 662 {
721 g_loadingMainFile = false; 721 g_loadingMainFile = false;
722 } 722 }
723 723
724 // ============================================================================= 724 // =============================================================================
725 // 725 //
726 bool LDDocument::save (QString savepath) 726 bool LDDocument::save (String savepath)
727 { 727 {
728 if (not savepath.length()) 728 if (not savepath.length())
729 savepath = fullPath(); 729 savepath = fullPath();
730 730
731 QFile f (savepath); 731 QFile f (savepath);
741 { 741 {
742 LDComment* nameComment = static_cast<LDComment*> (nameObject); 742 LDComment* nameComment = static_cast<LDComment*> (nameObject);
743 743
744 if (nameComment->text().left (6) == "Name: ") 744 if (nameComment->text().left (6) == "Name: ")
745 { 745 {
746 QString newname = shortenName (savepath); 746 String newname = shortenName (savepath);
747 nameComment->setText (format ("Name: %1", newname)); 747 nameComment->setText (format ("Name: %1", newname));
748 g_win->buildObjList(); 748 g_win->buildObjList();
749 } 749 }
750 } 750 }
751 751
769 769
770 // ============================================================================= 770 // =============================================================================
771 // 771 //
772 class LDParseError : public std::exception 772 class LDParseError : public std::exception
773 { 773 {
774 PROPERTY (private, QString, error, setError, STOCK_WRITE) 774 PROPERTY (private, String, error, setError, STOCK_WRITE)
775 PROPERTY (private, QString, line, setLine, STOCK_WRITE) 775 PROPERTY (private, String, line, setLine, STOCK_WRITE)
776 776
777 public: 777 public:
778 LDParseError (QString line, QString a) : 778 LDParseError (String line, String a) :
779 m_error (a), 779 m_error (a),
780 m_line (line) {} 780 m_line (line) {}
781 781
782 const char* what() const throw() 782 const char* what() const throw()
783 { 783 {
785 } 785 }
786 }; 786 };
787 787
788 // ============================================================================= 788 // =============================================================================
789 // 789 //
790 void checkTokenCount (QString line, const QStringList& tokens, int num) 790 void checkTokenCount (String line, const QStringList& tokens, int num)
791 { 791 {
792 if (tokens.size() != num) 792 if (tokens.size() != num)
793 throw LDParseError (line, format ("Bad amount of tokens, expected %1, got %2", num, tokens.size())); 793 throw LDParseError (line, format ("Bad amount of tokens, expected %1, got %2", num, tokens.size()));
794 } 794 }
795 795
796 // ============================================================================= 796 // =============================================================================
797 // 797 //
798 void checkTokenNumbers (QString line, const QStringList& tokens, int min, int max) 798 void checkTokenNumbers (String line, const QStringList& tokens, int min, int max)
799 { 799 {
800 bool ok; 800 bool ok;
801 801
802 // Check scientific notation, e.g. 7.99361e-15 802 // Check scientific notation, e.g. 7.99361e-15
803 QRegExp scient ("\\-?[0-9]+\\.[0-9]+e\\-[0-9]+"); 803 QRegExp scient ("\\-?[0-9]+\\.[0-9]+e\\-[0-9]+");
826 // ============================================================================= 826 // =============================================================================
827 // This is the LDraw code parser function. It takes in a string containing LDraw 827 // This is the LDraw code parser function. It takes in a string containing LDraw
828 // code and returns the object parsed from it. parseLine never returns null, 828 // code and returns the object parsed from it. parseLine never returns null,
829 // the object will be LDError if it could not be parsed properly. 829 // the object will be LDError if it could not be parsed properly.
830 // ============================================================================= 830 // =============================================================================
831 LDObject* parseLine (QString line) 831 LDObject* parseLine (String line)
832 { 832 {
833 try 833 try
834 { 834 {
835 QStringList tokens = line.split (" ", QString::SkipEmptyParts); 835 QStringList tokens = line.split (" ", String::SkipEmptyParts);
836 836
837 if (tokens.size() <= 0) 837 if (tokens.size() <= 0)
838 { 838 {
839 // Line was empty, or only consisted of whitespace 839 // Line was empty, or only consisted of whitespace
840 return new LDEmpty; 840 return new LDEmpty;
848 switch (num) 848 switch (num)
849 { 849 {
850 case 0: 850 case 0:
851 { 851 {
852 // Comment 852 // Comment
853 QString comm = line.mid (line.indexOf ("0") + 1).simplified(); 853 String comm = line.mid (line.indexOf ("0") + 1).simplified();
854 854
855 // Handle BFC statements 855 // Handle BFC statements
856 if (tokens.size() > 2 && tokens[1] == "BFC") 856 if (tokens.size() > 2 && tokens[1] == "BFC")
857 { 857 {
858 for (int i = 0; i < LDBFC::NumStatements; ++i) 858 for (int i = 0; i < LDBFC::NumStatements; ++i)
862 // MLCAD is notorious for stuffing these statements in parts it 862 // MLCAD is notorious for stuffing these statements in parts it
863 // creates. The above block only handles valid statements, so we 863 // creates. The above block only handles valid statements, so we
864 // need to handle MLCAD-style invertnext, clip and noclip separately. 864 // need to handle MLCAD-style invertnext, clip and noclip separately.
865 struct 865 struct
866 { 866 {
867 QString a; 867 String a;
868 LDBFC::Statement b; 868 LDBFC::Statement b;
869 } BFCData[] = 869 } BFCData[] =
870 { 870 {
871 { "INVERTNEXT", LDBFC::InvertNext }, 871 { "INVERTNEXT", LDBFC::InvertNext },
872 { "NOCLIP", LDBFC::NoClip }, 872 { "NOCLIP", LDBFC::NoClip },
1011 } 1011 }
1012 } 1012 }
1013 1013
1014 // ============================================================================= 1014 // =============================================================================
1015 // 1015 //
1016 LDDocument* getDocument (QString filename) 1016 LDDocument* getDocument (String filename)
1017 { 1017 {
1018 // Try find the file in the list of loaded files 1018 // Try find the file in the list of loaded files
1019 LDDocument* doc = findDocument (filename); 1019 LDDocument* doc = findDocument (filename);
1020 1020
1021 // If it's not loaded, try open it 1021 // If it's not loaded, try open it
1221 assert (idx >= 0 && idx < m_objects.size()); 1221 assert (idx >= 0 && idx < m_objects.size());
1222 1222
1223 // Mark this change to history 1223 // Mark this change to history
1224 if (not m_history->isIgnoring()) 1224 if (not m_history->isIgnoring())
1225 { 1225 {
1226 QString oldcode = getObject (idx)->asText(); 1226 String oldcode = getObject (idx)->asText();
1227 QString newcode = obj->asText(); 1227 String newcode = obj->asText();
1228 *m_history << new EditHistory (idx, oldcode, newcode); 1228 *m_history << new EditHistory (idx, oldcode, newcode);
1229 } 1229 }
1230 1230
1231 removeKnownVerticesOf (m_objects[idx]); 1231 removeKnownVerticesOf (m_objects[idx]);
1232 m_objects[idx]->unselect(); 1232 m_objects[idx]->unselect();
1276 return !isImplicit() && history()->position() != savePosition(); 1276 return !isImplicit() && history()->position() != savePosition();
1277 } 1277 }
1278 1278
1279 // ============================================================================= 1279 // =============================================================================
1280 // 1280 //
1281 QString LDDocument::getDisplayName() 1281 String LDDocument::getDisplayName()
1282 { 1282 {
1283 if (not name().isEmpty()) 1283 if (not name().isEmpty())
1284 return name(); 1284 return name();
1285 1285
1286 if (not defaultName().isEmpty()) 1286 if (not defaultName().isEmpty())
1504 addToHistory (new SwapHistory (one->id(), other->id())); 1504 addToHistory (new SwapHistory (one->id(), other->id()));
1505 } 1505 }
1506 1506
1507 // ============================================================================= 1507 // =============================================================================
1508 // 1508 //
1509 QString LDDocument::shortenName (QString a) // [static] 1509 String LDDocument::shortenName (String a) // [static]
1510 { 1510 {
1511 QString shortname = basename (a); 1511 String shortname = basename (a);
1512 QString topdirname = basename (dirname (a)); 1512 String topdirname = basename (dirname (a));
1513 1513
1514 if (g_specialSubdirectories.contains (topdirname)) 1514 if (g_specialSubdirectories.contains (topdirname))
1515 shortname.prepend (topdirname + "\\"); 1515 shortname.prepend (topdirname + "\\");
1516 1516
1517 return shortname; 1517 return shortname;

mercurial