Files
create/src/App/Document.h

618 lines
29 KiB
C++

/***************************************************************************
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef APP_DOCUMENT_H
#define APP_DOCUMENT_H
#include <CXX/Objects.hxx>
#include <Base/Observer.h>
#include <Base/Persistence.h>
#include <Base/Type.h>
#include <Base/Handle.h>
#include "PropertyContainer.h"
#include "PropertyLinks.h"
#include "PropertyStandard.h"
#include <map>
#include <vector>
#include <QString>
namespace Base {
class Writer;
}
namespace App
{
class TransactionalObject;
class DocumentObject;
class DocumentObjectExecReturn;
class Document;
class DocumentPy; // the python document class
class Application;
class Transaction;
class StringHasher;
using StringHasherRef = Base::Reference<StringHasher>;
}
namespace App
{
/// The document class
class AppExport Document : public App::PropertyContainer
{
PROPERTY_HEADER_WITH_OVERRIDE(App::Document);
public:
enum Status {
SkipRecompute = 0,
KeepTrailingDigits = 1,
Closable = 2,
Restoring = 3,
Recomputing = 4,
PartialRestore = 5,
Importing = 6,
PartialDoc = 7,
AllowPartialRecompute = 8, // allow recomputing editing object if SkipRecompute is set
TempDoc = 9, // Mark as temporary document without prompt for save
RestoreError = 10,
LinkStampChanged = 11, // Indicates during restore time if any linked document's time stamp has changed
IgnoreErrorOnRecompute = 12, // Don't report errors if the recompute failed
};
/** @name Properties */
//@{
/// holds the long name of the document (utf-8 coded)
PropertyString Label;
/// full qualified (with path) file name (utf-8 coded)
PropertyString FileName;
/// creators name (utf-8)
PropertyString CreatedBy;
PropertyString CreationDate;
/// user last modified the document
PropertyString LastModifiedBy;
PropertyString LastModifiedDate;
/// company name UTF8(optional)
PropertyString Company;
/// long comment or description (UTF8 with line breaks)
PropertyString Comment;
/// Id e.g. Part number
PropertyString Id;
/// unique identifier of the document
PropertyUUID Uid;
/// Full name of the licence e.g. "Creative Commons Attribution". See https://spdx.org/licenses/
App::PropertyString License;
/// License description/contract URL
App::PropertyString LicenseURL;
/// Meta descriptions
App::PropertyMap Meta;
/// Material descriptions, used and defined in the Material module.
App::PropertyMap Material;
/// read-only name of the temp dir created when the document is opened
PropertyString TransientDir;
/// Tip object of the document (if any)
PropertyLink Tip;
/// Tip object of the document (if any)
PropertyString TipName;
/// Whether to show hidden items in TreeView
PropertyBool ShowHidden;
//@}
/** @name Signals of the document */
//@{
/// signal before changing an doc property
boost::signals2::signal<void (const App::Document&, const App::Property&)> signalBeforeChange;
/// signal on changed doc property
boost::signals2::signal<void (const App::Document&, const App::Property&)> signalChanged;
/// signal on new Object
boost::signals2::signal<void (const App::DocumentObject&)> signalNewObject;
//boost::signals2::signal<void (const App::DocumentObject&)> m_sig;
/// signal on deleted Object
boost::signals2::signal<void (const App::DocumentObject&)> signalDeletedObject;
/// signal before changing an Object
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalBeforeChangeObject;
/// signal on changed Object
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalChangedObject;
/// signal on manually called DocumentObject::touch()
boost::signals2::signal<void (const App::DocumentObject&)> signalTouchedObject;
/// signal on relabeled Object
boost::signals2::signal<void (const App::DocumentObject&)> signalRelabelObject;
/// signal on activated Object
boost::signals2::signal<void (const App::DocumentObject&)> signalActivatedObject;
/// signal on created object
boost::signals2::signal<void (const App::DocumentObject&, Transaction*)> signalTransactionAppend;
/// signal on removed object
boost::signals2::signal<void (const App::DocumentObject&, Transaction*)> signalTransactionRemove;
/// signal on undo
boost::signals2::signal<void (const App::Document&)> signalUndo;
/// signal on redo
boost::signals2::signal<void (const App::Document&)> signalRedo;
/** signal on load/save document
* this signal is given when the document gets streamed.
* you can use this hook to write additional information in
* the file (like the Gui::Document does).
*/
boost::signals2::signal<void (Base::Writer &)> signalSaveDocument;
boost::signals2::signal<void (Base::XMLReader&)> signalRestoreDocument;
boost::signals2::signal<void (const std::vector<App::DocumentObject*>&,
Base::Writer &)> signalExportObjects;
boost::signals2::signal<void (const std::vector<App::DocumentObject*>&,
Base::Writer &)> signalExportViewObjects;
boost::signals2::signal<void (const std::vector<App::DocumentObject*>&,
Base::XMLReader&)> signalImportObjects;
boost::signals2::signal<void (const std::vector<App::DocumentObject*>&, Base::Reader&,
const std::map<std::string, std::string>&)> signalImportViewObjects;
boost::signals2::signal<void (const std::vector<App::DocumentObject*>&)> signalFinishImportObjects;
//signal starting a save action to a file
boost::signals2::signal<void (const App::Document&, const std::string&)> signalStartSave;
//signal finishing a save action to a file
boost::signals2::signal<void (const App::Document&, const std::string&)> signalFinishSave;
boost::signals2::signal<void (const App::Document&)> signalBeforeRecompute;
boost::signals2::signal<void (const App::Document&, const std::vector<App::DocumentObject*>&)> signalRecomputed;
boost::signals2::signal<void (const App::DocumentObject&)> signalRecomputedObject;
//signal a new opened transaction
boost::signals2::signal<void (const App::Document&, std::string)> signalOpenTransaction;
// signal a committed transaction
boost::signals2::signal<void (const App::Document&)> signalCommitTransaction;
// signal an aborted transaction
boost::signals2::signal<void (const App::Document&)> signalAbortTransaction;
boost::signals2::signal<void (const App::Document&, const std::vector<App::DocumentObject*>&)> signalSkipRecompute;
boost::signals2::signal<void (const App::DocumentObject&)> signalFinishRestoreObject;
boost::signals2::signal<void (const App::Document&,const App::Property&)> signalChangePropertyEditor;
//@}
boost::signals2::signal<void (std::string)> signalLinkXsetValue;
void clearDocument();
/** @name File handling of the document */
//@{
/// Save the Document under a new Name
//void saveAs (const char* Name);
/// Save the document to the file in Property Path
bool save ();
bool saveAs(const char* file);
bool saveCopy(const char* file) const;
/// Restore the document from the file in Property Path
void restore (const char *filename=nullptr,
bool delaySignal=false, const std::vector<std::string> &objNames={});
bool afterRestore(bool checkPartial=false);
bool afterRestore(const std::vector<App::DocumentObject *> &, bool checkPartial=false);
enum ExportStatus {
NotExporting,
Exporting,
};
ExportStatus isExporting(const App::DocumentObject *obj) const;
void exportObjects(const std::vector<App::DocumentObject*>&, std::ostream&);
void exportGraphviz(std::ostream&) const;
std::vector<App::DocumentObject*> importObjects(Base::XMLReader& reader);
/** Import any externally linked objects
*
* @param objs: input list of objects. Only objects belonging to this document will
* be checked for external links. And all found external linked object will be imported
* to this document. Link type properties of those input objects will be automatically
* reassigned to the imported objects. Note that the link properties of other objects
* in the document but not included in the input list, will not be affected even if they
* point to some object beining imported. To import all objects, simply pass in all objects
* of this document.
*
* @return the list of imported objects
*/
std::vector<App::DocumentObject*> importLinks(
const std::vector<App::DocumentObject*> &objs = {});
/// Opens the document from its file name
//void open (void);
/// Is the document already saved to a file?
bool isSaved() const;
/// Get the document name
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;
//@}
void Save (Base::Writer &writer) const override;
void Restore(Base::XMLReader &reader) override;
/// returns the complete document memory consumption, including all managed DocObjects and Undo Redo.
unsigned int getMemSize () const override;
/** @name Object handling */
//@{
/** Add a feature of sType with sName (ASCII) to this document and set it active.
* Unicode names are set through the Label property.
* @param sType the type of created object
* @param pObjectName if nonNULL use that name otherwise generate a new unique name based on the \a sType
* @param isNew if false don't call the \c DocumentObject::setupObject() callback (default is true)
* @param viewType override object's view provider name
* @param isPartial indicate if this object is meant to be partially loaded
*/
DocumentObject *addObject(const char* sType, const char* pObjectName=nullptr,
bool isNew=true, const char *viewType=nullptr, bool isPartial=false);
/** Add an array of features of the given types and names.
* Unicode names are set through the Label property.
* @param sType The type of created object
* @param objectNames A list of object names
* @param isNew If false don't call the \c DocumentObject::setupObject() callback (default is true)
*/
std::vector<DocumentObject *>addObjects(const char* sType, const std::vector<std::string>& objectNames, bool isNew=true);
/// Remove a feature out of the document
void removeObject(const char* sName);
/** Add an existing feature with sName (ASCII) to this document and set it active.
* Unicode names are set through the Label property.
* This is an overloaded function of the function above and can be used to create
* a feature outside and add it to the document afterwards.
* \note The passed feature must not yet be added to a document, otherwise an exception
* is raised.
*/
void addObject(DocumentObject*, const char* pObjectName=nullptr);
/** Copy objects from another document to this document
*
* @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<DocumentObject*> copyObject(
const std::vector<DocumentObject*> &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.
* Returns the moved object itself or 0 if the object is already part of this
* document..
*/
DocumentObject* moveObject(DocumentObject* obj, bool recursive=false);
/// Returns the active Object of this document
DocumentObject *getActiveObject() const;
/// Returns a Object of this document
DocumentObject *getObject(const char *Name) const;
/// Returns a Object of this document by its id
DocumentObject *getObjectByID(long id) const;
/// Returns true if the DocumentObject is contained in this document
bool isIn(const DocumentObject *pFeat) const;
/// Returns a Name of an Object or 0
const char *getObjectName(DocumentObject *pFeat) const;
/// Returns a Name of an Object or 0
std::string getUniqueObjectName(const char *Name) const;
/// Returns a name of the form prefix_number. d specifies the number of digits.
std::string getStandardObjectName(const char *Name, int d) const;
/// Returns a list of document's objects including the dependencies
std::vector<DocumentObject*> getDependingObjects() const;
/// Returns a list of all Objects
const std::vector<DocumentObject*> &getObjects() const;
std::vector<DocumentObject*> getObjectsOfType(const Base::Type& typeId) const;
/// Returns all object with given extensions. If derived=true also all objects with extensions derived from the given one
std::vector<DocumentObject*> getObjectsWithExtension(const Base::Type& typeId, bool derived = true) const;
std::vector<DocumentObject*> findObjects(const Base::Type& typeId, const char* objname, const char* label) const;
/// Returns an array with the correct types already.
template<typename T> inline std::vector<T*> getObjectsOfType() const;
int countObjectsOfType(const Base::Type& typeId) const;
/// get the number of objects in the document
int countObjects() const;
//@}
/** @name methods for modification and state handling
*/
//@{
/// Remove all modifications. After this call The document becomes Valid again.
void purgeTouched();
/// check if there is any touched object in this document
bool isTouched() const;
/// check if there is any object must execute in this document
bool mustExecute() const;
/// returns all touched objects
std::vector<App::DocumentObject *> getTouched() const;
/// set the document to be closable, this is on by default.
void setClosable(bool);
/// check whether the document can be closed
bool isClosable() const;
/** Recompute touched features and return the number of recalculated features
*
* @param objs: specify a sub set of objects to recompute. If empty, then
* all object in this document is checked for recompute
*/
int recompute(const std::vector<App::DocumentObject*> &objs={},
bool force=false,bool *hasError=nullptr, int options=0);
/// Recompute only one feature
bool recomputeFeature(DocumentObject* Feat,bool recursive=false);
/// get the text of the error of a specified object
const char* getErrorDescription(const App::DocumentObject*) const;
/// return the status bits
bool testStatus(Status pos) const;
/// set the status bits
void setStatus(Status pos, bool on);
//@}
/** @name methods for the UNDO REDO and Transaction handling
*
* Introduce a new concept of transaction ID. Each transaction must be
* unique inside the document. Multiple transactions from different
* documents can be grouped together with the same transaction ID.
*
* When undo, Gui component can query getAvailableUndo(id) to see if it is
* possible to undo with a given ID. If there more than one undo
* transactions, meaning that there are other transactions before the given
* ID. The Gui component shall ask user if they want to undo multiple steps.
* And if the user agrees, call undo(id) to unroll all transaction before
* and including the one with the given ID. Same applies for redo.
*
* The new transaction ID describe here is fully backward compatible.
* Calling the APIs with a default id=0 gives the original behavior.
*/
//@{
/// switch the level of Undo/Redo
void setUndoMode(int iMode);
/// switch the level of Undo/Redo
int getUndoMode() const;
/// switch the transaction mode
void setTransactionMode(int iMode);
/** Open a new command Undo/Redo, an UTF-8 name can be specified
*
* @param name: transaction name
*
* This function calls App::Application::setActiveTransaction(name) instead
* to setup a potential transaction which will only be created if there is
* actual changes.
*/
void openTransaction(const char* name=nullptr);
/// Rename the current transaction if the id matches
void renameTransaction(const char *name, int id);
/// Commit the Command transaction. Do nothing If there is no Command transaction open.
void commitTransaction();
/// Abort the actually running transaction.
void abortTransaction();
/// Check if a transaction is open
bool hasPendingTransaction() const;
/// Return the undo/redo transaction ID starting from the back
int getTransactionID(bool undo, unsigned pos=0) const;
/// Check if a transaction is open and its list is empty.
/// If no transaction is open true is returned.
bool isTransactionEmpty() const;
/// Set the Undo limit in Byte!
void setUndoLimit(unsigned int UndoMemSize=0);
/// Returns the actual memory consumption of the Undo redo stuff.
unsigned int getUndoMemSize () const;
/// Set the Undo limit as stack size
void setMaxUndoStackSize(unsigned int UndoMaxStackSize=20);
/// Set the Undo limit as stack size
unsigned int getMaxUndoStackSize()const;
/// Remove all stored Undos and Redos
void clearUndos();
/// Returns the number of stored Undos. If greater than 0 Undo will be effective.
int getAvailableUndos(int id=0) const;
/// Returns a list of the Undo names
std::vector<std::string> getAvailableUndoNames() const;
/// Will UNDO one step, returns False if no undo was done (Undos == 0).
bool undo(int id=0);
/// Returns the number of stored Redos. If greater than 0 Redo will be effective.
int getAvailableRedos(int id=0) const;
/// Returns a list of the Redo names.
std::vector<std::string> getAvailableRedoNames() const;
/// Will REDO one step, returns False if no redo was done (Redos == 0).
bool redo(int id=0) ;
/// returns true if the document is in an Transaction phase, e.g. currently performing a redo/undo or rollback
bool isPerformingTransaction() const;
/// \internal add or remove property from a transactional object
void addOrRemovePropertyOfObject(TransactionalObject*, Property *prop, bool add);
//@}
/** @name dependency stuff */
//@{
/// write GraphViz file
void writeDependencyGraphViz(std::ostream &out);
/// checks if the graph is directed and has no cycles
bool checkOnCycle();
/// get a list of all objects linking to the given object
std::vector<App::DocumentObject*> getInList(const DocumentObject* me) const;
/// Option bit flags used by getDepenencyList()
enum DependencyOption {
/// Return topological sorted list
DepSort = 1,
/// Do no include object linked by PropertyXLink, as it can handle external link
DepNoXLinked = 2,
/// Raise exception on cycles
DepNoCycle = 4,
};
/** Get a complete list of all objects the given objects depend on.
*
* This function is defined as static because it accepts objects from
* different documents, and the returned list will contain dependent
* objects from all relevant documents
*
* @param objs: input objects to query for dependency.
* @param options: See DependencyOption
*/
static std::vector<App::DocumentObject*> getDependencyList(
const std::vector<App::DocumentObject*> &objs, int options=0);
std::vector<App::Document*> getDependentDocuments(bool sort=true);
static std::vector<App::Document*> getDependentDocuments(std::vector<App::Document*> docs, bool sort);
// set Changed
//void setChanged(DocumentObject* change);
/// get a list of topological sorted objects (https://en.wikipedia.org/wiki/Topological_sorting)
std::vector<App::DocumentObject*> topologicalSort() const;
/// get all root objects (objects no other one reference too)
std::vector<App::DocumentObject*> getRootObjects() const;
/// get all possible paths from one object to another following the OutList
std::vector<std::list<App::DocumentObject*> > getPathsByOutList
(const App::DocumentObject* from, const App::DocumentObject* to) const;
//@}
/** Called by a property during save to store its StringHasher
*
* @param hasher: the input hasher
* @return Returns a pair<bool,int>. The boolean indicates if the
* StringHasher has been added before. The integer is the hasher index.
*
* The StringHasher object is designed to be shared among multiple objects.
* We must not save duplicate copies of the same hasher, and must be
* able to restore with the same sharing relationship. This function returns
* whether the hasher has been added before by other objects, and the index
* of the hasher. If the hasher has not been added before, the object must
* save the hasher by calling StringHasher::Save
*/
std::pair<bool,int> addStringHasher(const StringHasherRef & hasher) const;
/** Called by property to restore its StringHasher
*
* @param index: the index previously returned by calling addStringHasher()
* during save. Or if is negative, then return document's own string hasher.
*
* @return Return the resulting string hasher.
*
* The caller is responsible for restoring the hasher if the caller is the first
* owner of the hasher, i.e. if addStringHasher() returns true during save.
*/
StringHasherRef getStringHasher(int index=-1) const;
/** Return the links to a given object
*
* @param links: holds the links found
* @param obj: the linked object. If NULL, then all links are returned.
* @param option: @sa App::GetLinkOption
* @param maxCount: limit the number of links returned, 0 means no limit
* @param objs: optional objects to search for, if empty, then all objects
* of this document are searched.
*/
void getLinksTo(std::set<DocumentObject*> &links,
const DocumentObject *obj, int options, int maxCount=0,
const std::vector<DocumentObject*> &objs = {}) const;
/// Check if there is any link to the given object
bool hasLinksTo(const DocumentObject *obj) const;
/// Called by objects during restore to ask for recompute
void addRecomputeObject(DocumentObject *obj);
const std::string &getOldLabel() const {return oldLabel;}
/// Function called to signal that an object identifier has been renamed
void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> & paths, const std::function<bool(const App::DocumentObject*)> &selector = [](const App::DocumentObject *) { return true; });
PyObject *getPyObject() override;
std::string getFullName() const override;
/// Indicate if there is any document restoring/importing
static bool isAnyRestoring();
friend class Application;
/// because of transaction handling
friend class TransactionalObject;
friend class DocumentObject;
friend class Transaction;
friend class TransactionDocumentObject;
/// Destruction
~Document() override;
protected:
/// Construction
explicit Document(const char *documentName = "");
void _removeObject(DocumentObject* pcObject);
void _addObject(DocumentObject* pcObject, const char* pObjectName);
/// checks if a valid transaction is open
void _checkTransaction(DocumentObject* pcDelObj, const Property *What, int line);
void breakDependency(DocumentObject* pcObject, bool clear);
std::vector<App::DocumentObject*> readObjects(Base::XMLReader& reader);
void writeObjects(const std::vector<App::DocumentObject*>&, Base::Writer &writer) const;
bool saveToFile(const char* filename) const;
void onBeforeChange(const Property* prop) override;
void onChanged(const Property* prop) override;
/// callback from the Document objects before property will be changed
void onBeforeChangeProperty(const TransactionalObject *Who, const Property *What);
/// callback from the Document objects after property was changed
void onChangedProperty(const DocumentObject *Who, const Property *What);
/// helper which Recompute only this feature
/// @return 0 if succeeded, 1 if failed, -1 if aborted by user.
int _recomputeFeature(DocumentObject* Feat);
void _clearRedos();
/// refresh the internal dependency graph
void _rebuildDependencyList(
const std::vector<App::DocumentObject*> &objs = std::vector<App::DocumentObject*>());
std::string getTransientDirectoryName(const std::string& uuid, const std::string& filename) const;
/** Open a new command Undo/Redo, an UTF-8 name can be specified
*
* @param name: transaction name
* @param id: transaction ID, if 0 then the ID is auto generated.
*
* @return: Return the ID of the new transaction.
*
* This function creates an actual transaction regardless of Application
* AutoTransaction setting.
*/
int _openTransaction(const char* name=nullptr, int id=0);
/// Internally called by App::Application to commit the Command transaction.
void _commitTransaction(bool notify=false);
/// Internally called by App::Application to abort the running transaction.
void _abortTransaction();
private:
// # Data Member of the document +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
std::list<Transaction*> mUndoTransactions;
std::map<int,Transaction*> mUndoMap;
std::list<Transaction*> mRedoTransactions;
std::map<int,Transaction*> mRedoMap;
struct DocumentP* d;
std::string oldLabel;
std::string myName;
};
template<typename T>
inline std::vector<T*> Document::getObjectsOfType() const
{
std::vector<T*> type;
std::vector<App::DocumentObject*> obj = this->getObjectsOfType(T::getClassTypeId());
type.reserve(obj.size());
for (std::vector<App::DocumentObject*>::iterator it = obj.begin(); it != obj.end(); ++it)
type.push_back(static_cast<T*>(*it));
return type;
}
} //namespace App
#endif // APP_DOCUMENT_H