• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.10.5 API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • akonadi
entitytreemodel.cpp
1 /*
2  Copyright (c) 2008 Stephen Kelly <steveire@gmail.com>
3 
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.
8 
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.
13 
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
17  02110-1301, USA.
18 */
19 
20 #include "entitytreemodel.h"
21 #include "entitytreemodel_p.h"
22 
23 #include "monitor_p.h"
24 
25 #include <QtCore/QHash>
26 #include <QtCore/QMimeData>
27 #include <QtCore/QTimer>
28 #include <QAbstractProxyModel>
29 #include <QApplication>
30 #include <QPalette>
31 
32 #include <KDE/KIcon>
33 #include <KDE/KLocale>
34 #include <KDE/KMessageBox>
35 #include <KDE/KUrl>
36 
37 #include <akonadi/attributefactory.h>
38 #include <akonadi/changerecorder.h>
39 #include <akonadi/collectionmodifyjob.h>
40 #include <akonadi/entitydisplayattribute.h>
41 #include <akonadi/transactionsequence.h>
42 #include <akonadi/itemmodifyjob.h>
43 #include <akonadi/session.h>
44 #include "collectionfetchscope.h"
45 
46 #include "collectionutils_p.h"
47 
48 #include "kdebug.h"
49 #include "pastehelper_p.h"
50 
51 Q_DECLARE_METATYPE( QSet<QByteArray> )
52 
53 using namespace Akonadi;
54 
55 EntityTreeModel::EntityTreeModel( ChangeRecorder *monitor,
56  QObject *parent
57  )
58  : QAbstractItemModel( parent ),
59  d_ptr( new EntityTreeModelPrivate( this ) )
60 {
61  Q_D( EntityTreeModel );
62  d->init( monitor );
63 }
64 
65 EntityTreeModel::EntityTreeModel( ChangeRecorder *monitor,
66  EntityTreeModelPrivate *d,
67  QObject *parent )
68  : QAbstractItemModel( parent ),
69  d_ptr( d )
70 {
71  d->init( monitor );
72 }
73 
74 EntityTreeModel::~EntityTreeModel()
75 {
76  Q_D( EntityTreeModel );
77 
78  foreach ( const QList<Node*> &list, d->m_childEntities ) {
79  QList<Node*>::const_iterator it = list.constBegin();
80  const QList<Node*>::const_iterator end = list.constEnd();
81  for ( ; it != end; ++it ) {
82  delete *it;
83  }
84  }
85 
86  d->m_rootNode = 0;
87 
88  delete d_ptr;
89 }
90 
91 bool EntityTreeModel::includeUnsubscribed() const
92 {
93  Q_D( const EntityTreeModel );
94  return d->m_includeUnsubscribed;
95 }
96 
97 void EntityTreeModel::setIncludeUnsubscribed( bool show )
98 {
99  Q_D( EntityTreeModel );
100  d->beginResetModel();
101  d->m_includeUnsubscribed = show;
102  d->m_monitor->setAllMonitored( show );
103  d->endResetModel();
104 }
105 
106 
107 bool EntityTreeModel::systemEntitiesShown() const
108 {
109  Q_D( const EntityTreeModel );
110  return d->m_showSystemEntities;
111 }
112 
113 void EntityTreeModel::setShowSystemEntities( bool show )
114 {
115  Q_D( EntityTreeModel );
116  d->m_showSystemEntities = show;
117 }
118 
119 void EntityTreeModel::clearAndReset()
120 {
121  Q_D( EntityTreeModel );
122  d->beginResetModel();
123  d->endResetModel();
124 }
125 
126 int EntityTreeModel::columnCount( const QModelIndex & parent ) const
127 {
128 // TODO: Statistics?
129  if ( parent.isValid() &&
130  parent.column() != 0 ) {
131  return 0;
132  }
133 
134  return qMax( entityColumnCount( CollectionTreeHeaders ), entityColumnCount( ItemListHeaders ) );
135 }
136 
137 
138 QVariant EntityTreeModel::entityData( const Item &item, int column, int role ) const
139 {
140  if ( column == 0 ) {
141  switch ( role ) {
142  case Qt::DisplayRole:
143  case Qt::EditRole:
144  if ( item.hasAttribute<EntityDisplayAttribute>() &&
145  !item.attribute<EntityDisplayAttribute>()->displayName().isEmpty() ) {
146  return item.attribute<EntityDisplayAttribute>()->displayName();
147  } else {
148  if ( !item.remoteId().isEmpty() ) {
149  return item.remoteId();
150  }
151  return QString( QLatin1String( "<" ) + QString::number( item.id() ) + QLatin1String( ">" ) );
152  }
153  break;
154  case Qt::DecorationRole:
155  if ( item.hasAttribute<EntityDisplayAttribute>() &&
156  !item.attribute<EntityDisplayAttribute>()->iconName().isEmpty() ) {
157  return item.attribute<EntityDisplayAttribute>()->icon();
158  }
159  break;
160  default:
161  break;
162  }
163  }
164 
165  return QVariant();
166 }
167 
168 QVariant EntityTreeModel::entityData( const Collection &collection, int column, int role ) const
169 {
170  Q_D( const EntityTreeModel );
171 
172  if ( column > 0 ) {
173  return QString();
174  }
175 
176  if ( collection == Collection::root() ) {
177  // Only display the root collection. It may not be edited.
178  if ( role == Qt::DisplayRole ) {
179  return d->m_rootCollectionDisplayName;
180  }
181 
182  if ( role == Qt::EditRole ) {
183  return QVariant();
184  }
185  }
186 
187  switch ( role ) {
188  case Qt::DisplayRole:
189  case Qt::EditRole:
190  if ( column == 0 ) {
191  if ( collection.hasAttribute<EntityDisplayAttribute>() &&
192  !collection.attribute<EntityDisplayAttribute>()->displayName().isEmpty() ) {
193  return collection.attribute<EntityDisplayAttribute>()->displayName();
194  }
195  if ( !collection.name().isEmpty() ) {
196  return collection.name();
197  }
198  return i18n( "Loading..." );
199  }
200  break;
201  case Qt::DecorationRole:
202  if ( collection.hasAttribute<EntityDisplayAttribute>() &&
203  !collection.attribute<EntityDisplayAttribute>()->iconName().isEmpty() ) {
204  return collection.attribute<EntityDisplayAttribute>()->icon();
205  }
206  return KIcon( CollectionUtils::defaultIconName( collection ) );
207  default:
208  break;
209  }
210 
211  return QVariant();
212 }
213 
214 QVariant EntityTreeModel::data( const QModelIndex & index, int role ) const
215 {
216  Q_D( const EntityTreeModel );
217  if ( role == SessionRole ) {
218  return QVariant::fromValue( qobject_cast<QObject *>( d->m_session ) );
219  }
220 
221  // Ugly, but at least the API is clean.
222  const HeaderGroup headerGroup = static_cast<HeaderGroup>( ( role / static_cast<int>( TerminalUserRole ) ) );
223 
224  role %= TerminalUserRole;
225  if ( !index.isValid() ) {
226  if ( ColumnCountRole != role ) {
227  return QVariant();
228  }
229 
230  return entityColumnCount( headerGroup );
231  }
232 
233  if ( ColumnCountRole == role ) {
234  return entityColumnCount( headerGroup );
235  }
236 
237  const Node *node = reinterpret_cast<Node *>( index.internalPointer() );
238 
239  if ( ParentCollectionRole == role &&
240  d->m_collectionFetchStrategy != FetchNoCollections ) {
241  const Collection parentCollection = d->m_collections.value( node->parent );
242  Q_ASSERT( parentCollection.isValid() );
243 
244  return QVariant::fromValue( parentCollection );
245  }
246 
247  if ( Node::Collection == node->type ) {
248 
249  const Collection collection = d->m_collections.value( node->id );
250 
251  if ( !collection.isValid() ) {
252  return QVariant();
253  }
254 
255  switch ( role ) {
256  case MimeTypeRole:
257  return collection.mimeType();
258  break;
259  case RemoteIdRole:
260  return collection.remoteId();
261  break;
262  case CollectionIdRole:
263  return collection.id();
264  break;
265  case ItemIdRole:
266  // QVariant().toInt() is 0, not -1, so we have to handle the ItemIdRole
267  // and CollectionIdRole (below) specially
268  return -1;
269  break;
270  case CollectionRole:
271  return QVariant::fromValue( collection );
272  break;
273  case EntityUrlRole:
274  return collection.url().url();
275  break;
276  case UnreadCountRole:
277  {
278  CollectionStatistics statistics = collection.statistics();
279  return statistics.unreadCount();
280  }
281  case FetchStateRole:
282  {
283  return d->m_pendingCollectionRetrieveJobs.contains( collection.id() ) ? FetchingState : IdleState;
284  }
285  case CollectionSyncProgressRole:
286  {
287  return d->m_collectionSyncProgress.value( collection.id() );
288  }
289  case IsPopulatedRole:
290  {
291  return d->m_populatedCols.contains( collection.id() );
292  }
293  case Qt::BackgroundRole:
294  {
295  if ( collection.hasAttribute<EntityDisplayAttribute>() ) {
296  EntityDisplayAttribute *eda = collection.attribute<EntityDisplayAttribute>();
297  QColor color = eda->backgroundColor();
298  if ( color.isValid() ) {
299  return color;
300  }
301  }
302  // fall through.
303  }
304  default:
305  return entityData( collection, index.column(), role );
306  break;
307  }
308 
309  } else if ( Node::Item == node->type ) {
310  const Item item = d->m_items.value( node->id );
311  if ( !item.isValid() ) {
312  return QVariant();
313  }
314 
315  switch ( role ) {
316  case ParentCollectionRole:
317  return QVariant::fromValue( item.parentCollection() );
318  case MimeTypeRole:
319  return item.mimeType();
320  break;
321  case RemoteIdRole:
322  return item.remoteId();
323  break;
324  case ItemRole:
325  return QVariant::fromValue( item );
326  break;
327  case ItemIdRole:
328  return item.id();
329  break;
330  case CollectionIdRole:
331  return -1;
332  break;
333  case LoadedPartsRole:
334  return QVariant::fromValue( item.loadedPayloadParts() );
335  break;
336  case AvailablePartsRole:
337  return QVariant::fromValue( item.availablePayloadParts() );
338  break;
339  case EntityUrlRole:
340  return item.url( Akonadi::Item::UrlWithMimeType ).url();
341  break;
342  case Qt::BackgroundRole:
343  {
344  if ( item.hasAttribute<EntityDisplayAttribute>() ) {
345  EntityDisplayAttribute *eda = item.attribute<EntityDisplayAttribute>();
346  const QColor color = eda->backgroundColor();
347  if ( color.isValid() ) {
348  return color;
349  }
350  }
351  // fall through.
352  }
353  default:
354  return entityData( item, index.column(), role );
355  break;
356  }
357  }
358 
359  return QVariant();
360 }
361 
362 
363 Qt::ItemFlags EntityTreeModel::flags( const QModelIndex & index ) const
364 {
365  Q_D( const EntityTreeModel );
366  // Pass modeltest.
367  // http://labs.trolltech.com/forums/topic/79
368  if ( !index.isValid() ) {
369  return 0;
370  }
371 
372  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
373 
374  const Node *node = reinterpret_cast<Node *>( index.internalPointer() );
375 
376  if ( Node::Collection == node->type ) {
377  // cut out entities will be shown as inactive
378  if ( d->m_pendingCutCollections.contains( node->id ) ) {
379  return Qt::ItemIsSelectable;
380  }
381 
382  const Collection collection = d->m_collections.value( node->id );
383  if ( collection.isValid() ) {
384 
385  if ( collection == Collection::root() ) {
386  // Selectable and displayable only.
387  return flags;
388  }
389 
390  const int rights = collection.rights();
391 
392  if ( rights & Collection::CanChangeCollection ) {
393  if ( index.column() == 0 ) {
394  flags |= Qt::ItemIsEditable;
395  }
396  // Changing the collection includes changing the metadata (child entityordering).
397  // Need to allow this by drag and drop.
398  flags |= Qt::ItemIsDropEnabled;
399  }
400  if ( rights & ( Collection::CanCreateCollection | Collection::CanCreateItem | Collection::CanLinkItem ) ) {
401  // Can we drop new collections and items into this collection?
402  flags |= Qt::ItemIsDropEnabled;
403  }
404 
405  // dragging is always possible, even for read-only objects, but they can only be copied, not moved.
406  flags |= Qt::ItemIsDragEnabled;
407 
408  }
409  } else if ( Node::Item == node->type ) {
410  if ( d->m_pendingCutItems.contains( node->id ) ) {
411  return Qt::ItemIsSelectable;
412  }
413 
414  // Rights come from the parent collection.
415 
416  Collection parentCollection;
417  if ( !index.parent().isValid() ) {
418  parentCollection = d->m_rootCollection;
419  } else {
420  const Node *parentNode = reinterpret_cast<Node *>( index.parent().internalPointer() );
421 
422  parentCollection = d->m_collections.value( parentNode->id );
423  }
424  if ( parentCollection.isValid() ) {
425  const int rights = parentCollection.rights();
426 
427  // Can't drop onto items.
428  if ( rights & Collection::CanChangeItem && index.column() == 0 ) {
429  flags = flags | Qt::ItemIsEditable;
430  }
431  // dragging is always possible, even for read-only objects, but they can only be copied, not moved.
432  flags |= Qt::ItemIsDragEnabled;
433  }
434  }
435 
436  return flags;
437 }
438 
439 Qt::DropActions EntityTreeModel::supportedDropActions() const
440 {
441  return ( Qt::CopyAction | Qt::MoveAction | Qt::LinkAction );
442 }
443 
444 QStringList EntityTreeModel::mimeTypes() const
445 {
446  // TODO: Should this return the mimetypes that the items provide? Allow dragging a contact from here for example.
447  return QStringList() << QLatin1String( "text/uri-list" );
448 }
449 
450 bool EntityTreeModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
451 {
452  Q_UNUSED( row );
453  Q_UNUSED( column );
454  Q_D( EntityTreeModel );
455 
456  // Can't drop onto Collection::root.
457  if ( !parent.isValid() ) {
458  return false;
459  }
460 
461  // TODO Use action and collection rights and return false if necessary
462 
463  // if row and column are -1, then the drop was on parent directly.
464  // data should then be appended on the end of the items of the collections as appropriate.
465  // That will mean begin insert rows etc.
466  // Otherwise it was a sibling of the row^th item of parent.
467  // Needs to be handled when ordering is accounted for.
468 
469  // Handle dropping between items as well as on items.
470 // if ( row != -1 && column != -1 )
471 // {
472 // }
473 
474 
475  if ( action == Qt::IgnoreAction ) {
476  return true;
477  }
478 
479 // Shouldn't do this. Need to be able to drop vcards for example.
480 // if ( !data->hasFormat( "text/uri-list" ) )
481 // return false;
482 
483  Node *node = reinterpret_cast<Node *>( parent.internalId() );
484 
485  Q_ASSERT( node );
486 
487  if ( Node::Item == node->type ) {
488  if ( !parent.parent().isValid() ) {
489  // The drop is somehow on an item with no parent (shouldn't happen)
490  // The drop should be considered handled anyway.
491  kWarning() << "Dropped onto item with no parent collection";
492  return true;
493  }
494 
495  // A drop onto an item should be considered as a drop onto its parent collection
496  node = reinterpret_cast<Node *>( parent.parent().internalId() );
497  }
498 
499  if ( Node::Collection == node->type ) {
500  const Collection destCollection = d->m_collections.value( node->id );
501 
502  // Applications can't create new collections in root. Only resources can.
503  if ( destCollection == Collection::root() ) {
504  // Accept the event so that it doesn't propagate.
505  return true;
506  }
507 
508  if ( data->hasFormat( QLatin1String( "text/uri-list" ) ) ) {
509 
510  MimeTypeChecker mimeChecker;
511  mimeChecker.setWantedMimeTypes( destCollection.contentMimeTypes() );
512 
513  const KUrl::List urls = KUrl::List::fromMimeData( data );
514  foreach ( const KUrl &url, urls ) {
515  const Collection collection = d->m_collections.value( Collection::fromUrl( url ).id() );
516  if ( collection.isValid() ) {
517  if ( collection.parentCollection().id() == destCollection.id() &&
518  action != Qt::CopyAction ) {
519  kDebug() << "Error: source and destination of move are the same.";
520  return false;
521  }
522 
523  if ( !mimeChecker.isWantedCollection( collection ) ) {
524  kDebug() << "unwanted collection" << mimeChecker.wantedMimeTypes() << collection.contentMimeTypes();
525  return false;
526  }
527 
528  if ( url.hasQueryItem( QLatin1String( "name" ) ) ) {
529  const QString collectionName = url.queryItemValue( QLatin1String( "name" ) );
530  const QStringList collectionNames = d->childCollectionNames( destCollection );
531 
532  if ( collectionNames.contains( collectionName ) ) {
533  KMessageBox::error( 0, i18n( "The target collection '%1' contains already\na collection with name '%2'.",
534  destCollection.name(), collection.name() ) );
535  return false;
536  }
537  }
538  } else {
539  const Item item = d->m_items.value( Item::fromUrl( url ).id() );
540  if ( item.isValid() ) {
541  if ( item.parentCollection().id() == destCollection.id() && action != Qt::CopyAction ) {
542  kDebug() << "Error: source and destination of move are the same.";
543  return false;
544  }
545 
546  if ( !mimeChecker.isWantedItem( item ) ) {
547  kDebug() << "unwanted item" << mimeChecker.wantedMimeTypes() << item.mimeType();
548  return false;
549  }
550  }
551  }
552  }
553 
554  KJob *job = PasteHelper::pasteUriList( data, destCollection, action, d->m_session );
555  if ( !job ) {
556  return false;
557  }
558 
559  connect( job, SIGNAL(result(KJob*)), SLOT(pasteJobDone(KJob*)) );
560 
561  // Accpet the event so that it doesn't propagate.
562  return true;
563  } else {
564 // not a set of uris. Maybe vcards etc. Check if the parent supports them, and maybe do
565  // fromMimeData for them. Hmm, put it in the same transaction with the above?
566  // TODO: This should be handled first, not last.
567  }
568  }
569 
570  return false;
571 }
572 
573 QModelIndex EntityTreeModel::index( int row, int column, const QModelIndex & parent ) const
574 {
575 
576  Q_D( const EntityTreeModel );
577 
578  if ( parent.column() > 0 ) {
579  return QModelIndex();
580  }
581 
582  //TODO: don't use column count here? Use some d-> func.
583  if ( column >= columnCount() ||
584  column < 0 ) {
585  return QModelIndex();
586  }
587 
588  QList<Node*> childEntities;
589 
590  const Node *parentNode = reinterpret_cast<Node*>( parent.internalPointer() );
591 
592  if ( !parentNode || !parent.isValid() ) {
593  if ( d->m_showRootCollection ) {
594  childEntities << d->m_childEntities.value( -1 );
595  } else {
596  childEntities = d->m_childEntities.value( d->m_rootCollection.id() );
597  }
598  } else {
599  if ( parentNode->id >= 0 ) {
600  childEntities = d->m_childEntities.value( parentNode->id );
601  }
602  }
603 
604  const int size = childEntities.size();
605  if ( row < 0 || row >= size ) {
606  return QModelIndex();
607  }
608 
609  Node *node = childEntities.at( row );
610 
611  return createIndex( row, column, reinterpret_cast<void*>( node ) );
612 }
613 
614 QModelIndex EntityTreeModel::parent( const QModelIndex & index ) const
615 {
616  Q_D( const EntityTreeModel );
617 
618  if ( !index.isValid() ) {
619  return QModelIndex();
620  }
621 
622  if ( d->m_collectionFetchStrategy == InvisibleCollectionFetch ||
623  d->m_collectionFetchStrategy == FetchNoCollections ) {
624  return QModelIndex();
625  }
626 
627  const Node *node = reinterpret_cast<Node*>( index.internalPointer() );
628 
629  if ( !node ) {
630  return QModelIndex();
631  }
632 
633  const Collection collection = d->m_collections.value( node->parent );
634 
635  if ( !collection.isValid() ) {
636  return QModelIndex();
637  }
638 
639  if ( collection.id() == d->m_rootCollection.id() ) {
640  if ( !d->m_showRootCollection ) {
641  return QModelIndex();
642  } else {
643  return createIndex( 0, 0, reinterpret_cast<void *>( d->m_rootNode ) );
644  }
645  }
646 
647  Q_ASSERT( collection.parentCollection().isValid() );
648  const int row = d->indexOf<Node::Collection>( d->m_childEntities.value( collection.parentCollection().id() ), collection.id() );
649 
650  Q_ASSERT( row >= 0 );
651  Node *parentNode = d->m_childEntities.value( collection.parentCollection().id() ).at( row );
652 
653  return createIndex( row, 0, reinterpret_cast<void*>( parentNode ) );
654 }
655 
656 int EntityTreeModel::rowCount( const QModelIndex & parent ) const
657 {
658  Q_D( const EntityTreeModel );
659 
660  if ( d->m_collectionFetchStrategy == InvisibleCollectionFetch ||
661  d->m_collectionFetchStrategy == FetchNoCollections ) {
662  if ( parent.isValid() ) {
663  return 0;
664  } else {
665  return d->m_items.size();
666  }
667  }
668 
669  if ( !parent.isValid() ) {
670  // If we're showing the root collection then it will be the only child of the root.
671  if ( d->m_showRootCollection ) {
672  return d->m_childEntities.value( -1 ).size();
673  }
674  return d->m_childEntities.value( d->m_rootCollection.id() ).size();
675  }
676 
677  if ( parent.column() != 0 ) {
678  return 0;
679  }
680 
681  const Node *node = reinterpret_cast<Node*>( parent.internalPointer() );
682 
683  if ( !node ) {
684  return 0;
685  }
686 
687  if ( Node::Item == node->type ) {
688  return 0;
689  }
690 
691  Q_ASSERT( parent.isValid() );
692  return d->m_childEntities.value( node->id ).size();
693 }
694 
695 int EntityTreeModel::entityColumnCount( HeaderGroup headerGroup ) const
696 {
697  // Not needed in this model.
698  Q_UNUSED( headerGroup );
699 
700  return 1;
701 }
702 
703 QVariant EntityTreeModel::entityHeaderData( int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup ) const
704 {
705  Q_D( const EntityTreeModel );
706  // Not needed in this model.
707  Q_UNUSED( headerGroup );
708 
709  if ( section == 0 &&
710  orientation == Qt::Horizontal &&
711  role == Qt::DisplayRole ) {
712  if ( d->m_rootCollection == Collection::root() ) {
713  return i18nc( "@title:column Name of a thing", "Name" );
714  }
715  return d->m_rootCollection.name();
716  }
717 
718  return QAbstractItemModel::headerData( section, orientation, role );
719 }
720 
721 QVariant EntityTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const
722 {
723  const HeaderGroup headerGroup = static_cast<HeaderGroup>( ( role / static_cast<int>( TerminalUserRole ) ) );
724 
725  role %= TerminalUserRole;
726  return entityHeaderData( section, orientation, role, headerGroup );
727 }
728 
729 QMimeData *EntityTreeModel::mimeData( const QModelIndexList &indexes ) const
730 {
731  Q_D( const EntityTreeModel );
732 
733  QMimeData *data = new QMimeData();
734  KUrl::List urls;
735  foreach ( const QModelIndex &index, indexes ) {
736  if ( index.column() != 0 ) {
737  continue;
738  }
739 
740  if ( !index.isValid() ) {
741  continue;
742  }
743 
744  const Node *node = reinterpret_cast<Node*>( index.internalPointer() );
745 
746  if ( Node::Collection == node->type ) {
747  urls << d->m_collections.value( node->id ).url( Collection::UrlWithName );
748  } else if ( Node::Item == node->type ) {
749  urls << d->m_items.value( node->id ).url( Item::UrlWithMimeType );
750  } else { // if that happens something went horrible wrong
751  Q_ASSERT( false );
752  }
753  }
754 
755  urls.populateMimeData( data );
756 
757  return data;
758 }
759 
760 // Always return false for actions which take place asyncronously, eg via a Job.
761 bool EntityTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
762 {
763  Q_D( EntityTreeModel );
764 
765  const Node *node = reinterpret_cast<Node*>( index.internalPointer() );
766 
767  if ( role == PendingCutRole ) {
768  if ( index.isValid() && value.toBool() ) {
769  if ( Node::Collection == node->type ) {
770  d->m_pendingCutCollections.append( node->id );
771  }
772 
773  if ( Node::Item == node->type ) {
774  d->m_pendingCutItems.append( node->id );
775  }
776  } else {
777  d->m_pendingCutCollections.clear();
778  d->m_pendingCutItems.clear();
779  }
780  return true;
781  }
782 
783  if ( index.isValid() &&
784  node->type == Node::Collection &&
785  ( role == CollectionRefRole ||
786  role == CollectionDerefRole ) ) {
787  const Collection collection = index.data( CollectionRole ).value<Collection>();
788  Q_ASSERT( collection.isValid() );
789 
790  if ( role == CollectionDerefRole ) {
791  d->deref( collection.id() );
792  } else if ( role == CollectionRefRole ) {
793  d->ref( collection.id() );
794  }
795  }
796 
797  if ( index.column() == 0 &&
798  ( role & ( Qt::EditRole | ItemRole | CollectionRole ) ) ) {
799  if ( Node::Collection == node->type ) {
800 
801  Collection collection = d->m_collections.value( node->id );
802 
803  if ( !collection.isValid() || !value.isValid() ) {
804  return false;
805  }
806 
807  if ( Qt::EditRole == role ) {
808  collection.setName( value.toString() );
809 
810  if ( collection.hasAttribute<EntityDisplayAttribute>() ) {
811  EntityDisplayAttribute *displayAttribute = collection.attribute<EntityDisplayAttribute>();
812  displayAttribute->setDisplayName( value.toString() );
813  }
814  }
815 
816  if ( Qt::BackgroundRole == role ) {
817  QColor color = value.value<QColor>();
818 
819  if ( !color.isValid() ) {
820  return false;
821  }
822 
823  EntityDisplayAttribute *eda = collection.attribute<EntityDisplayAttribute>( Entity::AddIfMissing );
824  eda->setBackgroundColor( color );
825  }
826 
827  if ( CollectionRole == role ) {
828  collection = value.value<Collection>();
829  }
830 
831  CollectionModifyJob *job = new CollectionModifyJob( collection, d->m_session );
832  connect( job, SIGNAL(result(KJob*)),
833  SLOT(updateJobDone(KJob*)) );
834 
835  return false;
836  } else if ( Node::Item == node->type ) {
837 
838  Item item = d->m_items.value( node->id );
839 
840  if ( !item.isValid() || !value.isValid() ) {
841  return false;
842  }
843 
844  if ( Qt::EditRole == role ) {
845  if ( item.hasAttribute<EntityDisplayAttribute>() ) {
846  EntityDisplayAttribute *displayAttribute = item.attribute<EntityDisplayAttribute>( Entity::AddIfMissing );
847  displayAttribute->setDisplayName( value.toString() );
848  }
849  }
850 
851  if ( Qt::BackgroundRole == role ) {
852  QColor color = value.value<QColor>();
853 
854  if ( !color.isValid() ) {
855  return false;
856  }
857 
858  EntityDisplayAttribute *eda = item.attribute<EntityDisplayAttribute>( Entity::AddIfMissing );
859  eda->setBackgroundColor( color );
860  }
861 
862  if ( ItemRole == role ) {
863  item = value.value<Item>();
864  Q_ASSERT( item.id() == node->id );
865  }
866 
867  ItemModifyJob *itemModifyJob = new ItemModifyJob( item, d->m_session );
868  connect( itemModifyJob, SIGNAL(result(KJob*)),
869  SLOT(updateJobDone(KJob*)) );
870 
871  return false;
872  }
873  }
874 
875  return QAbstractItemModel::setData( index, value, role );
876 }
877 
878 bool EntityTreeModel::canFetchMore( const QModelIndex & parent ) const
879 {
880  Q_UNUSED( parent )
881  return false;
882 }
883 
884 void EntityTreeModel::fetchMore( const QModelIndex & parent )
885 {
886  Q_D( EntityTreeModel );
887 
888  if ( !d->canFetchMore( parent ) ) {
889  return;
890  }
891 
892  if ( d->m_collectionFetchStrategy == InvisibleCollectionFetch ) {
893  return;
894  }
895 
896  if ( d->m_itemPopulation == ImmediatePopulation ) {
897  // Nothing to do. The items are already in the model.
898  return;
899  } else if ( d->m_itemPopulation == LazyPopulation ) {
900  const Collection collection = parent.data( CollectionRole ).value<Collection>();
901 
902  if ( !collection.isValid() ) {
903  return;
904  }
905 
906  d->fetchItems( collection );
907  }
908 }
909 
910 bool EntityTreeModel::hasChildren( const QModelIndex &parent ) const
911 {
912  Q_D( const EntityTreeModel );
913 
914  if ( d->m_collectionFetchStrategy == InvisibleCollectionFetch ||
915  d->m_collectionFetchStrategy == FetchNoCollections ) {
916  return parent.isValid() ? false : !d->m_items.isEmpty();
917  }
918 
919  // TODO: Empty collections right now will return true and get a little + to expand.
920  // There is probably no way to tell if a collection
921  // has child items in akonadi without first attempting an itemFetchJob...
922  // Figure out a way to fix this. (Statistics)
923  return ( ( rowCount( parent ) > 0 ) ||
924  ( canFetchMore( parent ) && d->m_itemPopulation == LazyPopulation ) );
925 }
926 
927 bool EntityTreeModel::isCollectionTreeFetched() const
928 {
929  Q_D( const EntityTreeModel );
930 
931  return d->m_collectionTreeFetched;
932 }
933 
934 bool EntityTreeModel::entityMatch( const Item &item, const QVariant &value, Qt::MatchFlags flags ) const
935 {
936  Q_UNUSED( item );
937  Q_UNUSED( value );
938  Q_UNUSED( flags );
939  return false;
940 }
941 
942 bool EntityTreeModel::entityMatch( const Collection &collection, const QVariant &value, Qt::MatchFlags flags ) const
943 {
944  Q_UNUSED( collection );
945  Q_UNUSED( value );
946  Q_UNUSED( flags );
947  return false;
948 }
949 
950 QModelIndexList EntityTreeModel::match( const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags ) const
951 {
952  Q_D( const EntityTreeModel );
953 
954  if ( role == CollectionIdRole || role == CollectionRole ) {
955  Collection::Id id;
956  if ( role == CollectionRole ) {
957  const Collection collection = value.value<Collection>();
958  id = collection.id();
959  } else {
960  id = value.toLongLong();
961  }
962 
963  QModelIndexList list;
964 
965  const Collection collection = d->m_collections.value( id );
966 
967  if ( !collection.isValid() ) {
968  return list;
969  }
970 
971  const QModelIndex collectionIndex = d->indexForCollection( collection );
972  Q_ASSERT( collectionIndex.isValid() );
973  list << collectionIndex;
974 
975  return list;
976  }
977 
978  if ( role == ItemIdRole || role == ItemRole ) {
979  Item::Id id;
980  if ( role == ItemRole ) {
981  const Item item = value.value<Item>();
982  id = item.id();
983  } else {
984  id = value.toLongLong();
985  }
986  QModelIndexList list;
987 
988  const Item item = d->m_items.value( id );
989  if ( !item.isValid() ) {
990  return list;
991  }
992 
993  return d->indexesForItem( item );
994  }
995 
996  if ( role == EntityUrlRole ) {
997  const KUrl url( value.toString() );
998  const Item item = Item::fromUrl( url );
999 
1000  if ( item.isValid() ) {
1001  return d->indexesForItem( d->m_items.value( item.id() ) );
1002  }
1003 
1004  const Collection collection = Collection::fromUrl( url );
1005  QModelIndexList list;
1006  if ( collection.isValid() ) {
1007  list << d->indexForCollection( collection );
1008  }
1009 
1010  return list;
1011  }
1012 
1013  if ( role != AmazingCompletionRole ) {
1014  return QAbstractItemModel::match( start, role, value, hits, flags );
1015  }
1016 
1017  // Try to match names, and email addresses.
1018  QModelIndexList list;
1019 
1020  if ( role < 0 ||
1021  !start.isValid() ||
1022  !value.isValid() ) {
1023  return list;
1024  }
1025 
1026  const int column = 0;
1027  int row = start.row();
1028  const QModelIndex parentIndex = start.parent();
1029  const int parentRowCount = rowCount( parentIndex );
1030 
1031  while ( row < parentRowCount &&
1032  ( hits == -1 || list.size() < hits ) ) {
1033  const QModelIndex idx = index( row, column, parentIndex );
1034  const Item item = idx.data( ItemRole ).value<Item>();
1035 
1036  if ( !item.isValid() ) {
1037  const Collection collection = idx.data( CollectionRole ).value<Collection>();
1038  if ( !collection.isValid() ) {
1039  continue;
1040  }
1041 
1042  if ( entityMatch( collection, value, flags ) ) {
1043  list << idx;
1044  }
1045 
1046  } else {
1047  if ( entityMatch( item, value, flags ) ) {
1048  list << idx;
1049  }
1050  }
1051 
1052  ++row;
1053  }
1054 
1055  return list;
1056 }
1057 
1058 bool EntityTreeModel::insertRows( int, int, const QModelIndex& )
1059 {
1060  return false;
1061 }
1062 
1063 bool EntityTreeModel::insertColumns( int, int, const QModelIndex& )
1064 {
1065  return false;
1066 }
1067 
1068 bool EntityTreeModel::removeRows( int, int, const QModelIndex& )
1069 {
1070  return false;
1071 }
1072 
1073 bool EntityTreeModel::removeColumns( int, int, const QModelIndex& )
1074 {
1075  return false;
1076 }
1077 
1078 void EntityTreeModel::setItemPopulationStrategy( ItemPopulationStrategy strategy )
1079 {
1080  Q_D( EntityTreeModel );
1081  d->beginResetModel();
1082  d->m_itemPopulation = strategy;
1083 
1084  if ( strategy == NoItemPopulation ) {
1085  disconnect( d->m_monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)),
1086  this, SLOT(monitoredItemAdded(Akonadi::Item,Akonadi::Collection)) );
1087  disconnect( d->m_monitor, SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>)),
1088  this, SLOT(monitoredItemChanged(Akonadi::Item,QSet<QByteArray>)) );
1089  disconnect( d->m_monitor, SIGNAL(itemRemoved(Akonadi::Item)),
1090  this, SLOT(monitoredItemRemoved(Akonadi::Item)) );
1091  disconnect( d->m_monitor, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)),
1092  this, SLOT(monitoredItemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)) );
1093 
1094  disconnect( d->m_monitor, SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)),
1095  this, SLOT(monitoredItemLinked(Akonadi::Item,Akonadi::Collection)) );
1096  disconnect( d->m_monitor, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)),
1097  this, SLOT(monitoredItemUnlinked(Akonadi::Item,Akonadi::Collection)) );
1098  }
1099 
1100  d->m_monitor->d_ptr->useRefCounting = ( strategy == LazyPopulation );
1101 
1102  d->endResetModel();
1103 }
1104 
1105 EntityTreeModel::ItemPopulationStrategy EntityTreeModel::itemPopulationStrategy() const
1106 {
1107  Q_D( const EntityTreeModel );
1108  return d->m_itemPopulation;
1109 }
1110 
1111 void EntityTreeModel::setIncludeRootCollection( bool include )
1112 {
1113  Q_D( EntityTreeModel );
1114  d->beginResetModel();
1115  d->m_showRootCollection = include;
1116  d->endResetModel();
1117 }
1118 
1119 bool EntityTreeModel::includeRootCollection() const
1120 {
1121  Q_D( const EntityTreeModel );
1122  return d->m_showRootCollection;
1123 }
1124 
1125 void EntityTreeModel::setRootCollectionDisplayName( const QString &displayName )
1126 {
1127  Q_D( EntityTreeModel );
1128  d->m_rootCollectionDisplayName = displayName;
1129 
1130  // TODO: Emit datachanged if it is being shown.
1131 }
1132 
1133 QString EntityTreeModel::rootCollectionDisplayName() const
1134 {
1135  Q_D( const EntityTreeModel );
1136  return d->m_rootCollectionDisplayName;
1137 }
1138 
1139 void EntityTreeModel::setCollectionFetchStrategy( CollectionFetchStrategy strategy )
1140 {
1141  Q_D( EntityTreeModel );
1142  d->beginResetModel();
1143  d->m_collectionFetchStrategy = strategy;
1144 
1145  if ( strategy == FetchNoCollections ||
1146  strategy == InvisibleCollectionFetch ) {
1147  disconnect( d->m_monitor, SIGNAL(collectionChanged(Akonadi::Collection)),
1148  this, SLOT(monitoredCollectionChanged(Akonadi::Collection)) );
1149  disconnect( d->m_monitor, SIGNAL(collectionAdded(Akonadi::Collection,Akonadi::Collection)),
1150  this, SLOT(monitoredCollectionAdded(Akonadi::Collection,Akonadi::Collection)) );
1151  disconnect( d->m_monitor, SIGNAL(collectionRemoved(Akonadi::Collection)),
1152  this, SLOT(monitoredCollectionRemoved(Akonadi::Collection)) );
1153  disconnect( d->m_monitor,
1154  SIGNAL(collectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)),
1155  this, SLOT(monitoredCollectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)) );
1156  d->m_monitor->fetchCollection( false );
1157  } else
1158  d->m_monitor->fetchCollection( true );
1159 
1160  d->endResetModel();
1161 }
1162 
1163 EntityTreeModel::CollectionFetchStrategy EntityTreeModel::collectionFetchStrategy() const
1164 {
1165  Q_D( const EntityTreeModel );
1166  return d->m_collectionFetchStrategy;
1167 }
1168 
1169 static QPair<QList<const QAbstractProxyModel *>, const EntityTreeModel *> proxiesAndModel( const QAbstractItemModel *model )
1170 {
1171  QList<const QAbstractProxyModel *> proxyChain;
1172  const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel *>( model );
1173  const QAbstractItemModel *_model = model;
1174  while ( proxy ) {
1175  proxyChain.prepend( proxy );
1176  _model = proxy->sourceModel();
1177  proxy = qobject_cast<const QAbstractProxyModel *>( _model );
1178  }
1179 
1180  const EntityTreeModel *etm = qobject_cast<const EntityTreeModel *>( _model );
1181  return qMakePair( proxyChain, etm );
1182 }
1183 
1184 static QModelIndex proxiedIndex( const QModelIndex &idx, QList<const QAbstractProxyModel *> proxyChain )
1185 {
1186  QListIterator<const QAbstractProxyModel *> it( proxyChain );
1187  QModelIndex _idx = idx;
1188  while ( it.hasNext() ) {
1189  _idx = it.next()->mapFromSource( _idx );
1190  }
1191  return _idx;
1192 }
1193 
1194 QModelIndex EntityTreeModel::modelIndexForCollection( const QAbstractItemModel *model, const Collection &collection )
1195 {
1196  QPair<QList<const QAbstractProxyModel *>, const EntityTreeModel*> pair = proxiesAndModel( model );
1197  QModelIndex idx = pair.second->d_ptr->indexForCollection( collection );
1198  return proxiedIndex( idx, pair.first );
1199 }
1200 
1201 QModelIndexList EntityTreeModel::modelIndexesForItem( const QAbstractItemModel *model, const Item &item )
1202 {
1203  QPair<QList<const QAbstractProxyModel *>, const EntityTreeModel*> pair = proxiesAndModel( model );
1204 
1205  if ( !pair.second ) {
1206  kWarning() << "Couldn't find an EntityTreeModel";
1207  return QModelIndexList();
1208  }
1209 
1210  QModelIndexList list = pair.second->d_ptr->indexesForItem( item );
1211  QModelIndexList proxyList;
1212  foreach ( const QModelIndex &idx, list ) {
1213  const QModelIndex pIdx = proxiedIndex( idx, pair.first );
1214  if ( pIdx.isValid() ) {
1215  proxyList << pIdx;
1216  }
1217  }
1218  return proxyList;
1219 }
1220 
1221 #include "moc_entitytreemodel.cpp"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:27:36 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs-4.10.5 API Reference

Skip menu "kdepimlibs-4.10.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal