~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to plasma/generic/dataengines/calendar/akonadi/calendar.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  Copyright (c) 2009 KDAB
 
3
  Author: Sebastian Sauer <sebsauer@kdab.net>
 
4
          Frank Osterfeld <frank@kdab.net>
 
5
 
 
6
  This program is free software; you can redistribute it and/or modify
 
7
  it under the terms of the GNU General Public License as published by
 
8
  the Free Software Foundation; either version 2 of the License, or
 
9
  (at your option) any later version.
 
10
 
 
11
  This program is distributed in the hope that it will be useful,
 
12
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
14
  GNU General Public License for more details.
 
15
 
 
16
  You should have received a copy of the GNU General Public License along
 
17
  with this program; if not, write to the Free Software Foundation, Inc.,
 
18
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
19
*/
 
20
 
 
21
#include "calendar.h"
 
22
#include "calendar_p.h"
 
23
 
 
24
#include "collectionselection.h"
 
25
#include "blockalarmsattribute.h"
 
26
#include "utils.h"
 
27
 
 
28
#include <KLocale>
 
29
#include <KSelectionProxyModel>
 
30
#include <Akonadi/EntityMimeTypeFilterModel>
 
31
#include <QtCore/QMultiHash>
 
32
#include <QItemSelection>
 
33
 
 
34
using namespace CalendarSupport;
 
35
 
 
36
Calendar::Private::Private( QAbstractItemModel *treeModel, QAbstractItemModel *model, Calendar *qq )
 
37
  : q( qq ),
 
38
    mTimeZones( new KCalCore::ICalTimeZones ),
 
39
    mNewObserver( false ),
 
40
    mObserversEnabled( true ),
 
41
    mDefaultFilter( new KCalCore::CalFilter ),
 
42
    m_treeModel( treeModel ),
 
43
    m_model( model )
 
44
{
 
45
  // Setup default filter, which does nothing
 
46
  mDefaultFilter->setEnabled( false );
 
47
  m_filterProxy = new CalFilterProxyModel( q );
 
48
  m_filterProxy->setFilter( mDefaultFilter );
 
49
  m_filterProxy->setSourceModel( model );
 
50
  m_filterProxy->setObjectName( "Implements KCalCore filtering functionality" );
 
51
 
 
52
  // user information...
 
53
  mOwner.setName( i18n( "Unknown Name" ) );
 
54
  mOwner.setEmail( i18n( "unknown@nowhere" ) );
 
55
 
 
56
  connect( m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
 
57
           this, SLOT(dataChanged(QModelIndex,QModelIndex)) );
 
58
 
 
59
  connect( m_model, SIGNAL(layoutChanged()),
 
60
           this, SLOT(layoutChanged()) );
 
61
 
 
62
  connect( m_model, SIGNAL(modelReset()),
 
63
           this, SLOT(modelReset()) );
 
64
 
 
65
  connect( m_model, SIGNAL(rowsInserted(QModelIndex,int,int)),
 
66
           this, SLOT(rowsInserted(QModelIndex,int,int)) );
 
67
 
 
68
  connect( m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
 
69
           this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) );
 
70
 
 
71
  // use the unfiltered model to catch collections
 
72
  connect( m_treeModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
 
73
           this, SLOT(rowsInsertedInTreeModel(QModelIndex,int,int)) );
 
74
 
 
75
  connect( m_treeModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
 
76
           this, SLOT(rowsAboutToBeRemovedInTreeModel(QModelIndex,int,int)) );
 
77
 
 
78
  connect( m_treeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
 
79
           this, SLOT(dataChangedInTreeModel(QModelIndex,QModelIndex)) );
 
80
 
 
81
  connect( m_treeModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
 
82
           SLOT(onRowsMovedInTreeModel(QModelIndex,int,int,QModelIndex,int)) );
 
83
 
 
84
  /*
 
85
  connect( m_monitor, SIGNAL(itemLinked(const Akonadi::Item,Akonadi::Collection)),
 
86
           this, SLOT(itemAdded(const Akonadi::Item,Akonadi::Collection)) );
 
87
  connect( m_monitor, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection )),
 
88
           this, SLOT(itemRemoved(Akonadi::Item,Akonadi::Collection )) );
 
89
  */
 
90
}
 
91
 
 
92
void Calendar::Private::rowsInsertedInTreeModel( const QModelIndex &parent, int start, int end )
 
93
{
 
94
  collectionsAdded( collectionsFromModel( m_treeModel, parent, start, end ) );
 
95
}
 
96
 
 
97
void Calendar::Private::rowsAboutToBeRemovedInTreeModel( const QModelIndex &parent,
 
98
                                                         int start, int end )
 
99
{
 
100
  collectionsRemoved( collectionsFromModel( m_treeModel, parent, start, end ) );
 
101
}
 
102
 
 
103
void Calendar::Private::dataChangedInTreeModel( const QModelIndex &topLeft,
 
104
                                                const QModelIndex &bottomRight )
 
105
{
 
106
  Q_ASSERT( topLeft.row() <= bottomRight.row() );
 
107
  const int endRow = bottomRight.row();
 
108
  QModelIndex i( topLeft );
 
109
  int row = i.row();
 
110
  while ( row <= endRow ) {
 
111
    const Akonadi::Collection col = collectionFromIndex( i );
 
112
    if ( col.isValid() ) {
 
113
      // Attributes might have changed, store the new collection and discard the old one
 
114
      m_collectionMap.insert( col.id(), col );
 
115
    }
 
116
    ++row;
 
117
    i = i.sibling( row, topLeft.column() );
 
118
  }
 
119
}
 
120
 
 
121
void Calendar::Private::rowsInserted( const QModelIndex &parent, int start, int end )
 
122
{
 
123
  itemsAdded( itemsFromModel( m_model, parent, start, end ) );
 
124
}
 
125
 
 
126
void Calendar::Private::rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end )
 
127
{
 
128
  itemsRemoved( itemsFromModel( m_model, parent, start, end ) );
 
129
}
 
130
 
 
131
void Calendar::Private::layoutChanged()
 
132
{
 
133
 
 
134
}
 
135
 
 
136
void Calendar::Private::onRowsMovedInTreeModel( const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
 
137
                                                const QModelIndex &destinationParent, int destinationRow )
 
138
{
 
139
  Q_ASSERT( sourceEnd >= sourceStart );
 
140
  Q_ASSERT( sourceStart >= 0 );
 
141
  Q_ASSERT( destinationRow >= 0 );
 
142
 
 
143
  const Akonadi::Collection sourceCollection = collectionFromIndex( sourceParent );
 
144
  const Akonadi::Collection destinationCollection = collectionFromIndex( destinationParent );
 
145
 
 
146
  if ( sourceCollection.isValid() && destinationCollection.isValid() &&
 
147
       sourceCollection.id() != destinationCollection.id() ) {
 
148
    const int numItems = sourceEnd - sourceStart + 1;
 
149
    Akonadi::Item::List movedItems = itemsFromModel( m_treeModel, destinationParent, destinationRow,
 
150
                                                     destinationRow + numItems - 1 );
 
151
 
 
152
    { // Start hack
 
153
      // KSelectionProxyModel doesn't honour rowsMoved() yet, so, if the source model emitted rowsMoved
 
154
      // (items changing collection) we could only catch it in the onLayoutChanged() slot, which isn't
 
155
      // performant. So we listen to the source model's rowsMoved() and check manuall if it when in or
 
156
      // out of the selection, and notify the application.
 
157
      Akonadi::EntityMimeTypeFilterModel *m = qobject_cast<Akonadi::EntityMimeTypeFilterModel*>( m_model );
 
158
      if ( m ) {
 
159
        KSelectionProxyModel *sm = qobject_cast<KSelectionProxyModel*>( m->sourceModel() );
 
160
        if ( sm ) {
 
161
          CollectionSelection collectionSelection( sm->selectionModel() );
 
162
          const bool sourceCollectionIsSelected = collectionSelection.contains( sourceCollection.id() );
 
163
          const bool destinationCollectionIsSelected = collectionSelection.contains( destinationCollection.id() );
 
164
          if ( sourceCollectionIsSelected && destinationCollectionIsSelected ) {
 
165
            foreach( const Akonadi::Item item, movedItems ) {
 
166
              if ( item.isValid() && item.hasPayload<KCalCore::Incidence::Ptr>() ) {
 
167
                // We have old items ( that think they belong to another collection ) inside m_itemMap
 
168
                if ( m_itemMap.contains( item.id() ) ) {
 
169
                  itemsRemoved( movedItems );
 
170
                  itemsAdded( movedItems );
 
171
                }
 
172
              }
 
173
            }
 
174
          } else if ( !sourceCollectionIsSelected && destinationCollectionIsSelected ) { // Added
 
175
            itemsAdded( movedItems );
 
176
          } else if ( sourceCollectionIsSelected && !destinationCollectionIsSelected ) { // Removed
 
177
            itemsRemoved( movedItems );
 
178
          }
 
179
        }
 
180
      }
 
181
    } // end hack
 
182
  }
 
183
}
 
184
 
 
185
void Calendar::Private::appendVirtualItems( Akonadi::Item::List &itemList )
 
186
{
 
187
  foreach( const Akonadi::Item &item, itemList ) {
 
188
    if ( m_virtualItems.contains( item.id() ) ) {
 
189
      itemList.append( m_virtualItems.value( item.id() ) );
 
190
    }
 
191
  }
 
192
}
 
193
 
 
194
void Calendar::Private::modelReset()
 
195
{
 
196
  clear();
 
197
  readFromModel();
 
198
}
 
199
 
 
200
void Calendar::Private::clear()
 
201
{
 
202
  itemsRemoved( m_itemMap.values() );
 
203
  Q_ASSERT( m_itemMap.isEmpty() );
 
204
  m_childToParent.clear();
 
205
  m_parentToChildren.clear();
 
206
  m_childToUnseenParent.clear();
 
207
  m_unseenParentToChildren.clear();
 
208
  m_itemIdsForDate.clear();
 
209
  m_itemDateForItemId.clear();
 
210
  m_virtualItems.clear();
 
211
}
 
212
 
 
213
void Calendar::Private::readFromModel()
 
214
{
 
215
  itemsAdded( itemsFromModel( m_model ) );
 
216
}
 
217
 
 
218
void Calendar::Private::dataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight )
 
219
{
 
220
  // kDebug();
 
221
  Q_ASSERT( topLeft.row() <= bottomRight.row() );
 
222
  const int endRow = bottomRight.row();
 
223
  QModelIndex i( topLeft );
 
224
  int row = i.row();
 
225
  while ( row <= endRow ) {
 
226
    const Akonadi::Item item = itemFromIndex( i );
 
227
    if ( item.isValid() ) {
 
228
      updateItem( item, AssertExists );
 
229
    }
 
230
    ++row;
 
231
    i = i.sibling( row, topLeft.column() );
 
232
  }
 
233
  emit q->calendarChanged();
 
234
}
 
235
 
 
236
Calendar::Private::~Private()
 
237
{
 
238
  Q_FOREACH ( const Akonadi::Item &item, m_itemMap ) {
 
239
    CalendarSupport::incidence( item )->unRegisterObserver( q );
 
240
  }
 
241
 
 
242
  delete mTimeZones;
 
243
  delete mDefaultFilter;
 
244
}
 
245
 
 
246
void Calendar::Private::assertInvariants() const
 
247
{
 
248
}
 
249
 
 
250
void Calendar::Private::updateItem( const Akonadi::Item &item, UpdateMode mode )
 
251
{
 
252
  assertInvariants();
 
253
  const bool alreadyExisted = m_itemMap.contains( item.id() );
 
254
  const Akonadi::Item::Id id = item.id();
 
255
 
 
256
  const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item );
 
257
 
 
258
  if ( !incidence ) {
 
259
    return;
 
260
  }
 
261
 
 
262
  // TODO: remove this debug message in a few months
 
263
  kDebug() << "id=" << item.id()
 
264
           << "version=" << item.revision()
 
265
           << "alreadyExisted=" << alreadyExisted
 
266
           << "; mode = " << mode
 
267
           << "; uid = " << incidence->uid()
 
