/* This file is part of Akonadi. Copyright (c) 2006 Tobias Koenig This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "agentwidget.h" #include "agentconfigdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class TextDialog : public KDialog { public: TextDialog( QWidget *parent = 0 ) : KDialog( parent ) { setButtons( Ok ); mText = new KTextEdit; mText->setReadOnly(true); setMainWidget( mText ); setInitialSize( QSize( 400, 600 ) ); } void setText( const QString &text ) { mText->setPlainText( text ); } private: KTextEdit *mText; }; using namespace Akonadi; AgentWidget::AgentWidget( QWidget *parent ) : QWidget( parent ) { ui.setupUi( this ); connect( ui.instanceWidget, SIGNAL(doubleClicked(Akonadi::AgentInstance)), SLOT(configureAgent()) ); connect( ui.instanceWidget, SIGNAL(currentChanged(Akonadi::AgentInstance,Akonadi::AgentInstance)), SLOT(currentChanged()) ); connect( ui.instanceWidget, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showContextMenu(QPoint)) ); connect( ui.instanceWidget->view()->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(selectionChanged()) ); connect( ui.instanceWidget->view()->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(selectionChanged()) ); connect( ui.instanceWidget->view()->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(slotDataChanged(QModelIndex,QModelIndex)) ); currentChanged(); ui.addButton->setGuiItem( KStandardGuiItem::add() ); connect( ui.addButton, SIGNAL(clicked()), this, SLOT(addAgent()) ); ui.removeButton->setGuiItem( KStandardGuiItem::remove() ); connect( ui.removeButton, SIGNAL(clicked()), this, SLOT(removeAgent()) ); mConfigMenu = new QMenu( i18n("Configure"), this ); mConfigMenu->addAction( i18n("Configure Natively..."), this, SLOT(configureAgent()) ); mConfigMenu->addAction( i18n("Configure Remotely..."), this, SLOT(configureAgentRemote()) ); mConfigMenu->setIcon( KStandardGuiItem::configure().icon() ); ui.configButton->setGuiItem( KStandardGuiItem::configure() ); ui.configButton->setMenu( mConfigMenu ); connect( ui.configButton, SIGNAL(clicked()), this, SLOT(configureAgent()) ); mSyncMenu = new QMenu( i18n("Synchronize"), this ); mSyncMenu->addAction( i18n("Synchronize All"), this, SLOT(synchronizeAgent()) ); mSyncMenu->addAction( i18n("Synchronize Collection Tree"), this, SLOT(synchronizeTree()) ); mSyncMenu->setIcon( KIcon("view-refresh" ) ); ui.syncButton->setMenu( mSyncMenu ); ui.syncButton->setIcon( KIcon( "view-refresh" ) ); connect( ui.syncButton, SIGNAL(clicked()), this, SLOT(synchronizeAgent()) ); ui.abortButton->setIcon( KIcon("dialog-cancel") ); connect( ui.abortButton, SIGNAL(clicked()), this, SLOT(abortAgent()) ); ui.restartButton->setIcon( KIcon( "system-reboot" ) ); //FIXME: Is using system-reboot icon here a good idea? connect( ui.restartButton, SIGNAL(clicked()), SLOT(restartAgent()) ); ui.mFilterAccount->setProxy( ui.instanceWidget->agentFilterProxyModel() ); ui.mFilterAccount->lineEdit()->setTrapReturnKey( true ); Control::widgetNeedsAkonadi( this ); } void AgentWidget::addAgent() { QPointer dlg = new Akonadi::AgentTypeDialog( this ); if ( dlg->exec() ) { const AgentType agentType = dlg->agentType(); if ( agentType.isValid() ) { AgentInstanceCreateJob *job = new AgentInstanceCreateJob( agentType, this ); job->configure( this ); job->start(); // TODO: check result } } delete dlg; } void AgentWidget::selectionChanged() { const bool multiSelection = ui.instanceWidget->view()->selectionModel()->selectedRows().size() > 1; // Only agent removal, sync and restart is possible when multiple items are selected. ui.configButton->setDisabled( multiSelection ); // Restarting an agent is not possible if it's in Running status... (see AgentProcessInstance::restartWhenIdle) AgentInstance agent = ui.instanceWidget->currentAgentInstance(); ui.restartButton->setEnabled( agent.isValid() && agent.status() != 1 ); } void AgentWidget::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& /*bottomRight*/ ) { QList selectedRows = ui.instanceWidget->view()->selectionModel()->selectedRows(); if ( selectedRows.isEmpty() ) { selectedRows.append( ui.instanceWidget->view()->selectionModel()->currentIndex() ); } QList rows; Q_FOREACH( const QModelIndex& index, selectedRows ) { rows.append( index.row() ); } qSort( rows ); // Assume topLeft.row == bottomRight.row if ( topLeft.row() >= rows.first() && topLeft.row() <= rows.last() ) { selectionChanged(); // depends on status currentChanged(); } } void AgentWidget::removeAgent() { QList list = ui.instanceWidget->selectedAgentInstances(); if ( !list.isEmpty() ) { if ( KMessageBox::questionYesNo( this, i18np( "Do you really want to delete the selected agent instance?", "Do you really want to delete these %1 agent instances?", list.size() ), list.size() == 1 ? i18n( "Agent Deletion" ) : i18n( "Multiple Agent Deletion" ), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous ) == KMessageBox::Yes ) { foreach( const AgentInstance &agent, list ) AgentManager::self()->removeInstance( agent ); } } } void AgentWidget::configureAgent() { AgentInstance agent = ui.instanceWidget->currentAgentInstance(); if ( agent.isValid() ) agent.configure( this ); } void AgentWidget::configureAgentRemote() { AgentInstance agent = ui.instanceWidget->currentAgentInstance(); if ( agent.isValid() ) { QPointer dlg = new AgentConfigDialog( this ); dlg->setAgentInstance( agent ); dlg->exec(); delete dlg; } } void AgentWidget::synchronizeAgent() { QList list = ui.instanceWidget->selectedAgentInstances(); if ( !list.isEmpty() ) foreach( AgentInstance agent, list ) agent.synchronize(); } void AgentWidget::toggleOnline() { AgentInstance agent = ui.instanceWidget->currentAgentInstance(); if ( agent.isValid() ) agent.setIsOnline( !agent.isOnline() ); } void AgentWidget::showTaskList() { AgentInstance agent = ui.instanceWidget->currentAgentInstance(); if ( !agent.isValid() ) return; QDBusInterface iface( QString::fromLatin1( "org.freedesktop.Akonadi.Agent.%1" ).arg( agent.identifier() ), "/Debug", QString() ); QDBusReply reply = iface.call("dumpToString"); QString txt; if ( reply.isValid() ) txt = reply.value(); else { txt = reply.error().message(); } QPointer dlg = new TextDialog( this ); dlg->setCaption( QLatin1String( "Resource Task List" ) ); dlg->setText( txt ); dlg->exec(); delete dlg; } void AgentWidget::showChangeNotifications() { AgentInstance agent = ui.instanceWidget->currentAgentInstance(); if ( !agent.isValid() ) return; QDBusInterface iface( QString::fromLatin1( "org.freedesktop.Akonadi.Agent.%1" ).arg( agent.identifier() ), "/Debug", QString() ); QDBusReply reply = iface.call("dumpNotificationListToString"); QString txt; if ( reply.isValid() ) txt = reply.value(); else { txt = reply.error().message(); } QPointer dlg = new TextDialog( this ); dlg->setCaption( QLatin1String( "Change Notification Log" ) ); dlg->setText( txt ); dlg->exec(); delete dlg; } void AgentWidget::synchronizeTree() { QList list = ui.instanceWidget->selectedAgentInstances(); if ( !list.isEmpty() ) foreach( AgentInstance agent, list ) agent.synchronizeCollectionTree(); } void AgentWidget::abortAgent() { QList list = ui.instanceWidget->selectedAgentInstances(); if ( !list.isEmpty() ) foreach( AgentInstance agent, list ) agent.abortCurrentTask(); } void AgentWidget::restartAgent() { AgentInstance agent = ui.instanceWidget->currentAgentInstance(); if ( agent.isValid() ) agent.restart(); } void AgentWidget::cloneAgent() { mCloneSource = ui.instanceWidget->currentAgentInstance(); if ( !mCloneSource.isValid() ) return; const AgentType agentType = mCloneSource.type(); if ( agentType.isValid() ) { AgentInstanceCreateJob *job = new AgentInstanceCreateJob( agentType, this ); connect( job, SIGNAL(result(KJob*)), SLOT(cloneAgent(KJob*)) ); job->start(); } else { kWarning() << "WTF?"; } } void AgentWidget::cloneAgent( KJob* job ) { if ( job->error() ) { KMessageBox::error( this, i18n("Cloning agent failed: %1.", job->errorText() ) ); return; } AgentInstance cloneTarget = static_cast( job )->instance(); Q_ASSERT( cloneTarget.isValid() ); Q_ASSERT( mCloneSource.isValid() ); QDBusInterface sourceIface( QString::fromLatin1("org.freedesktop.Akonadi.Agent.%1").arg( mCloneSource.identifier() ), "/Settings" ); if ( !sourceIface.isValid() ) { kError() << "Unable to obtain KConfigXT D-Bus interface of source agent" << mCloneSource.identifier(); return; } QDBusInterface targetIface( QString::fromLatin1("org.freedesktop.Akonadi.Agent.%1").arg( cloneTarget.identifier() ), "/Settings" ); if ( !targetIface.isValid() ) { kError() << "Unable to obtain KConfigXT D-Bus interface of target agent" << cloneTarget.identifier(); return; } cloneTarget.setName( mCloneSource.name() + " (Clone)" ); // iterate over all getter methods in the source interface and call the // corresponding setter in the target interface for ( int i = 0; i < sourceIface.metaObject()->methodCount(); ++i ) { const QMetaMethod method = sourceIface.metaObject()->method( i ); if ( QByteArray( method.typeName() ).isEmpty() ) // returns void continue; const QByteArray signature( method.signature() ); if ( signature.isEmpty() ) continue; if ( signature.startsWith( "set" ) || !signature.contains( "()" ) ) // setter or takes parameters // krazy:exclude=strings continue; if ( signature.startsWith( "Introspect" ) ) // D-Bus stuff // krazy:exclude=strings continue; const QString methodName = QString::fromLatin1( signature.left( signature.indexOf( '(' ) ) ); const QDBusMessage reply = sourceIface.call( methodName ); if ( reply.arguments().count() != 1 ) { kError() << "call to method" << signature << "failed: " << reply.arguments() << reply.errorMessage(); continue; } const QString setterName = QLatin1String("set") + methodName.at( 0 ).toUpper() + methodName.mid( 1 ); targetIface.call( setterName, reply.arguments().at( 0 ) ); } cloneTarget.reconfigure(); } void AgentWidget::currentChanged() { AgentInstance instance = ui.instanceWidget->currentAgentInstance(); ui.removeButton->setEnabled( instance.isValid() ); ui.configButton->setEnabled( instance.isValid() ); ui.syncButton->setEnabled( instance.isValid() ); ui.restartButton->setEnabled( instance.isValid() ); if ( instance.isValid() ) { ui.identifierLabel->setText( instance.identifier() ); ui.typeLabel->setText( instance.type().name() ); QString onlineStatus = ( instance.isOnline() ? i18n( "Online" ) : i18n( "Offline" ) ); QString agentStatus; switch( instance.status() ) { case AgentInstance::Idle: agentStatus = i18nc( "agent is in an idle state", "Idle" ); break; case AgentInstance::Running: agentStatus = i18nc( "agent is running", "Running (%1%)", instance.progress() ); break; case AgentInstance::Broken: agentStatus = i18nc( "agent is broken somehow", "Broken" ); break; case AgentInstance::NotConfigured: agentStatus = i18nc( "agent is not yet configured", "Not Configured" ); break; } ui.statusLabel->setText( i18nc( "Two statuses, for example \"Online, Running (66%)\" or \"Offline, Broken\"", "%1, %2", onlineStatus, agentStatus ) ); ui.statusMessageLabel->setText( instance.statusMessage() ); ui.capabilitiesLabel->setText( instance.type().capabilities().join( ", " ) ); ui.mimeTypeLabel->setText( instance.type().mimeTypes().join( ", " ) ); } else { ui.identifierLabel->setText( QString() ); ui.typeLabel->setText( QString() ); ui.statusLabel->setText( QString() ); ui.capabilitiesLabel->setText( QString() ); ui.mimeTypeLabel->setText( QString() ); } } void AgentWidget::showContextMenu(const QPoint& pos) { QMenu menu( this ); menu.addAction( KIcon("list-add"), i18n("Add Agent..."), this, SLOT(addAgent()) ); menu.addAction( KIcon("edit-copy"), i18n("Clone Agent"), this, SLOT(cloneAgent()) ); menu.addSeparator(); menu.addMenu( mSyncMenu ); menu.addAction( KIcon("dialog-cancel"), i18n("Abort Activity"), this, SLOT(abortAgent()) ); menu.addAction( KIcon("system-reboot"), i18n("Restart Agent"), this, SLOT(restartAgent()) ); //FIXME: Is using system-reboot icon here a good idea? menu.addAction( KIcon("network-disconnect"), i18n("Toggle Online/Offline"), this, SLOT(toggleOnline()) ); menu.addAction( KIcon(""), i18n("Show task list"), this, SLOT(showTaskList()) ); menu.addAction( KIcon(""), i18n("Show change-notification log"), this, SLOT(showChangeNotifications()) ); menu.addMenu( mConfigMenu ); menu.addAction( KIcon("list-remove"), i18n("Remove Agent"), this, SLOT(removeAgent()) ); menu.exec( ui.instanceWidget->mapToGlobal( pos ) ); } void AgentWidget::resizeEvent( QResizeEvent *event ) { ui.detailsBox->setVisible( event->size().height() > 400 ); }