Gui/Application.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) 2004 Jürgen Riegel <juergen.riegel@web.de>              *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00024 #include "PreCompiled.h"
00025 
00026 #ifndef _PreComp_
00027 # include "InventorAll.h"
00028 # include <boost/signals.hpp>
00029 # include <boost/bind.hpp>
00030 # include <sstream>
00031 # include <stdexcept>
00032 # include <QCloseEvent>
00033 # include <QLocale>
00034 # include <QMessageBox>
00035 # include <QPointer>
00036 # include <QGLFormat>
00037 # include <QGLPixelBuffer>
00038 #if QT_VERSION >= 0x040200
00039 # include <QGLFramebufferObject>
00040 #endif
00041 # include <QSessionManager>
00042 #endif
00043 
00044 
00045 // FreeCAD Base header
00046 #include <Base/Console.h>
00047 #include <Base/Interpreter.h>
00048 #include <Base/Parameter.h>
00049 #include <Base/Exception.h>
00050 #include <Base/Factory.h>
00051 #include <Base/FileInfo.h>
00052 #include <Base/Tools.h>
00053 #include <App/Document.h>
00054 #include <App/DocumentObjectPy.h>
00055 
00056 #include "Application.h"
00057 #include "GuiApplicationNativeEventAware.h"
00058 #include "MainWindow.h"
00059 #include "Document.h"
00060 #include "View.h"
00061 #include "View3DPy.h"
00062 #include "WidgetFactory.h"
00063 #include "Command.h"
00064 #include "Macro.h"
00065 #include "ProgressBar.h"
00066 #include "Workbench.h"
00067 #include "WorkbenchManager.h"
00068 #include "ToolBoxManager.h"
00069 #include "WaitCursor.h"
00070 #include "MenuManager.h"
00071 #include "Window.h"
00072 #include "Selection.h"
00073 #include "BitmapFactory.h"
00074 #include "SoFCDB.h"
00075 #include "PythonConsolePy.h"
00076 #include "PythonDebugger.h"
00077 #include "View3DPy.h"
00078 #include "DlgOnlineHelpImp.h"
00079 #include "SpaceballEvent.h"
00080 
00081 #include "View3DInventor.h"
00082 #include "ViewProvider.h"
00083 #include "ViewProviderExtern.h"
00084 #include "ViewProviderFeature.h"
00085 #include "ViewProviderPythonFeature.h"
00086 #include "ViewProviderDocumentObjectGroup.h"
00087 #include "ViewProviderGeometryObject.h"
00088 #include "ViewProviderInventorObject.h"
00089 #include "ViewProviderVRMLObject.h"
00090 #include "ViewProviderAnnotation.h"
00091 #include "ViewProviderMeasureDistance.h"
00092 
00093 #include "Language/Translator.h"
00094 #include "TaskView/TaskDialogPython.h"
00095 #include "GuiInitScript.h"
00096 
00097 
00098 using namespace Gui;
00099 using namespace Gui::DockWnd;
00100 using namespace std;
00101 
00102 
00103 Application* Application::Instance = 0L;
00104 
00105 namespace Gui {
00106 
00107 // Pimpl class
00108 struct ApplicationP
00109 {
00110     ApplicationP() : 
00111     activeDocument(0L), 
00112     isClosing(false), 
00113     startingUp(true)
00114     {
00115         // create the macro manager
00116         macroMngr = new MacroManager();
00117     }
00118 
00119     ~ApplicationP()
00120     {
00121         delete macroMngr;
00122     }
00123 
00125     std::map<const App::Document*, Gui::Document*> documents;
00127     Gui::Document*   activeDocument;
00128     MacroManager*  macroMngr;
00130     std::list<Gui::BaseView*> passive;
00131     bool isClosing;
00132     bool startingUp;
00134     CommandManager commandManager;
00135 };
00136 
00143 class ObjectLabelObserver
00144 {
00145 public:
00147     static ObjectLabelObserver* instance();
00149     static void destruct ();
00150 
00154     void slotRelabelObject(const App::DocumentObject&, const App::Property&);
00155 
00156 private:
00157     static ObjectLabelObserver* _singleton;
00158 
00159     ObjectLabelObserver();
00160     ~ObjectLabelObserver();
00161     const App::DocumentObject* current;
00162     ParameterGrp::handle _hPGrp;
00163 };
00164 
00165 ObjectLabelObserver* ObjectLabelObserver::_singleton = 0;
00166 
00167 ObjectLabelObserver* ObjectLabelObserver::instance()
00168 {
00169     if (!_singleton)
00170         _singleton = new ObjectLabelObserver;
00171     return _singleton;
00172 }
00173 
00174 void ObjectLabelObserver::destruct ()
00175 {
00176     delete _singleton;
00177     _singleton = 0;
00178 }
00179 
00180 void ObjectLabelObserver::slotRelabelObject(const App::DocumentObject& obj, const App::Property& prop)
00181 {
00182     // observe only the Label property
00183     if (&prop == &obj.Label) {
00184         // have we processed this (or another?) object right now?
00185         if (current) {
00186             return;
00187         }
00188 
00189         std::string label = obj.Label.getValue();
00190         App::Document* doc = obj.getDocument();
00191         if (doc && !_hPGrp->GetBool("DuplicateLabels")) {
00192             std::vector<std::string> objectLabels;
00193             std::vector<App::DocumentObject*>::const_iterator it;
00194             std::vector<App::DocumentObject*> objs = doc->getObjects();
00195             bool match = false;
00196 
00197             for (it = objs.begin();it != objs.end();++it) {
00198                 if (*it == &obj)
00199                     continue; // don't compare object with itself
00200                 std::string objLabel = (*it)->Label.getValue();
00201                 if (!match && objLabel == label)
00202                     match = true;
00203                 objectLabels.push_back(objLabel);
00204             }
00205 
00206             // make sure that there is a name conflict otherwise we don't have to do anything
00207             if (match) {
00208                 // remove number from end to avoid lengthy names
00209                 size_t lastpos = label.length()-1;
00210                 while (label[lastpos] >= 48 && label[lastpos] <= 57)
00211                     lastpos--;
00212                 label = label.substr(0, lastpos+1);
00213                 label = Base::Tools::getUniqueName(label, objectLabels, 3);
00214                 this->current = &obj;
00215                 const_cast<App::DocumentObject&>(obj).Label.setValue(label);
00216                 this->current = 0;
00217             }
00218         }
00219     }
00220 }
00221 
00222 ObjectLabelObserver::ObjectLabelObserver() : current(0)
00223 {
00224     App::GetApplication().signalChangedObject.connect(boost::bind
00225         (&ObjectLabelObserver::slotRelabelObject, this, _1, _2));
00226     _hPGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp");
00227     _hPGrp = _hPGrp->GetGroup("Preferences")->GetGroup("Document");
00228 }
00229 
00230 ObjectLabelObserver::~ObjectLabelObserver()
00231 {
00232 }
00233 
00234 static PyObject *
00235 FreeCADGui_subgraphFromObject(PyObject * /*self*/, PyObject *args)
00236 {
00237     PyObject *o;
00238     if (!PyArg_ParseTuple(args, "O!",&(App::DocumentObjectPy::Type), &o))
00239         return NULL;
00240     App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(o)->getDocumentObjectPtr();
00241     std::string vp = obj->getViewProviderName();
00242     SoNode* node = 0;
00243     try {
00244         Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(vp.c_str(), true));
00245         if (base && base->getTypeId().isDerivedFrom(Gui::ViewProviderDocumentObject::getClassTypeId())) {
00246             std::auto_ptr<Gui::ViewProviderDocumentObject> vp(static_cast<Gui::ViewProviderDocumentObject*>(base));
00247             std::map<std::string, App::Property*> Map;
00248             obj->getPropertyMap(Map);
00249             vp->attach(obj);
00250             for (std::map<std::string, App::Property*>::iterator it = Map.begin(); it != Map.end(); ++it) {
00251                 vp->updateData(it->second);
00252             }
00253 
00254             std::vector<std::string> modes = vp->getDisplayModes();
00255             if (!modes.empty())
00256                 vp->setDisplayMode(modes.front().c_str());
00257             node = vp->getRoot()->copy();
00258             node->ref();
00259             std::string type = "So";
00260             type += node->getTypeId().getName().getString();
00261             type += " *";
00262             PyObject* proxy = 0;
00263             proxy = Base::Interpreter().createSWIGPointerObj("pivy.coin", type.c_str(), (void*)node, 1);
00264             return Py::new_reference_to(Py::Object(proxy, true));
00265         }
00266     }
00267     catch (const Base::Exception& e) {
00268         if (node) node->unref();
00269         PyErr_SetString(PyExc_RuntimeError, e.what());
00270         return 0;
00271     }
00272 
00273     Py_INCREF(Py_None);
00274     return Py_None;
00275 }
00276 
00277 static PyObject *
00278 FreeCADGui_getSoDBVersion(PyObject * /*self*/, PyObject *args)
00279 {
00280     if (!PyArg_ParseTuple(args, ""))
00281         return NULL;
00282     return PyString_FromString(SoDB::getVersion());
00283 }
00284 
00285 static PyObject *
00286 FreeCADGui_getSoQtVersion(PyObject * /*self*/, PyObject *args)
00287 {
00288     if (!PyArg_ParseTuple(args, ""))
00289         return NULL;
00290     return PyString_FromString(SoQt::getVersionString());
00291 }
00292 
00293 struct PyMethodDef FreeCADGui_methods[] = {
00294     {"subgraphFromObject",FreeCADGui_subgraphFromObject,METH_VARARGS,
00295      "subgraphFromObject(object) -> Node\n\n"
00296      "Return the Inventor subgraph to an object"},
00297     {"getSoDBVersion",FreeCADGui_getSoDBVersion,METH_VARARGS,
00298      "getSoDBVersion() -> String\n\n"
00299      "Return a text string containing the name\n"
00300      "of the Coin library and version information"},
00301     {"getSoQtVersion",FreeCADGui_getSoQtVersion,METH_VARARGS,
00302      "getSoQtVersion() -> String\n\n"
00303      "Return a text string containing the name\n"
00304      "of the SoQt library and version information"},
00305     {NULL, NULL}  /* sentinel */
00306 };
00307 
00308 } // namespace Gui
00309 
00310 Application::Application(bool GUIenabled)
00311 {
00312     //App::GetApplication().Attach(this);
00313     if (GUIenabled) {
00314         App::GetApplication().signalNewDocument.connect(boost::bind(&Gui::Application::slotNewDocument, this, _1));
00315         App::GetApplication().signalDeleteDocument.connect(boost::bind(&Gui::Application::slotDeleteDocument, this, _1));
00316         App::GetApplication().signalRenameDocument.connect(boost::bind(&Gui::Application::slotRenameDocument, this, _1));
00317         App::GetApplication().signalActiveDocument.connect(boost::bind(&Gui::Application::slotActiveDocument, this, _1));
00318         App::GetApplication().signalRelabelDocument.connect(boost::bind(&Gui::Application::slotRelabelDocument, this, _1));
00319 
00320 
00321         // install the last active language
00322         ParameterGrp::handle hPGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp");
00323         hPGrp = hPGrp->GetGroup("Preferences")->GetGroup("General");
00324         QString lang = QLocale::languageToString(QLocale::system().language());
00325         Translator::instance()->activateLanguage(hPGrp->GetASCII("Language", (const char*)lang.toAscii()).c_str());
00326         GetWidgetFactorySupplier();
00327 
00328         // setting up Python binding
00329         Base::PyGILStateLocker lock;
00330         PyObject* module = Py_InitModule3("FreeCADGui", Application::Methods,
00331             "The functions in the FreeCADGui module allow working with GUI documents,\n"
00332             "view providers, views, workbenches and much more.\n\n"
00333             "The FreeCADGui instance provides a list of references of GUI documents which\n"
00334             "can be addressed by a string. These documents contain the view providers for\n"
00335             "objects in the associated App document. An App and GUI document can be\n"
00336             "accessed with the same name.\n\n"
00337             "The FreeCADGui module also provides a set of functions to work with so called\n"
00338             "workbenches.");
00339         Py::Module(module).setAttr(std::string("ActiveDocument"),Py::None());
00340 
00341         //insert Selection module
00342         PyObject* pSelectionModule = Py_InitModule3("Selection", SelectionSingleton::Methods,
00343             "Selection module");
00344         Py_INCREF(pSelectionModule);
00345         PyModule_AddObject(module, "Selection", pSelectionModule);
00346 
00347         SelectionFilterPy::init_type();
00348         Base::Interpreter().addType(SelectionFilterPy::type_object(),
00349             pSelectionModule,"Filter");
00350 
00351         Gui::TaskView::ControlPy::init_type();
00352         Py::Module(module).setAttr(std::string("Control"),
00353             Py::Object(Gui::TaskView::ControlPy::getInstance(), true));
00354     }
00355 
00356     Base::PyGILStateLocker lock;
00357     PyObject *module = PyImport_AddModule("FreeCADGui");
00358     PyMethodDef *meth = FreeCADGui_methods;
00359     PyObject *dict = PyModule_GetDict(module);
00360     for (; meth->ml_name != NULL; meth++) {
00361         PyObject *descr;
00362         descr = PyCFunction_NewEx(meth,0,0);
00363         if (descr == NULL)
00364             break;
00365         if (PyDict_SetItemString(dict, meth->ml_name, descr) != 0)
00366             break;
00367         Py_DECREF(descr);
00368     }
00369 
00370     // Python console binding
00371     PythonDebugModule   ::init_module();
00372     PythonStdout        ::init_type();
00373     PythonStderr        ::init_type();
00374     OutputStdout        ::init_type();
00375     OutputStderr        ::init_type();
00376     PythonStdin         ::init_type();
00377     View3DInventorPy    ::init_type();
00378 
00379     d = new ApplicationP;
00380 
00381     // global access 
00382     Instance = this;
00383 
00384     // instanciate the workbench dictionary
00385     _pcWorkbenchDictionary = PyDict_New();
00386 
00387     createStandardOperations();
00388     MacroCommand::load();
00389     ObjectLabelObserver::instance();
00390 }
00391 
00392 Application::~Application()
00393 {
00394     Base::Console().Log("Destruct Gui::Application\n");
00395     WorkbenchManager::destruct();
00396     SelectionSingleton::destruct();
00397     Translator::destruct();
00398     WidgetFactorySupplier::destruct();
00399     BitmapFactoryInst::destruct();
00400 
00401 #if 0
00402     // we must run the garbage collector before shutting down the SoDB or SoQt 
00403     // subsystem because we may reference some class objects of them in Python
00404     Base::Interpreter().cleanupSWIG("SoBase *");
00405     // finish also Inventor subsystem
00406     SoFCDB::finish();
00407     SoQt::done();
00408 
00409 #if (COIN_MAJOR_VERSION >= 2) && (COIN_MINOR_VERSION >= 4)
00410     SoDB::finish();
00411 #elif (COIN_MAJOR_VERSION >= 3)
00412     SoDB::finish();
00413 #else
00414     SoDB::cleanup();
00415 #endif
00416 #endif
00417     {
00418     Base::PyGILStateLocker lock;
00419     Py_DECREF(_pcWorkbenchDictionary);
00420     }
00421 
00422     // save macros
00423     MacroCommand::save();
00424     //App::GetApplication().Detach(this);
00425 
00426     delete d;
00427     Instance = 0;
00428 }
00429 
00430 
00431 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00432 // creating std commands
00433 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00434 
00435 void Application::open(const char* FileName, const char* Module)
00436 {
00437     WaitCursor wc;
00438     Base::FileInfo File(FileName);
00439     string te = File.extension();
00440 
00441     // if the active document is empty and not modified, close it
00442     // in case of an automatically created empty document at startup
00443     App::Document* act = App::GetApplication().getActiveDocument();
00444     Gui::Document* gui = this->getDocument(act);
00445     if (act && act->countObjects() == 0 && gui && gui->isModified() == false){
00446         Command::doCommand(Command::App, "App.closeDocument('%s')", act->getName());
00447         qApp->processEvents(); // an update is needed otherwise the new view isn't shown
00448     }
00449 
00450     if (Module != 0) {
00451         // issue module loading
00452         Command::doCommand(Command::App, "import %s", Module);
00453         try {
00454             // load the file with the module
00455             Command::doCommand(Command::App, "%s.open(\"%s\")", Module, File.filePath().c_str());
00456             // ViewFit
00457             if (!File.hasExtension("FCStd") && sendHasMsgToActiveView("ViewFit"))
00458                 //Command::doCommand(Command::Gui, "Gui.activeDocument().activeView().fitAll()");
00459                 Command::doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewFit\")");
00460             // the original file name is required
00461             getMainWindow()->appendRecentFile(QString::fromUtf8(File.filePath().c_str()));
00462         }
00463         catch (const Base::PyException& e){
00464             // Usually thrown if the file is invalid somehow
00465             e.ReportException();
00466         }
00467     }
00468     else {
00469         wc.restoreCursor();
00470         QMessageBox::warning(getMainWindow(), QObject::tr("Unknown filetype"),
00471             QObject::tr("Cannot open unknown filetype: %1").arg(QLatin1String(te.c_str())));
00472         wc.setWaitCursor();
00473         return;
00474     }
00475 }
00476 
00477 void Application::importFrom(const char* FileName, const char* DocName, const char* Module)
00478 {
00479     WaitCursor wc;
00480     Base::FileInfo File(FileName);
00481     std::string te = File.extension();
00482 
00483     if (Module != 0) {
00484         // issue module loading
00485         Command::doCommand(Command::App, "import %s", Module);
00486 
00487         try {
00488             // load the file with the module
00489             if (File.hasExtension("FCStd")) {
00490                 Command::doCommand(Command::App, "%s.open(\"%s\")"
00491                                                , Module, File.filePath().c_str());
00492                 if (activeDocument())
00493                     activeDocument()->setModified(false);
00494             }
00495             else {
00496                 Command::doCommand(Command::App, "%s.insert(\"%s\",\"%s\")"
00497                                                , Module, File.filePath().c_str(), DocName);
00498                 Command::doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewFit\")");
00499                 if (getDocument(DocName))
00500                     getDocument(DocName)->setModified(true);
00501             }
00502 
00503             // the original file name is required
00504             getMainWindow()->appendRecentFile(QString::fromUtf8(File.filePath().c_str()));
00505         }
00506         catch (const Base::PyException& e){
00507             // Usually thrown if the file is invalid somehow
00508             e.ReportException();
00509         }
00510     }
00511     else {
00512         wc.restoreCursor();
00513         QMessageBox::warning(getMainWindow(), QObject::tr("Unknown filetype"),
00514             QObject::tr("Cannot open unknown filetype: %1").arg(QLatin1String(te.c_str())));
00515         wc.setWaitCursor();
00516     }
00517 }
00518 
00519 void Application::exportTo(const char* FileName, const char* DocName, const char* Module)
00520 {
00521     WaitCursor wc;
00522     Base::FileInfo File(FileName);
00523     std::string te = File.extension();
00524 
00525     if (Module != 0) {
00526         try {
00527             std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType
00528                 (App::DocumentObject::getClassTypeId(),DocName);
00529             if (sel.empty()) {
00530                 App::Document* doc = App::GetApplication().getDocument(DocName);
00531                 sel = doc->getObjectsOfType(App::DocumentObject::getClassTypeId());
00532             }
00533 
00534             std::stringstream str;
00535             str << "__objs__=[]" << std::endl;
00536             for (std::vector<App::DocumentObject*>::iterator it = sel.begin(); it != sel.end(); ++it) {
00537                 str << "__objs__.append(FreeCAD.getDocument(\"" << DocName << "\").getObject(\""
00538                     << (*it)->getNameInDocument() << "\"))" << std::endl;
00539             }
00540 
00541             str << "import " << Module << std::endl;
00542             str << Module << ".export(__objs__,\"" << File.filePath() << "\")" << std::endl;
00543             str << "del __objs__" << std::endl;
00544 
00545             std::string code = str.str();
00546             // the original file name is required
00547             if (runPythonCode(code.c_str(), false))
00548                 getMainWindow()->appendRecentFile(QString::fromUtf8(File.filePath().c_str()));
00549         }
00550         catch (const Base::PyException& e){
00551             // Usually thrown if the file is invalid somehow
00552             e.ReportException();
00553         }
00554     }
00555     else {
00556         wc.restoreCursor();
00557         QMessageBox::warning(getMainWindow(), QObject::tr("Unknown filetype"),
00558             QObject::tr("Cannot save to unknown filetype: %1").arg(QLatin1String(te.c_str())));
00559         wc.setWaitCursor();
00560     }
00561 }
00562 
00563 void Application::createStandardOperations()
00564 {
00565     // register the application Standard commands from CommandStd.cpp
00566     Gui::CreateStdCommands();
00567     Gui::CreateDocCommands();
00568     Gui::CreateFeatCommands();
00569     Gui::CreateMacroCommands();
00570     Gui::CreateViewStdCommands();
00571     Gui::CreateWindowStdCommands();
00572     Gui::CreateTestCommands();
00573 }
00574 
00575 void Application::slotNewDocument(const App::Document& Doc)
00576 {
00577 #ifdef FC_DEBUG
00578     std::map<const App::Document*, Gui::Document*>::const_iterator it = d->documents.find(&Doc);
00579     assert(it==d->documents.end());
00580 #endif
00581     Gui::Document* pDoc = new Gui::Document(const_cast<App::Document*>(&Doc),this);
00582     d->documents[&Doc] = pDoc;
00583 
00584     // connect the signals to the application for the new document
00585     pDoc->signalNewObject.connect(boost::bind(&Gui::Application::slotNewObject, this, _1));
00586     pDoc->signalDeletedObject.connect(boost::bind(&Gui::Application::slotDeletedObject, this, _1));
00587     pDoc->signalChangedObject.connect(boost::bind(&Gui::Application::slotChangedObject, this, _1, _2));
00588     pDoc->signalRenamedObject.connect(boost::bind(&Gui::Application::slotRenamedObject, this, _1));
00589     pDoc->signalActivatedObject.connect(boost::bind(&Gui::Application::slotActivatedObject, this, _1));
00590 
00591 
00592     signalNewDocument(*pDoc);
00593     pDoc->createView("View3DIv");
00594     qApp->processEvents(); // make sure to show the window stuff on the right place
00595 }
00596 
00597 void Application::slotDeleteDocument(const App::Document& Doc)
00598 {
00599     std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
00600     if (doc == d->documents.end()) {
00601         Base::Console().Log("GUI document '%s' already deleted\n", Doc.getName());
00602         return;
00603     }
00604 
00605     // We must clear the selection here to notify all observers
00606     Gui::Selection().clearSelection(doc->second->getDocument()->getName());
00607     signalDeleteDocument(*doc->second);
00608 
00609     // If the active document gets destructed we must set it to 0. If there are further existing documents then the 
00610     // view that becomes active sets the active document again. So, we needn't worry about this.
00611     if (d->activeDocument == doc->second)
00612         setActiveDocument(0);
00613 
00614     // For exception-safety use a smart pointer
00615     auto_ptr<Document> delDoc (doc->second);
00616     d->documents.erase(doc);
00617 }
00618 
00619 void Application::slotRelabelDocument(const App::Document& Doc)
00620 {
00621     std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
00622 #ifdef FC_DEBUG
00623     assert(doc!=d->documents.end());
00624 #endif
00625 
00626     signalRelabelDocument(*doc->second);
00627     doc->second->onRelabel();
00628 }
00629 
00630 void Application::slotRenameDocument(const App::Document& Doc)
00631 {
00632     std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
00633 #ifdef FC_DEBUG
00634     assert(doc!=d->documents.end());
00635 #endif
00636 
00637     signalRenameDocument(*doc->second);
00638 }
00639 
00640 void Application::slotActiveDocument(const App::Document& Doc)
00641 {
00642     std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
00643     // this can happen when closing a document with two views opened
00644     if (doc != d->documents.end())
00645         signalActiveDocument(*doc->second);
00646 }
00647 
00648 void Application::slotNewObject(const ViewProvider& vp)
00649 {
00650     this->signalNewObject(vp);
00651 }
00652 
00653 void Application::slotDeletedObject(const ViewProvider& vp)
00654 {
00655     this->signalDeletedObject(vp);
00656 }
00657 
00658 void Application::slotChangedObject(const ViewProvider& vp, const App::Property& prop)
00659 {
00660     this->signalChangedObject(vp,prop);
00661 }
00662 
00663 void Application::slotRenamedObject(const ViewProvider& vp)
00664 {
00665     this->signalRenamedObject(vp);
00666 }
00667 
00668 void Application::slotActivatedObject(const ViewProvider& vp)
00669 {
00670     this->signalActivatedObject(vp);
00671 }
00672 
00673 void Application::onLastWindowClosed(Gui::Document* pcDoc)
00674 {
00675     if (!d->isClosing && pcDoc) {
00676         try {
00677             // Call the closing mechanism from Python. This also checks whether pcDoc is the last open document.
00678             Command::doCommand(Command::Doc, "App.closeDocument(\"%s\")", pcDoc->getDocument()->getName());
00679         }
00680         catch (const Base::PyException& e) {
00681             e.ReportException();
00682         }
00683     }
00684 }
00685 
00687 bool Application::sendMsgToActiveView(const char* pMsg, const char** ppReturn)
00688 {
00689     MDIView* pView = getMainWindow()->activeWindow();
00690     return pView ? pView->onMsg(pMsg,ppReturn) : false;
00691 }
00692 
00693 bool Application::sendHasMsgToActiveView(const char* pMsg)
00694 {
00695     MDIView* pView = getMainWindow()->activeWindow();
00696     return pView ? pView->onHasMsg(pMsg) : false;
00697 }
00698 
00700 Gui::Document* Application::activeDocument(void) const
00701 {
00702     return d->activeDocument;
00703 }
00704 
00705 void Application::setActiveDocument(Gui::Document* pcDocument)
00706 {
00707     if (d->activeDocument == pcDocument)
00708         return; // nothing needs to be done
00709     d->activeDocument = pcDocument;
00710     std::string name;
00711  
00712     // This adds just a line to the macro file but does not set the active document
00713     if (pcDocument){
00714         name += "App.setActiveDocument(\"";
00715         name += pcDocument->getDocument()->getName(); 
00716         name +=  "\")\n";
00717         name += "App.ActiveDocument=App.getDocument(\"";
00718         name += pcDocument->getDocument()->getName(); 
00719         name +=  "\")\n";
00720         name += "Gui.ActiveDocument=Gui.getDocument(\"";
00721         name += pcDocument->getDocument()->getName(); 
00722         name +=  "\")";
00723         macroManager()->addLine(MacroManager::Gui,name.c_str());
00724     }
00725     else {
00726         name += "App.setActiveDocument(\"\")\n";
00727         name += "App.ActiveDocument=None\n";
00728         name += "Gui.ActiveDocument=None";
00729         macroManager()->addLine(MacroManager::Gui,name.c_str());
00730     }
00731 
00732     // Sets the currently active document
00733     try {
00734         Base::Interpreter().runString(name.c_str());
00735     }
00736     catch (const Base::Exception& e) {
00737         Base::Console().Warning(e.what());
00738         return;
00739     }
00740 
00741     // May be useful for error detection
00742     if (d->activeDocument) {
00743         App::Document* doc = d->activeDocument->getDocument();
00744         Base::Console().Log("Active document is %s (at %p)\n",doc->getName(), doc);
00745     }
00746     else {
00747         Base::Console().Log("No active document\n");
00748     }
00749 
00750     // notify all views attached to the application (not views belong to a special document)
00751     for(list<Gui::BaseView*>::iterator It=d->passive.begin();It!=d->passive.end();It++)
00752         (*It)->setDocument(pcDocument);
00753 }
00754 
00755 Gui::Document* Application::getDocument(const char* name) const
00756 {
00757     App::Document* pDoc = App::GetApplication().getDocument( name );
00758     std::map<const App::Document*, Gui::Document*>::const_iterator it = d->documents.find(pDoc);
00759     if ( it!=d->documents.end() )
00760         return it->second;
00761     else
00762         return 0;
00763 }
00764 
00765 Gui::Document* Application::getDocument(const App::Document* pDoc) const
00766 {
00767     std::map<const App::Document*, Gui::Document*>::const_iterator it = d->documents.find(pDoc);
00768     if ( it!=d->documents.end() )
00769         return it->second;
00770     else
00771         return 0;
00772 }
00773 
00774 void Application::showViewProvider(App::DocumentObject* obj)
00775 {
00776     ViewProvider* vp = getViewProvider(obj);
00777     if (vp) vp->show();
00778 }
00779 
00780 void Application::hideViewProvider(App::DocumentObject* obj)
00781 {
00782     ViewProvider* vp = getViewProvider(obj);
00783     if (vp) vp->hide();
00784 }
00785 
00786 Gui::ViewProvider* Application::getViewProvider(App::DocumentObject* obj) const
00787 {
00788     App::Document* doc = obj->getDocument();
00789     if (doc) {
00790         Gui::Document* gui = getDocument(doc);
00791         if (gui) {
00792             ViewProvider* vp = gui->getViewProvider(obj);
00793             return vp;
00794         }
00795     }
00796 
00797     return 0;
00798 }
00799 
00800 void Application::attachView(Gui::BaseView* pcView)
00801 {
00802     d->passive.push_back(pcView);
00803 }
00804 
00805 void Application::detachView(Gui::BaseView* pcView)
00806 {
00807     d->passive.remove(pcView);
00808 }
00809 
00810 void Application::onUpdate(void)
00811 {
00812     // update all documents
00813     std::map<const App::Document*, Gui::Document*>::iterator It;
00814     for (It = d->documents.begin();It != d->documents.end();It++)
00815         It->second->onUpdate();
00816     // update all the independed views
00817     for (std::list<Gui::BaseView*>::iterator It2 = d->passive.begin();It2 != d->passive.end();It2++)
00818         (*It2)->onUpdate();
00819 }
00820 
00822 void Application::viewActivated(MDIView* pcView)
00823 {
00824     // May be useful for error detection
00825     Base::Console().Log("Active view is %s (at %p)\n",
00826                  (const char*)pcView->windowTitle().toUtf8(),pcView);
00827 
00828     signalActivateView(pcView);
00829 
00830     // Set the new active document which is taken of the activated view. If, however,
00831     // this view is passive we let the currently active document unchanged as we would
00832     // have no document active which is causing a lot of trouble.
00833     if (!pcView->isPassive())
00834         setActiveDocument(pcView->getGuiDocument());
00835 }
00836 
00837 
00838 void Application::updateActive(void)
00839 {
00840     activeDocument()->onUpdate();
00841 }
00842 
00843 void Application::tryClose(QCloseEvent * e)
00844 {
00845     if (d->documents.size() == 0) {
00846         e->accept();
00847     }
00848     else {
00849         // ask all documents if closable
00850         std::map<const App::Document*, Gui::Document*>::iterator It;
00851         for (It = d->documents.begin();It!=d->documents.end();It++) {
00852             MDIView* active = It->second->getActiveView();
00853             e->setAccepted(active->canClose());
00854             if (!e->isAccepted())
00855                 return;
00856         }
00857     }
00858 
00859     // ask all passive views if closable
00860     for (std::list<Gui::BaseView*>::iterator It = d->passive.begin();It!=d->passive.end();It++) {
00861         e->setAccepted((*It)->canClose());
00862         if (!e->isAccepted())
00863             return;
00864     }
00865 
00866     if (e->isAccepted()) {
00867         d->isClosing = true;
00868 
00869         std::map<const App::Document*, Gui::Document*>::iterator It;
00870 
00871         //detach the passive views
00872         //SetActiveDocument(0);
00873         std::list<Gui::BaseView*>::iterator itp = d->passive.begin();
00874         while (itp != d->passive.end()) {
00875             (*itp)->onClose();
00876             itp = d->passive.begin();
00877         }
00878 
00879         // remove all documents
00880         size_t cnt = d->documents.size();
00881         while (d->documents.size() > 0 && cnt > 0) {
00882             // destroys also the Gui document
00883             It = d->documents.begin();
00884             App::GetApplication().closeDocument(It->second->getDocument()->getName());
00885             --cnt; // avoid infinite loop
00886         }
00887     }
00888 }
00889 
00899 bool Application::activateWorkbench(const char* name)
00900 {
00901     bool ok = false;
00902     WaitCursor wc;
00903     Workbench* oldWb = WorkbenchManager::instance()->active();
00904     if (oldWb && oldWb->name() == name)
00905         return false; // already active
00906 
00907     // we check for the currently active workbench and call its 'Deactivated'
00908     // method, if available
00909     PyObject* pcOldWorkbench = 0;
00910     if (oldWb) {
00911         pcOldWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, oldWb->name().c_str());
00912     }
00913 
00914     // get the python workbench object from the dictionary
00915     Base::PyGILStateLocker lock;
00916     PyObject* pcWorkbench = 0;
00917     pcWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, name);
00918     // test if the workbench exists
00919     if (!pcWorkbench)
00920         return false;
00921 
00922     try {
00923         std::string type;
00924         Py::Object handler(pcWorkbench);
00925         if (!handler.hasAttr(std::string("__Workbench__"))) {
00926             // call its GetClassName method if possible
00927             Py::Callable method(handler.getAttr(std::string("GetClassName")));
00928             Py::Tuple args;
00929             Py::String result(method.apply(args));
00930             type = result.as_std_string();
00931             if (type == "Gui::PythonWorkbench") {
00932                 Workbench* wb = WorkbenchManager::instance()->createWorkbench(name, type);
00933                 handler.setAttr(std::string("__Workbench__"), Py::Object(wb->getPyObject(), true));
00934             }
00935 
00936             // import the matching module first
00937             Py::Callable activate(handler.getAttr(std::string("Initialize")));
00938             activate.apply(args);
00939         }
00940 
00941         // does the Python workbench handler have changed the workbench?
00942         Workbench* curWb = WorkbenchManager::instance()->active();
00943         if (curWb && curWb->name() == name)
00944             ok = true; // already active
00945         // now try to create and activate the matching workbench object
00946         else if (WorkbenchManager::instance()->activate(name, type)) {
00947             getMainWindow()->activateWorkbench(QString::fromAscii(name));
00948             this->signalActivateWorkbench(name);
00949             ok = true;
00950         }
00951 
00952         // if we still not have this member then it must be built-in C++ workbench
00953         // which could be created after loading the appropriate module
00954         if (!handler.hasAttr(std::string("__Workbench__"))) {
00955             Workbench* wb = WorkbenchManager::instance()->getWorkbench(name);
00956             if (wb) handler.setAttr(std::string("__Workbench__"), Py::Object(wb->getPyObject(), true));
00957         }
00958 
00959         // If the method Deactivate is available we call it
00960         if (pcOldWorkbench) {
00961             Py::Object handler(pcOldWorkbench);
00962             if (handler.hasAttr(std::string("Deactivated"))) {
00963                 Py::Object method(handler.getAttr(std::string("Deactivated")));
00964                 if (method.isCallable()) {
00965                     Py::Tuple args;
00966                     Py::Callable activate(method);
00967                     activate.apply(args);
00968                 }
00969             }
00970         }
00971 
00972         if (oldWb)
00973             oldWb->deactivated();
00974 
00975         // If the method Activate is available we call it
00976         if (handler.hasAttr(std::string("Activated"))) {
00977             Py::Object method(handler.getAttr(std::string("Activated")));
00978             if (method.isCallable()) {
00979                 Py::Tuple args;
00980                 Py::Callable activate(method);
00981                 activate.apply(args);
00982             }
00983         }
00984 
00985         // now get the newly activated workbench
00986         Workbench* newWb = WorkbenchManager::instance()->active();
00987         if (newWb)
00988             newWb->activated();
00989     }
00990     catch (Py::Exception&) {
00991         Base::PyException e; // extract the Python error text
00992         QString msg = QString::fromAscii(e.what());
00993         QRegExp rx;
00994         // ignore '<type 'exceptions.ImportError'>' prefixes
00995         rx.setPattern(QLatin1String("^\\s*<type 'exceptions.ImportError'>:\\s*"));
00996         int pos = rx.indexIn(msg);
00997         while ( pos != -1 ) {
00998             msg = msg.mid(rx.matchedLength());
00999             pos = rx.indexIn(msg);
01000         }
01001 
01002         Base::Console().Error("%s\n", (const char*)msg.toAscii());
01003         Base::Console().Log("%s\n", e.getStackTrace().c_str());
01004         if (!d->startingUp) {
01005             wc.restoreCursor();
01006             QMessageBox::critical(getMainWindow(), QObject::tr("Workbench failure"), 
01007                 QObject::tr("%1").arg(msg));
01008             wc.setWaitCursor();
01009         }
01010     }
01011 
01012     return ok;
01013 }
01014 
01015 QPixmap Application::workbenchIcon(const QString& wb) const
01016 {
01017     Base::PyGILStateLocker lock;
01018     // get the python workbench object from the dictionary
01019     PyObject* pcWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, wb.toAscii());
01020     // test if the workbench exists
01021     if (pcWorkbench) {
01022         // make a unique icon name
01023         std::stringstream str;
01024         str << static_cast<const void *>(pcWorkbench) << std::ends;
01025         std::string iconName = str.str();
01026         QPixmap icon;
01027         if (BitmapFactory().findPixmapInCache(iconName.c_str(), icon))
01028             return icon;
01029 
01030         // get its Icon member if possible
01031         try {
01032             Py::Object handler(pcWorkbench);
01033             Py::Object member = handler.getAttr(std::string("Icon"));
01034             Py::String data(member);
01035             std::string content = data.as_std_string();
01036 
01037             // test if in XPM format
01038             QByteArray ary;
01039             int strlen = (int)content.size();
01040             ary.resize(strlen);
01041             for (int j=0; j<strlen; j++)
01042                 ary[j]=content[j];
01043             if (ary.indexOf("/* XPM */") > 0) {
01044                 // Make sure to remove crap around the XPM data
01045                 QList<QByteArray> lines = ary.split('\n');
01046                 QByteArray buffer;
01047                 buffer.reserve(ary.size()+lines.size());
01048                 for (QList<QByteArray>::iterator it = lines.begin(); it != lines.end(); ++it) {
01049                     QByteArray trim = it->trimmed();
01050                     if (!trim.isEmpty()) {
01051                         buffer.append(trim);
01052                         buffer.append('\n');
01053                     }
01054                 }
01055                 icon.loadFromData(buffer, "XPM");
01056             }
01057             else {
01058                 // is it a file name...
01059                 QString file = QString::fromUtf8(content.c_str());
01060                 icon.load(file);
01061                 if (icon.isNull()) {
01062                     // ... or the name of another icon?
01063                     icon = BitmapFactory().pixmap(file.toUtf8());
01064                 }
01065             }
01066 
01067             if (!icon.isNull()) {
01068                 BitmapFactory().addPixmapToCache(iconName.c_str(), icon);
01069             }
01070 
01071             return icon;
01072         }
01073         catch (Py::Exception& e) {
01074             e.clear();
01075         }
01076     }
01077 
01078     return QPixmap();
01079 }
01080 
01081 QString Application::workbenchToolTip(const QString& wb) const
01082 {
01083     // get the python workbench object from the dictionary
01084     Base::PyGILStateLocker lock;
01085     PyObject* pcWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, wb.toAscii());
01086     // test if the workbench exists
01087     if (pcWorkbench) {
01088         // get its ToolTip member if possible
01089         try {
01090             Py::Object handler(pcWorkbench);
01091             Py::Object member = handler.getAttr(std::string("ToolTip"));
01092             if (member.isString()) {
01093                 Py::String tip(member);
01094                 return QString::fromUtf8(tip.as_std_string().c_str());
01095             }
01096         }
01097         catch (Py::Exception& e) {
01098             e.clear();
01099         }
01100     }
01101 
01102     return QString();
01103 }
01104 
01105 QString Application::workbenchMenuText(const QString& wb) const
01106 {
01107     // get the python workbench object from the dictionary
01108     Base::PyGILStateLocker lock;
01109     PyObject* pcWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, wb.toAscii());
01110     // test if the workbench exists
01111     if (pcWorkbench) {
01112         // get its ToolTip member if possible
01113         Base::PyGILStateLocker locker;
01114         try {
01115             Py::Object handler(pcWorkbench);
01116             Py::Object member = handler.getAttr(std::string("MenuText"));
01117             if (member.isString()) {
01118                 Py::String tip(member);
01119                 return QString::fromUtf8(tip.as_std_string().c_str());
01120             }
01121         }
01122         catch (Py::Exception& e) {
01123             e.clear();
01124         }
01125     }
01126 
01127     return QString();
01128 }
01129 
01130 QStringList Application::workbenches(void) const
01131 {
01132     // If neither 'HiddenWorkbench' nor 'ExtraWorkbench' is set then all workbenches are returned.
01133     const std::map<std::string,std::string>& config = App::Application::Config();
01134     std::map<std::string, std::string>::const_iterator ht = config.find("HiddenWorkbench");
01135     std::map<std::string, std::string>::const_iterator et = config.find("ExtraWorkbench");
01136     std::map<std::string, std::string>::const_iterator st = config.find("StartWorkbench");
01137     const char* start = (st != config.end() ? st->second.c_str() : "<none>");
01138     QStringList hidden, extra;
01139     if (ht != config.end()) { 
01140         QString items = QString::fromAscii(ht->second.c_str());
01141         hidden = items.split(QLatin1Char(';'), QString::SkipEmptyParts);
01142         if (hidden.isEmpty())
01143             hidden.push_back(QLatin1String(""));
01144     }
01145     if (et != config.end()) { 
01146         QString items = QString::fromAscii(et->second.c_str());
01147         extra = items.split(QLatin1Char(';'), QString::SkipEmptyParts);
01148         if (extra.isEmpty())
01149             extra.push_back(QLatin1String(""));
01150     }
01151 
01152     PyObject *key, *value;
01153     Py_ssize_t pos = 0;
01154     QStringList wb;
01155     // insert all items
01156     while (PyDict_Next(_pcWorkbenchDictionary, &pos, &key, &value)) {
01157         /* do something interesting with the values... */
01158         const char* wbName = PyString_AsString(key);
01159         // add only allowed workbenches
01160         bool ok = true;
01161         if (!extra.isEmpty()&&ok) {
01162             ok = (extra.indexOf(QString::fromAscii(wbName)) != -1);
01163         }
01164         if (!hidden.isEmpty()&&ok) {
01165             ok = (hidden.indexOf(QString::fromAscii(wbName)) == -1);
01166         }
01167     
01168         // okay the item is visible
01169         if (ok)
01170             wb.push_back(QString::fromAscii(wbName));
01171         // also allow start workbench in case it is hidden
01172         else if (strcmp(wbName, start) == 0)
01173             wb.push_back(QString::fromAscii(wbName));
01174     }
01175 
01176     return wb;
01177 }
01178 
01179 void Application::setupContextMenu(const char* recipient, MenuItem* items) const
01180 {
01181     Workbench* actWb = WorkbenchManager::instance()->active();
01182     if (actWb) {
01183         // when populating the context-menu of a Python workbench invoke the method 
01184         // 'ContextMenu' of the handler object
01185         if (actWb->getTypeId().isDerivedFrom(PythonWorkbench::getClassTypeId())) {
01186             static_cast<PythonWorkbench*>(actWb)->clearContextMenu();
01187             Base::PyGILStateLocker lock;
01188             PyObject* pWorkbench = 0;
01189             pWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, actWb->name().c_str());
01190 
01191             try {
01192                 // call its GetClassName method if possible
01193                 Py::Object handler(pWorkbench);
01194                 Py::Callable method(handler.getAttr(std::string("ContextMenu")));
01195                 Py::Tuple args(1);
01196                 args.setItem(0, Py::String(recipient));
01197                 method.apply(args);
01198             }
01199             catch (Py::Exception& e) {
01200                 Py::Object o = Py::type(e);
01201                 e.clear();
01202                 if (o.isString()) {
01203                     Py::String s(o);
01204                     std::clog << "Application::setupContextMenu: " << s.as_std_string() << std::endl;
01205                 }
01206             }
01207         }
01208         actWb->setupContextMenu(recipient, items);
01209     }
01210 }
01211 
01212 bool Application::isClosing(void)
01213 {
01214     return d->isClosing;
01215 }
01216 
01217 MacroManager *Application::macroManager(void)
01218 {
01219     return d->macroMngr;
01220 }
01221 
01222 CommandManager &Application::commandManager(void)
01223 {
01224     return d->commandManager;
01225 }
01226 
01227 void Application::runCommand(bool bForce, const char* sCmd,...)
01228 {
01229     // temp buffer
01230     size_t format_len = std::strlen(sCmd)+4024;
01231     char* format = (char*) malloc(format_len);
01232     va_list namelessVars;
01233     va_start(namelessVars, sCmd);  // Get the "..." vars
01234     vsnprintf(format, format_len, sCmd, namelessVars);
01235     va_end(namelessVars);
01236 
01237     if (bForce)
01238         d->macroMngr->addLine(MacroManager::Base,format);
01239     else
01240         d->macroMngr->addLine(MacroManager::Gui,format);
01241 
01242     try { 
01243         Base::Interpreter().runString(format);
01244     }
01245     catch (...) {
01246         // free memory to avoid a leak if an exception occurred
01247         free (format);
01248         throw;
01249     }
01250 
01251     free (format);
01252 }
01253 
01254 bool Application::runPythonCode(const char* cmd, bool gui, bool pyexc)
01255 {
01256     if (gui)
01257         d->macroMngr->addLine(MacroManager::Gui,cmd);
01258     else
01259         d->macroMngr->addLine(MacroManager::Base,cmd);
01260 
01261     try {
01262         Base::Interpreter().runString(cmd);
01263         return true;
01264     }
01265     catch (Base::PyException &e) {
01266         if (pyexc) {
01267             e.ReportException();
01268             Base::Console().Error("Stack Trace: %s\n",e.getStackTrace().c_str());
01269         }
01270         else {
01271             throw; // re-throw to handle in calling instance
01272         }
01273     }
01274     catch (Base::AbortException&) {
01275     }
01276     catch (Base::Exception &e) {
01277         e.ReportException();
01278     }
01279     catch (std::exception &e) {
01280         std::string str;
01281         str += "C++ exception thrown (";
01282         str += e.what();
01283         str += ")";
01284         Base::Console().Error(str.c_str());
01285     }
01286     catch (const char* e) {
01287         Base::Console().Error("%s\n", e);
01288     }
01289 #ifndef FC_DEBUG
01290     catch (...) {
01291         Base::Console().Error("Unknown C++ exception in command thrown\n");
01292     }
01293 #endif
01294     return false;
01295 }
01296 
01297 //**************************************************************************
01298 // Init, Destruct and ingleton
01299 
01300 typedef void (*_qt_msg_handler_old)(QtMsgType type, const char *msg);
01301 _qt_msg_handler_old old_qtmsg_handler = 0;
01302 
01303 void messageHandler(QtMsgType type, const char *msg)
01304 {
01305 #ifdef FC_DEBUG
01306     switch (type)
01307     {
01308     case QtDebugMsg:
01309         Base::Console().Message("%s\n", msg);
01310         break;
01311     case QtWarningMsg:
01312         Base::Console().Warning("%s\n", msg);
01313         break;
01314     case QtFatalMsg:
01315         Base::Console().Error("%s\n", msg);
01316         abort();                    // deliberately core dump
01317     }
01318 #ifdef FC_OS_WIN32
01319     if (old_qtmsg_handler)
01320         (*old_qtmsg_handler)(type, msg);
01321 #endif
01322 #else
01323     // do not stress user with Qt internals but write to log file if enabled
01324     Base::Console().Log("%s\n", msg);
01325 #endif
01326 }
01327 
01328 #ifdef FC_DEBUG // redirect Coin messages to FreeCAD
01329 void messageHandlerCoin(const SoError * error, void * userdata)
01330 {
01331     if (error && error->getTypeId() == SoDebugError::getClassTypeId()) {
01332         const SoDebugError* dbg = static_cast<const SoDebugError*>(error);
01333         const char* msg = error->getDebugString().getString();
01334         switch (dbg->getSeverity())
01335         {
01336         case SoDebugError::INFO:
01337             Base::Console().Message( msg );
01338             break;
01339         case SoDebugError::WARNING:
01340             Base::Console().Warning( msg );
01341             break;
01342         default: // error
01343             Base::Console().Error( msg );
01344             break;
01345         }
01346 #ifdef FC_OS_WIN32
01347     if (old_qtmsg_handler)
01348         (*old_qtmsg_handler)(QtDebugMsg, msg);
01349 #endif
01350     }
01351     else if (error) {
01352         const char* msg = error->getDebugString().getString();
01353         Base::Console().Log( msg );
01354     }
01355 }
01356 
01357 void messageHandlerSoQt(const SbString errmsg, SoQt::FatalErrors errcode, void *userdata)
01358 {
01359     Base::Console().Error( errmsg.getString() );
01360 }
01361 #endif
01362 
01363 // To fix bug #0000345 move Q_INIT_RESOURCE() outside initApplication()
01364 static void init_resources()
01365 {
01366     // init resources
01367     Q_INIT_RESOURCE(resource);
01368     Q_INIT_RESOURCE(translation);
01369 }
01370 
01371 void Application::initApplication(void)
01372 {
01373     try {
01374         initTypes();
01375         new Base::ScriptProducer( "FreeCADGuiInit", FreeCADGuiInit );
01376         init_resources();
01377         old_qtmsg_handler = qInstallMsgHandler(messageHandler);
01378     }
01379     catch (...) {
01380         // force to flush the log
01381         App::Application::destructObserver();
01382         throw;
01383     }
01384 }
01385 
01386 void Application::initTypes(void)
01387 {
01388     // views
01389     Gui::BaseView                               ::init();
01390     Gui::MDIView                                ::init();
01391     Gui::View3DInventor                         ::init();
01392     // View Provider
01393     Gui::ViewProvider                           ::init();
01394     Gui::ViewProviderExtern                     ::init();
01395     Gui::ViewProviderDocumentObject             ::init();
01396     Gui::ViewProviderFeature                    ::init();
01397     Gui::ViewProviderDocumentObjectGroup        ::init();
01398     Gui::ViewProviderGeometryObject             ::init();
01399     Gui::ViewProviderInventorObject             ::init();
01400     Gui::ViewProviderVRMLObject                 ::init();
01401     Gui::ViewProviderAnnotation                 ::init();
01402     Gui::ViewProviderAnnotationLabel            ::init();
01403     Gui::ViewProviderPointMarker                ::init();
01404     Gui::ViewProviderMeasureDistance            ::init();
01405     Gui::ViewProviderPythonFeature              ::init();
01406     Gui::ViewProviderPythonGeometry             ::init();
01407 
01408     // Workbench
01409     Gui::Workbench                              ::init();
01410     Gui::StdWorkbench                           ::init();
01411     Gui::BlankWorkbench                         ::init();
01412     Gui::NoneWorkbench                          ::init();
01413     Gui::TestWorkbench                          ::init();
01414     Gui::PythonWorkbench                        ::init();
01415 }
01416 
01417 namespace Gui {
01421 class GUIApplication : public GUIApplicationNativeEventAware
01422 {
01423 public:
01424     GUIApplication(int & argc, char ** argv)
01425         : GUIApplicationNativeEventAware(argc, argv)
01426     {
01427     }
01428 
01433     bool notify (QObject * receiver, QEvent * event)
01434     {
01435         if (!receiver && event) {
01436             Base::Console().Log("GUIApplication::notify: Unexpected null receiver, event type: %d\n",
01437                 (int)event->type());
01438         }
01439         try {
01440             if (event->type() == Spaceball::ButtonEvent::ButtonEventType)
01441                 return processSpaceballEvent(receiver, event);
01442             else
01443                 return QApplication::notify(receiver, event);
01444         }
01445         catch (const Base::Exception& e) {
01446             Base::Console().Error("Unhandled Base::Exception caught in GUIApplication::notify.\n"
01447                                   "The error message is: %s\n", e.what());
01448         }
01449         catch (const std::exception& e) {
01450             Base::Console().Error("Unhandled std::exception caught in GUIApplication::notify.\n"
01451                                   "The error message is: %s\n", e.what());
01452         }
01453         catch (...) {
01454             Base::Console().Error("Unhandled unknown exception caught in GUIApplication::notify.\n");
01455         }
01456 
01457         // Print some more information to the log file (if active) to ease bug fixing
01458         if (receiver && event) {
01459             std::stringstream dump;
01460             dump << "The event type " << (int)event->type() << " was sent to "
01461                  << receiver->metaObject()->className() << "\n";
01462             dump << "Object tree:\n";
01463             if (receiver->isWidgetType()) {
01464                 QWidget* w = qobject_cast<QWidget*>(receiver);
01465                 while (w) {
01466                     dump << "\t";
01467                     dump << w->metaObject()->className();
01468                     QString name = w->objectName();
01469                     if (!name.isEmpty())
01470                         dump << " (" << (const char*)name.toUtf8() << ")";
01471                     w = w->parentWidget();
01472                     if (w)
01473                         dump << " is child of\n";
01474                 }
01475                 std::string str = dump.str();
01476                 Base::Console().Log("%s",str.c_str());
01477             }
01478         }
01479 
01480         return true;
01481     }
01482     void commitData(QSessionManager &manager)
01483     {
01484         if (manager.allowsInteraction()) {
01485             if (!Gui::getMainWindow()->close()) {
01486                 // cancel the shutdown
01487                 manager.release();
01488                 manager.cancel();
01489             }
01490         }
01491         else {
01492             // no user interaction allowed, thus close all documents and
01493             // the main window
01494             App::GetApplication().closeAllDocuments();
01495             Gui::getMainWindow()->close();
01496         }
01497 
01498     }
01499 };
01500 }
01501 
01502 void Application::runApplication(void)
01503 {
01504     // A new QApplication
01505     Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
01506     // if application not yet created by the splasher
01507     int argc = App::Application::GetARGC();
01508     GUIApplication mainApp(argc, App::Application::GetARGV());
01509     // set application icon and window title
01510     mainApp.setWindowIcon(Gui::BitmapFactory().pixmap(App::Application::Config()["AppIcon"].c_str()));
01511     mainApp.setApplicationName(QString::fromAscii(App::GetApplication().getExecutableName()));
01512     QString plugin;
01513     plugin = QString::fromUtf8(App::GetApplication().GetHomePath());
01514     plugin += QLatin1String("/plugins");
01515     QCoreApplication::addLibraryPath(plugin);
01516 
01517     // check for OpenGL
01518     if (!QGLFormat::hasOpenGL()) {
01519         QMessageBox::critical(0, QObject::tr("No OpenGL"), QObject::tr("This system does not support OpenGL"));
01520         throw Base::Exception("This system does not support OpenGL");
01521     }
01522 #if QT_VERSION >= 0x040200
01523     if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) {
01524         Base::Console().Log("This system does not support framebuffer objects");
01525     }
01526 #endif
01527     if (!QGLPixelBuffer::hasOpenGLPbuffers()) {
01528         Base::Console().Log("This system does not support pbuffers");
01529     }
01530 
01531     QGLFormat::OpenGLVersionFlags version = QGLFormat::openGLVersionFlags ();
01532 #if QT_VERSION >= 0x040500
01533     if (version & QGLFormat::OpenGL_Version_3_0)
01534         Base::Console().Log("OpenGL version 3.0 or higher is present\n");
01535     else
01536 #endif
01537     if (version & QGLFormat::OpenGL_Version_2_1)
01538         Base::Console().Log("OpenGL version 2.1 or higher is present\n");
01539     else if (version & QGLFormat::OpenGL_Version_2_0)
01540         Base::Console().Log("OpenGL version 2.0 or higher is present\n");
01541     else if (version & QGLFormat::OpenGL_Version_1_5)
01542         Base::Console().Log("OpenGL version 1.5 or higher is present\n");
01543     else if (version & QGLFormat::OpenGL_Version_1_4)
01544         Base::Console().Log("OpenGL version 1.4 or higher is present\n");
01545     else if (version & QGLFormat::OpenGL_Version_1_3)
01546         Base::Console().Log("OpenGL version 1.3 or higher is present\n");
01547     else if (version & QGLFormat::OpenGL_Version_1_2)
01548         Base::Console().Log("OpenGL version 1.2 or higher is present\n");
01549     else if (version & QGLFormat::OpenGL_Version_1_1)
01550         Base::Console().Log("OpenGL version 1.1 or higher is present\n");
01551     else if (version & QGLFormat::OpenGL_Version_None)
01552         Base::Console().Log("No OpenGL is present or no OpenGL context is current\n");
01553 
01554     Application app(true);
01555     MainWindow mw;
01556     mw.setWindowTitle(mainApp.applicationName());
01557 
01558     // set toolbar icon size
01559     ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
01560     int size = hGrp->GetInt("ToolbarIconSize", 0);
01561     if (size >= 16) // must not be lower than this
01562         mw.setIconSize(QSize(size,size));
01563 
01564     // init the Inventor subsystem
01565     SoDB::init();
01566     SoQt::init(&mw);
01567     SoFCDB::init();
01568 
01569     const std::map<std::string,std::string>& cfg = App::Application::Config();
01570     std::map<std::string,std::string>::const_iterator it;
01571     it = cfg.find("WindowTitle");
01572     if (it != cfg.end()) {
01573         QString title = QString::fromUtf8(it->second.c_str());
01574         mw.setWindowTitle(title);
01575     }
01576     it = cfg.find("WindowIcon");
01577     if (it != cfg.end()) {
01578         QString path = QString::fromUtf8(it->second.c_str());
01579         QApplication::setWindowIcon(QIcon(path));
01580     }
01581     it = cfg.find("ProgramLogo");
01582     if (it != cfg.end()) {
01583         QString path = QString::fromUtf8(it->second.c_str());
01584         QPixmap px(path);
01585         if (!px.isNull()) {
01586             QLabel* logo = new QLabel();
01587             logo->setPixmap(px.scaledToHeight(32));
01588             mw.statusBar()->addPermanentWidget(logo, 0);
01589             logo->setFrameShape(QFrame::NoFrame);
01590         }
01591     }
01592 
01593     // show splasher while initializing the GUI
01594     mw.startSplasher();
01595 
01596     // running the GUI init script
01597     try {
01598         Base::Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADGuiInit"));
01599     }
01600     catch (const Base::Exception& e) {
01601         Base::Console().Error("Error in FreeCADGuiInit.py: %s\n", e.what());
01602         mw.stopSplasher();
01603         throw;
01604     }
01605 
01606     // stop splash screen and set immediately the active window that may be of interest
01607     // for scripts using Python binding for Qt
01608     mw.stopSplasher();
01609     mainApp.setActiveWindow(&mw);
01610 
01611     // Activate the correct workbench
01612     std::string start = App::Application::Config()["StartWorkbench"];
01613     Base::Console().Log("Init: Activating default workbench %s\n", start.c_str());
01614     start = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
01615                            GetASCII("AutoloadModule", start.c_str());
01616     // if the auto workbench is not visible then force to use the default workbech
01617     // and replace the wrong entry in the parameters
01618     QStringList wb = app.workbenches();
01619     if (!wb.contains(QString::fromAscii(start.c_str()))) {
01620         start = App::Application::Config()["StartWorkbench"];
01621         App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
01622                               SetASCII("AutoloadModule", start.c_str());
01623     }
01624 
01625     app.activateWorkbench(start.c_str());
01626 
01627     // show the main window
01628     Base::Console().Log("Init: Showing main window\n");
01629     mw.loadWindowSettings();
01630 
01631     //initialize spaceball.
01632     mainApp.initSpaceball(&mw);
01633 
01634 #ifdef FC_DEBUG // redirect Coin messages to FreeCAD
01635     SoDebugError::setHandlerCallback( messageHandlerCoin, 0 );
01636     SoQt::setFatalErrorHandler( messageHandlerSoQt, 0 );
01637 #endif
01638 
01639     Instance->d->startingUp = false;
01640 
01641 #if 0
01642     // processing all command line files
01643     App::Application::processCmdLineFiles();
01644 
01645     // Create new document?
01646     ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Document");
01647     if (hGrp->GetBool("CreateNewDoc", false)) {
01648         App::GetApplication().newDocument();
01649     }
01650 #else
01651     // gets called once we start the event loop
01652     QTimer::singleShot(0, &mw, SLOT(delayedStartup()));
01653 #endif
01654 
01655     // run the Application event loop
01656     Base::Console().Log("Init: Entering event loop\n");
01657 
01658     try {
01659         mainApp.exec();
01660     }
01661     catch(...) {
01662         // catching nasty stuff coming out of the event loop
01663         App::Application::destructObserver();
01664         Base::Console().Error("Event loop left through unhandled exception\n");
01665         throw;
01666     }
01667 
01668     Base::Console().Log("Finish: Event loop left\n");
01669 }

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