2 Copyright (c) 2007 Volker Krause <vkrause@kde.org>
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 #include "browserwidget.h"
22 #include "collectionattributespage.h"
23 #include "collectioninternalspage.h"
24 #include "collectionaclpage.h"
26 #include "akonadibrowsermodel.h"
28 #include <akonadi/attributefactory.h>
29 #include <akonadi/changerecorder.h>
30 #include <akonadi/control.h>
31 #include <akonadi/entitymimetypefiltermodel.h>
32 #include <akonadi/item.h>
33 #include <akonadi/itemfetchjob.h>
34 #include <akonadi/itemfetchscope.h>
35 #include <akonadi/itemmodifyjob.h>
36 #include <akonadi/job.h>
37 #include <akonadi/collectionfilterproxymodel.h>
38 #include <akonadi/collectionpropertiesdialog.h>
39 #include <akonadi/selectionproxymodel.h>
40 #include <akonadi/session.h>
41 #include <akonadi/standardactionmanager.h>
42 #include <akonadi/entitylistview.h>
43 #include <akonadi/entitytreeview.h>
44 #include <akonadi/etmviewstatesaver.h>
45 #include <akonadi/favoritecollectionsmodel.h>
46 #include <akonadi_next/quotacolorproxymodel.h>
47 #include <akonadi/statisticsproxymodel.h>
48 #include <akonadi_next/kviewstatemaintainer.h>
50 #include <kabc/addressee.h>
51 #include <kabc/contactgroup.h>
52 #include <kcalcore/incidence.h>
56 #include <kconfiggroup.h>
57 #include <kfiledialog.h>
58 #include <kmessagebox.h>
59 #include <kxmlguiwindow.h>
61 #include <nepomuk/resource.h>
62 #include <nepomuk/resourcemanager.h>
63 #include <nepomuk/variant.h>
67 #include <QVBoxLayout>
68 #include <QStackedWidget>
69 #include <QtGui/QSortFilterProxyModel>
70 #include <QStandardItemModel>
73 using namespace Akonadi
;
75 AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY(CollectionAttributePageFactory
, CollectionAttributePage
)
76 AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY(CollectionInternalsPageFactory
, CollectionInternalsPage
)
77 AKONADI_COLLECTION_PROPERTIES_PAGE_FACTORY(CollectionAclPageFactory
, CollectionAclPage
)
79 Q_DECLARE_METATYPE( QSet
<QByteArray
> )
81 BrowserWidget::BrowserWidget(KXmlGuiWindow
*xmlGuiWindow
, QWidget
* parent
) :
85 mStdActionManager( 0 ),
88 Q_ASSERT( xmlGuiWindow
);
89 QVBoxLayout
*layout
= new QVBoxLayout( this );
91 QSplitter
*splitter
= new QSplitter( Qt::Horizontal
, this );
92 splitter
->setObjectName( "collectionSplitter" );
93 layout
->addWidget( splitter
);
95 QSplitter
*splitter2
= new QSplitter( Qt::Vertical
, this );
96 splitter2
->setObjectName( "ffvSplitter" );
98 mCollectionView
= new Akonadi::EntityTreeView( xmlGuiWindow
, this );
99 mCollectionView
->setObjectName( "CollectionView" );
100 mCollectionView
->setSelectionMode( QAbstractItemView::ExtendedSelection
);
101 splitter2
->addWidget( mCollectionView
);
103 EntityListView
*favoritesView
= new EntityListView( xmlGuiWindow
, this );
104 //favoritesView->setViewMode( QListView::IconMode );
105 splitter2
->addWidget( favoritesView
);
107 splitter
->addWidget( splitter2
);
109 Session
*session
= new Session( "AkonadiConsole Browser Widget", this );
111 // monitor collection changes
112 ChangeRecorder
*monitor
= new ChangeRecorder( this );
113 monitor
->setSession(session
);
114 monitor
->setCollectionMonitored( Collection::root() );
115 monitor
->fetchCollection( true );
116 monitor
->setAllMonitored( true );
117 // TODO: Only fetch the envelope etc if possible.
118 monitor
->itemFetchScope().fetchFullPayload(true);
120 mBrowserModel
= new AkonadiBrowserModel( monitor
, this );
121 mBrowserModel
->setItemPopulationStrategy( EntityTreeModel::LazyPopulation
);
122 mBrowserModel
->setShowSystemEntities( true );
124 // new ModelTest( mBrowserModel );
126 EntityMimeTypeFilterModel
*collectionFilter
= new EntityMimeTypeFilterModel( this );
127 collectionFilter
->setSourceModel( mBrowserModel
);
128 collectionFilter
->addMimeTypeInclusionFilter( Collection::mimeType() );
129 collectionFilter
->setHeaderGroup( EntityTreeModel::CollectionTreeHeaders
);
130 collectionFilter
->setDynamicSortFilter( true );
131 collectionFilter
->setSortCaseSensitivity( Qt::CaseInsensitive
);
133 statisticsProxyModel
= new StatisticsProxyModel( this );
134 statisticsProxyModel
->setToolTipEnabled( true );
135 statisticsProxyModel
->setSourceModel( collectionFilter
);
137 QuotaColorProxyModel
*quotaProxyModel
= new QuotaColorProxyModel( this );
138 quotaProxyModel
->setWarningThreshold( 50.0 );
139 quotaProxyModel
->setSourceModel( statisticsProxyModel
);
141 mCollectionView
->setModel( quotaProxyModel
);
143 Akonadi::SelectionProxyModel
*selectionProxyModel
= new Akonadi::SelectionProxyModel( mCollectionView
->selectionModel(), this );
144 selectionProxyModel
->setSourceModel( mBrowserModel
);
145 selectionProxyModel
->setFilterBehavior( KSelectionProxyModel::ChildrenOfExactSelection
);
147 EntityMimeTypeFilterModel
*itemFilter
= new EntityMimeTypeFilterModel( this );
148 itemFilter
->setSourceModel( selectionProxyModel
);
149 itemFilter
->addMimeTypeExclusionFilter( Collection::mimeType() );
150 itemFilter
->setHeaderGroup( EntityTreeModel::ItemListHeaders
);
152 const KConfigGroup group
= KGlobal::config()->group( "FavoriteCollectionsModel" );
153 FavoriteCollectionsModel
*favoritesModel
= new FavoriteCollectionsModel( mBrowserModel
, group
, this );
154 favoritesView
->setModel( favoritesModel
);
156 QSplitter
*splitter3
= new QSplitter( Qt::Vertical
, this );
157 splitter3
->setObjectName( "itemSplitter" );
158 splitter
->addWidget( splitter3
);
160 QWidget
*itemViewParent
= new QWidget( this );
161 itemUi
.setupUi( itemViewParent
);
163 itemUi
.modelBox
->addItem( "Generic" );
164 itemUi
.modelBox
->addItem( "Mail" );
165 itemUi
.modelBox
->addItem( "Contacts" );
166 itemUi
.modelBox
->addItem( "Calendar" );
167 connect( itemUi
.modelBox
, SIGNAL(activated(int)), SLOT(modelChanged()) );
168 QTimer::singleShot( 0, this, SLOT(modelChanged()) );
170 itemUi
.itemView
->setXmlGuiClient( xmlGuiWindow
);
171 itemUi
.itemView
->setModel( itemFilter
);
172 itemUi
.itemView
->setSelectionMode( QAbstractItemView::ExtendedSelection
);
173 connect( itemUi
.itemView
, SIGNAL( activated( QModelIndex
) ), SLOT(itemActivated( QModelIndex
) ) );
174 connect( itemUi
.itemView
, SIGNAL( clicked( QModelIndex
) ), SLOT(itemActivated( QModelIndex
) ) );
175 splitter3
->addWidget( itemViewParent
);
176 itemViewParent
->layout()->setMargin( 0 );
178 QWidget
*contentViewParent
= new QWidget( this );
179 contentUi
.setupUi( contentViewParent
);
180 connect( contentUi
.saveButton
, SIGNAL(clicked()), SLOT(save()) );
181 splitter3
->addWidget( contentViewParent
);
183 connect( contentUi
.attrAddButton
, SIGNAL(clicked()), SLOT(addAttribute()) );
184 connect( contentUi
.attrDeleteButton
, SIGNAL(clicked()), SLOT(delAttribute()) );
186 CollectionPropertiesDialog::registerPage( new CollectionAclPageFactory() );
187 CollectionPropertiesDialog::registerPage( new CollectionAttributePageFactory() );
188 CollectionPropertiesDialog::registerPage( new CollectionInternalsPageFactory() );
190 Control::widgetNeedsAkonadi( this );
192 mStdActionManager
= new StandardActionManager( xmlGuiWindow
->actionCollection(), xmlGuiWindow
);
193 mStdActionManager
->setCollectionSelectionModel( mCollectionView
->selectionModel() );
194 mStdActionManager
->setItemSelectionModel( itemUi
.itemView
->selectionModel() );
195 mStdActionManager
->setFavoriteCollectionsModel( favoritesModel
);
196 mStdActionManager
->setFavoriteSelectionModel( favoritesView
->selectionModel() );
197 mStdActionManager
->createAllActions();
199 Nepomuk::ResourceManager::instance()->init();
201 m_stateMaintainer
= new Future::KViewStateMaintainer
<ETMViewStateSaver
>( KGlobal::config()->group("CollectionViewState"), this );
202 m_stateMaintainer
->setView( mCollectionView
);
204 m_stateMaintainer
->restoreState();
207 BrowserWidget::~BrowserWidget()
209 m_stateMaintainer
->saveState();
212 void BrowserWidget::clear()
214 contentUi
.stack
->setCurrentWidget( contentUi
.unsupportedTypePage
);
215 contentUi
.dataView
->clear();
216 contentUi
.id
->clear();
217 contentUi
.remoteId
->clear();
218 contentUi
.mimeType
->clear();
219 contentUi
.revision
->clear();
220 contentUi
.size
->clear();
221 contentUi
.modificationtime
->clear();
222 contentUi
.flags
->clear();
223 contentUi
.attrView
->setModel( 0 );
226 void BrowserWidget::itemActivated(const QModelIndex
& index
)
228 const Item item
= index
.sibling( index
.row(), 0 ).data( EntityTreeModel::ItemRole
).value
< Item
>();
229 if ( !item
.isValid() ) {
234 ItemFetchJob
*job
= new ItemFetchJob( item
, this );
235 job
->fetchScope().fetchFullPayload();
236 job
->fetchScope().fetchAllAttributes();
237 connect( job
, SIGNAL( result( KJob
* ) ), SLOT( itemFetchDone( KJob
* ) ), Qt::QueuedConnection
);
240 void BrowserWidget::itemFetchDone(KJob
* job
)
242 ItemFetchJob
*fetch
= static_cast<ItemFetchJob
*>( job
);
243 if ( job
->error() ) {
244 kWarning( 5265 ) << "Item fetch failed: " << job
->errorString();
245 } else if ( fetch
->items().isEmpty() ) {
246 kWarning( 5265 ) << "No item found!";
248 const Item item
= fetch
->items().first();
253 void BrowserWidget::setItem( const Akonadi::Item
&item
)
256 if ( item
.hasPayload
<KABC::Addressee
>() ) {
257 contentUi
.contactView
->setItem( item
);
258 contentUi
.stack
->setCurrentWidget( contentUi
.contactViewPage
);
259 } else if ( item
.hasPayload
<KABC::ContactGroup
>() ) {
260 contentUi
.contactGroupView
->setItem( item
);
261 contentUi
.stack
->setCurrentWidget( contentUi
.contactGroupViewPage
);
262 } else if ( item
.hasPayload
<KCalCore::Incidence::Ptr
>() ) {
263 contentUi
.incidenceView
->setItem( item
);
264 contentUi
.stack
->setCurrentWidget( contentUi
.incidenceViewPage
);
265 } else if ( item
.mimeType() == "message/rfc822"
266 || item
.mimeType() == "message/news" ) {
267 contentUi
.mailView
->setMessageItem( item
, MessageViewer::Viewer::Force
);
268 contentUi
.stack
->setCurrentWidget( contentUi
.mailViewPage
);
269 } else if ( item
.hasPayload
<QPixmap
>() ) {
270 contentUi
.imageView
->setPixmap( item
.payload
<QPixmap
>() );
271 contentUi
.stack
->setCurrentWidget( contentUi
.imageViewPage
);
273 contentUi
.stack
->setCurrentWidget( contentUi
.unsupportedTypePage
);
276 QByteArray data
= item
.payloadData();
277 contentUi
.dataView
->setPlainText( data
);
279 contentUi
.id
->setText( QString::number( item
.id() ) );
280 contentUi
.remoteId
->setText( item
.remoteId() );
281 contentUi
.mimeType
->setText( item
.mimeType() );
282 contentUi
.revision
->setText( QString::number( item
.revision() ) );
283 contentUi
.size
->setText( QString::number( item
.size() ) );
284 contentUi
.modificationtime
->setText( item
.modificationTime().toString() + ( " UTC" ) );
286 foreach ( const Item::Flag
&f
, item
.flags() )
287 flags
<< QString::fromUtf8( f
);
288 contentUi
.flags
->setItems( flags
);
290 Attribute::List list
= item
.attributes();
292 mAttrModel
= new QStandardItemModel( list
.count(), 2 );
294 labels
<< i18n( "Attribute" ) << i18n( "Value" );
295 mAttrModel
->setHorizontalHeaderLabels( labels
);
296 for ( int i
= 0; i
< list
.count(); ++i
) {
297 QModelIndex index
= mAttrModel
->index( i
, 0 );
298 Q_ASSERT( index
.isValid() );
299 mAttrModel
->setData( index
, QString::fromLatin1( list
[i
]->type() ) );
300 index
= mAttrModel
->index( i
, 1 );
301 Q_ASSERT( index
.isValid() );
302 mAttrModel
->setData( index
, QString::fromLatin1( list
[i
]->serialized() ) );
303 mAttrModel
->itemFromIndex( index
)->setFlags( Qt::ItemIsEditable
| mAttrModel
->flags( index
) );
305 contentUi
.attrView
->setModel( mAttrModel
);
307 if ( Settings::self()->nepomukEnabled() ) {
308 Nepomuk::Resource
res( item
.url() );
310 contentUi
.tagWidget
->setTaggedResource( res
);
311 contentUi
.ratingWidget
->setRating( res
.rating() );
313 delete mNepomukModel
;
315 if ( res
.isValid() ) {
316 contentUi
.rdfClassName
->setText( res
.className() );
317 QHash
<QUrl
, Nepomuk::Variant
> props
= res
.properties();
318 mNepomukModel
= new QStandardItemModel( props
.count(), 2, this );
320 labels
<< i18n( "Property" ) << i18n( "Value" );
321 mNepomukModel
->setHorizontalHeaderLabels( labels
);
323 for ( QHash
<QUrl
, Nepomuk::Variant
>::ConstIterator it
= props
.constBegin(); it
!= props
.constEnd(); ++it
, ++row
) {
324 QModelIndex index
= mNepomukModel
->index( row
, 0 );
325 Q_ASSERT( index
.isValid() );
326 mNepomukModel
->setData( index
, it
.key().toString() );
327 index
= mNepomukModel
->index( row
, 1 );
328 Q_ASSERT( index
.isValid() );
329 mNepomukModel
->setData( index
, it
.value().toString() );
331 contentUi
.nepomukView
->setEnabled( true );
333 contentUi
.nepomukView
->setEnabled( false );
335 contentUi
.nepomukView
->setModel( mNepomukModel
);
336 contentUi
.nepomukTab
->setEnabled( true );
338 contentUi
.nepomukTab
->setEnabled( false );
342 mMonitor
->deleteLater(); // might be the one calling us
343 mMonitor
= new Monitor( this );
344 mMonitor
->setItemMonitored( item
);
345 mMonitor
->itemFetchScope().fetchFullPayload();
346 mMonitor
->itemFetchScope().fetchAllAttributes();
347 qRegisterMetaType
<QSet
<QByteArray
> >();
348 connect( mMonitor
, SIGNAL(itemChanged(Akonadi::Item
,QSet
<QByteArray
>)), SLOT(setItem(Akonadi::Item
)), Qt::QueuedConnection
);
351 void BrowserWidget::modelChanged()
353 switch ( itemUi
.modelBox
->currentIndex() ) {
355 mBrowserModel
->setItemDisplayMode(AkonadiBrowserModel::MailMode
);
358 mBrowserModel
->setItemDisplayMode(AkonadiBrowserModel::ContactsMode
);
361 mBrowserModel
->setItemDisplayMode(AkonadiBrowserModel::CalendarMode
);
364 mBrowserModel
->setItemDisplayMode(AkonadiBrowserModel::GenericMode
);
368 void BrowserWidget::save()
370 Q_ASSERT( mAttrModel
);
372 const QByteArray data
= contentUi
.dataView
->toPlainText().toUtf8();
373 Item item
= mCurrentItem
;
374 item
.setRemoteId( contentUi
.remoteId
->text() );
375 foreach ( const Item::Flag
&f
, mCurrentItem
.flags() )
377 foreach ( const QString
&s
, contentUi
.flags
->items() )
378 item
.setFlag( s
.toUtf8() );
379 item
.setPayloadFromData( data
);
381 item
.clearAttributes();
382 for ( int i
= 0; i
< mAttrModel
->rowCount(); ++i
) {
383 const QModelIndex typeIndex
= mAttrModel
->index( i
, 0 );
384 Q_ASSERT( typeIndex
.isValid() );
385 const QModelIndex valueIndex
= mAttrModel
->index( i
, 1 );
386 Q_ASSERT( valueIndex
.isValid() );
387 Attribute
* attr
= AttributeFactory::createAttribute( mAttrModel
->data( typeIndex
).toString().toLatin1() );
389 attr
->deserialize( mAttrModel
->data( valueIndex
).toString().toLatin1() );
390 item
.addAttribute( attr
);
393 ItemModifyJob
*store
= new ItemModifyJob( item
, this );
394 connect( store
, SIGNAL(result(KJob
*)), SLOT(saveResult(KJob
*)) );
396 if ( Settings::self()->nepomukEnabled() ) {
397 Nepomuk::Resource
res( item
.url() );
398 res
.setRating( contentUi
.ratingWidget
->rating() );
402 void BrowserWidget::saveResult(KJob
* job
)
404 if ( job
->error() ) {
405 KMessageBox::error( this, i18n( "Failed to save changes: %1", job
->errorString() ) );
409 void BrowserWidget::addAttribute()
411 if ( !mAttrModel
|| contentUi
.attrName
->text().isEmpty() )
413 const int row
= mAttrModel
->rowCount();
414 mAttrModel
->insertRow( row
);
415 QModelIndex index
= mAttrModel
->index( row
, 0 );
416 Q_ASSERT( index
.isValid() );
417 mAttrModel
->setData( index
, contentUi
.attrName
->text() );
418 contentUi
.attrName
->clear();
421 void BrowserWidget::delAttribute()
425 QModelIndexList selection
= contentUi
.attrView
->selectionModel()->selectedRows();
426 if ( selection
.count() != 1 )
428 mAttrModel
->removeRow( selection
.first().row() );
431 void BrowserWidget::dumpToXml()
433 const Collection root
= mCollectionView
->currentIndex().data( EntityTreeModel::CollectionRole
).value
<Collection
>();
434 if ( !root
.isValid() )
436 const QString fileName
= KFileDialog::getSaveFileName( KUrl(), "*.xml", this, i18n( "Select XML file" ) );
437 if ( fileName
.isEmpty() )
439 #if 0 // TODO: port me, can't use XmlWriteJob here, it's in runtime, call the akonadi2xml cli tool instead
440 XmlWriteJob
*job
= new XmlWriteJob( root
, fileName
, this );
441 connect( job
, SIGNAL(result(KJob
*)), SLOT(dumpToXmlResult(KJob
*)) );
445 void BrowserWidget::dumpToXmlResult( KJob
* job
)
448 KMessageBox::error( this, job
->errorString() );
451 #include "browserwidget.moc"