FreeCAD

Форк
0
/
Application.cpp 
3417 строк · 123.5 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de>              *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
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.                                 *
11
 *                                                                         *
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.                  *
16
 *                                                                         *
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  *
20
 *   USA                                                                   *
21
 *                                                                         *
22
 ***************************************************************************/
23

24
#include "PreCompiled.h"
25

26
#ifndef _PreComp_
27
# if defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD)
28
#  include <unistd.h>
29
#  include <pwd.h>
30
#  include <sys/types.h>
31
# elif defined(__MINGW32__)
32
#  undef WINVER
33
#  define WINVER 0x502 // needed for SetDllDirectory
34
#  include <Windows.h>
35
# endif
36
# include <boost/program_options.hpp>
37
# include <boost/date_time/posix_time/posix_time.hpp>
38
#endif
39

40
#ifdef FC_OS_WIN32
41
# include <Shlobj.h>
42
# include <codecvt>
43
#endif
44

45
#if defined(FC_OS_BSD)
46
#include <sys/param.h>
47
#include <sys/sysctl.h>
48
#endif
49

50
#include <QDir>
51
#include <QFileInfo>
52
#include <QProcessEnvironment>
53
#include <QStandardPaths>
54
#include <LibraryVersions.h>
55

56
#include <App/MaterialPy.h>
57
#include <App/MetadataPy.h>
58
// FreeCAD Base header
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>
79
#include <Base/Type.h>
80
#include <Base/TypePy.h>
81
#include <Base/UnitPy.h>
82
#include <Base/UnitsApi.h>
83
#include <Base/VectorPy.h>
84

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"
100
#include "Link.h"
101
#include "LinkBaseExtensionPy.h"
102
#include "VarSet.h"
103
#include "MaterialObject.h"
104
#include "MeasureDistance.h"
105
#include "Origin.h"
106
#include "OriginFeature.h"
107
#include "OriginGroupExtension.h"
108
#include "OriginGroupExtensionPy.h"
109
#include "SuppressibleExtension.h"
110
#include "SuppressibleExtensionPy.h"
111
#include "Part.h"
112
#include "PartPy.h"
113
#include "Placement.h"
114
#include "ProgramOptionsUtilities.h"
115
#include "Property.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"
126

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>
131
#include "Branding.h"
132

133

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>
138

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
143
# endif
144

145
# include <new.h>
146
# include <eh.h> // VC exception handling
147
#else // Ansi C/C++ new handler
148
# include <new>
149
#endif
150

151
FC_LOG_LEVEL_INIT("App", true, true)
152

153
using namespace App;
154
using namespace Base;
155
using namespace std;
156
using namespace boost;
157
using namespace boost::program_options;
158
using Base::FileInfo;
159
namespace sp = std::placeholders;
160

161
//==========================================================================
162
// Application
163
//==========================================================================
164

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;
169

170
AppExport std::map<std::string, std::string> Application::mConfig;
171

172

173
//**************************************************************************
174
// Construction and destruction
175

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"
180
     "\n"
181
     "The document has the read-only attribute FileName which points to the\n"
182
     "file the document should be stored to.\n"
183
    );
184

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."
191
    );
192

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"
196
    );
197

198
// This is called via the PyImport_AppendInittab mechanism called
199
// during initialization, to make the built-in __FreeCADBase__
200
// module known to Python.
201
PyMODINIT_FUNC
202
init_freecad_base_module(void)
203
{
204
    static struct PyModuleDef BaseModuleDef = {
205
        PyModuleDef_HEAD_INIT,
206
        "__FreeCADBase__", Base_doc, -1,
207
        nullptr, nullptr, nullptr, nullptr, nullptr
208
    };
209
    return PyModule_Create(&BaseModuleDef);
210
}
211

212
// Set in inside Application
213
static PyMethodDef* ApplicationMethods = nullptr;
214

215
PyMODINIT_FUNC
216
init_freecad_module(void)
217
{
218
    static struct PyModuleDef FreeCADModuleDef = {
219
        PyModuleDef_HEAD_INIT,
220
        "FreeCAD", FreeCAD_doc, -1,
221
        ApplicationMethods,
222
        nullptr, nullptr, nullptr, nullptr
223
    };
224
    return PyModule_Create(&FreeCADModuleDef);
225
}
226

227
PyMODINIT_FUNC
228
init_image_module()
229
{
230
    static struct PyModuleDef ImageModuleDef = {
231
        PyModuleDef_HEAD_INIT,
232
        "Image", "", -1,
233
        nullptr,
234
        nullptr, nullptr, nullptr, nullptr
235
    };
236
    return PyModule_Create(&ImageModuleDef);
237
}
238

239
Application::Application(std::map<std::string,std::string> &mConfig)
240
  : _mConfig(mConfig)
241
{
242
    //_hApp = new ApplicationOCC;
243
    mpcPramManager["System parameter"] = _pcSysParamMngr;
244
    mpcPramManager["User parameter"] = _pcUserParamMngr;
245

246
    setupPythonTypes();
247
}
248

249
Application::~Application() = default;
250

251
void Application::setupPythonTypes()
252
{
253
    // setting up Python binding
254
    Base::PyGILStateLocker lock;
255
    PyObject* modules = PyImport_GetModuleDict();
256

257
    ApplicationMethods = Application::Methods;
258
    PyObject* pAppModule = PyImport_ImportModule ("FreeCAD");
259
    if (!pAppModule) {
260
        PyErr_Clear();
261
        pAppModule = init_freecad_module();
262
        PyDict_SetItemString(modules, "FreeCAD", pAppModule);
263
    }
264
    Py::Module(pAppModule).setAttr(std::string("ActiveDocument"),Py::None());
265

266
    static struct PyModuleDef ConsoleModuleDef = {
267
        PyModuleDef_HEAD_INIT,
268
        "__FreeCADConsole__", Console_doc, -1,
269
        Base::ConsoleSingleton::Methods,
270
        nullptr, nullptr, nullptr, nullptr
271
    };
272
    PyObject* pConsoleModule = PyModule_Create(&ConsoleModuleDef);
273

274
    // fake Image module
275
    PyObject* imageModule = init_image_module();
276
    PyDict_SetItemString(modules, "Image", imageModule);
277

278
    // introducing additional classes
279

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");
289

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.
293

294
    PyObject* pBaseModule = PyImport_ImportModule ("__FreeCADBase__");
295
    if (!pBaseModule) {
296
        PyErr_Clear();
297
        pBaseModule = init_freecad_base_module();
298
        PyDict_SetItemString(modules, "__FreeCADBase__", pBaseModule);
299
    }
300

301
    setupPythonException(pBaseModule);
302

303

304
    // Python types
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");
314

315
    Base::Interpreter().addType(&App::MaterialPy::Type, pAppModule, "Material");
316
    Base::Interpreter().addType(&App::MetadataPy::Type, pAppModule, "Metadata");
317

318
    Base::Interpreter().addType(&App::StringHasherPy::Type, pAppModule, "StringHasher");
319
    Base::Interpreter().addType(&App::StringIDPy::Type, pAppModule, "StringID");
320

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");
329

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");
337

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);
343

344
    // Translate module
345
    PyObject* pTranslateModule = Base::Interpreter().addModule(new Base::Translate);
346
    Py_INCREF(pTranslateModule);
347
    PyModule_AddObject(pAppModule, "Qt", pTranslateModule);
348

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
355
    };
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");
360

361
    Py_INCREF(pUnitsModule);
362
    PyModule_AddObject(pAppModule, "Units", pUnitsModule);
363

364
    Base::ProgressIndicatorPy::init_type();
365
    Base::Interpreter().addType(Base::ProgressIndicatorPy::type_object(),
366
        pBaseModule,"ProgressIndicator");
367

368
    Base::Vector2dPy::init_type();
369
    Base::Interpreter().addType(Base::Vector2dPy::type_object(),
370
        pBaseModule,"Vector2d");
371
}
372

373
void Application::setupPythonException(PyObject* module)
374
{
375
    // Define custom Python exception types
376
    //
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);
380

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);
384

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);
388

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);
392

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);
396

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);
400

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);
404

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);
408

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);
412

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);
416

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);
420
}
421

422
//**************************************************************************
423
// Interface
424

425
/// get called by the document when the name is changing
426
void Application::renameDocument(const char *OldName, const char *NewName)
427
{
428
    (void)OldName;
429
    (void)NewName;
430
    throw Base::RuntimeError("Renaming document internal name is no longer allowed!");
431
}
432

433
Document* Application::newDocument(const char * Name, const char * UserName, bool createView, bool tempDoc)
434
{
435
    auto getNameAndLabel = [this](const char * Name, const char * UserName) -> std::tuple<std::string, std::string> {
436
        bool defaultName = (!Name || Name[0] == '\0');
437

438
        // get a valid name anyway!
439
        if (defaultName) {
440
            Name = "Unnamed";
441
        }
442

443
        std::string userName;
444
        if (UserName && UserName[0] != '\0') {
445
            userName = UserName;
446
        }
447
        else {
448
            userName = defaultName ? QObject::tr("Unnamed").toStdString() : Name;
449

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());
454
            }
455

456
            if (!names.empty()) {
457
                userName = Base::Tools::getUniqueName(userName, names);
458
            }
459
        }
460

461
        return std::make_tuple(std::string(Name), userName);
462
    };
463

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);
468

469
    // return the temporary document if it exists
