--- a/src/file.cpp Thu Oct 03 18:07:06 2013 +0300 +++ b/src/file.cpp Thu Oct 03 20:56:20 2013 +0300 @@ -33,6 +33,7 @@ #include "history.h" #include "dialogs.h" #include "gldraw.h" +#include "moc_file.cpp" cfg (String, io_ldpath, ""); cfg (List, io_recentfiles, {}); @@ -51,59 +52,68 @@ // ============================================================================= // ----------------------------------------------------------------------------- -namespace LDPaths { +namespace LDPaths +{ static str pathError; - - struct { - str LDConfigPath; + + struct + { str LDConfigPath; str partsPath, primsPath; } pathInfo; - - void initPaths() { - if (!tryConfigure (io_ldpath)) { - LDrawPathDialog dlg (false); - + + void initPaths() + { if (!tryConfigure (io_ldpath)) + { LDrawPathDialog dlg (false); + if (!dlg.exec()) exit (0); - + io_ldpath = dlg.filename(); } } - - bool tryConfigure (str path) { - QDir dir; - - if (!dir.cd (path)) { - pathError = "Directory does not exist."; + + bool tryConfigure (str path) + { QDir dir; + + if (!dir.cd (path)) + { pathError = "Directory does not exist."; return false; } - + QStringList mustHave = { "LDConfig.ldr", "parts", "p" }; QStringList contents = dir.entryList (mustHave); - - if (contents.size() != mustHave.size()) { - pathError = "Not an LDraw directory! Must<br />have LDConfig.ldr, parts/ and p/."; + + if (contents.size() != mustHave.size()) + { pathError = "Not an LDraw directory! Must<br />have LDConfig.ldr, parts/ and p/."; return false; } - + pathInfo.partsPath = fmt ("%1" DIRSLASH "parts", path); pathInfo.LDConfigPath = fmt ("%1" DIRSLASH "LDConfig.ldr", path); pathInfo.primsPath = fmt ("%1" DIRSLASH "p", path); - + return true; } - + // Accessors - str getError() { return pathError; } - str ldconfig() { return pathInfo.LDConfigPath; } - str prims() { return pathInfo.primsPath; } - str parts() { return pathInfo.partsPath; } + str getError() + { return pathError; + } + str ldconfig() + { return pathInfo.LDConfigPath; + } + str prims() + { return pathInfo.primsPath; + } + str parts() + { return pathInfo.partsPath; + } } // ============================================================================= // ----------------------------------------------------------------------------- -LDFile::LDFile() { - setImplicit (true); +LDFile::LDFile() +{ setImplicit (true); setSavePos (-1); setListItem (null); m_history.setFile (this); @@ -111,120 +121,125 @@ // ============================================================================= // ----------------------------------------------------------------------------- -LDFile::~LDFile() { - // Clear everything from the model - for (LDObject* obj : objects()) +LDFile::~LDFile() +{ // Clear everything from the model +for (LDObject * obj : objects()) delete obj; - + // Clear the cache as well - for (LDObject* obj : cache()) +for (LDObject * obj : cache()) delete obj; - + // Remove this file from the list of files - for (ulong i = 0; i < g_loadedFiles.size(); ++i) { - if (g_loadedFiles[i] == this) { - g_loadedFiles.erase (i); + for (ulong i = 0; i < g_loadedFiles.size(); ++i) + { if (g_loadedFiles[i] == this) + { g_loadedFiles.erase (i); break; } } - + // If we just closed the current file, we need to set the current // file as something else. - if (this == LDFile::current()) { - // If we closed the last file, create a blank one. + if (this == LDFile::current()) + { // If we closed the last file, create a blank one. if (countExplicitFiles() == 0) newFile(); - else { - // Find the first explicit file loaded + else + { // Find the first explicit file loaded int idx = 0; + while (g_loadedFiles[idx]->implicit()) idx++; - + LDFile::setCurrent (g_loadedFiles[idx]); } } - + g_win->updateFileList(); } // ============================================================================= // ----------------------------------------------------------------------------- -LDFile* findLoadedFile (str name) { - for (LDFile* file : g_loadedFiles) +LDFile* findLoadedFile (str name) +{ for (LDFile * file : g_loadedFiles) if (!file->name().isEmpty() && file->getShortName() == name) return file; - + return null; } // ============================================================================= // ----------------------------------------------------------------------------- -str dirname (str path) { - long lastpos = path.lastIndexOf (DIRSLASH); - +str dirname (str path) +{ long lastpos = path.lastIndexOf (DIRSLASH); + if (lastpos > 0) return path.left (lastpos); - + #ifndef _WIN32 + if (path[0] == DIRSLASH_CHAR) return DIRSLASH; + #endif // _WIN32 - + return ""; } // ============================================================================= // ----------------------------------------------------------------------------- -str basename (str path) { - long lastpos = path.lastIndexOf (DIRSLASH); - +str basename (str path) +{ long lastpos = path.lastIndexOf (DIRSLASH); + if (lastpos != -1) return path.mid (lastpos + 1); - + return path; } // ============================================================================= // ----------------------------------------------------------------------------- -File* openLDrawFile (str relpath, bool subdirs) { - print ("%1: Try to open %2\n", __func__, relpath); +File* openLDrawFile (str relpath, bool subdirs) +{ print ("%1: Try to open %2\n", __func__, relpath); File* f = new File; str fullPath; - + // LDraw models use Windows-style path separators. If we're not on Windows, // replace the path separator now before opening any files. #ifndef WIN32 relpath.replace ("\\", "/"); #endif // WIN32 - - if (LDFile::current()) { - // First, try find the file in the current model's file path. We want a file + + if (LDFile::current()) + { // First, try find the file in the current model's file path. We want a file // in the immediate vicinity of the current model to override stock LDraw stuff. str partpath = fmt ("%1" DIRSLASH "%2", dirname (LDFile::current()->name()), relpath); - + if (f->open (partpath, File::Read)) return f; } - + if (f->open (relpath, File::Read)) return f; - + // Try with just the LDraw path first fullPath = fmt ("%1" DIRSLASH "%2", io_ldpath, relpath); - + if (f->open (fullPath, File::Read)) return f; - - if (subdirs) { - // Look in sub-directories: parts and p. Also look in net_downloadpath. - for (const str& topdir : initlist<str> ({ io_ldpath, net_downloadpath })) - for (const str& subdir : initlist<str> ({ "parts", "p" })) { - fullPath = fmt ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relpath); - if (f->open (fullPath, File::Read)) - return f; - } + + if (subdirs) + { // Look in sub-directories: parts and p. Also look in net_downloadpath. + for (const str & topdir : initlist<str> ( { io_ldpath, net_downloadpath })) + + for (const str & subdir : initlist<str> ( { "parts", "p" })) + { fullPath = fmt ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relpath); + + if (f->open (fullPath, File::Read)) + return f; + } } - + // Did not find the file. print ("could not find %1\n", relpath); delete f; @@ -233,14 +248,14 @@ // ============================================================================= // ----------------------------------------------------------------------------- -void FileLoader::start() { - setDone (false); +void FileLoader::start() +{ setDone (false); setProgress (0); setAborted (false); - - if (concurrent()) { - g_aborted = false; - + + if (concurrent()) + { g_aborted = false; + // Show a progress dialog if we're loading the main file here so we can // show progress updates and keep the WM posted that we're still here. // Of course we cannot exec() the dialog because then the dialog would @@ -249,70 +264,71 @@ dlg->setNumLines (lines().size()); dlg->setModal (true); dlg->show(); - + // Connect the loader in so we can show updates connect (this, SIGNAL (workDone()), dlg, SLOT (accept())); connect (dlg, SIGNAL (rejected()), this, SLOT (abort())); - } else + } + else dlg = null; - + // Begin working work (0); } // ============================================================================= // ----------------------------------------------------------------------------- -void FileLoader::work (int i) { - // User wishes to abort, so stop here now. - if (aborted()) { - for (LDObject* obj : m_objs) +void FileLoader::work (int i) +{ // User wishes to abort, so stop here now. + if (aborted()) +{ for (LDObject * obj : m_objs) delete obj; - + m_objs.clear(); setDone (true); return; } - + // Parse up to 300 lines per iteration int max = i + 300; - - for (; i < max && i < (int) lines().size(); ++i) { - str line = lines()[i]; - + + for (; i < max && i < (int) lines().size(); ++i) + { str line = lines() [i]; + // Trim the trailing newline qchar c; - - while ((c = line[line.length() - 1]) == '\n' || c == '\r') + + while ( (c = line[line.length() - 1]) == '\n' || c == '\r') line.chop (1); - + LDObject* obj = parseLine (line); - + // Check for parse errors and warn about tthem - if (obj->getType() == LDObject::Error) { - log ("Couldn't parse line #%1: %2", m_progress + 1, static_cast<LDError*> (obj)->reason); - + if (obj->getType() == LDObject::Error) + { log ("Couldn't parse line #%1: %2", m_progress + 1, static_cast<LDError*> (obj)->reason); + if (m_warningsPointer) - (*m_warningsPointer)++; + (*m_warningsPointer) ++; } - + m_objs << obj; setProgress (i); - + // If we have a dialog pointer, update the progress now if (concurrent()) dlg->updateProgress (i); } - + // If we're done now, tell the environment we're done and stop. - if (i >= ((int) lines().size()) - 1) { - emit workDone(); + if (i >= ( (int) lines().size()) - 1) + { emit workDone(); setDone (true); return; } - + // Otherwise, continue, by recursing back. - if (!done()) { - // If we have a dialog to show progress output to, we cannot just call + if (!done()) + { // If we have a dialog to show progress output to, we cannot just call // work() again immediately as the dialog needs some processor cycles as // well. Thus, take a detour through the event loop by using the // meta-object system. @@ -331,166 +347,169 @@ // ============================================================================= // ----------------------------------------------------------------------------- -void FileLoader::abort() { - setAborted (true); - +void FileLoader::abort() +{ setAborted (true); + if (concurrent()) g_aborted = true; } // ============================================================================= // ----------------------------------------------------------------------------- -List<LDObject*> loadFileContents (File* f, ulong* numWarnings, bool* ok) { - List<str> lines; +List<LDObject*> loadFileContents (File* f, ulong* numWarnings, bool* ok) +{ List<str> lines; List<LDObject*> objs; - + if (numWarnings) *numWarnings = 0; - + // Calculate the amount of lines - for (str line : *f) +for (str line : *f) lines << line; - + f->rewind(); - + FileLoader* loader = new FileLoader; loader->setWarningsPointer (numWarnings); loader->setLines (lines); loader->setConcurrent (g_loadingMainFile); loader->start(); - + // After start() returns, if the loader isn't done yet, it's delaying // its next iteration through the event loop. We need to catch this here // by telling the event loop to tick, which will tick the file loader again. // We keep doing this until the file loader is ready. while (loader->done() == false) qApp->processEvents(); - + // If we wanted the success value, supply that now if (ok) *ok = !loader->aborted(); - + objs = loader->objs(); return objs; } // ============================================================================= // ----------------------------------------------------------------------------- -LDFile* openDATFile (str path, bool search) { - // Convert the file name to lowercase since some parts contain uppercase +LDFile* openDATFile (str path, bool search) +{ // Convert the file name to lowercase since some parts contain uppercase // file names. I'll assume here that the library will always use lowercase // file names for the actual parts.. File* f; - + if (search) f = openLDrawFile (path.toLower(), true); - else { - f = new File (path, File::Read); - - if (!*f) { - delete f; + else + { f = new File (path, File::Read); + + if (!*f) + { delete f; return null; } } - + if (!f) return null; - + LDFile* load = new LDFile; load->setName (path); - + ulong numWarnings; bool ok; List<LDObject*> objs = loadFileContents (f, &numWarnings, &ok); - + if (!ok) return null; - - for (LDObject* obj : objs) + +for (LDObject * obj : objs) load->addObject (obj); - + delete f; g_loadedFiles << load; - - if (g_loadingMainFile) { - LDFile::setCurrent (load); + + if (g_loadingMainFile) + { LDFile::setCurrent (load); g_win->R()->setFile (load); log (QObject::tr ("File %1 parsed successfully (%2 errors)."), path, numWarnings); } - + return load; } // ============================================================================= // ----------------------------------------------------------------------------- -bool LDFile::safeToClose() { - typedef QMessageBox msgbox; +bool LDFile::safeToClose() +{ typedef QMessageBox msgbox; setlocale (LC_ALL, "C"); - + // If we have unsaved changes, warn and give the option of saving. - if (hasUnsavedChanges()) { - str message = fmt ("There are unsaved changes to %1. Should it be saved?", + if (hasUnsavedChanges()) + { str message = fmt ("There are unsaved changes to %1. Should it be saved?", (name().length() > 0) ? name() : "<anonymous>"); - + int button = msgbox::question (g_win, "Unsaved Changes", message, - (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel); - - switch (button) { - case msgbox::Yes: - // If we don't have a file path yet, we have to ask the user for one. - if (name().length() == 0) { - str newpath = QFileDialog::getSaveFileName (g_win, "Save As", - LDFile::current()->name(), "LDraw files (*.dat *.ldr)"); - - if (newpath.length() == 0) - return false; - - setName (newpath); - } - - if (!save()) { - message = fmt (QObject::tr ("Failed to save %1: %2\nDo you still want to close?"), - name(), strerror (errno)); - - if (msgbox::critical (g_win, "Save Failure", message, - (msgbox::Yes | msgbox::No), msgbox::No) == msgbox::No) { - return false; + (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel); + + switch (button) + { case msgbox::Yes: + + // If we don't have a file path yet, we have to ask the user for one. + if (name().length() == 0) + { str newpath = QFileDialog::getSaveFileName (g_win, "Save As", + LDFile::current()->name(), "LDraw files (*.dat *.ldr)"); + + if (newpath.length() == 0) + return false; + + setName (newpath); } - } - break; - - case msgbox::Cancel: - return false; - - default: - break; + + if (!save()) + { message = fmt (QObject::tr ("Failed to save %1: %2\nDo you still want to close?"), + name(), strerror (errno)); + + if (msgbox::critical (g_win, "Save Failure", message, + (msgbox::Yes | msgbox::No), msgbox::No) == msgbox::No) + { return false; + } + } + + break; + + case msgbox::Cancel: + return false; + + default: + break; } } - + return true; } // ============================================================================= // ----------------------------------------------------------------------------- -void closeAll() { - // Remove all loaded files and the objects they contain +void closeAll() +{ // Remove all loaded files and the objects they contain List<LDFile*> files = g_loadedFiles; - for (LDFile* file : files) + +for (LDFile * file : files) delete file; } // ============================================================================= // ----------------------------------------------------------------------------- -void newFile() { - // Create a new anonymous file and set it to our current +void newFile() +{ // Create a new anonymous file and set it to our current LDFile* f = new LDFile; f->setName (""); f->setImplicit (false); g_loadedFiles << f; LDFile::setCurrent (f); - + LDFile::closeInitialFile(); - + g_win->R()->setFile (f); g_win->fullRefresh(); g_win->updateTitle(); @@ -499,26 +518,26 @@ // ============================================================================= // ----------------------------------------------------------------------------- -void addRecentFile (str path) { - alias rfiles = io_recentfiles.value; +void addRecentFile (str path) +{ alias rfiles = io_recentfiles.value; int idx = rfiles.indexOf (path); - + // If this file already is in the list, pop it out. - if (idx != -1) { - if (rfiles.size() == 1) + if (idx != -1) + { if (rfiles.size() == 1) return; // only recent file - abort and do nothing - + // Pop it out. rfiles.removeAt (idx); } - + // If there's too many recent files, drop one out. while (rfiles.size() > (g_MaxRecentFiles - 1)) rfiles.removeAt (0); - + // Add the file rfiles << path; - + Config::save(); g_win->updateRecentFilesMenu(); } @@ -526,35 +545,35 @@ // ============================================================================= // Open an LDraw file and set it as the main model // ----------------------------------------------------------------------------- -void openMainFile (str path) { - g_loadingMainFile = true; +void openMainFile (str path) +{ g_loadingMainFile = true; LDFile* file = openDATFile (path, false); - - if (!file) { - // Loading failed, thus drop down to a new file since we + + if (!file) + { // Loading failed, thus drop down to a new file since we // closed everything prior. newFile(); - - if (!g_aborted) { - // Tell the user loading failed. + + if (!g_aborted) + { // Tell the user loading failed. setlocale (LC_ALL, "C"); critical (fmt (QObject::tr ("Failed to open %1: %2"), path, strerror (errno))); } - + g_loadingMainFile = false; return; } - + file->setImplicit (false); - + // If we have an anonymous, unchanged file open as the only open file // (aside of the one we just opened), close it now. LDFile::closeInitialFile(); - + // Rebuild the object tree view now. LDFile::setCurrent (file); g_win->fullRefresh(); - + // Add it to the recent files list. addRecentFile (path); g_loadingMainFile = false; @@ -562,49 +581,49 @@ // ============================================================================= // ----------------------------------------------------------------------------- -bool LDFile::save (str savepath) { - if (!savepath.length()) +bool LDFile::save (str savepath) +{ if (!savepath.length()) savepath = name(); - + File f (savepath, File::Write); - + if (!f) return false; - + // If the second object in the list holds the file name, update that now. // Only do this if the file is explicitly open. If it's saved into a directory // called "s" or "48", prepend that into the name. LDComment* fpathComment = null; LDObject* first = object (1); - - if (!implicit() && first != null && first->getType() == LDObject::Comment) { - fpathComment = static_cast<LDComment*> (first); - - if (fpathComment->text.left (6) == "Name: ") { - str newname; + + if (!implicit() && first != null && first->getType() == LDObject::Comment) + { fpathComment = static_cast<LDComment*> (first); + + if (fpathComment->text.left (6) == "Name: ") + { str newname; str dir = basename (dirname (savepath)); - + if (dir == "s" || dir == "48") newname = dir + "\\"; - + newname += basename (savepath); fpathComment->text = fmt ("Name: %1", newname); g_win->buildObjList(); } } - + // File is open, now save the model to it. Note that LDraw requires files to // have DOS line endings, so we terminate the lines with \r\n. - for (LDObject* obj : objects()) +for (LDObject * obj : objects()) f.write (obj->raw() + "\r\n"); - + // File is saved, now clean up. f.close(); - + // We have successfully saved, update the save position now. setSavePos (history().pos()); setName (savepath); - + g_win->updateFileListItem (this); g_win->updateTitle(); return true; @@ -623,12 +642,12 @@ // ============================================================================= // ----------------------------------------------------------------------------- -static vertex parseVertex (QStringList& s, const ushort n) { - vertex v; - - for (const Axis ax : g_Axes) +static vertex parseVertex (QStringList& s, const ushort n) +{ vertex v; + +for (const Axis ax : g_Axes) v[ax] = atof (s[n + ax]); - + return v; } @@ -637,250 +656,251 @@ // code and returns the object parsed from it. parseLine never returns null, // the object will be LDError if it could not be parsed properly. // ----------------------------------------------------------------------------- -LDObject* parseLine (str line) { - QStringList tokens = line.split (" ", str::SkipEmptyParts); - - if (tokens.size() <= 0) { - // Line was empty, or only consisted of whitespace +LDObject* parseLine (str line) +{ QStringList tokens = line.split (" ", str::SkipEmptyParts); + + if (tokens.size() <= 0) + { // Line was empty, or only consisted of whitespace return new LDEmpty; } - + if (tokens[0].length() != 1 || tokens[0][0].isDigit() == false) return new LDError (line, "Illogical line code"); - + int num = tokens[0][0].digitValue(); - - switch (num) { - case 0: { - // Comment - str comm = line.mid (line.indexOf ("0") + 1); - - // Remove any leading whitespace - while (comm[0] == ' ') - comm.remove (0, 1); - - // Handle BFC statements - if (tokens.size() > 2 && tokens[1] == "BFC") { - for (short i = 0; i < LDBFC::NumStatements; ++i) - if (comm == fmt ("BFC %1", LDBFC::statements [i])) - return new LDBFC ((LDBFC::Type) i); - - // MLCAD is notorious for stuffing these statements in parts it - // creates. The above block only handles valid statements, so we - // need to handle MLCAD-style invertnext, clip and noclip separately. - struct { - const char* a; - LDBFC::Type b; - } BFCData[] = { - { "INVERTNEXT", LDBFC::InvertNext }, - { "NOCLIP", LDBFC::NoClip }, - { "CLIP", LDBFC::Clip } - }; - - for (const auto& i : BFCData) - if (comm == fmt ("BFC CERTIFY %1", i.a)) - return new LDBFC (i.b); + + switch (num) + { case 0: + { // Comment + str comm = line.mid (line.indexOf ("0") + 1); + + // Remove any leading whitespace + while (comm[0] == ' ') + comm.remove (0, 1); + + // Handle BFC statements + if (tokens.size() > 2 && tokens[1] == "BFC") + { for (short i = 0; i < LDBFC::NumStatements; ++i) + if (comm == fmt ("BFC %1", LDBFC::statements [i])) + return new LDBFC ( (LDBFC::Type) i); + + // MLCAD is notorious for stuffing these statements in parts it + // creates. The above block only handles valid statements, so we + // need to handle MLCAD-style invertnext, clip and noclip separately. + struct + { const char* a; + LDBFC::Type b; + } BFCData[] = + { { "INVERTNEXT", LDBFC::InvertNext }, + { "NOCLIP", LDBFC::NoClip }, + { "CLIP", LDBFC::Clip } + }; + + for (const auto & i : BFCData) + if (comm == fmt ("BFC CERTIFY %1", i.a)) + return new LDBFC (i.b); + } + + if (tokens.size() > 2 && tokens[1] == "!LDFORGE") + { // Handle LDForge-specific types, they're embedded into comments too + if (tokens[2] == "VERTEX") + { // Vertex (0 !LDFORGE VERTEX) + CHECK_TOKEN_COUNT (7) + CHECK_TOKEN_NUMBERS (3, 6) + + LDVertex* obj = new LDVertex; + obj->setColor (tokens[3].toLong()); + + for (const Axis ax : g_Axes) + obj->pos[ax] = tokens[4 + ax].toDouble(); // 4 - 6 + + return obj; + } elif (tokens[2] == "OVERLAY") + + { CHECK_TOKEN_COUNT (9); + CHECK_TOKEN_NUMBERS (5, 8) + + LDOverlay* obj = new LDOverlay; + obj->setFilename (tokens[3]); + obj->setCamera (tokens[4].toLong()); + obj->setX (tokens[5].toLong()); + obj->setY (tokens[6].toLong()); + obj->setWidth (tokens[7].toLong()); + obj->setHeight (tokens[8].toLong()); + return obj; + } + } + + // Just a regular comment: + LDComment* obj = new LDComment; + obj->text = comm; + return obj; } - - if (tokens.size() > 2 && tokens[1] == "!LDFORGE") { - // Handle LDForge-specific types, they're embedded into comments too - if (tokens[2] == "VERTEX") { - // Vertex (0 !LDFORGE VERTEX) - CHECK_TOKEN_COUNT (7) - CHECK_TOKEN_NUMBERS (3, 6) - - LDVertex* obj = new LDVertex; - obj->setColor (tokens[3].toLong()); - - for (const Axis ax : g_Axes) - obj->pos[ax] = tokens[4 + ax].toDouble(); // 4 - 6 - - return obj; - } elif (tokens[2] == "OVERLAY") { - CHECK_TOKEN_COUNT (9); - CHECK_TOKEN_NUMBERS (5, 8) - - LDOverlay* obj = new LDOverlay; - obj->setFilename (tokens[3]); - obj->setCamera (tokens[4].toLong()); - obj->setX (tokens[5].toLong()); - obj->setY (tokens[6].toLong()); - obj->setWidth (tokens[7].toLong()); - obj->setHeight (tokens[8].toLong()); + + case 1: + { // Subfile + CHECK_TOKEN_COUNT (15) + CHECK_TOKEN_NUMBERS (1, 13) + + // Try open the file. Disable g_loadingMainFile temporarily since we're + // not loading the main file now, but the subfile in question. + bool tmp = g_loadingMainFile; + g_loadingMainFile = false; + LDFile* load = getFile (tokens[14]); + g_loadingMainFile = tmp; + + // If we cannot open the file, mark it an error + if (!load) + { LDError* obj = new LDError (line, fmt ("Could not open %1", tokens[14])); + obj->setFileRef (tokens[14]); return obj; } + + LDSubfile* obj = new LDSubfile; + obj->setColor (tokens[1].toLong()); + obj->setPosition (parseVertex (tokens, 2)); // 2 - 4 + + matrix transform; + + for (short i = 0; i < 9; ++i) + transform[i] = tokens[i + 5].toDouble(); // 5 - 13 + + obj->setTransform (transform); + obj->setFileInfo (load); + return obj; } - - // Just a regular comment: - LDComment* obj = new LDComment; - obj->text = comm; - return obj; - } - - case 1: { - // Subfile - CHECK_TOKEN_COUNT (15) - CHECK_TOKEN_NUMBERS (1, 13) - - // Try open the file. Disable g_loadingMainFile temporarily since we're - // not loading the main file now, but the subfile in question. - bool tmp = g_loadingMainFile; - g_loadingMainFile = false; - LDFile* load = getFile (tokens[14]); - g_loadingMainFile = tmp; - - // If we cannot open the file, mark it an error - if (!load) { - LDError* obj = new LDError (line, fmt ("Could not open %1", tokens[14])); - obj->setFileRef (tokens[14]); + + case 2: + { CHECK_TOKEN_COUNT (8) + CHECK_TOKEN_NUMBERS (1, 7) + + // Line + LDLine* obj = new LDLine; + obj->setColor (tokens[1].toLong()); + + for (short i = 0; i < 2; ++i) + obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 7 + return obj; } - - LDSubfile* obj = new LDSubfile; - obj->setColor (tokens[1].toLong()); - obj->setPosition (parseVertex (tokens, 2)); // 2 - 4 - - matrix transform; - - for (short i = 0; i < 9; ++i) - transform[i] = tokens[i + 5].toDouble(); // 5 - 13 - - obj->setTransform (transform); - obj->setFileInfo (load); - return obj; - } - - case 2: { - CHECK_TOKEN_COUNT (8) - CHECK_TOKEN_NUMBERS (1, 7) - - // Line - LDLine* obj = new LDLine; - obj->setColor (tokens[1].toLong()); - - for (short i = 0; i < 2; ++i) - obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 7 - - return obj; - } - - case 3: { - CHECK_TOKEN_COUNT (11) - CHECK_TOKEN_NUMBERS (1, 10) - - // Triangle - LDTriangle* obj = new LDTriangle; - obj->setColor (tokens[1].toLong()); - - for (short i = 0; i < 3; ++i) - obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 10 - - return obj; - } - - case 4: - case 5: { - CHECK_TOKEN_COUNT (14) - CHECK_TOKEN_NUMBERS (1, 13) - - // Quadrilateral / Conditional line - LDObject* obj; - - if (num == 4) - obj = new LDQuad; - else - obj = new LDCndLine; - - obj->setColor (tokens[1].toLong()); - - for (short i = 0; i < 4; ++i) - obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 13 - - return obj; - } - - default: // Strange line we couldn't parse - return new LDError (line, "Unknown line code number"); + + case 3: + { CHECK_TOKEN_COUNT (11) + CHECK_TOKEN_NUMBERS (1, 10) + + // Triangle + LDTriangle* obj = new LDTriangle; + obj->setColor (tokens[1].toLong()); + + for (short i = 0; i < 3; ++i) + obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 10 + + return obj; + } + + case 4: + case 5: + { CHECK_TOKEN_COUNT (14) + CHECK_TOKEN_NUMBERS (1, 13) + + // Quadrilateral / Conditional line + LDObject* obj; + + if (num == 4) + obj = new LDQuad; + else + obj = new LDCndLine; + + obj->setColor (tokens[1].toLong()); + + for (short i = 0; i < 4; ++i) + obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 13 + + return obj; + } + + default: // Strange line we couldn't parse + return new LDError (line, "Unknown line code number"); } } // ============================================================================= // ----------------------------------------------------------------------------- -LDFile* getFile (str filename) { - // Try find the file in the list of loaded files +LDFile* getFile (str filename) +{ // Try find the file in the list of loaded files LDFile* load = findLoadedFile (filename); - + // If it's not loaded, try open it if (!load) load = openDATFile (filename, true); - + return load; } // ============================================================================= // ----------------------------------------------------------------------------- -void reloadAllSubfiles() { - if (!LDFile::current()) +void reloadAllSubfiles() +{ if (!LDFile::current()) return; - + g_loadedFiles.clear(); g_loadedFiles << LDFile::current(); - + // Go through all objects in the current file and reload the subfiles - for (LDObject* obj : LDFile::current()->objects()) { - if (obj->getType() == LDObject::Subfile) { - LDSubfile* ref = static_cast<LDSubfile*> (obj); +for (LDObject * obj : LDFile::current()->objects()) + { if (obj->getType() == LDObject::Subfile) + { LDSubfile* ref = static_cast<LDSubfile*> (obj); LDFile* fileInfo = getFile (ref->fileInfo()->name()); - + if (fileInfo) ref->setFileInfo (fileInfo); else ref->replace (new LDError (ref->raw(), "Could not open referred file")); } - + // Reparse gibberish files. It could be that they are invalid because // of loading errors. Circumstances may be different now. if (obj->getType() == LDObject::Error) obj->replace (parseLine (static_cast<LDError*> (obj)->contents)); } - + // Close all files left unused LDFile::closeUnused(); } // ============================================================================= // ----------------------------------------------------------------------------- -ulong LDFile::addObject (LDObject* obj) { - m_history.add (new AddHistory (objects().size(), obj)); +ulong LDFile::addObject (LDObject* obj) +{ m_history.add (new AddHistory (objects().size(), obj)); m_objects << obj; - + if (obj->getType() == LDObject::Vertex) m_vertices << obj; - + obj->setFile (this); return numObjs() - 1; } // ============================================================================= // ----------------------------------------------------------------------------- -void LDFile::addObjects (const List<LDObject*> objs) { - for (LDObject* obj : objs) +void LDFile::addObjects (const List<LDObject*> objs) +{ for (LDObject * obj : objs) if (obj) addObject (obj); } // ============================================================================= // ----------------------------------------------------------------------------- -void LDFile::insertObj (const ulong pos, LDObject* obj) { - m_history.add (new AddHistory (pos, obj)); +void LDFile::insertObj (const ulong pos, LDObject* obj) +{ m_history.add (new AddHistory (pos, obj)); m_objects.insert (pos, obj); obj->setFile (this); } // ============================================================================= // ----------------------------------------------------------------------------- -void LDFile::forgetObject (LDObject* obj) { - ulong idx = obj->getIndex(); +void LDFile::forgetObject (LDObject* obj) +{ ulong idx = obj->getIndex(); m_history.add (new DelHistory (idx, obj)); m_objects.erase (idx); obj->setFile (null); @@ -888,181 +908,184 @@ // ============================================================================= // ----------------------------------------------------------------------------- -bool safeToCloseAll() { - for (LDFile* f : g_loadedFiles) +bool safeToCloseAll() +{ for (LDFile * f : g_loadedFiles) if (!f->safeToClose()) return false; - + return true; } // ============================================================================= // ----------------------------------------------------------------------------- -void LDFile::setObject (ulong idx, LDObject* obj) { - assert (idx < numObjs()); - +void LDFile::setObject (ulong idx, LDObject* obj) +{ assert (idx < numObjs()); + // Mark this change to history str oldcode = object (idx)->raw(); str newcode = obj->raw(); m_history << new EditHistory (idx, oldcode, newcode); - + obj->setFile (this); m_objects[idx] = obj; } // ============================================================================= // ----------------------------------------------------------------------------- -static List<LDFile*> getFilesUsed (LDFile* node) { - List<LDFile*> filesUsed; - - for (LDObject* obj : node->objects()) { - if (obj->getType() != LDObject::Subfile) +static List<LDFile*> getFilesUsed (LDFile* node) +{ List<LDFile*> filesUsed; + +for (LDObject * obj : node->objects()) + { if (obj->getType() != LDObject::Subfile) continue; - + LDSubfile* ref = static_cast<LDSubfile*> (obj); filesUsed << ref->fileInfo(); filesUsed << getFilesUsed (ref->fileInfo()); } - + return filesUsed; } // ============================================================================= // Find out which files are unused and close them. // ----------------------------------------------------------------------------- -void LDFile::closeUnused() { - List<LDFile*> filesUsed = getFilesUsed (LDFile::current()); - +void LDFile::closeUnused() +{ List<LDFile*> filesUsed = getFilesUsed (LDFile::current()); + // Anything that's explicitly opened must not be closed - for (LDFile* file : g_loadedFiles) +for (LDFile * file : g_loadedFiles) if (!file->implicit()) filesUsed << file; - + // Remove duplicated entries filesUsed.makeUnique(); - + // Close all open files that aren't in filesUsed - for (LDFile* file : g_loadedFiles) { - bool isused = false; - - for (LDFile* usedFile : filesUsed) { - if (file == usedFile) { - isused = true; +for (LDFile * file : g_loadedFiles) + { bool isused = false; + + for (LDFile * usedFile : filesUsed) + { if (file == usedFile) + { isused = true; break; } } - + if (!isused) delete file; } - + g_loadedFiles.clear(); g_loadedFiles << filesUsed; } // ============================================================================= // ----------------------------------------------------------------------------- -LDObject* LDFile::object (ulong pos) const { - if (m_objects.size() <= pos) +LDObject* LDFile::object (ulong pos) const +{ if (m_objects.size() <= pos) return null; - + return m_objects[pos]; } // ============================================================================= // ----------------------------------------------------------------------------- -LDObject* LDFile::obj (ulong pos) const { - return object (pos); +LDObject* LDFile::obj (ulong pos) const +{ return object (pos); } // ============================================================================= // ----------------------------------------------------------------------------- -ulong LDFile::numObjs() const { - return objects().size(); +ulong LDFile::numObjs() const +{ return objects().size(); } // ============================================================================= // ----------------------------------------------------------------------------- -bool LDFile::hasUnsavedChanges() const { - return !implicit() && history().pos() != savePos(); +bool LDFile::hasUnsavedChanges() const +{ return !implicit() && history().pos() != savePos(); } // ============================================================================= // ----------------------------------------------------------------------------- -str LDFile::getShortName() { - if (name().length() > 0) +str LDFile::getShortName() +{ if (name().length() > 0) return basename (name()); - + return tr ("<anonymous>"); } // ============================================================================= // ----------------------------------------------------------------------------- -List<LDObject*> LDFile::inlineContents (LDSubfile::InlineFlags flags) { - // Possibly substitute with logoed studs: +List<LDObject*> LDFile::inlineContents (LDSubfile::InlineFlags flags) +{ // Possibly substitute with logoed studs: // stud.dat -> stud-logo.dat // stud2.dat -> stud-logo2.dat - if (gl_logostuds && (flags & LDSubfile::RendererInline)) { - if (name() == "stud.dat" && g_logoedStud) + if (gl_logostuds && (flags & LDSubfile::RendererInline)) + { if (name() == "stud.dat" && g_logoedStud) return g_logoedStud->inlineContents (flags); + elif (name() == "stud2.dat" && g_logoedStud2) - return g_logoedStud2->inlineContents (flags); + return g_logoedStud2->inlineContents (flags); } - + List<LDObject*> objs, objcache; - + bool deep = flags & LDSubfile::DeepInline, - doCache = flags & LDSubfile::CacheInline; - + doCache = flags & LDSubfile::CacheInline; + // If we have this cached, just clone that - if (deep && cache().size()) { - for (LDObject* obj : cache()) + if (deep && cache().size()) +{ for (LDObject * obj : cache()) objs << obj->clone(); - } else { - if (!deep) + } + else + { if (!deep) doCache = false; - - for (LDObject* obj : objects()) { - // Skip those without scemantic meaning + + for (LDObject * obj : objects()) + { // Skip those without scemantic meaning if (!obj->isScemantic()) continue; - + // Got another sub-file reference, inline it if we're deep-inlining. If not, // just add it into the objects normally. Also, we only cache immediate // subfiles and this is not one. Yay, recursion! - if (deep && obj->getType() == LDObject::Subfile) { - LDSubfile* ref = static_cast<LDSubfile*> (obj); - + if (deep && obj->getType() == LDObject::Subfile) + { LDSubfile* ref = static_cast<LDSubfile*> (obj); + // We only want to cache immediate subfiles, so shed the caching // flag when recursing deeper in hierarchy. - List<LDObject*> otherobjs = ref->inlineContents (flags & ~(LDSubfile::CacheInline)); - - for (LDObject* otherobj : otherobjs) { - // Cache this object, if desired + List<LDObject*> otherobjs = ref->inlineContents (flags & ~ (LDSubfile::CacheInline)); + + for (LDObject * otherobj : otherobjs) + { // Cache this object, if desired if (doCache) objcache << otherobj->clone(); - + objs << otherobj; } - } else { - if (doCache) + } + else + { if (doCache) objcache << obj->clone(); - + objs << obj->clone(); } } - + if (doCache) setCache (objcache); } - + return objs; } // ============================================================================= // ----------------------------------------------------------------------------- -LDFile* LDFile::current() { - return m_curfile; +LDFile* LDFile::current() +{ return m_curfile; } // ============================================================================= @@ -1071,16 +1094,16 @@ // // FIXME: f can be temporarily null. This probably should not be the case. // ----------------------------------------------------------------------------- -void LDFile::setCurrent (LDFile* f) { - // Implicit files were loaded for caching purposes and must never be set +void LDFile::setCurrent (LDFile* f) +{ // Implicit files were loaded for caching purposes and must never be set // current. if (f && f->implicit()) return; - + m_curfile = f; - - if (g_win && f) { - // A ton of stuff needs to be updated + + if (g_win && f) + { // A ton of stuff needs to be updated g_win->clearSelection(); g_win->updateFileListItem (f); g_win->buildObjList(); @@ -1088,20 +1111,20 @@ g_win->R()->setFile (f); g_win->R()->resetAngles(); g_win->R()->repaint(); - + log ("Changed file to %1", f->getShortName()); } } // ============================================================================= // ----------------------------------------------------------------------------- -int LDFile::countExplicitFiles() { - int count = 0; - - for (LDFile* f : g_loadedFiles) +int LDFile::countExplicitFiles() +{ int count = 0; + +for (LDFile * f : g_loadedFiles) if (f->implicit() == false) count++; - + return count; } @@ -1109,8 +1132,8 @@ // This little beauty closes the initial file that was open at first when opening // a new file over it. // ----------------------------------------------------------------------------- -void LDFile::closeInitialFile() { - if ( +void LDFile::closeInitialFile() +{ if ( countExplicitFiles() == 2 && g_loadedFiles[0]->name() == "" && !g_loadedFiles[0]->hasUnsavedChanges() @@ -1120,13 +1143,12 @@ // ============================================================================= // ----------------------------------------------------------------------------- -void loadLogoedStuds() { - print ("Loading logoed studs...\n"); - +void loadLogoedStuds() +{ print ("Loading logoed studs...\n"); + delete g_logoedStud; delete g_logoedStud2; - + g_logoedStud = openDATFile ("stud-logo.dat", true); g_logoedStud2 = openDATFile ("stud2-logo.dat", true); } -#include "moc_file.cpp"