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,
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>
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
51 TextDialog( QWidget
*parent
= 0 )
56 mText
= new QTextEdit
;
57 setMainWidget( mText
);
60 void setText( const QString
&text
)
62 mText
->setPlainText( text
);
69 using namespace Akonadi
;
71 AgentWidget::AgentWidget( QWidget
*parent
)
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 );
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?",
149 i18n( "Multiple Agent Deletion" ),
150 KStandardGuiItem::del(),
151 KStandardGuiItem::cancel(),
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
);
179 void AgentWidget::synchronizeAgent()
181 QList
<AgentInstance
> list
= ui
.instanceWidget
->selectedAgentInstances();
182 if ( !list
.isEmpty() )
183 foreach( AgentInstance agent
, list
)
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() )
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
) )
205 QDataStream
stream( &file
);
206 stream
.setVersion( QDataStream::Qt_4_6
);
209 QByteArray sessionId
, resource
;
211 qlonglong uid
, parentCollection
, parentDestCollection
;
212 QString remoteId
, mimeType
;
213 QSet
<QByteArray
> itemParts
;
218 for ( qulonglong i
= 0; i
< size
; ++i
) {
225 stream
>> parentCollection
;
226 stream
>> parentDestCollection
;
234 typeString
= QLatin1String( "Collection" );
237 typeString
= QLatin1String( "Item" );
241 typeString
= QLatin1String( "Item" );
245 QString operationString
;
246 switch ( operation
) {
248 operationString
= QLatin1String( "Add" );
251 operationString
= QLatin1String( "Modify" );
254 operationString
= QLatin1String( "Move" );
257 operationString
= QLatin1String( "Remove" );
260 operationString
= QLatin1String( "Link" );
263 operationString
= QLatin1String( "Unlink" );
266 operationString
= QLatin1String( "Subscribe" );
269 operationString
= QLatin1String( "Unsubscribe" );
273 operationString
= QLatin1String( "InvalidOp" );
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
) )
279 .arg( operationString
)
282 .arg( QString::fromLatin1( resource
) )
283 .arg( parentCollection
)
284 .arg( parentDestCollection
)
286 .arg( QLatin1String("foobar") );
291 TextDialog
dlg( this );
292 dlg
.setCaption( QLatin1String( "Change Notification Log" ) );
293 dlg
.setText( list
.join( QLatin1String( "\n" ) ) );
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() )
321 void AgentWidget::cloneAgent()
323 mCloneSource
= ui
.instanceWidget
->currentAgentInstance();
324 if ( !mCloneSource
.isValid() )
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
*)) );
332 kWarning() << "WTF?";
336 void AgentWidget::cloneAgent( KJob
* job
)
338 if ( job
->error() ) {
339 KMessageBox::error( this, i18n("Cloneing agent failed: %1.", job
->errorText() ) );
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() ),
349 if ( !sourceIface
.isValid() ) {
350 kError() << "Unable to obtain KConfigXT D-Bus interface of source agent" << mCloneSource
.identifier();
354 QDBusInterface
targetIface( QString::fromLatin1("org.freedesktop.Akonadi.Agent.%1").arg( cloneTarget
.identifier() ),
356 if ( !targetIface
.isValid() ) {
357 kError() << "Unable to obtain KConfigXT D-Bus interface of target agent" << cloneTarget
.identifier();
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
369 const QByteArray
signature( method
.signature() );
370 if ( signature
.isEmpty() )
372 if ( signature
.startsWith( "set" ) || !signature
.contains( "()" ) ) // setter or takes parameters // krazy:exclude=strings
374 if ( signature
.startsWith( "Introspect" ) ) // D-Bus stuff // krazy:exclude=strings
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();
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" ) );
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( ", " ) );
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
)
423 menu
.addAction( KIcon("list-add"), i18n("Add Agent..."), this, SLOT(addAgent()) );
424 menu
.addAction( KIcon("edit-copy"), i18n("Clone Agent"), this, SLOT(cloneAgent()) );
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"