From 33e873d8dae644b28dd3d6cbcf04450d6675fafd Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 28 May 2013 01:25:03 -0400 Subject: Changed compile GUI logic to properly handler automatic reload on cascading changes diff --git a/src/AppleEvents.cc b/src/AppleEvents.cc index 1fb4f99..3102792 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/MainWindow.h b/src/MainWindow.h index bd32bdd..fc64137 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 @@ -77,8 +78,8 @@ private: void updateTemporalVariables(); bool fileChangedOnDisk(); bool includesChanged(); - 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(); @@ -103,6 +104,10 @@ private slots: void actionReload(); void actionShowLibraryFolder(); + void instantiateRoot(); + void compileDone(bool didchange); + void compileEnded(); + private slots: void pasteViewportTranslation(); void pasteViewportRotation(); @@ -110,10 +115,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(); @@ -132,6 +140,7 @@ public: void clearCurrentOutput(); public slots: + void actionReloadRenderCSG(); #ifdef ENABLE_OPENCSG void viewModeOpenCSG(); #endif @@ -164,13 +173,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/lexer.l b/src/lexer.l index 0b8048f..3dd3b6b 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 ac75269..52b432c 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -205,10 +205,16 @@ MainWindow::MainWindow(const QString &filename) connect(animate_timer, SIGNAL(timeout()), this, SLOT(updateTVal())); autoReloadTimer = new QTimer(this); - autoReloadTimer->setSingleShot(false); + autoReloadTimer->setSingleShot(true); + 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,88 @@ 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) +{ + bool shouldcompiletoplevel = false; + bool didcompile = false; + + // Reload checks the timestamp of the toplevel file and refreshes if necessary, + if (reload) { + if (fileChangedOnDisk() && checkEditorModified()) { + shouldcompiletoplevel = true; + refreshDocument(); + } + } + else { + shouldcompiletoplevel = true; + } + + if (!shouldcompiletoplevel && includesChanged()) { + shouldcompiletoplevel = true; + } + + if (shouldcompiletoplevel) { + 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->includes.size() > 0 || + this->root_module->usedlibs.size() > 0) { + 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() { - if (!compileTopLevelDocument(reload)) return false; + // 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 +732,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(); this->root_inst = ModuleInstantiation("group"); @@ -677,10 +757,8 @@ bool MainWindow::compile(bool reload, bool procevents) } else { PRINT("ERROR: Compilation failed!"); } - if (procevents) QApplication::processEvents(); + if (this->procevents) QApplication::processEvents(); } - - return true; } /*! @@ -1049,66 +1127,44 @@ bool MainWindow::includesChanged() } /*! - 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 (includesChanged()) shouldcompiletoplevel = true; - - if (reload && fileChangedOnDisk() && checkEditorModified()) { - shouldcompiletoplevel = true; - refreshDocument(); - } + console->clear(); - 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 ); - } + 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 ); } - - } - - 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) @@ -1117,7 +1173,7 @@ void MainWindow::autoReloadSet(bool on) settings.setValue("design/autoReload",designActionAutoReload->isChecked()); if (on) { autoReloadId = ""; - autoReloadTimer->start(200); + autoReloadTimer->start(); } else { autoReloadTimer->stop(); } @@ -1139,15 +1195,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 @@ -1161,20 +1224,26 @@ 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 @@ -1198,7 +1267,7 @@ void MainWindow::actionCompile() img.save(filename, "PNG"); } - clearCurrentOutput(); + compileEnded(); } #ifdef ENABLE_CGAL @@ -1206,15 +1275,20 @@ 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; } @@ -1234,7 +1308,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); } @@ -1297,9 +1370,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 */ @@ -1612,7 +1683,7 @@ void MainWindow::viewModeAnimate() { if (viewActionAnimate->isChecked()) { animate_panel->show(); - actionCompile(); + actionRenderCSG(); updatedFps(); } else { animate_panel->hide(); diff --git a/src/parsersettings.cc b/src/parsersettings.cc index ab93b78..04c20ed 100644 --- a/src/parsersettings.cc +++ b/src/parsersettings.cc @@ -30,24 +30,24 @@ fs::path search_libs(const fs::path &localpath) } // files must be 'ordinary' - they must exist and be non-directories -// FIXME: We cannot print any output here since these function is called periodically -// from "Automatic reload and compile" +// FIXME: Don't print anything from this function as it's called repeatedly +// for the automatic reload function (FileModule::include_modified()) 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); @@ -55,7 +55,7 @@ static bool check_valid(const fs::path &p, const std::vector *openf if (openfilenames) { BOOST_FOREACH(const std::string &s, *openfilenames) { if (s == fullname) { -// PRINTB("WARNING: circular include file %s", fullname); + //PRINTB("WARNING: circular include file %s", fullname); return false; } } -- cgit v0.10.1