268
           << "; storageCollection.id() = " << item.storageCollectionId() // the real collection
 
269
           << "; parentCollection.id() = " << item.parentCollection().id(); // can be a virtual collection
 
270
 
 
271
  if ( mode != AssertExists && alreadyExisted ) {
 
272
    // An item from a virtual folder was inserted and we already have an item with
 
273
    // this id, belonging to the real collection. So we just insert it in m_virtualItems
 
274
    // so we keep track of it. Most hashes are indexed by Item::Id, and korg does lookups by Id too,
 
275
    // so we can't just treat this item as an independent one.
 
276
    if ( m_itemMap[id].parentCollection().id() != item.parentCollection().id() ) {
 
277
      m_virtualItems[item.id()].append( item );
 
278
      q->notifyIncidenceAdded( item );
 
279
    } else {
 
280
      kError() << "Item " << item.id() << " is already known.";
 
281
    }
 
282
    return;
 
283
  }
 
284
 
 
285
  //Q_ASSERT( mode == DontCare || alreadyExisted == ( mode == AssertExists ) );
 
286
 
 
287
  if ( alreadyExisted ) {
 
288
    if ( !m_itemMap.contains( id ) ) {
 
289
      // Item was deleted almost at the same time the change was made
 
290
      // ignore this change
 
291
      return;
 
292
    }
 
293
 
 
294
    if ( item.storageCollectionId() == -1 ) {
 
295
      // A valid item can have an invalid storage id if it was deleted while
 
296
      // fetching the ancestor
 
297
      return;
 
298
    }
 
299
 
 
300
    if ( item.storageCollectionId() != m_itemMap.value( id ).storageCollectionId() ) {
 
301
      // An item moved happened, update our internal copy, the storateCollectionId has changed.
 
302
      Akonadi::Collection::Id oldCollectionId = m_itemMap.value( id ).storageCollectionId();
 
303
      m_itemMap.insert( id, item );
 
304
      if ( item.isValid() ) {
 
305
        UnseenItem oldUi;
 
306
        UnseenItem newUi;
 
307
        oldUi.collection = oldCollectionId;
 
308
        oldUi.uid = incidence->uid();
 
309
        if ( m_uidToItemId.contains( oldUi ) ) {
 
310
          newUi.collection = item.storageCollectionId();
 
311
          newUi.uid = oldUi.uid;
 
312
          m_uidToItemId.remove( oldUi );
 
313
          m_uidToItemId.insert( newUi, item.id() );
 
314
        } else {
 
315
          Q_ASSERT_X( false, "Calendar::Private::updateItem", "Item wasn't found in m_uidToItemId" );
 
316
          return;
 
317
        }
 
318
      }
 
319
    }
 
320
    // update-only goes here
 
321
  } else {
 
322
    // new-only goes here
 
323
    const Akonadi::Collection::Rights rights = item.parentCollection().rights();
 
324
    if ( !( rights & Akonadi::Collection::CanDeleteItem ) &&
 
325
         !( rights & Akonadi::Collection::CanChangeItem ) &&
 
326
         !incidence->isReadOnly() ) {
 
327
      incidence->setReadOnly( true );
 
328
    }
 
329
  }
 
330
 
 
331
  if ( alreadyExisted && m_itemDateForItemId.contains( item.id() ) ) {
 
332
    // for changed items, we must remove existing date entries (they might have changed)
 
333
    m_itemIdsForDate.remove( m_itemDateForItemId[item.id()], item.id() );
 
334
    m_itemDateForItemId.remove( item.id() );
 
335
  }
 
336
 
 
337
  QString date;
 
338
  if ( const KCalCore::Todo::Ptr t = CalendarSupport::todo( item ) ) {
 
339
    if ( t->hasDueDate() ) {
 
340
      date = t->dtDue().date().toString();
 
341
    }
 
342
  } else if ( const KCalCore::Event::Ptr e = CalendarSupport::event( item ) ) {
 
343
    if ( !e->recurs() && !e->isMultiDay() ) {
 
344
      date = e->dtStart().date().toString();
 
345
    }
 
346
  } else if ( const KCalCore::Journal::Ptr j = CalendarSupport::journal( item ) ) {
 
347
    date = j->dtStart().date().toString();
 
348
  } else {
 
349
    kError() << "Item id is " << item.id()
 
350
             << item.hasPayload<KCalCore::Incidence::Ptr>()
 
351
             << item.hasPayload<KCalCore::Event::Ptr>()
 
352
             << item.hasPayload<KCalCore::Todo::Ptr>()
 
353
             << item.hasPayload<KCalCore::Journal::Ptr>();
 
354
    KCalCore::Incidence::Ptr p = CalendarSupport::incidence( item );
 
355
    if ( p ) {
 
356
      kError() << "incidence uid is " << p->uid()
 
357
               << " and type is " << p->typeStr();
 
358
    }
 
359
 
 
360
    Q_ASSERT( false );
 
361
    return;
 
362
  }
 
363
 
 
364
  if ( !m_itemIdsForDate.contains( date, item.id() ) && !date.isEmpty() ) {
 
365
    m_itemIdsForDate.insert( date, item.id() );
 
366
    m_itemDateForItemId.insert( item.id(), date );
 
367
  }
 
368
 
 
369
  m_itemMap.insert( id, item );
 
370
 
 
371
  UnseenItem ui;
 
372
  ui.collection = item.storageCollectionId();
 
373
  ui.uid = incidence->uid();
 
374
 
 
375
  //REVIEW(AKONADI_PORT)
 
376
  //UIDs might be duplicated and thus not unique, so for now we assume that the relatedTo
 
377
  // UID refers to an item in the same collection.
 
378
  //this might break with virtual collections, so we might fall back to a global UID
 
379
  //to akonadi item mapping, and pick just any item (or the first found, or whatever
 
380
  //strategy makes sense) from the ones with the same UID
 
381
  const QString parentUID = incidence->relatedTo();
 
382
  const bool hasParent = !parentUID.isEmpty();
 
383
  UnseenItem parentItem;
 
384
  QMap<UnseenItem,Akonadi::Item::Id>::const_iterator parentIt = m_uidToItemId.constEnd();
 
385
  bool knowParent = false;
 
386
  bool parentNotChanged = false;
 
387
  if ( hasParent ) {
 
388
    parentItem.collection = item.storageCollectionId();
 
389
    parentItem.uid = parentUID;
 
390
    parentIt = m_uidToItemId.constFind( parentItem );
 
391
    knowParent = parentIt != m_uidToItemId.constEnd();
 
392
  }
 
393
 
 
394
  if ( alreadyExisted ) { // We're updating an existing item
 
395
    const bool existedInUidMap = m_uidToItemId.contains( ui );
 
396
    if ( m_uidToItemId.value( ui ) != item.id() ) {
 
397
      kError()<< "Ignoring item. item.id() = " << item.id() << "; cached id = " << m_uidToItemId.value( ui )
 
398
              << "; item uid = "  << ui.uid
 
399
              << "; calendar = " << q->objectName()
 
400
              << "; existed in cache = " << existedInUidMap
 
401
              << "; storageCollection.id() = " << item.storageCollectionId() // the real collection
 
402
              << "; parentCollection.id() = " << item.parentCollection().id() // can be a virtual collection
 
403
              << "; hasParent = " << hasParent
 
404
              << "; knowParent = " << knowParent;
 
405
      if ( existedInUidMap ) {
 
406
        Q_ASSERT_X( false, "updateItem", "uidToId map disagrees with item id" );
 
407
      } else {
 
408
        kDebug() << "m_uidToItemId has size " << m_uidToItemId.count();
 
409
        QMapIterator<UnseenItem, Akonadi::Item::Id> i( m_uidToItemId );
 
410
        while ( i.hasNext() ) {
 
411
          i.next();
 
412
          if ( i.key().uid == ui.uid || i.value() == item.id() ) {
 
413
            kDebug() << " key " << i.key().uid << i.key().collection << " has value " << i.value();
 
414
          }
 
415
        }
 
416
        kError() << "Possible cause is that the resource isn't explicitly setting an uid ( and a random one is generated )";
 
417
        Q_ASSERT_X( false, "updateItem", "Item not found inside m_uidToItemId" );
 
418
      }
 
419
      return;
 
420
    }
 
421
 
 
422
    QHash<Akonadi::Item::Id,Akonadi::Item::Id>::Iterator oldParentIt = m_childToParent.find( id );
 
423
    if ( oldParentIt != m_childToParent.end() ) {
 
424
      const KCalCore::Incidence::Ptr parentInc =
 
425
        CalendarSupport::incidence( m_itemMap.value( oldParentIt.value() ) );
 
426
      Q_ASSERT( parentInc );
 
427
      if ( parentInc->uid() != parentUID ) {
 
428
        //parent changed, remove old entries
 
429
        QList<Akonadi::Item::Id>& l = m_parentToChildren[oldParentIt.value()];
 
430
        l.removeAll( id );
 
431
        m_childToParent.remove( id );
 
432
      } else {
 
433
        parentNotChanged = true;
 
434
      }
 
435
    } else { //old parent not seen, maybe unseen?
 
436
      QHash<Akonadi::Item::Id,UnseenItem>::Iterator oldUnseenParentIt =
 
437
        m_childToUnseenParent.find( id );
 
438
      if ( oldUnseenParentIt != m_childToUnseenParent.end() ) {
 
439
        if ( oldUnseenParentIt.value().uid != parentUID ) {
 
440
          //parent changed, remove old entries
 
441
          QList<Akonadi::Item::Id>& l = m_unseenParentToChildren[oldUnseenParentIt.value()];
 
442
          l.removeAll( id );
 
443
          m_childToUnseenParent.remove( id );
 
444
        } else {
 
445
          parentNotChanged = true;
 
446
        }
 
447
      }
 
448
    }
 
449
  } else { // We're inserting a new item
 
450
    m_uidToItemId.insert( ui, item.id() );
 
451
 
 
452
    //check for already known children:
 
453
    const QList<Akonadi::Item::Id> orphanedChildren = m_unseenParentToChildren.value( ui );
 
454
    if ( !orphanedChildren.isEmpty() ) {
 
455
      m_parentToChildren.insert( id, orphanedChildren );
 
456
    }
 
457
 
 
458
    Q_FOREACH ( const Akonadi::Item::Id &cid, orphanedChildren ) {
 
459
      m_childToParent.insert( cid, id );
 
460
    }
 
461
 
 
462
    m_unseenParentToChildren.remove( ui );
 
463
    m_childToUnseenParent.remove( id );
 
464
  }
 
465
 
 
466
  if ( hasParent && !parentNotChanged ) {
 
467
    if ( knowParent ) {
 
468
      Q_ASSERT( !m_parentToChildren.value( parentIt.value() ).contains( id ) );
 
469
      const KCalCore::Incidence::Ptr parentInc =
 
470
        CalendarSupport::incidence( m_itemMap.value( parentIt.value() ) );
 
471
      Q_ASSERT( parentInc );
 
472
      m_parentToChildren[parentIt.value()].append( id );
 
473
      m_childToParent.insert( id, parentIt.value() );
 
474
    } else {
 
475
      m_childToUnseenParent.insert( id, parentItem );
 
476
      m_unseenParentToChildren[parentItem].append( id );
 
477
    }
 
478
  }
 
479
 
 
480
  if ( !alreadyExisted ) {
 
481
    incidence->registerObserver( q );
 
482
    q->notifyIncidenceAdded( item );
 
483
  } else {
 
484
    q->notifyIncidenceChanged( item );
 
485
  }
 
486
  assertInvariants();
 
487
}
 
488
 
 
489
void Calendar::Private::itemChanged( const Akonadi::Item &item )
 
490
{
 
491
  assertInvariants();
 
492
  Q_ASSERT( item.isValid() );
 
493
  const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item );
 
494
  if ( !incidence ) {
 
495
    kWarning() << "Really? No incidence for item.id() " << item.id();
 
496
    return;
 
497
  }
 
498
  updateItem( item, AssertExists );
 
499
  emit q->calendarChanged();
 
500
  assertInvariants();
 
501
}
 
502
 
 
503
void Calendar::Private::itemsAdded( const Akonadi::Item::List &items )
 
