~chris.gagnon/+junk/qtpim-coverage

« back to all changes in this revision

Viewing changes to src/organizer/qorganizermanagerengine.cpp

  • Committer: chris.gagnon
  • Date: 2013-12-10 23:09:37 UTC
  • Revision ID: chris.gagnon@canonical.com-20131210230937-2akf1ft1edcttk87
first post

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the QtOrganizer module of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** Commercial License Usage
 
10
** Licensees holding valid commercial Qt licenses may use this file in
 
11
** accordance with the commercial license agreement provided with the
 
12
** Software or, alternatively, in accordance with the terms contained in
 
13
** a written agreement between you and Digia.  For licensing terms and
 
14
** conditions see http://qt.digia.com/licensing.  For further information
 
15
** use the contact form at http://qt.digia.com/contact-us.
 
16
**
 
17
** GNU Lesser General Public License Usage
 
18
** Alternatively, this file may be used under the terms of the GNU Lesser
 
19
** General Public License version 2.1 as published by the Free Software
 
20
** Foundation and appearing in the file LICENSE.LGPL included in the
 
21
** packaging of this file.  Please review the following information to
 
22
** ensure the GNU Lesser General Public License version 2.1 requirements
 
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
24
**
 
25
** In addition, as a special exception, Digia gives you certain additional
 
26
** rights.  These rights are described in the Digia Qt LGPL Exception
 
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
28
**
 
29
** GNU General Public License Usage
 
30
** Alternatively, this file may be used under the terms of the GNU
 
31
** General Public License version 3.0 as published by the Free Software
 
32
** Foundation and appearing in the file LICENSE.GPL included in the
 
33
** packaging of this file.  Please review the following information to
 
34
** ensure the GNU General Public License version 3.0 requirements will be
 
35
** met: http://www.gnu.org/copyleft/gpl.html.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include <qorganizermanagerengine.h>
 
43
#include <qorganizeritems.h>
 
44
#include <qorganizeritemdetails.h>
 
45
#include <qorganizeritemfilters.h>
 
46
#include <qorganizeritemrequests.h>
 
47
#include <private/qorganizeritemrequests_p.h>
 
48
 
 
49
#include <QtCore/qmutex.h>
 
50
 
 
51
QT_BEGIN_NAMESPACE_ORGANIZER
 
52
 
 
53
/*!
 
54
    \class QOrganizerManagerEngine
 
55
    \brief The QOrganizerManagerEngine class provides the interface to implement functionalities
 
56
           of organizer managers.
 
57
    \inmodule QtOrganizer
 
58
    \ingroup organizer-backends
 
59
 
 
60
    This class should only be used by backend developers. Instances of this class are provided to
 
61
    QOrganizerManager by a QOrganizerManagerEngineFactory instance, which is loaded from a plugin.
 
62
 
 
63
    The default implementation of this interface provides a backend doing nothing, so that backend
 
64
    developers only need to reimplement the functionalities needed.
 
65
 
 
66
    More information on writing a organizer engine plugin is available in the \l{Qt Organizer Manager Engines}
 
67
    documentation.
 
68
 
 
69
    \sa QOrganizerManager, QOrganizerManagerEngineFactory
 
70
 */
 
71
 
 
72
/*!
 
73
    \fn QOrganizerManagerEngine::dataChanged()
 
74
 
 
75
    This signal should be emitted if the internal state of the plugin changes, and it is unable to
 
76
    determine the changes which occurred, or if it considers the changes to be radical enough to
 
77
    require clients to reload all data.
 
78
 
 
79
    If this signal is emitted, no other signals will be emitted for the associated changes.
 
80
 
 
81
    \sa itemsAdded(), itemsChanged(), itemsRemoved()
 
82
 */
 
83
 
 
84
/*!
 
85
    \fn QOrganizerManagerEngine::itemsAdded(const QList<QOrganizerItemId> &itemIds);
 
86
 
 
87
    This signal should be emitted at some point once the items identified by \a itemIds have been
 
88
    added to the backend.
 
89
 
 
90
    This signal should not be emitted if the dataChanged() signal was previously emitted for these
 
91
    changes.
 
92
 
 
93
    \sa dataChanged()
 
94
 */
 
95
 
 
96
/*!
 
97
    \fn QOrganizerManagerEngine::itemsChanged(const QList<QOrganizerItemId> &itemIds);
 
98
 
 
99
    This signal should be emitted at some point once the items identified by \a itemIds have been
 
100
    modified in the backend.
 
101
 
 
102
    This signal should not be emitted if the dataChanged() signal was previously emitted for these
 
103
    changes.
 
104
 
 
105
    \sa dataChanged()
 
106
 */
 
107
 
 
108
/*!
 
109
    \fn QOrganizerManagerEngine::itemsRemoved(const QList<QOrganizerItemId> &itemIds);
 
110
 
 
111
    This signal should be emitted at some point once the items identified by \a itemIds have been
 
112
    removed from the backend.
 
113
 
 
114
    This signal should not be emitted if the dataChanged() signal was previously emitted for these
 
115
    changes.
 
116
 
 
117
    \sa dataChanged()
 
118
 */
 
119
 
 
120
/*!
 
121
    \fn QOrganizerManagerEngine::itemsModified(const QList<QPair<QOrganizerItemId, QOrganizerManager::Operation> > &itemIds)
 
122
 
 
123
    This signal should be emitted at some point once the items identified by \a itemIds have been
 
124
    modified in the backend.
 
125
 
 
126
    This signal should not be emitted if the dataChanged() signal was previously emitted for these
 
127
    changes.
 
128
 
 
129
    \sa dataChanged()
 
130
 */
 
131
 
 
132
/*!
 
133
    \fn QOrganizerManagerEngine::collectionsAdded(const QList<QOrganizerCollectionId> &collectionIds)
 
134
 
 
135
    This signal should be emitted at some point once the collections identified by \a collectionIds
 
136
    have been added to the backend.
 
137
 
 
138
    This signal should not be emitted if the dataChanged() signal was previously emitted for these
 
139
    changes.
 
140
 
 
141
    \sa dataChanged()
 
142
 */
 
143
 
 
144
/*!
 
145
    \fn QOrganizerManagerEngine::collectionsChanged(const QList<QOrganizerCollectionId> &collectionIds)
 
146
 
 
147
    This signal should be emitted at some point once the collections identified by \a collectionIds
 
148
    have been changed in the backend.
 
149
 
 
150
    This signal should not be emitted if items in the collections have been added, modified, or
 
151
    removed.
 
152
 
 
153
    This signal should not be emitted if the dataChanged() signal was previously emitted for these
 
154
    changes.
 
155
 
 
156
    \sa dataChanged()
 
157
 */
 
158
 
 
159
/*!
 
160
    \fn QOrganizerManagerEngine::collectionsRemoved(const QList<QOrganizerCollectionId> &collectionIds)
 
161
 
 
162
    This signal should be emitted at some point once the collections identified by \a collectionIds
 
163
    have been removed from the backend.
 
164
 
 
165
    This signal should not be emitted if the dataChanged() signal was previously emitted for these
 
166
    changes.
 
167
 
 
168
    \sa dataChanged()
 
169
 */
 
170
 
 
171
/*!
 
172
    \fn QOrganizerManagerEngine::collectionsModified(const QList<QPair<QOrganizerCollectionId, QOrganizerManager::Operation> > &collectionIds)
 
173
 
 
174
    This signal should be emitted at some point once the collections identified by \a collectionIds
 
175
    have been modified in the backend.
 
176
 
 
177
    This signal should not be emitted if the dataChanged() signal was previously emitted for these
 
178
    changes.
 
179
 
 
180
    \sa dataChanged()
 
181
 */
 
182
 
 
183
/*!
 
184
    Constructs an empty QOrganizerManagerEngine with the given \a parent.
 
185
 */
 
186
QOrganizerManagerEngine::QOrganizerManagerEngine(QObject *parent)
 
187
    : QObject(parent)
 
188
{
 
189
}
 
190
 
 
191
/*!
 
192
    This function should be reimplemented to return the name of this backend. The default implementation
 
193
    returns the name "invalid".
 
194
*/
 
195
QString QOrganizerManagerEngine::managerName() const
 
196
{
 
197
    return QString(QStringLiteral("invalid"));
 
198
}
 
199
 
 
200
/*!
 
201
    This function should be reimplemented to return the parameters used in when constructing this
 
202
    backend. The default implementation returns an empty QMap.
 
203
 
 
204
    If certain paramters are invalid, or discarded by the backend, they should not be returned.
 
205
 */
 
206
QMap<QString, QString> QOrganizerManagerEngine::managerParameters() const
 
207
{
 
208
    return QMap<QString, QString>();
 
209
}
 
210
 
 
211
/*!
 
212
    Returns the unique URI of this manager, which is built from the manager name and the parameters
 
213
    used to construct it.
 
214
 */
 
215
QString QOrganizerManagerEngine::managerUri() const
 
216
{
 
217
    return QOrganizerManager::buildUri(managerName(), managerParameters());
 
218
}
 
219
 
 
220
/*!
 
221
    This function should be reimplemented to support synchronous calls to fetch occurrences of the
 
222
    given parent item.
 
223
 
 
224
    This function is supposed to return a list of a maximum of \a maxCount organizer item instances
 
225
    which are occurrences of the given \a parentItem recurring item, which occur between the given
 
226
    \a startDateTime and the given \a endDateTime date, inclusive. Any error which occurs should be
 
227
    saved in \a error.
 
228
 
 
229
    A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything
 
230
    which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime
 
231
    specifies an open end date time (matches anything which occurs after the \a startDateTime). If
 
232
    both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of
 
233
    all items.
 
234
 
 
235
    It's up to the backend to decide how many occurrences are returned if the given \a maxCount is
 
236
    negative.
 
237
 
 
238
    It's up to the backend to decide if fetch hint is supported. If supported, only the details
 
239
    defined by \a fetchHint will be fetched.
 
240
  */
 
241
QList<QOrganizerItem> QOrganizerManagerEngine::itemOccurrences(const QOrganizerItem &parentItem,
 
242
                                                               const QDateTime &startDateTime,
 
243
                                                               const QDateTime &endDateTime, int maxCount,
 
244
                                                               const QOrganizerItemFetchHint &fetchHint,
 
245
                                                               QOrganizerManager::Error *error)
 
246
{
 
247
    Q_UNUSED(parentItem);
 
248
    Q_UNUSED(startDateTime);
 
249
    Q_UNUSED(endDateTime);
 
250
    Q_UNUSED(maxCount);
 
251
    Q_UNUSED(fetchHint);
 
252
 
 
253
    *error = QOrganizerManager::NotSupportedError;
 
254
    return QList<QOrganizerItem>();
 
255
}
 
256
 
 
257
/*!
 
258
    This function should be reimplemented to support synchronous calls to fetch organizer item IDs.
 
259
 
 
260
    This function is supposed to return a list of item IDs of persisted organizer items that match
 
261
    the given \a filter, sorted according to the given list of \a sortOrders, for any item which
 
262
    occurs (or has an occurrence which occurs) in the range specified by the given \a startDateTime
 
263
    and \a endDateTime, inclusive. Any error which occurs should be saved in \a error.
 
264
 
 
265
    A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything
 
266
    which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime
 
267
    specifies an open end date time (matches anything which occurs after the \a startDateTime). If
 
268
    both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of
 
269
    all items which match the \a filter criteria.
 
270
 
 
271
    It's up to the backend to decide how filters are supported.
 
272
 */
 
273
QList<QOrganizerItemId> QOrganizerManagerEngine::itemIds(const QOrganizerItemFilter &filter,
 
274
                                                         const QDateTime &startDateTime,
 
275
                                                         const QDateTime &endDateTime,
 
276
                                                         const QList<QOrganizerItemSortOrder> &sortOrders,
 
277
                                                         QOrganizerManager::Error *error)
 
278
{
 
279
    Q_UNUSED(startDateTime)
 
280
    Q_UNUSED(endDateTime)
 
281
    Q_UNUSED(filter)
 
282
    Q_UNUSED(sortOrders)
 
283
 
 
284
    *error = QOrganizerManager::NotSupportedError;
 
285
    return QList<QOrganizerItemId>();
 
286
}
 
