Index: kdepimlibs/akonadi/erroroverlay.cpp =================================================================== --- kdepimlibs/akonadi/erroroverlay.cpp (révision 1088137) +++ kdepimlibs/akonadi/erroroverlay.cpp (révision 1088138) @@ -18,7 +18,7 @@ */ #include "erroroverlay_p.h" -#include "servermanager.h" +#include "ui_erroroverlay.h" #include "selftestdialog_p.h" #include <KDebug> @@ -53,7 +53,8 @@ ErrorOverlay::ErrorOverlay( QWidget *baseWidget, QWidget * parent ) : QWidget( parent ? parent : baseWidget->window() ), - mBaseWidget( baseWidget ) + mBaseWidget( baseWidget ), + ui( new Ui::ErrorOverlay ) { Q_ASSERT( baseWidget ); @@ -85,35 +86,26 @@ connect( baseWidget, SIGNAL(destroyed()), SLOT(deleteLater()) ); mPreviousState = mBaseWidget->isEnabled(); - QBoxLayout *topLayout = new QVBoxLayout( this ); - topLayout->addStretch(); - mIconLabel = new QLabel( this ); - mIconLabel->setPixmap( KIcon( QString::fromLatin1("dialog-error") ).pixmap( 64 ) ); - mIconLabel->setAlignment( Qt::AlignHCenter | Qt::AlignVCenter ); - topLayout->addWidget( mIconLabel ); + ui->setupUi( this ); + ui->staticIconLabel->setPixmap( KIcon( QString::fromLatin1("dialog-error") ).pixmap( 64 ) ); + ui->progressIconLabel->setPixmap( KIcon( QLatin1String( "akonadi") ).pixmap( 32 ) ); - mDescLabel = new QLabel( this ); - mDescLabel->setText( i18n( "<p style=\"color: white;\"><b>Akonadi not operational.<br/>" + ui->staticDescriptionLabel->setText( i18n( "<p><b>Akonadi not operational.<br/>" "<a href=\"details\" style=\"color: white;\">Details...</a></b></p>" ) ); - mDescLabel->setWordWrap( true ); - mDescLabel->setAlignment( Qt::AlignHCenter | Qt::AlignVCenter ); - connect( mDescLabel, SIGNAL(linkActivated(QString)), SLOT(linkActivated()) ); - topLayout->addWidget( mDescLabel ); - topLayout->addStretch(); + connect( ui->staticDescriptionLabel, SIGNAL(linkActivated(QString)), SLOT(linkActivated()) ); - setToolTip( i18n( "The Akonadi personal information management framework is not operational.\n" + ui->staticPage->setToolTip( i18n( "The Akonadi personal information management framework is not operational.\n" "Click on \"Details...\" to obtain detailed information on this problem." ) ); + ui->stackWidget->setCurrentWidget( ui->staticPage ); - mOverlayActive = ServerManager::isRunning(); - if ( mOverlayActive ) - started(); - else - stopped(); - connect( ServerManager::self(), SIGNAL(started()), SLOT(started()) ); - connect( ServerManager::self(), SIGNAL(stopped()), SLOT(stopped()) ); + const ServerManager::State state = ServerManager::state(); + mOverlayActive = state == ServerManager::Running; + serverStateChanged( state ); + connect( ServerManager::self(), SIGNAL(stateChanged(ServerManager::State)), SLOT(serverStateChanged(ServerManager::State))); QPalette p = palette(); p.setColor( backgroundRole(), QColor( 0, 0, 0, 128 ) ); + p.setColor( foregroundRole(), Qt::white ); setPalette( p ); setAutoFillBackground( true ); @@ -174,25 +166,44 @@ dlg.exec(); } -void ErrorOverlay::started() +void ErrorOverlay::serverStateChanged( ServerManager::State state ) { if ( !mBaseWidget ) return; - mOverlayActive = false; - hide(); - mBaseWidget->setEnabled( mPreviousState ); -} -void ErrorOverlay::stopped() -{ - if ( !mBaseWidget ) - return; - mOverlayActive = true; - if ( mBaseWidget->isVisible() ) - show(); - mPreviousState = mBaseWidget->isEnabled(); - mBaseWidget->setEnabled( false ); - reposition(); + if ( state == ServerManager::Running && mOverlayActive ) { + mOverlayActive = false; + hide(); + mBaseWidget->setEnabled( mPreviousState ); + } else if ( !mOverlayActive ) { + mOverlayActive = true; + if ( mBaseWidget->isVisible() ) + show(); + mPreviousState = mBaseWidget->isEnabled(); + mBaseWidget->setEnabled( false ); + reposition(); + } + + if ( mOverlayActive ) { + switch ( state ) { + case ServerManager::NotRunning: + case ServerManager::Broken: + ui->stackWidget->setCurrentWidget( ui->staticPage ); + break; + case ServerManager::Starting: + ui->progressPage->setToolTip( i18n( "Akonadi personal information management service is starting...") ); + ui->progressDescriptionLabel->setText( i18n( "Akonadi personal information management service is starting...") ); + ui->stackWidget->setCurrentWidget( ui->progressPage ); + break; + case ServerManager::Stopping: + ui->progressPage->setToolTip( i18n( "Akonadi personal information management service is shutting down...") ); + ui->progressDescriptionLabel->setText( i18n( "Akonadi personal information management service is shutting down...") ); + ui->stackWidget->setCurrentWidget( ui->progressPage ); + break; + case ServerManager::Running: + break; + } + } } //@endcond Index: kdepimlibs/akonadi/erroroverlay.ui =================================================================== --- kdepimlibs/akonadi/erroroverlay.ui (révision 0) +++ kdepimlibs/akonadi/erroroverlay.ui (révision 1088138) @@ -0,0 +1,171 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ErrorOverlay</class> + <widget class="QWidget" name="ErrorOverlay"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QStackedWidget" name="stackWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="staticPage"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>118</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="staticIconLabel"> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="staticDescriptionLabel"> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>118</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="progressPage"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>113</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="progressIconLabel"> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QProgressBar" name="progressBar"> + <property name="maximum"> + <number>0</number> + </property> + <property name="value"> + <number>6735</number> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="progressDescriptionLabel"> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>113</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> Index: kdepimlibs/akonadi/servermanager.cpp =================================================================== --- kdepimlibs/akonadi/servermanager.cpp (révision 1088137) +++ kdepimlibs/akonadi/servermanager.cpp (révision 1088138) @@ -30,7 +30,10 @@ #include <KGlobal> #include <QtDBus> +#include <QTimer> +#include <boost/scoped_ptr.hpp> + #define AKONADI_CONTROL_SERVICE QLatin1String("org.freedesktop.Akonadi.Control") #define AKONADI_SERVER_SERVICE QLatin1String("org.freedesktop.Akonadi") @@ -40,9 +43,12 @@ { public: ServerManagerPrivate() : - instance( new ServerManager( this ) ) + instance( new ServerManager( this ) ), mState( ServerManager::NotRunning ), mSafetyTimer( new QTimer ) { - operational = instance->isRunning(); + mState = instance->state(); + mSafetyTimer->setSingleShot( true ); + mSafetyTimer->setInterval( 10000 ); + QObject::connect( mSafetyTimer.get(), SIGNAL(timeout()), instance, SLOT(timeout()) ); } ~ServerManagerPrivate() @@ -62,19 +68,37 @@ void checkStatusChanged() { - const bool status = instance->isRunning(); - if ( status == operational ) - return; - operational = status; - if ( operational ) - emit instance->started(); - else - emit instance->stopped(); + setState( instance->state() ); } + void setState( ServerManager::State state ) + { + + if ( mState != state ) { + mState = state; + emit instance->stateChanged( state ); + if ( state == ServerManager::Running ) + emit instance->started(); + else if ( state == ServerManager::NotRunning || state == ServerManager::Broken ) + emit instance->stopped(); + + if ( state == ServerManager::Starting || state == ServerManager::Stopping ) + mSafetyTimer->start(); + else + mSafetyTimer->stop(); + } + } + + void timeout() + { + if ( mState == ServerManager::Starting || mState == ServerManager::Stopping ) + setState( ServerManager::Broken ); + } + ServerManager *instance; static int serverProtocolVersion; - bool operational; + ServerManager::State mState; + boost::scoped_ptr<QTimer> mSafetyTimer; }; int ServerManagerPrivate::serverProtocolVersion = -1; @@ -103,6 +127,20 @@ bool ServerManager::start() { + const bool controlRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE ); + const bool serverRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_SERVER_SERVICE ); + if ( controlRegistered && serverRegistered ) + return true; + + // TODO: use AKONADI_CONTROL_SERVICE_LOCK instead once we depend on a recent enough Akonadi server + const bool controlLockRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE + QLatin1String(".lock") ); + if ( controlLockRegistered || controlRegistered ) { + kDebug() << "Akonadi server is already starting up"; + sInstance->setState( Starting ); + return true; + } + + kDebug() << "executing akonadi_control"; const bool ok = QProcess::startDetached( QLatin1String("akonadi_control") ); if ( !ok ) { kWarning() << "Unable to execute akonadi_control, falling back to D-Bus auto-launch"; @@ -113,6 +151,7 @@ return false; } } + sInstance->setState( Starting ); return true; } @@ -124,6 +163,7 @@ if ( !iface.isValid() ) return false; iface.call( QDBus::NoBlock, QString::fromLatin1("shutdown") ); + sInstance->setState( Stopping ); return true; } @@ -136,31 +176,57 @@ bool ServerManager::isRunning() { - if ( !QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE ) || - !QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_SERVER_SERVICE ) ) { - return false; + return state() == Running; +} + +ServerManager::State ServerManager::state() +{ + ServerManager::State previousState = NotRunning; + if ( sInstance.exists() ) // be careful, this is called from the ServerManager::Private ctor, so using sInstance unprotected can cause infinite recursion + previousState = sInstance->mState; + + const bool controlRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE ); + const bool serverRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_SERVER_SERVICE ); + if ( controlRegistered && serverRegistered ) { + // check if the server protocol is recent enough + if ( sInstance.exists() ) { + if ( Internal::serverProtocolVersion() >= 0 && + Internal::serverProtocolVersion() < SessionPrivate::minimumProtocolVersion() ) + return Broken; + } + + // HACK see if we are a agent ourselves and skip the test below which can in some cases deadlock the server + // and is not really needed in this case anyway since we happen to know at least one agent is available + QObject *obj = QDBusConnection::sessionBus().objectRegisteredAt( QLatin1String("/") ); + if ( obj && dynamic_cast<AgentBase*>( obj ) ) + return Running; + + // besides the running server processes we also need at least one resource to be operational + AgentType::List agentTypes = AgentManager::self()->types(); + foreach ( const AgentType &type, agentTypes ) { + if ( type.capabilities().contains( QLatin1String("Resource") ) ) + return Running; + } + return Broken; } - // check if the server protocol is recent enough - if ( sInstance.exists() ) { - if ( Internal::serverProtocolVersion() >= 0 && - Internal::serverProtocolVersion() < SessionPrivate::minimumProtocolVersion() ) - return false; + // TODO: use AKONADI_CONTROL_SERVICE_LOCK instead once we depend on a recent enough Akonadi server + const bool controlLockRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_CONTROL_SERVICE + QLatin1String(".lock") ); + if ( controlLockRegistered || controlRegistered ) { + kDebug() << "Akonadi server is already starting up"; + if ( previousState == Running ) + return NotRunning; // we don't know if it's starting or stopping, probably triggered by someone else + return previousState; } - // HACK see if we are a agent ourselves and skip the test below which can in some cases deadlock the server - // and is not really needed in this case anyway since we happen to know at least one agent is available - QObject *obj = QDBusConnection::sessionBus().objectRegisteredAt( QLatin1String("/") ); - if ( obj && dynamic_cast<AgentBase*>( obj ) ) - return true; + if ( serverRegistered ) { + kWarning() << "Akonadi server running without control process!"; + return Broken; + } - // besides the running server processes we also need at least one resource to be operational - AgentType::List agentTypes = AgentManager::self()->types(); - foreach ( const AgentType &type, agentTypes ) { - if ( type.capabilities().contains( QLatin1String("Resource") ) ) - return true; - } - return false; + if ( previousState == Starting || previousState == Broken ) // valid cases where nothing might be running (yet) + return previousState; + return NotRunning; } int Internal::serverProtocolVersion() Index: kdepimlibs/akonadi/erroroverlay_p.h =================================================================== --- kdepimlibs/akonadi/erroroverlay_p.h (révision 1088137) +++ kdepimlibs/akonadi/erroroverlay_p.h (révision 1088138) @@ -20,11 +20,17 @@ #ifndef AKONADI_ERROROVERLAY_P_H #define AKONADI_ERROROVERLAY_P_H +#include "servermanager.h" + #include <QtCore/QPointer> #include <QtGui/QWidget> -class QLabel; +#include <boost/scoped_ptr.hpp> +namespace Ui { + class ErrorOverlay; +} + namespace Akonadi { /** @@ -54,15 +60,13 @@ private slots: void linkActivated(); - void started(); - void stopped(); + void serverStateChanged( ServerManager::State state ); private: QPointer<QWidget> mBaseWidget; - QLabel *mIconLabel; - QLabel *mDescLabel; bool mPreviousState; bool mOverlayActive; + boost::scoped_ptr<Ui::ErrorOverlay> ui; }; Index: kdepimlibs/akonadi/servermanager.h =================================================================== --- kdepimlibs/akonadi/servermanager.h (révision 1088137) +++ kdepimlibs/akonadi/servermanager.h (révision 1088138) @@ -42,6 +42,15 @@ { Q_OBJECT public: + /** Enum for the various states the server can be in. */ + enum State { + NotRunning, ///< Server is not running, could be noone started it yet or it failed to start. + Starting, ///< Server was started but is not yet running. + Running, ///< Server is running and operational. + Stopping, ///< Server is shutting down. + Broken ///< Server is not operational and an error has been detected. + }; + /** * Starts the server. This method returns imediately and does not wait * until the server is actually up and running. It is not checked if the @@ -74,6 +83,11 @@ static bool isRunning(); /** + * Returns the state of the server. + */ + static State state(); + + /** * Returns the singleton instance of this class, for connecting to its * signals */ @@ -90,6 +104,11 @@ */ void stopped(); + /** + * Emitted when the server state changes. + */ + void stateChanged( ServerManager::State state ); + private: //@cond PRIVATE friend class ServerManagerPrivate; @@ -97,6 +116,7 @@ ServerManagerPrivate* const d; Q_PRIVATE_SLOT( d, void serviceOwnerChanged( const QString&, const QString&, const QString& ) ) Q_PRIVATE_SLOT( d, void checkStatusChanged() ) + Q_PRIVATE_SLOT( d, void timeout() ) //@endcond };