504
{
 
505
  assertInvariants();
 
506
  foreach ( const Akonadi::Item &item, items ) {
 
507
    Q_ASSERT( item.isValid() );
 
508
    if ( !hasIncidence( item ) ) {
 
509
      continue;
 
510
    }
 
511
    updateItem( item, AssertNew );
 
512
    const KCalCore::Incidence::Ptr incidence = item.payload<KCalCore::Incidence::Ptr>();
 
513
  }
 
514
  emit q->calendarChanged();
 
515
  assertInvariants();
 
516
}
 
517
 
 
518
void Calendar::Private::collectionsAdded( const Akonadi::Collection::List &collections )
 
519
{
 
520
  foreach ( const Akonadi::Collection &collection, collections ) {
 
521
    m_collectionMap[collection.id()] = collection;
 
522
  }
 
523
}
 
524
 
 
525
void Calendar::Private::collectionsRemoved( const Akonadi::Collection::List &collections )
 
526
{
 
527
  // kDebug() << "removing collections: " << collections.count();
 
528
  foreach ( const Akonadi::Collection &collection, collections ) {
 
529
    m_collectionMap.remove( collection.id() );
 
530
  }
 
531
}
 
532
 
 
533
void Calendar::Private::removeItemFromMaps( const Akonadi::Item &item )
 
534
{
 
535
  UnseenItem unseen_item;
 
536
  UnseenItem unseen_parent;
 
537
 
 
538
  unseen_item.collection = unseen_parent.collection = item.storageCollectionId();
 
539
 
 
540
  KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item );
 
541
  if ( incidence ) {
 
542
    unseen_item.uid  = incidence->uid();
 
543
    unseen_parent.uid = incidence->relatedTo();
 
544
  }
 
545
 
 
546
  if ( m_childToParent.contains( item.id() ) ) {
 
547
    Akonadi::Item::Id parentId = m_childToParent.take( item.id() );
 
548
    m_parentToChildren[parentId].removeAll( item.id() );
 
549
  }
 
550
 
 
551
  foreach ( const Akonadi::Item::Id &id, m_parentToChildren[item.id()] ) {
 
552
    m_childToUnseenParent[id] = unseen_item;
 
553
    m_unseenParentToChildren[unseen_item].push_back( id );
 
554
  }
 
555
 
 
556
  m_parentToChildren.remove( item.id() );
 
557
 
 
558
  m_childToUnseenParent.remove( item.id() );
 
559
 
 
560
  m_unseenParentToChildren[unseen_parent].removeAll( item.id() );
 
561
 
 
562
  m_uidToItemId.remove( unseen_item );
 
563
  m_itemDateForItemId.remove( item.id() );
 
564
 
 
565
  const QList<QString> entriesToDelete = m_itemIdsForDate.keys( item.id() );
 
566
  foreach( const QString &entryToDelete, entriesToDelete ) {
 
567
    m_itemIdsForDate.remove( entryToDelete );
 
568
  }
 
569
}
 
570
 
 
571
void Calendar::Private::itemsRemoved( const Akonadi::Item::List &items )
 
572
{
 
573
  assertInvariants();
 
574
  foreach ( const Akonadi::Item &item, items ) {
 
575
    Q_ASSERT( item.isValid() );
 
576
 
 
577
    if ( !m_virtualItems.value( item.id() ).isEmpty() ) {
 
578
      // We have more than one item with the same id, due to virtual folders, so we can't
 
579
      // cleanup any hashes, to-do hierarchies, id to uid maps, etc, yet. We can only do that
 
580
      // when the last item is removed. Just decrement and return.
 
581
      m_virtualItems[item.id()].removeLast();
 
582
      q->notifyIncidenceDeleted( item );
 
583
      emit q->calendarChanged();
 
584
      return;
 
585
    }
 
586
 
 
587
    Akonadi::Item oldItem( m_itemMap.take( item.id() ) );
 
588
 
 
589
    removeItemFromMaps( oldItem );
 
590
 
 
591
    Q_ASSERT( oldItem.hasPayload<KCalCore::Incidence::Ptr>() );
 
592
    const KCalCore::Incidence::Ptr incidence = oldItem.payload<KCalCore::Incidence::Ptr>();
 
593
    /*
 
594
    kDebug() << "Remove uid=" << incidence->uid()
 
595
             << "summary=" << incidence->summary()
 
596
             << "type=" << int( incidence->type() )
 
597
             << "; id= " << item.id() << "; revision=" << item.revision()
 
598
             << " calendar = "
 
599
             << q;
 
600
    */
 
601
 
 
602
    if ( const KCalCore::Event::Ptr e = incidence.dynamicCast<KCalCore::Event>() ) {
 
603
      if ( !e->recurs() ) {
 
604
        m_itemIdsForDate.remove( e->dtStart().date().toString(), item.id() );
 
605
      }
 
606
    } else if ( const KCalCore::Todo::Ptr t = incidence.dynamicCast<KCalCore::Todo>( ) ) {
 
607
      if ( t->hasDueDate() ) {
 
608
        m_itemIdsForDate.remove( t->dtDue().date().toString(), item.id() );
 
609
      }
 
610
    } else if ( const KCalCore::Journal::Ptr j = incidence.dynamicCast<KCalCore::Journal>() ) {
 
611
      m_itemIdsForDate.remove( j->dtStart().date().toString(), item.id() );
 
612
    } else {
 
613
      kError() << "Unsupported incidence type: " << incidence;
 
614
      Q_ASSERT( false );
 
615
      continue;
 
616
    }
 
617
 
 
618
    // oldItem will almost always be the same as item, but, when you move an item from one collection
 
619
    // and the destination collection isn't selected, itemsRemoved() is called, and they will differ
 
620
    // on the parentCollection id.
 
621
    q->notifyIncidenceDeleted( oldItem );
 
622
    incidence->unRegisterObserver( q );
 
623
  }
 
624
  emit q->calendarChanged();
 
625
  assertInvariants();
 
626
}
 
627
 
 
628
Calendar::Calendar( QAbstractItemModel *treeModel, QAbstractItemModel *model,
 
629
                    const KDateTime::Spec &timeSpec, QObject *parent )
 
630
  : QObject( parent ), d( new Private( treeModel, model, this ) )
 
631
{
 
632
  d->mTimeSpec = timeSpec;
 
633
  d->mViewTimeSpec = timeSpec;
 
634
  d->readFromModel();
 
635
}
 
636
 
 
637
Calendar::~Calendar()
 
638
{
 
639
  delete d;
 
640
}
 
641
 
 
642
QAbstractItemModel *Calendar::treeModel() const
 
643
{
 
644
  return d->m_treeModel;
 
645
}
 
646
 
 
647
QAbstractItemModel *Calendar::model() const
 
648
{
 
649
  return d->m_filterProxy;
 
650
}
 
651
 
 
652
QAbstractItemModel *Calendar::unfilteredModel() const
 
653
{
 
654
  return d->m_model;
 
655
}
 
656
 
 
657
void Calendar::setUnfilteredModel( QAbstractItemModel *model )
 
658
{
 
659
 
 
660
  if ( d->m_model == model ) {
 
661
    return;
 
662
  }
 
663
 
 
664
  if ( d->m_model ) {
 
665
    disconnect( d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
 
666
                d, SLOT(dataChanged(QModelIndex,QModelIndex)) );
 
667
 
 
668
    disconnect( d->m_model, SIGNAL(layoutChanged()),
 
669
                d, SLOT(layoutChanged()) );
 
670
 
 
671
    disconnect( d->m_model, SIGNAL(modelReset()),
 
672
                d, SLOT(modelReset()) );
 
673
 
 
674
    disconnect( d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)),
 
675
                d, SLOT(rowsInserted(QModelIndex,int,int)) );
 
676
 
 
677
    disconnect( d->m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
 
678
                d, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) );
 
679
  }
 
680
  d->m_model = model;
 
681
  d->m_filterProxy->setSourceModel( model );
 
682
  if ( model ) {
 
683
    connect( d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
 
684
             d, SLOT(dataChanged(QModelIndex,QModelIndex)) );
 
685
 
 
686
    connect( d->m_model, SIGNAL(layoutChanged()),
 
687
             d, SLOT(layoutChanged()) );
 
688
 
 
689
    connect( d->m_model, SIGNAL(modelReset()),
 
690
             d, SLOT(modelReset()) );
 
691
 
 
692
    connect( d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)),
 
693
             d, SLOT(rowsInserted(QModelIndex,int,int)) );
 
694
 
 
695
    connect( d->m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
 
696
             d, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) );
 
697
    d->modelReset();
 
698
  }
 
699
}
 
700
 
 
701
// This method will be called probably multiple times if a series of changes where done.
 
702
// One finished the endChange() method got called.
 
703
 
 
704
void Calendar::incidenceUpdate( const QString &uid, const KDateTime &recurrenceId )
 
705
{
 
706
  Q_UNUSED( uid );
 
707
  Q_UNUSED( recurrenceId );
 
708
}
 
709
 
 
710
void Calendar::incidenceUpdated( const QString &uid, const KDateTime &recurrenceId )
 
711
{
 
712
  Q_UNUSED( recurrenceId );
 
713
  KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( itemForIncidenceUid( uid ) );
 
714
 
 
715
  if ( !incidence ) {
 
716
    return;
 
717
  }
 
718
 
 
719
  incidence->setLastModified( KDateTime::currentUtcDateTime() );
 
720
  // we should probably update the revision number here,
 
721
  // or internally in the Event itself when certain things change.
 
722
  // need to verify with ical documentation.
 
723
 
 
724
  // The static_cast is ok as the CalendarLocal only observes Incidence objects
 
725
#ifdef AKONADI_PORT_DISABLED
 
726
  notifyIncidenceChanged( static_cast<KCalCore::Incidence::Ptr >( incidence ) );
 
727
#else
 
728
  kDebug() << "AKONADI PORT: Disabled code in  " << Q_FUNC_INFO;
 
729
#endif
 
730
}
 
731
 
 
732
Akonadi::Item Calendar::event( Akonadi::Item::Id id ) const
 
733
{
 
734
  const Akonadi::Item item = d->m_itemMap.value( id );
 
735
  if ( CalendarSupport::event( item ) ) {
 
736
    return item;
 
737
  } else {
 
738
    return Akonadi::Item();
 
739
  }
 
740
}
 
741
 
 
742
Akonadi::Item Calendar::todo( Akonadi::Item::Id id ) const
 
743
{
 
744
  const Akonadi::Item item = d->m_itemMap.value( id );
 
745
  if ( CalendarSupport::todo( item ) ) {
 
746
    return item;
 
747
  } else {
 
748
    return Akonadi::Item();
 
749
  }
 
750
}
 
751
 
 
752
Akonadi::Item::List Calendar::rawTodos( TodoSortField sortField,
 
753
                                        SortDirection sortDirection )
 
754
{
 
755
  Akonadi::Item::List todoList;
 
756
  QHashIterator<Akonadi::Item::Id, Akonadi::Item> i( d->m_itemMap );
 
757
  while ( i.hasNext() ) {
 
758
    i.next();
 
759
    if ( CalendarSupport::todo( i.value() ) ) {
 
760
      todoList.append( i.value() );
 
761
    }
 
762
  }
 
763
  d->appendVirtualItems( todoList );
 
764
  return sortTodos( todoList, sortField, sortDirection );
 
765
}
 
766
 
 
767
Akonadi::Item::List Calendar::rawTodosForDate( const QDate &date )
 
768
{
 
769
  Akonadi::Item::List todoList;
 
770
  QString dateStr = date.toString();
 
771
  QMultiHash<QString, Akonadi::Item::Id>::const_iterator it =
 
772
    d->m_itemIdsForDate.constFind( dateStr );
 
773
  while ( it != d->m_itemIdsForDate.constEnd() && it.key() == dateStr ) {
 
774
    if ( CalendarSupport::todo( d->m_itemMap[it.value()] ) ) {
 
775
      todoList.append( d->m_itemMap[it.value()] );
 
776
    }
 
777
    ++it;
 
778
  }
 
779
  d->appendVirtualItems( todoList );
 
780
  return todoList;
 
781
}
 
