//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see .
//
//---------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include // for QCoreApplication::setAttribute
#include
#include
#include "WMainWindow.h" // this has to be included before any other includes
#include "WApplication.h"
#include "core/common/WConditionOneShot.h"
#include "core/common/WIOTools.h"
#include "core/common/WPathHelper.h"
#include "core/dataHandler/WDataHandler.h"
#include "core/dataHandler/WSubject.h"
#include "core/graphicsEngine/WGraphicsEngine.h"
#include "core/kernel/WKernel.h"
#include "core/kernel/WModuleContainer.h"
#include "core/kernel/WProjectFile.h"
#include "core/kernel/WROIManager.h"
#include "controlPanel/WQtControlPanel.h"
#include "events/WModuleAssocEvent.h"
#include "events/WModuleConnectEvent.h"
#include "events/WModuleCrashEvent.h"
#include "events/WModuleDisconnectEvent.h"
#include "events/WModuleReadyEvent.h"
#include "events/WModuleRemovedEvent.h"
#include "events/WRoiAssocEvent.h"
#include "events/WRoiRemoveEvent.h"
#include "events/WUpdateTextureSorterEvent.h"
#include "events/WLogEvent.h"
#include "WQtModuleConfig.h"
#include "WQtGui.h"
WMainWindow* WQtGui::m_mainWindow = NULL;
QSettings* WQtGui::m_settings = NULL;
WQtGui::WQtGui( const boost::program_options::variables_map& options, int argc, char** argv )
: WUI( argc, argv ),
m_optionsMap( options ),
m_loadDeferredOnce( true )
{
QCoreApplication::setAttribute( Qt::AA_X11InitThreads );
}
WQtGui::~WQtGui()
{
delete m_settings;
}
void WQtGui::moduleError( boost::shared_ptr< WModule > module, const WException& exception )
{
QCoreApplication::postEvent( m_mainWindow, new WModuleCrashEvent( module, exception.what() ) );
QCoreApplication::postEvent( m_mainWindow->getNetworkEditor(), new WModuleCrashEvent( module, exception.what() ) );
}
WMainWindow* WQtGui::getMainWindow()
{
return m_mainWindow;
}
WIconManager* WQtGui::getIconManager()
{
return getMainWindow()->getIconManager();
}
void WQtGui::deferredLoad()
{
m_deferredLoadMutex.lock();
if( m_loadDeferredOnce )
{
m_loadDeferredOnce = false;
wlog::debug( "OpenWalnut" ) << "Deferred loading of data and project files.";
bool useInputFileNameAsProject = false;
// check if we want to load data due to command line and call the respective function
if( m_optionsMap.count( "input" ) )
{
std::vector< std::string > dataFileNames = m_optionsMap["input"].as< std::vector< std::string > >();
useInputFileNameAsProject = ( dataFileNames.size() == 1
&& ( boost::algorithm::ends_with( dataFileNames[0], ".owp" )
|| boost::algorithm::ends_with( dataFileNames[0], ".owproj" ) ) );
if( !useInputFileNameAsProject )
{
m_kernel->loadDataSets( dataFileNames );
}
}
// Load project file
if( m_optionsMap.count( "project" ) || useInputFileNameAsProject )
{
std::string projectFileName;
if( useInputFileNameAsProject )
{
projectFileName = m_optionsMap["input"].as< std::vector< std::string > >()[0];
}
else
{
projectFileName = m_optionsMap["project"].as< std::string >();
}
try
{
// This call is asynchronous. It parses the file and the starts a thread to actually do all the stuff
m_mainWindow->asyncProjectLoad( projectFileName );
}
catch( const WException& e )
{
wlog::error( "GUI" ) << "Project file \"" << m_optionsMap["project"].as< std::string >() << "\" could not be loaded. Message: " <<
e.what();
}
}
}
m_deferredLoadMutex.unlock();
}
int WQtGui::run()
{
m_splash = NULL;
// init logger
m_loggerConnection = WLogger::getLogger()->subscribeSignal( WLogger::AddLog, boost::bind( &WQtGui::slotAddLog, this, _1 ) );
// make qapp instance before using the applicationDirPath() function
#ifdef Q_OS_OSX
//TODO(mario): this should run on all platforms but crashes at least on Linux right now. Therefore, I only use it on OSX
WApplication appl( m_argc, m_argv, true );
#else
// TODO(mario): I want a WApplication here for session handling but that code crashes
QApplication appl( m_argc, m_argv, true );
#endif
// the call path of the application, this uses QApplication which needs to be instantiated.
boost::filesystem::path walnutBin = boost::filesystem::path( QApplication::applicationDirPath().toStdString() );
// setup path helper which provides several paths to others
#ifdef Q_OS_OSX
// apple has a special file hierarchy in so-called bundles
// this code determines whether we are started from a bundle context
// and sets the paths according to Apple's guidelines inside the bundle
if( QApplication::applicationDirPath().endsWith( "/MacOS" ) )
{
// we are in a bundle
// TODO(mario): apply default OSX behavior of using $HOME/Library/OpenWalnut ?
WPathHelper::getPathHelper()->setBasePathsOSXBundle( walnutBin, boost::filesystem::path( QDir::homePath().toStdString() ) / ".OpenWalnut" );
}
else
{
// assume standard behavior
WPathHelper::getPathHelper()->setBasePaths( walnutBin, boost::filesystem::path( QDir::homePath().toStdString() ) / ".OpenWalnut" );
}
#else
// on all other platforms, get the home directory form Qt and the path from the application binary location
WPathHelper::getPathHelper()->setBasePaths( walnutBin, boost::filesystem::path( QDir::homePath().toStdString() ) / ".OpenWalnut" );
#endif
QPixmap splashPixmap( QString::fromStdString( ( WPathHelper::getPathHelper()->getSharePath() / "qtgui" / "splash.png" ).string() ) );
m_splash = new QSplashScreen( splashPixmap );
m_splash->show();
// with the correct paths, we can load the settings
m_settings = new QSettings( QString::fromStdString( ( WPathHelper::getHomePath() / "config.qtgui" ).string() ), QSettings::IniFormat );
WQtModuleConfig::initPathHelper();
// get the minimum log level from preferences
LogLevel logLevel = static_cast< LogLevel >( WQtGui::getSettings().value( "qtgui/logLevel", LL_DEBUG ).toInt() );
WLogger::getLogger()->setDefaultLogLevel( logLevel );
// print the first output
wlog::debug( "OpenWalnut" ) << "OpenWalnut binary path: " << walnutBin;
wlog::info( "GUI" ) << "Bringing up GUI";
// startup graphics engine
m_ge = WGraphicsEngine::getGraphicsEngine();
// and startup kernel
m_kernel = boost::shared_ptr< WKernel >( WKernel::instance( m_ge, shared_from_this() ) );
m_kernel->run();
m_kernel->subscribeSignal( WKernel::KERNEL_STARTUPCOMPLETE, boost::bind( &WQtGui::deferredLoad, this ) );
t_ModuleErrorSignalHandlerType func = boost::bind( &WQtGui::moduleError, this, _1, _2 );
m_kernel->getRootContainer()->addDefaultNotifier( WM_ERROR, func );
// bind the GUI's slot with the signals provided by the kernel
WCondition::t_ConditionNotifierType newDatasetSignal = boost::bind( &WQtGui::slotUpdateTextureSorter, this );
WDataHandler::getDefaultSubject()->getListChangeCondition()->subscribeSignal( newDatasetSignal );
// Assoc Event
t_ModuleGenericSignalHandlerType assocSignal = boost::bind( &WQtGui::slotAddDatasetOrModuleToTree, this, _1 );
m_kernel->getRootContainer()->addDefaultNotifier( WM_ASSOCIATED, assocSignal );
// Ready Event
t_ModuleGenericSignalHandlerType readySignal = boost::bind( &WQtGui::slotActivateDatasetOrModuleInTree, this, _1 );
m_kernel->getRootContainer()->addDefaultNotifier( WM_READY, readySignal );
// Remove Event
t_ModuleGenericSignalHandlerType removedSignal = boost::bind( &WQtGui::slotRemoveDatasetOrModuleInTree, this, _1 );
m_kernel->getRootContainer()->addDefaultNotifier( WM_REMOVED, removedSignal );
// Connect Event
t_GenericSignalHandlerType connectionEstablishedSignal = boost::bind( &WQtGui::slotConnectionEstablished, this, _1, _2 );
m_kernel->getRootContainer()->addDefaultNotifier( CONNECTION_ESTABLISHED, connectionEstablishedSignal );
// Disconnect Event
t_GenericSignalHandlerType connectionClosedSignal = boost::bind( &WQtGui::slotConnectionClosed, this, _1, _2 );
m_kernel->getRootContainer()->addDefaultNotifier( CONNECTION_CLOSED, connectionClosedSignal );
boost::shared_ptr< boost::function< void( osg::ref_ptr< WROI > ) > > assocRoiSignal;
assocRoiSignal =
boost::shared_ptr< boost::function< void( osg::ref_ptr< WROI > ) > >(
new boost::function< void( osg::ref_ptr< WROI > ) > ( boost::bind( &WQtGui::slotAddRoiToTree, this, _1 ) ) );
m_kernel->getRoiManager()->addAddNotifier( assocRoiSignal );
boost::shared_ptr< boost::function< void( osg::ref_ptr< WROI > ) > > removeRoiSignal;
removeRoiSignal =
boost::shared_ptr< boost::function< void( osg::ref_ptr< WROI > ) > >(
new boost::function< void( osg::ref_ptr< WROI > ) > ( boost::bind( &WQtGui::slotRemoveRoiFromTree, this, _1 ) ) );
m_kernel->getRoiManager()->addRemoveNotifier( removeRoiSignal );
// create the window
m_mainWindow = new WMainWindow( m_splash );
m_widgetFactory = WUIQtWidgetFactory::SPtr( new WUIQtWidgetFactory( m_mainWindow ) );
#ifdef Q_OS_OSX
//TODO(mario): this should run on all platforms but crashes at least on Linux right now. Therefore, I only use it on OSX
appl.setMyMainWidget( m_mainWindow );
#endif
m_mainWindow->setupGUI();
m_mainWindow->show();
// connect loader signal with kernel
#ifdef _WIN32
getLoadButtonSignal()->connect( boost::bind( &WKernel::loadDataSetsSynchronously, m_kernel, _1, false ) );
#else
getLoadButtonSignal()->connect( boost::bind( &WKernel::loadDataSets, m_kernel, _1, false ) );
#endif
// now we are initialized
m_isInitialized( true );
// run
int qtRetCode = appl.exec();
delete m_mainWindow;
m_mainWindow = NULL; // the log slot needs this to be null now
// signal everybody to shut down properly.
WKernel::getRunningKernel()->wait( true );
WKernel::getRunningKernel()->getGraphicsEngine()->wait( true );
m_loggerConnection.disconnect();
return qtRetCode;
}
void WQtGui::slotUpdateTextureSorter()
{
// create a new event for this and insert it into event queue
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WUpdateTextureSorterEvent() );
}
void WQtGui::slotAddLog( const WLogEntry& entry )
{
// emit event but the main window might not be available. Check this.
// NOTE: we disable debug log messages completely, since their extensive use in some modules causes SEVERE slowdown of the GUI as millions of
// events need to be handled each GUI event loop. See issue #283 for details.
if( m_mainWindow && ( entry.getLogLevel() != LL_DEBUG ) )
{
QCoreApplication::postEvent( m_mainWindow, new WLogEvent( entry ) );
}
}
void WQtGui::slotAddDatasetOrModuleToTree( boost::shared_ptr< WModule > module )
{
// create a new event for this and insert it into event queue
if( m_mainWindow->getNetworkEditor() )
{
QCoreApplication::postEvent( m_mainWindow->getNetworkEditor(), new WModuleAssocEvent( module ) );
}
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WModuleAssocEvent( module ) );
}
void WQtGui::slotAddRoiToTree( osg::ref_ptr< WROI > roi )
{
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WRoiAssocEvent( roi ) );
}
void WQtGui::slotRemoveRoiFromTree( osg::ref_ptr< WROI > roi )
{
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WRoiRemoveEvent( roi ) );
}
void WQtGui::slotActivateDatasetOrModuleInTree( boost::shared_ptr< WModule > module )
{
// create a new event for this and insert it into event queue
if( m_mainWindow->getNetworkEditor() )
{
QCoreApplication::postEvent( m_mainWindow->getNetworkEditor(), new WModuleReadyEvent( module ) );
}
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WModuleReadyEvent( module ) );
QCoreApplication::postEvent( m_mainWindow, new WModuleReadyEvent( module ) );
}
void WQtGui::slotRemoveDatasetOrModuleInTree( boost::shared_ptr< WModule > module )
{
// create a new event for this and insert it into event queue
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WModuleRemovedEvent( module ) );
QCoreApplication::postEvent( m_mainWindow, new WModuleRemovedEvent( module ) );
if( m_mainWindow->getNetworkEditor() )
{
QCoreApplication::postEvent( m_mainWindow->getNetworkEditor(), new WModuleRemovedEvent( module ) );
}
}
void WQtGui::slotConnectionEstablished( boost::shared_ptr in, boost::shared_ptr out )
{
// create a new event for this and insert it into event queue
if( in->isInputConnector() )
{
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WModuleConnectEvent( in, out ) );
if( m_mainWindow->getNetworkEditor() )
{
QCoreApplication::postEvent( m_mainWindow->getNetworkEditor(), new WModuleConnectEvent( in, out ) );
}
}
else
{
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WModuleConnectEvent( out, in ) );
if( m_mainWindow->getNetworkEditor() )
{
QCoreApplication::postEvent( m_mainWindow->getNetworkEditor(), new WModuleConnectEvent( out, in ) );
}
}
}
void WQtGui::slotConnectionClosed( boost::shared_ptr in, boost::shared_ptr out )
{
// create a new event for this and insert it into event queue
if( in->isInputConnector() )
{
if( m_mainWindow->getNetworkEditor() )
{
QCoreApplication::postEvent( m_mainWindow->getNetworkEditor(), new WModuleDisconnectEvent( in, out ) );
}
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WModuleDisconnectEvent( in, out ) );
}
else
{
if( m_mainWindow->getNetworkEditor() )
{
QCoreApplication::postEvent( m_mainWindow->getNetworkEditor(), new WModuleDisconnectEvent( out, in ) );
}
QCoreApplication::postEvent( m_mainWindow->getControlPanel(), new WModuleDisconnectEvent( out, in ) );
}
}
boost::shared_ptr< WModule > WQtGui::getSelectedModule()
{
return m_mainWindow->getControlPanel()->getSelectedModule();
}
boost::signals2::signal1< void, std::vector< std::string > >* WQtGui::getLoadButtonSignal()
{
return m_mainWindow->getLoaderSignal();
}
QSettings& WQtGui::getSettings()
{
return *m_settings;
}
const boost::program_options::variables_map& WQtGui::getOptionMap() const
{
return m_optionsMap;
}
WUIWidgetFactory::SPtr WQtGui::getWidgetFactory() const
{
return m_widgetFactory;
}
void WQtGui::execInGUIThread( boost::function< void( void ) > functor, WCondition::SPtr notify )
{
if( !notify )
{
// the user did not specify a condition. We create our own
notify = WCondition::SPtr( new WConditionOneShot() );
}
WDeferredCallEvent* ev = new WDeferredCallEvent( functor, notify );
QCoreApplication::postEvent( getMainWindow(), ev );
notify->wait();
}
void WQtGui::execInGUIThreadAsync( boost::function< void( void ) > functor, WCondition::SPtr notify )
{
WDeferredCallEvent* ev = new WDeferredCallEvent( functor, notify );
QCoreApplication::postEvent( getMainWindow(), ev );
}