287
 
 
288
/*!
 
289
    This function should be reimplemented to support synchronous calls to fetch organizer items.
 
290
 
 
291
    This function is supposed to return a list of a maximum of \a maxCount organizer items and
 
292
    occurrences that match the given \a filter, which occur in the range specified by the given
 
293
    \a startDateTime and \a endDateTime, inclusive, and sorted according to the given list of
 
294
    \a sortOrders. Any operation error which occurs should be saved in \a error.
 
295
 
 
296
    A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything
 
297
    which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime
 
298
    specifies an open end date time (matches anything which occurs after the \a startDateTime). If
 
299
    both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of
 
300
    all items which match the \a filter criteria.
 
301
 
 
302
    If no sort order is provided, the list is returned sorted by date.
 
303
 
 
304
    It's up to the backend to decide how many items should be returned if \a maxCount is negative.
 
305
 
 
306
    It's up to the backend to decide if filter and fetch hint are supported.
 
307
 */
 
308
QList<QOrganizerItem> QOrganizerManagerEngine::items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime,
 
309
                                                     const QDateTime &endDateTime, int maxCount,
 
310
                                                     const QList<QOrganizerItemSortOrder> &sortOrders,
 
311
                                                     const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error)
 
312
{
 
313
    Q_UNUSED(filter)
 
314
    Q_UNUSED(startDateTime)
 
315
    Q_UNUSED(endDateTime)
 
316
    Q_UNUSED(maxCount)
 
317
    Q_UNUSED(sortOrders)
 
318
    Q_UNUSED(fetchHint)
 
319
 
 
320
    *error = QOrganizerManager::NotSupportedError;
 
321
    return QList<QOrganizerItem>();
 
322
}
 
323
 
 
324
/*!
 
325
    This function should be reimplemented to support synchronous calls to fetch organizer items for
 
326
    export.
 
327
 
 
328
    This function is supposed to return a list of organizer items that match the given \a filter,
 
329
    sorted according to the given list of \a sortOrders, for any item which occurs (or has an
 
330
    occurrence which occurs) in the range specified by the given \a startDateTime and \a endDateTime,
 
331
    inclusive. Any operation error which occurs should be saved in \a error.
 
332
 
 
333
    Note that event occurrences and TODO occurrences should only be returned when they represent an
 
334
    exceptional occurrence (i.e. the client has specifically saved the occurrence in the backend).
 
335
 
 
336
    A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything
 
337
    which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime
 
338
    specifies an open end date time (matches anything which occurs after the \a startDateTime). If
 
339
    both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of
 
340
    all items which match the \a filter criteria.
 
341
 
 
342
    It's up to the backend to decide if filter and fetch hint are supported. If the fetch hint is
 
343
    supported, only the details defined by \a fetchHint will be fetched.
 
344
 */
 
345
QList<QOrganizerItem> QOrganizerManagerEngine::itemsForExport(const QDateTime &startDateTime,
 
346
                                                              const QDateTime &endDateTime,
 
347
                                                              const QOrganizerItemFilter &filter,
 
348
                                                              const QList<QOrganizerItemSortOrder> &sortOrders,
 
349
                                                              const QOrganizerItemFetchHint &fetchHint,
 
350
                                                              QOrganizerManager::Error *error)
 
351
{
 
352
    Q_UNUSED(startDateTime)
 
353
    Q_UNUSED(endDateTime)
 
354
    Q_UNUSED(filter)
 
355
    Q_UNUSED(sortOrders)
 
356
    Q_UNUSED(fetchHint)
 
357
 
 
358
    *error = QOrganizerManager::NotSupportedError;
 
359
    return QList<QOrganizerItem>();
 
360
}
 
361
 
 
362
/*!
 
363
    This function should be reimplemented to support synchronous calls to fetch organizer items by
 
364
    their IDs \a itemIds.
 
365
 
 
366
    The items fetched by the backend should have a one-to-one correspondence to the IDs passed into
 
367
    this class.  That is, the nth item in the returned list should have an ID which is equal to the
 
368
    nth ID in the list of IDs.  Any invalid ID should correspond to an empty QOrganizerItem.
 
369
 
 
370
    It's up to the backend to decide if fetch hint is supported. If supported, only the details
 
371
    defined by \a fetchHint will be fetched.
 
372
 
 
373
    Any operation error which occurs should be saved in \a error. And the per-input errors should be
 
374
    stored in \a errorMap.
 
375
 
 
376
 */
 
377
QList<QOrganizerItem> QOrganizerManagerEngine::items(const QList<QOrganizerItemId> &itemIds, const QOrganizerItemFetchHint &fetchHint,
 
378
                                                     QMap<int, QOrganizerManager::Error> *errorMap, QOrganizerManager::Error *error)
 
379
{
 
380
    Q_UNUSED(itemIds)
 
381
    Q_UNUSED(fetchHint)
 
382
    Q_UNUSED(errorMap)
 
383
 
 
384
    *error = QOrganizerManager::NotSupportedError;
 
385
    return QList<QOrganizerItem>();
 
386
}
 
387
 
 
388
/*!
 
389
    This function should be reimplemented to return the list of filters supported by this backend.
 
390
    The default implementation returns an empty list.
 
391
 */
 
392
QList<QOrganizerItemFilter::FilterType> QOrganizerManagerEngine::supportedFilters() const
 
393
{
 
394
    return QList<QOrganizerItemFilter::FilterType>();
 
395
}
 
396
 
 
397
/*!
 
398
    This function should be reimplemented to return the list of details supported by this backend
 
399
    for the given \a itemType. The default implementation returns an empty list.
 
400
 */
 
401
QList<QOrganizerItemDetail::DetailType> QOrganizerManagerEngine::supportedItemDetails(QOrganizerItemType::ItemType itemType) const
 
402
{
 
403
    Q_UNUSED(itemType)
 
404
    return QList<QOrganizerItemDetail::DetailType>();
 
405
}
 
406
 
 
407
/*!
 
408
    This function should be reimplemented to return the list of item types supported by this backend.
 
409
    The default implementation returns an empty list.
 
410
 */
 
411
QList<QOrganizerItemType::ItemType> QOrganizerManagerEngine::supportedItemTypes() const
 
412
{
 
413
    return QList<QOrganizerItemType::ItemType>();
 
414
}
 
415
 
 
416
/*!
 
417
    This function should be reimplemented to support synchronous calls to save organizer items.
 
418
 
 
419
    This function is supposed to save the given list of \a items to the backend, and returns true on
 
420
    success or false otherwise.
 
421
 
 
422
    A new organizer item will be created in the backend store if the item ID of it is null. Otherwise,
 
423
    an existing item with the same ID will be updated. If the given item ID does not exist in the
 
424
    backend, it will result a QOrganizerManager::DoesNotExistError error.
 
425
 
 
426
    If the collection ID of the item is null, it will be saved to the default collection. If the given
 
427
    collection ID doesn't exist, the saving will fail and \a error will be set to QOrganizerManager::InvalidCollectionError.
 
428
 
 
429
    If the \a detailMask is empty, only the details currently existing in the item will be saved.
 
430
    Otherwise, only details masked by the \a detailMask will be saved or updated, others are kept
 
431
    untouched. It's useful to avoid information loss if the items are retrieved with a fetch hint.
 
432
 
 
433
    Note that upon successful saving, the backend may update the item, e.g. item ID for newly saved
 
434
    items, GUID, timestamp, version, etc.
 
435
 
 
436
    Any error which occurs should be saved in \a error, and per-input errors for the operation should
 
437
    be stored in \a errorMap.
 
438
 */
 
439
bool QOrganizerManagerEngine::saveItems(QList<QOrganizerItem> *items, const QList<QOrganizerItemDetail::DetailType> &detailMask,
 
440
                                        QMap<int, QOrganizerManager::Error> *errorMap, QOrganizerManager::Error *error)
 
441
{
 
442
    Q_UNUSED(items)
 
443
    Q_UNUSED(detailMask)
 
444
    Q_UNUSED(errorMap)
 
445
 
 
446
    *error = QOrganizerManager::NotSupportedError;
 
447
    return false;
 
448
}
 
449
 
 
450
/*!
 
451
    This function should be reimplemented to support synchronous calls to remove organizer items.
 
452
 
 
453
    This function is supposed to remove all the items whose ID is contained in the given list of
 
454
    \a itemIds, and all the occurrences whose parent ID is containd in the \a itemIds. If the list
 
455
    contains ids which do not identify a valid item in the manager \a error will be set to \c QOrganizerManager::DoesNotExist.
 
456
    Returns true if all the items and occurrences are successfully removed, or false otherwise.
 
457
 
 
458
    Any error which occurs should be saved in \a error, and per-input errors for the operation should
 
459
    be stored in \a errorMap.
 
460
 */
 
461
bool QOrganizerManagerEngine::removeItems(const QList<QOrganizerItemId> &itemIds, QMap<int, QOrganizerManager::Error> *errorMap,
 
462
                                          QOrganizerManager::Error *error)
 
463
{
 
464
    Q_UNUSED(itemIds)
 
465
    Q_UNUSED(errorMap)
 
466
 
 
467
    *error = QOrganizerManager::NotSupportedError;
 
468
    return false;
 
469
}
 
470
 
 
471
/*!
 
472
    This function should be reimplemented to support synchronous calls to remove organizer items.
 
473
 
 
474
    This function is supposed to remove all the items in the given list of \a items, and all the
 
475
    occurrences whose parent is containd in the \a items. If item in the list is a generated occurrence,
 
476
    an exception date is added to the parent item. If the list contains ids which do not identify a valid
 
477
    item in the manager \a error will be set to \c QOrganizerManager::DoesNotExist. Returns true if all
 
478
    the items and occurrences are successfully removed, or false otherwise.
 
479
 
 
480
    Any error which occurs should be saved in \a error, and per-input errors for the operation should
 
481
    be stored in \a errorMap.
 
482
 */
 
483
bool QOrganizerManagerEngine::removeItems(const QList<QOrganizerItem> *items, QMap<int, QOrganizerManager::Error> *errorMap,
 
484
                                          QOrganizerManager::Error *error)
 
485
{
 
486
    Q_UNUSED(items)
 
487
    Q_UNUSED(errorMap)
 
488
 
 
489
    *error = QOrganizerManager::NotSupportedError;
 
490
    return false;
 
491
}
 
492
 
 
493
 
 
494
/*!
 
495
    This function should be reimplemented to support synchronous calls to fetch the default collection.
 
496
    Any errors encountered during this operation should be stored to \a error.
 
497
*/
 
498
QOrganizerCollection QOrganizerManagerEngine::defaultCollection(QOrganizerManager::Error* error)
 
499
{
 
500
    *error = QOrganizerManager::NotSupportedError;
 
501
    return QOrganizerCollection();
 
502
}
 
503
 
 
504
/*!
 
505
    This function should be reimplemented to support synchronous calls to fetch a collection based
 
506
    on its ID. Any errors encountered during this operation should be stored to \a error. If the
 
507
    given \a collectionId does not specify a valid collection, \a error will be set to
 
508
    \c QOrganizerManager::DoesNotExistError.
 
509
 
 
510
*/
 
511
QOrganizerCollection QOrganizerManagerEngine::collection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error)
 
512
{
 
513
    Q_UNUSED(collectionId);
 
514
    *error = QOrganizerManager::NotSupportedError;
 
515
    return QOrganizerCollection();
 
516
}
 
517
 
 
518
/*!
 
519
    This function should be reimplemented to support synchronous calls to fetch all the collections
 
520
    managed by this backend. Any errors encountered during this operation should be stored to \a error.
 
521
 */
 
522
QList<QOrganizerCollection> QOrganizerManagerEngine::collections(QOrganizerManager::Error* error)
 
523
{
 
524
    *error = QOrganizerManager::NotSupportedError;
 
525
    return QList<QOrganizerCollection>();
 
526
}
 
527
 
 
528
/*!
 
529
    This function should be reimplemented to support synchronous calls to save a collection.
 
530
 
 
531
    This function is supposed to save the given \a collection to the backend, and returns true on
 
532
    success or false otherwise. Any errors encountered during this operation should be stored to
 
533
    \a error.
 
534
 
 
535
    A new collection will be created in the backend store if the collection ID of it is null.
 
536
    Otherwise, an existing collection with the same ID will be updated. If the given collection ID
 
537
    does not exist in the backend, it will result a QOrganizerManager::DoesNotExistError error.
 
538
 
 
539
    Note that upon successful saving, the backend may update the collection, e.g. collection ID for
 
540
    newly saved collections.
 
541
*/
 
542
bool QOrganizerManagerEngine::saveCollection(QOrganizerCollection* collection, QOrganizerManager::Error* error)
 
543
{
 
544
    Q_UNUSED(collection);
 
545
 
 
546
    *error = QOrganizerManager::NotSupportedError;
 
547
    return false;
 
548
}
 