782
 
 
783
KCalCore::Alarm::List Calendar::alarmsTo( const KDateTime &to )
 
784
{
 
785
  return alarms( KDateTime( QDate( 1900, 1, 1 ) ), to );
 
786
}
 
787
 
 
788
KCalCore::Alarm::List Calendar::alarms( const KDateTime &from, const KDateTime &to, bool excludeBlockedAlarms )
 
789
{
 
790
  KCalCore::Alarm::List alarmList;
 
791
  QHashIterator<Akonadi::Item::Id, Akonadi::Item> i( d->m_itemMap );
 
792
  while ( i.hasNext() ) {
 
793
    const Akonadi::Item item = i.next().value();
 
794
 
 
795
    if ( excludeBlockedAlarms ) {
 
796
      // take the collection from m_collectionMap, because we need the up-to-date collection attributes
 
797
      const Akonadi::Collection parentCollection = d->m_collectionMap.value( item.storageCollectionId() );
 
798
      if ( parentCollection.isValid() ) {
 
799
        if ( parentCollection.hasAttribute<BlockAlarmsAttribute>() )
 
800
          continue; // do not include alarms from this collection
 
801
      }
 
802
    }
 
803
 
 
804
    KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item );
 
805
    if ( !incidence ) {
 
806
      continue;
 
807
    }
 
808
 
 
809
    if ( incidence->recurs() ) {
 
810
      appendRecurringAlarms( alarmList, item, from, to );
 
811
    } else {
 
812
      appendAlarms( alarmList, item, from, to );
 
813
    }
 
814
  }
 
815
  return alarmList;
 
816
}
 
817
 
 
818
Akonadi::Item::List Calendar::rawEventsForDate( const QDate &date,
 
819
                                                const KDateTime::Spec &timespec,
 
820
                                                EventSortField sortField,
 
821
                                                SortDirection sortDirection )
 
822
{
 
823
  Akonadi::Item::List eventList;
 
824
  // Find the hash for the specified date
 
825
  const QString dateStr = date.toString();
 
826
  // Iterate over all non-recurring, single-day events that start on this date
 
827
  QMultiHash<QString, Akonadi::Item::Id>::const_iterator it =
 
828
    d->m_itemIdsForDate.constFind( dateStr );
 
829
  KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
 
830
  KDateTime kdt( date, ts );
 
831
  while ( it != d->m_itemIdsForDate.constEnd() && it.key() == dateStr ) {
 
832
    if ( KCalCore::Event::Ptr ev = CalendarSupport::event( d->m_itemMap[it.value()] ) ) {
 
833
      KDateTime end( ev->dtEnd().toTimeSpec( ev->dtStart() ) );
 
834
      if ( ev->allDay() ) {
 
835
        end.setDateOnly( true );
 
836
      } else {
 
837
        end = end.addSecs( -1 );
 
838
      }
 
839
      if ( end >= kdt ) {
 
840
        eventList.append( d->m_itemMap[it.value()] );
 
841
      }
 
842
    }
 
843
    ++it;
 
844
  }
 
845
  // Iterate over all events. Look for recurring events that occur on this date
 
846
  QHashIterator<Akonadi::Item::Id, Akonadi::Item> i( d->m_itemMap );
 
847
  while ( i.hasNext() ) {
 
848
    i.next();
 
849
    if ( KCalCore::Event::Ptr ev = CalendarSupport::event( i.value() ) ) {
 
850
      if ( ev->recurs() ) {
 
851
        if ( ev->isMultiDay() ) {
 
852
          int extraDays = ev->dtStart().date().daysTo( ev->dtEnd().date() );
 
853
          for ( int j = 0; j <= extraDays; ++j ) {
 
854
            if ( ev->recursOn( date.addDays( -j ), ts ) ) {
 
855
              eventList.append( i.value() );
 
856
              break;
 
857
            }
 
858
          }
 
859
        } else {
 
860
          if ( ev->recursOn( date, ts ) ) {
 
861
            eventList.append( i.value() );
 
862
          }
 
863
        }
 
864
      } else {
 
865
        if ( ev->isMultiDay() ) {
 
866
          if ( ev->dtStart().date() <= date && ev->dtEnd().date() >= date ) {
 
867
            eventList.append( i.value() );
 
868
          }
 
869
        }
 
870
      }
 
871
    }
 
872
  }
 
873
 
 
874
  d->appendVirtualItems( eventList );
 
875
 
 
876
  return sortEvents( eventList, sortField, sortDirection );
 
877
}
 
878
 
 
879
Akonadi::Item::List Calendar::rawEvents( const QDate &start, const QDate &end,
 
880
                                         const KDateTime::Spec &timespec, bool inclusive )
 
881
{
 
882
  Akonadi::Item::List eventList;
 
883
  KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec();
 
884
  KDateTime st( start, ts );
 
885
  KDateTime nd( end, ts );
 
886
  KDateTime yesterStart = st.addDays( -1 );
 
887
  // Get non-recurring events
 
888
  QHashIterator<Akonadi::Item::Id, Akonadi::Item> i( d->m_itemMap );
 
889
  while ( i.hasNext() ) {
 
890
    i.next();
 
891
    if ( KCalCore::Event::Ptr event = CalendarSupport::event( i.value() ) ) {
 
892
      KDateTime rStart = event->dtStart();
 
893
      if ( nd < rStart ) continue;
 
894
      if ( inclusive && rStart < st ) {
 
895
        continue;
 
896
      }
 
897
      if ( !event->recurs() ) { // non-recurring events
 
898
        KDateTime rEnd = event->dtEnd();
 
899
        if ( rEnd < st ) {
 
900
          continue;
 
901
        }
 
902
        if ( inclusive && nd < rEnd ) {
 
903
          continue;
 
904
        }
 
905
      } else { // recurring events
 
906
        switch( event->recurrence()->duration() ) {
 
907
        case -1: // infinite
 
908
          if ( inclusive ) {
 
909
            continue;
 
910
          }
 
911
          break;
 
912
        case 0: // end date given
 
913
        default: // count given
 
914
          KDateTime rEnd( event->recurrence()->endDate(), ts );
 
915
          if ( !rEnd.isValid() ) {
 
916
            continue;
 
917
          }
 
918
          if ( rEnd < st ) {
 
919
            continue;
 
920
          }
 
921
          if ( inclusive && nd < rEnd ) {
 
922
            continue;
 
923
          }
 
924
          break;
 
925
        } // switch(duration)
 
926
      } //if (recurs)
 
927
      eventList.append( i.value() );
 
928
    }
 
929
  }
 
930
 
 
931
  d->appendVirtualItems( eventList );
 
932
 
 
933
  return eventList;
 
934
}
 
935
 
 
936
Akonadi::Item::List Calendar::rawEventsForDate( const KDateTime &kdt )
 
937
{
 
938
  return rawEventsForDate( kdt.date(), kdt.timeSpec() );
 
939
}
 
940
 
 
941
Akonadi::Item::List Calendar::rawEvents( EventSortField sortField,
 
942
                                         SortDirection sortDirection )
 
943
{
 
944
  Akonadi::Item::List eventList;
 
945
  QHashIterator<Akonadi::Item::Id, Akonadi::Item> i( d->m_itemMap );
 
946
  while ( i.hasNext() ) {
 
947
    i.next();
 
948
    if ( CalendarSupport::event( i.value() ) ) {
 
949
      eventList.append( i.value() );
 
950
    }
 
951
  }
 
952
  d->appendVirtualItems( eventList );
 
953
  return sortEvents( eventList, sortField, sortDirection );
 
954
}
 
955
 
 
956
Akonadi::Item Calendar::journal( Akonadi::Item::Id id ) const
 
957
{
 
958
  const Akonadi::Item item = d->m_itemMap.value( id );
 
959
  if ( CalendarSupport::journal( item ) ) {
 
960
    return item;
 
961
  } else {
 
962
    return Akonadi::Item();
 
963
  }
 
964
}
 
965
 
 
966
Akonadi::Item::List Calendar::rawJournals( JournalSortField sortField,
 
967
                                           SortDirection sortDirection )
 
968
{
 
969
  Akonadi::Item::List journalList;
 
970
  QHashIterator<Akonadi::Item::Id, Akonadi::Item> i( d->m_itemMap );
 
971
  while ( i.hasNext() ) {
 
972
    i.next();
 
973
    if ( CalendarSupport::journal( i.value() ) ) {
 
974
      journalList.append( i.value() );
 
975
    }
 
976
  }
 
977
  d->appendVirtualItems( journalList );
 
978
  return sortJournals( journalList, sortField, sortDirection );
 
979
}
 
980
 
 
981
Akonadi::Item::List Calendar::rawJournalsForDate( const QDate &date )
 
982
{
 
983
  Akonadi::Item::List journalList;
 
984
  QString dateStr = date.toString();
 
985
  QMultiHash<QString, Akonadi::Item::Id>::const_iterator it =
 
986
    d->m_itemIdsForDate.constFind( dateStr );
 
987
  while ( it != d->m_itemIdsForDate.constEnd() && it.key() == dateStr ) {
 
988
    if ( CalendarSupport::journal( d->m_itemMap[it.value()] ) ) {
 
989
      journalList.append( d->m_itemMap[it.value()] );
 
990
    }
 
991
    ++it;
 
992
  }
 
993
  d->appendVirtualItems( journalList );
 
994
  return journalList;
 
995
}
 
996
 
 
997
Akonadi::Item Calendar::findParent( const Akonadi::Item &child ) const
 
998
{
 
999
  return d->m_itemMap.value( d->m_childToParent.value( child.id() ) );
 
1000
}
 
1001
 
 
1002
Akonadi::Item::List Calendar::findChildren( const KCalCore::Incidence::Ptr &incidence ) const
 
1003
{
 
1004
  Akonadi::Item item = itemForIncidenceUid( incidence->uid() );
 
1005
 
 
1006
  return findChildren( item );
 
1007
}
 
1008
 
 
1009
Akonadi::Item::List Calendar::findChildren( const Akonadi::Item &parent ) const
 
1010
{
 
1011
  Akonadi::Item::List l;
 
1012
  Q_FOREACH( const Akonadi::Item::Id &id, d->m_parentToChildren.value( parent.id() ) ) {
 
1013
    l.push_back( d->m_itemMap.value( id ) );
 
1014
  }
 
1015
  return l;
 
1016
}
 
1017
 
 
1018
bool Calendar::isChild( const Akonadi::Item &parent, const Akonadi::Item &child ) const
 
1019
{
 
1020
  return d->m_childToParent.value( child.id() ) == parent.id();
 
1021
}
 
1022
 
 
1023
Akonadi::Item::Id Calendar::itemIdForIncidenceUid( const QString &uid ) const
 
1024
{
 
1025
  QHashIterator<Akonadi::Item::Id, Akonadi::Item> i( d->m_itemMap );
 
1026
  while ( i.hasNext() ) {
 
1027
    i.next();
 
1028
    const Akonadi::Item item = i.value();
 
1029
    Q_ASSERT( item.isValid() );
 
1030
    Q_ASSERT( item.hasPayload<KCalCore::Incidence::Ptr>() );
 
1031
    KCalCore::Incidence::Ptr inc = item.payload<KCalCore::Incidence::Ptr>();
 
1032
    if ( inc->uid() == uid ) {
 
1033
      return item.id();
 
1034
    }
 
1035
  }
 
1036
  kWarning() << "Failed to find Akonadi::Item for KCal uid " << uid;
 
1037
  return -1;
 
1038
}
 
1039
 
 
1040
Akonadi::Item Calendar::itemForIncidenceUid( const QString &uid ) const
 
1041
{
 
1042
  return incidence( itemIdForIncidenceUid( uid ) );
 
1043
}
 
1044
 
 
1045
// calendarbase.cpp
 
1046
 
 
1047
KCalCore::Person Calendar::owner() const
 
1048
{
 
1049
  return d->mOwner;
 
1050
}
 
1051
 
 
1052
void Calendar::setOwner( const KCalCore::Person &owner )
 
1053
{
 
1054
  d->mOwner = owner;
 
1055
}
 
1056
 
 
1057
void Calendar::setTimeSpec( const KDateTime::Spec &timeSpec )
 