470
    if (tempDoc) {
471
        auto it = DocMap.find(name);
472
        if (it != DocMap.end() && it->second->testStatus(Document::TempDoc))
473
            return it->second;
474
    }
475

476
    // create the FreeCAD document
477
    std::unique_ptr<Document> newDoc(new Document(name.c_str()));
478
    newDoc->setStatus(Document::TempDoc, tempDoc);
479

480
    auto oldActiveDoc = _pActiveDoc;
481
    auto doc = newDoc.release(); // now owned by the Application
482

483
    // add the document to the internal list
484
    DocMap[name] = doc;
485
    _pActiveDoc = doc;
486

487
    //NOLINTBEGIN
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));
508
    //NOLINTEND
509

510
    // make sure that the active document is set in case no GUI is up
511
    {
512
        Base::PyGILStateLocker lock;
513
        Py::Object active(_pActiveDoc->getPyObject(), true);
514
        Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"), active);
515
    }
516

517
    signalNewDocument(*_pActiveDoc, createView);
518

519
    // set the UserName after notifying all observers
520
    _pActiveDoc->Label.setValue(userName);
521

522
    // set the old document active again if the new is temporary
523
    if (tempDoc && oldActiveDoc)
524
        setActiveDocument(oldActiveDoc);
525

526
    return doc;
527
}
528

529
bool Application::closeDocument(const char* name)
530
{
531
    map<string,Document*>::iterator pos = DocMap.find( name );
532
    if (pos == DocMap.end()) // no such document
533
        return false;
534

535
    Base::ConsoleRefreshDisabler disabler;
536

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);
540

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);
545
    DocMap.erase( pos );
546
    DocFileMap.erase(FileInfo(delDoc->FileName.getValue()).filePath());
547

548
    _objCount = -1;
549

550
    // Trigger observers after removing the document from the internal map.
551
    signalDeletedDocument();
552

553
    return true;
554
}
555

556
void Application::closeAllDocuments()
557
{
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());
562
}
563

564
App::Document* Application::getDocument(const char *Name) const
565
{
566
    std::map<std::string,Document*>::const_iterator pos;
567

568
    pos = DocMap.find(Name);
569

570
    if (pos == DocMap.end())
571
        return nullptr;
572

573
    return pos->second;
574
}
575

576
const char * Application::getDocumentName(const App::Document* doc) const
577
{
578
    for (const auto & it : DocMap) {
579
        if (it.second == doc) {
580
            return it.first.c_str();
581
        }
582
    }
583

584
    return nullptr;
585
}
586

587
std::vector<App::Document*> Application::getDocuments() const
588
{
589
    std::vector<App::Document*> docs;
590
    for (const auto & it : DocMap)
591
        docs.push_back(it.second);
592
    return docs;
593
}
594

595
std::string Application::getUniqueDocumentName(const char *Name, bool tempDoc) const
596
{
597
    if (!Name || *Name == '\0')
598
        return {};
599
    std::string CleanName = Base::Tools::getIdentifier(Name);
600

601
    // name in use?
602
    std::map<string,Document*>::const_iterator pos;
603
    pos = DocMap.find(CleanName);
604

605
    if (pos == DocMap.end() || (tempDoc && pos->second->testStatus(Document::TempDoc))) {
606
        // if not, name is OK
607
        return CleanName;
608
    }
609
    else {
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);
615
        }
616
        return Base::Tools::getUniqueName(CleanName, names);
617
    }
618
}
619

620
int Application::addPendingDocument(const char *FileName, const char *objName, bool allowPartial)
621
{
622
    if(!_isRestoring)
623
        return 0;
624
    if(allowPartial && _allowPartial)
625
        return -1;
626
    assert(FileName && FileName[0]);
627
    assert(objName && objName[0]);
628
    if(!_docReloadAttempts[FileName].emplace(objName).second)
629
        return -1;
630
    auto ret =  _pendingDocMap.emplace(FileName,std::vector<std::string>());
631
    ret.first->second.emplace_back(objName);
632
    if(ret.second) {
633
        _pendingDocs.emplace_back(ret.first->first.c_str());
634
        return 1;
635
    }
636
    return -1;
637
}
638

639
bool Application::isRestoring() const {
640
    return _isRestoring || Document::isAnyRestoring();
641
}
642

643
bool Application::isClosingAll() const {
644
    return _isClosingAll;
645
}
646

647
struct DocTiming {
648
    FC_DURATION_DECLARE(d1);
649
    FC_DURATION_DECLARE(d2);
650
    DocTiming() {
651
        FC_DURATION_INIT(d1);
652
        FC_DURATION_INIT(d2);
653
    }
654
};
655

656
class DocOpenGuard {
657
public:
658
    bool &flag;
659
    boost::signals2::signal<void ()> &signal;
660
    DocOpenGuard(bool &f, boost::signals2::signal<void ()> &s)
661
        :flag(f),signal(s)
662
    {
663
        flag = true;
664
    }
665
    ~DocOpenGuard() {
666
        if(flag) {
667
            flag = false;
668
            try {
669
                signal();
670
            }
671
            catch (const boost::exception&) {
672
                // reported by code analyzers
673
                Base::Console().Warning("~DocOpenGuard: Unexpected boost exception\n");
674
            }
675
        }
676
    }
677
};
678

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);
682
    if(!docs.empty())
683
        return docs.front();
684
    return nullptr;
685
}
686

687
Document *Application::getDocumentByPath(const char *path, PathMatchMode checkCanonical) const {
688
    if(!path || !path[0])
689
        return nullptr;
690
    if(DocFileMap.empty()) {
691
        for(const auto &v : DocMap) {
692
            const auto &file = v.second->FileName.getStrValue();
693
            if(!file.empty())
694
                DocFileMap[FileInfo(file.c_str()).filePath()] = v.second;
695
        }
696
    }
697
    auto it = DocFileMap.find(FileInfo(path).filePath());
698
    if(it != DocFileMap.end())
699
        return it->second;
700

701
    if (checkCanonical == PathMatchMode::MatchAbsolute)
702
        return nullptr;
703

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)
710
                return v.second;
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() << "'");
716
            break;
717
        }
718
    }
719
    return nullptr;
720
}
721

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,
726
                                                  bool createView)
727
{
728
    std::vector<Document*> res(filenames.size(), nullptr);
729
    if (filenames.empty())
730
        return res;
731

732
    if (errs)
733
        errs->resize(filenames.size());
734

735
    DocOpenGuard guard(_isRestoring, signalFinishOpenDocument);
736
    _pendingDocs.clear();
737
    _pendingDocsReopen.clear();
738
    _pendingDocMap.clear();
739
    _docReloadAttempts.clear();
740

741
    signalStartOpenDocument();
742

743
    ParameterGrp::handle hGrp = GetParameterGroupByPath("User parameter:BaseApp/Preferences/Document");
744
    _allowPartial = !hGrp->GetBool("NoPartialLoading",false);
745

746
    for (auto &name : filenames)
747
        _pendingDocs.emplace_back(name.c_str());
748

749
    std::map<DocumentT, DocTiming> timings;
750

751
    FC_TIME_INIT(t);
752

753
    std::vector<DocumentT> openedDocs;
754

755
    int pass = 0;
756
    do {
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());
762

763
            try {
764
                _objCount = -1;
765
                std::vector<std::string> objNames;
766
                if (_allowPartial) {
767
                    auto it = _pendingDocMap.find(name);
768
                    if (it != _pendingDocMap.end()) {
769
                        if(isMainDoc)
770
                            it->second.clear();
771
                        else
772
                            objNames.swap(it->second);
773
                        _pendingDocMap.erase(it);
774
                    }
775
                }
776

777
                FC_TIME_INIT(t1);
778
                DocTiming timing;
779

780
                const char *path = name.c_str();
781
                const char *label = nullptr;
782
                if (isMainDoc) {
783
                    if (paths && paths->size()>count)
784
                        path = (*paths)[count].c_str();
785

786
                    if (labels && labels->size()>count)
787
                        label = (*labels)[count].c_str();
788
                }
789

790
                auto doc = openDocumentPrivate(path, name.c_str(), label, isMainDoc, createView, std::move(objNames));
791
                FC_DURATION_PLUS(timing.d1,t1);
792
                if (doc) {
793
                    timings[doc].d1 += timing.d1;
794
                    newDocs.emplace(doc);
795
                }
796

797
                if (isMainDoc)
798
                    res[count] = doc;
799
                _objCount = -1;
800
            }
801
            catch (const Base::Exception &e) {
802
                e.ReportException();
803
                if (!errs && isMainDoc)
804
                    throw;
805
                if (errs && isMainDoc)
806
                    (*errs)[count] = e.what();
807
                else
808
                    Base::Console().Error("Exception opening file: %s [%s]\n", name.c_str(), e.what());
809
            }
810
            catch (const std::exception &e) {
811
                if (!errs && isMainDoc)
812
                    throw;
813
                if (errs && isMainDoc)
814
                    (*errs)[count] = e.what();
815
                else
816
                    Base::Console().Error("Exception opening file: %s [%s]\n", name.c_str(), e.what());
817
            }
818
            catch (...) {
819
                if (errs) {
820
                    if (isMainDoc)
821
                        (*errs)[count] = "unknown error";
822
                }
823
                else {
824
                    _pendingDocs.clear();
825
                    _pendingDocsReopen.clear();
826
                    _pendingDocMap.clear();
827
                    throw;
828
                }
829
            }
830

831
            if (_pendingDocs.empty()) {
832
                if(_pendingDocsReopen.empty())
833
                    break;
834
                _pendingDocs = std::move(_pendingDocsReopen);
835
                _pendingDocsReopen.clear();
836
                for(const auto &file : _pendingDocs) {
837
                    auto doc = getDocumentByPath(file.c_str());
838
                    if(doc)
839
                        closeDocument(doc->getName());
840
                }
841
            }
842
        }