549
 
 
550
/*!
 
551
    This function should be reimplemented to support synchronous calls to remove a collection.
 
552
 
 
553
    This function is supposed to remove the collection identified by the given \a collectionId, and
 
554
    all items in the collection. Returns true on success, or false otherwise. Any errors encountered
 
555
    during this operation should be stored to \a error.
 
556
 
 
557
    Note that removing the default collection should not be allowed and should result a
 
558
    QOrganizerManager::PermissionsError error.
 
559
*/
 
560
bool QOrganizerManagerEngine::removeCollection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error)
 
561
{
 
562
    Q_UNUSED(collectionId);
 
563
 
 
564
    *error = QOrganizerManager::NotSupportedError;
 
565
    return false;
 
566
}
 
567
 
 
568
/*!
 
569
  Given an input \a filter, returns the canonical version of the filter.
 
570
 
 
571
  Some of the following transformations may be applied:
 
572
  \list
 
573
   \li Any QOrganizerItemInvalidFilters contained in a union filter will be removed
 
574
   \li Any default QOrganizerItemFilters contained in an intersection filter will be removed
 
575
   \li Any QOrganizerItemIntersectionFilters with a QOrganizerItemInvalidFilter contained will be
 
576
     replaced with a QOrganizerItemInvalidFilter
 
577
   \li Any QOrganizerItemUnionFilters with a default QOrganizerItemFilter contained will be replaced
 
578
     with a default QOrganizerItemFilter
 
579
   \li An empty QOrganizerItemIntersectionFilter will be replaced with a QOrganizerItemDefaultFilter
 
580
   \li An empty QOrganizerItemUnionFilter will be replaced with a QOrganizerItemInvalidFilter
 
581
   \li An empty QOrganizerItemIdFilter will be replaced with a QOrganizerItemInvalidFilter
 
582
   \li An intersection or union filter with a single entry will be replaced by that entry
 
583
   \li A QOrganizerItemDetailFieldFilter or QOrganizerItemDetailRangeFilter with no definition name will be replaced with a QOrganizerItemInvalidFilter
 
584
   \li A QOrganizerItemDetailRangeFilter with no range specified will be converted to a QOrganizerItemDetailFieldFilter
 
585
  \endlist
 
586
*/
 
587
QOrganizerItemFilter QOrganizerManagerEngine::canonicalizedFilter(const QOrganizerItemFilter &filter)
 
588
{
 
589
    switch (filter.type()) {
 
590
        case QOrganizerItemFilter::IntersectionFilter:
 
591
        {
 
592
            QOrganizerItemIntersectionFilter f(filter);
 
593
            QList<QOrganizerItemFilter> filters = f.filters();
 
594
            QList<QOrganizerItemFilter>::iterator it = filters.begin();
 
595
 
 
596
            // XXX in theory we can remove duplicates in a set filter
 
597
            while (it != filters.end()) {
 
598
                QOrganizerItemFilter canon = canonicalizedFilter(*it);
 
599
                if (canon.type() == QOrganizerItemFilter::DefaultFilter) {
 
600
                    it = filters.erase(it);
 
601
                } else if (canon.type() == QOrganizerItemFilter::InvalidFilter) {
 
602
                    return QOrganizerItemInvalidFilter();
 
603
                } else {
 
604
                    *it = canon;
 
605
                    ++it;
 
606
                }
 
607
            }
 
608
 
 
609
            if (filters.count() == 0)
 
610
                return QOrganizerItemFilter();
 
611
            if (filters.count() == 1)
 
612
                return filters.first();
 
613
 
 
614
            f.setFilters(filters);
 
615
            return f;
 
616
        }
 
617
        // unreachable
 
618
 
 
619
        case QOrganizerItemFilter::UnionFilter:
 
620
        {
 
621
            QOrganizerItemUnionFilter f(filter);
 
622
            QList<QOrganizerItemFilter> filters = f.filters();
 
623
            QList<QOrganizerItemFilter>::iterator it = filters.begin();
 
624
 
 
625
            // XXX in theory we can remove duplicates in a set filter
 
626
            while (it != filters.end()) {
 
627
                QOrganizerItemFilter canon = canonicalizedFilter(*it);
 
628
                if (canon.type() == QOrganizerItemFilter::InvalidFilter) {
 
629
                    it = filters.erase(it);
 
630
                } else if (canon.type() == QOrganizerItemFilter::DefaultFilter) {
 
631
                    return QOrganizerItemFilter();
 
632
                } else {
 
633
                    *it = canon;
 
634
                    ++it;
 
635
                }
 
636
            }
 
637
 
 
638
            if (filters.count() == 0)
 
639
                return QOrganizerItemInvalidFilter();
 
640
            if (filters.count() == 1)
 
641
                return filters.first();
 
642
 
 
643
            f.setFilters(filters);
 
644
            return f;
 
645
        }
 
646
        // unreachable
 
647
 
 
648
        case QOrganizerItemFilter::IdFilter:
 
649
        {
 
650
            QOrganizerItemIdFilter f(filter);
 
651
            if (f.ids().count() == 0)
 
652
                return QOrganizerItemInvalidFilter();
 
653
        }
 
654
        break; // fall through to return at end
 
655
 
 
656
        case QOrganizerItemFilter::DetailRangeFilter:
 
657
        {
 
658
            QOrganizerItemDetailRangeFilter f(filter);
 
659
            if (f.detailType() == QOrganizerItemDetail::TypeUndefined)
 
660
                return QOrganizerItemInvalidFilter();
 
661
            if (f.minValue() == f.maxValue()
 
662
                && f.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeLower | QOrganizerItemDetailRangeFilter::ExcludeUpper))
 
663
                return QOrganizerItemInvalidFilter();
 
664
            if ((f.minValue().isNull() && f.maxValue().isNull()) || (f.minValue() == f.maxValue())) {
 
665
                QOrganizerItemDetailFieldFilter df;
 
666
                df.setDetail(f.detailType(), f.detailField());
 
667
                df.setMatchFlags(f.matchFlags());
 
668
                df.setValue(f.minValue());
 
669
                return df;
 
670
            }
 
671
        }
 
672
        break; // fall through to return at end
 
673
 
 
674
        case QOrganizerItemFilter::DetailFieldFilter:
 
675
        {
 
676
            QOrganizerItemDetailFieldFilter f(filter);
 
677
            if (f.detailType() == QOrganizerItemDetail::TypeUndefined)
 
678
                return QOrganizerItemInvalidFilter();
 
679
        }
 
680
        break; // fall through to return at end
 
681
 
 
682
        default:
 
683
            break; // fall through to return at end
 
684
    }
 
685
    return filter;
 
686
}
 
687
 
 
688
/*!
 
689
  Compares \a first against \a second.  If the types are
 
690
  strings (QVariant::String), the \a sensitivity argument controls
 
691
  case sensitivity when comparing.
 
692
 
 
693
  Returns:
 
694
  <0 if \a first is less than \a second
 
695
   0 if \a first is equal to \a second
 
696
  >0 if \a first is greater than \a second.
 
697
 
 
698
  The results are undefined if the variants are different types, or
 
699
  cannot be compared.
 
700
 */
 
701
int QOrganizerManagerEngine::compareVariant(const QVariant& first, const QVariant& second, Qt::CaseSensitivity sensitivity)
 
702
{
 
703
    switch(first.type()) {
 
704
        case QVariant::Int:
 
705
            return first.toInt() - second.toInt();
 
706
 
 
707
        case QVariant::LongLong:
 
708
            return first.toLongLong() - second.toLongLong();
 
709
 
 
710
        case QVariant::Bool:
 
711
        case QVariant::Char:
 
712
        case QVariant::UInt:
 
713
            return first.toUInt() - second.toUInt();
 
714
 
 
715
        case QVariant::ULongLong:
 
716
            return first.toULongLong() - second.toULongLong();
 
717
 
 
718
       case QVariant::String:
 
719
            return first.toString().compare(second.toString(), sensitivity);
 
720
 
 
721
        case QVariant::Double:
 
722
            {
 
723
                const double a = first.toDouble();
 
724
                const double b = second.toDouble();
 
725
                return (a < b) ? -1 : ((a == b) ? 0 : 1);
 
726
            }
 
727
 
 
728
        case QVariant::DateTime:
 
729
            {
 
730
                const QDateTime a = first.toDateTime();
 
731
                const QDateTime b = second.toDateTime();
 
732
                return (a < b) ? -1 : ((a == b) ? 0 : 1);
 
733
            }
 
734
 
 
735
        case QVariant::Date:
 
736
            return first.toDate().toJulianDay() - second.toDate().toJulianDay();
 
737
 
 
738
        case QVariant::Time:
 
739
            {
 
740
                const QTime a = first.toTime();
 
741
                const QTime b = second.toTime();
 
742
                return (a < b) ? -1 : ((a == b) ? 0 : 1);
 
743
            }
 
744
 
 
745
        default:
 
746
            return 0;
 
747
    }
 
748
}
 
749
 
 
750
/*!
 
751
  Returns true if the supplied item \a item matches the supplied filter \a filter.
 
752
 
 
753
  This function will test each condition in the filter, possibly recursing.
 
754
 */
 
755
bool QOrganizerManagerEngine::testFilter(const QOrganizerItemFilter &filter, const QOrganizerItem &item)
 