1058
{
 
1059
  d->mTimeSpec = timeSpec;
 
1060
  d->mBuiltInTimeZone = KCalCore::ICalTimeZone();
 
1061
  setViewTimeSpec( timeSpec );
 
1062
 
 
1063
  doSetTimeSpec( d->mTimeSpec );
 
1064
}
 
1065
 
 
1066
KDateTime::Spec Calendar::timeSpec() const
 
1067
{
 
1068
  return d->mTimeSpec;
 
1069
}
 
1070
 
 
1071
void Calendar::setTimeZoneId( const QString &timeZoneId )
 
1072
{
 
1073
  d->mTimeSpec = d->timeZoneIdSpec( timeZoneId, false );
 
1074
  d->mViewTimeSpec = d->mTimeSpec;
 
1075
  d->mBuiltInViewTimeZone = d->mBuiltInTimeZone;
 
1076
 
 
1077
  doSetTimeSpec( d->mTimeSpec );
 
1078
}
 
1079
 
 
1080
//@cond PRIVATE
 
1081
KDateTime::Spec Calendar::Private::timeZoneIdSpec( const QString &timeZoneId,
 
1082
                                                   bool view )
 
1083
{
 
1084
  if ( view ) {
 
1085
    mBuiltInViewTimeZone = KCalCore::ICalTimeZone();
 
1086
  } else {
 
1087
    mBuiltInTimeZone = KCalCore::ICalTimeZone();
 
1088
  }
 
1089
  if ( timeZoneId == QLatin1String( "UTC" ) ) {
 
1090
    return KDateTime::UTC;
 
1091
  }
 
1092
  KCalCore::ICalTimeZone tz = mTimeZones->zone( timeZoneId );
 
1093
  if ( !tz.isValid() ) {
 
1094
    KCalCore::ICalTimeZoneSource tzsrc;
 
1095
#ifdef AKONADI_PORT_DISABLED
 
1096
    tz = tzsrc.parse( icaltimezone_get_builtin_timezone( timeZoneId.toLatin1() ) );
 
1097
#else
 
1098
    kDebug() << "AKONADI PORT: Disabled code in  " << Q_FUNC_INFO;
 
1099
#endif
 
1100
    if ( view ) {
 
1101
      mBuiltInViewTimeZone = tz;
 
1102
    } else {
 
1103
      mBuiltInTimeZone = tz;
 
1104
    }
 
1105
  }
 
1106
  if ( tz.isValid() ) {
 
1107
    return tz;
 
1108
  } else {
 
1109
    return KDateTime::ClockTime;
 
1110
  }
 
1111
}
 
1112
//@endcond
 
1113
 
 
1114
QString Calendar::timeZoneId() const
 
1115
{
 
1116
  KTimeZone tz = d->mTimeSpec.timeZone();
 
1117
  return tz.isValid() ? tz.name() : QString();
 
1118
}
 
1119
 
 
1120
void Calendar::setViewTimeSpec( const KDateTime::Spec &timeSpec ) const
 
1121
{
 
1122
  d->mViewTimeSpec = timeSpec;
 
1123
  d->mBuiltInViewTimeZone = KCalCore::ICalTimeZone();
 
1124
}
 
1125
 
 
1126
void Calendar::setViewTimeZoneId( const QString &timeZoneId ) const
 
1127
{
 
1128
  d->mViewTimeSpec = d->timeZoneIdSpec( timeZoneId, true );
 
1129
}
 
1130
 
 
1131
KDateTime::Spec Calendar::viewTimeSpec() const
 
1132
{
 
1133
  return d->mViewTimeSpec;
 
1134
}
 
1135
 
 
1136
QString Calendar::viewTimeZoneId() const
 
1137
{
 
1138
  KTimeZone tz = d->mViewTimeSpec.timeZone();
 
1139
  return tz.isValid() ? tz.name() : QString();
 
1140
}
 
1141
 
 
1142
void Calendar::shiftTimes( const KDateTime::Spec &oldSpec,
 
1143
                           const KDateTime::Spec &newSpec )
 
1144
{
 
1145
  setTimeSpec( newSpec );
 
1146
  int i, end;
 
1147
  Akonadi::Item::List ev = events();
 
1148
  for ( i = 0, end = ev.count();  i < end;  ++i ) {
 
1149
    CalendarSupport::event( ev[i] )->shiftTimes( oldSpec, newSpec );
 
1150
  }
 
1151
 
 
1152
  Akonadi::Item::List to = todos();
 
1153
  for ( i = 0, end = to.count();  i < end;  ++i ) {
 
1154
    CalendarSupport::todo( to[i] )->shiftTimes( oldSpec, newSpec );
 
1155
  }
 
1156
 
 
1157
  Akonadi::Item::List jo = journals();
 
1158
  for ( i = 0, end = jo.count();  i < end;  ++i ) {
 
1159
    CalendarSupport::journal( jo[i] )->shiftTimes( oldSpec, newSpec );
 
1160
  }
 
1161
}
 
1162
 
 
1163
void Calendar::setFilter( KCalCore::CalFilter *filter )
 
1164
{
 
1165
  d->m_filterProxy->setFilter( filter ? filter : d->mDefaultFilter );
 
1166
}
 
1167
 
 
1168
KCalCore::CalFilter *Calendar::filter()
 
1169
{
 
1170
  return d->m_filterProxy->filter();
 
1171
}
 
1172
 
 
1173
QStringList Calendar::categories( Calendar *cal )
 
1174
{
 
1175
  Akonadi::Item::List rawInc( cal->rawIncidences() );
 
1176
  QStringList cats, thisCats;
 
1177
  // @TODO: For now just iterate over all incidences. In the future,
 
1178
  // the list of categories should be built when reading the file.
 
1179
  Q_FOREACH( const Akonadi::Item &i, rawInc ) {
 
1180
    thisCats = CalendarSupport::incidence( i )->categories();
 
1181
    for ( QStringList::ConstIterator si = thisCats.constBegin();
 
1182
          si != thisCats.constEnd(); ++si ) {
 
1183
      if ( !cats.contains( *si ) ) {
 
1184
        cats.append( *si );
 
1185
      }
 
1186
    }
 
1187
  }
 
1188
  return cats;
 
1189
}
 
1190
 
 
1191
Akonadi::Item::List Calendar::incidences( const QDate &date )
 
1192
{
 
1193
  return mergeIncidenceList( events( date ), todos( date ), journals( date ) );
 
1194
}
 
1195
 
 
1196
Akonadi::Item::List Calendar::incidences()
 
1197
{
 
1198
  if ( d->m_filterProxy->filter() == 0 || !d->m_filterProxy->filter()->isEnabled() ) {
 
1199
    // Lets skip the filterProxy and return m_itemMap, which is cheaper.
 
1200
    return rawIncidences();
 
1201
  } else {
 
1202
    return itemsFromModel( d->m_filterProxy );
 
1203
  }
 
1204
}
 
1205
 
 
1206
Akonadi::Item::List Calendar::rawIncidences()
 
1207
{
 
1208
  // The following code is 100x faster than: return itemsFromModel( d->m_model )
 
1209
  QHashIterator<Akonadi::Item::Id, Akonadi::Item> i( d->m_itemMap );
 
1210
  Akonadi::Item::List list;
 
1211
  while ( i.hasNext() ) {
 
1212
    i.next();
 
1213
    list.append( i.value() );
 
1214
  }
 
1215
 
 
1216
  return list;
 
1217
}
 
1218
 
 
1219
Akonadi::Item::List Calendar::sortEvents( const Akonadi::Item::List &eventList_,
 
1220
                                          EventSortField sortField,
 
1221
                                          SortDirection sortDirection )
 
1222
{
 
1223
  Akonadi::Item::List eventList = eventList_;
 
1224
  Akonadi::Item::List eventListSorted;
 
1225
  Akonadi::Item::List tempList, t;
 
1226
  Akonadi::Item::List alphaList;
 
1227
  Akonadi::Item::List::Iterator sortIt;
 
1228
  Akonadi::Item::List::Iterator eit;
 
1229
 
 
1230
  // Notice we alphabetically presort Summaries first.
 
1231
  // We do this so comparison "ties" stay in a nice order.
 
1232
 
 
1233
  switch( sortField ) {
 
1234
  case EventSortUnsorted:
 
1235
    eventListSorted = eventList;
 
1236
    break;
 
1237
 
 
1238
  case EventSortStartDate:
 
1239
    alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
 
1240
    for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
 
1241
      KCalCore::Event::Ptr e = CalendarSupport::event( *eit );
 
1242
      Q_ASSERT( e );
 
1243
      if ( e->dtStart().isDateOnly() ) {
 
1244
        tempList.append( *eit );
 
1245
        continue;
 
1246
      }
 
1247
      sortIt = eventListSorted.begin();
 
1248
      if ( sortDirection == SortDirectionAscending ) {
 
1249
        while ( sortIt != eventListSorted.end() &&
 
1250
                e->dtStart() >= CalendarSupport::event(*sortIt)->dtStart() ) {
 
1251
          ++sortIt;
 
1252
        }
 
1253
      } else {
 
1254
        while ( sortIt != eventListSorted.end() &&
 
1255
                e->dtStart() < CalendarSupport::event(*sortIt)->dtStart() ) {
 
1256
          ++sortIt;
 
1257
        }
 
1258
      }
 
1259
      eventListSorted.insert( sortIt, *eit );
 
1260
    }
 
1261
    if ( sortDirection == SortDirectionAscending ) {
 
1262
      // Prepend the list of Events without End DateTimes
 
1263
      tempList += eventListSorted;
 
1264
      eventListSorted = tempList;
 
1265
    } else {
 
1266
      // Append the list of Events without End DateTimes
 
1267
      eventListSorted += tempList;
 
1268
    }
 
1269
    break;
 
1270
 
 
1271
  case EventSortEndDate:
 
1272
    alphaList = sortEvents( eventList, EventSortSummary, sortDirection );
 
1273
    for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) {
 
1274
      KCalCore::Event::Ptr e = CalendarSupport::event( *eit );
 
1275
      Q_ASSERT( e );
 
1276
      if ( e->hasEndDate() ) {
 
1277
        sortIt = eventListSorted.begin();
 
1278
        if ( sortDirection == SortDirectionAscending ) {
 
1279
          while ( sortIt != eventListSorted.end() &&
 
1280
                  e->dtEnd() >= CalendarSupport::event(*sortIt)->dtEnd() ) {
 
1281
            ++sortIt;
 
1282
          }
 
1283
        } else {
 
1284
          while ( sortIt != eventListSorted.end() &&
 
1285
                  e->dtEnd() < CalendarSupport::event(*sortIt)->dtEnd() ) {
 
1286
            ++sortIt;
 
1287
          }
 
1288
        }
 
1289
      } else {
 
1290
        // Keep a list of the Events without End DateTimes
 
1291
        tempList.append( *eit );
 
1292
      }
 
1293
      eventListSorted.insert( sortIt, *eit );
 
1294
    }
 
1295
    if ( sortDirection == SortDirectionAscending ) {
 
1296
      // Append the list of Events without End DateTimes
 
1297
      eventListSorted += tempList;
 
1298
    } else {
 
1299
      // Prepend the list of Events without End DateTimes
 
1300
      tempList += eventListSorted;
 
1301
      eventListSorted = tempList;
 
1302
    }
 
1303
    break;
 
1304
 
 
1305
  case EventSortSummary:
 
1306
    for ( eit = eventList.begin(); eit != eventList.end(); ++eit ) {
 
1307
      KCalCore::Event::Ptr e = CalendarSupport::event( *eit );
 
1308
      Q_ASSERT( e );
 
1309
      sortIt = eventListSorted.begin();
 
1310
      if ( sortDirection == SortDirectionAscending ) {
 
1311
        while ( sortIt != eventListSorted.end() &&
 
1312
                e->summary() >= CalendarSupport::event(*sortIt)->summary() ) {
 
1313
          ++sortIt;
 
1314
        }
 
1315
      } else {
 
1316
        while ( sortIt != eventListSorted.end() &&
 
1317
                e->summary() < CalendarSupport::event(*sortIt)->summary() ) {
 
1318
          ++sortIt;
 
1319
        }
 
1320
      }
 
1321
      eventListSorted.insert( sortIt, *eit );
 
1322
    }
 