843

844
        ++pass;
845
        _pendingDocMap.clear();
846

847
        std::vector<Document*> docs;
848
        docs.reserve(newDocs.size());
849
        for(const auto &d : newDocs) {
850
            auto doc = d.getDocument();
851
            if(!doc)
852
                continue;
853
            // Notify PropertyXLink to attach newly opened documents and restore
854
            // relevant external links
855
            PropertyXLink::restoreDocument(*doc);
856
            docs.push_back(doc);
857
        }
858

859
        Base::SequencerLauncher seq("Postprocessing...", docs.size());
860

861
        // After external links has been restored, we can now sort the document
862
        // according to their dependency order.
863
        try {
864
            docs = Document::getDependentDocuments(docs, true);
865
        } catch (Base::Exception &e) {
866
            e.ReportException();
867
        }
868
        for(auto it=docs.begin(); it!=docs.end();) {
869
            auto doc = *it;
870

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)) {
875
                it = docs.erase(it);
876
                continue;
877
            }
878

879
            auto &timing = timings[doc];
880
            FC_TIME_INIT(t1);
881
            // Finalize document restoring with the correct order
882
            if(doc->afterRestore(true)) {
883
                openedDocs.emplace_back(doc);
884
                it = docs.erase(it);
885
            } else {
886
                ++it;
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());
896
            }
897
            FC_DURATION_PLUS(timing.d2,t1);
898
            seq.next();
899
        }
900
        // Close the document for reloading
901
        for(const auto doc : docs)
902
            closeDocument(doc->getName());
903

904
    }while(!_pendingDocs.empty());
905

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) {
909
        if (doc) {
910
            setActiveDocument(doc);
911
            break;
912
        }
913
    }
914

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");
919
    }
920
    FC_TIME_LOG(t,"total");
921
    _isRestoring = false;
922

923
    signalFinishOpenDocument();
924
    return res;
925
}
926

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)
931
{
932
    FileInfo File(FileName);
933

934
    if (!File.exists()) {
935
        std::stringstream str;
936
        str << "File '" << FileName << "' does not exist!";
937
        throw Base::FileSystemError(str.str().c_str());
938
    }
939

940
    // Before creating a new document we check whether the document is already open
941
    auto doc = getDocumentByPath(File.filePath().c_str(), PathMatchMode::MatchCanonicalWarning);
942
    if(doc) {
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
948

949
            if(isMainDoc) {
950
                // Main document must be open fully, so close and reopen
951
                closeDocument(doc->getName());
952
                doc = nullptr;
953
            } else if(_allowPartial) {
954
                bool reopen = false;
955
                for(const auto &name : objNames) {
956
                    auto obj = doc->getObject(name.c_str());
957
                    if(!obj || obj->testStatus(App::PartialObject)) {
958
                        reopen = true;
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);
969
                        break;
970
                    }
971
                }
972
                if(!reopen)
973
                    return nullptr;
974
            }
975

976
            if(doc) {
977
                _pendingDocsReopen.emplace_back(FileName);
978
                return nullptr;
979
            }
980
        }
981

982
        if(!isMainDoc)
983
            return nullptr;
984
        else if(doc)
985
            return doc;
986
    }
987

988
    std::string name;
989
    if(propFileName != FileName) {
990
        FileInfo fi(propFileName);
991
        name = fi.fileNamePure();
992
    }else
993
        name = File.fileNamePure();
994

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.
998
    if(!label)
999
        label = name.c_str();
1000
    Document* newDoc = newDocument(name.c_str(),label,isMainDoc && createView);
1001

1002
    newDoc->FileName.setValue(propFileName==FileName?File.filePath():propFileName);
1003

1004
    try {
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;
1009
        return newDoc;
1010
    }
1011
    // if the project file itself is corrupt then
1012
    // close the document
1013
    catch (const Base::FileException&) {
1014
        closeDocument(newDoc->getName());
1015
        throw;
1016
    }
1017
    catch (const std::ios_base::failure&) {
1018
        closeDocument(newDoc->getName());
1019
        throw;
1020
    }
1021
    // but for any other exceptions leave it open to give the
1022
    // user a chance to fix it
1023
    catch (...) {
1024
        throw;
1025
    }
1026
}
1027

1028
Document* Application::getActiveDocument() const
1029
{
1030
    return _pActiveDoc;
1031
}
1032

1033
void Application::setActiveDocument(Document* pDoc)
1034
{
1035
    _pActiveDoc = pDoc;
1036

1037
    // make sure that the active document is set in case no GUI is up
1038
    if (pDoc) {
1039
        Base::PyGILStateLocker lock;
1040
        Py::Object active(pDoc->getPyObject(), true);
1041
        Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"),active);
1042
    }
1043
    else {
1044
        Base::PyGILStateLocker lock;
1045
        Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"),Py::None());
1046
    }
1047

1048
    if (pDoc)
1049
        signalActiveDocument(*pDoc);
1050
}
1051

1052
void Application::setActiveDocument(const char *Name)
1053
{
1054
    // If no active document is set, resort to a default.
1055
    if (*Name == '\0') {
1056
        _pActiveDoc = nullptr;
1057
        return;
1058
    }
1059

1060
    std::map<std::string,Document*>::iterator pos;
1061
    pos = DocMap.find(Name);
1062

1063
    if (pos != DocMap.end()) {
1064
        setActiveDocument(pos->second);
1065
    }
1066
    else {
1067
        std::stringstream s;
1068
        s << "Try to activate unknown document '" << Name << "'";
1069
        throw Base::RuntimeError(s.str());
1070
    }
1071
}
1072

1073
static int _TransSignalCount;
1074
static bool _TransSignalled;
1075
Application::TransactionSignaller::TransactionSignaller(bool abort, bool signal)
1076
    :abort(abort)
1077
{
1078
    ++_TransSignalCount;
1079
    if(signal && !_TransSignalled) {
1080
        _TransSignalled = true;
1081
        GetApplication().signalBeforeCloseTransaction(abort);
1082
    }
1083
}
1084

1085
Application::TransactionSignaller::~TransactionSignaller() {
1086
    if(--_TransSignalCount == 0 && _TransSignalled) {
1087
        _TransSignalled = false;
1088
        try {
1089
            GetApplication().signalCloseTransaction(abort);
1090
        }
1091
        catch (const boost::exception&) {
1092
            // reported by code analyzers
1093
            Base::Console().Warning("~TransactionSignaller: Unexpected boost exception\n");
1094
        }
1095
    }
1096
}
1097

1098
std::string Application::getHomePath()
1099
{
1100
    return mConfig["AppHomePath"];
1101
}
1102

1103
std::string Application::getExecutableName()
1104
{
1105
    return mConfig["ExeName"];
1106
}
1107

1108
std::string Application::getTempPath()
1109
{
1110
    return mConfig["AppTempPath"];
1111
}
1112

1113
std::string Application::getTempFileName(const char* FileName)
1114
{
1115
    return Base::FileInfo::getTempFileName(FileName, getTempPath().c_str());
1116
}
1117

1118
std::string Application::getUserCachePath()
1119
{
1120
    return mConfig["UserCachePath"];
1121
}
1122

1123
std::string Application::getUserConfigPath()
1124
{
1125
    return mConfig["UserConfigPath"];
1126
}
1127

1128
std::string Application::getUserAppDataDir()
1129
{
1130
    return mConfig["UserAppData"];
1131
}
1132

1133
std::string Application::getUserMacroDir()
1134
{
1135
    return mConfig["UserMacroPath"];
1136
}
1137

1138
std::string Application::getResourceDir()
1139
{
1140
#ifdef RESOURCEDIR
1141
    // #6892: Conda may inject null characters => remove them
1142
    std::string path = std::string(RESOURCEDIR).c_str();
1143
    path += PATHSEP;
1144
    QDir dir(QString::fromStdString(path));
1145
    if (dir.isAbsolute())
1146
        return path;
1147
    return mConfig["AppHomePath"] + path;
1148
#else
1149
    return mConfig["AppHomePath"];
1150
#endif
1151
}
1152

1153
std::string Application::getLibraryDir()
1154
{
1155
#ifdef LIBRARYDIR
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())
1160
        return path;
1161
    return mConfig["AppHomePath"] + path;
1162
#else
1163
    return mConfig["AppHomePath"] + "lib";
1164
#endif
1165
}
1166

1167
std::string Application::getHelpDir()
1168
{
1169
#ifdef DOCDIR
1170
    // #6892: Conda may inject null characters => remove them
1171
    std::string path = std::string(DOCDIR).c_str();
1172
    path += PATHSEP;
1173
    QDir dir(QString::fromStdString(path));
1174
    if (dir.isAbsolute())
1175
        return path;
1176
    return mConfig["AppHomePath"] + path;
1177
#else
1178
    return mConfig["DocPath"];
1179
#endif
1180
}
1181

