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

Skip menu "kdepimlibs-4.9.4 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • 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