1323
    break;
 
1324
  }
 
1325
 
 
1326
  return eventListSorted;
 
1327
}
 
1328
 
 
1329
Akonadi::Item::List Calendar::events( const QDate &date,
 
1330
                                      const KDateTime::Spec &timeSpec,
 
1331
                                      EventSortField sortField,
 
1332
                                      SortDirection sortDirection )
 
1333
{
 
1334
  const Akonadi::Item::List el = rawEventsForDate( date, timeSpec, sortField, sortDirection );
 
1335
  return applyCalFilter( el, filter() );
 
1336
}
 
1337
 
 
1338
Akonadi::Item::List Calendar::events( const KDateTime &dt )
 
1339
{
 
1340
  const Akonadi::Item::List el = rawEventsForDate( dt );
 
1341
  return applyCalFilter( el, filter() );
 
1342
}
 
1343
 
 
1344
Akonadi::Item::List Calendar::events( const QDate &start, const QDate &end,
 
1345
                                      const KDateTime::Spec &timeSpec,
 
1346
                                      bool inclusive )
 
1347
{
 
1348
  const Akonadi::Item::List el = rawEvents( start, end, timeSpec, inclusive );
 
1349
  return applyCalFilter( el, filter() );
 
1350
}
 
1351
 
 
1352
Akonadi::Item::List Calendar::events( EventSortField sortField,
 
1353
                                      SortDirection sortDirection )
 
1354
{
 
1355
  const Akonadi::Item::List el = rawEvents( sortField, sortDirection );
 
1356
  return applyCalFilter( el, filter() );
 
1357
}
 
1358
 
 
1359
KCalCore::Incidence::Ptr Calendar::dissociateOccurrence( const Akonadi::Item &item,
 
1360
                                                         const QDate &date,
 
1361
                                                         const KDateTime::Spec &spec,
 
1362
                                                         bool single )
 
1363
{
 
1364
  if ( !item.isValid() ) {
 
1365
    return KCalCore::Incidence::Ptr();
 
1366
  }
 
1367
 
 
1368
  const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item );
 
1369
  if ( !incidence || !incidence->recurs() ) {
 
1370
    return KCalCore::Incidence::Ptr();
 
1371
  }
 
1372
 
 
1373
  KCalCore::Incidence::Ptr newInc = KCalCore::Incidence::Ptr( incidence->clone() );
 
1374
  newInc->recreate();
 
1375
  // Do not call setRelatedTo() when dissociating recurring to-dos, otherwise the new to-do
 
1376
  // will appear as a child.  Originally, we planned to set a relation with reltype SIBLING
 
1377
  // when dissociating to-dos, but currently kcal only supports reltype PARENT.
 
1378
  // We can uncomment the following line when we support the PARENT reltype.
 
1379
  //newInc->setRelatedTo( incidence );
 
1380
  KCalCore::Recurrence *recur = newInc->recurrence();
 
1381
  if ( single ) {
 
1382
    recur->clear();
 
1383
  } else {
 
1384
    // Adjust the recurrence for the future incidences. In particular adjust
 
1385
    // the "end after n occurrences" rules! "No end date" and "end by ..."
 
1386
    // don't need to be modified.
 
1387
    int duration = recur->duration();
 
1388
    if ( duration > 0 ) {
 
1389
      int doneduration = recur->durationTo( date.addDays( -1 ) );
 
1390
      if ( doneduration >= duration ) {
 
1391
        kDebug() << "The dissociated event already occurred more often"
 
1392
                 << "than it was supposed to ever occur. ERROR!";
 
1393
        recur->clear();
 
1394
      } else {
 
1395
        recur->setDuration( duration - doneduration );
 
1396
      }
 
1397
    }
 
1398
  }
 
1399
  // Adjust the date of the incidence
 
1400
  if ( incidence->type() == KCalCore::IncidenceBase::TypeEvent ) {
 
1401
    KCalCore::Event::Ptr ev = newInc.staticCast<KCalCore::Event>();
 
1402
    KDateTime start( ev->dtStart() );
 
1403
    int daysTo = start.toTimeSpec( spec ).date().daysTo( date );
 
1404
    ev->setDtStart( start.addDays( daysTo ) );
 
1405
    ev->setDtEnd( ev->dtEnd().addDays( daysTo ) );
 
1406
  } else if ( incidence->type() == KCalCore::IncidenceBase::TypeTodo ) {
 
1407
    KCalCore::Todo::Ptr td = newInc.staticCast<KCalCore::Todo>();
 
1408
    bool haveOffset = false;
 
1409
    int daysTo = 0;
 
1410
    if ( td->hasDueDate() ) {
 
1411
      KDateTime due( td->dtDue() );
 
1412
      daysTo = due.toTimeSpec( spec ).date().daysTo( date );
 
1413
      td->setDtDue( due.addDays( daysTo ), true );
 
1414
      haveOffset = true;
 
1415
    }
 
1416
    if ( td->hasStartDate() ) {
 
1417
      KDateTime start( td->dtStart() );
 
1418
      if ( !haveOffset ) {
 
1419
        daysTo = start.toTimeSpec( spec ).date().daysTo( date );
 
1420
      }
 
1421
      td->setDtStart( start.addDays( daysTo ) );
 
1422
      haveOffset = true;
 
1423
    }
 
1424
  }
 
1425
  recur = incidence->recurrence();
 
1426
  if ( recur ) {
 
1427
    if ( single ) {
 
1428
      recur->addExDate( date );
 
1429
    } else {
 
1430
      // Make sure the recurrence of the past events ends
 
1431
      // at the corresponding day
 
1432
      recur->setEndDate( date.addDays(-1) );
 
1433
    }
 
1434
  }
 
1435
  return KCalCore::Incidence::Ptr( newInc );
 
1436
}
 
1437
 
 
1438
Akonadi::Item Calendar::incidence( Akonadi::Item::Id uid ) const
 
1439
{
 
1440
  Akonadi::Item i = event( uid );
 
1441
  if ( i.isValid() ) {
 
1442
    return i;
 
1443
  }
 
1444
 
 
1445
  i = todo( uid );
 
1446
  if ( i.isValid() ) {
 
1447
    return i;
 
1448
  }
 
1449
 
 
1450
  i = journal( uid );
 
1451
  return i;
 
1452
}
 
1453
 
 
1454
Akonadi::Item::List Calendar::incidencesFromSchedulingID( const QString &sid )
 
1455
{
 
1456
  Akonadi::Item::List result;
 
1457
  const Akonadi::Item::List incidences = rawIncidences();
 
1458
  Akonadi::Item::List::const_iterator it = incidences.begin();
 
1459
  for ( ; it != incidences.end(); ++it ) {
 
1460
    if ( CalendarSupport::incidence(*it)->schedulingID() == sid ) {
 
1461
      result.append( *it );
 
1462
    }
 
1463
  }
 
1464
  return result;
 
1465
}
 
1466
 
 
1467
Akonadi::Item Calendar::incidenceFromSchedulingID( const QString &UID )
 
1468
{
 
1469
  const Akonadi::Item::List incidences = rawIncidences();
 
1470
  Akonadi::Item::List::const_iterator it = incidences.begin();
 
1471
  for ( ; it != incidences.end(); ++it ) {
 
1472
    if ( CalendarSupport::incidence(*it)->schedulingID() == UID ) {
 
1473
      // Touchdown, and the crowd goes wild
 
1474
      return *it;
 
1475
    }
 
1476
  }
 
1477
  // Not found
 
1478
  return Akonadi::Item();
 
1479
}
 
1480
 
 
1481
Akonadi::Item::List Calendar::sortTodos( const Akonadi::Item::List &todoList_,
 
1482
                                         TodoSortField sortField,
 
1483
                                         SortDirection sortDirection )
 
1484
{
 
1485
  Akonadi::Item::List todoList( todoList_ );
 
1486
  Akonadi::Item::List todoListSorted;
 
1487
  Akonadi::Item::List tempList, t;
 
1488
  Akonadi::Item::List alphaList;
 
1489
  Akonadi::Item::List::Iterator sortIt;
 
1490
  Akonadi::Item::List::ConstIterator eit;
 
1491
 
 
1492
  // Notice we alphabetically presort Summaries first.
 
1493
  // We do this so comparison "ties" stay in a nice order.
 
1494
 
 
1495
  // Note that To-dos may not have Start DateTimes nor due DateTimes.
 
1496
 
 
1497
  switch( sortField ) {
 
1498
  case TodoSortUnsorted:
 
1499
    todoListSorted = todoList;
 
1500
    break;
 
1501
 
 
1502
  case TodoSortStartDate:
 
1503
    alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
 
1504
    for ( eit = alphaList.constBegin(); eit != alphaList.constEnd(); ++eit ) {
 
1505
      const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit );
 
1506
      if ( e->hasStartDate() ) {
 
1507
        sortIt = todoListSorted.begin();
 
1508
        if ( sortDirection == SortDirectionAscending ) {
 
1509
          while ( sortIt != todoListSorted.end() &&
 
1510
                  e->dtStart() >= CalendarSupport::todo(*sortIt)->dtStart() ) {
 
1511
            ++sortIt;
 
1512
          }
 
1513
        } else {
 
1514
          while ( sortIt != todoListSorted.end() &&
 
1515
                  e->dtStart() < CalendarSupport::todo(*sortIt)->dtStart() ) {
 
1516
            ++sortIt;
 
1517
          }
 
1518
        }
 
1519
        todoListSorted.insert( sortIt, *eit );
 
1520
      } else {
 
1521
        // Keep a list of the To-dos without Start DateTimes
 
1522
        tempList.append( *eit );
 
1523
      }
 
1524
    }
 
1525
    if ( sortDirection == SortDirectionAscending ) {
 
1526
      // Append the list of To-dos without Start DateTimes
 
1527
      todoListSorted += tempList;
 
1528
    } else {
 
1529
      // Prepend the list of To-dos without Start DateTimes
 
1530
      tempList += todoListSorted;
 
1531
      todoListSorted = tempList;
 
1532
    }
 
1533
    break;
 
1534
 
 
1535
  case TodoSortDueDate:
 
1536
    alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
 
1537
    for ( eit = alphaList.constBegin(); eit != alphaList.constEnd(); ++eit ) {
 
1538
      const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit );
 
1539
      if ( e->hasDueDate() ) {
 
1540
        sortIt = todoListSorted.begin();
 
1541
        if ( sortDirection == SortDirectionAscending ) {
 
1542
          while ( sortIt != todoListSorted.end() &&
 
1543
                  e->dtDue() >= CalendarSupport::todo( *sortIt )->dtDue() ) {
 
1544
            ++sortIt;
 
1545
          }
 
1546
        } else {
 
1547
          while ( sortIt != todoListSorted.end() &&
 
1548
                  e->dtDue() < CalendarSupport::todo( *sortIt )->dtDue() ) {
 
1549
            ++sortIt;
 
1550
          }
 
1551
        }
 
1552
        todoListSorted.insert( sortIt, *eit );
 
1553
      } else {
 
1554
        // Keep a list of the To-dos without Due DateTimes
 
1555
        tempList.append( *eit );
 
1556
      }
 
1557
    }
 
1558
    if ( sortDirection == SortDirectionAscending ) {
 
1559
      // Append the list of To-dos without Due DateTimes
 
1560
      todoListSorted += tempList;
 
1561
    } else {
 
1562
      // Prepend the list of To-dos without Due DateTimes
 
1563
      tempList += todoListSorted;
 
1564
      todoListSorted = tempList;
 
1565
    }
 
1566
    break;
 
1567
 
 
1568
  case TodoSortPriority:
 
1569
    alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
 
1570
    for ( eit = alphaList.constBegin(); eit != alphaList.constEnd(); ++eit ) {
 
1571
      const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit );
 
1572
      sortIt = todoListSorted.begin();
 