1182
int Application::checkLinkDepth(int depth, MessageOption option)
1183
{
1184
    if (_objCount < 0) {
1185
        _objCount = 0;
1186
        for (auto &v : DocMap) {
1187
            _objCount += v.second->countObjects();
1188
        }
1189
    }
1190

1191
    if (depth > _objCount + 2) {
1192
        const char *msg = "Link recursion limit reached. "
1193
                "Please check for cyclic reference.";
1194
        switch (option) {
1195
        case MessageOption::Quiet:
1196
            return 0;
1197
        case MessageOption::Error:
1198
            FC_ERR(msg);
1199
            return 0;
1200
        case MessageOption::Throw:
1201
            throw Base::RuntimeError(msg);
1202
        }
1203
    }
1204

1205
    return _objCount + 2;
1206
}
1207

1208
std::set<DocumentObject *> Application::getLinksTo(
1209
        const DocumentObject *obj, int options, int maxCount) const
1210
{
1211
    std::set<DocumentObject *> links;
1212
    if(!obj) {
1213
        for(auto &v : DocMap) {
1214
            v.second->getLinksTo(links,obj,options,maxCount);
1215
            if(maxCount && (int)links.size()>=maxCount)
1216
                break;
1217
        }
1218
    } else {
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)
1224
                    break;
1225
            }
1226
        }
1227
    }
1228
    return links;
1229
}
1230

1231
bool Application::hasLinksTo(const DocumentObject *obj) const {
1232
    return !getLinksTo(obj,0,1).empty();
1233
}
1234

1235
ParameterManager & Application::GetSystemParameter()
1236
{
1237
    return *_pcSysParamMngr;
1238
}
1239

1240
ParameterManager & Application::GetUserParameter()
1241
{
1242
    return *_pcUserParamMngr;
1243
}
1244

1245
ParameterManager * Application::GetParameterSet(const char* sName) const
1246
{
1247
    auto it = mpcPramManager.find(sName);
1248
    if ( it != mpcPramManager.end() )
1249
        return it->second;
1250
    else
1251
        return nullptr;
1252
}
1253

1254
const std::map<std::string,Base::Reference<ParameterManager>> &
1255
Application::GetParameterSetList() const
1256
{
1257
    return mpcPramManager;
1258
}
1259

1260
void Application::AddParameterSet(const char* sName)
1261
{
1262
    auto it = mpcPramManager.find(sName);
1263
    if ( it != mpcPramManager.end() )
1264
        return;
1265
    mpcPramManager[sName] = ParameterManager::Create();
1266
}
1267

1268
void Application::RemoveParameterSet(const char* sName)
1269
{
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 )
1273
        return;
1274
    mpcPramManager.erase(it);
1275
}
1276

1277
Base::Reference<ParameterGrp>  Application::GetParameterGroupByPath(const char* sName)
1278
{
1279
    std::string cName = sName,cTemp;
1280

1281
    std::string::size_type pos = cName.find(':');
1282

1283
    // is there a path separator ?
1284
    if (pos == std::string::npos) {
1285
        throw Base::ValueError("Application::GetParameterGroupByPath() no parameter set name specified");
1286
    }
1287
    // assigning the parameter set name
1288
    cTemp.assign(cName,0,pos);
1289
    cName.erase(0,pos+1);
1290

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");
1295

1296
    return It->second->GetGroup(cName.c_str());
1297
}
1298

1299
void Application::addImportType(const char* Type, const char* ModuleName)
1300
{
1301
    FileTypeItem item;
1302
    item.filter = Type;
1303
    item.module = ModuleName;
1304

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);
1313
    }
1314

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);
1322
    }
1323
    else {
1324
        _mImportTypes.push_back(item);
1325
    }
1326
}
1327

1328
void Application::changeImportModule(const char* Type, const char* OldModuleName, const char* NewModuleName)
1329
{
1330
    for (auto& it : _mImportTypes) {
1331
        if (it.filter == Type && it.module == OldModuleName) {
1332
            it.module = NewModuleName;
1333
            break;
1334
        }
1335
    }
1336
}
1337

1338
std::vector<std::string> Application::getImportModules(const char* Type) const
1339
{
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) {
1344
#ifdef __GNUC__
1345
            if (strcasecmp(Type,jt.c_str()) == 0)
1346
#else
1347
            if (_stricmp(Type,jt.c_str()) == 0)
1348
#endif
1349
                modules.push_back(it.module);
1350
        }
1351
    }
1352

1353
    return modules;
1354
}
1355

1356
std::vector<std::string> Application::getImportModules() const
1357
{
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());
1363
    return modules;
1364
}
1365

1366
std::vector<std::string> Application::getImportTypes(const char* Module) const
1367
{
1368
    std::vector<std::string> types;
1369
    for (const auto & it : _mImportTypes) {
1370
#ifdef __GNUC__
1371
        if (strcasecmp(Module,it.module.c_str()) == 0)
1372
#else
1373
        if (_stricmp(Module,it.module.c_str()) == 0)
1374
#endif
1375
            types.insert(types.end(), it.types.begin(), it.types.end());
1376
    }
1377

1378
    return types;
1379
}
1380

1381
std::vector<std::string> Application::getImportTypes() const
1382
{
1383
    std::vector<std::string> types;
1384
    for (const auto & it : _mImportTypes) {
1385
        types.insert(types.end(), it.types.begin(), it.types.end());
1386
    }
1387

1388
    std::sort(types.begin(), types.end());
1389
    types.erase(std::unique(types.begin(), types.end()), types.end());
1390

1391
    return types;
1392
}
1393

1394
std::map<std::string, std::string> Application::getImportFilters(const char* Type) const
1395
{
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) {
1400
#ifdef __GNUC__
1401
            if (strcasecmp(Type,jt.c_str()) == 0)
1402
#else
1403
            if (_stricmp(Type,jt.c_str()) == 0)
1404
#endif
1405
                moduleFilter[it.filter] = it.module;
1406
        }
1407
    }
1408

1409
    return moduleFilter;
1410
}
1411

1412
std::map<std::string, std::string> Application::getImportFilters() const
1413
{
1414
    std::map<std::string, std::string> filter;
1415
    for (const auto & it : _mImportTypes) {
1416
        filter[it.filter] = it.module;
1417
    }
1418

1419
    return filter;
1420
}
1421

1422
void Application::addExportType(const char* Type, const char* ModuleName)
1423
{
1424
    FileTypeItem item;
1425
    item.filter = Type;
1426
    item.module = ModuleName;
1427

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);
1436
    }
1437

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);
1445
    }
1446
    else {
1447
        _mExportTypes.push_back(item);
1448
    }
1449
}
1450

1451
void Application::changeExportModule(const char* Type, const char* OldModuleName, const char* NewModuleName)
1452
{
1453
    for (auto& it : _mExportTypes) {
1454
        if (it.filter == Type && it.module == OldModuleName) {
1455
            it.module = NewModuleName;
1456
            break;
1457
        }
1458
    }
1459
}
1460

1461
std::vector<std::string> Application::getExportModules(const char* Type) const
1462
{
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) {
1467
#ifdef __GNUC__
1468
            if (strcasecmp(Type,jt.c_str()) == 0)
1469
#else
1470
            if (_stricmp(Type,jt.c_str()) == 0)
1471
#endif
1472
                modules.push_back(it.module);
1473
        }
1474
    }
1475

1476
    return modules;
1477
}
1478

1479
std::vector<std::string> Application::getExportModules() const
1480
{
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());
1486
    return modules;
1487
}
1488

1489
std::vector<std::string> Application::getExportTypes(const char* Module) const
1490
{
1491
    std::vector<std::string> types;
1492
    for (const auto & it : _mExportTypes) {
1493
#ifdef __GNUC__
1494
        if (strcasecmp(Module,it.module.c_str()) == 0)
1495
#else
1496
        if (_stricmp(Module,it.module.c_str()) == 0)
1497
#endif
1498
            types.insert(types.end(), it.types.begin(), it.types.end());
1499
    }
1500

1501
    return types;
1502
}
1503

1504
std::vector<std::string> Application::getExportTypes() const
1505
{
1506
    std::vector<std::string> types;
1507
    for (const FileTypeItem& it : _mExportTypes) {
1508
        types.insert(types.end(), it.types.begin(), it.types.end());
1509
    }
1510

1511
    std::sort(types.begin(), types.end());
1512
    types.erase(std::unique(types.begin(), types.end()), types.end());
1513

1514
    return types;
1515
}
1516

1517
std::map<std::string, std::string> Application::getExportFilters(const char* Type) const
1518
{
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) {
1523
#ifdef __GNUC__
1524
            if (strcasecmp(Type,jt.c_str()) == 0)
1525
#else
1526
            if (_stricmp(Type,jt.c_str()) == 0)
1527
#endif
1528
                moduleFilter[it.filter] = it.module;
1529
        }
1530
    }
1531

1532
    return moduleFilter;
1533
}
1534

1535
std::map<std::string, std::string> Application::getExportFilters() const
1536
{
1537
    std::map<std::string, std::string> filter;
1538
    for (const FileTypeItem& it : _mExportTypes) {
1539
        filter[it.filter] = it.module;
1540
    }
1541

1542
    return filter;
1543
}
1544

1545
//**************************************************************************
1546
// signaling
1547
void Application::slotBeforeChangeDocument(const App::Document& doc, const Property& prop)
1548
{
1549
    this->signalBeforeChangeDocument(doc, prop);
1550
}
1551

1552
void Application::slotChangedDocument(const App::Document& doc, const Property& prop)
1553
{
1554
    this->signalChangedDocument(doc, prop);
1555
}
1556

1557
void Application::slotNewObject(const App::DocumentObject&O)
1558
{
1559
    this->signalNewObject(O);
1560
    _objCount = -1;
1561
}
1562

