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

akonadi

  • akonadi
  • calendar
etmcalendar.cpp
1 /*
2  Copyright (C) 2011-2012 Sérgio Martins <iamsergio@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 "blockalarmsattribute.h"
21 #include "etmcalendar.h"
22 #include "etmcalendar_p.h"
23 #include "incidencefetchjob_p.h"
24 #include "calendarmodel_p.h"
25 #include "kcolumnfilterproxymodel_p.h"
26 
27 #include <akonadi/item.h>
28 #include <akonadi/session.h>
29 #include <akonadi/collection.h>
30 #include <akonadi/changerecorder.h>
31 #include <akonadi/itemfetchscope.h>
32 #include <akonadi/entitydisplayattribute.h>
33 #include <akonadi/entitymimetypefiltermodel.h>
34 #include <akonadi/entitytreemodel.h>
35 #include <KSelectionProxyModel>
36 #include <kcheckableproxymodel.h>
37 
38 #include <QSortFilterProxyModel>
39 #include <QItemSelectionModel>
40 
41 using namespace Akonadi;
42 using namespace KCalCore;
43 
44 //TODO: implement batchAdding
45 
46 ETMCalendarPrivate::ETMCalendarPrivate( ETMCalendar *qq ) : CalendarBasePrivate( qq )
47  , mETM( 0 )
48  , mFilteredETM( 0 )
49  , mCheckableProxyModel( 0 )
50  , q( qq )
51 {
52 }
53 
54 void ETMCalendarPrivate::init()
55 {
56  Akonadi::Session *session = new Akonadi::Session( "ETMCalendar", q );
57  Akonadi::ChangeRecorder *monitor = new Akonadi::ChangeRecorder( q );
58  connect( monitor, SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)),
59  q, SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)) );
60 
61  Akonadi::ItemFetchScope scope;
62  scope.fetchFullPayload( true );
63  scope.fetchAttribute<Akonadi::EntityDisplayAttribute>();
64 
65  monitor->setSession( session );
66  monitor->setCollectionMonitored( Akonadi::Collection::root() );
67  monitor->fetchCollection( true );
68  monitor->setItemFetchScope( scope );
69  monitor->setMimeTypeMonitored( "text/calendar", true );
70  monitor->setMimeTypeMonitored( KCalCore::Event::eventMimeType(), true );
71  monitor->setMimeTypeMonitored( KCalCore::Todo::todoMimeType(), true );
72  monitor->setMimeTypeMonitored( KCalCore::Journal::journalMimeType(), true );
73  mETM = new CalendarModel( monitor, q );
74  mETM->setObjectName( "ETM" );
75 
76  setupFilteredETM();
77 
78  connect( mETM, SIGNAL(rowsInserted(QModelIndex,int,int)),
79  SLOT(onRowsInserted(QModelIndex,int,int)) );
80  connect( mETM, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
81  SLOT(onDataChanged(QModelIndex,QModelIndex)) );
82  connect( mETM, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
83  SLOT(onRowsMoved(QModelIndex,int,int,QModelIndex,int)) );
84  connect( mETM, SIGNAL(rowsRemoved(QModelIndex,int,int)),
85  SLOT(onRowsRemoved(QModelIndex,int,int)) );
86 
87  connect( mFilteredETM, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
88  SLOT(onDataChangedInFilteredModel(QModelIndex,QModelIndex)) );
89  connect( mFilteredETM, SIGNAL(layoutChanged()),
90  SLOT(onLayoutChangedInFilteredModel()) );
91  connect( mFilteredETM, SIGNAL(modelReset()),
92  SLOT(onModelResetInFilteredModel()) );
93  connect( mFilteredETM, SIGNAL(rowsInserted(QModelIndex,int,int)),
94  SLOT(onRowsInsertedInFilteredModel(QModelIndex,int,int)) );
95  connect( mFilteredETM, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
96  SLOT(onRowsAboutToBeRemovedInFilteredModel(QModelIndex,int,int)) );
97 
98  loadFromETM();
99 }
100 
101 void ETMCalendarPrivate::setupFilteredETM()
102 {
103  // Our calendar tree must be sorted.
104  QSortFilterProxyModel *sortFilterProxy = new QSortFilterProxyModel( this );
105  sortFilterProxy->setObjectName( "Sort" );
106  sortFilterProxy->setDynamicSortFilter( true );
107  sortFilterProxy->setSortCaseSensitivity( Qt::CaseInsensitive );
108  sortFilterProxy->setSourceModel( mETM );
109 
110  // We're only interested in the CollectionTitle column
111  KColumnFilterProxyModel *columnFilterProxy = new KColumnFilterProxyModel( this );
112  columnFilterProxy->setSourceModel( sortFilterProxy );
113  columnFilterProxy->setVisibleColumn( CalendarModel::CollectionTitle );
114  columnFilterProxy->setObjectName( "Remove columns" );
115 
116  // Keep track of selected items.
117  QItemSelectionModel* selectionModel = new QItemSelectionModel( columnFilterProxy );
118  selectionModel->setObjectName( "Calendar Selection Model" );
119 
120  // Make item selection work by means of checkboxes.
121  mCheckableProxyModel = new KCheckableProxyModel( this );
122  mCheckableProxyModel->setSelectionModel( selectionModel );
123  mCheckableProxyModel->setSourceModel( columnFilterProxy );
124  mCheckableProxyModel->setObjectName( "Add checkboxes" );
125 
126  KSelectionProxyModel* selectionProxy = new KSelectionProxyModel( selectionModel, this );
127  selectionProxy->setObjectName( "Only show items of selected collection" );
128  selectionProxy->setFilterBehavior( KSelectionProxyModel::ChildrenOfExactSelection );
129  selectionProxy->setSourceModel( mETM );
130 
131  mFilteredETM = new Akonadi::EntityMimeTypeFilterModel( this );
132  mFilteredETM->setHeaderGroup( Akonadi::EntityTreeModel::ItemListHeaders );
133  mFilteredETM->setSourceModel( selectionProxy );
134  mFilteredETM->setSortRole( CalendarModel::SortRole );
135  mFilteredETM->setObjectName( "Show headers" );
136 }
137 
138 ETMCalendarPrivate::~ETMCalendarPrivate()
139 {
140 }
141 
142 void ETMCalendarPrivate::loadFromETM()
143 {
144  itemsAdded( itemsFromModel( mETM ) );
145 }
146 
147 void ETMCalendarPrivate::clear()
148 {
149  mCollectionMap.clear();
150 
151  itemsRemoved( mItemById.values() );
152  Q_ASSERT( mItemById.isEmpty() );
153  Q_ASSERT( mItemIdByUid.isEmpty() );
154 
155  //m_virtualItems.clear();
156 }
157 
158 Akonadi::Item::List ETMCalendarPrivate::itemsFromModel( const QAbstractItemModel *model,
159  const QModelIndex &parentIndex,
160  int start, int end )
161 {
162  const int endRow = end >= 0 ? end : model->rowCount( parentIndex ) - 1;
163  Akonadi::Item::List items;
164  int row = start;
165  QModelIndex i = model->index( row, 0, parentIndex );
166  while ( row <= endRow ) {
167  const Akonadi::Item item = itemFromIndex( i );
168  if ( item.hasPayload<KCalCore::Incidence::Ptr>() ) {
169  items << item;
170  } else {
171  const QModelIndex childIndex = i.child( 0, 0 );
172  if ( childIndex.isValid() ) {
173  items << itemsFromModel( model, i );
174  }
175  }
176  ++row;
177  i = i.sibling( row, 0 );
178  }
179  return items;
180 }
181 
182 Akonadi::Collection::List ETMCalendarPrivate::collectionsFromModel( const QAbstractItemModel *model,
183  const QModelIndex &parentIndex,
184  int start, int end )
185 {
186  const int endRow = end >= 0 ? end : model->rowCount( parentIndex ) - 1;
187  Akonadi::Collection::List collections;
188  int row = start;
189  QModelIndex i = model->index( row, 0, parentIndex );
190  while ( row <= endRow ) {
191  const Akonadi::Collection collection = collectionFromIndex( i );
192  if ( collection.isValid() ) {
193  collections << collection;
194  QModelIndex childIndex = i.child( 0, 0 );
195  if ( childIndex.isValid() ) {
196  collections << collectionsFromModel( model, i );
197  }
198  }
199  ++row;
200  i = i.sibling( row, 0 );
201  }
202  return collections;
203 }
204 
205 Akonadi::Item ETMCalendarPrivate::itemFromIndex( const QModelIndex &idx )
206 {
207  Akonadi::Item item = idx.data( Akonadi::EntityTreeModel::ItemRole ).value<Akonadi::Item>();
208  item.setParentCollection(
209  idx.data( Akonadi::EntityTreeModel::ParentCollectionRole ).value<Akonadi::Collection>() );
210  return item;
211 }
212 
213 void ETMCalendarPrivate::itemsAdded( const Akonadi::Item::List &items )
214 {
215  foreach( const Akonadi::Item &item, items ) {
216  internalInsert( item );
217  }
218  emit q->calendarChanged();
219 }
220 
221 void ETMCalendarPrivate::itemsRemoved( const Akonadi::Item::List &items )
222 {
223  foreach( const Akonadi::Item &item, items ) {
224  internalRemove( item );
225  }
226  emit q->calendarChanged();
227 }
228 
229 Akonadi::Collection ETMCalendarPrivate::collectionFromIndex( const QModelIndex &index )
230 {
231  return index.data( Akonadi::EntityTreeModel::CollectionRole ).value<Akonadi::Collection>();
232 }
233 
234 void ETMCalendarPrivate::onRowsInserted( const QModelIndex &index,
235  int start, int end )
236 {
237  Akonadi::Collection::List collections = collectionsFromModel( mETM, index,
238  start, end );
239 
240  foreach ( const Akonadi::Collection &collection, collections ) {
241  mCollectionMap[collection.id()] = collection;
242  }
243 
244  if ( !collections.isEmpty() )
245  emit q->collectionsAdded( collections );
246 }
247 
248 void ETMCalendarPrivate::onRowsRemoved( const QModelIndex &index, int start, int end )
249 {
250  Akonadi::Collection::List collections = collectionsFromModel( mETM, index, start, end );
251  foreach ( const Akonadi::Collection &collection, collections ) {
252  mCollectionMap.remove( collection.id() );
253  }
254 
255  if ( !collections.isEmpty() )
256  emit q->collectionsRemoved( collections );
257 }
258 
259 void ETMCalendarPrivate::onDataChanged( const QModelIndex &topLeft,
260  const QModelIndex &bottomRight )
261 {
262  // We only update collections, because items are handled in the filtered model
263  Q_ASSERT( topLeft.row() <= bottomRight.row() );
264  const int endRow = bottomRight.row();
265  QModelIndex i( topLeft );
266  int row = i.row();
267  while ( row <= endRow ) {
268  const Akonadi::Collection col = collectionFromIndex( i );
269  if ( col.isValid() ) {
270  // Attributes might have changed, store the new collection and discard the old one
271  mCollectionMap.insert( col.id(), col );
272  }
273  ++row;
274  }
275 }
276 
277 void ETMCalendarPrivate::onRowsMoved( const QModelIndex &sourceParent,
278  int sourceStart,
279  int sourceEnd,
280  const QModelIndex &destinationParent,
281  int destinationRow )
282 {
283  //TODO
284  Q_UNUSED( sourceParent );
285  Q_UNUSED( sourceStart );
286  Q_UNUSED( sourceEnd );
287  Q_UNUSED( destinationParent );
288  Q_UNUSED( destinationRow );
289 }
290 
291 void ETMCalendarPrivate::onLayoutChangedInFilteredModel()
292 {
293  clear();
294  loadFromETM();
295 }
296 
297 void ETMCalendarPrivate::onModelResetInFilteredModel()
298 {
299  clear();
300  loadFromETM();
301 }
302 
303 void ETMCalendarPrivate::onDataChangedInFilteredModel( const QModelIndex &topLeft,
304  const QModelIndex &bottomRight )
305 {
306  Q_ASSERT( topLeft.row() <= bottomRight.row() );
307  const int endRow = bottomRight.row();
308  QModelIndex i( topLeft );
309  int row = i.row();
310  while ( row <= endRow ) {
311  const Akonadi::Item item = itemFromIndex( i );
312  if ( item.isValid() ) {
313  Incidence::Ptr newIncidence = item.payload<KCalCore::Incidence::Ptr>();
314  Q_ASSERT( newIncidence );
315  Q_ASSERT( !newIncidence->uid().isEmpty() );
316  IncidenceBase::Ptr existingIncidence = q->incidence( newIncidence->uid() );
317  Q_ASSERT( existingIncidence );
318 
319  // The item needs updating too, revision changed.
320  mItemById.insert( item.id(), item );
321 
322  *(existingIncidence.data()) = *( newIncidence.data() );
323  }
324  ++row;
325  i = i.sibling( row, topLeft.column() );
326  }
327  emit q->calendarChanged();
328 }
329 
330 void ETMCalendarPrivate::onRowsInsertedInFilteredModel( const QModelIndex &index,
331  int start, int end )
332 {
333  itemsAdded( itemsFromModel( mFilteredETM, index, start, end ) );
334 }
335 
336 void ETMCalendarPrivate::onRowsAboutToBeRemovedInFilteredModel( const QModelIndex &index,
337  int start, int end )
338 {
339  itemsRemoved( itemsFromModel( mFilteredETM, index, start, end ) );
340 }
341 
342 ETMCalendar::ETMCalendar() : CalendarBase( new ETMCalendarPrivate( this ) )
343 {
344  Q_D( ETMCalendar );
345  d->init();
346 }
347 
348 ETMCalendar::~ETMCalendar()
349 {
350 }
351 
352 //TODO: move this up?
353 Akonadi::Collection ETMCalendar::collection( Akonadi::Collection::Id id ) const
354 {
355  Q_D( const ETMCalendar );
356  return d->mCollectionMap.value( id );
357 }
358 
359 bool ETMCalendar::hasRight( const QString &uid, Akonadi::Collection::Right right ) const
360 {
361  return hasRight( item( uid ), right );
362 }
363 
364 bool ETMCalendar::hasRight( const Akonadi::Item &item, Akonadi::Collection::Right right ) const
365 {
366  // if the users changes the rights, item.parentCollection()
367  // can still have the old rights, so we use call collection()
368  // which returns the updated one
369  const Akonadi::Collection col = collection( item.storageCollectionId() );
370  return col.rights() & right;
371 }
372 
373 QAbstractItemModel *ETMCalendar::filteredModel() const
374 {
375  Q_D( const ETMCalendar );
376  return d->mFilteredETM;
377 }
378 
379 QAbstractItemModel *ETMCalendar::unfilteredModel() const
380 {
381  Q_D( const ETMCalendar );
382  return d->mETM;
383 }
384 
385 KCheckableProxyModel *ETMCalendar::checkableProxyModel() const
386 {
387  Q_D( const ETMCalendar );
388  return d->mCheckableProxyModel;
389 }
390 
391 KCalCore::Alarm::List ETMCalendar::alarms( const KDateTime &from,
392  const KDateTime &to,
393  bool excludeBlockedAlarms ) const
394 {
395  Q_D( const ETMCalendar );
396  KCalCore::Alarm::List alarmList;
397  QHashIterator<Akonadi::Item::Id, Akonadi::Item> i( d->mItemById );
398  while ( i.hasNext() ) {
399  const Akonadi::Item item = i.next().value();
400 
401  if ( excludeBlockedAlarms ) {
402  // take the collection from m_collectionMap, because we need the up-to-date collection attrs
403  Akonadi::Collection parentCollection = d->mCollectionMap.value( item.storageCollectionId() );
404  if ( parentCollection.isValid() && parentCollection.hasAttribute<BlockAlarmsAttribute>() ) {
405  continue;
406  }
407  }
408 
409  KCalCore::Incidence::Ptr incidence;
410  if ( item.isValid() && item.hasPayload<KCalCore::Incidence::Ptr>() ) {
411  incidence = item.payload<KCalCore::Incidence::Ptr>();
412  } else {
413  continue;
414  }
415 
416  if ( !incidence ) {
417  continue;
418  }
419 
420  if ( incidence->recurs() ) {
421  appendRecurringAlarms( alarmList, incidence, from, to );
422  } else {
423  appendAlarms( alarmList, incidence, from, to );
424  }
425  }
426  return alarmList;
427 }
428 
429 #include "etmcalendar.moc"
430 #include "etmcalendar_p.moc"
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