// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2002 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License (LGPL) * * as published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * for detail see the LICENCE text file. * * * * FreeCAD 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 FreeCAD; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * * * ***************************************************************************/ #ifndef SRC_APP_APPLICATION_H_ #define SRC_APP_APPLICATION_H_ #include #include #include #include #include #include #include #include #include #include #include #include // forward declarations using PyObject = struct _object; using PyMethodDef = struct PyMethodDef; namespace Base { class ConsoleObserverStd; class ConsoleObserverFile; } namespace App { class Document; class DocumentObject; class ApplicationDirectories; class ApplicationObserver; class Property; class AutoTransaction; class ExtensionContainer; enum GetLinkOption { /// Get all links (both directly and in directly) linked to the given object GetLinkRecursive = 1, /// Get link array element instead of the array GetLinkArrayElement = 2, /// Get linked object instead of the link, no effect if GetLinkRecursive GetLinkedObject = 4, /// Get only external links, no effect if GetLinkRecursive GetLinkExternal = 8, }; enum class MessageOption { Quiet, /**< Suppress error. */ Error, /**< Print an error message. */ Throw, /**< Throw an exception. */ }; struct DocumentInitFlags { bool createView {true}; bool temporary {false}; }; /** The Application * The root of the whole application * @see App::Document */ class AppExport Application { public: //--------------------------------------------------------------------- // exported functions go here +++++++++++++++++++++++++++++++++++++++++ //--------------------------------------------------------------------- /** @name methods for document handling */ //@{ /** Creates a new document * @param proposedName: a prototype name used to create the permanent Name for the document. * It is converted to be like an identifier in a programming language, * with no spaces and not starting with a number. This name gets also forced to be unique * in this Application. You can obtain the unique name using doc.getDocumentName * on the returned document. * @param proposedLabel: a UTF8 name of any kind. It's that name normally shown to * the user and stored in the App::Document::Label property. */ App::Document* newDocument(const char * proposedName=nullptr, const char * proposedLabel=nullptr, DocumentInitFlags CreateFlags=DocumentInitFlags()); /// 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, bool tempDoc=false) const; /// Open an existing document from a file App::Document* openDocument(const char * FileName=nullptr, DocumentInitFlags initFlags = DocumentInitFlags{}); /** Open multiple documents * * @param filenames: input file names * @param paths: optional input file path in case it is different from * filenames (mainly used during recovery). * @param labels: optional label assign to document (mainly used during recovery). * @param errs: optional output error message corresponding to each input * file name. If errs is given, this function will catch all * Base::Exception and save the error message inside. Otherwise, it will * throw on exception when opening the input files. * @param createView: whether to signal Gui module to create view on restore. * * @return Return opened document object corresponding to each input file * name, which maybe NULL if failed. * * This function will also open any external referenced files. */ std::vector openDocuments(const std::vector &filenames, const std::vector *paths=nullptr, const std::vector *labels=nullptr, std::vector *errs=nullptr, DocumentInitFlags initFlags = DocumentInitFlags{}); /// Retrieve the active document App::Document* getActiveDocument() const; /// Retrieve a named document App::Document* getDocument(const char *Name) const; /// Path matching mode for getDocumentByPath() enum class PathMatchMode { /// Match by resolving to absolute file path MatchAbsolute = 0, /** Match by absolute path first. If not found then match by resolving * to canonical file path where any intermediate '.' '..' and symlinks * are resolved. */ MatchCanonical = 1, /** Same as MatchCanonical, but if a document is found by canonical * path match, which means the document can be resolved using two * different absolute path, a warning is printed and the found document * is not returned. This is to allow the caller to intentionally load * the same physical file as separate documents. */ MatchCanonicalWarning = 2, }; /** Retrieve a document based on file path * * @param path: file path * @param checkCanonical: file path matching mode, @sa PathMatchMode. * @return Return the document found by matching with the given path */ App::Document* getDocumentByPath(const char *path, PathMatchMode checkCanonical = PathMatchMode::MatchAbsolute) const; /// gets the (internal) name of the document const char * getDocumentName(const App::Document* ) const; /// get a list of all documents in the application std::vector getDocuments() const; /// Set the active document void setActiveDocument(App::Document* pDoc); void setActiveDocument(const char* Name); /// close all documents (without saving) void closeAllDocuments(); /// Add pending document to open together with the current opening document int addPendingDocument(const char *FileName, const char *objName, bool allowPartial); /// Indicate whether the application is opening (restoring) some document bool isRestoring() const; /// Indicate the application is closing all document bool isClosingAll() const; //@} /** @name Application-wide trandaction setting */ //@{ /** Setup a pending application-wide active transaction * * @param name: new transaction name * @param persist: by default, if the calling code is inside any invocation * of a command, it will be auto closed once all command within the current * stack exists. To disable auto closing, set persist=true * * @return The new transaction ID. * * Call this function to setup an application-wide transaction. All current * pending transactions of opening documents will be committed first. * However, no new transaction is created by this call. Any subsequent * changes in any current opening document will auto create a transaction * with the given name and ID. If more than one document is changed, the * transactions will share the same ID, and will be undo/redo together. */ int setActiveTransaction(const char *name, bool persist=false); /// Return the current active transaction name and ID const char *getActiveTransaction(int *tid=nullptr) const; /** Commit/abort current active transactions * * @param abort: whether to abort or commit the transactions * * Bsides calling this function directly, it will be called by automatically * if 1) any new transaction is created with a different ID, or 2) any * transaction with the current active transaction ID is either committed or * aborted */ void closeActiveTransaction(bool abort=false, int id=0); //@} // NOLINTBEGIN // clang-format off /** @name Signals of the Application */ //@{ /// signal on new Document fastsignals::signal signalNewDocument; /// signal on document getting deleted fastsignals::signal signalDeleteDocument; /// signal on already deleted Document fastsignals::signal signalDeletedDocument; /// signal on relabeling Document (user name) fastsignals::signal signalRelabelDocument; /// signal on renaming Document (internal name) fastsignals::signal signalRenameDocument; /// signal on activating Document fastsignals::signal signalActiveDocument; /// signal on saving Document fastsignals::signal signalSaveDocument; /// signal on starting to restore Document fastsignals::signal signalStartRestoreDocument; /// signal on restoring Document fastsignals::signal signalFinishRestoreDocument; /// signal on pending reloading of a partial Document fastsignals::signal signalPendingReloadDocument; /// signal on starting to save Document fastsignals::signal signalStartSaveDocument; /// signal on saved Document fastsignals::signal signalFinishSaveDocument; /// signal on undo in document fastsignals::signal signalUndoDocument; /// signal on application wide undo fastsignals::signal signalUndo; /// signal on redo in document fastsignals::signal signalRedoDocument; /// signal on application wide redo fastsignals::signal signalRedo; /// signal before open active transaction fastsignals::signal signalBeforeOpenTransaction; /// signal before close/abort active transaction fastsignals::signal signalBeforeCloseTransaction; /// signal after close/abort active transaction fastsignals::signal signalCloseTransaction; /// signal on show hidden items fastsignals::signal signalShowHidden; /// signal on start opening document(s) fastsignals::signal signalStartOpenDocument; /// signal on finished opening document(s) fastsignals::signal signalFinishOpenDocument; //@} /** @name Signals of the document * This signals are an aggregation of all document. If you only * the signal of a special document connect to the document itself */ //@{ /// signal before change of doc property fastsignals::signal signalBeforeChangeDocument; /// signal on changed doc property fastsignals::signal signalChangedDocument; /// signal on new Object fastsignals::signal signalNewObject; //fastsignals::signal m_sig; /// signal on deleted Object fastsignals::signal signalDeletedObject; /// signal on changed Object fastsignals::signal signalBeforeChangeObject; /// signal on changed Object fastsignals::signal signalChangedObject; /// signal on relabeled Object fastsignals::signal signalRelabelObject; /// signal on activated Object fastsignals::signal signalActivatedObject; /// signal before recomputed document fastsignals::signal signalBeforeRecomputeDocument; /// signal on recomputed document fastsignals::signal signalRecomputed; /// signal on recomputed document object fastsignals::signal signalObjectRecomputed; // signal on opened transaction fastsignals::signal signalOpenTransaction; // signal a committed transaction fastsignals::signal signalCommitTransaction; // signal an aborted transaction fastsignals::signal signalAbortTransaction; //@} /** @name Signals of property changes * These signals are emitted on property additions or removal. * The changed object can be any sub-class of PropertyContainer. */ //@{ /// signal on adding a dynamic property fastsignals::signal signalAppendDynamicProperty; /// signal on renaming a dynamic property fastsignals::signal signalRenameDynamicProperty; /// signal on about removing a dynamic property fastsignals::signal signalRemoveDynamicProperty; /// signal on about changing the editor mode of a property fastsignals::signal signalChangePropertyEditor; //@} /** @name Signals of extension changes * These signals are emitted on dynamic extension addition. Dynamic extensions are the ones added by python (c++ ones are part * of the class definition, hence not dynamic) * The extension in question is provided as parameter. */ //@{ /// signal before adding the extension fastsignals::signal signalBeforeAddingDynamicExtension; /// signal after the extension was added fastsignals::signal signalAddedDynamicExtension; //@} // clang-format off // NOLINTEND /** @name methods for parameter handling */ //@{ /// returns the system parameter ParameterManager & GetSystemParameter(); /// returns the user parameter ParameterManager & GetUserParameter(); /** Gets a parameter group by a full qualified path * It's an easy method to get a group: * \code * // getting standard parameter * ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Raytracing"); * std::string cDir = hGrp->GetASCII("ProjectPath", ""); * std::string cCameraName = hGrp->GetASCII("CameraName", "TempCamera.inc"); * \endcode */ Base::Reference GetParameterGroupByPath(const char* sName); ParameterManager * GetParameterSet(const char* sName) const; const std::map> & GetParameterSetList() const; void AddParameterSet(const char* sName); void RemoveParameterSet(const char* sName); //@} /** @name methods for the open handler * With this facility an Application module can register * an ending (filetype) which it can handle to open. * The ending and the module name are stored and if the file * type is opened the module gets loaded and needs to register an * OpenHandler class in the OpenHandlerFactorySingleton. * After the module is loaded, an OpenHandler of this type is created * and the file gets loaded. * @see OpenHandler * @see OpenHandlerFactorySingleton */ //@{ /// Register an import filetype and a module name void addImportType(const char* Type, const char* ModuleName); /// Change the module name of a registered filetype void changeImportModule(const char* Type, const char* OldModuleName, const char* NewModuleName); /// Return a list of modules that support the given filetype. std::vector getImportModules(const char* Type) const; /// Return a list of all modules. std::vector getImportModules() const; /// Return a list of filetypes that are supported by a module. std::vector getImportTypes(const char* Module) const; /// Return a list of all filetypes. std::vector getImportTypes() const; /// Return the import filters with modules of a given filetype. std::map getImportFilters(const char* Type) const; /// Return a list of all import filters. std::map getImportFilters() const; //@} //@{ /// Register an export filetype and a module name void addExportType(const char* Type, const char* ModuleName); /// Change the module name of a registered filetype void changeExportModule(const char* Type, const char* OldModuleName, const char* NewModuleName); /// Return a list of modules that support the given filetype. std::vector getExportModules(const char* Type) const; /// Return a list of all modules. std::vector getExportModules() const; /// Return a list of filetypes that are supported by a module. std::vector getExportTypes(const char* Module) const; /// Return a list of all filetypes. std::vector getExportTypes() const; /// Return the export filters with modules of a given filetype. std::map getExportFilters(const char* Type) const; /// Return a list of all export filters. std::map getExportFilters() const; //@} /** @name Init, Destruct an Access methods */ //@{ static void init(int argc, char ** argv); static void initTypes(); static void destruct(); static void destructObserver(); static void processCmdLineFiles(); static std::list getCmdLineFiles(); static std::list processFiles(const std::list&); static void runApplication(); friend Application &GetApplication(); static std::map &Config(){return mConfig;} static int GetARGC(){return _argc;} static char** GetARGV(){return _argv;} static int64_t applicationPid(); //@} /** @name Application directories */ //@{ static std::string getHomePath(); static std::string getExecutableName(); static std::string getNameWithVersion(); static bool isDevelopmentVersion(); /// Access to the various directories for the program a replacement for the get*Path methods below static const std::unique_ptr& directories(); /*! Returns the temporary directory. By default, this is set to the system's temporary directory but can be customized by the user. */ static std::string getTempPath(); static std::string getTempFileName(const char* FileName=nullptr); static std::string getUserCachePath(); static std::string getUserConfigPath(); static std::string getUserAppDataDir(); static std::string getUserMacroDir(); static std::string getResourceDir(); static std::string getLibraryDir(); static std::string getHelpDir(); //@} /** @name Verbose Information */ //@{ static void getVerboseCommonInfo(QTextStream& str, const std::map& mConfig); static void getVerboseAddOnsInfo(QTextStream& str, const std::map& mConfig); static void addModuleInfo(QTextStream& str, const QString& modPath, bool& firstMod); static QString prettyProductInfoWrapper(); static QString getValueOrEmpty(const std::map& map, const std::string& key); static constexpr const char* verboseVersionEmitMessage{"verbose_version"}; //@} /** @name Link handling */ //@{ /** Check for link recursion depth * * @param depth: current depth * @param option: whether to throw exception, print an error message or quieten any output. * In the latter case the caller must check the returned value. * * @return Return the maximum remaining depth. * * The function uses an internal count of all objects in all documents as * the limit of recursion depth. */ int checkLinkDepth(int depth, MessageOption option = MessageOption::Error); /** Return the links to a given object * * @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 */ std::set getLinksTo( const DocumentObject *, int options, int maxCount=0) const; /// Check if there is any link to the given object bool hasLinksTo(const DocumentObject *obj) const; //@} /// Gets the base progress indicator instance. Base::ProgressIndicator& getProgressIndicator() { return _progressIndicator; } friend class App::Document; protected: /// get called by the document when the name is changing void renameDocument(const char *OldName, const char *NewName); /** @name I/O of the document * This slot gets connected to all App::Documents created */ //@{ void slotBeforeChangeDocument(const App::Document& doc, const App::Property& prop); void slotChangedDocument(const App::Document& doc, const App::Property& prop); void slotNewObject(const App::DocumentObject& obj); void slotDeletedObject(const App::DocumentObject& obj); void slotBeforeChangeObject(const App::DocumentObject& obj, const App::Property& prop); void slotChangedObject(const App::DocumentObject& obj, const App::Property& prop); void slotRelabelObject(const App::DocumentObject& obj); void slotActivatedObject(const App::DocumentObject& obj); void slotUndoDocument(const App::Document& doc); void slotRedoDocument(const App::Document& doc); void slotRecomputedObject(const App::DocumentObject& obj); void slotRecomputed(const App::Document& doc); void slotBeforeRecompute(const App::Document& doc); void slotOpenTransaction(const App::Document& doc, std::string name); void slotCommitTransaction(const App::Document& doc); void slotAbortTransaction(const App::Document& doc); void slotStartSaveDocument(const App::Document& doc, const std::string& filename); void slotFinishSaveDocument(const App::Document& doc, const std::string& filename); void slotChangePropertyEditor(const App::Document& doc, const App::Property& prop); //@} /// open single document only App::Document* openDocumentPrivate(const char * FileName, const char *propFileName, const char *label, bool isMainDoc, DocumentInitFlags initFlags, std::vector &&objNames); /// Helper class for App::Document to signal on close/abort transaction class AppExport TransactionSignaller { public: TransactionSignaller(bool abort, bool signal); ~TransactionSignaller(); private: bool abort; }; private: /// Constructor. The passed configuration must last for the lifetime of the constructed Application // NOLINTNEXTLINE(runtime/references) explicit Application(std::map &mConfig); /// Destructor virtual ~Application(); static void cleanupUnits(); void setActiveDocumentNoSignal(App::Document* pDoc); /** @name member for parameter */ //@{ static Base::Reference _pcSysParamMngr; static Base::Reference _pcUserParamMngr; //@} //--------------------------------------------------------------------- // python exports goes here +++++++++++++++++++++++++++++++++++++++++++ //--------------------------------------------------------------------- static void setupPythonTypes(); static void setupPythonException(PyObject*); // clang-format off // static python wrapper of the exported functions static PyObject* sGetParam (PyObject *self, PyObject *args); static PyObject* sSaveParameter (PyObject *self, PyObject *args); static PyObject* sGetVersion (PyObject *self, PyObject *args); static PyObject* sGetConfig (PyObject *self, PyObject *args); static PyObject* sSetConfig (PyObject *self, PyObject *args); static PyObject* sDumpConfig (PyObject *self, PyObject *args); static PyObject* sAddImportType (PyObject *self, PyObject *args); static PyObject* sChangeImportModule(PyObject *self, PyObject *args); static PyObject* sGetImportType (PyObject *self, PyObject *args); static PyObject* sAddExportType (PyObject *self, PyObject *args); static PyObject* sChangeExportModule(PyObject *self, PyObject *args); static PyObject* sGetExportType (PyObject *self, PyObject *args); static PyObject* sGetResourcePath (PyObject *self, PyObject *args); static PyObject* sGetLibraryPath (PyObject *self, PyObject *args); static PyObject* sGetTempPath (PyObject *self, PyObject *args); static PyObject* sGetUserCachePath (PyObject *self, PyObject *args); static PyObject* sGetUserConfigPath (PyObject *self, PyObject *args); static PyObject* sGetUserAppDataPath(PyObject *self, PyObject *args); static PyObject* sGetUserMacroPath (PyObject *self, PyObject *args); static PyObject* sGetHelpPath (PyObject *self, PyObject *args); static PyObject* sGetHomePath (PyObject *self, PyObject *args); static PyObject* sLoadFile (PyObject *self,PyObject *args); static PyObject* sOpenDocument (PyObject *self,PyObject *args, PyObject *kwd); static PyObject* sSaveDocument (PyObject *self,PyObject *args); static PyObject* sSaveDocumentAs (PyObject *self,PyObject *args); static PyObject* sNewDocument (PyObject *self,PyObject *args, PyObject *kwd); static PyObject* sCloseDocument (PyObject *self,PyObject *args); static PyObject* sActiveDocument (PyObject *self,PyObject *args); static PyObject* sSetActiveDocument (PyObject *self,PyObject *args); static PyObject* sGetDocument (PyObject *self,PyObject *args); static PyObject* sListDocuments (PyObject *self,PyObject *args); static PyObject* sAddDocObserver (PyObject *self,PyObject *args); static PyObject* sRemoveDocObserver (PyObject *self,PyObject *args); static PyObject *sIsRestoring (PyObject *self,PyObject *args); static PyObject *sSetLogLevel (PyObject *self,PyObject *args); static PyObject *sGetLogLevel (PyObject *self,PyObject *args); static PyObject *sCheckLinkDepth (PyObject *self,PyObject *args); static PyObject *sGetLinksTo (PyObject *self,PyObject *args); static PyObject *sGetDependentObjects(PyObject *self,PyObject *args); static PyObject *sSetActiveTransaction (PyObject *self,PyObject *args); static PyObject *sGetActiveTransaction (PyObject *self,PyObject *args); static PyObject *sCloseActiveTransaction(PyObject *self,PyObject *args); static PyObject *sCheckAbort(PyObject *self,PyObject *args); static PyMethodDef Methods[]; // clang-format on friend class ApplicationObserver; /** @name Private Init, Destruct an Access methods */ //@{ static void initConfig(int argc, char ** argv); static void initApplication(); static void logStatus(); // the one and only pointer to the application object static Application *_pcSingleton; /// checks if the environment is alright //static void CheckEnv(void); /// Search for the FreeCAD home path based on argv[0] /*! * There are multiple implementations of this method per-OS */ static std::string FindHomePath(const char* sCall); /// Print the help message static void PrintInitHelp(); /// figure out some things static void ExtractUserPath(); /// load the user and system parameter set static void LoadParameters(); /// puts the given env variable in the config static void SaveEnv(const char *); /// startup configuration container static std::map mConfig; /// Management of and access to applications directories static std::unique_ptr _appDirs; static int _argc; static char ** _argv; //@} struct FileTypeItem { std::string filter; std::string module; std::vector types; }; /// open ending information std::vector _mImportTypes; std::vector _mExportTypes; std::map DocMap; mutable std::map DocFileMap; std::map> mpcPramManager; std::map &_mConfig; App::Document* _pActiveDoc{nullptr}; std::deque _pendingDocs; std::deque _pendingDocsReopen; std::map > _pendingDocMap; // To prevent infinite recursion of reloading a partial document due a truly // missing object std::map > _docReloadAttempts; bool _isRestoring{false}; bool _allowPartial{false}; bool _isClosingAll{false}; // for estimate max link depth int _objCount{-1}; friend class AutoTransaction; std::string _activeTransactionName; int _activeTransactionID{0}; int _activeTransactionGuard{0}; bool _activeTransactionTmpName{false}; Base::ProgressIndicator _progressIndicator; static Base::ConsoleObserverStd *_pConsoleObserverStd; static Base::ConsoleObserverFile *_pConsoleObserverFile; }; /// Singleton getter of the Application inline App::Application &GetApplication(){ return *App::Application::_pcSingleton; } } // namespace App #endif // SRC_APP_APPLICATION_H_