FreeCAD/src/Gui/Application.cpp

1742 lines
60 KiB
C++

/***************************************************************************
* Copyright (c) 2004 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include "InventorAll.h"
# include <boost/signals.hpp>
# include <boost/bind.hpp>
# include <sstream>
# include <stdexcept>
# include <QCloseEvent>
# include <QDir>
# include <QFileInfo>
# include <QLocale>
# include <QMessageBox>
# include <QPointer>
# include <QGLFormat>
# include <QGLPixelBuffer>
#if QT_VERSION >= 0x040200
# include <QGLFramebufferObject>
#endif
# include <QSessionManager>
#endif
// FreeCAD Base header
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <Base/Parameter.h>
#include <Base/Exception.h>
#include <Base/Factory.h>
#include <Base/FileInfo.h>
#include <Base/Tools.h>
#include <Base/UnitsApi.h>
#include <App/Document.h>
#include <App/DocumentObjectPy.h>
#include "Application.h"
#include "GuiApplicationNativeEventAware.h"
#include "MainWindow.h"
#include "Document.h"
#include "View.h"
#include "View3DPy.h"
#include "WidgetFactory.h"
#include "Command.h"
#include "Macro.h"
#include "ProgressBar.h"
#include "Workbench.h"
#include "WorkbenchManager.h"
#include "ToolBoxManager.h"
#include "WaitCursor.h"
#include "MenuManager.h"
#include "Window.h"
#include "Selection.h"
#include "BitmapFactory.h"
#include "SoFCDB.h"
#include "PythonConsolePy.h"
#include "PythonDebugger.h"
#include "View3DPy.h"
#include "DlgOnlineHelpImp.h"
#include "SpaceballEvent.h"
#include "SplitView3DInventor.h"
#include "View3DInventor.h"
#include "ViewProvider.h"
#include "ViewProviderExtern.h"
#include "ViewProviderFeature.h"
#include "ViewProviderPythonFeature.h"
#include "ViewProviderDocumentObjectGroup.h"
#include "ViewProviderGeometryObject.h"
#include "ViewProviderInventorObject.h"
#include "ViewProviderVRMLObject.h"
#include "ViewProviderAnnotation.h"
#include "ViewProviderMeasureDistance.h"
#include "Language/Translator.h"
#include "TaskView/TaskDialogPython.h"
#include "GuiInitScript.h"
using namespace Gui;
using namespace Gui::DockWnd;
using namespace std;
Application* Application::Instance = 0L;
namespace Gui {
// Pimpl class
struct ApplicationP
{
ApplicationP() :
activeDocument(0L),
isClosing(false),
startingUp(true)
{
// create the macro manager
macroMngr = new MacroManager();
}
~ApplicationP()
{
delete macroMngr;
}
/// list of all handled documents
std::map<const App::Document*, Gui::Document*> documents;
/// Active document
Gui::Document* activeDocument;
MacroManager* macroMngr;
/// List of all registered views
std::list<Gui::BaseView*> passive;
bool isClosing;
bool startingUp;
/// Handles all commands
CommandManager commandManager;
};
/** Observer that watches relabeled objects and make sure that the labels inside
* a document are unique.
* @note In the FreeCAD design it is explicitly allowed to have duplicate labels
* (i.e. the user visible text e.g. in the tree view) while the internal names
* are always guaranteed to be unique.
*/
class ObjectLabelObserver
{
public:
/// The one and only instance.
static ObjectLabelObserver* instance();
/// Destructs the sole instance.
static void destruct ();
/** Checks the new label of the object and relabel it if needed
* to make it unique document-wide
*/
void slotRelabelObject(const App::DocumentObject&, const App::Property&);
private:
static ObjectLabelObserver* _singleton;
ObjectLabelObserver();
~ObjectLabelObserver();
const App::DocumentObject* current;
ParameterGrp::handle _hPGrp;
};
ObjectLabelObserver* ObjectLabelObserver::_singleton = 0;
ObjectLabelObserver* ObjectLabelObserver::instance()
{
if (!_singleton)
_singleton = new ObjectLabelObserver;
return _singleton;
}
void ObjectLabelObserver::destruct ()
{
delete _singleton;
_singleton = 0;
}
void ObjectLabelObserver::slotRelabelObject(const App::DocumentObject& obj, const App::Property& prop)
{
// observe only the Label property
if (&prop == &obj.Label) {
// have we processed this (or another?) object right now?
if (current) {
return;
}
std::string label = obj.Label.getValue();
App::Document* doc = obj.getDocument();
if (doc && !_hPGrp->GetBool("DuplicateLabels")) {
std::vector<std::string> objectLabels;
std::vector<App::DocumentObject*>::const_iterator it;
std::vector<App::DocumentObject*> objs = doc->getObjects();
bool match = false;
for (it = objs.begin();it != objs.end();++it) {
if (*it == &obj)
continue; // don't compare object with itself
std::string objLabel = (*it)->Label.getValue();
if (!match && objLabel == label)
match = true;
objectLabels.push_back(objLabel);
}
// make sure that there is a name conflict otherwise we don't have to do anything
if (match) {
// remove number from end to avoid lengthy names
size_t lastpos = label.length()-1;
while (label[lastpos] >= 48 && label[lastpos] <= 57)
lastpos--;
label = label.substr(0, lastpos+1);
label = Base::Tools::getUniqueName(label, objectLabels, 3);
this->current = &obj;
const_cast<App::DocumentObject&>(obj).Label.setValue(label);
this->current = 0;
}
}
}
}
ObjectLabelObserver::ObjectLabelObserver() : current(0)
{
App::GetApplication().signalChangedObject.connect(boost::bind
(&ObjectLabelObserver::slotRelabelObject, this, _1, _2));
_hPGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp");
_hPGrp = _hPGrp->GetGroup("Preferences")->GetGroup("Document");
}
ObjectLabelObserver::~ObjectLabelObserver()
{
}
static PyObject *
FreeCADGui_subgraphFromObject(PyObject * /*self*/, PyObject *args)
{
PyObject *o;
if (!PyArg_ParseTuple(args, "O!",&(App::DocumentObjectPy::Type), &o))
return NULL;
App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(o)->getDocumentObjectPtr();
std::string vp = obj->getViewProviderName();
SoNode* node = 0;
try {
Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(vp.c_str(), true));
if (base && base->getTypeId().isDerivedFrom(Gui::ViewProviderDocumentObject::getClassTypeId())) {
std::auto_ptr<Gui::ViewProviderDocumentObject> vp(static_cast<Gui::ViewProviderDocumentObject*>(base));
std::map<std::string, App::Property*> Map;
obj->getPropertyMap(Map);
vp->attach(obj);
for (std::map<std::string, App::Property*>::iterator it = Map.begin(); it != Map.end(); ++it) {
vp->updateData(it->second);
}
std::vector<std::string> modes = vp->getDisplayModes();
if (!modes.empty())
vp->setDisplayMode(modes.front().c_str());
node = vp->getRoot()->copy();
node->ref();
std::string type = "So";
type += node->getTypeId().getName().getString();
type += " *";
PyObject* proxy = 0;
proxy = Base::Interpreter().createSWIGPointerObj("pivy.coin", type.c_str(), (void*)node, 1);
return Py::new_reference_to(Py::Object(proxy, true));
}
}
catch (const Base::Exception& e) {
if (node) node->unref();
PyErr_SetString(PyExc_RuntimeError, e.what());
return 0;
}
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
FreeCADGui_getSoDBVersion(PyObject * /*self*/, PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return NULL;
return PyString_FromString(SoDB::getVersion());
}
static PyObject *
FreeCADGui_getSoQtVersion(PyObject * /*self*/, PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return NULL;
return PyString_FromString(SoQt::getVersionString());
}
struct PyMethodDef FreeCADGui_methods[] = {
{"subgraphFromObject",FreeCADGui_subgraphFromObject,METH_VARARGS,
"subgraphFromObject(object) -> Node\n\n"
"Return the Inventor subgraph to an object"},
{"getSoDBVersion",FreeCADGui_getSoDBVersion,METH_VARARGS,
"getSoDBVersion() -> String\n\n"
"Return a text string containing the name\n"
"of the Coin library and version information"},
{"getSoQtVersion",FreeCADGui_getSoQtVersion,METH_VARARGS,
"getSoQtVersion() -> String\n\n"
"Return a text string containing the name\n"
"of the SoQt library and version information"},
{NULL, NULL} /* sentinel */
};
} // namespace Gui
Application::Application(bool GUIenabled)
{
//App::GetApplication().Attach(this);
if (GUIenabled) {
App::GetApplication().signalNewDocument.connect(boost::bind(&Gui::Application::slotNewDocument, this, _1));
App::GetApplication().signalDeleteDocument.connect(boost::bind(&Gui::Application::slotDeleteDocument, this, _1));
App::GetApplication().signalRenameDocument.connect(boost::bind(&Gui::Application::slotRenameDocument, this, _1));
App::GetApplication().signalActiveDocument.connect(boost::bind(&Gui::Application::slotActiveDocument, this, _1));
App::GetApplication().signalRelabelDocument.connect(boost::bind(&Gui::Application::slotRelabelDocument, this, _1));
// install the last active language
ParameterGrp::handle hPGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp");
hPGrp = hPGrp->GetGroup("Preferences")->GetGroup("General");
QString lang = QLocale::languageToString(QLocale::system().language());
Translator::instance()->activateLanguage(hPGrp->GetASCII("Language", (const char*)lang.toAscii()).c_str());
GetWidgetFactorySupplier();
ParameterGrp::handle hUnits = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/Units");
Base::UnitsApi::setDecimals(hUnits->GetInt("Decimals", Base::UnitsApi::getDecimals()));
// setting up Python binding
Base::PyGILStateLocker lock;
PyObject* module = Py_InitModule3("FreeCADGui", Application::Methods,
"The functions in the FreeCADGui module allow working with GUI documents,\n"
"view providers, views, workbenches and much more.\n\n"
"The FreeCADGui instance provides a list of references of GUI documents which\n"
"can be addressed by a string. These documents contain the view providers for\n"
"objects in the associated App document. An App and GUI document can be\n"
"accessed with the same name.\n\n"
"The FreeCADGui module also provides a set of functions to work with so called\n"
"workbenches.");
Py::Module(module).setAttr(std::string("ActiveDocument"),Py::None());
UiLoaderPy::init_type();
Base::Interpreter().addType(UiLoaderPy::type_object(),
module,"UiLoader");
//insert Selection module
PyObject* pSelectionModule = Py_InitModule3("Selection", SelectionSingleton::Methods,
"Selection module");
Py_INCREF(pSelectionModule);
PyModule_AddObject(module, "Selection", pSelectionModule);
SelectionFilterPy::init_type();
Base::Interpreter().addType(SelectionFilterPy::type_object(),
pSelectionModule,"Filter");
Gui::TaskView::ControlPy::init_type();
Py::Module(module).setAttr(std::string("Control"),
Py::Object(Gui::TaskView::ControlPy::getInstance(), true));
}
Base::PyGILStateLocker lock;
PyObject *module = PyImport_AddModule("FreeCADGui");
PyMethodDef *meth = FreeCADGui_methods;
PyObject *dict = PyModule_GetDict(module);
for (; meth->ml_name != NULL; meth++) {
PyObject *descr;
descr = PyCFunction_NewEx(meth,0,0);
if (descr == NULL)
break;
if (PyDict_SetItemString(dict, meth->ml_name, descr) != 0)
break;
Py_DECREF(descr);
}
// Python console binding
PythonDebugModule ::init_module();
PythonStdout ::init_type();
PythonStderr ::init_type();
OutputStdout ::init_type();
OutputStderr ::init_type();
PythonStdin ::init_type();
View3DInventorPy ::init_type();
d = new ApplicationP;
// global access
Instance = this;
// instanciate the workbench dictionary
_pcWorkbenchDictionary = PyDict_New();
createStandardOperations();
MacroCommand::load();
ObjectLabelObserver::instance();
}
Application::~Application()
{
Base::Console().Log("Destruct Gui::Application\n");
WorkbenchManager::destruct();
SelectionSingleton::destruct();
Translator::destruct();
WidgetFactorySupplier::destruct();
BitmapFactoryInst::destruct();
#if 0
// we must run the garbage collector before shutting down the SoDB or SoQt
// subsystem because we may reference some class objects of them in Python
Base::Interpreter().cleanupSWIG("SoBase *");
// finish also Inventor subsystem
SoFCDB::finish();
SoQt::done();
#if (COIN_MAJOR_VERSION >= 2) && (COIN_MINOR_VERSION >= 4)
SoDB::finish();
#elif (COIN_MAJOR_VERSION >= 3)
SoDB::finish();
#else
SoDB::cleanup();
#endif
#endif
{
Base::PyGILStateLocker lock;
Py_DECREF(_pcWorkbenchDictionary);
}
// save macros
MacroCommand::save();
//App::GetApplication().Detach(this);
delete d;
Instance = 0;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// creating std commands
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Application::open(const char* FileName, const char* Module)
{
WaitCursor wc;
Base::FileInfo File(FileName);
string te = File.extension();
// if the active document is empty and not modified, close it
// in case of an automatically created empty document at startup
App::Document* act = App::GetApplication().getActiveDocument();
Gui::Document* gui = this->getDocument(act);
if (act && act->countObjects() == 0 && gui && gui->isModified() == false){
Command::doCommand(Command::App, "App.closeDocument('%s')", act->getName());
qApp->processEvents(); // an update is needed otherwise the new view isn't shown
}
if (Module != 0) {
// issue module loading
Command::doCommand(Command::App, "import %s", Module);
try {
// load the file with the module
Command::doCommand(Command::App, "%s.open(\"%s\")", Module, File.filePath().c_str());
// ViewFit
if (!File.hasExtension("FCStd") && sendHasMsgToActiveView("ViewFit"))
//Command::doCommand(Command::Gui, "Gui.activeDocument().activeView().fitAll()");
Command::doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewFit\")");
// the original file name is required
getMainWindow()->appendRecentFile(QString::fromUtf8(File.filePath().c_str()));
}
catch (const Base::PyException& e){
// Usually thrown if the file is invalid somehow
e.ReportException();
}
}
else {
wc.restoreCursor();
QMessageBox::warning(getMainWindow(), QObject::tr("Unknown filetype"),
QObject::tr("Cannot open unknown filetype: %1").arg(QLatin1String(te.c_str())));
wc.setWaitCursor();
return;
}
}
void Application::importFrom(const char* FileName, const char* DocName, const char* Module)
{
WaitCursor wc;
Base::FileInfo File(FileName);
std::string te = File.extension();
if (Module != 0) {
// issue module loading
Command::doCommand(Command::App, "import %s", Module);
try {
// load the file with the module
if (File.hasExtension("FCStd")) {
Command::doCommand(Command::App, "%s.open(\"%s\")"
, Module, File.filePath().c_str());
if (activeDocument())
activeDocument()->setModified(false);
}
else {
Command::doCommand(Command::App, "%s.insert(\"%s\",\"%s\")"
, Module, File.filePath().c_str(), DocName);
Command::doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewFit\")");
if (getDocument(DocName))
getDocument(DocName)->setModified(true);
}
// the original file name is required
getMainWindow()->appendRecentFile(QString::fromUtf8(File.filePath().c_str()));
}
catch (const Base::PyException& e){
// Usually thrown if the file is invalid somehow
e.ReportException();
}
}
else {
wc.restoreCursor();
QMessageBox::warning(getMainWindow(), QObject::tr("Unknown filetype"),
QObject::tr("Cannot open unknown filetype: %1").arg(QLatin1String(te.c_str())));
wc.setWaitCursor();
}
}
void Application::exportTo(const char* FileName, const char* DocName, const char* Module)
{
WaitCursor wc;
Base::FileInfo File(FileName);
std::string te = File.extension();
if (Module != 0) {
try {
std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType
(App::DocumentObject::getClassTypeId(),DocName);
if (sel.empty()) {
App::Document* doc = App::GetApplication().getDocument(DocName);
sel = doc->getObjectsOfType(App::DocumentObject::getClassTypeId());
}
std::stringstream str;
str << "__objs__=[]" << std::endl;
for (std::vector<App::DocumentObject*>::iterator it = sel.begin(); it != sel.end(); ++it) {
str << "__objs__.append(FreeCAD.getDocument(\"" << DocName << "\").getObject(\""
<< (*it)->getNameInDocument() << "\"))" << std::endl;
}
str << "import " << Module << std::endl;
str << Module << ".export(__objs__,\"" << File.filePath() << "\")" << std::endl;
str << "del __objs__" << std::endl;
std::string code = str.str();
// the original file name is required
if (runPythonCode(code.c_str(), false))
getMainWindow()->appendRecentFile(QString::fromUtf8(File.filePath().c_str()));
}
catch (const Base::PyException& e){
// Usually thrown if the file is invalid somehow
e.ReportException();
}
}
else {
wc.restoreCursor();
QMessageBox::warning(getMainWindow(), QObject::tr("Unknown filetype"),
QObject::tr("Cannot save to unknown filetype: %1").arg(QLatin1String(te.c_str())));
wc.setWaitCursor();
}
}
void Application::createStandardOperations()
{
// register the application Standard commands from CommandStd.cpp
Gui::CreateStdCommands();
Gui::CreateDocCommands();
Gui::CreateFeatCommands();
Gui::CreateMacroCommands();
Gui::CreateViewStdCommands();
Gui::CreateWindowStdCommands();
Gui::CreateTestCommands();
}
void Application::slotNewDocument(const App::Document& Doc)
{
#ifdef FC_DEBUG
std::map<const App::Document*, Gui::Document*>::const_iterator it = d->documents.find(&Doc);
assert(it==d->documents.end());
#endif
Gui::Document* pDoc = new Gui::Document(const_cast<App::Document*>(&Doc),this);
d->documents[&Doc] = pDoc;
// connect the signals to the application for the new document
pDoc->signalNewObject.connect(boost::bind(&Gui::Application::slotNewObject, this, _1));
pDoc->signalDeletedObject.connect(boost::bind(&Gui::Application::slotDeletedObject, this, _1));
pDoc->signalChangedObject.connect(boost::bind(&Gui::Application::slotChangedObject, this, _1, _2));
pDoc->signalRenamedObject.connect(boost::bind(&Gui::Application::slotRenamedObject, this, _1));
pDoc->signalActivatedObject.connect(boost::bind(&Gui::Application::slotActivatedObject, this, _1));
signalNewDocument(*pDoc);
pDoc->createView("View3DIv");
qApp->processEvents(); // make sure to show the window stuff on the right place
}
void Application::slotDeleteDocument(const App::Document& Doc)
{
std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
if (doc == d->documents.end()) {
Base::Console().Log("GUI document '%s' already deleted\n", Doc.getName());
return;
}
// We must clear the selection here to notify all observers
Gui::Selection().clearSelection(doc->second->getDocument()->getName());
signalDeleteDocument(*doc->second);
// If the active document gets destructed we must set it to 0. If there are further existing documents then the
// view that becomes active sets the active document again. So, we needn't worry about this.
if (d->activeDocument == doc->second)
setActiveDocument(0);
// For exception-safety use a smart pointer
auto_ptr<Document> delDoc (doc->second);
d->documents.erase(doc);
}
void Application::slotRelabelDocument(const App::Document& Doc)
{
std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
#ifdef FC_DEBUG
assert(doc!=d->documents.end());
#endif
signalRelabelDocument(*doc->second);
doc->second->onRelabel();
}
void Application::slotRenameDocument(const App::Document& Doc)
{
std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
#ifdef FC_DEBUG
assert(doc!=d->documents.end());
#endif
signalRenameDocument(*doc->second);
}
void Application::slotActiveDocument(const App::Document& Doc)
{
std::map<const App::Document*, Gui::Document*>::iterator doc = d->documents.find(&Doc);
// this can happen when closing a document with two views opened
if (doc != d->documents.end())
signalActiveDocument(*doc->second);
}
void Application::slotNewObject(const ViewProvider& vp)
{
this->signalNewObject(vp);
}
void Application::slotDeletedObject(const ViewProvider& vp)
{
this->signalDeletedObject(vp);
}
void Application::slotChangedObject(const ViewProvider& vp, const App::Property& prop)
{
this->signalChangedObject(vp,prop);
}
void Application::slotRenamedObject(const ViewProvider& vp)
{
this->signalRenamedObject(vp);
}
void Application::slotActivatedObject(const ViewProvider& vp)
{
this->signalActivatedObject(vp);
}
void Application::onLastWindowClosed(Gui::Document* pcDoc)
{
if (!d->isClosing && pcDoc) {
try {
// Call the closing mechanism from Python. This also checks whether pcDoc is the last open document.
Command::doCommand(Command::Doc, "App.closeDocument(\"%s\")", pcDoc->getDocument()->getName());
}
catch (const Base::PyException& e) {
e.ReportException();
}
}
}
/// send Messages to the active view
bool Application::sendMsgToActiveView(const char* pMsg, const char** ppReturn)
{
MDIView* pView = getMainWindow()->activeWindow();
return pView ? pView->onMsg(pMsg,ppReturn) : false;
}
bool Application::sendHasMsgToActiveView(const char* pMsg)
{
MDIView* pView = getMainWindow()->activeWindow();
return pView ? pView->onHasMsg(pMsg) : false;
}
/// Getter for the active view
Gui::Document* Application::activeDocument(void) const
{
return d->activeDocument;
}
void Application::setActiveDocument(Gui::Document* pcDocument)
{
if (d->activeDocument == pcDocument)
return; // nothing needs to be done
if (pcDocument) {
// This happens if a document with more than one view is about being
// closed and a second view is activated. The document is still not
// removed from the map.
App::Document* doc = pcDocument->getDocument();
if (d->documents.find(doc) == d->documents.end())
return;
}
d->activeDocument = pcDocument;
std::string name;
// This adds just a line to the macro file but does not set the active document
if (pcDocument){
name += "App.setActiveDocument(\"";
name += pcDocument->getDocument()->getName();
name += "\")\n";
name += "App.ActiveDocument=App.getDocument(\"";
name += pcDocument->getDocument()->getName();
name += "\")\n";
name += "Gui.ActiveDocument=Gui.getDocument(\"";
name += pcDocument->getDocument()->getName();
name += "\")";
macroManager()->addLine(MacroManager::Gui,name.c_str());
}
else {
name += "App.setActiveDocument(\"\")\n";
name += "App.ActiveDocument=None\n";
name += "Gui.ActiveDocument=None";
macroManager()->addLine(MacroManager::Gui,name.c_str());
}
// Sets the currently active document
try {
Base::Interpreter().runString(name.c_str());
}
catch (const Base::Exception& e) {
Base::Console().Warning(e.what());
return;
}
// May be useful for error detection
if (d->activeDocument) {
App::Document* doc = d->activeDocument->getDocument();
Base::Console().Log("Active document is %s (at %p)\n",doc->getName(), doc);
}
else {
Base::Console().Log("No active document\n");
}
// notify all views attached to the application (not views belong to a special document)
for(list<Gui::BaseView*>::iterator It=d->passive.begin();It!=d->passive.end();It++)
(*It)->setDocument(pcDocument);
}
Gui::Document* Application::getDocument(const char* name) const
{
App::Document* pDoc = App::GetApplication().getDocument( name );
std::map<const App::Document*, Gui::Document*>::const_iterator it = d->documents.find(pDoc);
if ( it!=d->documents.end() )
return it->second;
else
return 0;
}
Gui::Document* Application::getDocument(const App::Document* pDoc) const
{
std::map<const App::Document*, Gui::Document*>::const_iterator it = d->documents.find(pDoc);
if ( it!=d->documents.end() )
return it->second;
else
return 0;
}
void Application::showViewProvider(App::DocumentObject* obj)
{
ViewProvider* vp = getViewProvider(obj);
if (vp) vp->show();
}
void Application::hideViewProvider(App::DocumentObject* obj)
{
ViewProvider* vp = getViewProvider(obj);
if (vp) vp->hide();
}
Gui::ViewProvider* Application::getViewProvider(App::DocumentObject* obj) const
{
App::Document* doc = obj->getDocument();
if (doc) {
Gui::Document* gui = getDocument(doc);
if (gui) {
ViewProvider* vp = gui->getViewProvider(obj);
return vp;
}
}
return 0;
}
void Application::attachView(Gui::BaseView* pcView)
{
d->passive.push_back(pcView);
}
void Application::detachView(Gui::BaseView* pcView)
{
d->passive.remove(pcView);
}
void Application::onUpdate(void)
{
// update all documents
std::map<const App::Document*, Gui::Document*>::iterator It;
for (It = d->documents.begin();It != d->documents.end();It++)
It->second->onUpdate();
// update all the independed views
for (std::list<Gui::BaseView*>::iterator It2 = d->passive.begin();It2 != d->passive.end();It2++)
(*It2)->onUpdate();
}
/// Gets called if a view gets activated, this manages the whole activation scheme
void Application::viewActivated(MDIView* pcView)
{
// May be useful for error detection
Base::Console().Log("Active view is %s (at %p)\n",
(const char*)pcView->windowTitle().toUtf8(),pcView);
signalActivateView(pcView);
// Set the new active document which is taken of the activated view. If, however,
// this view is passive we let the currently active document unchanged as we would
// have no document active which is causing a lot of trouble.
if (!pcView->isPassive())
setActiveDocument(pcView->getGuiDocument());
}
void Application::updateActive(void)
{
activeDocument()->onUpdate();
}
void Application::tryClose(QCloseEvent * e)
{
if (d->documents.size() == 0) {
e->accept();
}
else {
// ask all documents if closable
std::map<const App::Document*, Gui::Document*>::iterator It;
for (It = d->documents.begin();It!=d->documents.end();It++) {
// a document may have several views attached, so ask it directly
#if 0
MDIView* active = It->second->getActiveView();
e->setAccepted(active->canClose());
#else
e->setAccepted(It->second->canClose());
#endif
if (!e->isAccepted())
return;
}
}
// ask all passive views if closable
for (std::list<Gui::BaseView*>::iterator It = d->passive.begin();It!=d->passive.end();It++) {
e->setAccepted((*It)->canClose());
if (!e->isAccepted())
return;
}
if (e->isAccepted()) {
d->isClosing = true;
std::map<const App::Document*, Gui::Document*>::iterator It;
//detach the passive views
//SetActiveDocument(0);
std::list<Gui::BaseView*>::iterator itp = d->passive.begin();
while (itp != d->passive.end()) {
(*itp)->onClose();
itp = d->passive.begin();
}
// remove all documents
size_t cnt = d->documents.size();
while (d->documents.size() > 0 && cnt > 0) {
// destroys also the Gui document
It = d->documents.begin();
App::GetApplication().closeDocument(It->second->getDocument()->getName());
--cnt; // avoid infinite loop
}
}
}
/**
* Activate the matching workbench to the registered workbench handler with name \a name.
* The handler must be an instance of a class written in Python.
* Normally, if a handler gets activated a workbench with the same name gets created unless it
* already exists.
*
* The old workbench gets deactivated before. If the workbench to the handler is already
* active or if the switch fails false is returned.
*/
bool Application::activateWorkbench(const char* name)
{
bool ok = false;
WaitCursor wc;
Workbench* oldWb = WorkbenchManager::instance()->active();
if (oldWb && oldWb->name() == name)
return false; // already active
// we check for the currently active workbench and call its 'Deactivated'
// method, if available
PyObject* pcOldWorkbench = 0;
if (oldWb) {
pcOldWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, oldWb->name().c_str());
}
// get the python workbench object from the dictionary
Base::PyGILStateLocker lock;
PyObject* pcWorkbench = 0;
pcWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, name);
// test if the workbench exists
if (!pcWorkbench)
return false;
try {
std::string type;
Py::Object handler(pcWorkbench);
if (!handler.hasAttr(std::string("__Workbench__"))) {
// call its GetClassName method if possible
Py::Callable method(handler.getAttr(std::string("GetClassName")));
Py::Tuple args;
Py::String result(method.apply(args));
type = result.as_std_string();
if (Base::Type::fromName(type.c_str()).isDerivedFrom(Gui::PythonBaseWorkbench::getClassTypeId())) {
Workbench* wb = WorkbenchManager::instance()->createWorkbench(name, type);
handler.setAttr(std::string("__Workbench__"), Py::Object(wb->getPyObject(), true));
}
// import the matching module first
Py::Callable activate(handler.getAttr(std::string("Initialize")));
activate.apply(args);
// Dependent on the implementation of a workbench handler the type
// can be defined after the call of Initialize()
if (type.empty()) {
Py::String result(method.apply(args));
type = result.as_std_string();
}
}
// does the Python workbench handler have changed the workbench?
Workbench* curWb = WorkbenchManager::instance()->active();
if (curWb && curWb->name() == name)
ok = true; // already active
// now try to create and activate the matching workbench object
else if (WorkbenchManager::instance()->activate(name, type)) {
getMainWindow()->activateWorkbench(QString::fromAscii(name));
this->signalActivateWorkbench(name);
ok = true;
}
// if we still not have this member then it must be built-in C++ workbench
// which could be created after loading the appropriate module
if (!handler.hasAttr(std::string("__Workbench__"))) {
Workbench* wb = WorkbenchManager::instance()->getWorkbench(name);
if (wb) handler.setAttr(std::string("__Workbench__"), Py::Object(wb->getPyObject(), true));
}
// If the method Deactivate is available we call it
if (pcOldWorkbench) {
Py::Object handler(pcOldWorkbench);
if (handler.hasAttr(std::string("Deactivated"))) {
Py::Object method(handler.getAttr(std::string("Deactivated")));
if (method.isCallable()) {
Py::Tuple args;
Py::Callable activate(method);
activate.apply(args);
}
}
}
if (oldWb)
oldWb->deactivated();
// If the method Activate is available we call it
if (handler.hasAttr(std::string("Activated"))) {
Py::Object method(handler.getAttr(std::string("Activated")));
if (method.isCallable()) {
Py::Tuple args;
Py::Callable activate(method);
activate.apply(args);
}
}
// now get the newly activated workbench
Workbench* newWb = WorkbenchManager::instance()->active();
if (newWb)
newWb->activated();
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
QString msg = QString::fromAscii(e.what());
QRegExp rx;
// ignore '<type 'exceptions.ImportError'>' prefixes
rx.setPattern(QLatin1String("^\\s*<type 'exceptions.ImportError'>:\\s*"));
int pos = rx.indexIn(msg);
while ( pos != -1 ) {
msg = msg.mid(rx.matchedLength());
pos = rx.indexIn(msg);
}
Base::Console().Error("%s\n", (const char*)msg.toAscii());
Base::Console().Log("%s\n", e.getStackTrace().c_str());
if (!d->startingUp) {
wc.restoreCursor();
QMessageBox::critical(getMainWindow(), QObject::tr("Workbench failure"),
QObject::tr("%1").arg(msg));
wc.setWaitCursor();
}
}
return ok;
}
QPixmap Application::workbenchIcon(const QString& wb) const
{
Base::PyGILStateLocker lock;
// get the python workbench object from the dictionary
PyObject* pcWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, wb.toAscii());
// test if the workbench exists
if (pcWorkbench) {
// make a unique icon name
std::stringstream str;
str << static_cast<const void *>(pcWorkbench) << std::ends;
std::string iconName = str.str();
QPixmap icon;
if (BitmapFactory().findPixmapInCache(iconName.c_str(), icon))
return icon;
// get its Icon member if possible
try {
Py::Object handler(pcWorkbench);
Py::Object member = handler.getAttr(std::string("Icon"));
Py::String data(member);
std::string content = data.as_std_string();
// test if in XPM format
QByteArray ary;
int strlen = (int)content.size();
ary.resize(strlen);
for (int j=0; j<strlen; j++)
ary[j]=content[j];
if (ary.indexOf("/* XPM */") > 0) {
// Make sure to remove crap around the XPM data
QList<QByteArray> lines = ary.split('\n');
QByteArray buffer;
buffer.reserve(ary.size()+lines.size());
for (QList<QByteArray>::iterator it = lines.begin(); it != lines.end(); ++it) {
QByteArray trim = it->trimmed();
if (!trim.isEmpty()) {
buffer.append(trim);
buffer.append('\n');
}
}
icon.loadFromData(buffer, "XPM");
}
else {
// is it a file name...
QString file = QString::fromUtf8(content.c_str());
icon.load(file);
if (icon.isNull()) {
// ... or the name of another icon?
icon = BitmapFactory().pixmap(file.toUtf8());
}
}
if (!icon.isNull()) {
BitmapFactory().addPixmapToCache(iconName.c_str(), icon);
}
return icon;
}
catch (Py::Exception& e) {
e.clear();
}
}
return QPixmap();
}
QString Application::workbenchToolTip(const QString& wb) const
{
// get the python workbench object from the dictionary
Base::PyGILStateLocker lock;
PyObject* pcWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, wb.toAscii());
// test if the workbench exists
if (pcWorkbench) {
// get its ToolTip member if possible
try {
Py::Object handler(pcWorkbench);
Py::Object member = handler.getAttr(std::string("ToolTip"));
if (member.isString()) {
Py::String tip(member);
return QString::fromUtf8(tip.as_std_string().c_str());
}
}
catch (Py::Exception& e) {
e.clear();
}
}
return QString();
}
QString Application::workbenchMenuText(const QString& wb) const
{
// get the python workbench object from the dictionary
Base::PyGILStateLocker lock;
PyObject* pcWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, wb.toAscii());
// test if the workbench exists
if (pcWorkbench) {
// get its ToolTip member if possible
Base::PyGILStateLocker locker;
try {
Py::Object handler(pcWorkbench);
Py::Object member = handler.getAttr(std::string("MenuText"));
if (member.isString()) {
Py::String tip(member);
return QString::fromUtf8(tip.as_std_string().c_str());
}
}
catch (Py::Exception& e) {
e.clear();
}
}
return QString();
}
QStringList Application::workbenches(void) const
{
// If neither 'HiddenWorkbench' nor 'ExtraWorkbench' is set then all workbenches are returned.
const std::map<std::string,std::string>& config = App::Application::Config();
std::map<std::string, std::string>::const_iterator ht = config.find("HiddenWorkbench");
std::map<std::string, std::string>::const_iterator et = config.find("ExtraWorkbench");
std::map<std::string, std::string>::const_iterator st = config.find("StartWorkbench");
const char* start = (st != config.end() ? st->second.c_str() : "<none>");
QStringList hidden, extra;
if (ht != config.end()) {
QString items = QString::fromAscii(ht->second.c_str());
hidden = items.split(QLatin1Char(';'), QString::SkipEmptyParts);
if (hidden.isEmpty())
hidden.push_back(QLatin1String(""));
}
if (et != config.end()) {
QString items = QString::fromAscii(et->second.c_str());
extra = items.split(QLatin1Char(';'), QString::SkipEmptyParts);
if (extra.isEmpty())
extra.push_back(QLatin1String(""));
}
PyObject *key, *value;
Py_ssize_t pos = 0;
QStringList wb;
// insert all items
while (PyDict_Next(_pcWorkbenchDictionary, &pos, &key, &value)) {
/* do something interesting with the values... */
const char* wbName = PyString_AsString(key);
// add only allowed workbenches
bool ok = true;
if (!extra.isEmpty()&&ok) {
ok = (extra.indexOf(QString::fromAscii(wbName)) != -1);
}
if (!hidden.isEmpty()&&ok) {
ok = (hidden.indexOf(QString::fromAscii(wbName)) == -1);
}
// okay the item is visible
if (ok)
wb.push_back(QString::fromAscii(wbName));
// also allow start workbench in case it is hidden
else if (strcmp(wbName, start) == 0)
wb.push_back(QString::fromAscii(wbName));
}
return wb;
}
void Application::setupContextMenu(const char* recipient, MenuItem* items) const
{
Workbench* actWb = WorkbenchManager::instance()->active();
if (actWb) {
// when populating the context-menu of a Python workbench invoke the method
// 'ContextMenu' of the handler object
if (actWb->getTypeId().isDerivedFrom(PythonWorkbench::getClassTypeId())) {
static_cast<PythonWorkbench*>(actWb)->clearContextMenu();
Base::PyGILStateLocker lock;
PyObject* pWorkbench = 0;
pWorkbench = PyDict_GetItemString(_pcWorkbenchDictionary, actWb->name().c_str());
try {
// call its GetClassName method if possible
Py::Object handler(pWorkbench);
Py::Callable method(handler.getAttr(std::string("ContextMenu")));
Py::Tuple args(1);
args.setItem(0, Py::String(recipient));
method.apply(args);
}
catch (Py::Exception& e) {
Py::Object o = Py::type(e);
e.clear();
if (o.isString()) {
Py::String s(o);
std::clog << "Application::setupContextMenu: " << s.as_std_string() << std::endl;
}
}
}
actWb->setupContextMenu(recipient, items);
}
}
bool Application::isClosing(void)
{
return d->isClosing;
}
MacroManager *Application::macroManager(void)
{
return d->macroMngr;
}
CommandManager &Application::commandManager(void)
{
return d->commandManager;
}
void Application::runCommand(bool bForce, const char* sCmd,...)
{
// temp buffer
size_t format_len = std::strlen(sCmd)+4024;
char* format = (char*) malloc(format_len);
va_list namelessVars;
va_start(namelessVars, sCmd); // Get the "..." vars
vsnprintf(format, format_len, sCmd, namelessVars);
va_end(namelessVars);
if (bForce)
d->macroMngr->addLine(MacroManager::Base,format);
else
d->macroMngr->addLine(MacroManager::Gui,format);
try {
Base::Interpreter().runString(format);
}
catch (...) {
// free memory to avoid a leak if an exception occurred
free (format);
throw;
}
free (format);
}
bool Application::runPythonCode(const char* cmd, bool gui, bool pyexc)
{
if (gui)
d->macroMngr->addLine(MacroManager::Gui,cmd);
else
d->macroMngr->addLine(MacroManager::Base,cmd);
try {
Base::Interpreter().runString(cmd);
return true;
}
catch (Base::PyException &e) {
if (pyexc) {
e.ReportException();
Base::Console().Error("Stack Trace: %s\n",e.getStackTrace().c_str());
}
else {
throw; // re-throw to handle in calling instance
}
}
catch (Base::AbortException&) {
}
catch (Base::Exception &e) {
e.ReportException();
}
catch (std::exception &e) {
std::string str;
str += "C++ exception thrown (";
str += e.what();
str += ")";
Base::Console().Error(str.c_str());
}
catch (const char* e) {
Base::Console().Error("%s\n", e);
}
#ifndef FC_DEBUG
catch (...) {
Base::Console().Error("Unknown C++ exception in command thrown\n");
}
#endif
return false;
}
//**************************************************************************
// Init, Destruct and ingleton
typedef void (*_qt_msg_handler_old)(QtMsgType type, const char *msg);
_qt_msg_handler_old old_qtmsg_handler = 0;
void messageHandler(QtMsgType type, const char *msg)
{
#ifdef FC_DEBUG
switch (type)
{
case QtDebugMsg:
Base::Console().Message("%s\n", msg);
break;
case QtWarningMsg:
Base::Console().Warning("%s\n", msg);
break;
case QtFatalMsg:
Base::Console().Error("%s\n", msg);
abort(); // deliberately core dump
}
#ifdef FC_OS_WIN32
if (old_qtmsg_handler)
(*old_qtmsg_handler)(type, msg);
#endif
#else
// do not stress user with Qt internals but write to log file if enabled
Base::Console().Log("%s\n", msg);
#endif
}
#ifdef FC_DEBUG // redirect Coin messages to FreeCAD
void messageHandlerCoin(const SoError * error, void * userdata)
{
if (error && error->getTypeId() == SoDebugError::getClassTypeId()) {
const SoDebugError* dbg = static_cast<const SoDebugError*>(error);
const char* msg = error->getDebugString().getString();
switch (dbg->getSeverity())
{
case SoDebugError::INFO:
Base::Console().Message("%s\n", msg);
break;
case SoDebugError::WARNING:
Base::Console().Warning("%s\n", msg);
break;
default: // error
Base::Console().Error("%s\n", msg);
break;
}
#ifdef FC_OS_WIN32
if (old_qtmsg_handler)
(*old_qtmsg_handler)(QtDebugMsg, msg);
#endif
}
else if (error) {
const char* msg = error->getDebugString().getString();
Base::Console().Log( msg );
}
}
void messageHandlerSoQt(const SbString errmsg, SoQt::FatalErrors errcode, void *userdata)
{
Base::Console().Error( errmsg.getString() );
}
#endif
// To fix bug #0000345 move Q_INIT_RESOURCE() outside initApplication()
static void init_resources()
{
// init resources
Q_INIT_RESOURCE(resource);
Q_INIT_RESOURCE(translation);
}
void Application::initApplication(void)
{
try {
initTypes();
new Base::ScriptProducer( "FreeCADGuiInit", FreeCADGuiInit );
init_resources();
old_qtmsg_handler = qInstallMsgHandler(messageHandler);
}
catch (...) {
// force to flush the log
App::Application::destructObserver();
throw;
}
}
void Application::initTypes(void)
{
// views
Gui::BaseView ::init();
Gui::MDIView ::init();
Gui::View3DInventor ::init();
Gui::AbstractSplitView ::init();
Gui::SplitView3DInventor ::init();
// View Provider
Gui::ViewProvider ::init();
Gui::ViewProviderExtern ::init();
Gui::ViewProviderDocumentObject ::init();
Gui::ViewProviderFeature ::init();
Gui::ViewProviderDocumentObjectGroup ::init();
Gui::ViewProviderDocumentObjectGroupPython ::init();
Gui::ViewProviderGeometryObject ::init();
Gui::ViewProviderInventorObject ::init();
Gui::ViewProviderVRMLObject ::init();
Gui::ViewProviderAnnotation ::init();
Gui::ViewProviderAnnotationLabel ::init();
Gui::ViewProviderPointMarker ::init();
Gui::ViewProviderMeasureDistance ::init();
Gui::ViewProviderPythonFeature ::init();
Gui::ViewProviderPythonGeometry ::init();
// Workbench
Gui::Workbench ::init();
Gui::StdWorkbench ::init();
Gui::BlankWorkbench ::init();
Gui::NoneWorkbench ::init();
Gui::TestWorkbench ::init();
Gui::PythonBaseWorkbench ::init();
Gui::PythonBlankWorkbench ::init();
Gui::PythonWorkbench ::init();
}
namespace Gui {
/** Override QCoreApplication::notify() to fetch exceptions in Qt widgets
* properly that are not handled in the event handler or slot.
*/
class GUIApplication : public GUIApplicationNativeEventAware
{
int systemExit;
public:
GUIApplication(int & argc, char ** argv, int exitcode)
: GUIApplicationNativeEventAware(argc, argv), systemExit(exitcode)
{
}
/**
* Make forwarding events exception-safe and get more detailed information
* where an unhandled exception comes from.
*/
bool notify (QObject * receiver, QEvent * event)
{
if (!receiver && event) {
Base::Console().Log("GUIApplication::notify: Unexpected null receiver, event type: %d\n",
(int)event->type());
}
try {
if (event->type() == Spaceball::ButtonEvent::ButtonEventType ||
event->type() == Spaceball::MotionEvent::MotionEventType)
return processSpaceballEvent(receiver, event);
else
return QApplication::notify(receiver, event);
}
catch (const Base::SystemExitException&) {
qApp->exit(systemExit);
return true;
}
catch (const Base::Exception& e) {
Base::Console().Error("Unhandled Base::Exception caught in GUIApplication::notify.\n"
"The error message is: %s\n", e.what());
}
catch (const std::exception& e) {
Base::Console().Error("Unhandled std::exception caught in GUIApplication::notify.\n"
"The error message is: %s\n", e.what());
}
catch (...) {
Base::Console().Error("Unhandled unknown exception caught in GUIApplication::notify.\n");
}
// Print some more information to the log file (if active) to ease bug fixing
if (receiver && event) {
std::stringstream dump;
dump << "The event type " << (int)event->type() << " was sent to "
<< receiver->metaObject()->className() << "\n";
dump << "Object tree:\n";
if (receiver->isWidgetType()) {
QWidget* w = qobject_cast<QWidget*>(receiver);
while (w) {
dump << "\t";
dump << w->metaObject()->className();
QString name = w->objectName();
if (!name.isEmpty())
dump << " (" << (const char*)name.toUtf8() << ")";
w = w->parentWidget();
if (w)
dump << " is child of\n";
}
std::string str = dump.str();
Base::Console().Log("%s",str.c_str());
}
}
return true;
}
void commitData(QSessionManager &manager)
{
if (manager.allowsInteraction()) {
if (!Gui::getMainWindow()->close()) {
// cancel the shutdown
manager.release();
manager.cancel();
}
}
else {
// no user interaction allowed, thus close all documents and
// the main window
App::GetApplication().closeAllDocuments();
Gui::getMainWindow()->close();
}
}
};
}
void Application::runApplication(void)
{
// A new QApplication
Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
// if application not yet created by the splasher
int argc = App::Application::GetARGC();
int systemExit = 1000;
GUIApplication mainApp(argc, App::Application::GetARGV(), systemExit);
// set application icon and window title
const std::map<std::string,std::string>& cfg = App::Application::Config();
std::map<std::string,std::string>::const_iterator it;
it = cfg.find("Application");
if (it != cfg.end()) {
mainApp.setApplicationName(QString::fromUtf8(it->second.c_str()));
}
else {
mainApp.setApplicationName(QString::fromUtf8(App::GetApplication().getExecutableName()));
}
mainApp.setWindowIcon(Gui::BitmapFactory().pixmap(App::Application::Config()["AppIcon"].c_str()));
QString plugin;
plugin = QString::fromUtf8(App::GetApplication().GetHomePath());
plugin += QLatin1String("/plugins");
QCoreApplication::addLibraryPath(plugin);
// check for OpenGL
if (!QGLFormat::hasOpenGL()) {
QMessageBox::critical(0, QObject::tr("No OpenGL"), QObject::tr("This system does not support OpenGL"));
throw Base::Exception("This system does not support OpenGL");
}
#if QT_VERSION >= 0x040200
if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) {
Base::Console().Log("This system does not support framebuffer objects");
}
#endif
if (!QGLPixelBuffer::hasOpenGLPbuffers()) {
Base::Console().Log("This system does not support pbuffers");
}
QGLFormat::OpenGLVersionFlags version = QGLFormat::openGLVersionFlags ();
#if QT_VERSION >= 0x040500
if (version & QGLFormat::OpenGL_Version_3_0)
Base::Console().Log("OpenGL version 3.0 or higher is present\n");
else
#endif
if (version & QGLFormat::OpenGL_Version_2_1)
Base::Console().Log("OpenGL version 2.1 or higher is present\n");
else if (version & QGLFormat::OpenGL_Version_2_0)
Base::Console().Log("OpenGL version 2.0 or higher is present\n");
else if (version & QGLFormat::OpenGL_Version_1_5)
Base::Console().Log("OpenGL version 1.5 or higher is present\n");
else if (version & QGLFormat::OpenGL_Version_1_4)
Base::Console().Log("OpenGL version 1.4 or higher is present\n");
else if (version & QGLFormat::OpenGL_Version_1_3)
Base::Console().Log("OpenGL version 1.3 or higher is present\n");
else if (version & QGLFormat::OpenGL_Version_1_2)
Base::Console().Log("OpenGL version 1.2 or higher is present\n");
else if (version & QGLFormat::OpenGL_Version_1_1)
Base::Console().Log("OpenGL version 1.1 or higher is present\n");
else if (version & QGLFormat::OpenGL_Version_None)
Base::Console().Log("No OpenGL is present or no OpenGL context is current\n");
Application app(true);
MainWindow mw;
mw.setWindowTitle(mainApp.applicationName());
// set toolbar icon size
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("General");
int size = hGrp->GetInt("ToolbarIconSize", 0);
if (size >= 16) // must not be lower than this
mw.setIconSize(QSize(size,size));
// init the Inventor subsystem
SoDB::init();
SoQt::init(&mw);
SoFCDB::init();
QString home = QString::fromUtf8(App::GetApplication().GetHomePath());
it = cfg.find("WindowTitle");
if (it != cfg.end()) {
QString title = QString::fromUtf8(it->second.c_str());
mw.setWindowTitle(title);
}
it = cfg.find("WindowIcon");
if (it != cfg.end()) {
QString path = QString::fromUtf8(it->second.c_str());
if (QDir(path).isRelative()) {
path = QFileInfo(QDir(home), path).absoluteFilePath();
}
QApplication::setWindowIcon(QIcon(path));
}
it = cfg.find("ProgramLogo");
if (it != cfg.end()) {
QString path = QString::fromUtf8(it->second.c_str());
if (QDir(path).isRelative()) {
path = QFileInfo(QDir(home), path).absoluteFilePath();
}
QPixmap px(path);
if (!px.isNull()) {
QLabel* logo = new QLabel();
logo->setPixmap(px.scaledToHeight(32));
mw.statusBar()->addPermanentWidget(logo, 0);
logo->setFrameShape(QFrame::NoFrame);
}
}
bool hidden = false;
it = cfg.find("StartHidden");
if (it != cfg.end()) {
hidden = true;
}
// show splasher while initializing the GUI
if (!hidden)
mw.startSplasher();
// running the GUI init script
try {
Base::Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADGuiInit"));
}
catch (const Base::Exception& e) {
Base::Console().Error("Error in FreeCADGuiInit.py: %s\n", e.what());
mw.stopSplasher();
throw;
}
// stop splash screen and set immediately the active window that may be of interest
// for scripts using Python binding for Qt
mw.stopSplasher();
mainApp.setActiveWindow(&mw);
// Activate the correct workbench
std::string start = App::Application::Config()["StartWorkbench"];
Base::Console().Log("Init: Activating default workbench %s\n", start.c_str());
start = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
GetASCII("AutoloadModule", start.c_str());
// if the auto workbench is not visible then force to use the default workbech
// and replace the wrong entry in the parameters
QStringList wb = app.workbenches();
if (!wb.contains(QString::fromAscii(start.c_str()))) {
start = App::Application::Config()["StartWorkbench"];
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")->
SetASCII("AutoloadModule", start.c_str());
}
app.activateWorkbench(start.c_str());
// show the main window
if (!hidden) {
Base::Console().Log("Init: Showing main window\n");
mw.loadWindowSettings();
}
//initialize spaceball.
mainApp.initSpaceball(&mw);
#ifdef FC_DEBUG // redirect Coin messages to FreeCAD
SoDebugError::setHandlerCallback( messageHandlerCoin, 0 );
SoQt::setFatalErrorHandler( messageHandlerSoQt, 0 );
#endif
Instance->d->startingUp = false;
#if 0
// processing all command line files
App::Application::processCmdLineFiles();
// Create new document?
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Document");
if (hGrp->GetBool("CreateNewDoc", false)) {
App::GetApplication().newDocument();
}
#else
// gets called once we start the event loop
QTimer::singleShot(0, &mw, SLOT(delayedStartup()));
#endif
// run the Application event loop
Base::Console().Log("Init: Entering event loop\n");
try {
int ret = mainApp.exec();
if (ret == systemExit)
throw Base::SystemExitException();
}
catch (const Base::SystemExitException&) {
Base::Console().Message("System exit\n");
throw;
}
catch (...) {
// catching nasty stuff coming out of the event loop
App::Application::destructObserver();
Base::Console().Error("Event loop left through unhandled exception\n");
throw;
}
Base::Console().Log("Finish: Event loop left\n");
}