diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/AboutDialog.html | 16 | ||||
-rw-r--r-- | src/MainWindow.h | 1 | ||||
-rw-r--r-- | src/ModuleCache.cc | 25 | ||||
-rw-r--r-- | src/ProgressWidget.cc | 2 | ||||
-rw-r--r-- | src/ProgressWidget.ui | 3 | ||||
-rw-r--r-- | src/expr.cc | 2 | ||||
-rw-r--r-- | src/mainwin.cc | 82 | ||||
-rw-r--r-- | src/module.cc | 66 | ||||
-rw-r--r-- | src/module.h | 19 | ||||
-rw-r--r-- | src/parsersettings.cc | 13 |
10 files changed, 133 insertions, 96 deletions
diff --git a/src/AboutDialog.html b/src/AboutDialog.html index 005f61f..b5a5d7c 100644 --- a/src/AboutDialog.html +++ b/src/AboutDialog.html @@ -7,7 +7,8 @@ make to do your testing. --> <head> - <meta name="qrichtext" content="1" /> + <meta charset="UTF-8"/> + <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> </head> @@ -74,18 +75,23 @@ Please visit this link for a copy of the license: <a href="http://www.gnu.org/li <b>OpenSCAD Github Project members (public)</b> </p> -<lu> +<ul> <li><a href="https://github.com/kintel">Marius Kintel </a> <li><a href="http://clifford.at">Clifford Wolf</a> <li><a href="http://www.github.com/GilesBathgate">Giles Bathgate</a> <li><a href="https://github.com/brad">Brad Pitcher</a> <li><a href="https://github.com/donbright">Don Bright</a> -</lu> +</ul> <p> -<b><a href="http://www.debian.org">Debian</a> maintainer:</b> - <a href="http://christian.amsuess.com/">chrysn</a> +<b>Package Maintainers</b> </p> +<ul> +<li><a href="http://www.debian.org">Debian</a> maintainer:</b> + <a href="http://christian.amsuess.com/">chrysn</a></lu> +<li><a href="http://fedoraproject.org/">Fedora</a> maintainer:</b> + <a href="http://hroncok.cz/">Miro HronĨok</a></lu> +</ul> <p> <b>Patches</b> diff --git a/src/MainWindow.h b/src/MainWindow.h index bd32bdd..1dcffeb 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -76,7 +76,6 @@ private: void refreshDocument(); void updateTemporalVariables(); bool fileChangedOnDisk(); - bool includesChanged(); bool compileTopLevelDocument(bool reload); bool compile(bool reload, bool procevents); void compileCSG(bool procevents); diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc index 505015e..83f7b7d 100644 --- a/src/ModuleCache.cc +++ b/src/ModuleCache.cc @@ -30,34 +30,37 @@ ModuleCache *ModuleCache::inst = NULL; */ FileModule *ModuleCache::evaluate(const std::string &filename) { + bool shouldCompile = true; FileModule *lib_mod = NULL; // Create cache ID struct stat st; memset(&st, 0, sizeof(struct stat)); - stat(filename.c_str(), &st); + bool valid = (stat(filename.c_str(), &st) == 0); + // If file isn't there, just return and let the cache retain the old module + if (!valid) return NULL; + std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size); // Lookup in cache - if (this->entries.find(filename) != this->entries.end() && - this->entries[filename].cache_id == cache_id) { -#ifdef DEBUG -// Causes too much debug output -// PRINTB("Using cached library: %s (%s)", filename % cache_id); -#endif + if (this->entries.find(filename) != this->entries.end()) { lib_mod = &(*this->entries[filename].module); + if (this->entries[filename].cache_id == cache_id) { + shouldCompile = false; - BOOST_FOREACH(const FileModule::IncludeContainer::value_type &include, lib_mod->includes) { - if (lib_mod->include_modified(include.second)) { + if (lib_mod->includesChanged()) { lib_mod = NULL; - break; + shouldCompile = true; } } } + else { + shouldCompile = valid; + } // If cache lookup failed (non-existing or old timestamp), compile module - if (!lib_mod) { + if (shouldCompile) { #ifdef DEBUG if (this->entries.find(filename) != this->entries.end()) { PRINTB("Recompiling cached library: %s (%s)", filename % cache_id); diff --git a/src/ProgressWidget.cc b/src/ProgressWidget.cc index ce66405..7aa6a4a 100644 --- a/src/ProgressWidget.cc +++ b/src/ProgressWidget.cc @@ -5,7 +5,7 @@ ProgressWidget::ProgressWidget(QWidget *parent) :QWidget(parent) { setupUi(this); - setRange(0, 100); + setRange(0, 1000); setValue(0); this->wascanceled = false; this->starttime.start(); diff --git a/src/ProgressWidget.ui b/src/ProgressWidget.ui index 895586c..24aefdb 100644 --- a/src/ProgressWidget.ui +++ b/src/ProgressWidget.ui @@ -22,6 +22,9 @@ </property> <item> <widget class="QProgressBar" name="progressBar"> + <property name="maximum"> + <number>1000</number> + </property> <property name="value"> <number>24</number> </property> diff --git a/src/expr.cc b/src/expr.cc index 46d606f..2e069f0 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -69,7 +69,7 @@ public: expr.recursioncount++; } ~FuncRecursionGuard() { expr.recursioncount--; } - bool recursion_detected() const { return (expr.recursioncount > 100); } + bool recursion_detected() const { return (expr.recursioncount > 1000); } private: const Expression &expr; }; diff --git a/src/mainwin.cc b/src/mainwin.cc index ac75269..946bc2a 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -122,7 +122,7 @@ static char helptitle[] = #endif "\nhttp://www.openscad.org\n\n"; static char copyrighttext[] = - "Copyright (C) 2009-2013 Marius Kintel <marius@kintel.net> and Clifford Wolf <clifford@clifford.at>\n" + "Copyright (C) 2009-2013 The OpenSCAD Developers\n" "\n" "This program is free software; you can redistribute it and/or modify " "it under the terms of the GNU General Public License as published by " @@ -462,12 +462,11 @@ void MainWindow::showProgress() void MainWindow::report_func(const class AbstractNode*, void *vp, int mark) { MainWindow *thisp = static_cast<MainWindow*>(vp); - int v = (int)((mark*100.0) / progress_report_count); - int percent = v < 100 ? v : 99; - - if (percent > thisp->progresswidget->value()) { + int v = (int)((mark*1000.0) / progress_report_count); + int permille = v < 1000 ? v : 999; + if (permille > thisp->progresswidget->value()) { QMetaObject::invokeMethod(thisp->progresswidget, "setValue", Qt::QueuedConnection, - Q_ARG(int, percent)); + Q_ARG(int, permille)); QApplication::processEvents(); } @@ -493,12 +492,12 @@ void MainWindow::openFile(const QString &new_filename) { QString actual_filename = new_filename; + QFileInfo fi(new_filename); + if (fi.suffix().toLower().contains(QRegExp("^(stl|off|dxf)$"))) { + actual_filename = QString(); + } #ifdef ENABLE_MDI if (!editor->toPlainText().isEmpty()) { - QFileInfo fi(new_filename); - if (fi.suffix().toLower().contains(QRegExp("^(stl|off|dxf)$"))) { - actual_filename = QString(); - } new MainWindow(actual_filename); clearCurrentOutput(); return; @@ -506,6 +505,7 @@ MainWindow::openFile(const QString &new_filename) #endif setFileName(actual_filename); + fileChangedOnDisk(); // force cached autoReloadId to update refreshDocument(); updateRecentFiles(); if (actual_filename.isEmpty()) { @@ -691,8 +691,7 @@ void MainWindow::compileCSG(bool procevents) { assert(this->root_node); PRINT("Compiling design (CSG Products generation)..."); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); // Main CSG evaluation QTime t; @@ -710,16 +709,16 @@ void MainWindow::compileCSG(bool procevents) PolySetEvaluator psevaluator(this->tree); #endif CSGTermEvaluator csgrenderer(this->tree, &psevaluator); + if (procevents) QApplication::processEvents(); this->root_raw_term = csgrenderer.evaluateCSGTerm(*root_node, highlight_terms, background_terms); if (!root_raw_term) { PRINT("ERROR: CSG generation failed! (no top level object found)"); - if (procevents) - QApplication::processEvents(); } PolySetCache::instance()->print(); #ifdef ENABLE_CGAL CGALCache::instance()->print(); #endif + if (procevents) QApplication::processEvents(); } catch (const ProgressCancelException &e) { PRINT("CSG generation cancelled."); @@ -731,8 +730,7 @@ void MainWindow::compileCSG(bool procevents) if (root_raw_term) { PRINT("Compiling design (CSG Products normalization)..."); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); size_t normalizelimit = 2 * Preferences::inst()->getValue("advanced/openCSGLimit").toUInt(); CSGTermNormalizer normalizer(normalizelimit); @@ -744,15 +742,13 @@ void MainWindow::compileCSG(bool procevents) else { this->root_chain = NULL; PRINT("WARNING: CSG normalization resulted in an empty tree"); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); } if (highlight_terms.size() > 0) { PRINTB("Compiling highlights (%d CSG Trees)...", highlight_terms.size()); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); highlights_chain = new CSGChain(); for (unsigned int i = 0; i < highlight_terms.size(); i++) { @@ -764,8 +760,7 @@ void MainWindow::compileCSG(bool procevents) if (background_terms.size() > 0) { PRINTB("Compiling background (%d CSG Trees)...", background_terms.size()); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); background_chain = new CSGChain(); for (unsigned int i = 0; i < background_terms.size(); i++) { @@ -794,8 +789,7 @@ void MainWindow::compileCSG(bool procevents) PRINT("CSG generation finished."); int s = t.elapsed() / 1000; PRINTB("Total rendering time: %d hours, %d minutes, %d seconds", (s / (60*60)) % ((s / 60) % 60) % (s % 60)); - if (procevents) - QApplication::processEvents(); + if (procevents) QApplication::processEvents(); } } @@ -973,7 +967,10 @@ void MainWindow::actionShowLibraryFolder() void MainWindow::actionReload() { - if (checkEditorModified()) refreshDocument(); + if (checkEditorModified()) { + fileChangedOnDisk(); // force cached autoReloadId to update + refreshDocument(); + } } void MainWindow::hideEditor() @@ -1027,7 +1024,10 @@ bool MainWindow::fileChangedOnDisk() if (!this->fileName.isEmpty()) { struct stat st; memset(&st, 0, sizeof(struct stat)); - stat(this->fileName.toLocal8Bit(), &st); + bool valid = (stat(this->fileName.toLocal8Bit(), &st) == 0); + // If file isn't there, just return and use current editor text + if (!valid) return false; + std::string newid = str(boost::format("%x.%x") % st.st_mtime % st.st_size); if (newid != this->autoReloadId) { @@ -1038,16 +1038,6 @@ bool MainWindow::fileChangedOnDisk() return false; } -bool MainWindow::includesChanged() -{ - if (this->root_module) { - BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, this->root_module->includes) { - if (this->root_module->include_modified(item.second)) return true; - } - } - return false; -} - /*! 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 @@ -1059,11 +1049,22 @@ bool MainWindow::compileTopLevelDocument(bool reload) { bool shouldcompiletoplevel = !reload; - if (includesChanged()) shouldcompiletoplevel = true; - - if (reload && fileChangedOnDisk() && checkEditorModified()) { + if (this->root_module && this->root_module->includesChanged()) { shouldcompiletoplevel = true; - refreshDocument(); + } + + 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; + } } if (shouldcompiletoplevel) { @@ -1116,7 +1117,6 @@ void MainWindow::autoReloadSet(bool on) QSettings settings; settings.setValue("design/autoReload",designActionAutoReload->isChecked()); if (on) { - autoReloadId = ""; autoReloadTimer->start(200); } else { autoReloadTimer->stop(); diff --git a/src/module.cc b/src/module.cc index cc0f99c..046d0c4 100644 --- a/src/module.cc +++ b/src/module.cc @@ -166,7 +166,7 @@ public: ~ModRecursionGuard() { inst.recursioncount--; } - bool recursion_detected() const { return (inst.recursioncount > 100); } + bool recursion_detected() const { return (inst.recursioncount > 1000); } private: const ModuleInstantiation &inst; }; @@ -226,6 +226,28 @@ void FileModule::registerInclude(const std::string &localpath, this->includes[localpath] = inc; } +bool FileModule::includesChanged() const +{ + BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, this->includes) { + if (include_modified(item.second)) return true; + } + return false; +} + +bool FileModule::include_modified(const IncludeFile &inc) const +{ + struct stat st; + memset(&st, 0, sizeof(struct stat)); + + fs::path fullpath = find_valid_path(this->path, inc.filename); + bool valid = !fullpath.empty() ? (stat(boosty::stringy(fullpath).c_str(), &st) == 0) : false; + + if (valid && !inc.valid) return true; // Detect appearance of file but not removal + if (valid && st.st_mtime > inc.mtime) return true; + + return false; +} + /*! Check if any dependencies have been modified and recompile them. Returns true if anything was recompiled. @@ -236,35 +258,49 @@ bool FileModule::handleDependencies() this->is_handling_dependencies = true; bool changed = false; - // Iterating manually since we want to modify the container while iterating - // If a lib in usedlibs was previously missing, we need to relocate it // by searching the applicable paths. We can identify a previously missing module // as it will have a relative path. + + // Iterating manually since we want to modify the container while iterating + std::vector<std::pair<std::string, FileModule*> > modified_modules; FileModule::ModuleContainer::iterator iter = this->usedlibs.begin(); while (iter != this->usedlibs.end()) { FileModule::ModuleContainer::iterator curr = iter++; FileModule *oldmodule = curr->second; + bool wasmissing = false; // Get an absolute filename for the module std::string filename = curr->first; if (!boosty::is_absolute(filename)) { + wasmissing = true; fs::path fullpath = find_valid_path(this->path, filename); if (!fullpath.empty()) filename = boosty::stringy(fullpath); } - curr->second = ModuleCache::instance()->evaluate(filename); - if (curr->second != oldmodule) { + FileModule *newmodule = ModuleCache::instance()->evaluate(filename); + // Detect appearance but not removal of files + if (newmodule && oldmodule != newmodule) { changed = true; #ifdef DEBUG - PRINTB_NOCACHE(" %s: %p", filename % curr->second); + PRINTB_NOCACHE(" %s: %p", filename % newmodule); #endif } - if (!curr->second) { - PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename); + if (newmodule) { + modified_modules.push_back(std::make_pair(filename, newmodule)); + this->usedlibs.erase(curr); + } + else { + // Only print warning if we're not part of an automatic reload + if (!oldmodule && !wasmissing) { + PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename); + } } } + BOOST_FOREACH(const FileModule::ModuleContainer::value_type &mod, modified_modules) { + this->usedlibs[mod.first] = mod.second; + } this->is_handling_dependencies = false; return changed; @@ -286,17 +322,3 @@ AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiat return node; } - -bool FileModule::include_modified(IncludeFile inc) -{ - struct stat st; - memset(&st, 0, sizeof(struct stat)); - - fs::path fullpath = find_valid_path(this->path, inc.filename); - bool valid = !fullpath.empty() ? (stat(boosty::stringy(fullpath).c_str(), &st) == 0) : false; - - if (valid != inc.valid) return true; - if (valid && st.st_mtime > inc.mtime) return true; - - return false; -} diff --git a/src/module.h b/src/module.h index 96e5649..e28677a 100644 --- a/src/module.h +++ b/src/module.h @@ -78,12 +78,6 @@ public: LocalScope scope; }; -struct IncludeFile { - std::string filename; - bool valid; - time_t mtime; -}; - // FIXME: A FileModule doesn't have definition arguments, so we shouldn't really // inherit from a Module class FileModule : public Module @@ -95,15 +89,24 @@ public: void setModulePath(const std::string &path) { this->path = path; } const std::string &modulePath() const { return this->path; } void registerInclude(const std::string &localpath, const std::string &fullpath); + bool includesChanged() const; bool handleDependencies(); virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const; + bool hasIncludes() const { return !this->includes.empty(); } + bool usesLibraries() const { return !this->usedlibs.empty(); } typedef boost::unordered_map<std::string, class FileModule*> ModuleContainer; ModuleContainer usedlibs; +private: + struct IncludeFile { + std::string filename; + bool valid; + time_t mtime; + }; + + bool include_modified(const IncludeFile &inc) const; typedef boost::unordered_map<std::string, struct IncludeFile> IncludeContainer; IncludeContainer includes; - bool include_modified(struct IncludeFile inc); -private: bool is_handling_dependencies; std::string path; }; diff --git a/src/parsersettings.cc b/src/parsersettings.cc index 8db33a8..ab93b78 100644 --- a/src/parsersettings.cc +++ b/src/parsersettings.cc @@ -30,23 +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" static bool check_valid(const fs::path &p, const std::vector<std::string> *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); - // searched === +// 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); @@ -54,7 +55,7 @@ static bool check_valid(const fs::path &p, const std::vector<std::string> *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; } } |