1573
      if ( sortDirection == SortDirectionAscending ) {
 
1574
        while ( sortIt != todoListSorted.end() &&
 
1575
                e->priority() >= CalendarSupport::todo(*sortIt)->priority() ) {
 
1576
          ++sortIt;
 
1577
        }
 
1578
      } else {
 
1579
        while ( sortIt != todoListSorted.end() &&
 
1580
                e->priority() < CalendarSupport::todo(*sortIt)->priority() ) {
 
1581
          ++sortIt;
 
1582
        }
 
1583
      }
 
1584
      todoListSorted.insert( sortIt, *eit );
 
1585
    }
 
1586
    break;
 
1587
 
 
1588
  case TodoSortPercentComplete:
 
1589
    alphaList = sortTodos( todoList, TodoSortSummary, sortDirection );
 
1590
    for ( eit = alphaList.constBegin(); eit != alphaList.constEnd(); ++eit ) {
 
1591
      const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit );
 
1592
      sortIt = todoListSorted.begin();
 
1593
      if ( sortDirection == SortDirectionAscending ) {
 
1594
        while ( sortIt != todoListSorted.end() &&
 
1595
                e->percentComplete() >= CalendarSupport::todo(*sortIt)->percentComplete() ) {
 
1596
          ++sortIt;
 
1597
        }
 
1598
      } else {
 
1599
        while ( sortIt != todoListSorted.end() &&
 
1600
                e->percentComplete() < CalendarSupport::todo(*sortIt)->percentComplete() ) {
 
1601
          ++sortIt;
 
1602
        }
 
1603
      }
 
1604
      todoListSorted.insert( sortIt, *eit );
 
1605
    }
 
1606
    break;
 
1607
 
 
1608
  case TodoSortSummary:
 
1609
    for ( eit = todoList.constBegin(); eit != todoList.constEnd(); ++eit ) {
 
1610
      const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit );
 
1611
      sortIt = todoListSorted.begin();
 
1612
      if ( sortDirection == SortDirectionAscending ) {
 
1613
        while ( sortIt != todoListSorted.end() &&
 
1614
                e->summary() >= CalendarSupport::todo(*sortIt)->summary() ) {
 
1615
          ++sortIt;
 
1616
        }
 
1617
      } else {
 
1618
        while ( sortIt != todoListSorted.end() &&
 
1619
                e->summary() < CalendarSupport::todo(*sortIt)->summary() ) {
 
1620
          ++sortIt;
 
1621
        }
 
1622
      }
 
1623
      todoListSorted.insert( sortIt, *eit );
 
1624
    }
 
1625
    break;
 
1626
  }
 
1627
 
 
1628
  return todoListSorted;
 
1629
}
 
1630
 
 
1631
Akonadi::Item::List Calendar::todos( TodoSortField sortField,
 
1632
                                     SortDirection sortDirection )
 
1633
{
 
1634
  const Akonadi::Item::List tl = rawTodos( sortField, sortDirection );
 
1635
  return CalendarSupport::applyCalFilter( tl, filter() );
 
1636
}
 
1637
 
 
1638
Akonadi::Item::List Calendar::todos( const QDate &date )
 
1639
{
 
1640
  Akonadi::Item::List el = rawTodosForDate( date );
 
1641
  return applyCalFilter( el, filter() );
 
1642
}
 
1643
 
 
1644
Akonadi::Item::List Calendar::sortJournals( const Akonadi::Item::List &journalList_,
 
1645
                                            JournalSortField sortField,
 
1646
                                            SortDirection sortDirection )
 
1647
{
 
1648
  Akonadi::Item::List journalList( journalList_ );
 
1649
  Akonadi::Item::List journalListSorted;
 
1650
  Akonadi::Item::List::Iterator sortIt;
 
1651
  Akonadi::Item::List::ConstIterator eit;
 
1652
 
 
1653
  switch( sortField ) {
 
1654
  case JournalSortUnsorted:
 
1655
    journalListSorted = journalList;
 
1656
    break;
 
1657
 
 
1658
  case JournalSortDate:
 
1659
    for ( eit = journalList.constBegin(); eit != journalList.constEnd(); ++eit ) {
 
1660
      const KCalCore::Journal::Ptr e = CalendarSupport::journal( *eit );
 
1661
      sortIt = journalListSorted.begin();
 
1662
      if ( sortDirection == SortDirectionAscending ) {
 
1663
        while ( sortIt != journalListSorted.end() &&
 
1664
                e->dtStart() >= CalendarSupport::journal(*sortIt)->dtStart() ) {
 
1665
          ++sortIt;
 
1666
        }
 
1667
      } else {
 
1668
        while ( sortIt != journalListSorted.end() &&
 
1669
                e->dtStart() < CalendarSupport::journal(*sortIt)->dtStart() ) {
 
1670
          ++sortIt;
 
1671
        }
 
1672
      }
 
1673
      journalListSorted.insert( sortIt, *eit );
 
1674
    }
 
1675
    break;
 
1676
 
 
1677
  case JournalSortSummary:
 
1678
    for ( eit = journalList.constBegin(); eit != journalList.constEnd(); ++eit ) {
 
1679
      const KCalCore::Journal::Ptr e = CalendarSupport::journal( *eit );
 
1680
      sortIt = journalListSorted.begin();
 
1681
      if ( sortDirection == SortDirectionAscending ) {
 
1682
        while ( sortIt != journalListSorted.end() &&
 
1683
                e->summary() >= CalendarSupport::journal(*sortIt)->summary() ) {
 
1684
          ++sortIt;
 
1685
        }
 
1686
      } else {
 
1687
        while ( sortIt != journalListSorted.end() &&
 
1688
                e->summary() < CalendarSupport::journal(*sortIt)->summary() ) {
 
1689
          ++sortIt;
 
1690
        }
 
1691
      }
 
1692
      journalListSorted.insert( sortIt, *eit );
 
1693
    }
 
1694
    break;
 
1695
  }
 
1696
 
 
1697
  return journalListSorted;
 
1698
}
 
1699
 
 
1700
Akonadi::Item::List Calendar::journals( JournalSortField sortField,
 
1701
                                        SortDirection sortDirection )
 
1702
{
 
1703
  const Akonadi::Item::List jl = rawJournals( sortField, sortDirection );
 
1704
  return CalendarSupport::applyCalFilter( jl, filter() );
 
1705
}
 
1706
 
 
1707
Akonadi::Item::List Calendar::journals( const QDate &date )
 
1708
{
 
1709
  Akonadi::Item::List el = rawJournalsForDate( date );
 
1710
  return CalendarSupport::applyCalFilter( el, filter() );
 
1711
}
 
1712
 
 
1713
void Calendar::beginBatchAdding()
 
1714
{
 
1715
  emit batchAddingBegins();
 
1716
}
 
1717
 
 
1718
void Calendar::endBatchAdding()
 
1719
{
 
1720
  emit batchAddingEnds();
 
1721
}
 
1722
 
 
1723
#ifdef AKONADI_PORT_DISABLED
 
1724
 
 
1725
void Calendar::setupRelations( const Akonadi::Item &forincidence )
 
1726
{
 
1727
  if ( !forincidence ) {
 
1728
    return;
 
1729
  }
 
1730
 
 
1731
  QString uid = forincidence->uid();
 
1732
 
 
1733
  // First, go over the list of orphans and see if this is their parent
 
1734
  QList<KCalCore::Incidence*> l = d->mOrphans.values( uid );
 
1735
  d->mOrphans.remove( uid );
 
1736
  for ( int i = 0, end = l.count();  i < end;  ++i ) {
 
1737
    forincidence->addRelation( l[i] );
 
1738
    d->mOrphanUids.remove( l[i]->uid() );
 
1739
  }
 
1740
 
 
1741
  // Now see about this incidences parent
 
1742
  if ( !forincidence->relatedTo() && !forincidence->relatedToUid().isEmpty() ) {
 
1743
    // Incidence has a uid it is related to but is not registered to it yet.
 
1744
    // Try to find it
 
1745
    KCalCore::Incidence::Ptr parent = incidence( forincidence->relatedToUid() );
 
1746
    if ( !parent ) {
 
1747
      // Not found, put this in the mOrphans list
 
1748
      // Note that the mOrphans dict might contain multiple entries with the
 
1749
      // same key! which are multiple children that wait for the parent
 
1750
      // incidence to be inserted.
 
1751
      d->mOrphans.insert( forincidence->relatedToUid(), forincidence );
 
1752
      d->mOrphanUids.insert( forincidence->uid(), forincidence );
 
1753
    }
 
1754
  }
 
1755
  }
 
1756
#endif // AKONADI_PORT_DISABLED
 
1757
 
 
1758
#ifdef AKONADI_PORT_DISABLED
 
1759
// If a to-do with sub-to-dos is deleted, move it's sub-to-dos to the orphan list
 
1760
void Calendar::removeRelations( const Akonadi::Item &incidence )
 
1761
{
 
1762
  if ( !incidence ) {
 
1763
    kDebug() << "Warning: incidence is 0";
 
1764
    return;
 
1765
  }
 
1766
 
 
1767
  QString uid = incidence->uid();
 
1768
  foreach ( KCalCore::Incidence::Ptr i, incidence->relations() ) {
 
1769
    if ( !d->mOrphanUids.contains( i->uid() ) ) {
 
1770
      d->mOrphans.insert( uid, i );
 
1771
      d->mOrphanUids.insert( i->uid(), i );
 
1772
      i->setRelatedTo( uid );
 
1773
    }
 
1774
  }
 
1775
 
 
1776
  // If this incidence is related to something else, tell that about it
 
1777
  if ( incidence->relatedTo() ) {
 
1778
    incidence->relatedTo()->removeRelation( incidence );
 
1779
  }
 
1780
 
 
1781
  // Remove this one from the orphans list
 
1782
  if ( d->mOrphanUids.remove( uid ) ) {
 
1783
    // This incidence is located in the orphans list - it should be removed
 
1784
    // Since the mOrphans dict might contain the same key (with different
 
1785
    // child incidence pointers!) multiple times, take care that we remove
 
1786
    // the correct one. So we need to remove all items with the given
 
1787
    // parent UID, and readd those that are not for this item. Also, there
 
1788
    // might be other entries with differnet UID that point to this
 
1789
    // incidence (this might happen when the relatedTo of the item is
 
1790
    // changed before its parent is inserted. This might happen with
 
1791
    // groupware servers....). Remove them, too
 
1792
    QStringList relatedToUids;
 
1793
 
 
1794
    // First, create a list of all keys in the mOrphans list which point
 
1795
    // to the removed item
 
1796
    relatedToUids << incidence->relatedToUid();
 
1797
    for ( QMultiHash<QString, Incidence*>::Iterator it = d->mOrphans.begin();
 
1798
          it != d->mOrphans.end(); ++it ) {
 
1799
      if ( it.value()->uid() == uid ) {
 
1800
        relatedToUids << it.key();
 
1801
      }
 
1802
    }
 
1803
 
 
1804
    // now go through all uids that have one entry that point to the incidence
 
1805
    for ( QStringList::const_iterator uidit = relatedToUids.constBegin();
 
1806
          uidit != relatedToUids.constEnd(); ++uidit ) {
 
1807
      Incidence::List tempList;
 
1808
      // Remove all to get access to the remaining entries
 
1809
      QList<KCalCore::Incidence*> l = d->mOrphans.values( *uidit );
 
1810
      d->mOrphans.remove( *uidit );
 
1811
      foreach ( Incidence *i, l ) {
 
1812
        if ( i != incidence ) {
 
1813
          tempList.append( i );
 
1814
        }
 
1815
      }
 
1816
      // Readd those that point to a different orphan incidence
 
1817
      for ( KCalCore::Incidence::List::Iterator incit = tempList.begin();
 
1818
            incit != tempList.end(); ++incit ) {
 
1819
        d->mOrphans.insert( *uidit, *incit );
 
1820
      }
 
1821
    }
 
1822
  }
 
1823
 
 
1824
  // Make sure the deleted incidence doesn't relate to a non-deleted incidence,
 
1825
  // since that would cause trouble in CalendarLocal::close(), as the deleted
 
1826
  // incidences are destroyed after the non-deleted incidences. The destructor
 
1827
  // of the deleted incidences would then try to access the already destroyed
 
1828
  // non-deleted incidence, which would segfault.
 
1829
  //
 
1830
  // So in short: Make sure dead incidences don't point to alive incidences
 
1831
  // via the relation.
 
1832
  //
 
1833
  // This crash is tested in CalendarLocalTest::testRelationsCrash().
 
1834
}
 