1563
void Application::slotDeletedObject(const App::DocumentObject&O)
1564
{
1565
    this->signalDeletedObject(O);
1566
    _objCount = -1;
1567
}
1568

1569
void Application::slotBeforeChangeObject(const DocumentObject& O, const Property& Prop)
1570
{
1571
    this->signalBeforeChangeObject(O, Prop);
1572
}
1573

1574
void Application::slotChangedObject(const App::DocumentObject&O, const App::Property& P)
1575
{
1576
    this->signalChangedObject(O,P);
1577
}
1578

1579
void Application::slotRelabelObject(const App::DocumentObject&O)
1580
{
1581
    this->signalRelabelObject(O);
1582
}
1583

1584
void Application::slotActivatedObject(const App::DocumentObject&O)
1585
{
1586
    this->signalActivatedObject(O);
1587
}
1588

1589
void Application::slotUndoDocument(const App::Document& d)
1590
{
1591
    this->signalUndoDocument(d);
1592
}
1593

1594
void Application::slotRedoDocument(const App::Document& d)
1595
{
1596
    this->signalRedoDocument(d);
1597
}
1598

1599
void Application::slotRecomputedObject(const DocumentObject& obj)
1600
{
1601
    this->signalObjectRecomputed(obj);
1602
}
1603

1604
void Application::slotRecomputed(const Document& doc)
1605
{
1606
    this->signalRecomputed(doc);
1607
}
1608

1609
void Application::slotBeforeRecompute(const Document& doc)
1610
{
1611
    this->signalBeforeRecomputeDocument(doc);
1612
}
1613

1614
void Application::slotOpenTransaction(const Document& d, string s)
1615
{
1616
    this->signalOpenTransaction(d, s);
1617
}
1618

1619
void Application::slotCommitTransaction(const Document& d)
1620
{
1621
    this->signalCommitTransaction(d);
1622
}
1623

1624
void Application::slotAbortTransaction(const Document& d)
1625
{
1626
    this->signalAbortTransaction(d);
1627
}
1628

1629
void Application::slotStartSaveDocument(const App::Document& doc, const std::string& filename)
1630
{
1631
    this->signalStartSaveDocument(doc, filename);
1632
}
1633

1634
void Application::slotFinishSaveDocument(const App::Document& doc, const std::string& filename)
1635
{
1636
    DocFileMap.clear();
1637
    this->signalFinishSaveDocument(doc, filename);
1638
}
1639

1640
void Application::slotChangePropertyEditor(const App::Document &doc, const App::Property &prop)
1641
{
1642
    this->signalChangePropertyEditor(doc,prop);
1643
}
1644

1645
//**************************************************************************
1646
// Init, Destruct and singleton
1647

1648
Application * Application::_pcSingleton = nullptr;
1649

1650
int Application::_argc;
1651
char ** Application::_argv;
1652

1653

1654
void Application::cleanupUnits()
1655
{
1656
    try {
1657
        Base::PyGILStateLocker lock;
1658
        Py::Module mod (Py::Module("FreeCAD").getAttr("Units").ptr());
1659

1660
        Py::List attr(mod.dir());
1661
        for (Py::List::iterator it = attr.begin(); it != attr.end(); ++it) {
1662
            mod.delAttr(Py::String(*it));
1663
        }
1664
    }
1665
    catch (Py::Exception& e) {
1666
        Base::PyGILStateLocker lock;
1667
        e.clear();
1668
    }
1669
}
1670

1671
void Application::destruct()
1672
{
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");
1681

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());
1690
            }
1691
        }
1692
    }
1693

1694
    paramMgr.clear();
1695
    _pcSysParamMngr = nullptr;
1696
    _pcUserParamMngr = nullptr;
1697

1698
#ifdef FC_DEBUG
1699
    // Do this only in debug mode for memory leak checkers
1700
    cleanupUnits();
1701
#endif
1702

1703
    // not initialized or double destruct!
1704
    assert(_pcSingleton);
1705
    delete _pcSingleton;
1706

1707
    // We must detach from console and delete the observer to save our file
1708
    destructObserver();
1709

1710
    Base::Interpreter().finalize();
1711

1712
    Base::ScriptFactorySingleton::Destruct();
1713
    Base::InterpreterSingleton::Destruct();
1714
    Base::Type::destruct();
1715
    ParameterManager::Terminate();
1716
}
1717

1718
void Application::destructObserver()
1719
{
1720
    if ( _pConsoleObserverFile ) {
1721
        Base::Console().DetachObserver(_pConsoleObserverFile);
1722
        delete _pConsoleObserverFile;
1723
        _pConsoleObserverFile = nullptr;
1724
    }
1725
    if ( _pConsoleObserverStd ) {
1726
        Base::Console().DetachObserver(_pConsoleObserverStd);
1727
        delete _pConsoleObserverStd;
1728
        _pConsoleObserverStd = nullptr;
1729
    }
1730
}
1731

1732
/** freecadNewHandler()
1733
 * prints an error message and throws an exception
1734
 */
1735
#ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler
1736
int __cdecl freecadNewHandler(size_t size )
1737
{
1738
    // throw an exception
1739
    throw Base::MemoryException();
1740
    return 0;
1741
}
1742
#else // Ansi C/C++ new handler
1743
static void freecadNewHandler ()
1744
{
1745
    // throw an exception
1746
    throw Base::MemoryException();
1747
}
1748
#endif
1749

1750
#if defined(FC_OS_LINUX)
1751
#include <execinfo.h>
1752
#include <dlfcn.h>
1753
#include <cxxabi.h>
1754

1755
#include <cstdio>
1756
#include <cstdlib>
1757
#include <string>
1758
#include <sstream>
1759

1760
#if HAVE_CONFIG_H
1761
#include <config.h>
1762
#endif // HAVE_CONFIG_H
1763

1764
// This function produces a stack backtrace with demangled function & method names.
1765
void printBacktrace(size_t skip=0)
1766
{
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);
1772

1773
    for (size_t i = skip; i < nFrames; i++) {
1774
        char *demangled = nullptr;
1775
        int status = -1;
1776
        Dl_info info;
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);
1780
            }
1781
        }
1782

1783
        std::stringstream str;
1784
        if (status == 0) {
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;
1787
            free(demangled);
1788
        }
1789
        else {
1790
            str << "#" << (i-skip) << "  " << symbols[i] << std::endl;
1791
        }
1792

1793
        // cannot directly print to cerr when using --write-log
1794
        std::cerr << str.str();
1795
    }
1796

1797
    free(symbols);
1798
#else //HAVE_BACKTRACE_SYMBOLS
1799
    (void)skip;
1800
    std::cerr << "Cannot print the stacktrace because the C runtime library doesn't provide backtrace or backtrace_symbols\n";
1801
#endif
1802
}
1803
#endif
1804

1805
void segmentation_fault_handler(int sig)
1806
{
1807
#if defined(FC_OS_LINUX)
1808
    (void)sig;
1809
    std::cerr << "Program received signal SIGSEGV, Segmentation fault.\n";
1810
    printBacktrace(2);
1811
#if defined(FC_DEBUG)
1812
    abort();
1813
#else
1814
    _exit(1);
1815
#endif
1816
#else
1817
    switch (sig) {
1818
        case SIGSEGV:
1819
            std::cerr << "Illegal storage access..." << std::endl;
1820
#if !defined(_DEBUG)
1821
            throw Base::AccessViolation("Illegal storage access! Please save your work under a new file name and restart the application!");
1822
#endif
1823
            break;
1824
        case SIGABRT:
1825
            std::cerr << "Abnormal program termination..." << std::endl;
1826
#if !defined(_DEBUG)
1827
            throw Base::AbnormalProgramTermination("Break signal occurred");
1828
#endif
1829
            break;
1830
        default:
1831
            std::cerr << "Unknown error occurred..." << std::endl;
1832
            break;
1833
    }
1834
#endif // FC_OS_LINUX
1835
}
1836

1837
void unhandled_exception_handler()
1838
{
1839
    std::cerr << "Terminating..." << std::endl;
1840
}
1841

1842
void unexpection_error_handler()
1843
{
1844
    std::cerr << "Unexpected error occurred..." << std::endl;
1845
    // try to throw an exception and give the user chance to save their work
1846
#if !defined(_DEBUG)
1847
    throw Base::AbnormalProgramTermination("Unexpected error occurred! Please save your work under a new file name and restart the application!");
1848
#else
1849
    terminate();
1850
#endif
1851
}
1852

1853
#if defined(FC_SE_TRANSLATOR) // Microsoft compiler
1854
void my_se_translator_filter(unsigned int code, EXCEPTION_POINTERS* pExp)
1855
{
1856
    Q_UNUSED(pExp)
1857
    switch (code)
1858
    {
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);
1865
        return;
1866
    }
1867

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());
1872
}
1873
#endif
1874

1875
void Application::init(int argc, char ** argv)
1876
{
1877
    try {
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
1884
#endif
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);
1894
#endif
1895
#if defined(FC_SE_TRANSLATOR)
1896
        _set_se_translator(my_se_translator_filter);
1897
#endif
1898
        initTypes();
1899

1900
        initConfig(argc,argv);
1901
        initApplication();
1902
    }
1903
    catch (...) {
1904
        // force the log to flush
1905
        destructObserver();
1906
        throw;
1907
    }
1908
}
1909

