diff --git a/src/AppleEvents.cc b/src/AppleEvents.cc index 1fb4f999..3102792c 100644 --- a/src/AppleEvents.cc +++ b/src/AppleEvents.cc @@ -15,7 +15,7 @@ OSErr eventHandler(const AppleEvent *, AppleEvent *, SRefCon ) if (mainwin) break; } if (mainwin) { - mainwin->actionReloadCompile(); + mainwin->actionReloadRenderCSG(); } return noErr; } diff --git a/src/CSGTermEvaluator.cc b/src/CSGTermEvaluator.cc index a6b654c2..71cf1497 100644 --- a/src/CSGTermEvaluator.cc +++ b/src/CSGTermEvaluator.cc @@ -115,6 +115,7 @@ Response CSGTermEvaluator::visit(State &state, const AbstractPolyNode &node) if (ps) { t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background, ps, node.modinst, node); + node.progress_report(); } } this->stored_term[node.index()] = t1; @@ -178,6 +179,7 @@ Response CSGTermEvaluator::visit(State &state, const RenderNode &node) shared_ptr ps; if (this->psevaluator) { ps = this->psevaluator->getPolySet(node, true); + node.progress_report(); } if (ps) { t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background, @@ -201,6 +203,7 @@ Response CSGTermEvaluator::visit(State &state, const CgaladvNode &node) if (ps) { t1 = evaluate_csg_term_from_ps(state, this->highlights, this->background, ps, node.modinst, node); + node.progress_report(); } this->stored_term[node.index()] = t1; addToParent(state, node); diff --git a/src/MainWindow.h b/src/MainWindow.h index 1dcffeb5..79e20809 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -28,6 +28,7 @@ public: QTimer *autoReloadTimer; std::string autoReloadId; + QTimer *waitAfterReloadTimer; ModuleContext top_ctx; FileModule *root_module; // Result of parsing @@ -76,8 +77,8 @@ private: void refreshDocument(); void updateTemporalVariables(); bool fileChangedOnDisk(); - bool compileTopLevelDocument(bool reload); - bool compile(bool reload, bool procevents); + void compileTopLevelDocument(); + void compile(bool reload, bool forcedone = false); void compileCSG(bool procevents); bool maybeSave(); bool checkEditorModified(); @@ -102,6 +103,10 @@ private slots: void actionReload(); void actionShowLibraryFolder(); + void instantiateRoot(); + void compileDone(bool didchange); + void compileEnded(); + private slots: void pasteViewportTranslation(); void pasteViewportRotation(); @@ -109,10 +114,13 @@ private slots: void preferences(); private slots: - void actionCompile(); + void actionRenderCSG(); + void csgRender(); + void csgReloadRender(); #ifdef ENABLE_CGAL void actionRenderCGAL(); void actionRenderCGALDone(class CGAL_Nef_polyhedron *); + void cgalRender(); #endif void actionDisplayAST(); void actionDisplayCSGTree(); @@ -131,6 +139,7 @@ public: void clearCurrentOutput(); public slots: + void actionReloadRenderCSG(); #ifdef ENABLE_OPENCSG void viewModeOpenCSG(); #endif @@ -163,13 +172,16 @@ public slots: void helpManual(); void helpLibrary(); void quit(); - void actionReloadCompile(); void checkAutoReload(); + void waitAfterReload(); void autoReloadSet(bool); private: static void report_func(const class AbstractNode*, void *vp, int mark); + char const * afterCompileSlot; + bool procevents; + class ProgressWidget *progresswidget; class CGALWorker *cgalworker; QMutex consolemutex; diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc index 99c0b206..de9af010 100644 --- a/src/ModuleCache.cc +++ b/src/ModuleCache.cc @@ -53,7 +53,6 @@ FileModule *ModuleCache::evaluate(const std::string &filename) if (lib_mod) { if (this->entries[filename].cache_id == cache_id) { shouldCompile = false; - if (lib_mod->includesChanged()) { lib_mod = NULL; shouldCompile = true; diff --git a/src/lexer.l b/src/lexer.l index 0b8048fa..3dd3b6bf 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -100,7 +100,7 @@ E [Ee][+-]?{D}+ %% -include[ \t\r\n>]*"<" { BEGIN(cond_include); filepath = filename = "";} +include[ \t\r\n>]*"<" { BEGIN(cond_include); filepath = filename = ""; } { [^\t\r\n>]*"/" { filepath = yytext; } [^\t\r\n>/]+ { filename = yytext; } diff --git a/src/mainwin.cc b/src/mainwin.cc index 95f72420..3d50d6f0 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -206,9 +206,15 @@ MainWindow::MainWindow(const QString &filename) autoReloadTimer = new QTimer(this); autoReloadTimer->setSingleShot(false); + autoReloadTimer->setInterval(200); connect(autoReloadTimer, SIGNAL(timeout()), this, SLOT(checkAutoReload())); - connect(this->e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile())); + waitAfterReloadTimer = new QTimer(this); + waitAfterReloadTimer->setSingleShot(true); + waitAfterReloadTimer->setInterval(200); + connect(waitAfterReloadTimer, SIGNAL(timeout()), this, SLOT(waitAfterReload())); + + connect(this->e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionRenderCSG())); connect(this->e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps())); animate_panel->hide(); @@ -288,8 +294,8 @@ MainWindow::MainWindow(const QString &filename) // Design menu connect(this->designActionAutoReload, SIGNAL(toggled(bool)), this, SLOT(autoReloadSet(bool))); - connect(this->designActionReloadAndCompile, SIGNAL(triggered()), this, SLOT(actionReloadCompile())); - connect(this->designActionCompile, SIGNAL(triggered()), this, SLOT(actionCompile())); + connect(this->designActionReloadAndCompile, SIGNAL(triggered()), this, SLOT(actionReloadRenderCSG())); + connect(this->designActionCompile, SIGNAL(triggered()), this, SLOT(actionRenderCSG())); #ifdef ENABLE_CGAL connect(this->designActionCompileAndRender, SIGNAL(triggered()), this, SLOT(actionRenderCGAL())); #else @@ -580,7 +586,7 @@ void MainWindow::updateTVal() double fps = this->e_fps->text().toDouble(&fps_ok); if (fps_ok) { if (fps <= 0) { - actionCompile(); + actionRenderCSG(); } else { double s = this->e_fsteps->text().toDouble(); double t = this->e_tval->text().toDouble() + 1/s; @@ -612,14 +618,96 @@ void MainWindow::refreshDocument() } /*! - Parse and evaluate the design => this->root_node - - Returns true if something was compiled, false if nothing was changed - and the root_node was left untouched. + compiles the design. Calls compileDone() if anything was compiled */ -bool MainWindow::compile(bool reload, bool procevents) +void MainWindow::compile(bool reload, bool forcedone) { - if (!compileTopLevelDocument(reload)) return false; + bool shouldcompiletoplevel = false; + bool didcompile = false; + + // Reload checks the timestamp of the toplevel file and refreshes if necessary, + if (reload) { + // Refresh files if it has changed on disk + if (fileChangedOnDisk() && checkEditorModified()) { + shouldcompiletoplevel = true; + refreshDocument(); + } + // If the file hasn't changed, we might still need to compile it + // if we haven't yet compiled the current text. + else { + QString current_doc = editor->toPlainText(); + if (current_doc != last_compiled_doc) shouldcompiletoplevel = true; + } + } + else { + shouldcompiletoplevel = true; + } + + if (!shouldcompiletoplevel && this->root_module && this->root_module->includesChanged()) { + shouldcompiletoplevel = true; + } + + if (shouldcompiletoplevel) { + console->clear(); + compileTopLevelDocument(); + didcompile = true; + } + + if (this->root_module) { + if (this->root_module->handleDependencies()) { + PRINTB("Module cache size: %d modules", ModuleCache::instance()->size()); + didcompile = true; + } + } + + // If we're auto-reloading, listen for a cascade of changes by starting a timer + // if something changed _and_ there are any external dependencies + if (reload && didcompile && this->root_module) { + if (this->root_module->hasIncludes() || + this->root_module->usesLibraries()) { + this->waitAfterReloadTimer->start(); + return; + } + } + compileDone(didcompile | forcedone); +} + +void MainWindow::waitAfterReload() +{ + if (this->root_module->handleDependencies()) { + this->waitAfterReloadTimer->start(); + return; + } + else { + compile(true, true); // In case file itself or top-level includes changed during dependency updates + } +} + +void MainWindow::compileDone(bool didchange) +{ + const char *callslot; + if (didchange) { + instantiateRoot(); + callslot = afterCompileSlot; + } + else { + callslot = "compileEnded"; + } + + this->procevents = false; + QMetaObject::invokeMethod(this, callslot); +} + +void MainWindow::compileEnded() +{ + clearCurrentOutput(); + GuiLocker::unlock(); + if (designActionAutoReload->isChecked()) autoReloadTimer->start(); +} + +void MainWindow::instantiateRoot() +{ + // Go on and instantiate root_node, then call the continuation slot // Invalidate renderers before we kill the CSG tree this->qglview->setRenderer(NULL); @@ -652,7 +740,7 @@ bool MainWindow::compile(bool reload, bool procevents) if (this->root_module) { // Evaluate CSG tree PRINT("Compiling design (CSG Tree generation)..."); - if (procevents) QApplication::processEvents(); + if (this->procevents) QApplication::processEvents(); AbstractNode::resetIndexCounter(); @@ -681,10 +769,8 @@ bool MainWindow::compile(bool reload, bool procevents) } else { PRINT("ERROR: Compilation failed!"); } - if (procevents) QApplication::processEvents(); + if (this->procevents) QApplication::processEvents(); } - - return true; } /*! @@ -1043,77 +1129,42 @@ bool MainWindow::fileChangedOnDisk() } /*! - If reload is true, does a timestamp check on the document and tries to reload it. - Otherwise, just reparses the current document and any dependencies, updates the - GUI accordingly and populates this->root_module. - Returns true if anything was compiled. */ -bool MainWindow::compileTopLevelDocument(bool reload) +void MainWindow::compileTopLevelDocument() { - bool shouldcompiletoplevel = !reload; - - if (this->root_module && this->root_module->includesChanged()) { - shouldcompiletoplevel = true; - } - - if (reload) { - // Refresh file if it has changed on disk - if (fileChangedOnDisk() && checkEditorModified()) { - shouldcompiletoplevel = true; - refreshDocument(); - } - // If the file hasn't changed, we might still need to compile it - // if we haven't yet compiled the current text. - else if (!editor->isContentModified()) { - QString current_doc = editor->toPlainText(); - if (current_doc != last_compiled_doc) shouldcompiletoplevel = true; - } - } + updateTemporalVariables(); - if (shouldcompiletoplevel) { - console->clear(); - - updateTemporalVariables(); - - this->last_compiled_doc = editor->toPlainText(); - std::string fulltext = - std::string(this->last_compiled_doc.toLocal8Bit().constData()) + - "\n" + commandline_commands; - - delete this->root_module; - this->root_module = NULL; - - this->root_module = parse(fulltext.c_str(), - this->fileName.isEmpty() ? - "" : - QFileInfo(this->fileName).absolutePath().toLocal8Bit(), - false); - - if (!animate_panel->isVisible()) { - highlighter->unhighlightLastError(); - if (!this->root_module) { - QTextCursor cursor = editor->textCursor(); - cursor.setPosition(parser_error_pos); - editor->setTextCursor(cursor); - highlighter->highlightError( parser_error_pos ); - } + this->last_compiled_doc = editor->toPlainText(); + std::string fulltext = + std::string(this->last_compiled_doc.toLocal8Bit().constData()) + + "\n" + commandline_commands; + + delete this->root_module; + this->root_module = NULL; + + this->root_module = parse(fulltext.c_str(), + this->fileName.isEmpty() ? + "" : + QFileInfo(this->fileName).absolutePath().toLocal8Bit(), + false); + + if (!animate_panel->isVisible()) { + highlighter->unhighlightLastError(); + if (!this->root_module) { + QTextCursor cursor = editor->textCursor(); + cursor.setPosition(parser_error_pos); + editor->setTextCursor(cursor); + highlighter->highlightError( parser_error_pos ); } - } - - bool changed = shouldcompiletoplevel; - if (this->root_module) { - changed |= this->root_module->handleDependencies(); - if (changed) PRINTB("Module cache size: %d modules", ModuleCache::instance()->size()); - } - - return changed; } void MainWindow::checkAutoReload() { - if (!this->fileName.isEmpty()) actionReloadCompile(); + if (!this->fileName.isEmpty()) { + actionReloadRenderCSG(); + } } void MainWindow::autoReloadSet(bool on) @@ -1143,15 +1194,22 @@ bool MainWindow::checkEditorModified() return true; } -void MainWindow::actionReloadCompile() +void MainWindow::actionReloadRenderCSG() { if (GuiLocker::isLocked()) return; - GuiLocker lock; + GuiLocker::lock(); + autoReloadTimer->stop(); setCurrentOutput(); // PRINT("Parsing design (AST generation)..."); // QApplication::processEvents(); - if (!compile(true, true)) return; + this->afterCompileSlot = "csgReloadRender"; + this->procevents = true; + compile(true); +} + +void MainWindow::csgReloadRender() +{ if (this->root_node) compileCSG(true); // Go to non-CGAL view mode @@ -1165,20 +1223,25 @@ void MainWindow::actionReloadCompile() viewModeThrownTogether(); #endif } - - clearCurrentOutput(); + compileEnded(); } -void MainWindow::actionCompile() +void MainWindow::actionRenderCSG() { if (GuiLocker::isLocked()) return; - GuiLocker lock; + GuiLocker::lock(); + autoReloadTimer->stop(); setCurrentOutput(); - console->clear(); PRINT("Parsing design (AST generation)..."); QApplication::processEvents(); - compile(false, !viewActionAnimate->isChecked()); + this->afterCompileSlot = "csgRender"; + this->procevents = !viewActionAnimate->isChecked(); + compile(false); +} + +void MainWindow::csgRender() +{ if (this->root_node) compileCSG(!viewActionAnimate->isChecked()); // Go to non-CGAL view mode @@ -1202,7 +1265,7 @@ void MainWindow::actionCompile() img.save(filename, "PNG"); } - clearCurrentOutput(); + compileEnded(); } #ifdef ENABLE_CGAL @@ -1210,15 +1273,19 @@ void MainWindow::actionCompile() void MainWindow::actionRenderCGAL() { if (GuiLocker::isLocked()) return; - GuiLocker lock; - + GuiLocker::lock(); + autoReloadTimer->stop(); setCurrentOutput(); - console->clear(); PRINT("Parsing design (AST generation)..."); QApplication::processEvents(); - compile(false, true); + this->afterCompileSlot = "cgalRender"; + this->procevents = true; + compile(false); +} +void MainWindow::cgalRender() +{ if (!this->root_module || !this->root_node) { return; } @@ -1238,7 +1305,6 @@ void MainWindow::actionRenderCGAL() progress_report_prep(this->root_node, report_func, this); - GuiLocker::lock(); // Will be unlocked in actionRenderCGALDone() this->cgalworker->start(this->tree); } @@ -1301,9 +1367,7 @@ void MainWindow::actionRenderCGALDone(CGAL_Nef_polyhedron *root_N) this->statusBar()->removeWidget(this->progresswidget); delete this->progresswidget; this->progresswidget = NULL; - clearCurrentOutput(); - - GuiLocker::unlock(); + compileEnded(); } #endif /* ENABLE_CGAL */ @@ -1616,7 +1680,7 @@ void MainWindow::viewModeAnimate() { if (viewActionAnimate->isChecked()) { animate_panel->show(); - actionCompile(); + actionRenderCSG(); updatedFps(); } else { animate_panel->hide(); diff --git a/src/module.cc b/src/module.cc index 8fb8506b..9c0272dd 100644 --- a/src/module.cc +++ b/src/module.cc @@ -264,6 +264,10 @@ bool FileModule::handleDependencies() // as it will have a relative path. // Iterating manually since we want to modify the container while iterating +<<<<<<< HEAD +======= + std::vector > modified_modules; +>>>>>>> origin/issue181 FileModule::ModuleContainer::iterator iter = this->usedlibs.begin(); while (iter != this->usedlibs.end()) { FileModule::ModuleContainer::iterator curr = iter++; @@ -293,6 +297,9 @@ bool FileModule::handleDependencies() } } } + BOOST_FOREACH(const FileModule::ModuleContainer::value_type &mod, modified_modules) { + this->usedlibs[mod.first] = mod.second; + } this->is_handling_dependencies = false; return changed; diff --git a/src/module.h b/src/module.h index 6027fe66..b5c58afe 100644 --- a/src/module.h +++ b/src/module.h @@ -107,6 +107,7 @@ private: }; bool include_modified(const IncludeFile &inc) const; + typedef boost::unordered_map IncludeContainer; IncludeContainer includes; bool is_handling_dependencies; diff --git a/src/parsersettings.cc b/src/parsersettings.cc index ab93b789..5ad30e15 100644 --- a/src/parsersettings.cc +++ b/src/parsersettings.cc @@ -35,19 +35,19 @@ fs::path search_libs(const fs::path &localpath) static bool check_valid(const fs::path &p, const std::vector *openfilenames) { if (p.empty()) { -// PRINTB("WARNING: File path is blank: %s",p); + //PRINTB("WARNING: File path is blank: %s",p); return false; } if (!p.has_parent_path()) { -// PRINTB("WARNING: No parent path: %s",p); + //PRINTB("WARNING: No parent path: %s",p); return false; } if (!fs::exists(p)) { -// PRINTB("WARNING: File not found: %s",p); + //PRINTB("WARNING: File not found: %s",p); return false; } if (fs::is_directory(p)) { -// PRINTB("WARNING: %s invalid - points to a directory",p); + //PRINTB("WARNING: %s invalid - points to a directory",p); return false; } std::string fullname = boosty::stringy(p);