diff --git a/src/App/Application.cpp b/src/App/Application.cpp index b4434ff359..59fc634f09 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -381,12 +381,19 @@ void Application::renameDocument(const char *OldName, const char *NewName) #endif } -Document* Application::newDocument(const char * Name, const char * UserName, bool createView) +Document* Application::newDocument(const char * Name, const char * UserName, bool createView, bool tempDoc) { // get a valid name anyway! if (!Name || Name[0] == '\0') Name = "Unnamed"; - string name = getUniqueDocumentName(Name); + string name = getUniqueDocumentName(Name, tempDoc); + + // return the temporary document if it exists + if (tempDoc) { + auto it = DocMap.find(name); + if (it != DocMap.end() && it->second->testStatus(Document::TempDoc)) + return it->second; + } std::string userName; if (UserName && UserName[0] != '\0') { @@ -407,12 +414,16 @@ Document* Application::newDocument(const char * Name, const char * UserName, boo // create the FreeCAD document std::unique_ptr newDoc(new Document(name.c_str())); + if (tempDoc) + newDoc->setStatus(Document::TempDoc, true); + + auto oldActiveDoc = _pActiveDoc; + auto doc = newDoc.get(); // add the document to the internal list DocMap[name] = newDoc.release(); // now owned by the Application _pActiveDoc = DocMap[name]; - // connect the signals to the application for the new document _pActiveDoc->signalBeforeChange.connect(boost::bind(&App::Application::slotBeforeChangeDocument, this, bp::_1, bp::_2)); _pActiveDoc->signalChanged.connect(boost::bind(&App::Application::slotChangedDocument, this, bp::_1, bp::_2)); @@ -442,12 +453,16 @@ Document* Application::newDocument(const char * Name, const char * UserName, boo Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"),active); } - signalNewDocument(*_pActiveDoc,createView); + signalNewDocument(*_pActiveDoc, createView); // set the UserName after notifying all observers _pActiveDoc->Label.setValue(userName); - return _pActiveDoc; + // set the old document active again if the new is temporary + if (tempDoc && oldActiveDoc) + setActiveDocument(oldActiveDoc); + + return doc; } bool Application::closeDocument(const char* name) @@ -513,7 +528,7 @@ std::vector Application::getDocuments() const return docs; } -std::string Application::getUniqueDocumentName(const char *Name) const +std::string Application::getUniqueDocumentName(const char *Name, bool tempDoc) const { if (!Name || *Name == '\0') return std::string(); @@ -523,7 +538,7 @@ std::string Application::getUniqueDocumentName(const char *Name) const std::map::const_iterator pos; pos = DocMap.find(CleanName); - if (pos == DocMap.end()) { + if (pos == DocMap.end() || (tempDoc && pos->second->testStatus(Document::TempDoc))) { // if not, name is OK return CleanName; } @@ -531,7 +546,8 @@ std::string Application::getUniqueDocumentName(const char *Name) const std::vector names; names.reserve(DocMap.size()); for (pos = DocMap.begin();pos != DocMap.end();++pos) { - names.push_back(pos->first); + if (!tempDoc || !pos->second->testStatus(Document::TempDoc)) + names.push_back(pos->first); } return Base::Tools::getUniqueName(CleanName, names); } diff --git a/src/App/Application.h b/src/App/Application.h index 642cfce019..eccf6962cb 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -86,11 +86,12 @@ public: * The second name is a UTF8 name of any kind. It's that name normally shown to * the user and stored in the App::Document::Name property. */ - App::Document* newDocument(const char * Name=0l, const char * UserName=0l, bool createView=true); + App::Document* newDocument(const char * Name=0l, const char * UserName=0l, + bool createView=true, bool tempDoc=false); /// Closes the document \a name and removes it from the application. bool closeDocument(const char* name); /// find a unique document name - std::string getUniqueDocumentName(const char *Name) const; + std::string getUniqueDocumentName(const char *Name, bool tempDoc=false) const; /// Open an existing document from a file App::Document* openDocument(const char * FileName=0l, bool createView=true); /** Open multiple documents diff --git a/src/App/ApplicationPy.cpp b/src/App/ApplicationPy.cpp index 8373b18f34..a03b881c91 100644 --- a/src/App/ApplicationPy.cpp +++ b/src/App/ApplicationPy.cpp @@ -119,11 +119,12 @@ PyMethodDef Application::Methods[] = { // "saveDocument(string) -- Save the document to a file."}, // {"saveDocumentAs", (PyCFunction) Application::sSaveDocumentAs, METH_VARARGS}, {"newDocument", reinterpret_cast(reinterpret_cast( Application::sNewDocument )), METH_VARARGS|METH_KEYWORDS, - "newDocument(name, label=None, hidden=False) -> object\n" + "newDocument(name, label=None, hidden=False, tmp=False) -> object\n" "Create a new document with a given name.\n\n" "name: unique document name which is checked automatically.\n" "label: optional user changeable label for the document.\n" - "hidden: whether to hide document 3D view."}, + "hidden: whether to hide document 3D view.\n" + "temp: mark the document as temporary so that it will not be saved"}, {"closeDocument", (PyCFunction) Application::sCloseDocument, METH_VARARGS, "closeDocument(string) -> None\n\n" "Close the document with a given name."}, @@ -269,13 +270,16 @@ PyObject* Application::sNewDocument(PyObject * /*self*/, PyObject *args, PyObjec char *docName = 0; char *usrName = 0; PyObject *hidden = Py_False; - static char *kwlist[] = {"name","label","hidden",0}; - if (!PyArg_ParseTupleAndKeywords(args, kwd, "|etetO", kwlist, + PyObject *temp = Py_False; + static char *kwlist[] = {"name","label","hidden","temp",0}; + if (!PyArg_ParseTupleAndKeywords(args, kwd, "|etetOO", kwlist, "utf-8", &docName, "utf-8", &usrName, &hidden)) return NULL; PY_TRY { - App::Document* doc = GetApplication().newDocument(docName, usrName,!PyObject_IsTrue(hidden)); + App::Document* doc = GetApplication().newDocument(docName, usrName, + !PyObject_IsTrue(hidden), + PyObject_IsTrue(temp)); PyMem_Free(docName); PyMem_Free(usrName); return doc->getPyObject(); diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 43d98af9ad..74b65748be 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -2894,6 +2894,12 @@ const char* Document::getProgramVersion() const return d->programVersion.c_str(); } +const char* Document::getFileName() const +{ + return testStatus(TempDoc) ? TransientDir.getValue() + : FileName.getValue(); +} + /// Remove all modifications. After this call The document becomes valid again. void Document::purgeTouched() { @@ -4241,7 +4247,7 @@ void Document::breakDependency(DocumentObject* pcObject, bool clear) } std::vector Document::copyObject( - const std::vector &objs, bool recursive) + const std::vector &objs, bool recursive, bool returnAll) { std::vector deps; if(!recursive) @@ -4249,10 +4255,11 @@ std::vector Document::copyObject( else deps = getDependencyList(objs,DepNoXLinked|DepSort); - if(!isSaved() && PropertyXLink::hasXLink(deps)) + if (!testStatus(TempDoc) && !isSaved() && PropertyXLink::hasXLink(deps)) { throw Base::RuntimeError( "Document must be saved at least once before link to external objects"); - + } + MergeDocuments md(this); // if not copying recursively then suppress possible warnings md.setVerbose(recursive); @@ -4291,7 +4298,7 @@ std::vector Document::copyObject( imported = md.importObjects(istr); } - if(imported.size()!=deps.size()) + if (returnAll || imported.size()!=deps.size()) return imported; std::unordered_map indices; diff --git a/src/App/Document.h b/src/App/Document.h index 5c38c1e56e..e8bb032437 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -73,6 +73,7 @@ public: Importing = 6, PartialDoc = 7, AllowPartialRecompute = 8, // allow recomputing editing object if SkipRecompute is set + TempDoc = 9, // Mark as temporary document without prompt for save }; /** @name Properties */ @@ -225,6 +226,13 @@ public: const char* getName() const; /// Get program version the project file was created with const char* getProgramVersion() const; + /** Returned filename + * + * For saved document, this will be the content stored in property + * 'Filename'. For unsaved temporary file, this will be the content of + * property 'TransientDir'. + */ + const char* getFileName() const; //@} virtual void Save (Base::Writer &writer) const override; @@ -269,10 +277,15 @@ public: * @param recursive: if true, then all objects this object depends on are * copied as well. By default \a recursive is false. * + * @param returnAll: if true, return all copied objects including those + * auto included by recursive searching. If false, then only return the + * copied object corresponding to the input objects. + * * @return Returns the list of objects copied. */ std::vector copyObject( - const std::vector &objs, bool recursive=false); + const std::vector &objs, + bool recursive=false, bool returnAll=false); /** Move an object from another document to this document * If \a recursive is true then all objects this object depends on * are moved as well. By default \a recursive is false. diff --git a/src/App/DocumentPy.xml b/src/App/DocumentPy.xml index c9ab6e5db4..c3dbdf7157 100644 --- a/src/App/DocumentPy.xml +++ b/src/App/DocumentPy.xml @@ -94,11 +94,13 @@ viewType (String): override the view provider type directly, only effective when -copyObject(object, with_dependencies=False) +copyObject(object, with_dependencies=False, return_all=False) Copy an object or objects from another document to this document. object: can either a single object or sequence of objects with_dependencies: if True, all internal dependent objects are copied too. +return_all: if True, return all copied objects, or else return only the copied + object corresponding to the input objects. @@ -328,6 +330,12 @@ sort: whether to topologically sort the return list + + + Check if this is a temporary document + + + diff --git a/src/App/DocumentPyImp.cpp b/src/App/DocumentPyImp.cpp index 1835af5757..6564432c93 100644 --- a/src/App/DocumentPyImp.cpp +++ b/src/App/DocumentPyImp.cpp @@ -289,8 +289,8 @@ PyObject* DocumentPy::removeObject(PyObject *args) PyObject* DocumentPy::copyObject(PyObject *args) { - PyObject *obj, *rec=Py_False; - if (!PyArg_ParseTuple(args, "O|O",&obj,&rec)) + PyObject *obj, *rec=Py_False, *retAll=Py_False; + if (!PyArg_ParseTuple(args, "O|OO",&obj,&rec,&retAll)) return NULL; // NULL triggers exception std::vector objs; @@ -314,8 +314,8 @@ PyObject* DocumentPy::copyObject(PyObject *args) } PY_TRY { - auto ret = getDocumentPtr()->copyObject(objs,PyObject_IsTrue(rec)); - if(ret.size()==1 && single) + auto ret = getDocumentPtr()->copyObject(objs, PyObject_IsTrue(rec), PyObject_IsTrue(retAll)); + if (ret.size()==1 && single) return ret[0]->getPyObject(); Py::Tuple tuple(ret.size()); @@ -866,11 +866,17 @@ Py::Boolean DocumentPy::getRecomputing(void) const return Py::Boolean(getDocumentPtr()->testStatus(Document::Status::Recomputing)); } -Py::Boolean DocumentPy::getTransacting() const { +Py::Boolean DocumentPy::getTransacting() const +{ return Py::Boolean(getDocumentPtr()->isPerformingTransaction()); } -Py::String DocumentPy::getOldLabel() const { +Py::String DocumentPy::getOldLabel() const +{ return Py::String(getDocumentPtr()->getOldLabel()); } +Py::Boolean DocumentPy::getTemporary() const +{ + return Py::Boolean(getDocumentPtr()->testStatus(Document::TempDoc)); +} diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index d498940487..fa035a57bd 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -2473,7 +2473,7 @@ public: return std::string(path.toUtf8().constData()); } - const char *docPath = pDoc->FileName.getValue(); + const char *docPath = pDoc->getFileName(); if(!docPath || *docPath==0) throw Base::RuntimeError("Owner document not saved"); @@ -2510,7 +2510,7 @@ public: l->testFlag(PropertyLinkBase::LinkAllowPartial))==0) { for(App::Document *doc : App::GetApplication().getDocuments()) { - if(getFullPath(doc->FileName.getValue()) == fullpath) { + if(getFullPath(doc->getFileName()) == fullpath) { info->attach(doc); break; } @@ -2585,7 +2585,7 @@ public: FC_ERR("document not found " << filePath()); else{ for(App::Document *doc : App::GetApplication().getDocuments()) { - if(getFullPath(doc->FileName.getValue()) == fullpath) { + if(getFullPath(doc->getFileName()) == fullpath) { attach(doc); return; } @@ -2599,7 +2599,7 @@ public: void attach(Document *doc) { assert(!pcDoc); pcDoc = doc; - FC_LOG("attaching " << doc->getName() << ", " << doc->FileName.getValue()); + FC_LOG("attaching " << doc->getName() << ", " << doc->getFileName()); std::map > parentLinks; for(auto it=links.begin(),itNext=it;it!=links.end();it=itNext) { ++itNext; @@ -2652,7 +2652,7 @@ public: void slotFinishRestoreDocument(const App::Document &doc) { if(pcDoc) return; QString fullpath(getFullPath()); - if(!fullpath.isEmpty() && getFullPath(doc.FileName.getValue())==fullpath) + if(!fullpath.isEmpty() && getFullPath(doc.getFileName())==fullpath) attach(const_cast(&doc)); } @@ -2665,7 +2665,7 @@ public: QFileInfo info(myPos->first); QString path(info.canonicalFilePath()); - const char *filename = doc.FileName.getValue(); + const char *filename = doc.getFileName(); QString docPath(getFullPath(filename)); if(path.isEmpty() || path!=docPath) { @@ -2937,7 +2937,7 @@ void PropertyXLink::setValue(App::DocumentObject *lValue, if(lValue->getDocument() != owner->getDocument()) { if(!docInfo || lValue->getDocument()!=docInfo->pcDoc) { - const char *filename = lValue->getDocument()->FileName.getValue(); + const char *filename = lValue->getDocument()->getFileName(); if(!filename || *filename==0) throw Base::RuntimeError("Linked document not saved"); FC_LOG("xlink set to new document " << lValue->getDocument()->getName()); @@ -3142,7 +3142,7 @@ void PropertyXLink::Save (Base::Writer &writer) const { _path = docInfo->filePath(); else { auto pDoc = owner->getDocument(); - const char *docPath = pDoc->FileName.getValue(); + const char *docPath = pDoc->getFileName(); if(docPath && docPath[0]) { if(filePath.size()) _path = DocInfo::getDocPath(filePath.c_str(),pDoc,false); diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index 8f45141ab0..ece948599e 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -927,17 +927,25 @@ void Application::onLastWindowClosed(Gui::Document* pcDoc) // Call the closing mechanism from Python. This also checks whether pcDoc is the last open document. Command::doCommand(Command::Doc, "App.closeDocument(\"%s\")", pcDoc->getDocument()->getName()); if (!d->activeDocument && d->documents.size()) { + Document *gdoc = nullptr; for(auto &v : d->documents) { + if (v.second->getDocument()->testStatus(App::Document::TempDoc)) + continue; + else if (!gdoc) + gdoc = v.second; + Gui::MDIView* view = v.second->getActiveView(); - if(view) { + if (view) { setActiveDocument(v.second); getMainWindow()->setActiveWindow(view); return; } } - auto gdoc = d->documents.begin()->second; - setActiveDocument(gdoc); - activateView(View3DInventor::getClassTypeId(),true); + + if (gdoc) { + setActiveDocument(gdoc); + activateView(View3DInventor::getClassTypeId(),true); + } } } catch (const Base::Exception& e) { diff --git a/src/Gui/AutoSaver.cpp b/src/Gui/AutoSaver.cpp index ca1d8241a4..636f75ea25 100644 --- a/src/Gui/AutoSaver.cpp +++ b/src/Gui/AutoSaver.cpp @@ -135,7 +135,9 @@ void AutoSaver::saveDocument(const std::string& name, AutoSaveProperty& saver) { Gui::WaitCursor wc; App::Document* doc = App::GetApplication().getDocument(name.c_str()); - if (doc && !doc->testStatus(App::Document::PartialDoc)) { + if (doc && !doc->testStatus(App::Document::PartialDoc) + && !doc->testStatus(App::Document::TempDoc)) + { // Set the document's current transient directory std::string dirName = doc->TransientDir.getValue(); dirName += "/fc_recovery_files"; diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index 73ef72bd7f..2ba663249b 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -1125,24 +1125,27 @@ bool Document::saveAs(void) } } -void Document::saveAll() { +void Document::saveAll() +{ std::vector docs; try { docs = App::Document::getDependentDocuments(App::GetApplication().getDocuments(),true); - }catch(Base::Exception &e) { + } + catch(Base::Exception &e) { e.ReportException(); int ret = QMessageBox::critical(getMainWindow(), QObject::tr("Failed to save document"), QObject::tr("Documents contains cyclic dependencies. Do you still want to save them?"), QMessageBox::Yes,QMessageBox::No); - if(ret!=QMessageBox::Yes) + if (ret != QMessageBox::Yes) return; docs = App::GetApplication().getDocuments(); } + std::map dmap; for(auto doc : docs) dmap[doc] = doc->mustExecute(); for(auto doc : docs) { - if(doc->testStatus(App::Document::PartialDoc)) + if (doc->testStatus(App::Document::PartialDoc) || doc->testStatus(App::Document::TempDoc)) continue; auto gdoc = Application::Instance->getDocument(doc); if(!gdoc) @@ -1161,7 +1164,8 @@ void Document::saveAll() { } Command::doCommand(Command::Doc,"App.getDocument('%s').save()",doc->getName()); gdoc->setModified(false); - } catch (const Base::Exception& e) { + } + catch (const Base::Exception& e) { QMessageBox::critical(getMainWindow(), QObject::tr("Failed to save document") + QString::fromLatin1(": %1").arg(QString::fromUtf8(doc->getName())), diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index d53de6ff6d..9bcf44b1b5 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -678,7 +678,9 @@ bool MainWindow::closeAllDocuments (bool close) continue; if(!gdoc->canClose(false)) return false; - if(!gdoc->isModified() || doc->testStatus(App::Document::PartialDoc)) + if(!gdoc->isModified() + || doc->testStatus(App::Document::PartialDoc) + || doc->testStatus(App::Document::TempDoc)) continue; bool save = saveAll; if(!save && checkModify) { diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index dc3a674d01..f8a6c9bb01 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -2147,6 +2147,8 @@ void TreeWidget::drawRow(QPainter *painter, const QStyleOptionViewItem &options, void TreeWidget::slotNewDocument(const Gui::Document& Doc, bool isMainDoc) { + if(Doc.getDocument()->testStatus(App::Document::TempDoc)) + return; DocumentItem* item = new DocumentItem(&Doc, this->rootItem); if(isMainDoc) this->expandItem(item);