756
{
 
757
    switch(filter.type()) {
 
758
        case QOrganizerItemFilter::InvalidFilter:
 
759
            return false;
 
760
 
 
761
        case QOrganizerItemFilter::DefaultFilter:
 
762
            return true;
 
763
 
 
764
        case QOrganizerItemFilter::IdFilter:
 
765
            {
 
766
                const QOrganizerItemIdFilter idf(filter);
 
767
                if (idf.ids().contains(item.id()))
 
768
                    return true;
 
769
            }
 
770
            // Fall through to end
 
771
            break;
 
772
        case QOrganizerItemFilter::DetailFilter:
 
773
            {
 
774
                const QOrganizerItemDetailFilter cdf(filter);
 
775
 
 
776
                QOrganizerItemDetail matchingDetail = cdf.detail();
 
777
                if ( (matchingDetail.isEmpty()) || (matchingDetail.type() == QOrganizerItemDetail::TypeUndefined) )
 
778
                    return false;
 
779
 
 
780
                /* See if this organizer item has one of these details in it */
 
781
                const QList<QOrganizerItemDetail>& details = item.details(cdf.detail().type());
 
782
                if (details.count() == 0)
 
783
                    return false; /* can't match */
 
784
 
 
785
                /* Value equality test */
 
786
                for (int j=0; j < details.count(); j++) {
 
787
                    if (details.at(j) == matchingDetail)
 
788
                        return true;
 
789
                }
 
790
                return false;
 
791
            }
 
792
            // Fall through to end
 
793
            break;
 
794
        case QOrganizerItemFilter::DetailFieldFilter:
 
795
            {
 
796
                const QOrganizerItemDetailFieldFilter cdf(filter);
 
797
                if (cdf.detailType() == QOrganizerItemDetail::TypeUndefined)
 
798
                    return false;
 
799
 
 
800
                /* See if this organizer item has one of these details in it */
 
801
                const QList<QOrganizerItemDetail>& details = item.details(cdf.detailType());
 
802
 
 
803
                if (details.count() == 0)
 
804
                    return false; /* can't match */
 
805
 
 
806
                /* See if we need to check the values */
 
807
                if (cdf.detailField() == -1)
 
808
                    return true;  /* just testing for the presence of a detail of the specified definition */
 
809
 
 
810
                /* Now figure out what tests we are doing */
 
811
                const bool valueTest = cdf.value().isValid();
 
812
                const bool presenceTest = !valueTest;
 
813
 
 
814
                /* See if we need to test any values at all */
 
815
                if (presenceTest) {
 
816
                    for(int j=0; j < details.count(); j++) {
 
817
                        const QOrganizerItemDetail& detail = details.at(j);
 
818
 
 
819
                        /* Check that the field is present and has a non-empty value */
 
820
                        if (detail.values().contains(cdf.detailField()) && !detail.value(cdf.detailField()).isNull())
 
821
                            return true;
 
822
                    }
 
823
                    return false;
 
824
                }
 
825
 
 
826
                /* Case sensitivity, for those parts that use it */
 
827
                Qt::CaseSensitivity cs = (cdf.matchFlags() & QOrganizerItemFilter::MatchCaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
 
828
 
 
829
                /* See what flags are requested, since we're looking at a value */
 
830
                if (cdf.matchFlags() & (QOrganizerItemFilter::MatchEndsWith | QOrganizerItemFilter::MatchStartsWith | QOrganizerItemFilter::MatchContains | QOrganizerItemFilter::MatchFixedString)) {
 
831
                    /* We're strictly doing string comparisons here */
 
832
                    bool matchStarts = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchStartsWith;
 
833
                    bool matchEnds = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchEndsWith;
 
834
                    bool matchContains = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchContains;
 
835
 
 
836
                    /* Value equality test */
 
837
                    for(int j=0; j < details.count(); j++) {
 
838
                        const QOrganizerItemDetail& detail = details.at(j);
 
839
                        const QString& var = detail.value(cdf.detailField()).toString();
 
840
                        const QString& needle = cdf.value().toString();
 
841
                        if (matchStarts && var.startsWith(needle, cs))
 
842
                            return true;
 
843
                        if (matchEnds && var.endsWith(needle, cs))
 
844
                            return true;
 
845
                        if (matchContains && var.contains(needle, cs))
 
846
                            return true;
 
847
                        if (QString::compare(var, needle, cs) == 0)
 
848
                            return true;
 
849
                    }
 
850
                    return false;
 
851
                } else {
 
852
                    /* Nope, testing the values as a variant */
 
853
                    /* Value equality test */
 
854
                    for(int j = 0; j < details.count(); j++) {
 
855
                        const QOrganizerItemDetail& detail = details.at(j);
 
856
                        const QVariant& var = detail.value(cdf.detailField());
 
857
                        if (!var.isNull() && compareVariant(var, cdf.value(), cs) == 0)
 
858
                            return true;
 
859
                    }
 
860
                }
 
861
            }
 
862
            break;
 
863
 
 
864
        case QOrganizerItemFilter::DetailRangeFilter:
 
865
            {
 
866
                const QOrganizerItemDetailRangeFilter cdf(filter);
 
867
                if (cdf.detailType() == QOrganizerItemDetail::TypeUndefined)
 
868
                    return false; /* we do not know which field to check */
 
869
 
 
870
                /* See if this organizer item has one of these details in it */
 
871
                const QList<QOrganizerItemDetail>& details = item.details(cdf.detailType());
 
872
 
 
873
                if (details.count() == 0)
 
874
                    return false; /* can't match */
 
875
 
 
876
                /* Check for a detail presence test */
 
877
                if (cdf.detailField() == -1)
 
878
                    return true;
 
879
 
 
880
                /* See if this is a field presence test */
 
881
                if (!cdf.minValue().isValid() && !cdf.maxValue().isValid()) {
 
882
                    for(int j=0; j < details.count(); j++) {
 
883
                        const QOrganizerItemDetail& detail = details.at(j);
 
884
                        if (detail.values().contains(cdf.detailField()))
 
885
                            return true;
 
886
                    }
 
887
                    return false;
 
888
                }
 
889
 
 
890
                /* open or closed interval testing support */
 
891
                const int minComp = cdf.rangeFlags() & QOrganizerItemDetailRangeFilter::ExcludeLower ? 1 : 0;
 
892
                const int maxComp = cdf.rangeFlags() & QOrganizerItemDetailRangeFilter::IncludeUpper ? 1 : 0;
 
893
 
 
894
                const bool testMin = cdf.minValue().isValid();
 
895
                const bool testMax = cdf.maxValue().isValid();
 
896
 
 
897
                /* At this point we know that at least of testMin & testMax is true */
 
898
 
 
899
                /* Case sensitivity, for those parts that use it */
 
900
                Qt::CaseSensitivity cs = (cdf.matchFlags() & QOrganizerItemFilter::MatchCaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
 
901
 
 
902
                /* See what flags are requested, since we're looking at a value */
 
903
                if (cdf.matchFlags() & (QOrganizerItemFilter::MatchEndsWith | QOrganizerItemFilter::MatchStartsWith | QOrganizerItemFilter::MatchContains | QOrganizerItemFilter::MatchFixedString)) {
 
904
                    /* We're strictly doing string comparisons here */
 
905
                    //bool matchStarts = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchStartsWith;
 
906
                    bool matchEnds = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchEndsWith;
 
907
                    bool matchContains = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchContains;
 
908
 
 
909
                    /* Min/Max and contains do not make sense */
 
910
                    if (matchContains)
 
911
                        return false;
 
912
 
 
913
                    QString minVal = cdf.minValue().toString();
 
914
                    QString maxVal = cdf.maxValue().toString();
 
915
 
 
916
                    /* Starts with is the normal compare case, endsWith is a bit trickier */
 
917
                    for(int j=0; j < details.count(); j++) {
 
918
                        const QOrganizerItemDetail& detail = details.at(j);
 
919
                        const QString& var = detail.value(cdf.detailField()).toString();
 
920
                        if (!matchEnds) {
 
921
                            // MatchStarts or MatchFixedString
 
922
                            if (testMin && QString::compare(var, minVal, cs) < minComp)
 
923
                                continue;
 
924
                            if (testMax && QString::compare(var, maxVal, cs) >= maxComp)
 
925
                                continue;
 
926
                            return true;
 
927
                        } else {
 
928
                            /* Have to test the length of min & max */
 
929
                            // using refs means the parameter order is backwards, so negate the result of compare
 
930
                            if (testMin && -QString::compare(minVal, var.rightRef(minVal.length()), cs) < minComp)
 
931
                                continue;
 
932
                            if (testMax && -QString::compare(maxVal, var.rightRef(maxVal.length()), cs) >= maxComp)
 
933
                                continue;
 
934
                            return true;
 
935
                        }
 
936
                    }
 
937
                    // Fall through to end
 
938
                } else {
 
939
                    /* Nope, testing the values as a variant */
 
940
                    for(int j=0; j < details.count(); j++) {
 
941
                        const QOrganizerItemDetail& detail = details.at(j);
 
942
                        const QVariant& var = detail.value(cdf.detailField());
 
943
 
 
944
                        if (testMin && compareVariant(var, cdf.minValue(), cs) < minComp)
 
945
                            continue;
 
946
                        if (testMax && compareVariant(var, cdf.maxValue(), cs) >= maxComp)
 
947
                            continue;
 
948
                        return true;
 
949
                    }
 
950
                    // Fall through to end
 
951
                }
 
952
            }
 
953
            break;
 
954
 
 
955
        case QOrganizerItemFilter::IntersectionFilter:
 
956
            {
 
957
                /* XXX In theory we could reorder the terms to put the native tests first */
 
958
                const QOrganizerItemIntersectionFilter bf(filter);
 
959
                const QList<QOrganizerItemFilter>& terms = bf.filters();
 
960
                if (terms.count() > 0) {
 
961
                    for(int j = 0; j < terms.count(); j++) {
 
962
                        if (!testFilter(terms.at(j), item)) {
 
963
                            return false;
 
964
                        }
 
965
                    }
 
966
                    return true;
 
967
                }
 
968
                // Fall through to end
 
969
            }
 
970
            break;
 
971
 
 
972
        case QOrganizerItemFilter::UnionFilter:
 
973
            {
 
974
                /* XXX In theory we could reorder the terms to put the native tests first */
 
975
                const QOrganizerItemUnionFilter bf(filter);
 
976
                const QList<QOrganizerItemFilter>& terms = bf.filters();
 
977
                if (terms.count() > 0) {
 
978
                    for(int j = 0; j < terms.count(); j++) {
 
979
                        if (testFilter(terms.at(j), item)) {
 
980
                            return true;
 
981
                        }
 
982
                    }
 
983
                    return false;
 
984
                }
 
985
                // Fall through to end
 
986
            }
 
987
            break;
 
988
 
 
989
    case QOrganizerItemFilter::CollectionFilter:
 
990
            {
 
991
                const QOrganizerItemCollectionFilter cf(filter);
 
992
                const QSet<QOrganizerCollectionId>& ids = cf.collectionIds();
 
993
                if (ids.contains(item.collectionId()))
 
994
                    return true;
 
995
                return false;
 
996
            }
 
997
    }
 
998
    return false;
 
999
}
 
1000
 
 
1001
/*!
 
1002
  Returns true if the given \a item (or an occurrence of the item) occurs within the range
 
1003
  specified by the \a startPeriod and the \a endPeriod, inclusive.
 
1004
  A default-constructed \a startPeriod signifies that the lower bound of the range is
 
1005
  infinitely small (i.e., will match anything up to the \a endPeriod) and a default-constructed
 
1006
  \a endPeriod signifies that the upper bound of the range is infinitely large
 
1007
  (i.e., will match anything which occurs after the \a startPeriod).
 
1008
 */
 
1009
bool QOrganizerManagerEngine::isItemBetweenDates(const QOrganizerItem& item, const QDateTime& startPeriod, const QDateTime& endPeriod)
 
1010
{
 
1011
    if (startPeriod.isNull() && endPeriod.isNull())
 
1012
        return true;
 
1013
 
 
1014
    QDateTime itemDateStart;
 
1015
    QDateTime itemDateEnd;
 
1016
 
 
1017
    if (item.type() == QOrganizerItemType::TypeEvent || item.type() == QOrganizerItemType::TypeEventOccurrence) {
 
1018
        QOrganizerEventTime etr = item.detail(QOrganizerItemDetail::TypeEventTime);
 
1019
        itemDateStart = etr.startDateTime();
 
1020
        itemDateEnd = etr.endDateTime();
 
1021
    } else if (item.type() == QOrganizerItemType::TypeTodo || item.type() == QOrganizerItemType::TypeTodoOccurrence) {
 
1022
        QOrganizerTodoTime ttr = item.detail(QOrganizerItemDetail::TypeTodoTime);
 
1023
        itemDateStart = ttr.startDateTime();
 
1024
        itemDateEnd = ttr.dueDateTime();
 
1025
    } else if (item.type() == QOrganizerItemType::TypeJournal) {
 
1026
        QOrganizerJournal journal = item;
 
1027
        itemDateStart = itemDateEnd = journal.dateTime();
 
1028
    } else if (item.type() == QOrganizerItemType::TypeNote) {
 
1029
        //for note, there is no such start/end datetime so we always return false
 
1030
        return false;
 
1031
    }
 
1032
 
 
1033
    // if period start date is not given, check that item is starting or ending before period end
 
1034
    if (startPeriod.isNull()) // endPeriod must be non-null because of initial test
 
1035
        return (!itemDateStart.isNull() && itemDateStart <= endPeriod) ||
 
1036
               (!itemDateEnd.isNull() && itemDateEnd <= endPeriod);
 
1037
 
 
1038
    // if period end date is not given, check that item is starting or ending after the period start
 
1039
    if (endPeriod.isNull())   // startPeriod must be non-null because of initial test
 
1040
        return (!itemDateEnd.isNull() && itemDateEnd >= startPeriod) ||
 
1041
               (!itemDateStart.isNull() && itemDateStart >= startPeriod);
 
1042
 
 
1043
    // Both startPeriod and endPeriod are not null
 
1044
    // check if item start date is between the period start and end date
 
1045
    if (!itemDateStart.isNull() && itemDateStart >= startPeriod && itemDateStart <= endPeriod)
 
1046
        return true;
 
1047
 
 
1048
    // check if item end date is between the period start and end date
 
1049
    if (!itemDateEnd.isNull() && itemDateEnd >= startPeriod && itemDateEnd <= endPeriod)
 
1050
        return true;
 
1051
 
 
1052
    // check if item interval is including the period interval
 
1053
    if (!itemDateStart.isNull() && !itemDateEnd.isNull() && itemDateStart <= startPeriod && itemDateEnd >= endPeriod)
 
1054
        return true;
 
1055
 
 
1056
    return false;
 
1057
}
 
1058
 
 
1059
/*!
 
1060
    \internal
 
1061
 
 
1062
    Returns the date associated with \a item that can be used for the purpose of date-sorting the item.
 
1063
 */
 
1064
QDateTime getDateForSorting(const QOrganizerItem& item)
 
1065
{
 
1066
    QDateTime retn;
 
1067
    {
 
1068
        QOrganizerEventTime detail = item.detail(QOrganizerItemDetail::TypeEventTime);
 
1069
        if (!detail.isEmpty()) {
 
1070
            retn = detail.startDateTime();
 
1071
            if (!retn.isValid())
 
1072
                retn = detail.endDateTime();
 
1073
            if (retn.isValid() && detail.isAllDay()) {
 
1074
                // set it to a millisecond before the given date to have it sorted correctly
 
1075
                retn.setTime(QTime(23, 59, 59, 999));
 
1076
                retn.addDays(-1);
 
1077
            }
 
1078
            return retn;
 
1079
        }
 
1080
    }
 
1081
    {
 
1082
        QOrganizerTodoTime detail = item.detail(QOrganizerItemDetail::TypeTodoTime);
 
1083
        if (!detail.isEmpty()) {
 
1084
            retn = detail.startDateTime();
 
1085
            if (!retn.isValid())
 
1086
                retn = detail.dueDateTime();
 
1087
            if (retn.isValid() && detail.isAllDay()) {
 
1088
                // set it to a millisecond before the given date to have it sorted correctly
 
1089
                retn.setTime(QTime(23, 59, 59, 999));
 
1090
                retn.addDays(-1);
 
1091
            }
 
1092
            return retn;
 
1093
        }
 
1094
    }
 
1095
 
 
1096
    // If it's a note, this will just return null, as expected
 
1097
    return item.detail(QOrganizerItemDetail::TypeJournalTime).value(QOrganizerJournalTime::FieldEntryDateTime).toDateTime();
 
1098
}
 
1099
 
 
1100
/*!
 
1101
    Returns true if and only if \a a is temporally less than \a b.  Items with an earlier date are
 
1102
    temporally less than items with a later date, or items with no date.  All day items are
 
1103
    temporally less than non-all day items on the same date.  For events and todos, the
 
1104
    start date is used, or if null, the end date is used.  This function defines a total ordering
 
1105
    suitable for use in a sort function.
 
1106
 */
 
1107
bool QOrganizerManagerEngine::itemLessThan(const QOrganizerItem& a, const QOrganizerItem& b)
 
1108
{
 
1109
    QDateTime date1 = getDateForSorting(a);
 
1110
    if (!date1.isValid()) {
 
1111
        return false;
 
1112
    } else {
 
1113
        QDateTime date2 = getDateForSorting(b);
 
1114
        if (!date2.isValid())
 
1115
            return true;
 
1116
        else
 
1117
            return date1 < date2;
 
1118
    }
 
1119
}
 
1120
 
 
1121
/*!
 
1122
  Compares two organizer items (\a a and \a b) using the given list of \a sortOrders.  Returns a negative number if \a a should appear
 
1123
  before \a b according to the sort order, a positive number if \a a should appear after \a b according to the sort order,
 
1124
  and zero if the two are unable to be sorted.
 
1125
 */
 
1126
int QOrganizerManagerEngine::compareItem(const QOrganizerItem& a, const QOrganizerItem& b, const QList<QOrganizerItemSortOrder>& sortOrders)
 
1127
{
 
1128
    QList<QOrganizerItemSortOrder> copy = sortOrders;
 
1129
    while (copy.size()) {
 
1130
        // retrieve the next sort order in the list
 
1131
        QOrganizerItemSortOrder sortOrder = copy.takeFirst();
 
1132
        if (!sortOrder.isValid())
 
1133
            break;
 
1134
 
 
1135
        // obtain the values which this sort order concerns
 
1136
        const QVariant& aVal = a.detail(sortOrder.detailType()).value(sortOrder.detailField());
 
1137
        const QVariant& bVal = b.detail(sortOrder.detailType()).value(sortOrder.detailField());
 
1138
 
 
1139
        bool aIsNull = false;
 
1140
        bool bIsNull = false;
 
1141
 
 
1142
        // treat empty strings as null qvariants.
 
1143
        if ((aVal.type() == QVariant::String && aVal.toString().isEmpty()) || aVal.isNull()) {
 
1144
            aIsNull = true;
 
1145
        }
 
1146
        if ((bVal.type() == QVariant::String && bVal.toString().isEmpty()) || bVal.isNull()) {
 
1147
            bIsNull = true;
 
1148
        }
 
1149
 
 
1150
        // early exit error checking
 
1151
        if (aIsNull && bIsNull)
 
1152
            continue; // use next sort criteria.
 
1153
        if (aIsNull)
 
1154
            return (sortOrder.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst ? -1 : 1);
 
1155
        if (bIsNull)
 
1156
            return (sortOrder.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst ? 1 : -1);
 
1157
 
 
1158
        // real comparison
 
1159
        int comparison = compareVariant(aVal, bVal, sortOrder.caseSensitivity()) * (sortOrder.direction() == Qt::AscendingOrder ? 1 : -1);
 
1160
        if (comparison == 0)
 
1161
            continue;
 
1162
        return comparison;
 
1163
    }
 
1164
 
 
1165
    return 0; // or according to id? return (a.id() < b.id() ? -1 : 1);
 
1166
}
 
1167
 
 
1168
 
 
1169
/*!
 
1170
    Insert \a toAdd to the \a sorted list, according to the provided \a sortOrders. The index where \a toAdd is inserted
 
1171
    is returned.
 
1172
 
 
1173
    The first one in the \a sortOrders list has the highest priority.
 
1174
 */
 
1175
int QOrganizerManagerEngine::addSorted(QList<QOrganizerItem> *sorted, const QOrganizerItem &toAdd, const QList<QOrganizerItemSortOrder> &sortOrders)
 
1176
{
 
1177
    if (sortOrders.count() > 0) {
 
1178
        for (int i = 0; i < sorted->size(); ++i) {
 
1179
            // check to see if the new item should be inserted here
 
1180
            int comparison = compareItem(sorted->at(i), toAdd, sortOrders);
 
1181
            if (comparison > 0) {
 
1182
                sorted->insert(i, toAdd);
 
1183
                return i;
 
1184
            }
 
1185
        }
 
1186
    }
 
1187
 
 
1188
    // hasn't been inserted yet?  append to the list.
 
1189
    sorted->append(toAdd);
 
1190
    return sorted->size() - 1;
 
1191
}
 
1192
 
 
1193
/*!
 
1194
    Insert \a toAdd to the \a defaultSorted map. If \a toAdd does not have valid start or end date,
 
1195
    returns false and does not insert \a toAdd to \a defaultSorted map.
 
1196
 
 
1197
    This function provides default sorting, which should be used for sorting fetch results, if no sort order
 
1198
    was defined for the fetch. The default sorting algorithm is to sort based on start time of an item. If start time
 
1199
    does not exist, end time or due time is used instead. For allday events, time 00:00 is used for sorting purposes.
 
1200
    Items with no start or end time are last in the sorting order.
 
1201
 
 
1202
    This function sorts items using QMultiMap, where QDateTime is used as a key. To get a sorted list of items,
 
1203
    QMultiMap::values function should be called and items without start and end date added to the end of the list.
 
1204
 */
 
1205
bool QOrganizerManagerEngine::addDefaultSorted(QMultiMap<QDateTime, QOrganizerItem> *defaultSorted, const QOrganizerItem &toAdd)
 
1206
{
 
1207
    QDateTime sortTime;
 
1208
    if (toAdd.type() == QOrganizerItemType::TypeEvent || toAdd.type() == QOrganizerItemType::TypeEventOccurrence) {
 
1209
        QOrganizerEventTime eventTime = toAdd.detail(QOrganizerItemDetail::TypeEventTime);
 
1210
        // both start and end times are mandatory for an event in jsondb schema, so all this checking might redundant
 
1211
        if (eventTime.startDateTime().isValid())
 
1212
            sortTime = eventTime.startDateTime();
 
1213
        else if (eventTime.endDateTime().isValid())
 
1214
            sortTime = eventTime.endDateTime();
 
1215
 
 
1216
        if (eventTime.isAllDay() && sortTime.isValid())
 
1217
            sortTime.setTime(QTime(0, 0, 0));
 
1218
 
 
1219
    } else if (toAdd.type() == QOrganizerItemType::TypeTodo || toAdd.type() == QOrganizerItemType::TypeTodoOccurrence) {
 
1220
        QOrganizerTodoTime todoTime = toAdd.detail(QOrganizerItemDetail::TypeTodoTime);
 
1221
        if (todoTime.startDateTime().isValid())
 
1222
            sortTime = todoTime.startDateTime();
 
1223
        else if (todoTime.dueDateTime().isValid())
 
1224
            sortTime = todoTime.dueDateTime();
 
1225
 
 
1226
        if (todoTime.isAllDay() && sortTime.isValid())
 
1227
            sortTime.setTime(QTime(0, 0, 0));
 
1228
    }
 
1229
 
 
1230
    if (sortTime.isValid()) {
 
1231
        // FIXME: sorting of events with exactly the same key
 
1232
        defaultSorted->insert(sortTime, toAdd);
 
1233
        return true;
 
1234
    } else {
 
1235
        return false;
 
1236
    }
 
1237
}
 
1238
 
 
1239
/*!
 
1240
    Generates a new occurrence for \a parentItem. All \a parentItem details, except for \l QOrganizerItemType and
 
1241
    \l QOrganizerItemRecurrence copied to the occurrence. Occurrence start date is set to the date given in \a rdate.
 
1242
    End date is modified accordingly. Occurrence's \l QOrganizerItemParent detail contains the id of \a parentItem
 
1243
    and the original date given in \a rdate.
 
1244
 */
 
1245
QOrganizerItem QOrganizerManagerEngine::generateOccurrence(const QOrganizerItem &parentItem, const QDateTime &rdate)
 
1246
{
 
1247
    QOrganizerItem instanceItem;
 
1248
    if (parentItem.type() == QOrganizerItemType::TypeEvent) {
 
1249
        instanceItem = QOrganizerEventOccurrence();
 
1250
    } else {
 
1251
        instanceItem = QOrganizerTodoOccurrence();
 
1252
    }
 
1253
 
 
1254
    instanceItem.setCollectionId(parentItem.collectionId());
 
1255
 
 
1256
    // XXX TODO: something better than this linear search...
 
1257
    // Grab all details from the parent item except the recurrence information, and event/todo time range
 
1258
    QList<QOrganizerItemDetail> allDetails = parentItem.details();
 
1259
    QList<QOrganizerItemDetail> occDetails;
 
1260
    foreach (const QOrganizerItemDetail &detail, allDetails) {
 
1261
        if (detail.type() != QOrganizerItemDetail::TypeRecurrence
 
1262
                && detail.type() != QOrganizerItemDetail::TypeEventTime
 
1263
                && detail.type() != QOrganizerItemDetail::TypeTodoTime) {
 
1264
            occDetails.append(detail);
 
1265
        }
 
1266
    }
 
1267
 
 
1268
    // add the detail which identifies exactly which instance this item is.
 
1269
    QOrganizerItemParent parentDetail;
 
1270
    parentDetail.setParentId(parentItem.id());
 
1271
    parentDetail.setOriginalDate(rdate.date());
 
1272
    occDetails.append(parentDetail);
 
1273
 
 
1274
    // save those details in the instance.
 
1275
    foreach (const QOrganizerItemDetail &detail, occDetails) {
 
1276
        // copy every detail except the type
 
1277
        if (detail.type() != QOrganizerItemDetail::TypeItemType) {
 
1278
            QOrganizerItemDetail modifiable = detail;
 
1279
            instanceItem.saveDetail(&modifiable);
 
1280
        }
 
1281
    }
 
1282
 
 
1283
    // and update the time range in the instance based on the current instance date
 
1284
    if (parentItem.type() == QOrganizerItemType::TypeEvent) {
 
1285
        QOrganizerEventTime etr = parentItem.detail(QOrganizerItemDetail::TypeEventTime);
 
1286
        if (!etr.isEmpty()) {
 
1287
            int eventDayCount = 0;
 
1288
            if (etr.startDateTime().isValid() && etr.endDateTime().isValid())
 
1289
                eventDayCount = etr.startDateTime().daysTo(etr.endDateTime());
 
1290
            QDateTime temp = etr.startDateTime();
 
1291
            temp.setDate(rdate.date());
 
1292
            etr.setStartDateTime(temp);
 
1293
            temp = etr.endDateTime();
 
1294
            QDate endDate = rdate.addDays(eventDayCount).date();
 
1295
            temp.setDate(endDate);
 
1296
            etr.setEndDateTime(temp);
 
1297
            instanceItem.saveDetail(&etr);
 
1298
        }
 
1299
    }
 
1300
 
 
1301
    // for todo's
 
1302
    if (parentItem.type() == QOrganizerItemType::TypeTodo) {
 
1303
        QOrganizerTodoTime ttr = parentItem.detail(QOrganizerItemDetail::TypeTodoTime);
 
1304
        if (!ttr.isEmpty()) {
 
1305
            int todoDayCount = 0;
 
1306
            if (ttr.startDateTime().isValid() && ttr.dueDateTime().isValid())
 
1307
                todoDayCount = ttr.startDateTime().daysTo(ttr.dueDateTime());
 
1308
            QDateTime temp = ttr.startDateTime();
 
1309
            temp.setDate(rdate.date());
 
1310
            ttr.setStartDateTime(temp);
 
1311
            temp = ttr.dueDateTime();
 
1312
            QDate endDate = rdate.addDays(todoDayCount).date();
 
1313
            temp.setDate(endDate);
 
1314
            ttr.setDueDateTime(temp);
 
1315
            instanceItem.saveDetail(&ttr);
 
1316
        }
 
1317
    }
 
1318
 
 
1319
    return instanceItem;
 
1320
}
 
1321
 
 
1322
/*!
 
1323
    Generates all start times for recurrence \a rrule during the given time period. The time period is defined by
 
1324
    \a periodStart and \a periodEnd. \a initialDateTime is the start time of the event, which defines the first
 
1325
    start time for \a rrule. \a maxCount can be used to limit the amount of generated start times.
 
1326
 */
 
1327
QList<QDateTime> QOrganizerManagerEngine::generateDateTimes(const QDateTime &initialDateTime, QOrganizerRecurrenceRule rrule, const QDateTime &periodStart, const QDateTime &periodEnd, int maxCount)
 
1328
{
 
1329
    QList<QDateTime> retn;
 
1330
    if (periodEnd.isValid() || maxCount <= 0)
 
1331
        maxCount = INT_MAX; // count of returned items is unlimited
 
1332
 
 
1333
    QDateTime realPeriodEnd(periodEnd);
 
1334
    if (rrule.limitType() == QOrganizerRecurrenceRule::DateLimit
 
1335
            && rrule.limitDate() < realPeriodEnd.date()) {
 
1336
        realPeriodEnd.setDate(rrule.limitDate());
 
1337
        realPeriodEnd.setTime(QTime(23,59,59,999)); // the last instant of the limit date, since it's prior to the periodEnd.
 
1338
    }
 
1339
 
 
1340
    QDate nextDate;
 
1341
    if (rrule.limitType() == QOrganizerRecurrenceRule::CountLimit)
 
1342
        nextDate = initialDateTime.date();
 
1343
    else
 
1344
        nextDate = periodStart.date();
 
1345
 
 
1346
    inferMissingCriteria(&rrule, initialDateTime.date());
 
1347
    int countLimitDates = 0;
 
1348
    bool periodEndReached = false;
 
1349
    while (!periodEndReached && nextDate <= realPeriodEnd.date() && retn.size() < maxCount) {
 
1350
        if (rrule.limitType() == QOrganizerRecurrenceRule::CountLimit && countLimitDates >= rrule.limitCount())
 
1351
            break; // reached limit count defined in the recurrence rule
 
1352
        // Skip nextDate if it is not the right multiple of intervals away from initialDateTime.
 
1353
        if (inMultipleOfInterval(nextDate, initialDateTime.date(), rrule.frequency(), rrule.interval(), rrule.firstDayOfWeek())) {
 
1354
            // Calculate the inclusive start and inclusive end of nextDate's week/month/year
 
1355
            QDate subPeriodStart(firstDateInPeriod(nextDate, rrule.frequency(), rrule.firstDayOfWeek()));
 
1356
            QDate subPeriodEnd(firstDateInNextPeriod(nextDate, rrule.frequency(), rrule.firstDayOfWeek()).addDays(-1));
 
1357
            // Compute matchesInPeriod to be the set of dates in the current week/month/year that match the rrule
 
1358
            QList<QDate> matchesInPeriod(filterByPosition(
 
1359
                    matchingDates(subPeriodStart, subPeriodEnd, rrule),
 
1360
                    rrule.positions()));
 
1361
            // A final filter over the dates list before adding it to the returned list
 
1362
            foreach (const QDate &match, matchesInPeriod) {
 
1363
                nextDate = match;
 
1364
                if (match > realPeriodEnd.date() || retn.size() >= maxCount) {
 
1365
                    break;
 
1366
                }
 
1367
 
 
1368
                QDateTime generatedDateTime(initialDateTime);
 
1369
 
 
1370
                generatedDateTime.setDate(match);
 
1371
                countLimitDates++;
 
1372
                if (generatedDateTime >= periodStart && generatedDateTime <= realPeriodEnd) {
 
1373
                    retn.append(generatedDateTime);
 
1374
                } else if (generatedDateTime > realPeriodEnd) {
 
1375
                    // We've gone past the end of the period.  Ensure we break both the foreach and
 
1376
                    // the while loop
 
1377
                    periodEndReached = true;
 
1378
                    break;
 
1379
                }
 
1380
                if (rrule.limitType() == QOrganizerRecurrenceRule::CountLimit && countLimitDates >= rrule.limitCount())
 
1381
                    break; // reached limit count defined in the recurrence rule
 
1382
            }
 
1383
        }
 
1384
        nextDate = firstDateInNextPeriod(nextDate, rrule.frequency(), rrule.firstDayOfWeek());
 
1385
    }
 
1386
    return retn;
 
1387
}
 
1388
 
 
1389
/*!
 
1390
   Determines if \a rrule is underspecified and if so, fills in missing information based on \a
 
1391
   initialDate.
 
1392
 */
 
1393
void QOrganizerManagerEngine::inferMissingCriteria(QOrganizerRecurrenceRule *rrule, const QDate &initialDate)
 
1394
{
 
1395
    switch (rrule->frequency()) {
 
1396
        case QOrganizerRecurrenceRule::Weekly:
 
1397
            if (rrule->daysOfWeek().isEmpty()) {
 
1398
                // derive day of week
 
1399
                QSet<Qt::DayOfWeek> days;
 
1400
                days << static_cast<Qt::DayOfWeek>(initialDate.dayOfWeek());
 
1401
                rrule->setDaysOfWeek(days);
 
1402
            }
 
1403
            break;
 
1404
        case QOrganizerRecurrenceRule::Monthly:
 
1405
            if (rrule->daysOfWeek().isEmpty() && rrule->daysOfMonth().isEmpty()) {
 
1406
                // derive day of month
 
1407
                QSet<int> days;
 
1408
                days << initialDate.day();
 
1409
                rrule->setDaysOfMonth(days);
 
1410
            }
 
1411
            break;
 
1412
        case QOrganizerRecurrenceRule::Yearly:
 
1413
            if (rrule->monthsOfYear().isEmpty()
 
1414
                    && rrule->weeksOfYear().isEmpty()
 
1415
                    && rrule->daysOfYear().isEmpty()
 
1416
                    && rrule->daysOfMonth().isEmpty()
 
1417
                    && rrule->daysOfWeek().isEmpty()) {
 
1418
                // derive day of month and month of year
 
1419
                QSet<int> daysOfMonth;
 
1420
                daysOfMonth << initialDate.day();
 
1421
                rrule->setDaysOfMonth(daysOfMonth);
 
1422
                QSet<QOrganizerRecurrenceRule::Month> months;
 
1423
                months << static_cast<QOrganizerRecurrenceRule::Month>(initialDate.month());
 
1424
                rrule->setMonthsOfYear(months);
 
1425
            } else if (!rrule->monthsOfYear().isEmpty()
 
1426
                    && rrule->weeksOfYear().isEmpty()
 
1427
                    && rrule->daysOfYear().isEmpty()
 
1428
                    && rrule->daysOfMonth().isEmpty()
 
1429
                    && rrule->daysOfWeek().isEmpty()) {
 
1430
                // derive day of month
 
1431
                QSet<int> daysOfMonth;
 
1432
                daysOfMonth << initialDate.day();
 
1433
                rrule->setDaysOfMonth(daysOfMonth);
 
1434
            } else if (!rrule->weeksOfYear().isEmpty()
 
1435
                    && rrule->daysOfYear().isEmpty()
 
1436
                    && rrule->daysOfMonth().isEmpty()
 
1437
                    && rrule->daysOfWeek().isEmpty()) {
 
1438
                // derive day of week
 
1439
                QSet<Qt::DayOfWeek> days;
 
1440
                days << static_cast<Qt::DayOfWeek>(initialDate.dayOfWeek());
 
1441
                rrule->setDaysOfWeek(days);
 
1442
            }
 
1443
            break;
 
1444
        case QOrganizerRecurrenceRule::Daily:
 
1445
            break;
 
1446
        case QOrganizerRecurrenceRule::Invalid:
 
1447
            Q_ASSERT(false);
 
1448
    }
 
1449
}
 
1450
 
 
1451
/*!
 
1452
   Returns true if the calendar period (specified by \a frequency) of \a date is an \a
 
1453
   interval multiple of periods ahead of the calendar period of \a initialDate. For Weekly frequencies,
 
1454
   \a firstDayOfWeek is used to determine when the week boundary is. eg. If \a frequency is Monthly
 
1455
   and \a interval is 3, then true is returned iff \a date is in the same month as \a initialDate,
 
1456
   in a month 3 months ahead, 6 months ahead, etc.
 
1457
 */
 
1458
bool QOrganizerManagerEngine::inMultipleOfInterval(const QDate &date, const QDate &initialDate, QOrganizerRecurrenceRule::Frequency frequency, int interval, Qt::DayOfWeek firstDayOfWeek)
 
1459
{
 
1460
    Q_ASSERT(date >= initialDate);
 
1461
    switch (frequency) {
 
1462
        case QOrganizerRecurrenceRule::Yearly: {
 
1463
            uint yearsDelta = date.year() - initialDate.year();
 
1464
            return (yearsDelta % interval == 0);
 
1465
        }
 
1466
        case QOrganizerRecurrenceRule::Monthly: {
 
1467
            uint monthsDelta = date.month() - initialDate.month() + (12 * (date.year() - initialDate.year()));
 
1468
            return (monthsDelta % interval == 0);
 
1469
        }
 
1470
        case QOrganizerRecurrenceRule::Weekly: {
 
1471
            // we need to adjust for the week start specified by the client if the interval is greater than 1
 
1472
            // ie, every time we hit the day specified, we increment the week count.
 
1473
            uint weekCount = 0;
 
1474
            QDate tempDate = initialDate;
 
1475
            while (tempDate < date) {
 
1476
                tempDate = tempDate.addDays(1);
 
1477
                if (static_cast<Qt::DayOfWeek>(tempDate.dayOfWeek()) == firstDayOfWeek) {
 
1478
                    weekCount += 1;
 
1479
                }
 
1480
            }
 
1481
            return (weekCount % interval == 0);
 
1482
        }
 
1483
        case QOrganizerRecurrenceRule::Daily: {
 
1484
            uint daysDelta = initialDate.daysTo(date);
 
1485
            return (daysDelta % interval == 0);
 
1486
        }
 
1487
        case QOrganizerRecurrenceRule::Invalid:
 
1488
            Q_ASSERT(false);
 
1489
    }
 
1490
    return true;
 
1491
}
 
1492
 
 
1493
/*!
 
1494
   Returns the date which is the first date of the calendar period that \a date resides in.  eg. if
 
1495
   the \a frequency is Monthly, then this returns the first day of \a date's month.  If the \a
 
1496
   frequency is Weekly, then it returns the first day of \a date's week, considering the week to
 
1497
   start on \a firstDayOfWeek
 
1498
 */
 
1499
QDate QOrganizerManagerEngine::firstDateInPeriod(const QDate &date, QOrganizerRecurrenceRule::Frequency frequency, Qt::DayOfWeek firstDayOfWeek)
 
1500
{
 
1501
    QDate retn(date);
 
1502
    switch (frequency) {
 
1503
        case QOrganizerRecurrenceRule::Yearly:
 
1504
            retn.setDate(date.year(), 1, 1);
 
1505
            return retn;
 
1506
        case QOrganizerRecurrenceRule::Monthly:
 
1507
            retn.setDate(date.year(), date.month(), 1);
 
1508
            return retn;
 
1509
        case QOrganizerRecurrenceRule::Weekly:
 
1510
            while (retn.dayOfWeek() != firstDayOfWeek) {
 
1511
                retn = retn.addDays(-1);
 
1512
            }
 
1513
            return retn;
 
1514
        case QOrganizerRecurrenceRule::Daily:
 
1515
            return retn;
 
1516
        default:
 
1517
            Q_ASSERT(false);
 
1518
            return retn;
 
1519
    }
 
1520
}
 
1521
 
 
1522
/*!
 
1523
   Returns the date which is the first date of the next calendar period after \a date specified by
 
1524
   \a frequency.  eg. if \a frequency is Monthly, then this returns the first day of the next month.
 
1525
   If \a frequency is Weekly, then it returns the first \a firstDayOfWeek after \a date.
 
1526
 */
 
1527
QDate QOrganizerManagerEngine::firstDateInNextPeriod(const QDate &date, QOrganizerRecurrenceRule::Frequency frequency, Qt::DayOfWeek firstDayOfWeek)
 
1528
{
 
1529
    QDate retn(date);
 
1530
    switch (frequency) {
 
1531
        case QOrganizerRecurrenceRule::Yearly:
 
1532
            retn.setDate(date.year()+1, 1, 1);
 
1533
            return retn;
 
1534
        case QOrganizerRecurrenceRule::Monthly:
 
1535
            {
 
1536
            int newMonth = date.month() + 1;
 
1537
            int newYear = date.year() + (newMonth==13 ? 1 : 0);
 
1538
            retn.setDate(newYear, newMonth==13 ? 1 : newMonth, 1);
 
1539
            }
 
1540
            return retn;
 
1541
        case QOrganizerRecurrenceRule::Weekly:
 
1542
            do {
 
1543
                retn = retn.addDays(1);
 
1544
            } while (retn.dayOfWeek() != firstDayOfWeek);
 
1545
            return retn;
 
1546
        case QOrganizerRecurrenceRule::Daily:
 
1547
            retn = retn.addDays(1);
 
1548
            return retn;
 
1549
        case QOrganizerRecurrenceRule::Invalid:
 
1550
            Q_ASSERT(false);
 
1551
    }
 
1552
    return retn;
 
1553
}
 
1554
 
 
1555
/*!
 
1556
   Returns a list of dates between \a periodStart (inclusive) and \a periodEnd (inclusive) which
 
1557
   match the \a rrule.  Only daysOfWeek, daysOfMonth, daysOfYear, weeksOfYear and months from the \a
 
1558
   rrule are matched.
 
1559
 */
 
1560
QList<QDate> QOrganizerManagerEngine::matchingDates(const QDate &periodStart, const QDate &periodEnd, const QOrganizerRecurrenceRule &rrule)
 
1561
{
 
1562
    QList<QDate> retn;
 
1563
 
 
1564
    QSet<Qt::DayOfWeek> daysOfWeek = rrule.daysOfWeek();
 
1565
    QSet<int> daysOfMonth = rrule.daysOfMonth();
 
1566
    QSet<int> daysOfYear = rrule.daysOfYear();
 
1567
    QSet<int> weeksOfYear = rrule.weeksOfYear();
 
1568
    QSet<QOrganizerRecurrenceRule::Month> monthsOfYear = rrule.monthsOfYear();
 
1569
 
 
1570
    QDate tempDate = periodStart;
 
1571
    while (tempDate <= periodEnd) {
 
1572
        if ((monthsOfYear.isEmpty() || monthsOfYear.contains(static_cast<QOrganizerRecurrenceRule::Month>(tempDate.month())))
 
1573
                && (weeksOfYear.isEmpty() || weeksOfYear.contains(tempDate.weekNumber()))
 
1574
                && (daysOfYear.isEmpty() || daysOfYear.contains(tempDate.dayOfYear()))
 
1575
                && (daysOfMonth.isEmpty() || daysOfMonth.contains(tempDate.day()))
 
1576
                && (daysOfWeek.isEmpty() || daysOfWeek.contains(static_cast<Qt::DayOfWeek>(tempDate.dayOfWeek())))) {
 
1577
            retn.append(tempDate);
 
1578
        }
 
1579
        tempDate = tempDate.addDays(1);
 
1580
    }
 
1581
    return retn;
 
1582
}
 
1583
 
 
1584
/*!
 
1585
   Returns a list of dates from \a dates which are at the indices specified by \a positions.
 
1586
   For positive values in \a positions, the values represent a 1-based index into \a dates.
 
1587
   For negative values, they represent indices counting from the end of \a dates (eg. -1 means the
 
1588
   last value of \a dates).
 
1589
 */
 
1590
QList<QDate> QOrganizerManagerEngine::filterByPosition(const QList<QDate> &dates, const QSet<int> positions)
 
1591
{
 
1592
    if (positions.isEmpty()) {
 
1593
        return dates;
 
1594
    }
 
1595
 
 
1596
    QList<QDate> retn;
 
1597
    foreach (int i, positions) {
 
1598
        if (i >= 1 && i <= dates.size()) {
 
1599
            // positions is 1-indexed, but the QList is 0-indexed
 
1600
            retn.append(dates[i-1]);
 
1601
        } else if (i <= -1 && i >= -dates.size()) {
 
1602
            // for negative values, count from the end of the list
 
1603
            retn.append(dates[dates.size() + i]);
 
1604
        }
 
1605
    }
 
1606
    return retn;
 
1607
}
 
1608
 
 
1609
/*!
 
1610
    Returns true if the given organizer item \a oi has any recurrence.
 
1611
 */
 
1612
bool QOrganizerManagerEngine::itemHasReccurence(const QOrganizerItem& oi)
 
1613
{
 
1614
    if (oi.type() == QOrganizerItemType::TypeEvent || oi.type() == QOrganizerItemType::TypeTodo) {
 
1615
        QOrganizerItemRecurrence recur = oi.detail(QOrganizerItemDetail::TypeRecurrence);
 
1616
        return !recur.recurrenceDates().isEmpty() || !recur.recurrenceRules().isEmpty();
 
1617
    }
 
1618
 
 
1619
    return false;
 
1620
}
 
1621
 
 
1622
 
 
1623
 
 
1624
 
 
1625
/*!
 
1626
    Returns the engine ID from the given item \a id.
 
1627
 
 
1628
    The caller does not take ownership of the pointer, and should not delete returned id or undefined
 
1629
    behavior may occur.
 
1630
 */
 
1631
const QOrganizerItemEngineId *QOrganizerManagerEngine::engineItemId(const QOrganizerItemId &id)
 
1632
{
 
1633
    return id.d.data();
 
1634
}
 
1635
 
 
1636
/*!
 
1637
    Returns the engine ID from the given collection \a id.
 
1638
 
 
1639
    The caller does not take ownership of the pointer, and should not delete returned id or undefined
 
1640
    behavior may occur.
 
1641
 */
 
1642
const QOrganizerCollectionEngineId* QOrganizerManagerEngine::engineCollectionId(const QOrganizerCollectionId& id)
 
1643
{
 
1644
    return id.d.data();
 
1645
}
 
1646
 
 
1647
/*!
 
1648
    This function is called when the given \a request has been destroyed by the client.
 
1649
 
 
1650
    When this function is called, it means for the backend:
 
1651
    \list
 
1652
    \li The client doesn't care about the request any more. The engine can still complete it, but
 
1653
       completion is not required.
 
1654
    \li It can't reliably access any properties of the request pointer any more. The pointer will
 
1655
       be invalid once this function returns.
 
1656
    \endlist
 
1657
 
 
1658
    Note that since the \a request may run in another thread, this function should be blocked until
 
1659
    the worker thread gets fully notified.
 
1660
 */
 
1661
void QOrganizerManagerEngine::requestDestroyed(QOrganizerAbstractRequest *request)
 
1662
{
 
1663
    Q_UNUSED(request);
 
1664
}
 
1665
 
 
1666
/*!
 
1667
    This function is called when the client tries to start the given asynchronous \a request. Returns
 
1668
    true if the request is started successfully, or false otherwise.
 
1669
 
 
1670
    Note that the request is supposed to run in an asynchronous manner that this function should
 
1671
    return as soon as possible. Therefore, it the operation would last sometime, a worker thread
 
1672
    should be used to queue and process the request. In such cases, backend should be aware that
 
1673
    the request may be deleted by the client, and requestDestroyed() function will be called.
 
1674
 */
 
1675
bool QOrganizerManagerEngine::startRequest(QOrganizerAbstractRequest* request)
 
1676
{
 
1677
    Q_UNUSED(request);
 
1678
    return false;
 
1679
}
 
1680
 
 
1681
/*!
 
1682
    This function is called when the client tries to cancel the given asynchronous \a request. Returns
 
1683
    true if the request is calcelled successfully, or false otherwise.
 
1684
 */
 
1685
bool QOrganizerManagerEngine::cancelRequest(QOrganizerAbstractRequest* request)
 
1686
{
 
1687
    Q_UNUSED(request);
 
1688
    return false;
 
1689
}
 
1690
 
 
1691
/*!
 
1692
    This function is called when the client wants to be blocked until the given \a request is completed,
 
1693
    or until \a msecs milliseconds have passed. Returns true when the request is completed, or false
 
1694
    otherwise.
 
1695
 */
 
1696
bool QOrganizerManagerEngine::waitForRequestFinished(QOrganizerAbstractRequest* request, int msecs)
 
1697
{
 
1698
    Q_UNUSED(request);
 
1699
    Q_UNUSED(msecs);
 
1700
    return false;
 
1701
}
 
1702
 
 
1703
/*!
 
1704
  Updates the given asynchronous request \a req by setting the new \a state
 
1705
  of the request.  If the new state is different, the stateChanged() signal
 
1706
  will be emitted by the request.
 
1707
 */
 
1708
void QOrganizerManagerEngine::updateRequestState(QOrganizerAbstractRequest* req, QOrganizerAbstractRequest::State state)
 
1709
{
 
1710
    if (req) {
 
1711
        QMutexLocker ml(&req->d_ptr->m_mutex);
 
1712
        if (req->d_ptr->m_state != state) {
 
1713
            req->d_ptr->m_state = state;
 
1714
            ml.unlock();
 
1715
            emit req->stateChanged(state);
 
1716
        }
 
1717
    }
 
1718
}
 
1719
 
 
1720
/*!
 
1721
  Updates the given QOrganizerItemOccurrenceFetchRequest \a req with the latest results \a result, and operation error \a error.
 
1722
  In addition, the state of the request will be changed to \a newState.
 
1723
 
 
1724
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1725
 
 
1726
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1727
 */
 
1728
void QOrganizerManagerEngine::updateItemOccurrenceFetchRequest(QOrganizerItemOccurrenceFetchRequest* req, const QList<QOrganizerItem>& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState)
 
1729
{
 
1730
    if (req) {
 
1731
        QPointer<QOrganizerItemOccurrenceFetchRequest> ireq(req); // Take this in case the first emit deletes us
 
1732
        QOrganizerItemOccurrenceFetchRequestPrivate* rd = static_cast<QOrganizerItemOccurrenceFetchRequestPrivate*>(req->d_ptr);
 
1733
        QMutexLocker ml(&rd->m_mutex);
 
1734
        bool emitState = rd->m_state != newState;
 
1735
        rd->m_organizeritems = result;
 
1736
        rd->m_error = error;
 
1737
        rd->m_state = newState;
 
1738
        ml.unlock();
 
1739
        emit ireq.data()->resultsAvailable();
 
1740
        if (emitState && ireq)
 
1741
            emit ireq.data()->stateChanged(newState);
 
1742
    }
 
1743
}
 
1744
 
 
1745
/*!
 
1746
  Updates the given QOrganizerItemIdFetchRequest \a req with the latest results \a result, and operation error \a error.
 
1747
  In addition, the state of the request will be changed to \a newState.
 
1748
 
 
1749
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1750
 
 
1751
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1752
 */
 
1753
void QOrganizerManagerEngine::updateItemIdFetchRequest(QOrganizerItemIdFetchRequest* req, const QList<QOrganizerItemId>& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState)
 
1754
{
 
1755
    if (req) {
 
1756
        QPointer<QOrganizerItemIdFetchRequest> ireq(req); // Take this in case the first emit deletes us
 
1757
        QOrganizerItemIdFetchRequestPrivate* rd = static_cast<QOrganizerItemIdFetchRequestPrivate*>(ireq.data()->d_ptr);
 
1758
        QMutexLocker ml(&rd->m_mutex);
 
1759
        bool emitState = rd->m_state != newState;
 
1760
        rd->m_ids = result;
 
1761
        rd->m_error = error;
 
1762
        rd->m_state = newState;
 
1763
        ml.unlock();
 
1764
        emit ireq.data()->resultsAvailable();
 
1765
        if (emitState && ireq)
 
1766
            emit ireq.data()->stateChanged(newState);
 
1767
    }
 
1768
}
 
1769
 
 
1770
/*!
 
1771
    Updates the given QOrganizerItemFetchByIdRequest \a req with the latest results \a result, and
 
1772
    operation error \a error, and map of input index to individual error \a errorMap. In addition,
 
1773
    the state of the request will be changed to \a newState.
 
1774
 
 
1775
    It then causes the request to emit its resultsAvailable() signal to notify clients of the request
 
1776
    progress.
 
1777
 
 
1778
    If the new request state is different from the previous state, the stateChanged() signal will
 
1779
    also be emitted from the request.
 
1780
 */
 
1781
void QOrganizerManagerEngine::updateItemFetchByIdRequest(QOrganizerItemFetchByIdRequest *req, const QList<QOrganizerItem> &result, QOrganizerManager::Error error, const QMap<int, QOrganizerManager::Error> &errorMap, QOrganizerAbstractRequest::State newState)
 
1782
{
 
1783
    if (req) {
 
1784
        QPointer<QOrganizerItemFetchByIdRequest> ireq(req); // Take this in case the first emit deletes us
 
1785
        QOrganizerItemFetchByIdRequestPrivate *rd = static_cast<QOrganizerItemFetchByIdRequestPrivate *>(ireq.data()->d_ptr);
 
1786
        QMutexLocker ml(&rd->m_mutex);
 
1787
        bool emitState = rd->m_state != newState;
 
1788
        rd->m_items = result;
 
1789
        rd->m_errors = errorMap;
 
1790
        rd->m_error = error;
 
1791
        rd->m_state = newState;
 
1792
        ml.unlock();
 
1793
        emit ireq.data()->resultsAvailable();
 
1794
        if (emitState && ireq)
 
1795
            emit ireq.data()->stateChanged(newState);
 
1796
    }
 
1797
}
 
1798
 
 
1799
/*!
 
1800
  Updates the given QOrganizerItemFetchRequest \a req with the latest results \a result, and operation error \a error.
 
1801
  In addition, the state of the request will be changed to \a newState.
 
1802
 
 
1803
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1804
 
 
1805
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1806
 */
 
1807
void QOrganizerManagerEngine::updateItemFetchRequest(QOrganizerItemFetchRequest* req, const QList<QOrganizerItem>& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState)
 
1808
{
 
1809
    if (req) {
 
1810
        QPointer<QOrganizerItemFetchRequest> ireq(req); // Take this in case the first emit deletes us
 
1811
        QOrganizerItemFetchRequestPrivate* rd = static_cast<QOrganizerItemFetchRequestPrivate*>(ireq.data()->d_ptr);
 
1812
        QMutexLocker ml(&rd->m_mutex);
 
1813
        bool emitState = rd->m_state != newState;
 
1814
        rd->m_organizeritems = result;
 
1815
        rd->m_error = error;
 
1816
        rd->m_state = newState;
 
1817
        ml.unlock();
 
1818
        emit ireq.data()->resultsAvailable();
 
1819
        if (emitState && ireq)
 
1820
            emit ireq.data()->stateChanged(newState);
 
1821
    }
 
1822
}
 
1823
 
 
1824
/*!
 
1825
  Updates the given QOrganizerItemFetchForExportRequest \a req with the latest results \a result, and operation error \a error.
 
1826
  In addition, the state of the request will be changed to \a newState.
 
1827
 
 
1828
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1829
 
 
1830
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1831
 */
 
1832
void QOrganizerManagerEngine::updateItemFetchForExportRequest(QOrganizerItemFetchForExportRequest* req, const QList<QOrganizerItem>& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState)
 
1833
{
 
1834
    if (req) {
 
1835
        QPointer<QOrganizerItemFetchForExportRequest> ireq(req); // Take this in case the first emit deletes us
 
1836
        QOrganizerItemFetchForExportRequestPrivate* rd = static_cast<QOrganizerItemFetchForExportRequestPrivate*>(ireq.data()->d_ptr);
 
1837
        QMutexLocker ml(&rd->m_mutex);
 
1838
        bool emitState = rd->m_state != newState;
 
1839
        rd->m_organizeritems = result;
 
1840
        rd->m_error = error;
 
1841
        rd->m_state = newState;
 
1842
        ml.unlock();
 
1843
        emit ireq.data()->resultsAvailable();
 
1844
        if (emitState && ireq)
 
1845
            emit ireq.data()->stateChanged(newState);
 
1846
    }
 
1847
}
 
1848
 
 
1849
/*!
 
1850
  Updates the given QOrganizerItemRemoveRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap.
 
1851
  In addition, the state of the request will be changed to \a newState.
 
1852
 
 
1853
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1854
 
 
1855
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1856
 */
 
1857
void QOrganizerManagerEngine::updateItemRemoveRequest(QOrganizerItemRemoveRequest* req, QOrganizerManager::Error error, const QMap<int, QOrganizerManager::Error>& errorMap, QOrganizerAbstractRequest::State newState)
 
1858
{
 
1859
    if (req) {
 
1860
        QPointer<QOrganizerItemRemoveRequest> ireq(req); // Take this in case the first emit deletes us
 
1861
        QOrganizerItemRemoveRequestPrivate* rd = static_cast<QOrganizerItemRemoveRequestPrivate*>(ireq.data()->d_ptr);
 
1862
        QMutexLocker ml(&rd->m_mutex);
 
1863
        bool emitState = rd->m_state != newState;
 
1864
        rd->m_errors = errorMap;
 
1865
        rd->m_error = error;
 
1866
        rd->m_state = newState;
 
1867
        ml.unlock();
 
1868
        emit ireq.data()->resultsAvailable();
 
1869
        if (emitState && ireq)
 
1870
            emit ireq.data()->stateChanged(newState);
 
1871
    }
 
1872
}
 
1873
 
 
1874
/*!
 
1875
  Updates the given QOrganizerItemRemoveByIdRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap.
 
1876
  In addition, the state of the request will be changed to \a newState.
 
1877
 
 
1878
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1879
 
 
1880
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1881
 */
 
1882
void QOrganizerManagerEngine::updateItemRemoveByIdRequest(QOrganizerItemRemoveByIdRequest* req, QOrganizerManager::Error error, const QMap<int, QOrganizerManager::Error>& errorMap, QOrganizerAbstractRequest::State newState)
 
1883
{
 
1884
    if (req) {
 
1885
        QPointer<QOrganizerItemRemoveByIdRequest> ireq(req); // Take this in case the first emit deletes us
 
1886
        QOrganizerItemRemoveByIdRequestPrivate* rd = static_cast<QOrganizerItemRemoveByIdRequestPrivate*>(ireq.data()->d_ptr);
 
1887
        QMutexLocker ml(&rd->m_mutex);
 
1888
        bool emitState = rd->m_state != newState;
 
1889
        rd->m_errors = errorMap;
 
1890
        rd->m_error = error;
 
1891
        rd->m_state = newState;
 
1892
        ml.unlock();
 
1893
        emit ireq.data()->resultsAvailable();
 
1894
        if (emitState && ireq)
 
1895
            emit ireq.data()->stateChanged(newState);
 
1896
    }
 
1897
}
 
1898
 
 
1899
/*!
 
1900
  Updates the given QOrganizerItemSaveRequest \a req with the latest results \a result, operation error \a error, and map of input index to individual error \a errorMap.
 
1901
  In addition, the state of the request will be changed to \a newState.
 
1902
 
 
1903
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1904
 
 
1905
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1906
 */
 
1907
void QOrganizerManagerEngine::updateItemSaveRequest(QOrganizerItemSaveRequest* req, const QList<QOrganizerItem>& result, QOrganizerManager::Error error, const QMap<int, QOrganizerManager::Error>& errorMap, QOrganizerAbstractRequest::State newState)
 
1908
{
 
1909
    if (req) {
 
1910
        QPointer<QOrganizerItemSaveRequest> ireq(req); // Take this in case the first emit deletes us
 
1911
        QOrganizerItemSaveRequestPrivate* rd = static_cast<QOrganizerItemSaveRequestPrivate*>(ireq.data()->d_ptr);
 
1912
        QMutexLocker ml(&rd->m_mutex);
 
1913
        bool emitState = rd->m_state != newState;
 
1914
        rd->m_organizeritems = result;
 
1915
        rd->m_errors = errorMap;
 
1916
        rd->m_error = error;
 
1917
        rd->m_state = newState;
 
1918
        ml.unlock();
 
1919
        emit ireq.data()->resultsAvailable();
 
1920
        if (emitState && ireq)
 
1921
            emit ireq.data()->stateChanged(newState);
 
1922
    }
 
1923
}
 
1924
 
 
1925
/*!
 
1926
  Updates the given QOrganizerCollectionFetchRequest \a req with the latest results \a result and an operation error \a error.
 
1927
  In addition, the state of the request will be changed to \a newState.
 
1928
 
 
1929
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1930
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1931
 */
 
1932
void QOrganizerManagerEngine::updateCollectionFetchRequest(QOrganizerCollectionFetchRequest* req, const QList<QOrganizerCollection>& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState)
 
1933
{
 
1934
    if (req) {
 
1935
        QPointer<QOrganizerCollectionFetchRequest> ireq(req); // Take this in case the first emit deletes us
 
1936
        QOrganizerCollectionFetchRequestPrivate* rd = static_cast<QOrganizerCollectionFetchRequestPrivate*>(ireq.data()->d_ptr);
 
1937
        QMutexLocker ml(&rd->m_mutex);
 
1938
        bool emitState = rd->m_state != newState;
 
1939
        rd->m_collections = result;
 
1940
        rd->m_error = error;
 
1941
        rd->m_state = newState;
 
1942
        ml.unlock();
 
1943
        emit ireq.data()->resultsAvailable();
 
1944
        if (emitState && ireq)
 
1945
            emit ireq.data()->stateChanged(newState);
 
1946
    }
 
1947
}
 
1948
 
 
1949
/*!
 
1950
  Updates the given QOrganizerCollectionRemoveRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap.
 
1951
  In addition, the state of the request will be changed to \a newState.
 
1952
 
 
1953
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1954
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1955
 */
 
1956
void QOrganizerManagerEngine::updateCollectionRemoveRequest(QOrganizerCollectionRemoveRequest* req, QOrganizerManager::Error error, const QMap<int, QOrganizerManager::Error>& errorMap, QOrganizerAbstractRequest::State newState)
 
1957
{
 
1958
    if (req) {
 
1959
        QPointer<QOrganizerCollectionRemoveRequest> ireq(req); // Take this in case the first emit deletes us
 
1960
        QOrganizerCollectionRemoveRequestPrivate* rd = static_cast<QOrganizerCollectionRemoveRequestPrivate*>(ireq.data()->d_ptr);
 
1961
        QMutexLocker ml(&rd->m_mutex);
 
1962
        bool emitState = rd->m_state != newState;
 
1963
        rd->m_errors = errorMap;
 
1964
        rd->m_error = error;
 
1965
        rd->m_state = newState;
 
1966
        ml.unlock();
 
1967
        emit ireq.data()->resultsAvailable();
 
1968
        if (emitState && ireq)
 
1969
            emit ireq.data()->stateChanged(newState);
 
1970
    }
 
1971
}
 
1972
 
 
1973
/*!
 
1974
  Updates the given QOrganizerCollectionSaveRequest \a req with the latest results \a result, operation error \a error, and map of input index to individual error \a errorMap.
 
1975
  In addition, the state of the request will be changed to \a newState.
 
1976
 
 
1977
  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
 
1978
  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
 
1979
 */
 
1980
void QOrganizerManagerEngine::updateCollectionSaveRequest(QOrganizerCollectionSaveRequest* req, const QList<QOrganizerCollection>& result, QOrganizerManager::Error error, const QMap<int, QOrganizerManager::Error>& errorMap, QOrganizerAbstractRequest::State newState)
 
1981
{
 
1982
    if (req) {
 
1983
        QPointer<QOrganizerCollectionSaveRequest> ireq(req); // Take this in case the first emit deletes us
 
1984
        QOrganizerCollectionSaveRequestPrivate* rd = static_cast<QOrganizerCollectionSaveRequestPrivate*>(ireq.data()->d_ptr);
 
1985
        QMutexLocker ml(&rd->m_mutex);
 
1986
        bool emitState = rd->m_state != newState;
 
1987
        rd->m_collections = result;
 
1988
        rd->m_errors = errorMap;
 
1989
        rd->m_error = error;
 
1990
        rd->m_state = newState;
 
1991
        ml.unlock();
 
1992
        emit ireq.data()->resultsAvailable();
 
1993
        if (emitState && ireq)
 
1994
            emit ireq.data()->stateChanged(newState);
 
1995
    }
 
1996
}
 
1997
 
 
1998
#include "moc_qorganizermanagerengine.cpp"
 
1999
 
 
2000
QT_END_NAMESPACE_ORGANIZER