1910
void Application::initTypes()
1911
{
1912
    // Base types
1913
    Base::Type                      ::init();
1914
    Base::BaseClass                 ::init();
1915
    Base::Exception                 ::init();
1916
    Base::AbortException            ::init();
1917
    Base::Persistence               ::init();
1918

1919
    // Complex data classes
1920
    Data::ComplexGeoData            ::init();
1921
    Data::Segment                   ::init();
1922

1923
    // Properties
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();
2049

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();
2066

2067
    // Document classes
2068
    App::TransactionalObject       ::init();
2069
    App::DocumentObject            ::init();
2070
    App::GeoFeature                ::init();
2071

2072
    // Test features
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();
2080

2081
    // Feature class
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();
2101
    App::Line                      ::init();
2102
    App::Part                      ::init();
2103
    App::Origin                    ::init();
2104
    App::Link                      ::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();
2111

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();
2124

2125
    // Topological naming classes
2126
    App::StringHasher              ::init();
2127
    App::StringID                  ::init();
2128

2129
    // register transaction type
2130
    new App::TransactionProducer<TransactionDocumentObject>
2131
            (DocumentObject::getClassTypeId());
2132

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>;
2165
}
2166

2167
namespace {
2168

2169
void parseProgramOptions(int ac, char ** av, const string& exe, variables_map& vm)
2170
{
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")
2184
    ;
2185

2186
    // Declare a group of options that will be
2187
    // allowed both on the command line and in
2188
    // the config file
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")
2203
    ;
2204

2205

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")
2237
#endif
2238
    ;
2239

2240
    // Ignored options, will be safely ignored. Mostly used by underlying libs.
2241
    //boost::program_options::options_description x11("X11 options");
2242
    //x11.add_options()
2243
    //    ("display",  boost::program_options::value< string >(), "set the X-Server")
2244
    //    ;
2245
    //0000723: improper handling of qt specific command line arguments
2246
    std::vector<std::string> args;
2247
    bool merge=false;
2248
    for (int i=1; i<ac; i++) {
2249
        if (merge) {
2250
            merge = false;
2251
            args.back() += "=";
2252
            args.back() += av[i];
2253
        }
2254
        else {
2255
            args.emplace_back(av[i]);
2256
        }
2257
        if (strcmp(av[i],"-style") == 0) {
2258
            merge = true;
2259
        }
2260
        else if (strcmp(av[i],"-stylesheet") == 0) {
2261
            merge = true;
2262
        }
2263
        else if (strcmp(av[i],"-session") == 0) {
2264
            merge = true;
2265
        }
2266
        else if (strcmp(av[i],"-graphicssystem") == 0) {
2267
            merge = true;
2268
        }
2269
    }
2270

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);
2275

2276
    boost::program_options::options_description config_file_options("Config");
2277
    config_file_options.add(config).add(hidden);
2278

2279
    boost::program_options::options_description visible("Allowed options");
2280
    visible.add(generic).add(config);
2281

2282
    boost::program_options::positional_options_description p;
2283
    p.add("input-file", -1);
2284

2285
    try {
2286
        store( boost::program_options::command_line_parser(args).
2287
               options(cmdline_options).positional(p).extra_parser(Util::customSyntax).run(), vm);
2288

2289
        std::ifstream ifs("FreeCAD.cfg");
2290
        if (ifs)
2291
            store(parse_config_file(ifs, config_file_options), vm);
2292
        notify(vm);
2293
    }
2294
    catch (const std::exception& e) {
2295
        std::stringstream str;
2296
        str << e.what() << endl << endl << visible << endl;
2297
        throw Base::UnknownProgramOption(str.str());
2298
    }
2299
    catch (...) {
2300
        std::stringstream str;
2301
        str << "Wrong or unknown option, bailing out!" << endl << endl << visible << endl;
2302
        throw Base::UnknownProgramOption(str.str());
2303
    }
2304

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());
2312
    }
2313

2314
    if (vm.count("response-file")) {
2315
        // Load the file and tokenize it
2316
        std::ifstream ifs(vm["response-file"].as<string>().c_str());
2317
        if (!ifs) {
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());
2323
        }
2324
        // Read the whole file into a string
2325
        stringstream ss;
2326
        ss << ifs.rdbuf();
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);
2335
    }
2336
}
2337

2338
void processProgramOptions(const variables_map& vm, std::map<std::string,std::string>& mConfig)
2339
{
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';
2351
#endif
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';
2358
#endif
2359
            str << "VTK      " << fcVtkVersion << '\n';
2360
            str << "xerces-c " << fcXercescVersion << '\n';
2361
        }
2362
        throw Base::ProgramInformation(str.str());
2363
    }
2364

2365
    if (vm.count("console")) {
2366
        mConfig["Console"] = "1";
2367
        mConfig["RunMode"] = "Cmd";
2368
    }
2369

2370
    if (vm.count("module-path")) {
2371
        vector<string> Mods = vm["module-path"].as< vector<string> >();
2372
        string temp;
2373
        for (const auto & It : Mods)
2374
            temp += It + ";";
2375
        temp.erase(temp.end()-1);
2376
        mConfig["AdditionalModulePaths"] = temp;
2377
    }
2378

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());
2383
    }
2384

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) {
2389

2390
            //cout << "Input files are: "
2391
            //     << vm["input-file"].as< vector<string> >() << "\n";
2392

2393
            std::ostringstream temp;
2394
            temp << "OpenFile" << OpenFileCount;
2395
            mConfig[temp.str()] = It;
2396
            OpenFileCount++;
2397
        }
2398
        std::ostringstream buffer;
2399
        buffer << OpenFileCount;
2400
        mConfig["OpenFileCount"] = buffer.str();
2401
    }
2402

2403
    if (vm.count("output")) {
2404
        string file = vm["output"].as<string>();
2405
        mConfig["SaveFile"] = file;
2406
    }
2407

2408
    if (vm.count("hidden")) {
2409
        mConfig["StartHidden"] = "1";
2410
    }
2411

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";
2416
    }
2417

2418
    if (vm.count("log-file")) {
2419
        mConfig["LoggingFile"] = "1";
2420
        mConfig["LoggingFileName"] = vm["log-file"].as<string>();
2421
    }
2422

2423
    if (vm.count("user-cfg")) {
2424
        mConfig["UserParameter"] = vm["user-cfg"].as<string>();
2425
    }
2426

2427
    if (vm.count("system-cfg")) {
2428
        mConfig["SystemParameter"] = vm["system-cfg"].as<string>();
2429
    }
2430

2431
    if (vm.count("run-test")) {
2432
        string testCase = vm["run-test"].as<string>();
2433
        if ( "0" == testCase) {
2434
            testCase = "TestApp.All";
2435
        }
2436
        else if (testCase.empty()) {
2437
            testCase = "TestApp.PrintAll";
2438
        }
2439
        mConfig["TestCase"] = testCase;
2440
        mConfig["RunMode"] = "Internal";
2441
        mConfig["ScriptFileName"] = "FreeCADTest";
2442
        //sScriptName = FreeCADTest;
2443
    }
2444

2445
    if (vm.count("single-instance")) {
2446
        mConfig["SingleInstance"] = "1";
2447
    }
2448

2449
    if (vm.count("dump-config")) {
2450
        std::stringstream str;
2451
        for (const auto & it : mConfig) {
2452
            str << it.first << "=" << it.second << std::endl;
2453
        }
2454
        throw Base::ProgramInformation(str.str());
2455
    }
2456

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()) {
2463
            str << pos->second;
2464
        }
2465
        str << std::endl;
2466
        throw Base::ProgramInformation(str.str());
2467
    }
2468

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);
2476
                mConfig[key] = val;
2477
            }
2478
        }
2479
    }
2480
}
2481
}
2482

2483
void Application::initConfig(int argc, char ** argv)
2484
{
2485
    // find the home path....
2486
    mConfig["AppHomePath"] = FindHomePath(argv[0]);
2487

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;
2506
#endif
2507
#if defined(FCRepositoryBranch)
2508
        App::Application::Config()["BuildRevisionBranch"] = FCRepositoryBranch;
2509
#endif
2510
    }
2511

2512
    _argc = argc;
2513
    _argv = argv;
2514

2515
    // Now it's time to read-in the file branding.xml if it exists
2516
    Branding brand;
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();
2523
        }
2524
    }
2525

2526
    variables_map vm;
2527
    parseProgramOptions(argc, argv, mConfig["ExeName"], vm);
2528

2529
    if (vm.count("keep-deprecated-paths")) {
2530
        mConfig["KeepDeprecatedPaths"] = "1";
2531
    }
2532

2533
    // extract home paths
2534
    ExtractUserPath();
2535

2536
#   ifdef FC_DEBUG
2537
    mConfig["Debug"] = "1";
2538
#   else
2539
    mConfig["Debug"] = "0";
2540
#   endif
2541

2542
    // init python
2543
    PyImport_AppendInittab ("FreeCAD", init_freecad_module);
2544
    PyImport_AppendInittab ("__FreeCADBase__", init_freecad_base_module);
2545
    const char* pythonpath = Base::Interpreter().init(argc,argv);
2546
    if (pythonpath)
2547
        mConfig["PythonSearchPath"] = pythonpath;
2548
    else
2549
        Base::Console().Warning("Encoding of Python paths failed\n");
2550

2551
    // Handle the options that have impact on the init process
2552
    processProgramOptions(vm, mConfig);
2553

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;
2563
    }
2564
    if (mConfig["Verbose"] == "Strict")
2565
        Base::Console().UnsetConsoleMode(Base::ConsoleSingleton::Verbose);
2566

2567
    // file logging Init ===========================================================
