App/Application.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   (c) Juergen Riegel (juergen.riegel@web.de) 2002                       *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This program is free software; you can redistribute it and/or modify  *
00007  *   it under the terms of the GNU Library General Public License (LGPL)   *
00008  *   as published by the Free Software Foundation; either version 2 of     *
00009  *   the License, or (at your option) any later version.                   *
00010  *   for detail see the LICENCE text file.                                 *
00011  *                                                                         *
00012  *   FreeCAD is distributed in the hope that it will be useful,            *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU Library General Public License for more details.                  *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU Library General Public     *
00018  *   License along with FreeCAD; if not, write to the Free Software        *
00019  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
00020  *   USA                                                                   *
00021  *                                                                         *
00022  *   Juergen Riegel 2002                                                   *
00023  ***************************************************************************/
00024 
00025 
00026 
00027 #include "PreCompiled.h"
00028 
00029 #ifndef _PreComp_
00030 # include <iostream>
00031 # include <sstream>
00032 # include <exception>
00033 # if defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD)
00034 # include <unistd.h>
00035 # include <pwd.h>
00036 # include <sys/types.h>
00037 # endif
00038 # include <ctime>
00039 # include <csignal>
00040 # include <boost/program_options.hpp>
00041 #endif
00042 
00043 #ifdef FC_OS_WIN32
00044 # include <Shlobj.h>
00045 // Doesn't seem to work with VS2010
00046 # if (defined(_MSC_VER) && (_MSC_VER < 1800))
00047 # include <Shfolder.h>
00048 # endif
00049 #endif
00050 
00051 
00052 
00053 #include "Application.h"
00054 #include "Document.h"
00055 
00056 // FreeCAD Base header
00057 #include <Base/Interpreter.h>
00058 #include <Base/Exception.h>
00059 #include <Base/Parameter.h>
00060 #include <Base/Console.h>
00061 #include <Base/Factory.h>
00062 #include <Base/FileInfo.h>
00063 #include <Base/Type.h>
00064 #include <Base/BaseClass.h>
00065 #include <Base/Persistence.h>
00066 #include <Base/Reader.h>
00067 #include <Base/MatrixPy.h>
00068 #include <Base/VectorPy.h>
00069 #include <Base/BoundBoxPy.h>
00070 #include <Base/PlacementPy.h>
00071 #include <Base/RotationPy.h>
00072 #include <Base/Sequencer.h>
00073 #include <Base/Tools.h>
00074 #include <Base/UnitsApi.h>
00075 
00076 #include "GeoFeature.h"
00077 #include "FeatureTest.h"
00078 #include "FeaturePython.h"
00079 #include "ComplexGeoData.h"
00080 #include "Property.h"
00081 #include "PropertyContainer.h"
00082 #include "PropertyUnits.h"
00083 #include "PropertyFile.h"
00084 #include "PropertyLinks.h"
00085 #include "PropertyPythonObject.h"
00086 #include "Document.h"
00087 #include "DocumentObjectGroup.h"
00088 #include "DocumentObjectFileIncluded.h"
00089 #include "InventorObject.h"
00090 #include "VRMLObject.h"
00091 #include "Annotation.h"
00092 #include "MeasureDistance.h"
00093 
00094 // If you stumble here, run the target "BuildExtractRevision" on Windows systems
00095 // or the Python script "SubWCRev.py" on Linux based systems which builds
00096 // src/Build/Version.h. Or create your own from src/Build/Version.h.in!
00097 #include <Build/Version.h>
00098 
00099 #include <boost/tokenizer.hpp>
00100 #include <boost/token_functions.hpp>
00101 #include <boost/signals.hpp>
00102 #include <boost/bind.hpp>
00103 #include <boost/version.hpp>
00104 
00105 using namespace App;
00106 using namespace std;
00107 using namespace boost;
00108 using namespace boost::program_options;
00109 
00110 
00111 // scriptings (scripts are build in but can be overridden by command line option)
00112 #include "InitScript.h"
00113 #include "TestScript.h"
00114 
00115 #ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler
00116 # include <new.h>
00117 #else // Ansi C/C++ new handler
00118 # include <new>
00119 #endif
00120 
00121 #ifdef MemDebugOn
00122 # define new DEBUG_CLIENTBLOCK
00123 #endif
00124 
00125 
00126 //using Base::GetConsole;
00127 using namespace Base;
00128 using namespace App;
00129 using namespace std;
00130 
00131 
00132 //==========================================================================
00133 // Application
00134 //==========================================================================
00135 
00136 ParameterManager *App::Application::_pcSysParamMngr;
00137 ParameterManager *App::Application::_pcUserParamMngr;
00138 Base::ConsoleObserverStd  *Application::_pConsoleObserverStd =0;
00139 Base::ConsoleObserverFile *Application::_pConsoleObserverFile =0;
00140 
00141 AppExport std::map<std::string,std::string> Application::mConfig;
00142 
00143 
00144 //**************************************************************************
00145 // Construction and destruction
00146 
00147 PyDoc_STRVAR(FreeCAD_doc,
00148      "The functions in the FreeCAD module allow working with documents.\n"
00149      "The FreeCAD instance provides a list of references of documents which\n"
00150      "can be addressed by a string. Hence the document name must be unique.\n"
00151      "\n"
00152      "The document has the read-only attribute FileName which points to the\n"
00153      "file the document should be stored to.\n"
00154     );
00155 
00156 PyDoc_STRVAR(Console_doc,
00157      "FreeCAD Console\n"
00158     );
00159 
00160 Application::Application(ParameterManager * /*pcSysParamMngr*/, 
00161                          ParameterManager * /*pcUserParamMngr*/,
00162                          std::map<std::string,std::string> &mConfig)
00163     ://_pcSysParamMngr(pcSysParamMngr),
00164     //_pcUserParamMngr(pcUserParamMngr),
00165     _mConfig(mConfig),
00166     _pActiveDoc(0)
00167 {
00168     //_hApp = new ApplicationOCC;
00169     mpcPramManager["System parameter"] = _pcSysParamMngr;
00170     mpcPramManager["User parameter"] = _pcUserParamMngr;
00171 
00172 
00173     // setting up Python binding
00174     Base::PyGILStateLocker lock;
00175     PyObject* pAppModule = Py_InitModule3("FreeCAD", Application::Methods, FreeCAD_doc);
00176     Py::Module(pAppModule).setAttr(std::string("ActiveDocument"),Py::None());
00177 
00178     PyObject* pConsoleModule = Py_InitModule3("__FreeCADConsole__", ConsoleSingleton::Methods, Console_doc);
00179 
00180     // introducing additional classes
00181 
00182     // NOTE: To finish the initialization of our own type objects we must
00183     // call PyType_Ready, otherwise we run into a segmentation fault, later on.
00184     // This function is responsible for adding inherited slots from a type's base class.
00185     if (PyType_Ready(&Base::VectorPy::Type) < 0) return;
00186     union PyType_Object pyVecType = {&Base::VectorPy::Type};
00187     PyModule_AddObject(pAppModule, "Vector", pyVecType.o);
00188 
00189     if (PyType_Ready(&Base::MatrixPy::Type) < 0) return;
00190     union PyType_Object pyMtxType = {&Base::MatrixPy::Type};
00191     PyModule_AddObject(pAppModule, "Matrix", pyMtxType.o);
00192 
00193     if (PyType_Ready(&Base::BoundBoxPy::Type) < 0) return;
00194     union PyType_Object pyBoundBoxType = {&Base::BoundBoxPy::Type};
00195     PyModule_AddObject(pAppModule, "BoundBox", pyBoundBoxType.o);
00196 
00197     if (PyType_Ready(&Base::PlacementPy::Type) < 0) return;
00198     union PyType_Object pyPlacementPyType = {&Base::PlacementPy::Type};
00199     PyModule_AddObject(pAppModule, "Placement", pyPlacementPyType.o);
00200 
00201     if (PyType_Ready(&Base::RotationPy::Type) < 0) return;
00202     union PyType_Object pyRotationPyType = {&Base::RotationPy::Type};
00203     PyModule_AddObject(pAppModule, "Rotation", pyRotationPyType.o);
00204 
00205     // Note: Create an own module 'Base' which should provide the python
00206     // binding classes from the base module. At a later stage we should 
00207     // remove these types from the FreeCAD module.
00208     PyObject* pBaseModule = Py_InitModule3("__FreeCADBase__", NULL,
00209         "The Base module contains the classes for the geometric basics\n"
00210         "like vector, matrix, bounding box, placement, rotation, ...");
00211     Base::Interpreter().addType(&Base::VectorPy     ::Type,pBaseModule,"Vector");
00212     Base::Interpreter().addType(&Base::MatrixPy     ::Type,pBaseModule,"Matrix");
00213     Base::Interpreter().addType(&Base::BoundBoxPy   ::Type,pBaseModule,"BoundBox");
00214     Base::Interpreter().addType(&Base::PlacementPy  ::Type,pBaseModule,"Placement");
00215     Base::Interpreter().addType(&Base::RotationPy   ::Type,pBaseModule,"Rotation");
00216 
00217     //insert Base and Console
00218     Py_INCREF(pBaseModule);
00219     PyModule_AddObject(pAppModule, "Base", pBaseModule);
00220     Py_INCREF(pConsoleModule);
00221     PyModule_AddObject(pAppModule, "Console", pConsoleModule);
00222 
00223     //insert Units module
00224     PyObject* pUnitsModule = Py_InitModule3("Units", Base::UnitsApi::Methods,
00225           "The Unit API");
00226     Py_INCREF(pUnitsModule);
00227     PyModule_AddObject(pAppModule, "Units", pUnitsModule);
00228 
00229     Base::ProgressIndicatorPy::init_type();
00230     Base::Interpreter().addType(Base::ProgressIndicatorPy::type_object(),
00231         pBaseModule,"ProgressIndicator");
00232 }
00233 
00234 Application::~Application()
00235 {
00236 }
00237 
00238 //**************************************************************************
00239 // Interface
00240 
00242 void Application::renameDocument(const char *OldName, const char *NewName)
00243 {
00244     std::map<std::string,Document*>::iterator pos;
00245     pos = DocMap.find(OldName);
00246 
00247     if (pos != DocMap.end()) {
00248         Document* temp;
00249         temp = pos->second;
00250         DocMap.erase(pos);
00251         DocMap[NewName] = temp;
00252         signalRenameDocument(*temp);
00253     }
00254     else
00255         Base::Exception("Application::renameDocument(): no document with this name to rename!");
00256 
00257 }
00258 
00259 Document* Application::newDocument(const char * Name, const char * UserName)
00260 {
00261     // get anyway a valid name!
00262     if (!Name || Name[0] == '\0')
00263         Name = "Unnamed";
00264     string name = getUniqueDocumentName(Name);
00265 
00266     // create the FreeCAD document
00267     auto_ptr<Document> newDoc(new Document() );
00268 
00269     // add the document to the internal list
00270     DocMap[name] = newDoc.release(); // now owned by the Application
00271     _pActiveDoc = DocMap[name];
00272 
00273 
00274     // connect the signals to the application for the new document
00275     _pActiveDoc->signalNewObject.connect(boost::bind(&App::Application::slotNewObject, this, _1));
00276     _pActiveDoc->signalDeletedObject.connect(boost::bind(&App::Application::slotDeletedObject, this, _1));
00277     _pActiveDoc->signalChangedObject.connect(boost::bind(&App::Application::slotChangedObject, this, _1, _2));
00278     _pActiveDoc->signalRenamedObject.connect(boost::bind(&App::Application::slotRenamedObject, this, _1));
00279     _pActiveDoc->signalActivatedObject.connect(boost::bind(&App::Application::slotActivatedObject, this, _1));
00280 
00281     // make sure that the active document is set in case no GUI is up
00282     {
00283         Base::PyGILStateLocker lock;
00284         Py::Object active(_pActiveDoc->getPyObject(), true);
00285         Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"),active);
00286     }
00287 
00288     signalNewDocument(*_pActiveDoc);
00289 
00290     // set the UserName after notifying all observers
00291     if (UserName)
00292         _pActiveDoc->Label.setValue(UserName);
00293     else
00294         _pActiveDoc->Label.setValue(name);
00295 
00296     return _pActiveDoc;
00297 }
00298 
00299 bool Application::closeDocument(const char* name)
00300 {
00301     map<string,Document*>::iterator pos = DocMap.find( name );
00302     if (pos == DocMap.end()) // no such document
00303         return false;
00304 
00305     if (!pos->second->isClosable())
00306         return false;
00307 
00308     // Trigger observers before removing the document from the internal map.
00309     // Some observers might rely on that this document is still there.
00310     signalDeleteDocument(*pos->second);
00311 
00312     // For exception-safety use a smart pointer
00313     if (_pActiveDoc == pos->second)
00314         setActiveDocument((Document*)0);
00315     auto_ptr<Document> delDoc (pos->second);
00316     DocMap.erase( pos );
00317 
00318     // Trigger observers after removing the document from the internal map.
00319     signalDeletedDocument();
00320 
00321     return true;
00322 }
00323 
00324 void Application::closeAllDocuments(void)
00325 {
00326     std::map<std::string,Document*>::iterator pos;
00327     while((pos = DocMap.begin()) != DocMap.end())
00328         closeDocument(pos->first.c_str());
00329 }
00330 
00331 App::Document* Application::getDocument(const char *Name) const
00332 {
00333     std::map<std::string,Document*>::const_iterator pos;
00334 
00335     pos = DocMap.find(Name);
00336 
00337     if (pos == DocMap.end())
00338         return 0;
00339 
00340     return pos->second;
00341 }
00342 
00343 const char * Application::getDocumentName(const App::Document* doc) const
00344 {
00345     for (std::map<std::string,Document*>::const_iterator it = DocMap.begin(); it != DocMap.end(); ++it)
00346         if (it->second == doc)
00347             return it->first.c_str();
00348 
00349     return 0;
00350 }
00351 
00352 std::vector<App::Document*> Application::getDocuments() const
00353 {
00354     std::vector<App::Document*> docs;
00355     for (std::map<std::string,Document*>::const_iterator it = DocMap.begin(); it != DocMap.end(); ++it)
00356         docs.push_back(it->second);
00357     return docs;
00358 }
00359 
00360 std::string Application::getUniqueDocumentName(const char *Name) const
00361 {
00362     if (!Name || *Name == '\0')
00363         return std::string();
00364     std::string CleanName = Base::Tools::getIdentifier(Name);
00365 
00366     // name in use?
00367     std::map<string,Document*>::const_iterator pos;
00368     pos = DocMap.find(CleanName);
00369 
00370     if (pos == DocMap.end()) {
00371         // if not, name is OK
00372         return CleanName;
00373     }
00374     else {
00375         std::vector<std::string> names;
00376         names.reserve(DocMap.size());
00377         for (pos = DocMap.begin();pos != DocMap.end();++pos) {
00378             names.push_back(pos->first);
00379         }
00380         return Base::Tools::getUniqueName(CleanName, names);
00381     }
00382 }
00383 
00384 Document* Application::openDocument(const char * FileName)
00385 {
00386     FileInfo File(FileName);
00387 
00388     if (!File.exists()) {
00389         std::stringstream str;
00390         str << "File '" << FileName << "' does not exist!";
00391         throw Base::Exception(str.str().c_str());
00392     }
00393 
00394     // Before creating a new document we check whether the document is already open
00395     std::string filepath = File.filePath();
00396     for (std::map<std::string,Document*>::iterator it = DocMap.begin(); it != DocMap.end(); ++it) {
00397         // get unique path separators
00398         std::string fi = FileInfo(it->second->FileName.getValue()).filePath();
00399         if (filepath == fi) {
00400             std::stringstream str;
00401             str << "The project '" << FileName << "' is already open!";
00402             throw Base::Exception(str.str().c_str());
00403         }
00404     }
00405 
00406     // Use the same name for the internal and user name.
00407     // The file name is UTF-8 encoded which means that the internal name will be modified
00408     // to only contain valid ASCII characters but the user name will be kept.
00409     Document* newDoc = newDocument(File.fileNamePure().c_str(), File.fileNamePure().c_str());
00410 
00411     newDoc->FileName.setValue(File.filePath());
00412 
00413     // read the document
00414     newDoc->restore();
00415 
00416     return newDoc;
00417 }
00418 
00419 Document* Application::getActiveDocument(void) const
00420 {
00421     return _pActiveDoc;
00422 }
00423 
00424 void Application::setActiveDocument(Document* pDoc)
00425 {
00426     _pActiveDoc = pDoc;
00427 
00428     // make sure that the active document is set in case no GUI is up
00429     if (pDoc) {
00430         Base::PyGILStateLocker lock;
00431         Py::Object active(pDoc->getPyObject(), true);
00432         Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"),active);
00433     }
00434     else {
00435         Base::PyGILStateLocker lock;
00436         Py::Module("FreeCAD").setAttr(std::string("ActiveDocument"),Py::None());
00437     }
00438 
00439     if (pDoc)
00440         signalActiveDocument(*pDoc);
00441 }
00442 
00443 void Application::setActiveDocument(const char *Name)
00444 {
00445     // Allows that no active document is set.
00446     if (*Name == '\0') {
00447         _pActiveDoc = 0;
00448         return;
00449     }
00450 
00451     std::map<std::string,Document*>::iterator pos;
00452     pos = DocMap.find(Name);
00453 
00454     if (pos != DocMap.end()) {
00455         setActiveDocument(pos->second);
00456     }
00457     else {
00458         std::stringstream s;
00459         s << "Try to activate unknown document '" << Name << "'";
00460         throw Base::Exception(s.str());
00461     }
00462 }
00463 
00464 const char* Application::GetHomePath(void) const
00465 {
00466     return _mConfig["AppHomePath"].c_str();
00467 }
00468 
00469 const char* Application::getExecutableName(void) const
00470 {
00471     return _mConfig["ExeName"].c_str();
00472 }
00473 
00474 std::string Application::getUserAppDataDir()
00475 {
00476     return mConfig["UserAppData"];
00477 }
00478 
00479 std::string Application::getResourceDir()
00480 {
00481 # ifdef RESOURCEDIR
00482     return std::string(RESOURCEDIR) + "/";
00483 #else
00484     return mConfig["AppHomePath"];
00485 #endif
00486 }
00487 
00488 std::string Application::getHelpDir()
00489 {
00490 # ifdef DOCDIR
00491     return std::string(DOCDIR) + "/";
00492 #else
00493     return mConfig["DocPath"];
00494 #endif
00495 }
00496 
00497 ParameterManager & Application::GetSystemParameter(void)
00498 {
00499     return *_pcSysParamMngr;
00500 }
00501 
00502 ParameterManager & Application::GetUserParameter(void)
00503 {
00504     return *_pcUserParamMngr;
00505 }
00506 
00507 ParameterManager * Application::GetParameterSet(const char* sName) const
00508 {
00509     std::map<std::string,ParameterManager *>::const_iterator it = mpcPramManager.find(sName);
00510     if ( it != mpcPramManager.end() )
00511         return it->second;
00512     else
00513         return 0;
00514 }
00515 
00516 const std::map<std::string,ParameterManager *> & Application::GetParameterSetList(void) const
00517 {
00518     return mpcPramManager;
00519 }
00520 
00521 void Application::AddParameterSet(const char* sName)
00522 {
00523     std::map<std::string,ParameterManager *>::const_iterator it = mpcPramManager.find(sName);
00524     if ( it != mpcPramManager.end() )
00525         return;
00526     mpcPramManager[sName] = new ParameterManager();
00527 }
00528 
00529 void Application::RemoveParameterSet(const char* sName)
00530 {
00531     std::map<std::string,ParameterManager *>::iterator it = mpcPramManager.find(sName);
00532     // Must not delete user or system parameter
00533     if ( it == mpcPramManager.end() || it->second == _pcUserParamMngr || it->second == _pcSysParamMngr )
00534         return;
00535     delete it->second;
00536     mpcPramManager.erase(it);
00537 }
00538 
00539 Base::Reference<ParameterGrp>  Application::GetParameterGroupByPath(const char* sName)
00540 {
00541     std::string cName = sName,cTemp;
00542 
00543     std::string::size_type pos = cName.find(':');
00544 
00545     // is there a path seperator ?
00546     if (pos == std::string::npos) {
00547         throw Base::Exception("Application::GetParameterGroupByPath() no parameter set name specified");
00548     }
00549     // assigning the parameter set name
00550     cTemp.assign(cName,0,pos);
00551     cName.erase(0,pos+1);
00552 
00553     // test if name is valid
00554     std::map<std::string,ParameterManager *>::iterator It = mpcPramManager.find(cTemp.c_str());
00555     if (It == mpcPramManager.end())
00556         throw Base::Exception("Application::GetParameterGroupByPath() unknown parameter set name specified");
00557 
00558     return It->second->GetGroup(cName.c_str());
00559 }
00560 
00561 void Application::addImportType(const char* Type, const char* ModuleName)
00562 {
00563     FileTypeItem item;
00564     item.filter = Type;
00565     item.module = ModuleName;
00566 
00567     // Extract each filetype from 'Type' literal
00568     std::string::size_type pos = item.filter.find("*.");
00569     while ( pos != std::string::npos ) {
00570         std::string::size_type next = item.filter.find_first_of(" )", pos+1);
00571         std::string::size_type len = next-pos-2;
00572         std::string type = item.filter.substr(pos+2,len);
00573         item.types.push_back(type);
00574         pos = item.filter.find("*.", next);
00575     }
00576 
00577     // Due to branding stuff replace FreeCAD through the application name
00578     if (strncmp(Type, "FreeCAD", 7) == 0) {
00579         std::string AppName = Config()["ExeName"];
00580         AppName += item.filter.substr(7);
00581         item.filter = AppName;
00582         // put to the front of the array
00583         _mImportTypes.insert(_mImportTypes.begin(),item);
00584     }
00585     else {
00586         _mImportTypes.push_back(item);
00587     }
00588 }
00589 
00590 std::vector<std::string> Application::getImportModules(const char* Type) const
00591 {
00592     std::vector<std::string> modules;
00593     for (std::vector<FileTypeItem>::const_iterator it = _mImportTypes.begin(); it != _mImportTypes.end(); ++it) {
00594         const std::vector<std::string>& types = it->types;
00595         for (std::vector<std::string>::const_iterator jt = types.begin(); jt != types.end(); ++jt) {
00596 #ifdef __GNUC__
00597             if (strcasecmp(Type,jt->c_str()) == 0)
00598 #else
00599             if (_stricmp(Type,jt->c_str()) == 0)
00600 #endif
00601                 modules.push_back(it->module);
00602         }
00603     }
00604 
00605     return modules;
00606 }
00607 
00608 std::vector<std::string> Application::getImportModules() const
00609 {
00610     std::vector<std::string> modules;
00611     for (std::vector<FileTypeItem>::const_iterator it = _mImportTypes.begin(); it != _mImportTypes.end(); ++it)
00612         modules.push_back(it->module);
00613     std::sort(modules.begin(), modules.end());
00614     modules.erase(std::unique(modules.begin(), modules.end()), modules.end());
00615     return modules;
00616 }
00617 
00618 std::vector<std::string> Application::getImportTypes(const char* Module) const
00619 {
00620     std::vector<std::string> types;
00621     for (std::vector<FileTypeItem>::const_iterator it = _mImportTypes.begin(); it != _mImportTypes.end(); ++it) {
00622 #ifdef __GNUC__
00623         if (strcasecmp(Module,it->module.c_str()) == 0)
00624 #else
00625         if (_stricmp(Module,it->module.c_str()) == 0)
00626 #endif
00627             types.insert(types.end(), it->types.begin(), it->types.end());
00628     }
00629 
00630     return types;
00631 }
00632 
00633 std::vector<std::string> Application::getImportTypes(void) const
00634 {
00635     std::vector<std::string> types;
00636     for (std::vector<FileTypeItem>::const_iterator it = _mImportTypes.begin(); it != _mImportTypes.end(); ++it) {
00637         types.insert(types.end(), it->types.begin(), it->types.end());
00638     }
00639 
00640     std::sort(types.begin(), types.end());
00641     types.erase(std::unique(types.begin(), types.end()), types.end());
00642 
00643     return types;
00644 }
00645 
00646 std::map<std::string, std::string> Application::getImportFilters(const char* Type) const
00647 {
00648     std::map<std::string, std::string> moduleFilter;
00649     for (std::vector<FileTypeItem>::const_iterator it = _mImportTypes.begin(); it != _mImportTypes.end(); ++it) {
00650         const std::vector<std::string>& types = it->types;
00651         for (std::vector<std::string>::const_iterator jt = types.begin(); jt != types.end(); ++jt) {
00652 #ifdef __GNUC__
00653             if (strcasecmp(Type,jt->c_str()) == 0)
00654 #else
00655             if (_stricmp(Type,jt->c_str()) == 0)
00656 #endif
00657                 moduleFilter[it->filter] = it->module;
00658         }
00659     }
00660 
00661     return moduleFilter;
00662 }
00663 
00664 std::map<std::string, std::string> Application::getImportFilters(void) const
00665 {
00666     std::map<std::string, std::string> filter;
00667     for (std::vector<FileTypeItem>::const_iterator it = _mImportTypes.begin(); it != _mImportTypes.end(); ++it) {
00668         filter[it->filter] = it->module;
00669     }
00670 
00671     return filter;
00672 }
00673 
00674 void Application::addExportType(const char* Type, const char* ModuleName)
00675 {
00676     FileTypeItem item;
00677     item.filter = Type;
00678     item.module = ModuleName;
00679 
00680     // Extract each filetype from 'Type' literal
00681     std::string::size_type pos = item.filter.find("*.");
00682     while ( pos != std::string::npos ) {
00683         std::string::size_type next = item.filter.find_first_of(" )", pos+1);
00684         std::string::size_type len = next-pos-2;
00685         std::string type = item.filter.substr(pos+2,len);
00686         item.types.push_back(type);
00687         pos = item.filter.find("*.", next);
00688     }
00689 
00690     // Due to branding stuff replace FreeCAD through the application name
00691     if (strncmp(Type, "FreeCAD", 7) == 0) {
00692         std::string AppName = Config()["ExeName"];
00693         AppName += item.filter.substr(7);
00694         item.filter = AppName;
00695         // put to the front of the array
00696         _mExportTypes.insert(_mExportTypes.begin(),item);
00697     }
00698     else {
00699         _mExportTypes.push_back(item);
00700     }
00701 }
00702 
00703 std::vector<std::string> Application::getExportModules(const char* Type) const
00704 {
00705     std::vector<std::string> modules;
00706     for (std::vector<FileTypeItem>::const_iterator it = _mExportTypes.begin(); it != _mExportTypes.end(); ++it) {
00707         const std::vector<std::string>& types = it->types;
00708         for (std::vector<std::string>::const_iterator jt = types.begin(); jt != types.end(); ++jt) {
00709 #ifdef __GNUC__
00710             if (strcasecmp(Type,jt->c_str()) == 0)
00711 #else
00712             if (_stricmp(Type,jt->c_str()) == 0)
00713 #endif
00714                 modules.push_back(it->module);
00715         }
00716     }
00717 
00718     return modules;
00719 }
00720 
00721 std::vector<std::string> Application::getExportModules() const
00722 {
00723     std::vector<std::string> modules;
00724     for (std::vector<FileTypeItem>::const_iterator it = _mExportTypes.begin(); it != _mExportTypes.end(); ++it)
00725         modules.push_back(it->module);
00726     std::sort(modules.begin(), modules.end());
00727     modules.erase(std::unique(modules.begin(), modules.end()), modules.end());
00728     return modules;
00729 }
00730 
00731 std::vector<std::string> Application::getExportTypes(const char* Module) const
00732 {
00733     std::vector<std::string> types;
00734     for (std::vector<FileTypeItem>::const_iterator it = _mExportTypes.begin(); it != _mExportTypes.end(); ++it) {
00735 #ifdef __GNUC__
00736         if (strcasecmp(Module,it->module.c_str()) == 0)
00737 #else
00738         if (_stricmp(Module,it->module.c_str()) == 0)
00739 #endif
00740             types.insert(types.end(), it->types.begin(), it->types.end());
00741     }
00742 
00743     return types;
00744 }
00745 
00746 std::vector<std::string> Application::getExportTypes(void) const
00747 {
00748     std::vector<std::string> types;
00749     for (std::vector<FileTypeItem>::const_iterator it = _mExportTypes.begin(); it != _mExportTypes.end(); ++it) {
00750         types.insert(types.end(), it->types.begin(), it->types.end());
00751     }
00752 
00753     std::sort(types.begin(), types.end());
00754     types.erase(std::unique(types.begin(), types.end()), types.end());
00755 
00756     return types;
00757 }
00758 
00759 std::map<std::string, std::string> Application::getExportFilters(const char* Type) const
00760 {
00761     std::map<std::string, std::string> moduleFilter;
00762     for (std::vector<FileTypeItem>::const_iterator it = _mExportTypes.begin(); it != _mExportTypes.end(); ++it) {
00763         const std::vector<std::string>& types = it->types;
00764         for (std::vector<std::string>::const_iterator jt = types.begin(); jt != types.end(); ++jt) {
00765 #ifdef __GNUC__
00766             if (strcasecmp(Type,jt->c_str()) == 0)
00767 #else
00768             if (_stricmp(Type,jt->c_str()) == 0)
00769 #endif
00770                 moduleFilter[it->filter] = it->module;
00771         }
00772     }
00773 
00774     return moduleFilter;
00775 }
00776 
00777 std::map<std::string, std::string> Application::getExportFilters(void) const
00778 {
00779     std::map<std::string, std::string> filter;
00780     for (std::vector<FileTypeItem>::const_iterator it = _mExportTypes.begin(); it != _mExportTypes.end(); ++it) {
00781         filter[it->filter] = it->module;
00782     }
00783 
00784     return filter;
00785 }
00786 
00787 //**************************************************************************
00788 // signaling
00789 void Application::slotNewObject(const App::DocumentObject&O)
00790 {
00791     this->signalNewObject(O);
00792 }
00793 
00794 void Application::slotDeletedObject(const App::DocumentObject&O)
00795 {
00796     this->signalDeletedObject(O);
00797 }
00798 
00799 void Application::slotChangedObject(const App::DocumentObject&O, const App::Property& P)
00800 {
00801     this->signalChangedObject(O,P);
00802 }
00803 
00804 void Application::slotRenamedObject(const App::DocumentObject&O)
00805 {
00806     this->signalRenamedObject(O);
00807 }
00808 
00809 void Application::slotActivatedObject(const App::DocumentObject&O)
00810 {
00811     this->signalActivatedObject(O);
00812 }
00813 
00814 
00815 //**************************************************************************
00816 // Init, Destruct and singleton
00817 
00818 Application * Application::_pcSingleton = 0;
00819 
00820 int Application::_argc;
00821 char ** Application::_argv;
00822 
00823 
00824 void Application::destruct(void)
00825 {
00826     // saving system parameter
00827     Console().Log("Saving system parameter...\n");
00828     _pcSysParamMngr->SaveDocument(mConfig["SystemParameter"].c_str());
00829     // saving the User parameter
00830     Console().Log("Saving system parameter...done\n");
00831     Console().Log("Saving user parameter...\n");
00832     _pcUserParamMngr->SaveDocument(mConfig["UserParameter"].c_str());
00833     Console().Log("Saving user parameter...done\n");
00834     // clean up
00835     delete _pcSysParamMngr;
00836     delete _pcUserParamMngr;
00837 
00838     // not initialized or doubel destruct!
00839     assert(_pcSingleton);
00840     delete _pcSingleton;
00841 
00842     // We must detach from console and delete the observer to save our file
00843     destructObserver();
00844 
00845     Base::Interpreter().finalize();
00846 
00847     ScriptFactorySingleton::Destruct();
00848     InterpreterSingleton::Destruct();
00849     Base::Type::destruct();
00850 }
00851 
00852 void Application::destructObserver(void)
00853 {
00854     if ( _pConsoleObserverFile ) {
00855         Console().DetachObserver(_pConsoleObserverFile);
00856         delete _pConsoleObserverFile;
00857         _pConsoleObserverFile = 0;
00858     }
00859     if ( _pConsoleObserverStd ) {
00860         Console().DetachObserver(_pConsoleObserverStd);
00861         delete _pConsoleObserverStd;
00862         _pConsoleObserverFile = 0;
00863     }
00864 }
00865 
00869 #ifdef _MSC_VER // New handler for Microsoft Visual C++ compiler
00870 int __cdecl freecadNewHandler(size_t size )
00871 {
00872     // throw an exception
00873     throw Base::MemoryException();
00874     return 0;
00875 }
00876 #else // Ansi C/C++ new handler
00877 static void freecadNewHandler ()
00878 {
00879     // throw an exception
00880     throw Base::MemoryException();
00881 }
00882 #endif
00883 
00884 void segmentation_fault_handler(int sig)
00885 {
00886     switch (sig) {
00887         case SIGSEGV:
00888             std::cerr << "Illegal storage access..." << std::endl;
00889             break;
00890         case SIGABRT:
00891             std::cerr << "Abnormal program termination..." << std::endl;
00892             break;
00893         default:
00894             std::cerr << "Unknown error occurred..." << std::endl;
00895             break;
00896     }
00897 
00898 #if defined(__GNUC__)
00899     // According to the documentation to C signals we should exit.
00900     exit(3);
00901 #endif
00902 }
00903 
00904 void unhandled_exception_handler()
00905 {
00906     std::cerr << "Unhandled exception..." << std::endl;
00907 }
00908 
00909 void unexpection_error_handler()
00910 {
00911     std::cerr << "Unexpected error occurred..." << std::endl;
00912     terminate();
00913 }
00914 
00915 
00916 void Application::init(int argc, char ** argv)
00917 {
00918     try {
00919         // install our own new handler
00920 #ifdef _MSC_VER // Microsoft compiler
00921         _set_new_handler ( freecadNewHandler ); // Setup new handler
00922         _set_new_mode( 1 ); // Re-route malloc failures to new handler !
00923 #else   // Ansi compiler
00924         std::set_new_handler (freecadNewHandler); // ANSI new handler
00925 #endif
00926         // if an unexpected crash occurs we can install a handler function to
00927         // write some additional information
00928         std::signal(SIGSEGV,segmentation_fault_handler);
00929         std::signal(SIGABRT,segmentation_fault_handler);
00930         std::set_terminate(unhandled_exception_handler);
00931         std::set_unexpected(unexpection_error_handler);
00932 
00933         initTypes();
00934 
00935 #if (BOOST_VERSION < 104600) || (BOOST_FILESYSTEM_VERSION == 2)
00936         boost::filesystem::path::default_name_check(boost::filesystem::no_check);
00937 #endif
00938 
00939         initConfig(argc,argv);
00940         initApplication();
00941     }
00942     catch (...) {
00943         // force to flush the log
00944         destructObserver();
00945         throw;
00946     }
00947 }
00948 
00949 void Application::initTypes(void)
00950 {
00951     // Base types
00952     Base::Type                      ::init();
00953     Base::BaseClass                 ::init();
00954     Base::Exception                 ::init();
00955     Base::Persistence               ::init();
00956 
00957     // Complex data classes
00958     Data::ComplexGeoData            ::init();
00959     Data::Segment                   ::init();
00960 
00961     // Properties
00962     App ::Property                  ::init();
00963     App ::PropertyContainer         ::init();
00964     App ::PropertyLists             ::init();
00965     App ::PropertyBool              ::init();
00966     App ::PropertyFloat             ::init();
00967     App ::PropertyFloatList         ::init();
00968     App ::PropertyFloatConstraint   ::init();
00969     App ::PropertyAngle             ::init();
00970     App ::PropertyDistance          ::init();
00971     App ::PropertyLength            ::init();
00972     App ::PropertySpeed             ::init();
00973     App ::PropertyAcceleration      ::init();
00974     App ::PropertyInteger           ::init();
00975     App ::PropertyIntegerConstraint ::init();
00976     App ::PropertyPercent           ::init();
00977     App ::PropertyEnumeration       ::init();
00978     App ::PropertyIntegerList       ::init();
00979     App ::PropertyString            ::init();
00980     App ::PropertyFont              ::init();
00981     App ::PropertyStringList        ::init();
00982     App ::PropertyLink              ::init();
00983     App ::PropertyLinkSub           ::init();
00984     App ::PropertyLinkList          ::init();
00985     App ::PropertyLinkSubList       ::init();
00986     App ::PropertyMatrix            ::init();
00987     App ::PropertyVector            ::init();
00988     App ::PropertyVectorList        ::init();
00989     App ::PropertyPlacement         ::init();
00990     App ::PropertyPlacementLink     ::init();
00991     App ::PropertyGeometry          ::init();
00992     App ::PropertyComplexGeoData    ::init();
00993     App ::PropertyColor             ::init();
00994     App ::PropertyColorList         ::init();
00995     App ::PropertyMaterial          ::init();
00996     App ::PropertyPath              ::init();
00997     App ::PropertyFile              ::init();
00998     App ::PropertyFileIncluded      ::init();
00999     App ::PropertyPythonObject      ::init();
01000     // Document classes
01001     App ::DocumentObject            ::init();
01002     App ::GeoFeature                ::init();
01003     App ::FeatureTest               ::init();
01004     App ::FeatureTestException      ::init();
01005     App ::FeaturePython             ::init();
01006     App ::GeometryPython            ::init();
01007     App ::Document                  ::init();
01008     App ::DocumentObjectGroup       ::init();
01009     App ::DocumentObjectGroupPython ::init();
01010     App ::DocumentObjectFileIncluded::init();
01011     App ::InventorObject            ::init();
01012     App ::VRMLObject                ::init();
01013     App ::Annotation                ::init();
01014     App ::AnnotationLabel           ::init();
01015     App ::MeasureDistance           ::init();
01016 }
01017 
01018 void Application::initConfig(int argc, char ** argv)
01019 {
01020     // find the home path....
01021     mConfig["AppHomePath"] = FindHomePath(argv[0]);
01022 
01023     // Version of the application extracted from SubWCRef into src/Build/Version.h
01024     // We only set these keys if not yet defined. Therefore it suffices to search
01025     // only for 'BuildVersionMajor'.
01026     if (App::Application::Config().find("BuildVersionMajor") == App::Application::Config().end()) {
01027         std::stringstream str; str << FCVersionMajor << "." << FCVersionMinor;
01028         App::Application::Config()["ExeVersion"]         = str.str();
01029         App::Application::Config()["BuildVersionMajor"]  = FCVersionMajor;
01030         App::Application::Config()["BuildVersionMinor"]  = FCVersionMinor;
01031         App::Application::Config()["BuildRevision"]      = FCRevision;
01032         App::Application::Config()["BuildRepositoryURL"] = FCRepositoryURL;
01033         App::Application::Config()["BuildRevisionDate"]  = FCCurrentDateT;
01034     }
01035 
01036     _argc = argc;
01037     _argv = argv;
01038 
01039     // extract home paths
01040     ExtractUserPath();
01041 
01042 #   ifdef FC_DEBUG
01043     mConfig["Debug"] = "1";
01044 #   else
01045     mConfig["Debug"] = "0";
01046 #   endif
01047 
01048     // init python
01049     mConfig["PythonSearchPath"] = Interpreter().init(argc,argv);
01050 
01051     // Parse the options which have impact to the init process
01052     ParseOptions(argc,argv);
01053 
01054     // Init console ===========================================================
01055     Base::PyGILStateLocker lock;
01056     _pConsoleObserverStd = new ConsoleObserverStd();
01057     Console().AttachObserver(_pConsoleObserverStd);
01058     if (mConfig["Verbose"] == "Strict")
01059         Console().SetMode(ConsoleSingleton::Verbose);
01060 
01061     // file logging Init ===========================================================
01062     if (mConfig["LoggingFile"] == "1") {
01063         _pConsoleObserverFile = new ConsoleObserverFile(mConfig["LoggingFileName"].c_str());
01064         Console().AttachObserver(_pConsoleObserverFile);
01065     }
01066     else
01067         _pConsoleObserverFile = 0;
01068 
01069     // Banner ===========================================================
01070     if (!(mConfig["Verbose"] == "Strict"))
01071         Console().Message("%s %s, Libs: %s.%sR%s\n%s",mConfig["ExeName"].c_str(),
01072                           mConfig["ExeVersion"].c_str(),
01073                           mConfig["BuildVersionMajor"].c_str(),
01074                           mConfig["BuildVersionMinor"].c_str(),
01075                           mConfig["BuildRevision"].c_str(),
01076                           mConfig["ConsoleBanner"].c_str());
01077     else
01078         Console().Message("%s %s, Libs: %s.%sB%s\n",mConfig["ExeName"].c_str(),
01079                           mConfig["ExeVersion"].c_str(),
01080                           mConfig["BuildVersionMajor"].c_str(),
01081                           mConfig["BuildVersionMinor"].c_str(),
01082                           mConfig["BuildRevision"].c_str());
01083 
01084     LoadParameters();
01085 
01086     // set the default units
01087     UnitsApi::setDefaults();
01088 
01089     // capture python variables
01090     SaveEnv("PYTHONPATH");
01091     SaveEnv("PYTHONHOME");
01092     SaveEnv("TCL_LIBRARY");
01093     SaveEnv("TCLLIBPATH");
01094 
01095     // capture CasCade variables
01096     SaveEnv("CSF_MDTVFontDirectory");
01097     SaveEnv("CSF_MDTVTexturesDirectory");
01098     SaveEnv("CSF_UnitsDefinition");
01099     SaveEnv("CSF_UnitsLexicon");
01100     SaveEnv("CSF_StandardDefaults");
01101     SaveEnv("CSF_PluginDefaults");
01102     SaveEnv("CSF_LANGUAGE");
01103     SaveEnv("CSF_SHMessage");
01104     SaveEnv("CSF_XCAFDefaults");
01105     SaveEnv("CSF_GraphicShr");
01106     SaveEnv("CSF_IGESDefaults");
01107     SaveEnv("CSF_STEPDefaults");
01108 
01109     // capture path
01110     SaveEnv("PATH");
01111     logStatus();
01112 }
01113 
01114 void Application::SaveEnv(const char* s)
01115 {
01116     char *c = getenv(s);
01117     if (c)
01118         mConfig[s] = c;
01119 }
01120 
01121 void Application::initApplication(void)
01122 {
01123     // interpreter and Init script ==========================================================
01124     // register scripts
01125     new ScriptProducer( "FreeCADInit",    FreeCADInit    );
01126     new ScriptProducer( "FreeCADTest",    FreeCADTest    );
01127 
01128     // creating the application
01129     if (!(mConfig["Verbose"] == "Strict")) Console().Log("Create Application\n");
01130     Application::_pcSingleton = new Application(0,0,mConfig);
01131 
01132     // set up Unit system default
01133     //ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
01134     //   ("User parameter:BaseApp/Preferences/Units");
01135     //UnitsApi::setSchema((UnitSystem)hGrp->GetInt("UserSchema",0));
01136 
01137     // starting the init script
01138     Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit"));
01139 }
01140 
01141 void Application::processCmdLineFiles(void)
01142 {
01143     Base::Console().Log("Init: Processing command line files\n");
01144 
01145     // cycling through all the open files
01146     unsigned short count = 0;
01147     count = atoi(mConfig["OpenFileCount"].c_str());
01148     std::string File;
01149 
01150     if (count == 0 && mConfig["RunMode"] == "Exit")
01151         mConfig["RunMode"] = "Cmd";
01152 
01153     for (unsigned short i=0; i<count; i++) {
01154         // getting file name
01155         std::ostringstream temp;
01156         temp << "OpenFile" << i;
01157 
01158         FileInfo File(mConfig[temp.str()].c_str());
01159 
01160         std::string Ext = File.extension();
01161         Base::Console().Log("Init:     Processing file: %s\n",File.filePath().c_str());
01162         try {
01163 
01164             if (File.hasExtension("fcstd") || File.hasExtension("std")) {
01165                 // try to open
01166                 Application::_pcSingleton->openDocument(File.filePath().c_str());
01167             }
01168             else if (File.hasExtension("fcscript")||File.hasExtension("fcmacro")) {
01169                 Base::Interpreter().runFile(File.filePath().c_str());
01170             }
01171             else if (File.hasExtension("py")) {
01172                 Base::Interpreter().loadModule(File.fileNamePure().c_str());
01173             }
01174             else {
01175                 std::vector<std::string> mods = App::GetApplication().getImportModules(Ext.c_str());
01176                 if (!mods.empty()) {
01177                     Base::Interpreter().loadModule(mods.front().c_str());
01178                     Base::Interpreter().runStringArg("import %s",mods.front().c_str());
01179                     Base::Interpreter().runStringArg("%s.open(\"%s\")",mods.front().c_str(),File.filePath().c_str());
01180                     Base::Console().Log("Command line open: %s.Open(\"%s\")\n",mods.front().c_str(),File.filePath().c_str());
01181                 }
01182                 else {
01183                     Console().Warning("File format not supported: %s \n", File.filePath().c_str());
01184                 }
01185             }
01186         }
01187         catch (const Base::Exception& e) {
01188             Console().Error("Exception while processing file: %s [%s]\n", File.filePath().c_str(), e.what());
01189         }
01190         catch (...) {
01191             Console().Error("Unknown exception while processing file: %s \n", File.filePath().c_str());
01192         }
01193     }
01194 }
01195 
01196 void Application::runApplication()
01197 {
01198     // process all files given through command line interface
01199     processCmdLineFiles();
01200 
01201     if (mConfig["RunMode"] == "Cmd") {
01202         // Run the comandline interface
01203         Interpreter().runCommandLine("FreeCAD Console mode");
01204     }
01205     else if (mConfig["RunMode"] == "Internal") {
01206         // run internal script
01207         Console().Log("Running internal script:\n");
01208         Interpreter().runString(Base::ScriptFactory().ProduceScript(mConfig["ScriptFileName"].c_str()));
01209     }
01210     else if (mConfig["RunMode"] == "Exit") {
01211         // geting out
01212         Console().Log("Exiting on purpose\n");
01213     }
01214     else {
01215         Console().Log("Unknown Run mode (%d) in main()?!?\n\n",mConfig["RunMode"].c_str());
01216     }
01217 }
01218 
01219 void Application::logStatus()
01220 {
01221     time_t now;
01222     time(&now);
01223     Console().Log("Time = %s", ctime(&now));
01224 
01225     for (std::map<std::string,std::string>::iterator It = mConfig.begin();It!= mConfig.end();It++) {
01226         Console().Log("%s = %s\n",It->first.c_str(),It->second.c_str());
01227     }
01228 }
01229 
01230 void Application::LoadParameters(void)
01231 {
01232     // create standard parameter sets
01233     _pcSysParamMngr = new ParameterManager();
01234     _pcUserParamMngr = new ParameterManager();
01235 
01236     // Init parameter sets ===========================================================
01237     //
01238     if (mConfig.find("UserParameter") == mConfig.end())
01239         mConfig["UserParameter"]   = mConfig["UserAppData"] + "user.cfg";
01240     if (mConfig.find("SystemParameter") == mConfig.end())
01241         mConfig["SystemParameter"] = mConfig["UserAppData"] + "system.cfg";
01242 
01243 
01244     try {
01245         if (_pcSysParamMngr->LoadOrCreateDocument(mConfig["SystemParameter"].c_str()) && !(mConfig["Verbose"] == "Strict")) {
01246             // Configuration file optional when using as Python module
01247             if (!Py_IsInitialized()) {
01248                 Console().Warning("   Parameter not existing, write initial one\n");
01249                 Console().Message("   This warning normally means that FreeCAD is running the first time\n"
01250                                   "   or the configuration was deleted or moved. Build up the standard\n"
01251                                   "   configuration.\n");
01252             }
01253         }
01254     }
01255     catch (const Base::Exception& e) {
01256         // try to proceed with an empty XML document
01257         Base::Console().Error("%s in file %s.\n"
01258                               "Continue with an empty configuration.",
01259                               e.what(), mConfig["SystemParameter"].c_str());
01260         _pcSysParamMngr->CreateDocument();
01261     }
01262 
01263     try {
01264         if (_pcUserParamMngr->LoadOrCreateDocument(mConfig["UserParameter"].c_str()) && !(mConfig["Verbose"] == "Strict")) {
01265             // Configuration file optional when using as Python module
01266             if (!Py_IsInitialized()) {
01267                 Console().Warning("   User settings not existing, write initial one\n");
01268                 Console().Message("   This warning normally means that FreeCAD is running the first time\n"
01269                                   "   or your configuration was deleted or moved. The system defaults\n"
01270                                   "   will be reestablished for you.\n");
01271             }
01272         }
01273     }
01274     catch (const Base::Exception& e) {
01275         // try to proceed with an empty XML document
01276         Base::Console().Error("%s in file %s.\n"
01277                               "Continue with an empty configuration.",
01278                               e.what(), mConfig["UserParameter"].c_str());
01279         _pcUserParamMngr->CreateDocument();
01280     }
01281 }
01282 
01283 
01284 #if (defined(_MSC_VER) && (_MSC_VER != 1600))
01285 // fix weird error while linking boost (all versions of VC)
01286 namespace boost { namespace program_options { std::string arg="arg"; } }
01287 #if (defined (BOOST_VERSION) && (BOOST_VERSION == 104100))
01288 namespace boost { namespace program_options { 
01289     const unsigned options_description::m_default_line_length = 80;
01290 } }
01291 #endif
01292 #endif
01293 
01294 #if 0 // it seemse the SUSE has fixed the broken boost package
01295 // reported for SUSE in issue #0000208
01296 #if defined(__GNUC__)
01297 #if BOOST_VERSION == 104400
01298 namespace boost { namespace filesystem { 
01299     bool no_check( const std::string & ) { return true; }
01300 } }
01301 #endif
01302 #endif
01303 #endif
01304 
01305 pair<string, string> customSyntax(const string& s)
01306 {
01307     if (s.find("-display") == 0)
01308         return make_pair(string("display"), string("null"));
01309     else if (s.find("-style") == 0)
01310         return make_pair(string("style"), string("null"));
01311     else if (s.find("-geometry") == 0)
01312         return make_pair(string("geometry"), string("null"));
01313     else if (s.find("-font") == 0)
01314         return make_pair(string("font"), string("null"));
01315     else if (s.find("-fn") == 0)
01316         return make_pair(string("fn"), string("null"));
01317     else if (s.find("-background") == 0)
01318         return make_pair(string("background"), string("null"));
01319     else if (s.find("-bg") == 0)
01320         return make_pair(string("bg"), string("null"));
01321     else if (s.find("-foreground") == 0)
01322         return make_pair(string("foreground"), string("null"));
01323     else if (s.find("-fg") == 0)
01324         return make_pair(string("fg"), string("null"));
01325     else if (s.find("-button") == 0)
01326         return make_pair(string("button"), string("null"));
01327     else if (s.find("-button") == 0)
01328         return make_pair(string("button"), string("null"));
01329     else if (s.find("-btn") == 0)
01330         return make_pair(string("btn"), string("null"));
01331     else if (s.find("-name") == 0)
01332         return make_pair(string("name"), string("null"));
01333     else if (s.find("-title") == 0)
01334         return make_pair(string("title"), string("null"));
01335     else if (s.find("-visual") == 0)
01336         return make_pair(string("visual"), string("null"));
01337 //  else if (s.find("-ncols") == 0)
01338 //    return make_pair(string("ncols"), boost::program_options::value<int>(1));
01339 //  else if (s.find("-cmap") == 0)
01340 //    return make_pair(string("cmap"), string("null"));
01341     else if ('@' == s[0])
01342         return std::make_pair(string("response-file"), s.substr(1));
01343     else
01344         return make_pair(string(), string());
01345 
01346 }
01347 
01348 // A helper function to simplify the main part.
01349 template<class T>
01350 ostream& operator<<(ostream& os, const vector<T>& v)
01351 {
01352     copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
01353     return os;
01354 }
01355 
01356 void Application::ParseOptions(int ac, char ** av)
01357 {
01358     // Declare a group of options that will be
01359     // allowed only on command line
01360     options_description generic("Generic options");
01361     generic.add_options()
01362     ("version,v", "Prints version string")
01363     ("help,h", "Prints help message")
01364     ("console,c", "Starts in console mode")
01365     ("response-file", value<string>(),"Can be specified with '@name', too")
01366     ;
01367 
01368     // Declare a group of options that will be
01369     // allowed both on command line and in
01370     // config file
01371     std::string descr("Writes a log file to:\n");
01372     descr += mConfig["UserAppData"];
01373     descr += mConfig["ExeName"];
01374     descr += ".log";
01375     boost::program_options::options_description config("Configuration");
01376     config.add_options()
01377     //("write-log,l", value<string>(), "write a log file")
01378     ("write-log,l", descr.c_str())
01379     ("log-file", value<string>(), "Unlike to --write-log this allows to log to an arbitrary file")
01380     ("user-cfg,u", value<string>(),"User config file to load/save user settings")
01381     ("system-cfg,s", value<string>(),"Systen config file to load/save system settings")
01382     ("run-test,t",   value<int>()   ,"Test level")
01383     ("module-path,M", value< vector<string> >()->composing(),"Additional module paths")
01384     ("python-path,P", value< vector<string> >()->composing(),"Additional python paths")
01385     ;
01386 
01387 
01388     // Hidden options, will be allowed both on command line and
01389     // in config file, but will not be shown to the user.
01390     boost::program_options::options_description hidden("Hidden options");
01391     hidden.add_options()
01392     ("input-file",  boost::program_options::value< vector<string> >(), "input file")
01393     // this are to ignore for the window system (QApplication)
01394     ("style",      boost::program_options::value< string >(), "set the application GUI style")
01395     ("display",    boost::program_options::value< string >(), "set the X-Server")
01396     ("geometry ",  boost::program_options::value< string >(), "set the X-Window geometry")
01397     ("font",       boost::program_options::value< string >(), "set the X-Window font")
01398     ("fn",         boost::program_options::value< string >(), "set the X-Window font")
01399     ("background", boost::program_options::value< string >(), "set the X-Window background color")
01400     ("bg",         boost::program_options::value< string >(), "set the X-Window background color")
01401     ("foreground", boost::program_options::value< string >(), "set the X-Window foreground color")
01402     ("fg",         boost::program_options::value< string >(), "set the X-Window foreground color")
01403     ("button",     boost::program_options::value< string >(), "set the X-Window button color")
01404     ("btn",        boost::program_options::value< string >(), "set the X-Window button color")
01405     ("name",       boost::program_options::value< string >(), "set the X-Window name")
01406     ("title",      boost::program_options::value< string >(), "set the X-Window title")
01407     ("visual",     boost::program_options::value< string >(), "set the X-Window to color scema")
01408     ("ncols",      boost::program_options::value< int    >(), "set the X-Window to color scema")
01409     ("cmap",                                                  "set the X-Window to color scema")
01410     ;
01411 
01412     // Ignored options, will be savely ignored. Mostly uses by underlaying libs.
01413     //boost::program_options::options_description x11("X11 options");
01414     //x11.add_options()
01415     //    ("display",  boost::program_options::value< string >(), "set the X-Server")
01416     //    ;
01417 
01418     options_description cmdline_options;
01419     cmdline_options.add(generic).add(config).add(hidden);
01420 
01421     boost::program_options::options_description config_file_options;
01422     config_file_options.add(config).add(hidden);
01423 
01424     boost::program_options::options_description visible("Allowed options");
01425     visible.add(generic).add(config);
01426 
01427     boost::program_options::positional_options_description p;
01428     p.add("input-file", -1);
01429 
01430     variables_map vm;
01431     try {
01432         store( boost::program_options::command_line_parser(ac, av).
01433                options(cmdline_options).positional(p).extra_parser(customSyntax).run(), vm);
01434 
01435         std::ifstream ifs("FreeCAD.cfg");
01436         store(parse_config_file(ifs, config_file_options), vm);
01437         notify(vm);
01438     }
01439     catch (const std::exception& e) {
01440         cerr << e.what() << endl << endl << visible << endl;
01441         exit(1);
01442     }
01443     catch (...) {
01444         cerr << "Wrong or unknown option, bailing out!" << endl << endl << visible << endl;
01445         exit(1);
01446     }
01447 
01448     if (vm.count("help")) {
01449         cout << mConfig["ExeName"] << endl << endl;
01450         cout << "For detailed descripton see http://free-cad.sf.net" << endl<<endl;
01451         cout << "Usage: " << mConfig["ExeName"] << " [options] File1 File2 ..." << endl << endl;
01452         cout << visible << endl;
01453         exit(0);
01454     }
01455 
01456     if (vm.count("response-file")) {
01457         // Load the file and tokenize it
01458         std::ifstream ifs(vm["response-file"].as<string>().c_str());
01459         if (!ifs) {
01460             Base::Console().Error("Could no open the response file\n");
01461             cerr << "Could no open the response file: '"
01462                  << vm["response-file"].as<string>() << "'" << endl;
01463             exit(1);
01464         }
01465         // Read the whole file into a string
01466         stringstream ss;
01467         ss << ifs.rdbuf();
01468         // Split the file content
01469         char_separator<char> sep(" \n\r");
01470         tokenizer<char_separator<char> > tok(ss.str(), sep);
01471         vector<string> args;
01472         copy(tok.begin(), tok.end(), back_inserter(args));
01473         // Parse the file and store the options
01474         store( boost::program_options::command_line_parser(ac, av).
01475                options(cmdline_options).positional(p).extra_parser(customSyntax).run(), vm);
01476     }
01477 
01478     if (vm.count("version")) {
01479         std::cout << mConfig["ExeName"] << " " << mConfig["ExeVersion"]
01480                   << " Revision: " << mConfig["BuildRevision"] << std::endl;
01481         exit(0);
01482     }
01483 
01484     if (vm.count("console")) {
01485         mConfig["RunMode"] = "Cmd";
01486     }
01487 
01488     if (vm.count("module-path")) {
01489         vector<string> Mods = vm["module-path"].as< vector<string> >();
01490         string temp;
01491         for (vector<string>::const_iterator It= Mods.begin();It != Mods.end();++It)
01492             temp += *It + ";";
01493         temp.erase(temp.end()-1);
01494         mConfig["AdditionalModulePaths"] = temp;
01495     }
01496 
01497     if (vm.count("python-path")) {
01498         vector<string> Paths = vm["python-path"].as< vector<string> >();
01499         for (vector<string>::const_iterator It= Paths.begin();It != Paths.end();++It)
01500             Base::Interpreter().addPythonPath(It->c_str());
01501     }
01502 
01503     if (vm.count("input-file")) {
01504         vector<string> files(vm["input-file"].as< vector<string> >());
01505         int OpenFileCount=0;
01506         for (vector<string>::const_iterator It = files.begin();It != files.end();++It) {
01507 
01508             //cout << "Input files are: "
01509             //     << vm["input-file"].as< vector<string> >() << "\n";
01510 
01511             std::ostringstream temp;
01512             temp << "OpenFile" << OpenFileCount;
01513             mConfig[temp.str()] = *It;
01514             OpenFileCount++;
01515         }
01516         std::ostringstream buffer;
01517         buffer << OpenFileCount;
01518         mConfig["OpenFileCount"] = buffer.str();
01519     }
01520 
01521     if (vm.count("write-log")) {
01522         mConfig["LoggingFile"] = "1";
01523         //mConfig["LoggingFileName"] = vm["write-log"].as<string>();
01524         mConfig["LoggingFileName"] = mConfig["UserAppData"] + mConfig["ExeName"] + ".log";
01525     }
01526 
01527     if (vm.count("log-file")) {
01528         mConfig["LoggingFile"] = "1";
01529         mConfig["LoggingFileName"] = vm["log-file"].as<string>();
01530     }
01531 
01532     if (vm.count("user-cfg")) {
01533         mConfig["UserParameter"] = vm["user-cfg"].as<string>();
01534     }
01535 
01536     if (vm.count("system-cfg")) {
01537         mConfig["SystemParameter"] = vm["system-cfg"].as<string>();
01538     }
01539 
01540     if (vm.count("run-test")) {
01541         int level = vm["run-test"].as<int>();
01542         switch (level) {
01543         case '0':
01544             // test script level 0
01545             mConfig["RunMode"] = "Internal";
01546             mConfig["ScriptFileName"] = "FreeCADTest";
01547             //sScriptName = FreeCADTest;
01548             break;
01549         default:
01550             //default testing level 0
01551             mConfig["RunMode"] = "Internal";
01552             mConfig["ScriptFileName"] = "FreeCADTest";
01553             //sScriptName = FreeCADTest;
01554             break;
01555         };
01556     }
01557 
01558 }
01559 
01560 void Application::ExtractUserPath()
01561 {
01562     // std paths
01563     mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP;
01564     mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP;
01565 
01566 #if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD)
01567     // Default paths for the user depending stuff on the platform
01568     struct passwd *pwd = getpwuid(getuid());
01569     if (pwd == NULL)
01570         throw Base::Exception("Getting HOME path from system failed!");
01571     mConfig["UserHomePath"] = pwd->pw_dir;
01572     std::string appData = pwd->pw_dir;
01573     Base::FileInfo fi(appData.c_str());
01574     if (!fi.exists()) {
01575         // This should never ever happen
01576         std::stringstream str;
01577         str << "Application data directory " << appData << " does not exist!";
01578         throw Base::Exception(str.str());
01579     }
01580 
01581     // Try to write into our data path, therefore we must create some directories, first.
01582     // If 'AppDataSkipVendor' is defined the value of 'ExeVendor' must not be part of
01583     // the path.
01584     appData += PATHSEP;
01585     appData += ".";
01586     if (mConfig.find("AppDataSkipVendor") == mConfig.end()) {
01587         appData += mConfig["ExeVendor"];
01588         fi.setFile(appData.c_str());
01589         if (!fi.exists() && !Py_IsInitialized()) {
01590             if (!fi.createDirectory()) {
01591                 std::string error = "Cannot create directory ";
01592                 error += appData;
01593                 // Want more details on console
01594                 std::cerr << error << std::endl;
01595                 throw Base::Exception(error);
01596             }
01597         }
01598         appData += PATHSEP;
01599     }
01600     
01601     appData += mConfig["ExeName"];
01602     fi.setFile(appData.c_str());
01603     if (!fi.exists() && !Py_IsInitialized()) {
01604         if (!fi.createDirectory()) {
01605             std::string error = "Cannot create directory ";
01606             error += appData;
01607             // Want more details on console
01608             std::cerr << error << std::endl;
01609             throw Base::Exception(error);
01610         }
01611     }
01612 
01613     // Actually the name of the directory where the parameters are stored should be the name of
01614     // the application due to branding reasons.
01615     appData += PATHSEP;
01616     mConfig["UserAppData"] = appData;
01617 
01618 #elif defined(FC_OS_MACOSX)
01619     // Default paths for the user depending stuff on the platform
01620     struct passwd *pwd = getpwuid(getuid());
01621     if (pwd == NULL)
01622         throw Base::Exception("Getting HOME path from system failed!");
01623     mConfig["UserHomePath"] = pwd->pw_dir;
01624     std::string appData = pwd->pw_dir;
01625     appData += PATHSEP;
01626     appData += "Library";
01627     appData += PATHSEP;
01628     appData += "Preferences";
01629     Base::FileInfo fi(appData.c_str());
01630     if (!fi.exists()) {
01631         // This should never ever happen
01632         std::stringstream str;
01633         str << "Application data directory " << appData << " does not exist!";
01634         throw Base::Exception(str.str());
01635     }
01636 
01637     // Try to write into our data path, therefore we must create some directories, first.
01638     // If 'AppDataSkipVendor' is defined the value of 'ExeVendor' must not be part of
01639     // the path.
01640     appData += PATHSEP;
01641     if (mConfig.find("AppDataSkipVendor") == mConfig.end()) {
01642         appData += mConfig["ExeVendor"];
01643         fi.setFile(appData.c_str());
01644         if (!fi.exists() && !Py_IsInitialized()) {
01645             if (!fi.createDirectory()) {
01646                 std::string error = "Cannot create directory ";
01647                 error += appData;
01648                 // Want more details on console
01649                 std::cerr << error << std::endl;
01650                 throw Base::Exception(error);
01651             }
01652         }
01653         appData += PATHSEP;
01654     }
01655     
01656     appData += mConfig["ExeName"];
01657     fi.setFile(appData.c_str());
01658     if (!fi.exists() && !Py_IsInitialized()) {
01659         if (!fi.createDirectory()) {
01660             std::string error = "Cannot create directory ";
01661             error += appData;
01662             // Want more details on console
01663             std::cerr << error << std::endl;
01664             throw Base::Exception(error);
01665         }
01666     }
01667 
01668     // Actually the name of the directory where the parameters are stored should be the name of
01669     // the application due to branding reasons.
01670     appData += PATHSEP;
01671     mConfig["UserAppData"] = appData;
01672 
01673 #elif defined(FC_OS_WIN32)
01674     WCHAR szPath[MAX_PATH];
01675     TCHAR dest[MAX_PATH*3];
01676     // Get the default path where we can save our documents. It seems that
01677     // 'CSIDL_MYDOCUMENTS' doesn't work on all machines, so we use 'CSIDL_PERSONAL'
01678     // which does the same.
01679     if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, szPath))) {
01680         WideCharToMultiByte(CP_UTF8, 0, szPath, -1,dest, 256, NULL, NULL);
01681         mConfig["UserHomePath"] = dest;
01682     }
01683     else
01684         mConfig["UserHomePath"] = mConfig["AppHomePath"];
01685 
01686     // In the second step we want the directory where user settings of the application can be
01687     // kept. There we create a directory with name of the vendor and a sub-directory with name
01688     // of the application.
01689     if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, szPath))) {
01690         // convert to UTF8
01691         WideCharToMultiByte(CP_UTF8, 0, szPath, -1,dest, 256, NULL, NULL);
01692 
01693         std::string appData = dest;
01694         Base::FileInfo fi(appData.c_str());
01695         if (!fi.exists()) {
01696             // This should never ever happen
01697             std::stringstream str;
01698             str << "Application data directory " << appData << " does not exist!";
01699             throw Base::Exception(str.str());
01700         }
01701 
01702         // Try to write into our data path, therefore we must create some directories, first.
01703         // If 'AppDataSkipVendor' is defined the value of 'ExeVendor' must not be part of
01704         // the path.
01705         if (mConfig.find("AppDataSkipVendor") == mConfig.end()) {
01706             appData += PATHSEP;
01707             appData += mConfig["ExeVendor"];
01708             fi.setFile(appData.c_str());
01709             if (!fi.exists() && !Py_IsInitialized()) {
01710                 if (!fi.createDirectory()) {
01711                     std::string error = "Cannot create directory ";
01712                     error += appData;
01713                     // Want more details on console
01714                     std::cerr << error << std::endl;
01715                     throw Base::Exception(error);
01716                 }
01717             }
01718         }
01719 
01720         appData += PATHSEP;
01721         appData += mConfig["ExeName"];
01722         fi.setFile(appData.c_str());
01723         if (!fi.exists() && !Py_IsInitialized()) {
01724             if (!fi.createDirectory()) {
01725                 std::string error = "Cannot create directory ";
01726                 error += appData;
01727                 // Want more details on console
01728                 std::cerr << error << std::endl;
01729                 throw Base::Exception(error);
01730             }
01731         }
01732 
01733         // Actually the name of the directory where the parameters are stored should be the name of
01734         // the application due to branding reasons.
01735         appData += PATHSEP;
01736         mConfig["UserAppData"] = appData;
01737     }
01738 #else
01739 # error "Implement ExtractUserPath() for your platform."
01740 #endif
01741 }
01742 
01743 #if defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD)
01744 #include <stdio.h>
01745 #include <stdlib.h>
01746 #include <sys/param.h>
01747 
01748 std::string Application::FindHomePath(const char* sCall)
01749 {
01750     // We have three ways to start this application either use one of the both executables or
01751     // import the FreeCAD.so module from a running Python session. In the latter case the
01752     // Python interpreter is already initialized.
01753     std::string absPath;
01754     std::string homePath;
01755     if (Py_IsInitialized()) {
01756         // Note: realpath is known to cause a buffer overflow because it
01757         // expands the given path to an absolute path of unknown length.
01758         // Even setting PATH_MAX does not necessarily solve the problem
01759         // for sure but the risk of overflow is rather small.
01760         char resolved[PATH_MAX];
01761         char* path = realpath(sCall, resolved);
01762         if (path)
01763             absPath = path;
01764     }
01765     else {
01766         // Find the path of the executable. Theoretically, there could  occur a
01767         // race condition when using readlink, but we only use  this method to
01768         // get the absolute path of the executable to compute the actual home
01769         // path. In the worst case we simply get q wrong path and FreeCAD is not
01770         // able to load its modules.
01771         char resolved[PATH_MAX];
01772         int nchars = readlink("/proc/self/exe", resolved, PATH_MAX);
01773         if (nchars < 0 || nchars >= PATH_MAX)
01774             throw Base::Exception("Cannot determine the absolute path of the executable");
01775         resolved[nchars] = '\0'; // enfore null termination
01776         absPath = resolved;
01777     }
01778 
01779     // should be an absolute path now
01780     std::string::size_type pos = absPath.find_last_of("/");
01781     homePath.assign(absPath,0,pos);
01782     pos = homePath.find_last_of("/");
01783     homePath.assign(homePath,0,pos+1);
01784 
01785     return homePath;
01786 }
01787 
01788 #elif defined(FC_OS_MACOSX)
01789 #include <mach-o/dyld.h>
01790 #include <string>
01791 #include <stdlib.h>
01792 #include <sys/param.h>
01793 
01794 std::string Application::FindHomePath(const char* call)
01795 {
01796     uint32_t sz = 0;
01797     char *buf;
01798 
01799     _NSGetExecutablePath(NULL, &sz); //function only returns "sz" if first arg is to small to hold value
01800     buf = (char*) malloc(++sz);
01801 
01802     if (_NSGetExecutablePath(buf, &sz) == 0) {
01803         char resolved[PATH_MAX];
01804         char* path = realpath(buf, resolved);
01805         free(buf);
01806 
01807         if (path) {
01808             std::string Call(resolved), TempHomePath;
01809             std::string::size_type pos = Call.find_last_of(PATHSEP);
01810             TempHomePath.assign(Call,0,pos);
01811             pos = TempHomePath.find_last_of(PATHSEP);
01812             TempHomePath.assign(TempHomePath,0,pos+1);
01813             return TempHomePath;
01814         }
01815     }
01816 
01817     return call; // error
01818 }
01819 
01820 #elif defined (FC_OS_WIN32)
01821 std::string Application::FindHomePath(const char* sCall)
01822 {
01823     // We have three ways to start this application either use one of the both executables or
01824     // import the FreeCAD.pyd module from a running Python session. In the latter case the
01825     // Python interpreter is already initialized.
01826     char  szFileName [MAX_PATH] ;
01827     if (Py_IsInitialized()) {
01828         GetModuleFileName(GetModuleHandle(sCall),szFileName, MAX_PATH-1);
01829     }
01830     else {
01831         GetModuleFileName(0, szFileName, MAX_PATH-1);
01832     }
01833 
01834     std::string Call(szFileName), TempHomePath;
01835     std::string::size_type pos = Call.find_last_of(PATHSEP);
01836     TempHomePath.assign(Call,0,pos);
01837     pos = TempHomePath.find_last_of(PATHSEP);
01838     TempHomePath.assign(TempHomePath,0,pos+1);
01839 
01840     // switch to posix style
01841     for (std::string::iterator i=TempHomePath.begin();i!=TempHomePath.end();++i) {
01842         if (*i == '\\')
01843             *i = '/';
01844     }
01845 
01846     return TempHomePath;
01847 }
01848 
01849 #else
01850 # error "std::string Application::FindHomePath(const char*) not implemented"
01851 #endif

Generated on Wed Nov 23 18:59:55 2011 for FreeCAD by  doxygen 1.6.1