1835
#endif // AKONADI_PORT_DISABLED
 
1836
 
 
1837
void Calendar::CalendarObserver::calendarIncidenceAdded( const Akonadi::Item &incidence )
 
1838
{
 
1839
  Q_UNUSED( incidence );
 
1840
}
 
1841
 
 
1842
void Calendar::CalendarObserver::calendarIncidenceChanged( const Akonadi::Item &incidence )
 
1843
{
 
1844
  Q_UNUSED( incidence );
 
1845
}
 
1846
 
 
1847
void Calendar::CalendarObserver::calendarIncidenceDeleted( const Akonadi::Item &incidence )
 
1848
{
 
1849
  Q_UNUSED( incidence );
 
1850
}
 
1851
 
 
1852
void Calendar::registerObserver( CalendarObserver *observer )
 
1853
{
 
1854
  if ( !d->mObservers.contains( observer ) ) {
 
1855
    d->mObservers.append( observer );
 
1856
  }
 
1857
  d->mNewObserver = true;
 
1858
}
 
1859
 
 
1860
void Calendar::unregisterObserver( CalendarObserver *observer )
 
1861
{
 
1862
  d->mObservers.removeAll( observer );
 
1863
}
 
1864
 
 
1865
void Calendar::doSetTimeSpec( const KDateTime::Spec &timeSpec )
 
1866
{
 
1867
  Q_UNUSED( timeSpec );
 
1868
}
 
1869
 
 
1870
void Calendar::notifyIncidenceAdded( const Akonadi::Item &i )
 
1871
{
 
1872
  if ( !d->mObserversEnabled ) {
 
1873
    return;
 
1874
  }
 
1875
 
 
1876
  foreach ( CalendarObserver *observer, d->mObservers ) {
 
1877
    observer->calendarIncidenceAdded( i );
 
1878
  }
 
1879
}
 
1880
 
 
1881
void Calendar::notifyIncidenceChanged( const Akonadi::Item &i )
 
1882
{
 
1883
  if ( !d->mObserversEnabled ) {
 
1884
    return;
 
1885
  }
 
1886
 
 
1887
  foreach ( CalendarObserver *observer, d->mObservers ) {
 
1888
    observer->calendarIncidenceChanged( i );
 
1889
  }
 
1890
}
 
1891
 
 
1892
void Calendar::notifyIncidenceDeleted( const Akonadi::Item &i )
 
1893
{
 
1894
  if ( !d->mObserversEnabled ) {
 
1895
    return;
 
1896
  }
 
1897
 
 
1898
  foreach ( CalendarObserver *observer, d->mObservers ) {
 
1899
    observer->calendarIncidenceDeleted( i );
 
1900
  }
 
1901
}
 
1902
 
 
1903
void Calendar::customPropertyUpdated()
 
1904
{
 
1905
}
 
1906
 
 
1907
void Calendar::setProductId( const QString &id )
 
1908
{
 
1909
  d->mProductId = id;
 
1910
}
 
1911
 
 
1912
QString Calendar::productId() const
 
1913
{
 
1914
  return d->mProductId;
 
1915
}
 
1916
 
 
1917
Akonadi::Item::List Calendar::mergeIncidenceList( const Akonadi::Item::List &events,
 
1918
                                                  const Akonadi::Item::List &todos,
 
1919
                                                  const Akonadi::Item::List &journals )
 
1920
{
 
1921
  Akonadi::Item::List incidences;
 
1922
 
 
1923
  int i, end;
 
1924
  for ( i = 0, end = events.count();  i < end;  ++i ) {
 
1925
    incidences.append( events[i] );
 
1926
  }
 
1927
 
 
1928
  for ( i = 0, end = todos.count();  i < end;  ++i ) {
 
1929
    incidences.append( todos[i] );
 
1930
  }
 
1931
 
 
1932
  for ( i = 0, end = journals.count();  i < end;  ++i ) {
 
1933
    incidences.append( journals[i] );
 
1934
  }
 
1935
 
 
1936
  return incidences;
 
1937
}
 
1938
 
 
1939
void Calendar::setObserversEnabled( bool enabled )
 
1940
{
 
1941
  d->mObserversEnabled = enabled;
 
1942
}
 
1943
 
 
1944
void Calendar::appendAlarms( KCalCore::Alarm::List &alarms, const Akonadi::Item &item,
 
1945
                             const KDateTime &from, const KDateTime &to )
 
1946
{
 
1947
  const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item );
 
1948
  Q_ASSERT( incidence );
 
1949
 
 
1950
  KDateTime preTime = from.addSecs(-1);
 
1951
 
 
1952
  KCalCore::Alarm::List alarmlist = incidence->alarms();
 
1953
  for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
 
1954
    if ( alarmlist[i]->enabled() ) {
 
1955
      KDateTime dt = alarmlist[i]->nextRepetition( preTime );
 
1956
      if ( dt.isValid() && dt <= to ) {
 
1957
        kDebug() << incidence->summary() << "':" << dt.toString();
 
1958
        alarms.append( alarmlist[i] );
 
1959
      }
 
1960
    }
 
1961
  }
 
1962
}
 
1963
 
 
1964
void Calendar::appendRecurringAlarms( KCalCore::Alarm::List &alarms,
 
1965
                                      const Akonadi::Item &item,
 
1966
                                      const KDateTime &from,
 
1967
                                      const KDateTime &to )
 
1968
{
 
1969
  KDateTime dt;
 
1970
  bool endOffsetValid = false;
 
1971
  KCalCore::Duration endOffset( 0 );
 
1972
  KCalCore::Duration period( from, to );
 
1973
 
 
1974
  const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item );
 
1975
  Q_ASSERT( incidence );
 
1976
 
 
1977
  KCalCore::Alarm::List alarmlist = incidence->alarms();
 
1978
  for ( int i = 0, iend = alarmlist.count();  i < iend;  ++i ) {
 
1979
    KCalCore::Alarm::Ptr a = alarmlist[i];
 
1980
    if ( a->enabled() ) {
 
1981
      if ( a->hasTime() ) {
 
1982
        // The alarm time is defined as an absolute date/time
 
1983
        dt = a->nextRepetition( from.addSecs(-1) );
 
1984
        if ( !dt.isValid() || dt > to ) {
 
1985
          continue;
 
1986
        }
 
1987
      } else {
 
1988
        // Alarm time is defined by an offset from the event start or end time.
 
1989
        // Find the offset from the event start time, which is also used as the
 
1990
        // offset from the recurrence time.
 
1991
        KCalCore::Duration offset( 0 );
 
1992
        if ( a->hasStartOffset() ) {
 
1993
          offset = a->startOffset();
 
1994
        } else if ( a->hasEndOffset() ) {
 
1995
          offset = a->endOffset();
 
1996
          if ( !endOffsetValid ) {
 
1997
            endOffset = KCalCore::Duration(
 
1998
              incidence->dtStart(),
 
1999
              incidence->dateTime( KCalCore::IncidenceBase::RoleAlarmEndOffset ) );
 
2000
            endOffsetValid = true;
 
2001
          }
 
2002
        }
 
2003
 
 
2004
        // Find the incidence's earliest alarm
 
2005
        KDateTime alarmStart =
 
2006
          offset.end( a->hasEndOffset() ?
 
2007
                      incidence->dateTime( KCalCore::IncidenceBase::RoleAlarmEndOffset ) :
 
2008
                      incidence->dtStart() );
 
2009
//        KDateTime alarmStart = incidence->dtStart().addSecs( offset );
 
2010
        if ( alarmStart > to ) {
 
2011
          continue;
 
2012
        }
 
2013
        KDateTime baseStart = incidence->dtStart();
 
2014
        if ( from > alarmStart ) {
 
2015
          alarmStart = from;   // don't look earlier than the earliest alarm
 
2016
          baseStart = (-offset).end( (-endOffset).end( alarmStart ) );
 
2017
        }
 
2018
 
 
2019
        // Adjust the 'alarmStart' date/time and find the next recurrence at or after it.
 
2020
        // Treate the two offsets separately in case one is daily and the other not.
 
2021
        dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs(-1) );
 
2022
        if ( !dt.isValid() ||
 
2023
             ( dt = endOffset.end( offset.end( dt ) ) ) > to ) // adjust 'dt' to get the alarm time
 
2024
        {
 
2025
          // The next recurrence is too late.
 
2026
          if ( !a->repeatCount() ) {
 
2027
            continue;
 
2028
          }
 
2029
 
 
2030
          // The alarm has repetitions, so check whether repetitions of previous
 
2031
          // recurrences fall within the time period.
 
2032
          bool found = false;
 
2033
          KCalCore::Duration alarmDuration = a->duration();
 
2034
          for ( KDateTime base = baseStart;
 
2035
                ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid();
 
2036
                base = dt ) {
 
2037
            if ( a->duration().end( dt ) < base ) {
 
2038
              break;  // this recurrence's last repetition is too early, so give up
 
2039
            }
 
2040
 
 
2041
            // The last repetition of this recurrence is at or after 'alarmStart' time.
 
2042
            // Check if a repetition occurs between 'alarmStart' and 'to'.
 
2043
            int snooze = a->snoozeTime().value();   // in seconds or days
 
2044
            if ( a->snoozeTime().isDaily() ) {
 
2045
              KCalCore::Duration toFromDuration( dt, base );
 
2046
              int toFrom = toFromDuration.asDays();
 
2047
              if ( a->snoozeTime().end( from ) <= to ||
 
2048
                   ( toFromDuration.isDaily() && toFrom % snooze == 0 ) ||
 
2049
                   ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asDays() ) {
 
2050
                found = true;
 
2051
#ifndef NDEBUG
 
2052
                // for debug output
 
2053
                dt = offset.end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
 
2054
#endif
 
2055
                break;
 
2056
              }
 
2057
            } else {
 
2058
              int toFrom = dt.secsTo( base );
 
2059
              if ( period.asSeconds() >= snooze ||
 
2060
                   toFrom % snooze == 0 ||
 
2061
                   ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asSeconds() )
 
2062
              {
 
2063
                found = true;
 
2064
#ifndef NDEBUG
 
2065
                // for debug output
 
2066
                dt = offset.end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze );
 
2067
#endif
 
2068
                break;
 
2069
              }
 
2070
            }
 
2071
          }
 
2072
          if ( !found ) {
 
2073
            continue;
 
2074
          }
 
2075
        }
 
2076
      }
 
2077
      // kDebug() << incidence->summary() << "':" << dt.toString();
 
2078
      alarms.append( a );
 
2079
    }
 
2080
  }
 
2081
}
 
2082
 
 
2083
Akonadi::Collection Calendar::collection( const Akonadi::Collection::Id &id ) const
 
2084
{
 
2085
  if ( d->m_collectionMap.contains( id ) ) {
 
2086
    return d->m_collectionMap[id];
 
2087
  } else {
 
2088
    return Akonadi::Collection();
 
2089
  }
 
2090
}
 
2091
 
 
2092
bool Calendar::hasChangeRights( const Akonadi::Item &item ) const
 
2093
{
 
2094
  // if the users changes the rights, item.parentCollection()
 
2095
  // can still have the old rights, so we use call collection()
 
2096
  // which returns the updated one
 
2097
  const Akonadi::Collection col = collection( item.storageCollectionId() );
 
2098
  return col.rights() & Akonadi::Collection::CanChangeItem;
 
2099
}
 
2100
 
 
2101
bool Calendar::hasDeleteRights( const Akonadi::Item &item ) const
 
2102
{
 
2103
  // if the users changes the rights, item.parentCollection()
 
2104
  // can still have the old rights, so we use call collection()
 
2105
  // which returns the updated one
 
2106
  const Akonadi::Collection col = collection( item.storageCollectionId() );
 
2107
  return col.rights() & Akonadi::Collection::CanDeleteItem;
 
2108
}
 
2109
 
 
2110
int Calendar::incidencesCount() const
 
2111
{
 
2112
  return d->m_model->rowCount();
 
2113
}