2568
    if (mConfig["LoggingFile"] == "1") {
2569
        _pConsoleObserverFile = new Base::ConsoleObserverFile(mConfig["LoggingFileName"].c_str());
2570
        Base::Console().AttachObserver(_pConsoleObserverFile);
2571
    }
2572
    else
2573
        _pConsoleObserverFile = nullptr;
2574

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());
2589
        else
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());
2598
    }
2599
    LoadParameters();
2600

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") {
2606
#ifndef FC_DEBUG
2607
            if (v.second>=0) {
2608
                hasDefault = true;
2609
                Base::Console().SetDefaultLogLevel(v.second);
2610
            }
2611
#endif
2612
        }
2613
        else if (v.first == "DebugDefault") {
2614
#ifdef FC_DEBUG
2615
            if (v.second>=0) {
2616
                hasDefault = true;
2617
                Base::Console().SetDefaultLogLevel(v.second);
2618
            }
2619
#endif
2620
        }
2621
        else {
2622
            *Base::Console().GetLogLevel(v.first.c_str()) = v.second;
2623
        }
2624
    }
2625

2626
    if (!hasDefault) {
2627
#ifdef FC_DEBUG
2628
        loglevelParam->SetInt("DebugDefault", Base::Console().LogLevel(-1));
2629
#else
2630
        loglevelParam->SetInt("Default", Base::Console().LogLevel(-1));
2631
#endif
2632
    }
2633

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;
2639
    }
2640

2641

2642
    // capture python variables
2643
    SaveEnv("PYTHONPATH");
2644
    SaveEnv("PYTHONHOME");
2645
    SaveEnv("TCL_LIBRARY");
2646
    SaveEnv("TCLLIBPATH");
2647

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");
2661

2662
    // capture path
2663
    SaveEnv("PATH");
2664

2665
    // Save version numbers of the libraries
2666
#ifdef OCC_VERSION_STRING_EXT
2667
    mConfig["OCC_VERSION"] = OCC_VERSION_STRING_EXT;
2668
#endif
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;
2676
#endif
2677
    mConfig["XERCESC_VERSION"] = fcXercescVersion;
2678

2679

2680
    logStatus();
2681
}
2682

2683
void Application::SaveEnv(const char* s)
2684
{
2685
    char *c = getenv(s);
2686
    if (c)
2687
        mConfig[s] = c;
2688
}
2689

2690
void Application::initApplication()
2691
{
2692
    // interpreter and Init script ==========================================================
2693
    // register scripts
2694
    new Base::ScriptProducer( "CMakeVariables", CMakeVariables );
2695
    new Base::ScriptProducer( "FreeCADInit",    FreeCADInit    );
2696
    new Base::ScriptProducer( "FreeCADTest",    FreeCADTest    );
2697

2698
    // creating the application
2699
    if (!(mConfig["Verbose"] == "Strict"))
2700
        Base::Console().Log("Create Application\n");
2701
    Application::_pcSingleton = new Application(mConfig);
2702

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()));
2708

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);
2712

2713

2714
#if defined (_DEBUG)
2715
    Base::Console().Log("Application is built with debug information\n");
2716
#endif
2717

2718
    // starting the init script
2719
    Base::Console().Log("Run App init script\n");
2720
    try {
2721
        Base::Interpreter().runString(Base::ScriptFactory().ProduceScript("CMakeVariables"));
2722
        Base::Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit"));
2723
    }
2724
    catch (const Base::Exception& e) {
2725
        e.ReportException();
2726
    }
2727

2728
    // seed randomizer
2729
    srand(time(nullptr));
2730
}
2731

2732
std::list<std::string> Application::getCmdLineFiles()
2733
{
2734
    std::list<std::string> files;
2735

2736
    // cycling through all the open files
2737
    unsigned short count = 0;
2738
    count = atoi(mConfig["OpenFileCount"].c_str());
2739
    std::string File;
2740

2741
    for (unsigned short i=0; i<count; i++) {
2742
        // getting file name
2743
        std::ostringstream temp;
2744
        temp << "OpenFile" << i;
2745

2746
        std::string file(mConfig[temp.str()]);
2747
        files.push_back(file);
2748
    }
2749

2750
    return files;
2751
}
2752

2753
std::list<std::string> Application::processFiles(const std::list<std::string>& files)
2754
{
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);
2759

2760
        Base::Console().Log("Init:     Processing file: %s\n",file.filePath().c_str());
2761

2762
        try {
2763
            if (file.hasExtension("fcstd") || file.hasExtension("std")) {
2764
                // try to open
2765
                Application::_pcSingleton->openDocument(file.filePath().c_str());
2766
                processed.push_back(it);
2767
            }
2768
            else if (file.hasExtension("fcscript") || file.hasExtension("fcmacro")) {
2769
                Base::Interpreter().runFile(file.filePath().c_str(), true);
2770
                processed.push_back(it);
2771
            }
2772
            else if (file.hasExtension("py")) {
2773
                try {
2774
                    Base::Interpreter().addPythonPath(file.dirPath().c_str());
2775
                    Base::Interpreter().loadModule(file.fileNamePure().c_str());
2776
                    processed.push_back(it);
2777
                }
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);
2782
                }
2783
            }
2784
            else {
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);
2790

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());
2797
                }
2798
                else if (file.exists()) {
2799
                    Base::Console().Warning("File format not supported: %s \n", file.filePath().c_str());
2800
                }
2801
            }
2802
        }
2803
        catch (const Base::SystemExitException&) {
2804
            throw; // re-throw to main() function
2805
        }
2806
        catch (const Base::Exception& e) {
2807
            Base::Console().Error("Exception while processing file: %s [%s]\n", file.filePath().c_str(), e.what());
2808
        }
2809
        catch (...) {
2810
            Base::Console().Error("Unknown exception while processing file: %s \n", file.filePath().c_str());
2811
        }
2812
    }
2813

2814
    return processed; // successfully processed files
2815
}
2816

2817
void Application::processCmdLineFiles()
2818
{
2819
    // process files passed to command line
2820
    std::list<std::string> files = getCmdLineFiles();
2821
    std::list<std::string> processed = processFiles(files);
2822

2823
    if (files.empty()) {
2824
        if (mConfig["RunMode"] == "Exit")
2825
            mConfig["RunMode"] = "Cmd";
2826
    }
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";
2834
        }
2835
    }
2836

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);
2842

2843
        Base::FileInfo fi(output);
2844
        std::string ext = fi.extension();
2845
        try {
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());
2852
            }
2853
            else {
2854
                Base::Console().Warning("File format not supported: %s \n", output.c_str());
2855
            }
2856
        }
2857
        catch (const Base::Exception& e) {
2858
            Base::Console().Error("Exception while saving to file: %s [%s]\n", output.c_str(), e.what());
2859
        }
2860
        catch (...) {
2861
            Base::Console().Error("Unknown exception while saving to file: %s \n", output.c_str());
2862
        }
2863
    }
2864
}
2865

2866
void Application::runApplication()
2867
{
2868
    // process all files given through command line interface
2869
    processCmdLineFiles();
2870

2871
    if (mConfig["RunMode"] == "Cmd") {
2872
        // Run the commandline interface
2873
        Base::Interpreter().runCommandLine("FreeCAD Console mode");
2874
    }
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()));
2879
    }
2880
    else if (mConfig["RunMode"] == "Exit") {
2881
        // getting out
2882
        Base::Console().Log("Exiting on purpose\n");
2883
    }
2884
    else {
2885
        Base::Console().Log("Unknown Run mode (%d) in main()?!?\n\n", mConfig["RunMode"].c_str());
2886
    }
2887
}
2888

2889
void Application::logStatus()
2890
{
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());
2894

2895
    for (const auto & It : mConfig) {
2896
        Base::Console().Log("%s = %s\n", It.first.c_str(), It.second.c_str());
2897
    }
2898
}
2899

2900
void Application::LoadParameters()
2901
{
2902
    // Init parameter sets ===========================================================
2903
    //
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";
2908

2909
    // create standard parameter sets
2910
    _pcSysParamMngr = ParameterManager::Create();
2911
    _pcSysParamMngr->SetSerializer(new ParameterSerializer(mConfig["SystemParameter"]));
2912

2913
    _pcUserParamMngr = ParameterManager::Create();
2914
    _pcUserParamMngr->SetSerializer(new ParameterSerializer(mConfig["UserParameter"]));
2915

2916
    try {
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");
2924
            }
2925
        }
2926
    }
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();
2933
    }
2934

2935
    try {
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();
2945
                }
2946
                QFileInfo fi(path);
2947
                if (fi.exists()) {
2948
                    _pcUserParamMngr->LoadDocument(path.toUtf8().constData());
2949
                }
2950
            }
2951

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");
2958
            }
2959
        }
2960
    }
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();
2967
    }
2968
}
2969

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;
2976
    } }
2977
#endif
2978

2979
// A helper function to simplify the main part.
2980
template<class T>
2981
ostream& operator<<(ostream& os, const vector<T>& v)
2982
{
2983
    copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
2984
    return os;
2985
}
2986

2987
namespace {
2988

2989
/*!
2990
 * \brief getUserHome
2991
 * Returns the user's home directory.
2992
 */
2993
QString getUserHome()
2994
{
2995
    QString path;
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
2998
    struct passwd pwd;
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);
3003
    Q_UNUSED(error)
3004
    if (!result)
3005
        throw Base::RuntimeError("Getting HOME path from system failed!");
3006
    path = QString::fromUtf8(result->pw_dir);
3007
#else
3008
    path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
3009
#endif
3010

3011
    return path;
3012
}
3013

