Build with clang.
[kdepim.git] / akonadiconsole / agentwidget.cpp
blobeebbac5a2893ff967efc0763b053c5d7638c2a08
1 /*
2 This file is part of Akonadi.
4 Copyright (c) 2006 Tobias Koenig <tokoe@kde.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19 USA.
22 #include "agentwidget.h"
23 #include "agentconfigdialog.h"
25 #include <akonadi/agenttypedialog.h>
26 #include <akonadi/agentinstancewidget.h>
27 #include <akonadi/agentmanager.h>
28 #include <akonadi/agentinstancecreatejob.h>
29 #include <akonadi/control.h>
31 #include <KDebug>
32 #include <KLocale>
33 #include <KMessageBox>
34 #include <KStandardDirs>
35 #include <KStandardGuiItem>
37 #include <QtCore/QFile>
38 #include <QtGui/QGridLayout>
39 #include <QtGui/QMenu>
40 #include <QtGui/QPushButton>
41 #include <QtGui/QTextEdit>
42 #include <QDBusInterface>
43 #include <QDBusMessage>
44 #include <QMetaObject>
45 #include <QMetaMethod>
46 #include <QResizeEvent>
48 class TextDialog : public KDialog
50 public:
51 TextDialog( QWidget *parent = 0 )
52 : KDialog( parent )
54 setButtons( Ok );
56 mText = new QTextEdit;
57 setMainWidget( mText );
60 void setText( const QString &text )
62 mText->setPlainText( text );
65 private:
66 QTextEdit *mText;
69 using namespace Akonadi;
71 AgentWidget::AgentWidget( QWidget *parent )
72 : QWidget( parent )
74 ui.setupUi( this );
76 connect( ui.instanceWidget, SIGNAL(doubleClicked(Akonadi::AgentInstance)), SLOT(configureAgent()) );
77 connect( ui.instanceWidget, SIGNAL(currentChanged(Akonadi::AgentInstance,Akonadi::AgentInstance)),
78 SLOT(currentChanged(Akonadi::AgentInstance)) );
79 connect( ui.instanceWidget, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showContextMenu(QPoint)) );
81 connect( ui.instanceWidget->view()->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(selectionChanged(QItemSelection,QItemSelection)) );
83 currentChanged( ui.instanceWidget->currentAgentInstance() );
85 ui.addButton->setGuiItem( KStandardGuiItem::add() );
86 connect( ui.addButton, SIGNAL(clicked()), this, SLOT(addAgent()) );
88 ui.removeButton->setGuiItem( KStandardGuiItem::remove() );
89 connect( ui.removeButton, SIGNAL(clicked()), this, SLOT(removeAgent()) );
91 mConfigMenu = new QMenu( i18n("Configure"), this );
92 mConfigMenu->addAction( i18n("Configure Natively..."), this, SLOT(configureAgent()) );
93 mConfigMenu->addAction( i18n("Configure Remotely..."), this, SLOT(configureAgentRemote()) );
94 mConfigMenu->setIcon( KStandardGuiItem::configure().icon() );
95 ui.configButton->setGuiItem( KStandardGuiItem::configure() );
96 ui.configButton->setMenu( mConfigMenu );
97 connect( ui.configButton, SIGNAL(clicked()), this, SLOT(configureAgent()) );
99 mSyncMenu = new QMenu( i18n("Synchronize"), this );
100 mSyncMenu->addAction( i18n("Synchronize All"), this, SLOT(synchronizeAgent()) );
101 mSyncMenu->addAction( i18n("Synchronize Collection Tree"), this, SLOT(synchronizeTree()) );
102 mSyncMenu->setIcon( KIcon("view-refresh" ) );
103 ui.syncButton->setMenu( mSyncMenu );
104 ui.syncButton->setIcon( KIcon( "view-refresh" ) );
105 connect( ui.syncButton, SIGNAL(clicked()), this, SLOT(synchronizeAgent()) );
107 ui.abortButton->setIcon( KIcon("dialog-cancel") );
108 connect( ui.abortButton, SIGNAL(clicked()), this, SLOT(abortAgent()) );
109 ui.restartButton->setIcon( KIcon( "system-reboot" ) ); //FIXME: Is using system-reboot icon here a good idea?
110 connect( ui.restartButton, SIGNAL(clicked()), SLOT(restartAgent()) );
112 Control::widgetNeedsAkonadi( this );
115 void AgentWidget::addAgent()
117 Akonadi::AgentTypeDialog dlg( this );
118 if ( dlg.exec() ) {
119 const AgentType agentType = dlg.agentType();
121 if ( agentType.isValid() ) {
122 AgentInstanceCreateJob *job = new AgentInstanceCreateJob( agentType, this );
123 job->configure( this );
124 job->start(); // TODO: check result
129 void AgentWidget::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
131 Q_UNUSED( selected );
132 Q_UNUSED( deselected );
134 const bool multiSelection = ui.instanceWidget->view()->selectionModel()->selectedRows().size() > 1;
135 // Only agent removal, sync and restart is possible when multiple items are selected.
136 ui.configButton->setDisabled( multiSelection );
140 void AgentWidget::removeAgent()
142 QList<AgentInstance> list = ui.instanceWidget->selectedAgentInstances();
143 if ( !list.isEmpty() )
145 if ( KMessageBox::questionYesNo( this,
146 i18np( "Do you really want to delete the selected agent instance?",
147 "Do you really want to delete these %1 agent instances?",
148 list.size() ),
149 i18n( "Multiple Agent Deletion" ),
150 KStandardGuiItem::del(),
151 KStandardGuiItem::cancel(),
152 QString(),
153 KMessageBox::Dangerous )
154 == KMessageBox::Yes )
156 foreach( const AgentInstance &agent, list )
157 AgentManager::self()->removeInstance( agent );
162 void AgentWidget::configureAgent()
164 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
165 if ( agent.isValid() )
166 agent.configure( this );
169 void AgentWidget::configureAgentRemote()
171 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
172 if ( agent.isValid() ) {
173 AgentConfigDialog dlg( this );
174 dlg.setAgentInstance( agent );
175 dlg.exec();
179 void AgentWidget::synchronizeAgent()
181 QList<AgentInstance> list = ui.instanceWidget->selectedAgentInstances();
182 if ( !list.isEmpty() )
183 foreach( AgentInstance agent, list )
184 agent.synchronize();
187 void AgentWidget::toggleOnline()
189 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
190 if ( agent.isValid() )
191 agent.setIsOnline( !agent.isOnline() );
194 void AgentWidget::showChangeNotifications()
196 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
197 if ( !agent.isValid() )
198 return;
200 const QString fileName = QString::fromLatin1( "%1/akonadi/agent_config_%2_changes.dat" ).arg( KGlobal::dirs()->localxdgconfdir() ).arg( agent.identifier() );
201 QFile file( fileName );
202 if ( !file.open( QIODevice::ReadOnly ) )
203 return;
205 QDataStream stream( &file );
206 stream.setVersion( QDataStream::Qt_4_6 );
208 qulonglong size;
209 QByteArray sessionId, resource;
210 int type, operation;
211 qlonglong uid, parentCollection, parentDestCollection;
212 QString remoteId, mimeType;
213 QSet<QByteArray> itemParts;
215 QStringList list;
217 stream >> size;
218 for ( qulonglong i = 0; i < size; ++i ) {
219 stream >> sessionId;
220 stream >> type;
221 stream >> operation;
222 stream >> uid;
223 stream >> remoteId;
224 stream >> resource;
225 stream >> parentCollection;
226 stream >> parentDestCollection;
227 stream >> mimeType;
228 stream >> itemParts;
231 QString typeString;
232 switch ( type ) {
233 case 1:
234 typeString = QLatin1String( "Collection" );
235 break;
236 case 2:
237 typeString = QLatin1String( "Item" );
238 break;
239 case 0:
240 default:
241 typeString = QLatin1String( "Item" );
242 break;
245 QString operationString;
246 switch ( operation ) {
247 case 1:
248 operationString = QLatin1String( "Add" );
249 break;
250 case 2:
251 operationString = QLatin1String( "Modify" );
252 break;
253 case 3:
254 operationString = QLatin1String( "Move" );
255 break;
256 case 4:
257 operationString = QLatin1String( "Remove" );
258 break;
259 case 5:
260 operationString = QLatin1String( "Link" );
261 break;
262 case 6:
263 operationString = QLatin1String( "Unlink" );
264 break;
265 case 7:
266 operationString = QLatin1String( "Subscribe" );
267 break;
268 case 8:
269 operationString = QLatin1String( "Unsubscribe" );
270 break;
271 case 0:
272 default:
273 operationString = QLatin1String( "InvalidOp" );
274 break;
276 const QString entry = QString::fromLatin1("session=%1 type=%2 operation=%3 uid=%4 remoteId=%5 resource=%6 parentCollection=%7 parentDestCollection=%8 mimeType=%9 itemParts=%10")
277 .arg( QString::fromLatin1( sessionId ) )
278 .arg( typeString )
279 .arg( operationString )
280 .arg( uid )
281 .arg( remoteId )
282 .arg( QString::fromLatin1( resource ) )
283 .arg( parentCollection )
284 .arg( parentDestCollection )
285 .arg( mimeType )
286 .arg( QLatin1String("foobar") );
288 list << entry;
291 TextDialog dlg( this );
292 dlg.setCaption( QLatin1String( "Change Notification Log" ) );
293 dlg.setText( list.join( QLatin1String( "\n" ) ) );
295 dlg.exec();
298 void AgentWidget::synchronizeTree()
300 QList<AgentInstance> list = ui.instanceWidget->selectedAgentInstances();
301 if ( !list.isEmpty() )
302 foreach( AgentInstance agent, list )
303 agent.synchronizeCollectionTree();
306 void AgentWidget::abortAgent()
308 QList<AgentInstance> list = ui.instanceWidget->selectedAgentInstances();
309 if ( !list.isEmpty() )
310 foreach( AgentInstance agent, list )
311 agent.abortCurrentTask();
314 void AgentWidget::restartAgent()
316 AgentInstance agent = ui.instanceWidget->currentAgentInstance();
317 if ( agent.isValid() )
318 agent.restart();
321 void AgentWidget::cloneAgent()
323 mCloneSource = ui.instanceWidget->currentAgentInstance();
324 if ( !mCloneSource.isValid() )
325 return;
326 const AgentType agentType = mCloneSource.type();
327 if ( agentType.isValid() ) {
328 AgentInstanceCreateJob *job = new AgentInstanceCreateJob( agentType, this );
329 connect( job, SIGNAL(result(KJob*)), SLOT(cloneAgent(KJob*)) );
330 job->start();
331 } else {
332 kWarning() << "WTF?";
336 void AgentWidget::cloneAgent( KJob* job )
338 if ( job->error() ) {
339 KMessageBox::error( this, i18n("Cloneing agent failed: %1.", job->errorText() ) );
340 return;
343 AgentInstance cloneTarget = static_cast<AgentInstanceCreateJob*>( job )->instance();
344 Q_ASSERT( cloneTarget.isValid() );
345 Q_ASSERT( mCloneSource.isValid() );
347 QDBusInterface sourceIface( QString::fromLatin1("org.freedesktop.Akonadi.Agent.%1").arg( mCloneSource.identifier() ),
348 "/Settings" );
349 if ( !sourceIface.isValid() ) {
350 kError() << "Unable to obtain KConfigXT D-Bus interface of source agent" << mCloneSource.identifier();
351 return;
354 QDBusInterface targetIface( QString::fromLatin1("org.freedesktop.Akonadi.Agent.%1").arg( cloneTarget.identifier() ),
355 "/Settings" );
356 if ( !targetIface.isValid() ) {
357 kError() << "Unable to obtain KConfigXT D-Bus interface of target agent" << cloneTarget.identifier();
358 return;
361 cloneTarget.setName( mCloneSource.name() + " (Clone)" );
363 // iterate over all getter methods in the source interface and call the
364 // corresponding setter in the target interface
365 for ( int i = 0; i < sourceIface.metaObject()->methodCount(); ++i ) {
366 const QMetaMethod method = sourceIface.metaObject()->method( i );
367 if ( QByteArray( method.typeName() ).isEmpty() ) // returns void
368 continue;
369 const QByteArray signature( method.signature() );
370 if ( signature.isEmpty() )
371 continue;
372 if ( signature.startsWith( "set" ) || !signature.contains( "()" ) ) // setter or takes parameters // krazy:exclude=strings
373 continue;
374 if ( signature.startsWith( "Introspect" ) ) // D-Bus stuff // krazy:exclude=strings
375 continue;
376 const QString methodName = QString::fromLatin1( signature.left( signature.indexOf( '(' ) ) );
377 const QDBusMessage reply = sourceIface.call( methodName );
378 if ( !reply.arguments().count() == 1 ) {
379 kError() << "call to method" << signature << "failed: " << reply.arguments() << reply.errorMessage();
380 continue;
382 const QString setterName = QLatin1String("set") + methodName.at( 0 ).toUpper() + methodName.mid( 1 );
383 targetIface.call( setterName, reply.arguments().at( 0 ) );
386 cloneTarget.reconfigure();
389 void AgentWidget::currentChanged(const Akonadi::AgentInstance& instance)
391 ui.removeButton->setEnabled( instance.isValid() );
392 ui.configButton->setEnabled( instance.isValid() );
393 ui.syncButton->setEnabled( instance.isValid() );
394 ui.restartButton->setEnabled( instance.isValid() );
396 if ( instance.isValid() ) {
397 ui.identifierLabel->setText( instance.identifier() );
398 ui.typeLabel->setText( instance.type().name() );
399 QString onlineStatus = ( instance.isOnline() ? i18n( "Online" ) : i18n( "Offline" ) );
400 QString agentStatus;
401 switch( instance.status() ) {
402 case AgentInstance::Idle: agentStatus = i18n( "Idle" ); break;
403 case AgentInstance::Running: agentStatus = i18n( "Running (%1%)", instance.progress() ); break;
404 case AgentInstance::Broken: agentStatus = i18n( "Broken" ); break;
406 ui.statusLabel->setText( i18nc( "Two statuses, for example \"Online, Running (66%)\" or \"Offline, Broken\"",
407 "%1, %2", onlineStatus, agentStatus ) );
408 ui.statusMessageLabel->setText( instance.statusMessage() );
409 ui.capabilitiesLabel->setText( instance.type().capabilities().join( ", " ) );
410 ui.mimeTypeLabel->setText( instance.type().mimeTypes().join( ", " ) );
411 } else {
412 ui.identifierLabel->setText( QString() );
413 ui.typeLabel->setText( QString() );
414 ui.statusLabel->setText( QString() );
415 ui.capabilitiesLabel->setText( QString() );
416 ui.mimeTypeLabel->setText( QString() );
420 void AgentWidget::showContextMenu(const QPoint& pos)
422 QMenu menu( this );
423 menu.addAction( KIcon("list-add"), i18n("Add Agent..."), this, SLOT(addAgent()) );
424 menu.addAction( KIcon("edit-copy"), i18n("Clone Agent"), this, SLOT(cloneAgent()) );
425 menu.addSeparator();
426 menu.addMenu( mSyncMenu );
427 menu.addAction( KIcon("dialog-cancel"), i18n("Abort Activity"), this, SLOT(abortAgent()) );
428 menu.addAction( KIcon("system-reboot"), i18n("Restart Agent"), this, SLOT(restartAgent()) ); //FIXME: Is using system-reboot icon here a good idea?
429 menu.addAction( KIcon("network-disconnect"), i18n("Toggle Online/Offline"), this, SLOT(toggleOnline()) );
430 menu.addAction( KIcon(""), i18n("Show change-notification log"), this, SLOT(showChangeNotifications()) );
431 menu.addMenu( mConfigMenu );
432 menu.addAction( KIcon("list-remove"), i18n("Remove Agent"), this, SLOT(removeAgent()) );
433 menu.exec( mapToGlobal( pos ) );
436 void AgentWidget::resizeEvent( QResizeEvent *event )
438 ui.detailsBox->setVisible( event->size().height() > 400 );
441 #include "agentwidget.moc"