1
/***************************************************************************
2
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This program is free software; you can redistribute it and/or modify *
7
* it under the terms of the GNU Library General Public License (LGPL) *
8
* as published by the Free Software Foundation; either version 2 of *
9
* the License, or (at your option) any later version. *
10
* for detail see the LICENCE text file. *
12
* FreeCAD is distributed in the hope that it will be useful, *
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15
* GNU Library General Public License for more details. *
17
* You should have received a copy of the GNU Library General Public *
18
* License along with FreeCAD; if not, write to the Free Software *
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
22
***************************************************************************/
24
#include "PreCompiled.h"
27
# if defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD)
30
# include <sys/types.h>
31
# elif defined(__MINGW32__)
33
# define WINVER 0x502 // needed for SetDllDirectory
36
# include <boost/program_options.hpp>
37
# include <boost/date_time/posix_time/posix_time.hpp>
47
#include <sys/sysctl.h>
52
#include <QProcessEnvironment>
53
#include <QStandardPaths>
54
#include <LibraryVersions.h>
56
#include <App/MaterialPy.h>
57
#include <App/MetadataPy.h>
59
#include <Base/AxisPy.h>
60
#include <Base/BaseClass.h>
61
#include <Base/BoundBoxPy.h>
62
#include <Base/ConsoleObserver.h>
63
#include <Base/CoordinateSystemPy.h>
64
#include <Base/Exception.h>
65
#include <Base/ExceptionFactory.h>
66
#include <Base/FileInfo.h>
67
#include <Base/GeometryPyCXX.h>
68
#include <Base/Interpreter.h>
69
#include <Base/MatrixPy.h>
70
#include <Base/QuantityPy.h>
71
#include <Base/Parameter.h>
72
#include <Base/Persistence.h>
73
#include <Base/PlacementPy.h>
74
#include <Base/PrecisionPy.h>
75
#include <Base/ProgressIndicatorPy.h>
76
#include <Base/RotationPy.h>
77
#include <Base/Tools.h>
78
#include <Base/Translate.h>
80
#include <Base/TypePy.h>
81
#include <Base/UnitPy.h>
82
#include <Base/UnitsApi.h>
83
#include <Base/VectorPy.h>
85
#include "Annotation.h"
86
#include "Application.h"
87
#include "ComplexGeoData.h"
88
#include "DocumentObjectFileIncluded.h"
89
#include "DocumentObjectGroup.h"
90
#include "DocumentObjectGroupPy.h"
91
#include "DocumentObserver.h"
92
#include "DocumentPy.h"
93
#include "ExpressionParser.h"
94
#include "FeatureTest.h"
95
#include "FeaturePython.h"
96
#include "GeoFeature.h"
97
#include "GeoFeatureGroupExtension.h"
98
#include "ImagePlane.h"
99
#include "InventorObject.h"
101
#include "LinkBaseExtensionPy.h"
103
#include "MaterialObject.h"
104
#include "MeasureDistance.h"
106
#include "OriginFeature.h"
107
#include "OriginGroupExtension.h"
108
#include "OriginGroupExtensionPy.h"
109
#include "SuppressibleExtension.h"
110
#include "SuppressibleExtensionPy.h"
113
#include "Placement.h"
114
#include "ProgramOptionsUtilities.h"
116
#include "PropertyContainer.h"
117
#include "PropertyExpressionEngine.h"
118
#include "PropertyFile.h"
119
#include "PropertyLinks.h"
120
#include "PropertyPythonObject.h"
121
#include "StringHasherPy.h"
122
#include "StringIDPy.h"
123
#include "TextDocument.h"
124
#include "Transactions.h"
125
#include "VRMLObject.h"
127
// If you stumble here, run the target "BuildExtractRevision" on Windows systems
128
// or the Python script "SubWCRev.py" on Linux based systems which builds
129
// src/Build/Version.h. Or create your own from src/Build/Version.h.in!
130
#include <Build/Version.h>
134
// scriptings (scripts are built-in but can be overridden by command line option)
135
#include <App/InitScript.h>
136
#include <App/TestScript.h>
137
#include <App/CMakeScript.h>
139
#ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler
140
# pragma warning( disable : 4535 )
141
# if !defined(_DEBUG) && defined(HAVE_SEH)
142
# define FC_SE_TRANSLATOR
146
# include <eh.h> // VC exception handling
147
#else // Ansi C/C++ new handler
151
FC_LOG_LEVEL_INIT("App", true, true)
156
using namespace boost;
157
using namespace boost::program_options;
159
namespace sp = std::placeholders;
161
//==========================================================================
163
//==========================================================================
165
Base::Reference<ParameterManager> App::Application::_pcSysParamMngr;
166
Base::Reference<ParameterManager> App::Application::_pcUserParamMngr;
167
Base::ConsoleObserverStd *Application::_pConsoleObserverStd = nullptr;
168
Base::ConsoleObserverFile *Application::_pConsoleObserverFile = nullptr;
170
AppExport std::map<std::string, std::string> Application::mConfig;
173
//**************************************************************************
174
// Construction and destruction
176
PyDoc_STRVAR(FreeCAD_doc,
177
"The functions in the FreeCAD module allow working with documents.\n"
178
"The FreeCAD instance provides a list of references of documents which\n"
179
"can be addressed by a string. Hence the document name must be unique.\n"
181
"The document has the read-only attribute FileName which points to the\n"
182
"file the document should be stored to.\n"
185
PyDoc_STRVAR(Console_doc,
186
"FreeCAD Console module.\n\n"
187
"The Console module contains functions to manage log entries, messages,\n"
188
"warnings and errors.\n"
189
"There are also functions to get/set the status of the observers used as\n"
190
"logging interfaces."
193
PyDoc_STRVAR(Base_doc,
194
"The Base module contains the classes for the geometric basics\n"
195
"like vector, matrix, bounding box, placement, rotation, axis, ...\n"
198
// This is called via the PyImport_AppendInittab mechanism called
199
// during initialization, to make the built-in __FreeCADBase__
200
// module known to Python.
202
init_freecad_base_module(void)
204
static struct PyModuleDef BaseModuleDef = {
205
PyModuleDef_HEAD_INIT,
206
"__FreeCADBase__", Base_doc, -1,
207
nullptr, nullptr, nullptr, nullptr, nullptr
209
return PyModule_Create(&BaseModuleDef);
212
// Set in inside Application
213
static PyMethodDef* ApplicationMethods = nullptr;
216
init_freecad_module(void)
218
static struct PyModuleDef FreeCADModuleDef = {
219
PyModuleDef_HEAD_INIT,
220
"FreeCAD", FreeCAD_doc, -1,
222
nullptr, nullptr, nullptr, nullptr
224
return PyModule_Create(&FreeCADModuleDef);
230
static struct PyModuleDef ImageModuleDef = {
231
PyModuleDef_HEAD_INIT,
234
nullptr, nullptr, nullptr, nullptr
236
return PyModule_Create(&ImageModuleDef);
239
Application::Application(std::map<std::string,std::string> &mConfig)
242
//_hApp = new ApplicationOCC;
243
mpcPramManager["System parameter"] = _pcSysParamMngr;
244
mpcPramManager["User parameter"] = _pcUserParamMngr;
249
Application::~Application() = default;
251
void Application::setupPythonTypes()
253
// setting up Python binding
254
Base::PyGILStateLocker lock;
255
PyObject* modules = PyImport_GetModuleDict();
257
ApplicationMethods = Application::Methods;
258
PyObject* pAppModule = PyImport_ImportModule ("FreeCAD");
261
pAppModule = init_freecad_module();
262
PyDict_SetItemString(modules, "FreeCAD", pAppModule);
264
Py::Module(pAppModule).setAttr(std::string("ActiveDocument"),Py::None());
266
static struct PyModuleDef ConsoleModuleDef = {
267
PyModuleDef_HEAD_INIT,
268
"__FreeCADConsole__", Console_doc, -1,
269
Base::ConsoleSingleton::Methods,
270
nullptr, nullptr, nullptr, nullptr
272
PyObject* pConsoleModule = PyModule_Create(&ConsoleModuleDef);
275
PyObject* imageModule = init_image_module();
276
PyDict_SetItemString(modules, "Image", imageModule);
278
// introducing additional classes
280
// NOTE: To finish the initialization of our own type objects we must
281
// call PyType_Ready, otherwise we run into a segmentation fault, later on.
282
// This function is responsible for adding inherited slots from a type's base class.
283
Base::Interpreter().addType(&Base::VectorPy::Type, pAppModule, "Vector");
284
Base::Interpreter().addType(&Base::MatrixPy::Type, pAppModule, "Matrix");
285
Base::Interpreter().addType(&Base::BoundBoxPy::Type, pAppModule, "BoundBox");
286
Base::Interpreter().addType(&Base::PlacementPy::Type, pAppModule, "Placement");
287
Base::Interpreter().addType(&Base::RotationPy::Type, pAppModule, "Rotation");
288
Base::Interpreter().addType(&Base::AxisPy::Type, pAppModule, "Axis");
290
// Note: Create an own module 'Base' which should provide the python
291
// binding classes from the base module. At a later stage we should
292
// remove these types from the FreeCAD module.
294
PyObject* pBaseModule = PyImport_ImportModule ("__FreeCADBase__");
297
pBaseModule = init_freecad_base_module();
298
PyDict_SetItemString(modules, "__FreeCADBase__", pBaseModule);
301
setupPythonException(pBaseModule);
305
Base::Interpreter().addType(&Base::VectorPy ::Type,pBaseModule,"Vector");
306
Base::Interpreter().addType(&Base::MatrixPy ::Type,pBaseModule,"Matrix");
307
Base::Interpreter().addType(&Base::BoundBoxPy ::Type,pBaseModule,"BoundBox");
308
Base::Interpreter().addType(&Base::PlacementPy ::Type,pBaseModule,"Placement");
309
Base::Interpreter().addType(&Base::RotationPy ::Type,pBaseModule,"Rotation");
310
Base::Interpreter().addType(&Base::AxisPy ::Type,pBaseModule,"Axis");
311
Base::Interpreter().addType(&Base::CoordinateSystemPy::Type,pBaseModule,"CoordinateSystem");
312
Base::Interpreter().addType(&Base::TypePy ::Type,pBaseModule,"TypeId");
313
Base::Interpreter().addType(&Base::PrecisionPy ::Type,pBaseModule,"Precision");
315
Base::Interpreter().addType(&App::MaterialPy::Type, pAppModule, "Material");
316
Base::Interpreter().addType(&App::MetadataPy::Type, pAppModule, "Metadata");
318
Base::Interpreter().addType(&App::StringHasherPy::Type, pAppModule, "StringHasher");
319
Base::Interpreter().addType(&App::StringIDPy::Type, pAppModule, "StringID");
321
// Add document types
322
Base::Interpreter().addType(&App::PropertyContainerPy::Type, pAppModule, "PropertyContainer");
323
Base::Interpreter().addType(&App::ExtensionContainerPy::Type, pAppModule, "ExtensionContainer");
324
Base::Interpreter().addType(&App::DocumentPy::Type, pAppModule, "Document");
325
Base::Interpreter().addType(&App::DocumentObjectPy::Type, pAppModule, "DocumentObject");
326
Base::Interpreter().addType(&App::DocumentObjectGroupPy::Type, pAppModule, "DocumentObjectGroup");
327
Base::Interpreter().addType(&App::GeoFeaturePy::Type, pAppModule, "GeoFeature");
328
Base::Interpreter().addType(&App::PartPy::Type, pAppModule, "Part");
330
// Add extension types
331
Base::Interpreter().addType(&App::ExtensionPy::Type, pAppModule, "Extension");
332
Base::Interpreter().addType(&App::DocumentObjectExtensionPy::Type, pAppModule, "DocumentObjectExtension");
333
Base::Interpreter().addType(&App::GroupExtensionPy::Type, pAppModule, "GroupExtension");
334
Base::Interpreter().addType(&App::GeoFeatureGroupExtensionPy::Type, pAppModule, "GeoFeatureGroupExtension");
335
Base::Interpreter().addType(&App::OriginGroupExtensionPy::Type, pAppModule, "OriginGroupExtension");
336
Base::Interpreter().addType(&App::LinkBaseExtensionPy::Type, pAppModule, "LinkBaseExtension");
338
//insert Base and Console
339
Py_INCREF(pBaseModule);
340
PyModule_AddObject(pAppModule, "Base", pBaseModule);
341
Py_INCREF(pConsoleModule);
342
PyModule_AddObject(pAppModule, "Console", pConsoleModule);
345
PyObject* pTranslateModule = Base::Interpreter().addModule(new Base::Translate);
346
Py_INCREF(pTranslateModule);
347
PyModule_AddObject(pAppModule, "Qt", pTranslateModule);
349
//insert Units module
350
static struct PyModuleDef UnitsModuleDef = {
351
PyModuleDef_HEAD_INIT,
352
"Units", "The Unit API", -1,
353
Base::UnitsApi::Methods,
354
nullptr, nullptr, nullptr, nullptr
356
PyObject* pUnitsModule = PyModule_Create(&UnitsModuleDef);
357
Base::Interpreter().addType(&Base::QuantityPy ::Type,pUnitsModule,"Quantity");
358
// make sure to set the 'nb_true_divide' slot
359
Base::Interpreter().addType(&Base::UnitPy ::Type,pUnitsModule,"Unit");
361
Py_INCREF(pUnitsModule);
362
PyModule_AddObject(pAppModule, "Units", pUnitsModule);
364
Base::ProgressIndicatorPy::init_type();
365
Base::Interpreter().addType(Base::ProgressIndicatorPy::type_object(),
366
pBaseModule,"ProgressIndicator");
368
Base::Vector2dPy::init_type();
369
Base::Interpreter().addType(Base::Vector2dPy::type_object(),
370
pBaseModule,"Vector2d");
373
void Application::setupPythonException(PyObject* module)
375
// Define custom Python exception types
377
Base::PyExc_FC_GeneralError = PyErr_NewException("Base.FreeCADError", PyExc_RuntimeError, nullptr);
378
Py_INCREF(Base::PyExc_FC_GeneralError);
379
PyModule_AddObject(module, "FreeCADError", Base::PyExc_FC_GeneralError);
381
Base::PyExc_FC_FreeCADAbort = PyErr_NewException("Base.FreeCADAbort", PyExc_BaseException, nullptr);
382
Py_INCREF(Base::PyExc_FC_FreeCADAbort);
383
PyModule_AddObject(module, "FreeCADAbort", Base::PyExc_FC_FreeCADAbort);
385
Base::PyExc_FC_XMLBaseException = PyErr_NewException("Base.XMLBaseException", PyExc_Exception, nullptr);
386
Py_INCREF(Base::PyExc_FC_XMLBaseException);
387
PyModule_AddObject(module, "XMLBaseException", Base::PyExc_FC_XMLBaseException);
389
Base::PyExc_FC_XMLParseException = PyErr_NewException("Base.XMLParseException", Base::PyExc_FC_XMLBaseException, nullptr);
390
Py_INCREF(Base::PyExc_FC_XMLParseException);
391
PyModule_AddObject(module, "XMLParseException", Base::PyExc_FC_XMLParseException);
393
Base::PyExc_FC_XMLAttributeError = PyErr_NewException("Base.XMLAttributeError", Base::PyExc_FC_XMLBaseException, nullptr);
394
Py_INCREF(Base::PyExc_FC_XMLAttributeError);
395
PyModule_AddObject(module, "XMLAttributeError", Base::PyExc_FC_XMLAttributeError);
397
Base::PyExc_FC_UnknownProgramOption = PyErr_NewException("Base.UnknownProgramOption", PyExc_BaseException, nullptr);
398
Py_INCREF(Base::PyExc_FC_UnknownProgramOption);
399
PyModule_AddObject(module, "UnknownProgramOption", Base::PyExc_FC_UnknownProgramOption);
401
Base::PyExc_FC_BadFormatError = PyErr_NewException("Base.BadFormatError", Base::PyExc_FC_GeneralError, nullptr);
402
Py_INCREF(Base::PyExc_FC_BadFormatError);
403
PyModule_AddObject(module, "BadFormatError", Base::PyExc_FC_BadFormatError);
405
Base::PyExc_FC_BadGraphError = PyErr_NewException("Base.BadGraphError", Base::PyExc_FC_GeneralError, nullptr);
406
Py_INCREF(Base::PyExc_FC_BadGraphError);
407
PyModule_AddObject(module, "BadGraphError", Base::PyExc_FC_BadGraphError);
409
Base::PyExc_FC_ExpressionError = PyErr_NewException("Base.ExpressionError", Base::PyExc_FC_GeneralError, nullptr);
410
Py_INCREF(Base::PyExc_FC_ExpressionError);
411
PyModule_AddObject(module, "ExpressionError", Base::PyExc_FC_ExpressionError);
413
Base::PyExc_FC_ParserError = PyErr_NewException("Base.ParserError", Base::PyExc_FC_GeneralError, nullptr);
414
Py_INCREF(Base::PyExc_FC_ParserError);
415
PyModule_AddObject(module, "ParserError", Base::PyExc_FC_ParserError);
417
Base::PyExc_FC_CADKernelError = PyErr_NewException("Base.CADKernelError", Base::PyExc_FC_GeneralError, nullptr);
418
Py_INCREF(Base::PyExc_FC_CADKernelError);
419
PyModule_AddObject(module, "CADKernelError", Base::PyExc_FC_CADKernelError);
422
//**************************************************************************
425
/// get called by the document when the name is changing
426
void Application::renameDocument(const char *OldName, const char *NewName)
430
throw Base::RuntimeError("Renaming document internal name is no longer allowed!");
433
Document* Application::newDocument(const char * Name, const char * UserName, bool createView, bool tempDoc)
435
auto getNameAndLabel = [this](const char * Name, const char * UserName) -> std::tuple<std::string, std::string> {
436
bool defaultName = (!Name || Name[0] == '\0');
438
// get a valid name anyway!
443
std::string userName;
444
if (UserName && UserName[0] != '\0') {
448
userName = defaultName ? QObject::tr("Unnamed").toStdString() : Name;
450
std::vector<std::string> names;
451
names.reserve(DocMap.size());
452
for (const auto& pos : DocMap) {
453
names.emplace_back(pos.second->Label.getValue());
456
if (!names.empty()) {
457
userName = Base::Tools::getUniqueName(userName, names);
461
return std::make_tuple(std::string(Name), userName);
464
auto tuple = getNameAndLabel(Name, UserName);
465
std::string name = std::get<0>(tuple);
466
std::string userName = std::get<1>(tuple);
467
name = getUniqueDocumentName(name.c_str(), tempDoc);
469
// return the temporary document if it exists
471
auto it = DocMap.find(name);
472
if (it != DocMap.end() && it->second->testStatus(Document::TempDoc))
476
// create the FreeCAD document
477
std::unique_ptr<Document> newDoc(new Document(name.c_str()));
478
newDoc->setStatus(Document::TempDoc, tempDoc);
480
auto oldActiveDoc = _pActiveDoc;
481
auto doc = newDoc.release(); // now owned by the Application
483
// add the document to the internal list
488
// connect the signals to the application for the new document
489
_pActiveDoc->signalBeforeChange.connect(std::bind(&App::Application::slotBeforeChangeDocument, this, sp::_1, sp::_2));
490
_pActiveDoc->signalChanged.connect(std::bind(&App::Application::slotChangedDocument, this, sp::_1, sp::_2));
491
_pActiveDoc->signalNewObject.connect(std::bind(&App::Application::slotNewObject, this, sp::_1));
492
_pActiveDoc->signalDeletedObject.connect(std::bind(&App::Application::slotDeletedObject, this, sp::_1));
493
_pActiveDoc->signalBeforeChangeObject.connect(std::bind(&App::Application::slotBeforeChangeObject, this, sp::_1, sp::_2));
494
_pActiveDoc->signalChangedObject.connect(std::bind(&App::Application::slotChangedObject, this, sp::_1, sp::_2));
495
_pActiveDoc->signalRelabelObject.connect(std::bind(&App::Application::slotRelabelObject, this, sp::_1));
496
_pActiveDoc->signalActivatedObject.connect(std::bind(&App::Application::slotActivatedObject, this, sp::_1));
497
_pActiveDoc->signalUndo.connect(std::bind(&App::Application::slotUndoDocument, this, sp::_1));
498
_pActiveDoc->signalRedo.connect(std::bind(&App::Application::slotRedoDocument, this, sp::_1));
499
_pActiveDoc->signalRecomputedObject.connect(std::bind(&App::Application::slotRecomputedObject, this, sp::_1));
500
_pActiveDoc->signalRecomputed.connect(std::bind(&App::Application::slotRecomputed, this, sp::_1));
501
_pActiveDoc->signalBeforeRecompute.connect(std::bind(&App::Application::slotBeforeRecompute, this, sp::_1));
502
_pActiveDoc->signalOpenTransaction.connect(std::bind(&App::Application::slotOpenTransaction, this, sp::_1, sp::_2));
503
_pActiveDoc->signalCommitTransaction.connect(std::bind(&App::Application::slotCommitTransaction, this, sp::_1));
504
_pActiveDoc->signalAbortTransaction.connect(std::bind(&App::Application::slotAbortTransaction, this, sp::_1));
505
_pActiveDoc->signalStartSave.connect(std::bind(&App::Application::slotStartSaveDocument, this, sp::_1, sp::_2));
506
_pActiveDoc->signalFinishSave.connect(std::bind(&App::Application::slotFinishSaveDocument, this, sp::_1, sp::_2));
507
_pActiveDoc->signalChangePropertyEditor.connect(std::bind(&App::Application::slotChangePropertyEditor, this, sp::_1, sp::_2));
510
// make sure that the active document is set in case no GUI is up
512
Base::PyGILStateLocker lock;
513
Py::Object active(_pActiveDoc->getPyObject(), true);
514
Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"), active);
517
signalNewDocument(*_pActiveDoc, createView);
519
// set the UserName after notifying all observers
520
_pActiveDoc->Label.setValue(userName);
522
// set the old document active again if the new is temporary
523
if (tempDoc && oldActiveDoc)
524
setActiveDocument(oldActiveDoc);
529
bool Application::closeDocument(const char* name)
531
map<string,Document*>::iterator pos = DocMap.find( name );
532
if (pos == DocMap.end()) // no such document
535
Base::ConsoleRefreshDisabler disabler;
537
// Trigger observers before removing the document from the internal map.
538
// Some observers might rely on this document still being there.
539
signalDeleteDocument(*pos->second);
541
// For exception-safety use a smart pointer
542
if (_pActiveDoc == pos->second)
543
setActiveDocument(static_cast<Document*>(nullptr));
544
std::unique_ptr<Document> delDoc (pos->second);
546
DocFileMap.erase(FileInfo(delDoc->FileName.getValue()).filePath());
550
// Trigger observers after removing the document from the internal map.
551
signalDeletedDocument();
556
void Application::closeAllDocuments()
558
Base::FlagToggler<bool> flag(_isClosingAll);
559
std::map<std::string,Document*>::iterator pos;
560
while((pos = DocMap.begin()) != DocMap.end())
561
closeDocument(pos->first.c_str());
564
App::Document* Application::getDocument(const char *Name) const
566
std::map<std::string,Document*>::const_iterator pos;
568
pos = DocMap.find(Name);
570
if (pos == DocMap.end())
576
const char * Application::getDocumentName(const App::Document* doc) const
578
for (const auto & it : DocMap) {
579
if (it.second == doc) {
580
return it.first.c_str();
587
std::vector<App::Document*> Application::getDocuments() const
589
std::vector<App::Document*> docs;
590
for (const auto & it : DocMap)
591
docs.push_back(it.second);
595
std::string Application::getUniqueDocumentName(const char *Name, bool tempDoc) const
597
if (!Name || *Name == '\0')
599
std::string CleanName = Base::Tools::getIdentifier(Name);
602
std::map<string,Document*>::const_iterator pos;
603
pos = DocMap.find(CleanName);
605
if (pos == DocMap.end() || (tempDoc && pos->second->testStatus(Document::TempDoc))) {
606
// if not, name is OK
610
std::vector<std::string> names;
611
names.reserve(DocMap.size());
612
for (pos = DocMap.begin(); pos != DocMap.end(); ++pos) {
613
if (!tempDoc || !pos->second->testStatus(Document::TempDoc))
614
names.push_back(pos->first);
616
return Base::Tools::getUniqueName(CleanName, names);
620
int Application::addPendingDocument(const char *FileName, const char *objName, bool allowPartial)
624
if(allowPartial && _allowPartial)
626
assert(FileName && FileName[0]);
627
assert(objName && objName[0]);
628
if(!_docReloadAttempts[FileName].emplace(objName).second)
630
auto ret = _pendingDocMap.emplace(FileName,std::vector<std::string>());
631
ret.first->second.emplace_back(objName);
633
_pendingDocs.emplace_back(ret.first->first.c_str());
639
bool Application::isRestoring() const {
640
return _isRestoring || Document::isAnyRestoring();
643
bool Application::isClosingAll() const {
644
return _isClosingAll;
648
FC_DURATION_DECLARE(d1);
649
FC_DURATION_DECLARE(d2);
651
FC_DURATION_INIT(d1);
652
FC_DURATION_INIT(d2);
659
boost::signals2::signal<void ()> &signal;
660
DocOpenGuard(bool &f, boost::signals2::signal<void ()> &s)
671
catch (const boost::exception&) {
672
// reported by code analyzers
673
Base::Console().Warning("~DocOpenGuard: Unexpected boost exception\n");
679
Document* Application::openDocument(const char * FileName, bool createView) {
680
std::vector<std::string> filenames(1,FileName);
681
auto docs = openDocuments(filenames, nullptr, nullptr, nullptr, createView);
687
Document *Application::getDocumentByPath(const char *path, PathMatchMode checkCanonical) const {
688
if(!path || !path[0])
690
if(DocFileMap.empty()) {
691
for(const auto &v : DocMap) {
692
const auto &file = v.second->FileName.getStrValue();
694
DocFileMap[FileInfo(file.c_str()).filePath()] = v.second;
697
auto it = DocFileMap.find(FileInfo(path).filePath());
698
if(it != DocFileMap.end())
701
if (checkCanonical == PathMatchMode::MatchAbsolute)
704
std::string filepath = FileInfo(path).filePath();
705
QString canonicalPath = QFileInfo(QString::fromUtf8(path)).canonicalFilePath();
706
for (const auto &v : DocMap) {
707
QFileInfo fi(QString::fromUtf8(v.second->FileName.getValue()));
708
if (canonicalPath == fi.canonicalFilePath()) {
709
if (checkCanonical == PathMatchMode::MatchCanonical)
711
bool samePath = (canonicalPath == QString::fromUtf8(filepath.c_str()));
712
FC_WARN("Identical physical path '" << canonicalPath.toUtf8().constData() << "'\n"
713
<< (samePath?"":" for file '") << (samePath?"":filepath.c_str()) << (samePath?"":"'\n")
714
<< " with existing document '" << v.second->Label.getValue()
715
<< "' in path: '" << v.second->FileName.getValue() << "'");
722
std::vector<Document*> Application::openDocuments(const std::vector<std::string> &filenames,
723
const std::vector<std::string> *paths,
724
const std::vector<std::string> *labels,
725
std::vector<std::string> *errs,
728
std::vector<Document*> res(filenames.size(), nullptr);
729
if (filenames.empty())
733
errs->resize(filenames.size());
735
DocOpenGuard guard(_isRestoring, signalFinishOpenDocument);
736
_pendingDocs.clear();
737
_pendingDocsReopen.clear();
738
_pendingDocMap.clear();
739
_docReloadAttempts.clear();
741
signalStartOpenDocument();
743
ParameterGrp::handle hGrp = GetParameterGroupByPath("User parameter:BaseApp/Preferences/Document");
744
_allowPartial = !hGrp->GetBool("NoPartialLoading",false);
746
for (auto &name : filenames)
747
_pendingDocs.emplace_back(name.c_str());
749
std::map<DocumentT, DocTiming> timings;
753
std::vector<DocumentT> openedDocs;
757
std::set<App::DocumentT> newDocs;
758
for (std::size_t count=0;; ++count) {
759
std::string name = std::move(_pendingDocs.front());
760
_pendingDocs.pop_front();
761
bool isMainDoc = (pass == 0 && count < filenames.size());
765
std::vector<std::string> objNames;
767
auto it = _pendingDocMap.find(name);
768
if (it != _pendingDocMap.end()) {
772
objNames.swap(it->second);
773
_pendingDocMap.erase(it);
780
const char *path = name.c_str();
781
const char *label = nullptr;
783
if (paths && paths->size()>count)
784
path = (*paths)[count].c_str();
786
if (labels && labels->size()>count)
787
label = (*labels)[count].c_str();
790
auto doc = openDocumentPrivate(path, name.c_str(), label, isMainDoc, createView, std::move(objNames));
791
FC_DURATION_PLUS(timing.d1,t1);
793
timings[doc].d1 += timing.d1;
794
newDocs.emplace(doc);
801
catch (const Base::Exception &e) {
803
if (!errs && isMainDoc)
805
if (errs && isMainDoc)
806
(*errs)[count] = e.what();
808
Base::Console().Error("Exception opening file: %s [%s]\n", name.c_str(), e.what());
810
catch (const std::exception &e) {
811
if (!errs && isMainDoc)
813
if (errs && isMainDoc)
814
(*errs)[count] = e.what();
816
Base::Console().Error("Exception opening file: %s [%s]\n", name.c_str(), e.what());
821
(*errs)[count] = "unknown error";
824
_pendingDocs.clear();
825
_pendingDocsReopen.clear();
826
_pendingDocMap.clear();
831
if (_pendingDocs.empty()) {
832
if(_pendingDocsReopen.empty())
834
_pendingDocs = std::move(_pendingDocsReopen);
835
_pendingDocsReopen.clear();
836
for(const auto &file : _pendingDocs) {
837
auto doc = getDocumentByPath(file.c_str());
839
closeDocument(doc->getName());
845
_pendingDocMap.clear();
847
std::vector<Document*> docs;
848
docs.reserve(newDocs.size());
849
for(const auto &d : newDocs) {
850
auto doc = d.getDocument();
853
// Notify PropertyXLink to attach newly opened documents and restore
854
// relevant external links
855
PropertyXLink::restoreDocument(*doc);
859
Base::SequencerLauncher seq("Postprocessing...", docs.size());
861
// After external links has been restored, we can now sort the document
862
// according to their dependency order.
864
docs = Document::getDependentDocuments(docs, true);
865
} catch (Base::Exception &e) {
868
for(auto it=docs.begin(); it!=docs.end();) {
871
// It is possible that the newly opened document depends on an existing
872
// document, which will be included with the above call to
873
// Document::getDependentDocuments(). Make sure to exclude that.
874
if(!newDocs.count(doc)) {
879
auto &timing = timings[doc];
881
// Finalize document restoring with the correct order
882
if(doc->afterRestore(true)) {
883
openedDocs.emplace_back(doc);
887
// Here means this is a partial loaded document, and we need to
888
// reload it fully because of touched objects. The reason of
889
// reloading a partial document with touched object is because
890
// partial document is supposed to be readonly, while a
891
// 'touched' object requires recomputation. And an object may
892
// become touched during restoring if externally linked
893
// document time stamp mismatches with the stamp saved.
894
_pendingDocs.emplace_back(doc->FileName.getValue());
895
_pendingDocMap.erase(doc->FileName.getValue());
897
FC_DURATION_PLUS(timing.d2,t1);
900
// Close the document for reloading
901
for(const auto doc : docs)
902
closeDocument(doc->getName());
904
}while(!_pendingDocs.empty());
906
// Set the active document using the first successfully restored main
907
// document (i.e. documents explicitly asked for by caller).
908
for (auto doc : res) {
910
setActiveDocument(doc);
915
for (auto &doc : openedDocs) {
916
auto &timing = timings[doc];
917
FC_DURATION_LOG(timing.d1, doc.getDocumentName() << " restore");
918
FC_DURATION_LOG(timing.d2, doc.getDocumentName() << " postprocess");
920
FC_TIME_LOG(t,"total");
921
_isRestoring = false;
923
signalFinishOpenDocument();
927
Document* Application::openDocumentPrivate(const char * FileName,
928
const char *propFileName, const char *label,
929
bool isMainDoc, bool createView,
930
std::vector<std::string> &&objNames)
932
FileInfo File(FileName);
934
if (!File.exists()) {
935
std::stringstream str;
936
str << "File '" << FileName << "' does not exist!";
937
throw Base::FileSystemError(str.str().c_str());
940
// Before creating a new document we check whether the document is already open
941
auto doc = getDocumentByPath(File.filePath().c_str(), PathMatchMode::MatchCanonicalWarning);
943
if(doc->testStatus(App::Document::PartialDoc)
944
|| doc->testStatus(App::Document::PartialRestore)) {
945
// Here means a document is already partially loaded, but the document
946
// is requested again, either partial or not. We must check if the
947
// document contains the required object
950
// Main document must be open fully, so close and reopen
951
closeDocument(doc->getName());
953
} else if(_allowPartial) {
955
for(const auto &name : objNames) {
956
auto obj = doc->getObject(name.c_str());
957
if(!obj || obj->testStatus(App::PartialObject)) {
959
// NOTE: We are about to reload this document with
960
// extra objects. However, it is possible to repeat
961
// this process several times, if it is linked by
962
// multiple documents and each with a different set of
963
// objects. To partially solve this problem, we do not
964
// close and reopen the document immediately here, but
965
// add it to _pendingDocsReopen to delay reloading.
966
for(auto obj : doc->getObjects())
967
objNames.emplace_back(obj->getNameInDocument());
968
_pendingDocMap[doc->FileName.getValue()] = std::move(objNames);
977
_pendingDocsReopen.emplace_back(FileName);
989
if(propFileName != FileName) {
990
FileInfo fi(propFileName);
991
name = fi.fileNamePure();
993
name = File.fileNamePure();
995
// Use the same name for the internal and user name.
996
// The file name is UTF-8 encoded which means that the internal name will be modified
997
// to only contain valid ASCII characters but the user name will be kept.
999
label = name.c_str();
1000
Document* newDoc = newDocument(name.c_str(),label,isMainDoc && createView);
1002
newDoc->FileName.setValue(propFileName==FileName?File.filePath():propFileName);
1005
// read the document
1006
newDoc->restore(File.filePath().c_str(),true,objNames);
1007
if(!DocFileMap.empty())
1008
DocFileMap[FileInfo(newDoc->FileName.getValue()).filePath()] = newDoc;
1011
// if the project file itself is corrupt then
1012
// close the document
1013
catch (const Base::FileException&) {
1014
closeDocument(newDoc->getName());
1017
catch (const std::ios_base::failure&) {
1018
closeDocument(newDoc->getName());
1021
// but for any other exceptions leave it open to give the
1022
// user a chance to fix it
1028
Document* Application::getActiveDocument() const
1033
void Application::setActiveDocument(Document* pDoc)
1037
// make sure that the active document is set in case no GUI is up
1039
Base::PyGILStateLocker lock;
1040
Py::Object active(pDoc->getPyObject(), true);
1041
Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"),active);
1044
Base::PyGILStateLocker lock;
1045
Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"),Py::None());
1049
signalActiveDocument(*pDoc);
1052
void Application::setActiveDocument(const char *Name)
1054
// If no active document is set, resort to a default.
1055
if (*Name == '\0') {
1056
_pActiveDoc = nullptr;
1060
std::map<std::string,Document*>::iterator pos;
1061
pos = DocMap.find(Name);
1063
if (pos != DocMap.end()) {
1064
setActiveDocument(pos->second);
1067
std::stringstream s;
1068
s << "Try to activate unknown document '" << Name << "'";
1069
throw Base::RuntimeError(s.str());
1073
static int _TransSignalCount;
1074
static bool _TransSignalled;
1075
Application::TransactionSignaller::TransactionSignaller(bool abort, bool signal)
1078
++_TransSignalCount;
1079
if(signal && !_TransSignalled) {
1080
_TransSignalled = true;
1081
GetApplication().signalBeforeCloseTransaction(abort);
1085
Application::TransactionSignaller::~TransactionSignaller() {
1086
if(--_TransSignalCount == 0 && _TransSignalled) {
1087
_TransSignalled = false;
1089
GetApplication().signalCloseTransaction(abort);
1091
catch (const boost::exception&) {
1092
// reported by code analyzers
1093
Base::Console().Warning("~TransactionSignaller: Unexpected boost exception\n");
1098
std::string Application::getHomePath()
1100
return mConfig["AppHomePath"];
1103
std::string Application::getExecutableName()
1105
return mConfig["ExeName"];
1108
std::string Application::getTempPath()
1110
return mConfig["AppTempPath"];
1113
std::string Application::getTempFileName(const char* FileName)
1115
return Base::FileInfo::getTempFileName(FileName, getTempPath().c_str());
1118
std::string Application::getUserCachePath()
1120
return mConfig["UserCachePath"];
1123
std::string Application::getUserConfigPath()
1125
return mConfig["UserConfigPath"];
1128
std::string Application::getUserAppDataDir()
1130
return mConfig["UserAppData"];
1133
std::string Application::getUserMacroDir()
1135
return mConfig["UserMacroPath"];
1138
std::string Application::getResourceDir()
1141
// #6892: Conda may inject null characters => remove them
1142
std::string path = std::string(RESOURCEDIR).c_str();
1144
QDir dir(QString::fromStdString(path));
1145
if (dir.isAbsolute())
1147
return mConfig["AppHomePath"] + path;
1149
return mConfig["AppHomePath"];
1153
std::string Application::getLibraryDir()
1156
// #6892: Conda may inject null characters => remove them
1157
std::string path = std::string(LIBRARYDIR).c_str();
1158
QDir dir(QString::fromStdString(path));
1159
if (dir.isAbsolute())
1161
return mConfig["AppHomePath"] + path;
1163
return mConfig["AppHomePath"] + "lib";
1167
std::string Application::getHelpDir()
1170
// #6892: Conda may inject null characters => remove them
1171
std::string path = std::string(DOCDIR).c_str();
1173
QDir dir(QString::fromStdString(path));
1174
if (dir.isAbsolute())
1176
return mConfig["AppHomePath"] + path;
1178
return mConfig["DocPath"];
1182
int Application::checkLinkDepth(int depth, MessageOption option)
1184
if (_objCount < 0) {
1186
for (auto &v : DocMap) {
1187
_objCount += v.second->countObjects();
1191
if (depth > _objCount + 2) {
1192
const char *msg = "Link recursion limit reached. "
1193
"Please check for cyclic reference.";
1195
case MessageOption::Quiet:
1197
case MessageOption::Error:
1200
case MessageOption::Throw:
1201
throw Base::RuntimeError(msg);
1205
return _objCount + 2;
1208
std::set<DocumentObject *> Application::getLinksTo(
1209
const DocumentObject *obj, int options, int maxCount) const
1211
std::set<DocumentObject *> links;
1213
for(auto &v : DocMap) {
1214
v.second->getLinksTo(links,obj,options,maxCount);
1215
if(maxCount && (int)links.size()>=maxCount)
1219
std::set<Document*> docs;
1220
for(auto o : obj->getInList()) {
1221
if(o && o->isAttachedToDocument() && docs.insert(o->getDocument()).second) {
1222
o->getDocument()->getLinksTo(links,obj,options,maxCount);
1223
if(maxCount && (int)links.size()>=maxCount)
1231
bool Application::hasLinksTo(const DocumentObject *obj) const {
1232
return !getLinksTo(obj,0,1).empty();
1235
ParameterManager & Application::GetSystemParameter()
1237
return *_pcSysParamMngr;
1240
ParameterManager & Application::GetUserParameter()
1242
return *_pcUserParamMngr;
1245
ParameterManager * Application::GetParameterSet(const char* sName) const
1247
auto it = mpcPramManager.find(sName);
1248
if ( it != mpcPramManager.end() )
1254
const std::map<std::string,Base::Reference<ParameterManager>> &
1255
Application::GetParameterSetList() const
1257
return mpcPramManager;
1260
void Application::AddParameterSet(const char* sName)
1262
auto it = mpcPramManager.find(sName);
1263
if ( it != mpcPramManager.end() )
1265
mpcPramManager[sName] = ParameterManager::Create();
1268
void Application::RemoveParameterSet(const char* sName)
1270
auto it = mpcPramManager.find(sName);
1271
// Must not delete user or system parameter
1272
if ( it == mpcPramManager.end() || it->second == _pcUserParamMngr || it->second == _pcSysParamMngr )
1274
mpcPramManager.erase(it);
1277
Base::Reference<ParameterGrp> Application::GetParameterGroupByPath(const char* sName)
1279
std::string cName = sName,cTemp;
1281
std::string::size_type pos = cName.find(':');
1283
// is there a path separator ?
1284
if (pos == std::string::npos) {
1285
throw Base::ValueError("Application::GetParameterGroupByPath() no parameter set name specified");
1287
// assigning the parameter set name
1288
cTemp.assign(cName,0,pos);
1289
cName.erase(0,pos+1);
1291
// test if name is valid
1292
auto It = mpcPramManager.find(cTemp.c_str());
1293
if (It == mpcPramManager.end())
1294
throw Base::ValueError("Application::GetParameterGroupByPath() unknown parameter set name specified");
1296
return It->second->GetGroup(cName.c_str());
1299
void Application::addImportType(const char* Type, const char* ModuleName)
1303
item.module = ModuleName;
1305
// Extract each filetype from 'Type' literal
1306
std::string::size_type pos = item.filter.find("*.");
1307
while ( pos != std::string::npos ) {
1308
std::string::size_type next = item.filter.find_first_of(" )", pos+1);
1309
std::string::size_type len = next-pos-2;
1310
std::string type = item.filter.substr(pos+2,len);
1311
item.types.push_back(type);
1312
pos = item.filter.find("*.", next);
1315
// Due to branding stuff replace "FreeCAD" with the branded application name
1316
if (strncmp(Type, "FreeCAD", 7) == 0) {
1317
std::string AppName = Config()["ExeName"];
1318
AppName += item.filter.substr(7);
1319
item.filter = AppName;
1320
// put to the front of the array
1321
_mImportTypes.insert(_mImportTypes.begin(),item);
1324
_mImportTypes.push_back(item);
1328
void Application::changeImportModule(const char* Type, const char* OldModuleName, const char* NewModuleName)
1330
for (auto& it : _mImportTypes) {
1331
if (it.filter == Type && it.module == OldModuleName) {
1332
it.module = NewModuleName;
1338
std::vector<std::string> Application::getImportModules(const char* Type) const
1340
std::vector<std::string> modules;
1341
for (const auto & it : _mImportTypes) {
1342
const std::vector<std::string>& types = it.types;
1343
for (const auto & jt : types) {
1345
if (strcasecmp(Type,jt.c_str()) == 0)
1347
if (_stricmp(Type,jt.c_str()) == 0)
1349
modules.push_back(it.module);
1356
std::vector<std::string> Application::getImportModules() const
1358
std::vector<std::string> modules;
1359
for (const auto & it : _mImportTypes)
1360
modules.push_back(it.module);
1361
std::sort(modules.begin(), modules.end());
1362
modules.erase(std::unique(modules.begin(), modules.end()), modules.end());
1366
std::vector<std::string> Application::getImportTypes(const char* Module) const
1368
std::vector<std::string> types;
1369
for (const auto & it : _mImportTypes) {
1371
if (strcasecmp(Module,it.module.c_str()) == 0)
1373
if (_stricmp(Module,it.module.c_str()) == 0)
1375
types.insert(types.end(), it.types.begin(), it.types.end());
1381
std::vector<std::string> Application::getImportTypes() const
1383
std::vector<std::string> types;
1384
for (const auto & it : _mImportTypes) {
1385
types.insert(types.end(), it.types.begin(), it.types.end());
1388
std::sort(types.begin(), types.end());
1389
types.erase(std::unique(types.begin(), types.end()), types.end());
1394
std::map<std::string, std::string> Application::getImportFilters(const char* Type) const
1396
std::map<std::string, std::string> moduleFilter;
1397
for (const auto & it : _mImportTypes) {
1398
const std::vector<std::string>& types = it.types;
1399
for (const auto & jt : types) {
1401
if (strcasecmp(Type,jt.c_str()) == 0)
1403
if (_stricmp(Type,jt.c_str()) == 0)
1405
moduleFilter[it.filter] = it.module;
1409
return moduleFilter;
1412
std::map<std::string, std::string> Application::getImportFilters() const
1414
std::map<std::string, std::string> filter;
1415
for (const auto & it : _mImportTypes) {
1416
filter[it.filter] = it.module;
1422
void Application::addExportType(const char* Type, const char* ModuleName)
1426
item.module = ModuleName;
1428
// Extract each filetype from 'Type' literal
1429
std::string::size_type pos = item.filter.find("*.");
1430
while ( pos != std::string::npos ) {
1431
std::string::size_type next = item.filter.find_first_of(" )", pos+1);
1432
std::string::size_type len = next-pos-2;
1433
std::string type = item.filter.substr(pos+2,len);
1434
item.types.push_back(type);
1435
pos = item.filter.find("*.", next);
1438
// Due to branding stuff replace "FreeCAD" with the branded application name
1439
if (strncmp(Type, "FreeCAD", 7) == 0) {
1440
std::string AppName = Config()["ExeName"];
1441
AppName += item.filter.substr(7);
1442
item.filter = AppName;
1443
// put to the front of the array
1444
_mExportTypes.insert(_mExportTypes.begin(),item);
1447
_mExportTypes.push_back(item);
1451
void Application::changeExportModule(const char* Type, const char* OldModuleName, const char* NewModuleName)
1453
for (auto& it : _mExportTypes) {
1454
if (it.filter == Type && it.module == OldModuleName) {
1455
it.module = NewModuleName;
1461
std::vector<std::string> Application::getExportModules(const char* Type) const
1463
std::vector<std::string> modules;
1464
for (const auto & it : _mExportTypes) {
1465
const std::vector<std::string>& types = it.types;
1466
for (const auto & jt : types) {
1468
if (strcasecmp(Type,jt.c_str()) == 0)
1470
if (_stricmp(Type,jt.c_str()) == 0)
1472
modules.push_back(it.module);
1479
std::vector<std::string> Application::getExportModules() const
1481
std::vector<std::string> modules;
1482
for (const auto & it : _mExportTypes)
1483
modules.push_back(it.module);
1484
std::sort(modules.begin(), modules.end());
1485
modules.erase(std::unique(modules.begin(), modules.end()), modules.end());
1489
std::vector<std::string> Application::getExportTypes(const char* Module) const
1491
std::vector<std::string> types;
1492
for (const auto & it : _mExportTypes) {
1494
if (strcasecmp(Module,it.module.c_str()) == 0)
1496
if (_stricmp(Module,it.module.c_str()) == 0)
1498
types.insert(types.end(), it.types.begin(), it.types.end());
1504
std::vector<std::string> Application::getExportTypes() const
1506
std::vector<std::string> types;
1507
for (const FileTypeItem& it : _mExportTypes) {
1508
types.insert(types.end(), it.types.begin(), it.types.end());
1511
std::sort(types.begin(), types.end());
1512
types.erase(std::unique(types.begin(), types.end()), types.end());
1517
std::map<std::string, std::string> Application::getExportFilters(const char* Type) const
1519
std::map<std::string, std::string> moduleFilter;
1520
for (const auto & it : _mExportTypes) {
1521
const std::vector<std::string>& types = it.types;
1522
for (const auto & jt : types) {
1524
if (strcasecmp(Type,jt.c_str()) == 0)
1526
if (_stricmp(Type,jt.c_str()) == 0)
1528
moduleFilter[it.filter] = it.module;
1532
return moduleFilter;
1535
std::map<std::string, std::string> Application::getExportFilters() const
1537
std::map<std::string, std::string> filter;
1538
for (const FileTypeItem& it : _mExportTypes) {
1539
filter[it.filter] = it.module;
1545
//**************************************************************************
1547
void Application::slotBeforeChangeDocument(const App::Document& doc, const Property& prop)
1549
this->signalBeforeChangeDocument(doc, prop);
1552
void Application::slotChangedDocument(const App::Document& doc, const Property& prop)
1554
this->signalChangedDocument(doc, prop);
1557
void Application::slotNewObject(const App::DocumentObject&O)
1559
this->signalNewObject(O);
1563
void Application::slotDeletedObject(const App::DocumentObject&O)
1565
this->signalDeletedObject(O);
1569
void Application::slotBeforeChangeObject(const DocumentObject& O, const Property& Prop)
1571
this->signalBeforeChangeObject(O, Prop);
1574
void Application::slotChangedObject(const App::DocumentObject&O, const App::Property& P)
1576
this->signalChangedObject(O,P);
1579
void Application::slotRelabelObject(const App::DocumentObject&O)
1581
this->signalRelabelObject(O);
1584
void Application::slotActivatedObject(const App::DocumentObject&O)
1586
this->signalActivatedObject(O);
1589
void Application::slotUndoDocument(const App::Document& d)
1591
this->signalUndoDocument(d);
1594
void Application::slotRedoDocument(const App::Document& d)
1596
this->signalRedoDocument(d);
1599
void Application::slotRecomputedObject(const DocumentObject& obj)
1601
this->signalObjectRecomputed(obj);
1604
void Application::slotRecomputed(const Document& doc)
1606
this->signalRecomputed(doc);
1609
void Application::slotBeforeRecompute(const Document& doc)
1611
this->signalBeforeRecomputeDocument(doc);
1614
void Application::slotOpenTransaction(const Document& d, string s)
1616
this->signalOpenTransaction(d, s);
1619
void Application::slotCommitTransaction(const Document& d)
1621
this->signalCommitTransaction(d);
1624
void Application::slotAbortTransaction(const Document& d)
1626
this->signalAbortTransaction(d);
1629
void Application::slotStartSaveDocument(const App::Document& doc, const std::string& filename)
1631
this->signalStartSaveDocument(doc, filename);
1634
void Application::slotFinishSaveDocument(const App::Document& doc, const std::string& filename)
1637
this->signalFinishSaveDocument(doc, filename);
1640
void Application::slotChangePropertyEditor(const App::Document &doc, const App::Property &prop)
1642
this->signalChangePropertyEditor(doc,prop);
1645
//**************************************************************************
1646
// Init, Destruct and singleton
1648
Application * Application::_pcSingleton = nullptr;
1650
int Application::_argc;
1651
char ** Application::_argv;
1654
void Application::cleanupUnits()
1657
Base::PyGILStateLocker lock;
1658
Py::Module mod (Py::Module("FreeCAD").getAttr("Units").ptr());
1660
Py::List attr(mod.dir());
1661
for (Py::List::iterator it = attr.begin(); it != attr.end(); ++it) {
1662
mod.delAttr(Py::String(*it));
1665
catch (Py::Exception& e) {
1666
Base::PyGILStateLocker lock;
1671
void Application::destruct()
1673
// saving system parameter
1674
Base::Console().Log("Saving system parameter...\n");
1675
_pcSysParamMngr->SaveDocument();
1676
// saving the User parameter
1677
Base::Console().Log("Saving system parameter...done\n");
1678
Base::Console().Log("Saving user parameter...\n");
1679
_pcUserParamMngr->SaveDocument();
1680
Base::Console().Log("Saving user parameter...done\n");
1682
// now save all other parameter files
1683
auto& paramMgr = _pcSingleton->mpcPramManager;
1684
for (auto it : paramMgr) {
1685
if ((it.second != _pcSysParamMngr) && (it.second != _pcUserParamMngr)) {
1686
if (it.second->HasSerializer()) {
1687
Base::Console().Log("Saving %s...\n", it.first.c_str());
1688
it.second->SaveDocument();
1689
Base::Console().Log("Saving %s...done\n", it.first.c_str());
1695
_pcSysParamMngr = nullptr;
1696
_pcUserParamMngr = nullptr;
1699
// Do this only in debug mode for memory leak checkers
1703
// not initialized or double destruct!
1704
assert(_pcSingleton);
1705
delete _pcSingleton;
1707
// We must detach from console and delete the observer to save our file
1710
Base::Interpreter().finalize();
1712
Base::ScriptFactorySingleton::Destruct();
1713
Base::InterpreterSingleton::Destruct();
1714
Base::Type::destruct();
1715
ParameterManager::Terminate();
1718
void Application::destructObserver()
1720
if ( _pConsoleObserverFile ) {
1721
Base::Console().DetachObserver(_pConsoleObserverFile);
1722
delete _pConsoleObserverFile;
1723
_pConsoleObserverFile = nullptr;
1725
if ( _pConsoleObserverStd ) {
1726
Base::Console().DetachObserver(_pConsoleObserverStd);
1727
delete _pConsoleObserverStd;
1728
_pConsoleObserverStd = nullptr;
1732
/** freecadNewHandler()
1733
* prints an error message and throws an exception
1735
#ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler
1736
int __cdecl freecadNewHandler(size_t size )
1738
// throw an exception
1739
throw Base::MemoryException();
1742
#else // Ansi C/C++ new handler
1743
static void freecadNewHandler ()
1745
// throw an exception
1746
throw Base::MemoryException();
1750
#if defined(FC_OS_LINUX)
1751
#include <execinfo.h>
1762
#endif // HAVE_CONFIG_H
1764
// This function produces a stack backtrace with demangled function & method names.
1765
void printBacktrace(size_t skip=0)
1767
#if defined HAVE_BACKTRACE_SYMBOLS
1768
void *callstack[128];
1769
size_t nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
1770
size_t nFrames = backtrace(callstack, nMaxFrames);
1771
char **symbols = backtrace_symbols(callstack, nFrames);
1773
for (size_t i = skip; i < nFrames; i++) {
1774
char *demangled = nullptr;
1777
if (dladdr(callstack[i], &info) && info.dli_sname && info.dli_fname) {
1778
if (info.dli_sname[0] == '_') {
1779
demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
1783
std::stringstream str;
1785
void* offset = (void*)((char*)callstack[i] - (char*)info.dli_saddr);
1786
str << "#" << (i-skip) << " " << callstack[i] << " in " << demangled << " from " << info.dli_fname << "+" << offset << std::endl;
1790
str << "#" << (i-skip) << " " << symbols[i] << std::endl;
1793
// cannot directly print to cerr when using --write-log
1794
std::cerr << str.str();
1798
#else //HAVE_BACKTRACE_SYMBOLS
1800
std::cerr << "Cannot print the stacktrace because the C runtime library doesn't provide backtrace or backtrace_symbols\n";
1805
void segmentation_fault_handler(int sig)
1807
#if defined(FC_OS_LINUX)
1809
std::cerr << "Program received signal SIGSEGV, Segmentation fault.\n";
1811
#if defined(FC_DEBUG)
1819
std::cerr << "Illegal storage access..." << std::endl;
1821
throw Base::AccessViolation("Illegal storage access! Please save your work under a new file name and restart the application!");
1825
std::cerr << "Abnormal program termination..." << std::endl;
1827
throw Base::AbnormalProgramTermination("Break signal occurred");
1831
std::cerr << "Unknown error occurred..." << std::endl;
1834
#endif // FC_OS_LINUX
1837
void unhandled_exception_handler()
1839
std::cerr << "Terminating..." << std::endl;
1842
void unexpection_error_handler()
1844
std::cerr << "Unexpected error occurred..." << std::endl;
1845
// try to throw an exception and give the user chance to save their work
1847
throw Base::AbnormalProgramTermination("Unexpected error occurred! Please save your work under a new file name and restart the application!");
1853
#if defined(FC_SE_TRANSLATOR) // Microsoft compiler
1854
void my_se_translator_filter(unsigned int code, EXCEPTION_POINTERS* pExp)
1859
case EXCEPTION_ACCESS_VIOLATION:
1860
throw Base::AccessViolation();
1861
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
1862
case EXCEPTION_INT_DIVIDE_BY_ZERO:
1863
//throw Base::ZeroDivisionError("Division by zero!");
1864
Base::Console().Error("SEH exception (%u): Division by zero\n", code);
1868
std::stringstream str;
1869
str << "SEH exception of type: " << code;
1870
// general C++ SEH exception for things we don't need to handle separately....
1871
throw Base::RuntimeError(str.str());
1875
void Application::init(int argc, char ** argv)
1878
// install our own new handler
1879
#ifdef _MSC_VER // Microsoft compiler
1880
_set_new_handler ( freecadNewHandler ); // Setup new handler
1881
_set_new_mode( 1 ); // Re-route malloc failures to new handler !
1882
#else // Ansi compiler
1883
std::set_new_handler (freecadNewHandler); // ANSI new handler
1885
// if an unexpected crash occurs we can install a handler function to
1886
// write some additional information
1887
#if defined (_MSC_VER) // Microsoft compiler
1888
std::signal(SIGSEGV,segmentation_fault_handler);
1889
std::signal(SIGABRT,segmentation_fault_handler);
1890
std::set_terminate(unhandled_exception_handler);
1891
::set_unexpected(unexpection_error_handler);
1892
#elif defined(FC_OS_LINUX)
1893
std::signal(SIGSEGV,segmentation_fault_handler);
1895
#if defined(FC_SE_TRANSLATOR)
1896
_set_se_translator(my_se_translator_filter);
1900
initConfig(argc,argv);
1904
// force the log to flush
1910
void Application::initTypes()
1913
Base::Type ::init();
1914
Base::BaseClass ::init();
1915
Base::Exception ::init();
1916
Base::AbortException ::init();
1917
Base::Persistence ::init();
1919
// Complex data classes
1920
Data::ComplexGeoData ::init();
1921
Data::Segment ::init();
1924
// Note: the order matters
1925
App::Property ::init();
1926
App::PropertyContainer ::init();
1927
App::PropertyLists ::init();
1928
App::PropertyBool ::init();
1929
App::PropertyBoolList ::init();
1930
App::PropertyFloat ::init();
1931
App::PropertyFloatList ::init();
1932
App::PropertyFloatConstraint ::init();
1933
App::PropertyPrecision ::init();
1934
App::PropertyQuantity ::init();
1935
App::PropertyQuantityConstraint ::init();
1936
App::PropertyInteger ::init();
1937
App::PropertyIntegerConstraint ::init();
1938
App::PropertyPercent ::init();
1939
App::PropertyEnumeration ::init();
1940
App::PropertyIntegerList ::init();
1941
App::PropertyIntegerSet ::init();
1942
App::PropertyMap ::init();
1943
App::PropertyString ::init();
1944
App::PropertyPersistentObject ::init();
1945
App::PropertyUUID ::init();
1946
App::PropertyFont ::init();
1947
App::PropertyStringList ::init();
1948
App::PropertyLinkBase ::init();
1949
App::PropertyLinkListBase ::init();
1950
App::PropertyLink ::init();
1951
App::PropertyLinkChild ::init();
1952
App::PropertyLinkGlobal ::init();
1953
App::PropertyLinkHidden ::init();
1954
App::PropertyLinkSub ::init();
1955
App::PropertyLinkSubChild ::init();
1956
App::PropertyLinkSubGlobal ::init();
1957
App::PropertyLinkSubHidden ::init();
1958
App::PropertyLinkList ::init();
1959
App::PropertyLinkListChild ::init();
1960
App::PropertyLinkListGlobal ::init();
1961
App::PropertyLinkListHidden ::init();
1962
App::PropertyLinkSubList ::init();
1963
App::PropertyLinkSubListChild ::init();
1964
App::PropertyLinkSubListGlobal ::init();
1965
App::PropertyLinkSubListHidden ::init();
1966
App::PropertyXLink ::init();
1967
App::PropertyXLinkSub ::init();
1968
App::PropertyXLinkSubList ::init();
1969
App::PropertyXLinkList ::init();
1970
App::PropertyXLinkContainer ::init();
1971
App::PropertyMatrix ::init();
1972
App::PropertyVector ::init();
1973
App::PropertyVectorDistance ::init();
1974
App::PropertyPosition ::init();
1975
App::PropertyDirection ::init();
1976
App::PropertyVectorList ::init();
1977
App::PropertyPlacement ::init();
1978
App::PropertyPlacementList ::init();
1979
App::PropertyPlacementLink ::init();
1980
App::PropertyRotation ::init();
1981
App::PropertyGeometry ::init();
1982
App::PropertyComplexGeoData ::init();
1983
App::PropertyColor ::init();
1984
App::PropertyColorList ::init();
1985
App::PropertyMaterial ::init();
1986
App::PropertyMaterialList ::init();
1987
App::PropertyPath ::init();
1988
App::PropertyFile ::init();
1989
App::PropertyFileIncluded ::init();
1990
App::PropertyPythonObject ::init();
1991
App::PropertyExpressionContainer::init();
1992
App::PropertyExpressionEngine ::init();
1993
// all know unit properties
1994
App::PropertyAcceleration ::init();
1995
App::PropertyAmountOfSubstance ::init();
1996
App::PropertyAngle ::init();
1997
App::PropertyArea ::init();
1998
App::PropertyCompressiveStrength ::init();
1999
App::PropertyCurrentDensity ::init();
2000
App::PropertyDensity ::init();
2001
App::PropertyDissipationRate ::init();
2002
App::PropertyDistance ::init();
2003
App::PropertyDynamicViscosity ::init();
2004
App::PropertyElectricalCapacitance ::init();
2005
App::PropertyElectricalConductance ::init();
2006
App::PropertyElectricalConductivity ::init();
2007
App::PropertyElectricalInductance ::init();
2008
App::PropertyElectricalResistance ::init();
2009
App::PropertyElectricCharge ::init();
2010
App::PropertyElectricCurrent ::init();
2011
App::PropertyElectricPotential ::init();
2012
App::PropertyFrequency ::init();
2013
App::PropertyForce ::init();
2014
App::PropertyHeatFlux ::init();
2015
App::PropertyInverseArea ::init();
2016
App::PropertyInverseLength ::init();
2017
App::PropertyInverseVolume ::init();
2018
App::PropertyKinematicViscosity ::init();
2019
App::PropertyLength ::init();
2020
App::PropertyLuminousIntensity ::init();
2021
App::PropertyMagneticFieldStrength ::init();
2022
App::PropertyMagneticFlux ::init();
2023
App::PropertyMagneticFluxDensity ::init();
2024
App::PropertyMagnetization ::init();
2025
App::PropertyMass ::init();
2026
App::PropertyPressure ::init();
2027
App::PropertyPower ::init();
2028
App::PropertyShearModulus ::init();
2029
App::PropertySpecificEnergy ::init();
2030
App::PropertySpecificHeat ::init();
2031
App::PropertySpeed ::init();
2032
App::PropertyStiffness ::init();
2033
App::PropertyStiffnessDensity ::init();
2034
App::PropertyStress ::init();
2035
App::PropertyTemperature ::init();
2036
App::PropertyThermalConductivity ::init();
2037
App::PropertyThermalExpansionCoefficient::init();
2038
App::PropertyThermalTransferCoefficient ::init();
2039
App::PropertyTime ::init();
2040
App::PropertyUltimateTensileStrength ::init();
2041
App::PropertyVacuumPermittivity ::init();
2042
App::PropertyVelocity ::init();
2043
App::PropertyVolume ::init();
2044
App::PropertyVolumeFlowRate ::init();
2045
App::PropertyVolumetricThermalExpansionCoefficient::init();
2046
App::PropertyWork ::init();
2047
App::PropertyYieldStrength ::init();
2048
App::PropertyYoungsModulus ::init();
2050
// Extension classes
2051
App::Extension ::init();
2052
App::ExtensionContainer ::init();
2053
App::DocumentObjectExtension ::init();
2054
App::GroupExtension ::init();
2055
App::GroupExtensionPython ::init();
2056
App::GeoFeatureGroupExtension ::init();
2057
App::GeoFeatureGroupExtensionPython::init();
2058
App::OriginGroupExtension ::init();
2059
App::OriginGroupExtensionPython ::init();
2060
App::LinkBaseExtension ::init();
2061
App::LinkBaseExtensionPython ::init();
2062
App::LinkExtension ::init();
2063
App::LinkExtensionPython ::init();
2064
App::SuppressibleExtension ::init();
2065
App::SuppressibleExtensionPython ::init();
2068
App::TransactionalObject ::init();
2069
App::DocumentObject ::init();
2070
App::GeoFeature ::init();
2073
App::FeatureTest ::init();
2074
App::FeatureTestException ::init();
2075
App::FeatureTestColumn ::init();
2076
App::FeatureTestRow ::init();
2077
App::FeatureTestAbsAddress ::init();
2078
App::FeatureTestPlacement ::init();
2079
App::FeatureTestAttribute ::init();
2082
App::FeaturePython ::init();
2083
App::GeometryPython ::init();
2084
App::Document ::init();
2085
App::DocumentObjectGroup ::init();
2086
App::DocumentObjectGroupPython ::init();
2087
App::DocumentObjectFileIncluded::init();
2088
Image::ImagePlane ::init();
2089
App::InventorObject ::init();
2090
App::VRMLObject ::init();
2091
App::Annotation ::init();
2092
App::AnnotationLabel ::init();
2093
App::MeasureDistance ::init();
2094
App::MaterialObject ::init();
2095
App::MaterialObjectPython ::init();
2096
App::TextDocument ::init();
2097
App::Placement ::init();
2098
App::PlacementPython ::init();
2099
App::OriginFeature ::init();
2100
App::Plane ::init();
2103
App::Origin ::init();
2105
App::LinkPython ::init();
2106
App::LinkElement ::init();
2107
App::LinkElementPython ::init();
2108
App::LinkGroup ::init();
2109
App::LinkGroupPython ::init();
2110
App::VarSet ::init();
2112
// Expression classes
2113
App::Expression ::init();
2114
App::UnitExpression ::init();
2115
App::NumberExpression ::init();
2116
App::ConstantExpression ::init();
2117
App::OperatorExpression ::init();
2118
App::VariableExpression ::init();
2119
App::ConditionalExpression ::init();
2120
App::StringExpression ::init();
2121
App::FunctionExpression ::init();
2122
App::RangeExpression ::init();
2123
App::PyObjectExpression ::init();
2125
// Topological naming classes
2126
App::StringHasher ::init();
2127
App::StringID ::init();
2129
// register transaction type
2130
new App::TransactionProducer<TransactionDocumentObject>
2131
(DocumentObject::getClassTypeId());
2133
// register exception producer types
2134
new Base::ExceptionProducer<Base::AbortException>;
2135
new Base::ExceptionProducer<Base::XMLBaseException>;
2136
new Base::ExceptionProducer<Base::XMLParseException>;
2137
new Base::ExceptionProducer<Base::XMLAttributeError>;
2138
new Base::ExceptionProducer<Base::FileException>;
2139
new Base::ExceptionProducer<Base::FileSystemError>;
2140
new Base::ExceptionProducer<Base::BadFormatError>;
2141
new Base::ExceptionProducer<Base::MemoryException>;
2142
new Base::ExceptionProducer<Base::AccessViolation>;
2143
new Base::ExceptionProducer<Base::AbnormalProgramTermination>;
2144
new Base::ExceptionProducer<Base::UnknownProgramOption>;
2145
new Base::ExceptionProducer<Base::ProgramInformation>;
2146
new Base::ExceptionProducer<Base::TypeError>;
2147
new Base::ExceptionProducer<Base::ValueError>;
2148
new Base::ExceptionProducer<Base::IndexError>;
2149
new Base::ExceptionProducer<Base::NameError>;
2150
new Base::ExceptionProducer<Base::ImportError>;
2151
new Base::ExceptionProducer<Base::AttributeError>;
2152
new Base::ExceptionProducer<Base::RuntimeError>;
2153
new Base::ExceptionProducer<Base::BadGraphError>;
2154
new Base::ExceptionProducer<Base::NotImplementedError>;
2155
new Base::ExceptionProducer<Base::ZeroDivisionError>;
2156
new Base::ExceptionProducer<Base::ReferenceError>;
2157
new Base::ExceptionProducer<Base::ExpressionError>;
2158
new Base::ExceptionProducer<Base::ParserError>;
2159
new Base::ExceptionProducer<Base::UnicodeError>;
2160
new Base::ExceptionProducer<Base::OverflowError>;
2161
new Base::ExceptionProducer<Base::UnderflowError>;
2162
new Base::ExceptionProducer<Base::UnitsMismatchError>;
2163
new Base::ExceptionProducer<Base::CADKernelError>;
2164
new Base::ExceptionProducer<Base::RestoreError>;
2169
void parseProgramOptions(int ac, char ** av, const string& exe, variables_map& vm)
2171
// Declare a group of options that will be
2172
// allowed only on the command line
2173
options_description generic("Generic options");
2174
generic.add_options()
2175
("version,v", "Prints version string")
2176
("verbose", "Prints verbose version string")
2177
("help,h", "Prints help message")
2178
("console,c", "Starts in console mode")
2179
("response-file", value<string>(),"Can be specified with '@name', too")
2180
("dump-config", "Dumps configuration")
2181
("get-config", value<string>(), "Prints the value of the requested configuration key")
2182
("set-config", value< vector<string> >()->multitoken(), "Sets the value of a configuration key")
2183
("keep-deprecated-paths", "If set then config files are kept on the old location")
2186
// Declare a group of options that will be
2187
// allowed both on the command line and in
2189
std::stringstream descr;
2190
descr << "Writes " << exe << ".log to the user directory.";
2191
boost::program_options::options_description config("Configuration");
2192
config.add_options()
2193
//("write-log,l", value<string>(), "write a log file")
2194
("write-log,l", descr.str().c_str())
2195
("log-file", value<string>(), "Unlike --write-log this allows logging to an arbitrary file")
2196
("user-cfg,u", value<string>(),"User config file to load/save user settings")
2197
("system-cfg,s", value<string>(),"System config file to load/save system settings")
2198
("run-test,t", value<string>()->implicit_value(""),"Run a given test case (use 0 (zero) to run all tests). If no argument is provided then return list of all available tests.")
2199
("module-path,M", value< vector<string> >()->composing(),"Additional module paths")
2200
("python-path,P", value< vector<string> >()->composing(),"Additional python paths")
2201
("single-instance", "Allow to run a single instance of the application")
2202
("pass", value< vector<string> >()->multitoken(), "Ignores the following arguments and pass them through to be used by a script")
2206
// Hidden options, will be allowed both on the command line and
2207
// in the config file, but will not be shown to the user.
2208
boost::program_options::options_description hidden("Hidden options");
2209
hidden.add_options()
2210
("input-file", boost::program_options::value< vector<string> >(), "input file")
2211
("output", boost::program_options::value<string>(),"output file")
2212
("hidden", "don't show the main window")
2213
// this are to ignore for the window system (QApplication)
2214
("style", boost::program_options::value< string >(), "set the application GUI style")
2215
("stylesheet", boost::program_options::value< string >(), "set the application stylesheet")
2216
("session", boost::program_options::value< string >(), "restore the application from an earlier session")
2217
("reverse", "set the application's layout direction from right to left")
2218
("widgetcount", "print debug messages about widgets")
2219
("graphicssystem", boost::program_options::value< string >(), "backend to be used for on-screen widgets and pixmaps")
2220
("display", boost::program_options::value< string >(), "set the X-Server")
2221
("geometry ", boost::program_options::value< string >(), "set the X-Window geometry")
2222
("font", boost::program_options::value< string >(), "set the X-Window font")
2223
("fn", boost::program_options::value< string >(), "set the X-Window font")
2224
("background", boost::program_options::value< string >(), "set the X-Window background color")
2225
("bg", boost::program_options::value< string >(), "set the X-Window background color")
2226
("foreground", boost::program_options::value< string >(), "set the X-Window foreground color")
2227
("fg", boost::program_options::value< string >(), "set the X-Window foreground color")
2228
("button", boost::program_options::value< string >(), "set the X-Window button color")
2229
("btn", boost::program_options::value< string >(), "set the X-Window button color")
2230
("name", boost::program_options::value< string >(), "set the X-Window name")
2231
("title", boost::program_options::value< string >(), "set the X-Window title")
2232
("visual", boost::program_options::value< string >(), "set the X-Window to color scheme")
2233
("ncols", boost::program_options::value< int >(), "set the X-Window to color scheme")
2234
("cmap", "set the X-Window to color scheme")
2235
#if defined(FC_OS_MACOSX)
2236
("psn", boost::program_options::value< string >(), "process serial number")
2240
// Ignored options, will be safely ignored. Mostly used by underlying libs.
2241
//boost::program_options::options_description x11("X11 options");
2243
// ("display", boost::program_options::value< string >(), "set the X-Server")
2245
//0000723: improper handling of qt specific command line arguments
2246
std::vector<std::string> args;
2248
for (int i=1; i<ac; i++) {
2252
args.back() += av[i];
2255
args.emplace_back(av[i]);
2257
if (strcmp(av[i],"-style") == 0) {
2260
else if (strcmp(av[i],"-stylesheet") == 0) {
2263
else if (strcmp(av[i],"-session") == 0) {
2266
else if (strcmp(av[i],"-graphicssystem") == 0) {
2271
// 0000659: SIGABRT on startup in boost::program_options (Boost 1.49)
2272
// Add some text to the constructor
2273
options_description cmdline_options("Command-line options");
2274
cmdline_options.add(generic).add(config).add(hidden);
2276
boost::program_options::options_description config_file_options("Config");
2277
config_file_options.add(config).add(hidden);
2279
boost::program_options::options_description visible("Allowed options");
2280
visible.add(generic).add(config);
2282
boost::program_options::positional_options_description p;
2283
p.add("input-file", -1);
2286
store( boost::program_options::command_line_parser(args).
2287
options(cmdline_options).positional(p).extra_parser(Util::customSyntax).run(), vm);
2289
std::ifstream ifs("FreeCAD.cfg");
2291
store(parse_config_file(ifs, config_file_options), vm);
2294
catch (const std::exception& e) {
2295
std::stringstream str;
2296
str << e.what() << endl << endl << visible << endl;
2297
throw Base::UnknownProgramOption(str.str());
2300
std::stringstream str;
2301
str << "Wrong or unknown option, bailing out!" << endl << endl << visible << endl;
2302
throw Base::UnknownProgramOption(str.str());
2305
if (vm.count("help")) {
2306
std::stringstream str;
2307
str << exe << endl << endl;
2308
str << "For a detailed description see https://www.freecad.org/wiki/Start_up_and_Configuration" << endl<<endl;
2309
str << "Usage: " << exe << " [options] File1 File2 ..." << endl << endl;
2310
str << visible << endl;
2311
throw Base::ProgramInformation(str.str());
2314
if (vm.count("response-file")) {
2315
// Load the file and tokenize it
2316
std::ifstream ifs(vm["response-file"].as<string>().c_str());
2318
Base::Console().Error("Could no open the response file\n");
2319
std::stringstream str;
2320
str << "Could no open the response file: '"
2321
<< vm["response-file"].as<string>() << "'" << endl;
2322
throw Base::UnknownProgramOption(str.str());
2324
// Read the whole file into a string
2327
// Split the file content
2328
char_separator<char> sep(" \n\r");
2329
tokenizer<char_separator<char> > tok(ss.str(), sep);
2330
vector<string> args;
2331
copy(tok.begin(), tok.end(), back_inserter(args));
2332
// Parse the file and store the options
2333
store( boost::program_options::command_line_parser(args).
2334
options(cmdline_options).positional(p).extra_parser(Util::customSyntax).run(), vm);
2338
void processProgramOptions(const variables_map& vm, std::map<std::string,std::string>& mConfig)
2340
if (vm.count("version")) {
2341
std::stringstream str;
2342
str << mConfig["ExeName"] << " " << mConfig["ExeVersion"]
2343
<< " Revision: " << mConfig["BuildRevision"] << std::endl;
2344
if (vm.count("verbose")) {
2345
str << "\nLibrary versions:\n";
2346
str << "boost " << BOOST_LIB_VERSION << '\n';
2347
str << "Coin3D " << fcCoin3dVersion << '\n';
2348
str << "Eigen3 " << fcEigen3Version << '\n';
2349
#ifdef OCC_VERSION_STRING_EXT
2350
str << "OCC " << OCC_VERSION_STRING_EXT << '\n';
2352
str << "Qt " << QT_VERSION_STR << '\n';
2353
str << "Python " << PY_VERSION << '\n';
2354
str << "PySide " << fcPysideVersion << '\n';
2355
str << "shiboken " << fcShibokenVersion << '\n';
2356
#ifdef SMESH_VERSION_STR
2357
str << "SMESH " << SMESH_VERSION_STR << '\n';
2359
str << "VTK " << fcVtkVersion << '\n';
2360
str << "xerces-c " << fcXercescVersion << '\n';
2362
throw Base::ProgramInformation(str.str());
2365
if (vm.count("console")) {
2366
mConfig["Console"] = "1";
2367
mConfig["RunMode"] = "Cmd";
2370
if (vm.count("module-path")) {
2371
vector<string> Mods = vm["module-path"].as< vector<string> >();
2373
for (const auto & It : Mods)
2375
temp.erase(temp.end()-1);
2376
mConfig["AdditionalModulePaths"] = temp;
2379
if (vm.count("python-path")) {
2380
vector<string> Paths = vm["python-path"].as< vector<string> >();
2381
for (const auto & It : Paths)
2382
Base::Interpreter().addPythonPath(It.c_str());
2385
if (vm.count("input-file")) {
2386
vector<string> files(vm["input-file"].as< vector<string> >());
2387
int OpenFileCount=0;
2388
for (const auto & It : files) {
2390
//cout << "Input files are: "
2391
// << vm["input-file"].as< vector<string> >() << "\n";
2393
std::ostringstream temp;
2394
temp << "OpenFile" << OpenFileCount;
2395
mConfig[temp.str()] = It;
2398
std::ostringstream buffer;
2399
buffer << OpenFileCount;
2400
mConfig["OpenFileCount"] = buffer.str();
2403
if (vm.count("output")) {
2404
string file = vm["output"].as<string>();
2405
mConfig["SaveFile"] = file;
2408
if (vm.count("hidden")) {
2409
mConfig["StartHidden"] = "1";
2412
if (vm.count("write-log")) {
2413
mConfig["LoggingFile"] = "1";
2414
//mConfig["LoggingFileName"] = vm["write-log"].as<string>();
2415
mConfig["LoggingFileName"] = mConfig["UserAppData"] + mConfig["ExeName"] + ".log";
2418
if (vm.count("log-file")) {
2419
mConfig["LoggingFile"] = "1";
2420
mConfig["LoggingFileName"] = vm["log-file"].as<string>();
2423
if (vm.count("user-cfg")) {
2424
mConfig["UserParameter"] = vm["user-cfg"].as<string>();
2427
if (vm.count("system-cfg")) {
2428
mConfig["SystemParameter"] = vm["system-cfg"].as<string>();
2431
if (vm.count("run-test")) {
2432
string testCase = vm["run-test"].as<string>();
2433
if ( "0" == testCase) {
2434
testCase = "TestApp.All";
2436
else if (testCase.empty()) {
2437
testCase = "TestApp.PrintAll";
2439
mConfig["TestCase"] = testCase;
2440
mConfig["RunMode"] = "Internal";
2441
mConfig["ScriptFileName"] = "FreeCADTest";
2442
//sScriptName = FreeCADTest;
2445
if (vm.count("single-instance")) {
2446
mConfig["SingleInstance"] = "1";
2449
if (vm.count("dump-config")) {
2450
std::stringstream str;
2451
for (const auto & it : mConfig) {
2452
str << it.first << "=" << it.second << std::endl;
2454
throw Base::ProgramInformation(str.str());
2457
if (vm.count("get-config")) {
2458
std::string configKey = vm["get-config"].as<string>();
2459
std::stringstream str;
2460
std::map<std::string,std::string>::iterator pos;
2461
pos = mConfig.find(configKey);
2462
if (pos != mConfig.end()) {
2466
throw Base::ProgramInformation(str.str());
2469
if (vm.count("set-config")) {
2470
std::vector<std::string> configKeyValue = vm["set-config"].as< std::vector<std::string> >();
2471
for (const auto& it : configKeyValue) {
2472
auto pos = it.find('=');
2473
if (pos != std::string::npos) {
2474
std::string key = it.substr(0, pos);
2475
std::string val = it.substr(pos + 1);
2483
void Application::initConfig(int argc, char ** argv)
2485
// find the home path....
2486
mConfig["AppHomePath"] = FindHomePath(argv[0]);
2488
// Version of the application extracted from SubWCRef into src/Build/Version.h
2489
// We only set these keys if not yet defined. Therefore it suffices to search
2490
// only for 'BuildVersionMajor'.
2491
if (App::Application::Config().find("BuildVersionMajor") == App::Application::Config().end()) {
2492
std::stringstream str;
2493
str << FCVersionMajor
2494
<< "." << FCVersionMinor
2495
<< "." << FCVersionPoint;
2496
App::Application::Config()["ExeVersion" ] = str.str();
2497
App::Application::Config()["BuildVersionMajor" ] = FCVersionMajor;
2498
App::Application::Config()["BuildVersionMinor" ] = FCVersionMinor;
2499
App::Application::Config()["BuildVersionPoint" ] = FCVersionPoint;
2500
App::Application::Config()["BuildVersionSuffix" ] = FCVersionSuffix;
2501
App::Application::Config()["BuildRevision" ] = FCRevision;
2502
App::Application::Config()["BuildRepositoryURL" ] = FCRepositoryURL;
2503
App::Application::Config()["BuildRevisionDate" ] = FCRevisionDate;
2504
#if defined(FCRepositoryHash)
2505
App::Application::Config()["BuildRevisionHash" ] = FCRepositoryHash;
2507
#if defined(FCRepositoryBranch)
2508
App::Application::Config()["BuildRevisionBranch"] = FCRepositoryBranch;
2515
// Now it's time to read-in the file branding.xml if it exists
2517
QString binDir = QString::fromUtf8((mConfig["AppHomePath"] + "bin").c_str());
2518
QFileInfo fi(binDir, QString::fromLatin1("branding.xml"));
2519
if (fi.exists() && brand.readFile(fi.absoluteFilePath())) {
2520
Branding::XmlConfig cfg = brand.getUserDefines();
2521
for (Branding::XmlConfig::iterator it = cfg.begin(); it != cfg.end(); ++it) {
2522
App::Application::Config()[it.key()] = it.value();
2527
parseProgramOptions(argc, argv, mConfig["ExeName"], vm);
2529
if (vm.count("keep-deprecated-paths")) {
2530
mConfig["KeepDeprecatedPaths"] = "1";
2533
// extract home paths
2537
mConfig["Debug"] = "1";
2539
mConfig["Debug"] = "0";
2543
PyImport_AppendInittab ("FreeCAD", init_freecad_module);
2544
PyImport_AppendInittab ("__FreeCADBase__", init_freecad_base_module);
2545
const char* pythonpath = Base::Interpreter().init(argc,argv);
2547
mConfig["PythonSearchPath"] = pythonpath;
2549
Base::Console().Warning("Encoding of Python paths failed\n");
2551
// Handle the options that have impact on the init process
2552
processProgramOptions(vm, mConfig);
2554
// Init console ===========================================================
2555
Base::PyGILStateLocker lock;
2556
_pConsoleObserverStd = new Base::ConsoleObserverStd();
2557
Base::Console().AttachObserver(_pConsoleObserverStd);
2558
if (mConfig["LoggingConsole"] != "1") {
2559
_pConsoleObserverStd->bMsg = false;
2560
_pConsoleObserverStd->bLog = false;
2561
_pConsoleObserverStd->bWrn = false;
2562
_pConsoleObserverStd->bErr = false;
2564
if (mConfig["Verbose"] == "Strict")
2565
Base::Console().UnsetConsoleMode(Base::ConsoleSingleton::Verbose);
2567
// file logging Init ===========================================================
2568
if (mConfig["LoggingFile"] == "1") {
2569
_pConsoleObserverFile = new Base::ConsoleObserverFile(mConfig["LoggingFileName"].c_str());
2570
Base::Console().AttachObserver(_pConsoleObserverFile);
2573
_pConsoleObserverFile = nullptr;
2575
// Banner ===========================================================
2576
if (!(mConfig["RunMode"] == "Cmd")) {
2577
// Remove banner if FreeCAD is invoked via the -c command as regular
2578
// Python interpreter
2579
if (!(mConfig["Verbose"] == "Strict"))
2580
Base::Console().Message("%s %s, Libs: %s.%s.%s%sR%s\n%s",
2581
mConfig["ExeName"].c_str(),
2582
mConfig["ExeVersion"].c_str(),
2583
mConfig["BuildVersionMajor"].c_str(),
2584
mConfig["BuildVersionMinor"].c_str(),
2585
mConfig["BuildVersionPoint"].c_str(),
2586
mConfig["BuildVersionSuffix"].c_str(),
2587
mConfig["BuildRevision"].c_str(),
2588
mConfig["CopyrightInfo"].c_str());
2590
Base::Console().Message("%s %s, Libs: %s.%s.%s%sR%s\n",
2591
mConfig["ExeName"].c_str(),
2592
mConfig["ExeVersion"].c_str(),
2593
mConfig["BuildVersionMajor"].c_str(),
2594
mConfig["BuildVersionMinor"].c_str(),
2595
mConfig["BuildVersionPoint"].c_str(),
2596
mConfig["BuildVersionSuffix"].c_str(),
2597
mConfig["BuildRevision"].c_str());
2601
auto loglevelParam = _pcUserParamMngr->GetGroup("BaseApp/LogLevels");
2602
const auto &loglevels = loglevelParam->GetIntMap();
2603
bool hasDefault = false;
2604
for (const auto &v : loglevels) {
2605
if (v.first == "Default") {
2609
Base::Console().SetDefaultLogLevel(v.second);
2613
else if (v.first == "DebugDefault") {
2617
Base::Console().SetDefaultLogLevel(v.second);
2622
*Base::Console().GetLogLevel(v.first.c_str()) = v.second;
2628
loglevelParam->SetInt("DebugDefault", Base::Console().LogLevel(-1));
2630
loglevelParam->SetInt("Default", Base::Console().LogLevel(-1));
2634
// Change application tmp. directory
2635
std::string tmpPath = _pcUserParamMngr->GetGroup("BaseApp/Preferences/General")->GetASCII("TempPath");
2636
Base::FileInfo di(tmpPath);
2637
if (di.exists() && di.isDir()) {
2638
mConfig["AppTempPath"] = tmpPath + PATHSEP;
2642
// capture python variables
2643
SaveEnv("PYTHONPATH");
2644
SaveEnv("PYTHONHOME");
2645
SaveEnv("TCL_LIBRARY");
2646
SaveEnv("TCLLIBPATH");
2648
// capture CasCade variables
2649
SaveEnv("CSF_MDTVFontDirectory");
2650
SaveEnv("CSF_MDTVTexturesDirectory");
2651
SaveEnv("CSF_UnitsDefinition");
2652
SaveEnv("CSF_UnitsLexicon");
2653
SaveEnv("CSF_StandardDefaults");
2654
SaveEnv("CSF_PluginDefaults");
2655
SaveEnv("CSF_LANGUAGE");
2656
SaveEnv("CSF_SHMessage");
2657
SaveEnv("CSF_XCAFDefaults");
2658
SaveEnv("CSF_GraphicShr");
2659
SaveEnv("CSF_IGESDefaults");
2660
SaveEnv("CSF_STEPDefaults");
2665
// Save version numbers of the libraries
2666
#ifdef OCC_VERSION_STRING_EXT
2667
mConfig["OCC_VERSION"] = OCC_VERSION_STRING_EXT;
2669
mConfig["BOOST_VERSION"] = BOOST_LIB_VERSION;
2670
mConfig["PYTHON_VERSION"] = PY_VERSION;
2671
mConfig["QT_VERSION"] = QT_VERSION_STR;
2672
mConfig["EIGEN_VERSION"] = fcEigen3Version;
2673
mConfig["PYSIDE_VERSION"] = fcPysideVersion;
2674
#ifdef SMESH_VERSION_STR
2675
mConfig["SMESH_VERSION"] = SMESH_VERSION_STR;
2677
mConfig["XERCESC_VERSION"] = fcXercescVersion;
2683
void Application::SaveEnv(const char* s)
2685
char *c = getenv(s);
2690
void Application::initApplication()
2692
// interpreter and Init script ==========================================================
2694
new Base::ScriptProducer( "CMakeVariables", CMakeVariables );
2695
new Base::ScriptProducer( "FreeCADInit", FreeCADInit );
2696
new Base::ScriptProducer( "FreeCADTest", FreeCADTest );
2698
// creating the application
2699
if (!(mConfig["Verbose"] == "Strict"))
2700
Base::Console().Log("Create Application\n");
2701
Application::_pcSingleton = new Application(mConfig);
2703
// set up Unit system default
2704
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
2705
("User parameter:BaseApp/Preferences/Units");
2706
Base::UnitsApi::setSchema((Base::UnitSystem)hGrp->GetInt("UserSchema",0));
2707
Base::UnitsApi::setDecimals(hGrp->GetInt("Decimals", Base::UnitsApi::getDecimals()));
2709
// In case we are using fractional inches, get user setting for min unit
2710
int denom = hGrp->GetInt("FracInch", Base::QuantityFormat::getDefaultDenominator());
2711
Base::QuantityFormat::setDefaultDenominator(denom);
2715
Base::Console().Log("Application is built with debug information\n");
2718
// starting the init script
2719
Base::Console().Log("Run App init script\n");
2721
Base::Interpreter().runString(Base::ScriptFactory().ProduceScript("CMakeVariables"));
2722
Base::Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit"));
2724
catch (const Base::Exception& e) {
2725
e.ReportException();
2729
srand(time(nullptr));
2732
std::list<std::string> Application::getCmdLineFiles()
2734
std::list<std::string> files;
2736
// cycling through all the open files
2737
unsigned short count = 0;
2738
count = atoi(mConfig["OpenFileCount"].c_str());
2741
for (unsigned short i=0; i<count; i++) {
2742
// getting file name
2743
std::ostringstream temp;
2744
temp << "OpenFile" << i;
2746
std::string file(mConfig[temp.str()]);
2747
files.push_back(file);
2753
std::list<std::string> Application::processFiles(const std::list<std::string>& files)
2755
std::list<std::string> processed;
2756
Base::Console().Log("Init: Processing command line files\n");
2757
for (const auto & it : files) {
2758
Base::FileInfo file(it);
2760
Base::Console().Log("Init: Processing file: %s\n",file.filePath().c_str());
2763
if (file.hasExtension("fcstd") || file.hasExtension("std")) {
2765
Application::_pcSingleton->openDocument(file.filePath().c_str());
2766
processed.push_back(it);
2768
else if (file.hasExtension("fcscript") || file.hasExtension("fcmacro")) {
2769
Base::Interpreter().runFile(file.filePath().c_str(), true);
2770
processed.push_back(it);
2772
else if (file.hasExtension("py")) {
2774
Base::Interpreter().addPythonPath(file.dirPath().c_str());
2775
Base::Interpreter().loadModule(file.fileNamePure().c_str());
2776
processed.push_back(it);
2778
catch (const Base::PyException&) {
2779
// if loading the module does not work, try just running the script (run in __main__)
2780
Base::Interpreter().runFile(file.filePath().c_str(),true);
2781
processed.push_back(it);
2785
std::string ext = file.extension();
2786
std::vector<std::string> mods = App::GetApplication().getImportModules(ext.c_str());
2787
if (!mods.empty()) {
2788
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(file.filePath().c_str());
2789
escapedstr = Base::Tools::escapeEncodeFilename(escapedstr);
2791
Base::Interpreter().loadModule(mods.front().c_str());
2792
Base::Interpreter().runStringArg("import %s",mods.front().c_str());
2793
Base::Interpreter().runStringArg("%s.open(u\"%s\")",mods.front().c_str(),
2794
escapedstr.c_str());
2795
processed.push_back(it);
2796
Base::Console().Log("Command line open: %s.open(u\"%s\")\n",mods.front().c_str(),escapedstr.c_str());
2798
else if (file.exists()) {
2799
Base::Console().Warning("File format not supported: %s \n", file.filePath().c_str());
2803
catch (const Base::SystemExitException&) {
2804
throw; // re-throw to main() function
2806
catch (const Base::Exception& e) {
2807
Base::Console().Error("Exception while processing file: %s [%s]\n", file.filePath().c_str(), e.what());
2810
Base::Console().Error("Unknown exception while processing file: %s \n", file.filePath().c_str());
2814
return processed; // successfully processed files
2817
void Application::processCmdLineFiles()
2819
// process files passed to command line
2820
std::list<std::string> files = getCmdLineFiles();
2821
std::list<std::string> processed = processFiles(files);
2823
if (files.empty()) {
2824
if (mConfig["RunMode"] == "Exit")
2825
mConfig["RunMode"] = "Cmd";
2827
else if (processed.empty() && files.size() == 1 && mConfig["RunMode"] == "Cmd") {
2828
// In case we are in console mode and the argument is not a file but Python code
2829
// then execute it. This is to behave like the standard Python executable.
2830
Base::FileInfo file(files.front());
2831
if (!file.exists()) {
2832
Base::Interpreter().runString(files.front().c_str());
2833
mConfig["RunMode"] = "Exit";
2837
const std::map<std::string,std::string>& cfg = Application::Config();
2838
std::map<std::string,std::string>::const_iterator it = cfg.find("SaveFile");
2839
if (it != cfg.end()) {
2840
std::string output = it->second;
2841
output = Base::Tools::escapeEncodeFilename(output);
2843
Base::FileInfo fi(output);
2844
std::string ext = fi.extension();
2846
std::vector<std::string> mods = App::GetApplication().getExportModules(ext.c_str());
2847
if (!mods.empty()) {
2848
Base::Interpreter().loadModule(mods.front().c_str());
2849
Base::Interpreter().runStringArg("import %s",mods.front().c_str());
2850
Base::Interpreter().runStringArg("%s.export(App.ActiveDocument.Objects, '%s')"
2851
,mods.front().c_str(),output.c_str());
2854
Base::Console().Warning("File format not supported: %s \n", output.c_str());
2857
catch (const Base::Exception& e) {
2858
Base::Console().Error("Exception while saving to file: %s [%s]\n", output.c_str(), e.what());
2861
Base::Console().Error("Unknown exception while saving to file: %s \n", output.c_str());
2866
void Application::runApplication()
2868
// process all files given through command line interface
2869
processCmdLineFiles();
2871
if (mConfig["RunMode"] == "Cmd") {
2872
// Run the commandline interface
2873
Base::Interpreter().runCommandLine("FreeCAD Console mode");
2875
else if (mConfig["RunMode"] == "Internal") {
2876
// run internal script
2877
Base::Console().Log("Running internal script:\n");
2878
Base::Interpreter().runString(Base::ScriptFactory().ProduceScript(mConfig["ScriptFileName"].c_str()));
2880
else if (mConfig["RunMode"] == "Exit") {
2882
Base::Console().Log("Exiting on purpose\n");
2885
Base::Console().Log("Unknown Run mode (%d) in main()?!?\n\n", mConfig["RunMode"].c_str());
2889
void Application::logStatus()
2891
std::string time_str = boost::posix_time::to_simple_string(
2892
boost::posix_time::second_clock::local_time());
2893
Base::Console().Log("Time = %s\n", time_str.c_str());
2895
for (const auto & It : mConfig) {
2896
Base::Console().Log("%s = %s\n", It.first.c_str(), It.second.c_str());
2900
void Application::LoadParameters()
2902
// Init parameter sets ===========================================================
2904
if (mConfig.find("UserParameter") == mConfig.end())
2905
mConfig["UserParameter"] = mConfig["UserConfigPath"] + "user.cfg";
2906
if (mConfig.find("SystemParameter") == mConfig.end())
2907
mConfig["SystemParameter"] = mConfig["UserConfigPath"] + "system.cfg";
2909
// create standard parameter sets
2910
_pcSysParamMngr = ParameterManager::Create();
2911
_pcSysParamMngr->SetSerializer(new ParameterSerializer(mConfig["SystemParameter"]));
2913
_pcUserParamMngr = ParameterManager::Create();
2914
_pcUserParamMngr->SetSerializer(new ParameterSerializer(mConfig["UserParameter"]));
2917
if (_pcSysParamMngr->LoadOrCreateDocument() && !(mConfig["Verbose"] == "Strict")) {
2918
// Configuration file optional when using as Python module
2919
if (!Py_IsInitialized()) {
2920
Base::Console().Warning(" Parameter does not exist, writing initial one\n");
2921
Base::Console().Message(" This warning normally means that FreeCAD is running for the first time\n"
2922
" or the configuration was deleted or moved. FreeCAD is generating the standard\n"
2923
" configuration.\n");
2927
catch (const Base::Exception& e) {
2928
// try to proceed with an empty XML document
2929
Base::Console().Error("%s in file %s.\n"
2930
"Continue with an empty configuration.\n",
2931
e.what(), mConfig["SystemParameter"].c_str());
2932
_pcSysParamMngr->CreateDocument();
2936
if (_pcUserParamMngr->LoadOrCreateDocument() && !(mConfig["Verbose"] == "Strict")) {
2937
// The user parameter file doesn't exist. When an alternative parameter file is offered
2938
// this will be used.
2939
std::map<std::string, std::string>::iterator it = mConfig.find("UserParameterTemplate");
2940
if (it != mConfig.end()) {
2941
QString path = QString::fromUtf8(it->second.c_str());
2942
if (QDir(path).isRelative()) {
2943
QString home = QString::fromUtf8(mConfig["AppHomePath"].c_str());
2944
path = QFileInfo(QDir(home), path).absoluteFilePath();
2948
_pcUserParamMngr->LoadDocument(path.toUtf8().constData());
2952
// Configuration file optional when using as Python module
2953
if (!Py_IsInitialized()) {
2954
Base::Console().Warning(" User settings do not exist, writing initial one\n");
2955
Base::Console().Message(" This warning normally means that FreeCAD is running for the first time\n"
2956
" or your configuration was deleted or moved. The system defaults\n"
2957
" will be automatically generated for you.\n");
2961
catch (const Base::Exception& e) {
2962
// try to proceed with an empty XML document
2963
Base::Console().Error("%s in file %s.\n"
2964
"Continue with an empty configuration.\n",
2965
e.what(), mConfig["UserParameter"].c_str());
2966
_pcUserParamMngr->CreateDocument();
2970
#if defined(_MSC_VER) && BOOST_VERSION < 108300
2971
// fix weird error while linking boost (all versions of VC)
2972
// VS2010: https://forum.freecad.org/viewtopic.php?f=4&t=1886&p=12553&hilit=boost%3A%3Afilesystem%3A%3Aget#p12553
2973
namespace boost { namespace program_options { std::string arg="arg"; } }
2974
namespace boost { namespace program_options {
2975
const unsigned options_description::m_default_line_length = 80;
2979
// A helper function to simplify the main part.
2981
ostream& operator<<(ostream& os, const vector<T>& v)
2983
copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
2990
* \brief getUserHome
2991
* Returns the user's home directory.
2993
QString getUserHome()
2996
#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD) || defined(FC_OS_MACOSX)
2997
// Default paths for the user specific stuff
2999
struct passwd *result;
3000
const std::size_t buflen = 16384;
3001
std::vector<char> buffer(buflen);
3002
int error = getpwuid_r(getuid(), &pwd, buffer.data(), buffer.size(), &result);
3005
throw Base::RuntimeError("Getting HOME path from system failed!");
3006
path = QString::fromUtf8(result->pw_dir);
3008
path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
3015
* \brief getOldGenericDataLocation
3016
* Returns a directory location where persistent data shared across applications can be stored.
3017
* This method returns the old non-XDG-compliant root path where to store config files and application data.
3019
#if defined(FC_OS_WIN32)
3020
QString getOldGenericDataLocation(QString home)
3022
#if defined(FC_OS_WIN32)
3023
WCHAR szPath[MAX_PATH];
3024
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
3025
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, szPath))) {
3026
return QString::fromStdString(converter.to_bytes(szPath));
3028
#elif defined(FC_OS_MACOSX)
3029
QFileInfo fi(home, QString::fromLatin1("Library/Preferences"));
3030
home = fi.absoluteFilePath();
3038
* \brief getSubDirectories
3039
* To a given path it adds the sub-directories where to store application specific files.
3041
void getSubDirectories(std::map<std::string,std::string>& mConfig, std::vector<std::string>& appData)
3043
// If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of
3045
if (mConfig.find("AppDataSkipVendor") == mConfig.end()) {
3046
appData.push_back(mConfig["ExeVendor"]);
3048
appData.push_back(mConfig["ExeName"]);
3052
* \brief getOldDataLocation
3053
* To a given path it adds the sub-directories where to store application specific files.
3054
* On Linux or BSD a hidden directory (i.e. starting with a dot) is added.
3056
void getOldDataLocation(std::map<std::string,std::string>& mConfig, std::vector<std::string>& appData)
3058
// Actually the name of the directory where the parameters are stored should be the name of
3059
// the application due to branding reasons.
3060
#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD)
3061
// If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of
3063
if (mConfig.find("AppDataSkipVendor") == mConfig.end()) {
3064
appData.push_back(std::string(".") + mConfig["ExeVendor"]);
3065
appData.push_back(mConfig["ExeName"]);
3067
appData.push_back(std::string(".") + mConfig["ExeName"]);
3070
#elif defined(FC_OS_MACOSX) || defined(FC_OS_WIN32)
3071
getSubDirectories(mConfig, appData);
3076
* \brief findUserHomePath
3077
* If the passed path name is not empty it will be returned, otherwise
3078
* the user home path of the system will be returned.
3080
QString findUserHomePath(const QString& userHome)
3082
if (userHome.isEmpty()) {
3083
return getUserHome();
3092
* Returns the path where to store application files to.
3093
* If \a customHome is not empty it will be used, otherwise a path starting from \a stdHome will be used.
3095
boost::filesystem::path findPath(const QString& stdHome, const QString& customHome,
3096
const std::vector<std::string>& paths, bool create)
3098
QString dataPath = customHome;
3099
if (dataPath.isEmpty()) {
3103
boost::filesystem::path appData(Base::FileInfo::stringToPath(dataPath.toStdString()));
3105
// If a custom user home path is given then don't modify it
3106
if (customHome.isEmpty()) {
3107
for (const auto& it : paths)
3108
appData = appData / it;
3111
// In order to write to our data path, we must create some directories, first.
3112
if (create && !boost::filesystem::exists(appData) && !Py_IsInitialized()) {
3114
boost::filesystem::create_directories(appData);
3115
} catch (const boost::filesystem::filesystem_error& e) {
3116
throw Base::FileSystemError("Could not create directories. Failed with: " + e.code().message());
3124
* \brief getCustomPaths
3125
* Returns a tuple of path names where to store config, data and temp. files.
3126
* The method therefore reads the environment variables:
3128
* \li FREECAD_USER_HOME
3129
* \li FREECAD_USER_DATA
3130
* \li FREECAD_USER_TEMP
3133
std::tuple<QString, QString, QString> getCustomPaths()
3135
QProcessEnvironment env(QProcessEnvironment::systemEnvironment());
3136
QString userHome = env.value(QString::fromLatin1("FREECAD_USER_HOME"));
3137
QString userData = env.value(QString::fromLatin1("FREECAD_USER_DATA"));
3138
QString userTemp = env.value(QString::fromLatin1("FREECAD_USER_TEMP"));
3140
auto toNativePath = [](QString& path) {
3141
if (!path.isEmpty()) {
3144
path = QDir::toNativeSeparators(dir.canonicalPath());
3150
// verify env. variables
3151
toNativePath(userHome);
3152
toNativePath(userData);
3153
toNativePath(userTemp);
3155
// if FREECAD_USER_HOME is set but not FREECAD_USER_DATA
3156
if (!userHome.isEmpty() && userData.isEmpty()) {
3157
userData = userHome;
3160
// if FREECAD_USER_HOME is set but not FREECAD_USER_TEMP
3161
if (!userHome.isEmpty() && userTemp.isEmpty()) {
3163
dir.mkdir(QString::fromLatin1("temp"));
3164
QFileInfo fi(dir, QString::fromLatin1("temp"));
3165
userTemp = fi.absoluteFilePath();
3168
return {userHome, userData, userTemp};
3172
* \brief getStandardPaths
3173
* Returns a tuple of XDG-compliant standard paths names where to store config, data and cached files.
3174
* The method therefore reads the environment variables:
3176
* \li XDG_CONFIG_HOME
3178
* \li XDG_CACHE_HOME
3181
std::tuple<QString, QString, QString, QString> getStandardPaths()
3183
QString configHome = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
3184
QString dataHome = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
3185
QString cacheHome = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
3186
QString tempPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
3188
// Keep the old behaviour
3189
#if defined(FC_OS_WIN32)
3190
configHome = getOldGenericDataLocation(QString());
3191
dataHome = configHome;
3193
// On systems with non-7-bit-ASCII application data directories
3194
// GetTempPathW will return a path in DOS format. This path will be
3195
// accepted by boost's file_lock class.
3196
// Since boost 1.76 there is now a version that accepts a wide string.
3197
#if (BOOST_VERSION < 107600)
3198
tempPath = QString::fromStdString(Base::FileInfo::getTempPath());
3199
cacheHome = tempPath;
3203
return std::make_tuple(configHome, dataHome, cacheHome, tempPath);
3207
void Application::ExtractUserPath()
3209
bool keepDeprecatedPaths = mConfig.count("KeepDeprecatedPaths") > 0;
3212
mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP;
3213
mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP;
3215
// this is to support a portable version of FreeCAD
3216
auto paths = getCustomPaths();
3217
QString customHome = std::get<0>(paths);
3218
QString customData = std::get<1>(paths);
3219
QString customTemp = std::get<2>(paths);
3221
// get the system standard paths
3222
auto stdPaths = getStandardPaths();
3223
QString configHome = std::get<0>(stdPaths);
3224
QString dataHome = std::get<1>(stdPaths);
3225
QString cacheHome = std::get<2>(stdPaths);
3226
QString tempPath = std::get<3>(stdPaths);
3230
QString homePath = findUserHomePath(customHome);
3231
mConfig["UserHomePath"] = homePath.toUtf8().data();
3233
// the old path name to save config and data files
3234
std::vector<std::string> subdirs;
3235
if (keepDeprecatedPaths) {
3236
configHome = homePath;
3237
dataHome = homePath;
3238
cacheHome = homePath;
3239
getOldDataLocation(mConfig, subdirs);
3242
getSubDirectories(mConfig, subdirs);
3247
boost::filesystem::path data = findPath(dataHome, customData, subdirs, true);
3248
mConfig["UserAppData"] = Base::FileInfo::pathToString(data) + PATHSEP;
3253
boost::filesystem::path config = findPath(configHome, customHome, subdirs, true);
3254
mConfig["UserConfigPath"] = Base::FileInfo::pathToString(config) + PATHSEP;
3259
std::vector<std::string> cachedirs = subdirs;
3260
cachedirs.emplace_back("Cache");
3261
boost::filesystem::path cache = findPath(cacheHome, customTemp, cachedirs, true);
3262
mConfig["UserCachePath"] = Base::FileInfo::pathToString(cache) + PATHSEP;
3265
// Set application tmp. directory
3267
std::vector<std::string> empty;
3268
boost::filesystem::path tmp = findPath(tempPath, customTemp, empty, true);
3269
mConfig["AppTempPath"] = Base::FileInfo::pathToString(tmp) + PATHSEP;
3272
// Set the default macro directory
3274
std::vector<std::string> macrodirs = subdirs;
3275
macrodirs.emplace_back("Macro");
3276
boost::filesystem::path macro = findPath(dataHome, customData, macrodirs, true);
3277
mConfig["UserMacroPath"] = Base::FileInfo::pathToString(macro) + PATHSEP;
3280
#if defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD)
3283
#include <sys/param.h>
3285
std::string Application::FindHomePath(const char* sCall)
3287
// We have three ways to start this application either use one of the two executables or
3288
// import the FreeCAD.so module from a running Python session. In the latter case the
3289
// Python interpreter is already initialized.
3290
std::string absPath;
3291
std::string homePath;
3292
if (Py_IsInitialized()) {
3293
// Note: realpath is known to cause a buffer overflow because it
3294
// expands the given path to an absolute path of unknown length.
3295
// Even setting PATH_MAX does not necessarily solve the problem
3296
// for sure but the risk of overflow is rather small.
3297
char resolved[PATH_MAX];
3298
char* path = realpath(sCall, resolved);
3303
// Find the path of the executable. Theoretically, there could occur a
3304
// race condition when using readlink, but we only use this method to
3305
// get the absolute path of the executable to compute the actual home
3306
// path. In the worst case we simply get q wrong path and FreeCAD is not
3307
// able to load its modules.
3308
char resolved[PATH_MAX];
3309
#if defined(FC_OS_BSD)
3313
mib[2] = KERN_PROC_PATHNAME;
3315
size_t cb = sizeof(resolved);
3316
sysctl(mib, 4, resolved, &cb, NULL, 0);
3317
int nchars = strlen(resolved);
3319
int nchars = readlink("/proc/self/exe", resolved, PATH_MAX);
3321
if (nchars < 0 || nchars >= PATH_MAX)
3322
throw Base::FileSystemError("Cannot determine the absolute path of the executable");
3323
resolved[nchars] = '\0'; // enforce null termination
3327
// should be an absolute path now
3328
std::string::size_type pos = absPath.find_last_of("/");
3329
homePath.assign(absPath,0,pos);
3330
pos = homePath.find_last_of("/");
3331
homePath.assign(homePath,0,pos+1);
3336
#elif defined(FC_OS_MACOSX)
3337
#include <mach-o/dyld.h>
3340
#include <sys/param.h>
3342
std::string Application::FindHomePath(const char* call)
3344
// If Python is initialized at this point, then we're being run from
3345
// MainPy.cpp, which hopefully rewrote argv[0] to point at the
3346
// FreeCAD shared library.
3347
if (!Py_IsInitialized()) {
3351
_NSGetExecutablePath(NULL, &sz); //function only returns "sz" if first arg is to small to hold value
3352
buf = new char[++sz];
3354
if (_NSGetExecutablePath(buf, &sz) == 0) {
3355
char resolved[PATH_MAX];
3356
char* path = realpath(buf, resolved);
3360
std::string Call(resolved), TempHomePath;
3361
std::string::size_type pos = Call.find_last_of(PATHSEP);
3362
TempHomePath.assign(Call,0,pos);
3363
pos = TempHomePath.find_last_of(PATHSEP);
3364
TempHomePath.assign(TempHomePath,0,pos+1);
3365
return TempHomePath;
3375
#elif defined (FC_OS_WIN32)
3376
std::string Application::FindHomePath(const char* sCall)
3378
// We have several ways to start this application:
3379
// * use one of the two executables
3380
// * import the FreeCAD.pyd module from a running Python session. In this case the
3381
// Python interpreter is already initialized.
3382
// * use a custom dll that links FreeCAD core dlls and that is loaded by its host application
3383
// In this case the calling name should be set to FreeCADBase.dll or FreeCADApp.dll in order
3384
// to locate the correct home directory
3385
wchar_t szFileName [MAX_PATH];
3386
QString dll(QString::fromUtf8(sCall));
3387
if (Py_IsInitialized() || dll.endsWith(QLatin1String(".dll"))) {
3388
GetModuleFileNameW(GetModuleHandleA(sCall),szFileName, MAX_PATH-1);
3391
GetModuleFileNameW(0, szFileName, MAX_PATH-1);
3394
std::wstring Call(szFileName), homePath;
3395
std::wstring::size_type pos = Call.find_last_of(PATHSEP);
3396
homePath.assign(Call,0,pos);
3397
pos = homePath.find_last_of(PATHSEP);
3398
homePath.assign(homePath,0,pos+1);
3400
// fixes #0001638 to avoid to load DLLs from Windows' system directories before FreeCAD's bin folder
3401
std::wstring binPath = homePath;
3403
SetDllDirectoryW(binPath.c_str());
3405
// https://stackoverflow.com/questions/5625884/conversion-of-stdwstring-to-qstring-throws-linker-error
3407
QString str = QString::fromUtf16(reinterpret_cast<const ushort *>(homePath.c_str()));
3409
QString str = QString::fromStdWString(homePath);
3412
return str.toUtf8().data();
3416
# error "std::string Application::FindHomePath(const char*) not implemented"