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

Skip menu "kdepimlibs-4.11.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