3014
/*!
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.
3018
 */
3019
#if defined(FC_OS_WIN32)
3020
QString getOldGenericDataLocation(QString home)
3021
{
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));
3027
    }
3028
#elif defined(FC_OS_MACOSX)
3029
    QFileInfo fi(home, QString::fromLatin1("Library/Preferences"));
3030
    home = fi.absoluteFilePath();
3031
#endif
3032

3033
    return home;
3034
}
3035
#endif
3036

3037
/*!
3038
 * \brief getSubDirectories
3039
 * To a given path it adds the sub-directories where to store application specific files.
3040
 */
3041
void getSubDirectories(std::map<std::string,std::string>& mConfig, std::vector<std::string>& appData)
3042
{
3043
    // If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of
3044
    // the path.
3045
    if (mConfig.find("AppDataSkipVendor") == mConfig.end()) {
3046
        appData.push_back(mConfig["ExeVendor"]);
3047
    }
3048
    appData.push_back(mConfig["ExeName"]);
3049
}
3050

3051
/*!
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.
3055
 */
3056
void getOldDataLocation(std::map<std::string,std::string>& mConfig, std::vector<std::string>& appData)
3057
{
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
3062
    // the path.
3063
    if (mConfig.find("AppDataSkipVendor") == mConfig.end()) {
3064
        appData.push_back(std::string(".") + mConfig["ExeVendor"]);
3065
        appData.push_back(mConfig["ExeName"]);
3066
    } else {
3067
        appData.push_back(std::string(".") + mConfig["ExeName"]);
3068
    }
3069

3070
#elif defined(FC_OS_MACOSX) || defined(FC_OS_WIN32)
3071
    getSubDirectories(mConfig, appData);
3072
#endif
3073
}
3074

3075
/*!
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.
3079
 */
3080
QString findUserHomePath(const QString& userHome)
3081
{
3082
    if (userHome.isEmpty()) {
3083
        return getUserHome();
3084
    }
3085
    else {
3086
        return userHome;
3087
    }
3088
}
3089

3090
/*!
3091
 * \brief findPath
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.
3094
 */
3095
boost::filesystem::path findPath(const QString& stdHome, const QString& customHome,
3096
                                 const std::vector<std::string>& paths, bool create)
3097
{
3098
    QString dataPath = customHome;
3099
    if (dataPath.isEmpty()) {
3100
        dataPath = stdHome;
3101
    }
3102

3103
    boost::filesystem::path appData(Base::FileInfo::stringToPath(dataPath.toStdString()));
3104

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;
3109
    }
3110

3111
    // In order to write to our data path, we must create some directories, first.
3112
    if (create && !boost::filesystem::exists(appData) && !Py_IsInitialized()) {
3113
        try {
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());
3117
        }
3118
    }
3119

3120
    return appData;
3121
}
3122

3123
/*!
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:
3127
 * \list
3128
 * \li FREECAD_USER_HOME
3129
 * \li FREECAD_USER_DATA
3130
 * \li FREECAD_USER_TEMP
3131
 * \endlist
3132
 */
3133
std::tuple<QString, QString, QString> getCustomPaths()
3134
{
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"));
3139

3140
    auto toNativePath = [](QString& path) {
3141
        if (!path.isEmpty()) {
3142
            QDir dir(path);
3143
            if (dir.exists())
3144
                path = QDir::toNativeSeparators(dir.canonicalPath());
3145
            else
3146
                path.clear();
3147
        }
3148
    };
3149

3150
    // verify env. variables
3151
    toNativePath(userHome);
3152
    toNativePath(userData);
3153
    toNativePath(userTemp);
3154

3155
    // if FREECAD_USER_HOME is set but not FREECAD_USER_DATA
3156
    if (!userHome.isEmpty() && userData.isEmpty()) {
3157
        userData = userHome;
3158
    }
3159

3160
    // if FREECAD_USER_HOME is set but not FREECAD_USER_TEMP
3161
    if (!userHome.isEmpty() && userTemp.isEmpty()) {
3162
        QDir dir(userHome);
3163
        dir.mkdir(QString::fromLatin1("temp"));
3164
        QFileInfo fi(dir, QString::fromLatin1("temp"));
3165
        userTemp = fi.absoluteFilePath();
3166
    }
3167

3168
    return {userHome, userData, userTemp};
3169
}
3170

3171
/*!
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:
3175
 * \list
3176
 * \li XDG_CONFIG_HOME
3177
 * \li XDG_DATA_HOME
3178
 * \li XDG_CACHE_HOME
3179
 * \endlist
3180
 */
3181
std::tuple<QString, QString, QString, QString> getStandardPaths()
3182
{
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);
3187

3188
    // Keep the old behaviour
3189
#if defined(FC_OS_WIN32)
3190
    configHome = getOldGenericDataLocation(QString());
3191
    dataHome = configHome;
3192

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;
3200
#endif
3201
#endif
3202

3203
    return std::make_tuple(configHome, dataHome, cacheHome, tempPath);
3204
}
3205
}
3206

3207
void Application::ExtractUserPath()
3208
{
3209
    bool keepDeprecatedPaths = mConfig.count("KeepDeprecatedPaths") > 0;
3210

3211
    // std paths
3212
    mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP;
3213
    mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP;
3214

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);
3220

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);
3227

3228
    // User home path
3229
    //
3230
    QString homePath = findUserHomePath(customHome);
3231
    mConfig["UserHomePath"] = homePath.toUtf8().data();
3232

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);
3240
    }
3241
    else {
3242
        getSubDirectories(mConfig, subdirs);
3243
    }
3244

3245
    // User data path
3246
    //
3247
    boost::filesystem::path data = findPath(dataHome, customData, subdirs, true);
3248
    mConfig["UserAppData"] = Base::FileInfo::pathToString(data) + PATHSEP;
3249

3250

3251
    // User config path
3252
    //
3253
    boost::filesystem::path config = findPath(configHome, customHome, subdirs, true);
3254
    mConfig["UserConfigPath"] = Base::FileInfo::pathToString(config) + PATHSEP;
3255

3256

3257
    // User cache path
3258
    //
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;
3263

3264

3265
    // Set application tmp. directory
3266
    //
3267
    std::vector<std::string> empty;
3268
    boost::filesystem::path tmp = findPath(tempPath, customTemp, empty, true);
3269
    mConfig["AppTempPath"] = Base::FileInfo::pathToString(tmp) + PATHSEP;
3270

3271

3272
    // Set the default macro directory
3273
    //
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;
3278
}
3279

3280
#if defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD)
3281
#include <cstdio>
3282
#include <cstdlib>
3283
#include <sys/param.h>
3284

3285
std::string Application::FindHomePath(const char* sCall)
3286
{
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);
3299
        if (path)
3300
            absPath = path;
3301
    }
3302
    else {
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)
3310
        int mib[4];
3311
        mib[0] = CTL_KERN;
3312
        mib[1] = KERN_PROC;
3313
        mib[2] = KERN_PROC_PATHNAME;
3314
        mib[3] = -1;
3315
        size_t cb = sizeof(resolved);
3316
        sysctl(mib, 4, resolved, &cb, NULL, 0);
3317
        int nchars = strlen(resolved);
3318
#else
3319
        int nchars = readlink("/proc/self/exe", resolved, PATH_MAX);
3320
#endif
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
3324
        absPath = resolved;
3325
    }
3326

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);
3332

3333
    return homePath;
3334
}
3335

3336
#elif defined(FC_OS_MACOSX)
3337
#include <mach-o/dyld.h>
3338
#include <string>
3339
#include <cstdlib>
3340
#include <sys/param.h>
3341

3342
std::string Application::FindHomePath(const char* call)
3343
{
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()) {
3348
        uint32_t sz = 0;
3349
        char *buf;
3350

3351
        _NSGetExecutablePath(NULL, &sz); //function only returns "sz" if first arg is to small to hold value
3352
        buf = new char[++sz];
3353

3354
        if (_NSGetExecutablePath(buf, &sz) == 0) {
3355
            char resolved[PATH_MAX];
3356
            char* path = realpath(buf, resolved);
3357
            delete [] buf;
3358

3359
            if (path) {
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;
3366
            }
3367
        } else {
3368
            delete [] buf;
3369
        }
3370
    }
3371

3372
    return call;
3373
}
3374

3375
#elif defined (FC_OS_WIN32)
3376
std::string Application::FindHomePath(const char* sCall)
3377
{
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);
3389
    }
3390
    else {
3391
        GetModuleFileNameW(0, szFileName, MAX_PATH-1);
3392
    }
3393

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);
3399

3400
    // fixes #0001638 to avoid to load DLLs from Windows' system directories before FreeCAD's bin folder
3401
    std::wstring binPath = homePath;
3402
    binPath += L"bin";
3403
    SetDllDirectoryW(binPath.c_str());
3404

3405
    // https://stackoverflow.com/questions/5625884/conversion-of-stdwstring-to-qstring-throws-linker-error
3406
#ifdef _MSC_VER
3407
    QString str = QString::fromUtf16(reinterpret_cast<const ushort *>(homePath.c_str()));
3408
#else
3409
    QString str = QString::fromStdWString(homePath);
3410
#endif
3411
    // convert to utf-8
3412
    return str.toUtf8().data();
3413
}
3414

3415
#else
3416
# error "std::string Application::FindHomePath(const char*) not implemented"
3417
#endif
3418

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.