| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2015 The Qt Company Ltd. | 
| 4 | ** Contact: http://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtOrganizer module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL21$ | 
| 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 The Qt Company. For licensing terms | 
| 14 | ** and conditions see http://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free | 
| 20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and | 
| 21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the | 
| 22 | ** following information to ensure the GNU Lesser General Public License | 
| 23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and | 
| 24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | 
| 25 | ** | 
| 26 | ** As a special exception, The Qt Company gives you certain additional | 
| 27 | ** rights. These rights are described in The Qt Company LGPL Exception | 
| 28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | 
| 29 | ** | 
| 30 | ** $QT_END_LICENSE$ | 
| 31 | ** | 
| 32 | ****************************************************************************/ | 
| 33 |  | 
| 34 | #include "qorganizeritemmemorybackend_p.h" | 
| 35 |  | 
| 36 | #include <QtOrganizer/qorganizeritemrecurrence.h> | 
| 37 | #include <QtOrganizer/qorganizeritems.h> | 
| 38 | #include <QtOrganizer/qorganizeritemdetails.h> | 
| 39 | #include <QtOrganizer/qorganizeritemfilters.h> | 
| 40 | #include <QtOrganizer/qorganizeritemrequests.h> | 
| 41 |  | 
| 42 | #ifndef QT_NO_DEBUG_STREAM | 
| 43 | #include <QtCore/qdebug.h> | 
| 44 | #endif | 
| 45 | #include <QtCore/qstringbuilder.h> | 
| 46 | #include <QtCore/quuid.h> | 
| 47 |  | 
| 48 | QT_BEGIN_NAMESPACE_ORGANIZER | 
| 49 |  | 
| 50 | QOrganizerManagerEngine* QOrganizerItemMemoryFactory::engine(const QMap<QString, QString>& parameters, QOrganizerManager::Error* error) | 
| 51 | { | 
| 52 |     Q_UNUSED(error); | 
| 53 |  | 
| 54 |     QOrganizerItemMemoryEngine *ret = QOrganizerItemMemoryEngine::createMemoryEngine(parameters); | 
| 55 |     return ret; | 
| 56 | } | 
| 57 |  | 
| 58 | QString QOrganizerItemMemoryFactory::managerName() const | 
| 59 | { | 
| 60 |     return QString::fromLatin1(str: "memory" ); | 
| 61 | } | 
| 62 |  | 
| 63 | /*! | 
| 64 |   \class QOrganizerItemMemoryEngine | 
| 65 |   \brief The QOrganizerItemMemoryEngine class provides an in-memory implementation | 
| 66 |   of an organizer item backend. | 
| 67 |   \inmodule QtOrganizer | 
| 68 |   \internal | 
| 69 |  | 
| 70 |   It may be used as a reference implementation, or when persistent storage is not required. | 
| 71 |  | 
| 72 |   During construction, it will load the in-memory data associated with the memory store | 
| 73 |   identified by the "id" parameter from the given parameters if it exists, or a new, | 
| 74 |   anonymous store if it does not. | 
| 75 |  | 
| 76 |   Data stored in this engine is only available in the current process. | 
| 77 |  | 
| 78 |   This engine supports sharing, so an internal reference count is increased | 
| 79 |   whenever a manager uses this backend, and is decreased when the manager | 
| 80 |   no longer requires this engine. | 
| 81 |  */ | 
| 82 |  | 
| 83 | typedef QHash<QString, QOrganizerItemMemoryEngineData *> EngineDatas; | 
| 84 | Q_GLOBAL_STATIC(EngineDatas, theEngineDatas); | 
| 85 |  | 
| 86 | /*! Constructor of a QOrganizerItemMemoryEngineData object | 
| 87 | */ | 
| 88 | QOrganizerItemMemoryEngineData::QOrganizerItemMemoryEngineData() | 
| 89 |     : QSharedData(), | 
| 90 |     m_nextOrganizerItemId(1), | 
| 91 |     m_nextOrganizerCollectionId(2) | 
| 92 | { | 
| 93 |  | 
| 94 | } | 
| 95 |  | 
| 96 | /*! | 
| 97 |  * Factory function for creating a new in-memory backend, based | 
| 98 |  * on the given \a parameters. | 
| 99 |  * | 
| 100 |  * The same engine will be returned for multiple calls with the | 
| 101 |  * same value for the "id" parameter, while one of them is in scope. | 
| 102 |  */ | 
| 103 | QOrganizerItemMemoryEngine* QOrganizerItemMemoryEngine::createMemoryEngine(const QMap<QString, QString>& parameters) | 
| 104 | { | 
| 105 |     QString idValue = parameters.value(QStringLiteral("id" )); | 
| 106 |  | 
| 107 |     EngineDatas &engineDatas = *theEngineDatas(); | 
| 108 |     QOrganizerItemMemoryEngineData* data = engineDatas.value(akey: idValue); | 
| 109 |     if (!data) { | 
| 110 |         data = new QOrganizerItemMemoryEngineData(); | 
| 111 |         // no store given?  new, anonymous store. | 
| 112 |         if (!idValue.isEmpty()) { | 
| 113 |             data->m_id = idValue; | 
| 114 |             engineDatas.insert(akey: idValue, avalue: data); | 
| 115 |         } | 
| 116 |     } | 
| 117 |     data->ref.ref(); | 
| 118 |     return new QOrganizerItemMemoryEngine(data); | 
| 119 | } | 
| 120 |  | 
| 121 | /*! | 
| 122 |  * Constructs a new in-memory backend which shares the given \a data with | 
| 123 |  * other shared memory engines. | 
| 124 |  */ | 
| 125 | QOrganizerItemMemoryEngine::QOrganizerItemMemoryEngine(QOrganizerItemMemoryEngineData* data) | 
| 126 |     : d(data) | 
| 127 | { | 
| 128 |     d->m_sharedEngines.append(t: this); | 
| 129 |  | 
| 130 |     // the default collection always exists. | 
| 131 |     if (d->m_idToCollectionHash.isEmpty()) { | 
| 132 |         d->m_managerUri = managerUri(); | 
| 133 |         const QOrganizerCollectionId defaultId = defaultCollectionId(); | 
| 134 |         QOrganizerCollection defaultCollection; | 
| 135 |         defaultCollection.setId(defaultId); | 
| 136 |         defaultCollection.setMetaData(key: QOrganizerCollection::KeyName, value: QString(QStringLiteral("Default Collection" ))); | 
| 137 |         d->m_idToCollectionHash.insert(akey: defaultId, avalue: defaultCollection); | 
| 138 |     } | 
| 139 | } | 
| 140 |  | 
| 141 | /*! Frees any memory used by this engine | 
| 142 | */ | 
| 143 | QOrganizerItemMemoryEngine::~QOrganizerItemMemoryEngine() | 
| 144 | { | 
| 145 |     d->m_sharedEngines.removeAll(t: this); | 
| 146 |     if (!d->ref.deref()) { | 
| 147 |         if (!d->m_id.isEmpty()) { | 
| 148 |             EngineDatas &engineDatas = *theEngineDatas(); | 
| 149 |             engineDatas.remove(akey: d->m_id); | 
| 150 |         } | 
| 151 |         delete d; | 
| 152 |     } | 
| 153 | } | 
| 154 |  | 
| 155 | /*! \reimp | 
| 156 | */ | 
| 157 | QString QOrganizerItemMemoryEngine::managerName() const | 
| 158 | { | 
| 159 |     return QStringLiteral("memory" ); | 
| 160 | } | 
| 161 |  | 
| 162 | /*! \reimp | 
| 163 | */ | 
| 164 | QMap<QString, QString> QOrganizerItemMemoryEngine::managerParameters() const | 
| 165 | { | 
| 166 |     QMap<QString, QString> params; | 
| 167 |     params.insert(QStringLiteral("id" ), avalue: d->m_id); | 
| 168 |     return params; | 
| 169 | } | 
| 170 |  | 
| 171 | /*! \reimp | 
| 172 | */ | 
| 173 | QMap<QString, QString> QOrganizerItemMemoryEngine::idInterpretationParameters() const | 
| 174 | { | 
| 175 |     return managerParameters(); | 
| 176 | } | 
| 177 |  | 
| 178 | QList<QOrganizerItem> QOrganizerItemMemoryEngine::items(const QList<QOrganizerItemId> &itemIds, const QOrganizerItemFetchHint &fetchHint, | 
| 179 |                                                         QMap<int, QOrganizerManager::Error> *errorMap, QOrganizerManager::Error *error) | 
| 180 | { | 
| 181 |     Q_UNUSED(fetchHint) | 
| 182 |  | 
| 183 |     QList<QOrganizerItem> items; | 
| 184 |     items.reserve(alloc: itemIds.size()); | 
| 185 |     QOrganizerItem tmp; | 
| 186 |     for (int i = 0; i < itemIds.size(); ++i) { | 
| 187 |         tmp = item(organizeritemId: itemIds.at(i)); | 
| 188 |         items.append(t: tmp); | 
| 189 |         if (tmp.isEmpty()) | 
| 190 |             errorMap->insert(akey: i, avalue: QOrganizerManager::DoesNotExistError); | 
| 191 |     } | 
| 192 |     *error = errorMap->isEmpty() ? QOrganizerManager::NoError : QOrganizerManager::DoesNotExistError; | 
| 193 |     return items; | 
| 194 | } | 
| 195 |  | 
| 196 | QList<QOrganizerItemId> QOrganizerItemMemoryEngine::itemIds(const QOrganizerItemFilter &filter, | 
| 197 |                                                             const QDateTime &startDateTime, | 
| 198 |                                                             const QDateTime &endDateTime, | 
| 199 |                                                             const QList<QOrganizerItemSortOrder> &sortOrders, | 
| 200 |                                                             QOrganizerManager::Error *error) | 
| 201 | { | 
| 202 |     if (startDateTime.isNull() && endDateTime.isNull() && filter.type() == QOrganizerItemFilter::DefaultFilter && sortOrders.count() == 0) | 
| 203 |         return d->m_idToItemHash.keys(); | 
| 204 |     else | 
| 205 |         return QOrganizerManager::extractIds(items: itemsForExport(startDateTime, endDateTime, filter, sortOrders, fetchHint: QOrganizerItemFetchHint(), error)); | 
| 206 | } | 
| 207 |  | 
| 208 | QList<QOrganizerItem> QOrganizerItemMemoryEngine::internalItemOccurrences(const QOrganizerItem& parentItem, const QDateTime& periodStart, const QDateTime& periodEnd, int maxCount, bool includeExceptions, bool sortItems, QList<QDate> *exceptionDates, QOrganizerManager::Error* error) const | 
| 209 | { | 
| 210 |     // given the generating item, grab it's QOrganizerItemRecurrence detail (if it exists), and calculate all of the dates within the given period. | 
| 211 |     // how would a real backend do this? | 
| 212 |     // Also, should this also return the exception instances (ie, return any persistent instances with parent information == parent item?) | 
| 213 |     // XXX TODO: in detail validation, ensure that the referenced parent Id exists... | 
| 214 |  | 
| 215 |     QDateTime realPeriodStart(periodStart); | 
| 216 |     QDateTime realPeriodEnd(periodEnd); | 
| 217 |     QDateTime initialDateTime; | 
| 218 |     if (parentItem.type() == QOrganizerItemType::TypeEvent) { | 
| 219 |         QOrganizerEvent evt = parentItem; | 
| 220 |         initialDateTime = evt.startDateTime().isValid() ? evt.startDateTime() : evt.endDateTime(); | 
| 221 |     } else if (parentItem.type() == QOrganizerItemType::TypeTodo) { | 
| 222 |         QOrganizerTodo todo = parentItem; | 
| 223 |         initialDateTime = todo.startDateTime().isValid() ? todo.startDateTime() : todo.dueDateTime(); | 
| 224 |     } else { | 
| 225 |         // erm... not a recurring item in our schema... | 
| 226 |         return QList<QOrganizerItem>(); | 
| 227 |     } | 
| 228 |  | 
| 229 |     if (realPeriodStart.isValid() && initialDateTime.isValid()) { | 
| 230 |         if (initialDateTime > realPeriodStart) | 
| 231 |             realPeriodStart = initialDateTime; | 
| 232 |     } else if (initialDateTime.isValid()) { | 
| 233 |         realPeriodStart = initialDateTime; | 
| 234 |     } | 
| 235 |  | 
| 236 |     if (!periodEnd.isValid()) { | 
| 237 |         // If no endDateTime is given, we'll only generate items that occur within the next 4 years of realPeriodStart. | 
| 238 |         realPeriodEnd.setDate(realPeriodStart.date().addDays(days: 1461)); | 
| 239 |         realPeriodEnd.setTime(realPeriodStart.time()); | 
| 240 |     } | 
| 241 |     if (realPeriodStart > realPeriodEnd) { | 
| 242 |         *error = QOrganizerManager::BadArgumentError; | 
| 243 |         return QList<QOrganizerItem>(); | 
| 244 |     } | 
| 245 |  | 
| 246 |     QList<QOrganizerItem> retn; | 
| 247 |     QList<QOrganizerItem> xoccurrences; | 
| 248 |     QOrganizerItemRecurrence recur = parentItem.detail(detailType: QOrganizerItemDetail::TypeRecurrence); | 
| 249 |  | 
| 250 |     if (includeExceptions) { | 
| 251 |         // first, retrieve all persisted instances (exceptions) which occur between the specified datetimes. | 
| 252 |         foreach (const QOrganizerItem& item, d->m_idToItemHash) { | 
| 253 |             if (item.detail(detailType: QOrganizerItemDetail::TypeParent).value<QOrganizerItemId>(field: QOrganizerItemParent::FieldParentId) == parentItem.id()) { | 
| 254 |                 QDateTime lowerBound; | 
| 255 |                 QDateTime upperBound; | 
| 256 |                 if (item.type() == QOrganizerItemType::TypeEventOccurrence) { | 
| 257 |                     QOrganizerEventOccurrence instance = item; | 
| 258 |                     lowerBound = instance.startDateTime(); | 
| 259 |                     upperBound = instance.endDateTime(); | 
| 260 |                 } else { | 
| 261 |                     QOrganizerTodoOccurrence instance = item; | 
| 262 |                     lowerBound = instance.startDateTime(); | 
| 263 |                     upperBound = instance.dueDateTime(); | 
| 264 |                 } | 
| 265 |  | 
| 266 |                 if ((lowerBound.isNull() || lowerBound >= realPeriodStart) && (upperBound.isNull() || upperBound <= realPeriodEnd)) { | 
| 267 |                     // this occurrence fulfils the criteria. | 
| 268 |                     xoccurrences.append(t: item); | 
| 269 |                 } | 
| 270 |             } | 
| 271 |         } | 
| 272 |     } | 
| 273 |  | 
| 274 |     // then, generate the required (unchanged) instances from the parentItem. | 
| 275 |     // before doing that, we have to find out all of the exception dates. | 
| 276 |     QList<QDate> xdates; | 
| 277 |     foreach (const QDate& xdate, recur.exceptionDates()) { | 
| 278 |         xdates += xdate; | 
| 279 |     } | 
| 280 |     if (realPeriodStart.isValid()) { | 
| 281 |         // Dates are interpreted as local time, but realPeriodStart is UTC | 
| 282 |         const QDate localStartDate(realPeriodStart.toLocalTime().date()); | 
| 283 |         QSet<QOrganizerRecurrenceRule> xrules = recur.exceptionRules(); | 
| 284 |         foreach (const QOrganizerRecurrenceRule& xrule, xrules) { | 
| 285 |             if (xrule.frequency() != QOrganizerRecurrenceRule::Invalid | 
| 286 |                     && ((xrule.limitType() != QOrganizerRecurrenceRule::DateLimit) || (xrule.limitDate() >= localStartDate))) { | 
| 287 |                 // we cannot skip it, since it applies in the given time period. | 
| 288 |                 QList<QDateTime> xdatetimes = generateDateTimes(initialDateTime, rrule: xrule, periodStart: realPeriodStart, periodEnd: realPeriodEnd, maxCount: 50); // max count of 50 is arbitrary... | 
| 289 |                 foreach (const QDateTime& xdatetime, xdatetimes) | 
| 290 |                     xdates += xdatetime.toLocalTime().date(); | 
| 291 |             } | 
| 292 |         } | 
| 293 |     } | 
| 294 |     // now generate a list of rdates (from the recurrenceDates and recurrenceRules) | 
| 295 |  | 
| 296 |     // QMap is used for storing dates, because we don't want to have duplicate dates and | 
| 297 |     // we want to have dates sorted | 
| 298 |     // Only key of the map is relevant (QDateTime), the value (int) is not used | 
| 299 |     QMap<QDateTime, int> rdateMap; | 
| 300 |     foreach (const QDate& rdate, recur.recurrenceDates()) { | 
| 301 |         QDateTime dt(initialDateTime.toLocalTime()); | 
| 302 |         dt.setDate(rdate); | 
| 303 |         rdateMap.insert(akey: dt.toUTC(), avalue: 0); | 
| 304 |     } | 
| 305 |  | 
| 306 |     if (realPeriodStart.isValid()) { | 
| 307 |         const QDate localStartDate(realPeriodStart.toLocalTime().date()); | 
| 308 |         QSet<QOrganizerRecurrenceRule> rrules = recur.recurrenceRules(); | 
| 309 |         foreach (const QOrganizerRecurrenceRule& rrule, rrules) { | 
| 310 |             if (rrule.frequency() != QOrganizerRecurrenceRule::Invalid | 
| 311 |                     && ((rrule.limitType() != QOrganizerRecurrenceRule::DateLimit) || (rrule.limitDate() >= localStartDate))) { | 
| 312 |                 // we cannot skip it, since it applies in the given time period. | 
| 313 |                 QList<QDateTime> rdatetimes = generateDateTimes(initialDateTime, rrule, periodStart: realPeriodStart, periodEnd: realPeriodEnd, maxCount: 50); // max count of 50 is arbitrary... | 
| 314 |                 foreach (const QDateTime& rdatetime, rdatetimes) | 
| 315 |                     rdateMap.insert(akey: rdatetime, avalue: 0); | 
| 316 |             } | 
| 317 |         } | 
| 318 |     } | 
| 319 |     // now order the contents of retn by date | 
| 320 |     QList<QDateTime> rdates = rdateMap.keys(); | 
| 321 |  | 
| 322 |     if (initialDateTime.isValid() && !recur.recurrenceDates().isEmpty() && qBinaryFind(container: rdates, value: initialDateTime) == rdates.constEnd()) { | 
| 323 |         rdates.prepend(t: initialDateTime); | 
| 324 |     } | 
| 325 |  | 
| 326 |     // now for each rdate which isn't also an xdate | 
| 327 |     foreach (const QDateTime& rdate, rdates) { | 
| 328 |         if (rdate >= realPeriodStart && rdate <= realPeriodEnd) { | 
| 329 |             const QDate localRDate(rdate.toLocalTime().date()); | 
| 330 |             if (!xdates.contains(t: localRDate)) { | 
| 331 |                 // generate the required instance and add it to the return list. | 
| 332 |                 retn.append(t: QOrganizerManagerEngine::generateOccurrence(parentItem, rdate)); | 
| 333 |             } else if (includeExceptions) { | 
| 334 |                 for (int i = 0; i < xoccurrences.size(); i++) { | 
| 335 |                     QOrganizerItemParent parentDetail = xoccurrences[i].detail(detailType: QOrganizerItemDetail::TypeParent); | 
| 336 |                     if (parentDetail.originalDate() == localRDate) | 
| 337 |                         retn.append(t: xoccurrences[i]); | 
| 338 |                 } | 
| 339 |             } else if (exceptionDates) { | 
| 340 |                 exceptionDates->append(t: localRDate); | 
| 341 |             } | 
| 342 |         } | 
| 343 |     } | 
| 344 |  | 
| 345 |     if (sortItems) { | 
| 346 |         // should we always sort if a maxCount is given? | 
| 347 |         QMultiMap<QDateTime, QOrganizerItem> defaultSorted; | 
| 348 |         foreach (QOrganizerItem item, retn) | 
| 349 |             QOrganizerManagerEngine::addDefaultSorted(defaultSorted: &defaultSorted, toAdd: item); | 
| 350 |  | 
| 351 |         retn = defaultSorted.values(); | 
| 352 |     } | 
| 353 |  | 
| 354 |     // and return the first maxCount entries. | 
| 355 |     return retn.mid(pos: 0, alength: maxCount); | 
| 356 | } | 
| 357 |  | 
| 358 | QList<QOrganizerItem> QOrganizerItemMemoryEngine::itemOccurrences(const QOrganizerItem &parentItem, | 
| 359 |                                                                   const QDateTime &startDateTime, | 
| 360 |                                                                   const QDateTime &endDateTime, int maxCount, | 
| 361 |                                                                   const QOrganizerItemFetchHint &fetchHint, | 
| 362 |                                                                   QOrganizerManager::Error *error) | 
| 363 | { | 
| 364 |     Q_UNUSED(fetchHint); | 
| 365 |     return internalItemOccurrences(parentItem, periodStart: startDateTime, periodEnd: endDateTime, maxCount, includeExceptions: true, sortItems: true, exceptionDates: 0, error); | 
| 366 | } | 
| 367 |  | 
| 368 | QList<QOrganizerItem> QOrganizerItemMemoryEngine::items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, | 
| 369 |                                                         const QDateTime &endDateTime, int maxCount, | 
| 370 |                                                         const QList<QOrganizerItemSortOrder> &sortOrders, | 
| 371 |                                                         const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) | 
| 372 | { | 
| 373 |     QList<QOrganizerItem> list; | 
| 374 |     if (sortOrders.size() > 0) { | 
| 375 |         list = internalItems(startDate: startDateTime, endDate: endDateTime, filter, sortOrders, fetchHint, error, forExport: false); | 
| 376 |     } else { | 
| 377 |         QOrganizerItemSortOrder sortOrder; | 
| 378 |         sortOrder.setDetail(detailType: QOrganizerItemDetail::TypeEventTime, field: QOrganizerEventTime::FieldStartDateTime); | 
| 379 |         sortOrder.setDirection(Qt::AscendingOrder); | 
| 380 |  | 
| 381 |         QList<QOrganizerItemSortOrder> sortOrders; | 
| 382 |         sortOrders.append(t: sortOrder); | 
| 383 |  | 
| 384 |         sortOrder.setDetail(detailType: QOrganizerItemDetail::TypeTodoTime, field: QOrganizerTodoTime::FieldStartDateTime); | 
| 385 |         sortOrders.append(t: sortOrder); | 
| 386 |  | 
| 387 |         sortOrder.setDetail(detailType: QOrganizerItemDetail::TypeTodoTime, field: QOrganizerTodoTime::FieldStartDateTime); | 
| 388 |         sortOrders.append(t: sortOrder); | 
| 389 |  | 
| 390 |         list = internalItems(startDate: startDateTime, endDate: endDateTime, filter, sortOrders, fetchHint, error, forExport: false); | 
| 391 |     } | 
| 392 |  | 
| 393 |     if (maxCount < 0) | 
| 394 |         return list; | 
| 395 |     else | 
| 396 |         return list.mid(pos: 0, alength: maxCount); | 
| 397 | } | 
| 398 |  | 
| 399 | QList<QOrganizerItem> QOrganizerItemMemoryEngine::itemsForExport(const QDateTime &startDateTime, | 
| 400 |                                                                  const QDateTime &endDateTime, | 
| 401 |                                                                  const QOrganizerItemFilter &filter, | 
| 402 |                                                                  const QList<QOrganizerItemSortOrder> &sortOrders, | 
| 403 |                                                                  const QOrganizerItemFetchHint &fetchHint, | 
| 404 |                                                                  QOrganizerManager::Error *error) | 
| 405 | { | 
| 406 |     return internalItems(startDate: startDateTime, endDate: endDateTime, filter, sortOrders, fetchHint, error, forExport: true); | 
| 407 | } | 
| 408 |  | 
| 409 | QList<QOrganizerItem> QOrganizerItemMemoryEngine::itemsForExport(const QList<QOrganizerItemId> &ids, const QOrganizerItemFetchHint &fetchHint, QMap<int, QOrganizerManager::Error> *errorMap, QOrganizerManager::Error *error) | 
| 410 | { | 
| 411 |     QOrganizerItemIdFilter filter; | 
| 412 |     filter.setIds(ids); | 
| 413 |  | 
| 414 |     QList<QOrganizerItem> unsorted = itemsForExport(startDateTime: QDateTime(), endDateTime: QDateTime(), filter, sortOrders: QOrganizerItemSortOrder(), fetchHint, error); | 
| 415 |  | 
| 416 |     // Build an index into the results | 
| 417 |     QHash<QOrganizerItemId, int> idMap; // value is index into unsorted | 
| 418 |     if (*error == QOrganizerManager::NoError) { | 
| 419 |         for (int i = 0; i < unsorted.size(); i++) { | 
| 420 |             idMap.insert(akey: unsorted[i].id(), avalue: i); | 
| 421 |         } | 
| 422 |     } | 
| 423 |  | 
| 424 |     // Build up the results and errors | 
| 425 |     QList<QOrganizerItem> results; | 
| 426 |     for (int i = 0; i < ids.count(); i++) { | 
| 427 |         QOrganizerItemId id(ids[i]); | 
| 428 |         if (!idMap.contains(akey: id)) { | 
| 429 |             if (errorMap) | 
| 430 |                 errorMap->insert(akey: i, avalue: QOrganizerManager::DoesNotExistError); | 
| 431 |             if (*error == QOrganizerManager::NoError) | 
| 432 |                 *error = QOrganizerManager::DoesNotExistError; | 
| 433 |             results.append(t: QOrganizerItem()); | 
| 434 |         } else { | 
| 435 |             results.append(t: unsorted[idMap[id]]); | 
| 436 |         } | 
| 437 |     } | 
| 438 |  | 
| 439 |     return results; | 
| 440 | } | 
| 441 |  | 
| 442 | QOrganizerItem QOrganizerItemMemoryEngine::item(const QOrganizerItemId& organizeritemId) const | 
| 443 | { | 
| 444 |     return d->m_idToItemHash.value(akey: organizeritemId); | 
| 445 | } | 
| 446 |  | 
| 447 | QList<QOrganizerItem> QOrganizerItemMemoryEngine::internalItems(const QDateTime& startDate, const QDateTime& endDate, const QOrganizerItemFilter& filter, const QList<QOrganizerItemSortOrder>& sortOrders, const QOrganizerItemFetchHint& fetchHint, QOrganizerManager::Error* error, bool forExport) const | 
| 448 | { | 
| 449 |     Q_UNUSED(fetchHint); // no optimisations are possible in the memory backend; ignore the fetch hint. | 
| 450 |     Q_UNUSED(error); | 
| 451 |  | 
| 452 |     QList<QOrganizerItem> sorted; | 
| 453 |     QSet<QOrganizerItemId> parentsAdded; | 
| 454 |     bool isDefFilter = (filter.type() == QOrganizerItemFilter::DefaultFilter); | 
| 455 |  | 
| 456 |     foreach(const QOrganizerItem& c, d->m_idToItemHash) { | 
| 457 |         if (itemHasReccurence(oi: c)) { | 
| 458 |             addItemRecurrences(sorted, c, startDate, endDate, filter, sortOrders, forExport, parentsAdded: &parentsAdded); | 
| 459 |         } else { | 
| 460 |             if ((isDefFilter || QOrganizerManagerEngine::testFilter(filter, item: c)) && QOrganizerManagerEngine::isItemBetweenDates(item: c, startPeriod: startDate, endPeriod: endDate)) { | 
| 461 |                 QOrganizerManagerEngine::addSorted(sorted: &sorted,toAdd: c, sortOrders); | 
| 462 |                 if (forExport | 
| 463 |                         && (c.type() == QOrganizerItemType::TypeEventOccurrence | 
| 464 |                         ||  c.type() == QOrganizerItemType::TypeTodoOccurrence)) { | 
| 465 |                     QOrganizerItemId parentId(c.detail(detailType: QOrganizerItemDetail::TypeParent).value<QOrganizerItemId>(field: QOrganizerItemParent::FieldParentId)); | 
| 466 |                     if (!parentsAdded.contains(value: parentId)) { | 
| 467 |                         parentsAdded.insert(value: parentId); | 
| 468 |                         QOrganizerManagerEngine::addSorted(sorted: &sorted, toAdd: item(organizeritemId: parentId), sortOrders); | 
| 469 |                     } | 
| 470 |                 } | 
| 471 |             } | 
| 472 |         } | 
| 473 |     } | 
| 474 |  | 
| 475 |     return sorted; | 
| 476 | } | 
| 477 |  | 
| 478 |  | 
| 479 | void QOrganizerItemMemoryEngine::addItemRecurrences(QList<QOrganizerItem>& sorted, const QOrganizerItem& c, const QDateTime& startDate, const QDateTime& endDate, const QOrganizerItemFilter& filter, const QList<QOrganizerItemSortOrder>& sortOrders, bool forExport, QSet<QOrganizerItemId>* parentsAdded) const | 
| 480 | { | 
| 481 |     QOrganizerManager::Error error = QOrganizerManager::NoError; | 
| 482 |     if (forExport && parentsAdded->contains(value: c.id())) | 
| 483 |         return; | 
| 484 |  | 
| 485 |     QList<QOrganizerItem> recItems = internalItemOccurrences(parentItem: c, periodStart: startDate, periodEnd: endDate, maxCount: forExport ? 1 : 50, includeExceptions: false, sortItems: false, exceptionDates: 0, error: &error); // XXX TODO: why maxcount of 50? | 
| 486 |     if (filter.type() == QOrganizerItemFilter::DefaultFilter) { | 
| 487 |         foreach(const QOrganizerItem& oi, recItems) { | 
| 488 |             QOrganizerManagerEngine::addSorted(sorted: &sorted, toAdd: forExport ? c : oi, sortOrders); | 
| 489 |             if (forExport) | 
| 490 |                 parentsAdded->insert(value: c.id()); | 
| 491 |         } | 
| 492 |     } else { | 
| 493 |         foreach(const QOrganizerItem& oi, recItems) { | 
| 494 |             if (QOrganizerManagerEngine::testFilter(filter, item: oi)) { | 
| 495 |                 QOrganizerManagerEngine::addSorted(sorted: &sorted, toAdd: forExport ? c : oi, sortOrders); | 
| 496 |                 if (forExport) | 
| 497 |                     parentsAdded->insert(value: c.id()); | 
| 498 |             } | 
| 499 |         } | 
| 500 |     } | 
| 501 | } | 
| 502 |  | 
| 503 | /*! Saves the given organizeritem \a theOrganizerItem, storing any error to \a error and | 
| 504 |     filling the \a changeSet with ids of changed organizeritems as required */ | 
| 505 | bool QOrganizerItemMemoryEngine::storeItem(QOrganizerItem* theOrganizerItem, QOrganizerItemChangeSet& changeSet, const QList<QOrganizerItemDetail::DetailType> &detailMask, QOrganizerManager::Error* error) | 
| 506 | { | 
| 507 |     QOrganizerCollectionId targetCollectionId = theOrganizerItem->collectionId(); | 
| 508 |  | 
| 509 |     // check that the collection exists (or is null :. default collection): | 
| 510 |     if (!targetCollectionId.isNull() && !d->m_idToCollectionHash.contains(akey: targetCollectionId)) { | 
| 511 |         *error = QOrganizerManager::InvalidCollectionError; | 
| 512 |         return false; | 
| 513 |     } | 
| 514 |  | 
| 515 |     if (theOrganizerItem->type() == QOrganizerItemType::TypeUndefined) { | 
| 516 |         *error = QOrganizerManager::InvalidItemTypeError; | 
| 517 |         return false; | 
| 518 |     } | 
| 519 |  | 
| 520 |     // check to see if this organizer item already exists | 
| 521 |     QOrganizerItemId theOrganizerItemId = theOrganizerItem->id(); | 
| 522 |     QHash<QOrganizerItemId, QOrganizerItem>::const_iterator hashIterator = d->m_idToItemHash.find(akey: theOrganizerItemId); | 
| 523 |     if (hashIterator != d->m_idToItemHash.constEnd()) { | 
| 524 |         /* We also need to check that there are no modified create only details */ | 
| 525 |         QOrganizerItem oldOrganizerItem = hashIterator.value(); | 
| 526 |  | 
| 527 |         if (oldOrganizerItem.type() != theOrganizerItem->type()) { | 
| 528 |             *error = QOrganizerManager::AlreadyExistsError; | 
| 529 |             return false; | 
| 530 |         } | 
| 531 |  | 
| 532 |         // check that the old and new collection is the same (ie, not attempting to save to a different collection) | 
| 533 |         if (targetCollectionId.isNull()) { | 
| 534 |             // it already exists, so save it where it already exists. | 
| 535 |             targetCollectionId = d->m_itemsInCollectionsHash.key(avalue: theOrganizerItemId); | 
| 536 |         } else if (!d->m_itemsInCollectionsHash.values(akey: targetCollectionId).contains(t: theOrganizerItemId)) { | 
| 537 |             // the given collection id was non-null but doesn't already contain this item.  error. | 
| 538 |             *error = QOrganizerManager::InvalidCollectionError; | 
| 539 |             return false; | 
| 540 |         } | 
| 541 |  | 
| 542 |         QOrganizerItemTimestamp ts = theOrganizerItem->detail(detailType: QOrganizerItemDetail::TypeTimestamp); | 
| 543 |         ts.setLastModified(QDateTime::currentDateTime()); | 
| 544 |         theOrganizerItem->saveDetail(detail: &ts); | 
| 545 |  | 
| 546 |         if (!fixOccurrenceReferences(item: theOrganizerItem, error)) { | 
| 547 |             return false; | 
| 548 |         } | 
| 549 |         // Looks ok, so continue | 
| 550 |         d->m_idToItemHash.insert(akey: theOrganizerItemId, avalue: *theOrganizerItem); // replacement insert. | 
| 551 |         changeSet.insertChangedItem(itemId: theOrganizerItemId, typesChanged: detailMask); | 
| 552 |  | 
| 553 |         // cross-check if stored exception occurrences are still valid | 
| 554 |         if (itemHasReccurence(oi: oldOrganizerItem)) { | 
| 555 |             // if we are updating an existing item and the item had recurrence defined, it might | 
| 556 |             // have some exception occurrences which don't match the current recurrence. | 
| 557 |  | 
| 558 |             // should we also check and remove exception dates if e.g. limit date or limit count has been changed | 
| 559 |             // to be earlier (or smaller) than before thus invelidating an exception date..? | 
| 560 |             QList<QOrganizerItemId> occurrenceIds = d->m_parentIdToChildIdHash.values(akey: theOrganizerItemId); | 
| 561 |             QOrganizerManager::Error occurrenceError = QOrganizerManager::NoError; | 
| 562 |             if (!occurrenceIds.isEmpty()) { | 
| 563 |                 if (itemHasReccurence(oi: *theOrganizerItem)) { | 
| 564 |                     // generate occurrences to get the dates when there can be an exception occurrence | 
| 565 |                     // if the new item does not have recurrence, all exception occurrences of this item | 
| 566 |                     // are removed | 
| 567 |                     QList<QDate> exceptionDates; | 
| 568 |                     QList<QOrganizerItem> occurrences = internalItemOccurrences(parentItem: *theOrganizerItem, periodStart: QDateTime(), periodEnd: QDateTime(), maxCount: -1, includeExceptions: false, sortItems: false, exceptionDates: &exceptionDates, error: &occurrenceError); | 
| 569 |                     foreach (const QOrganizerItemId &occurrenceId, occurrenceIds) { | 
| 570 |                         // remove all occurrence ids from the list which have valid exception date | 
| 571 |                         QOrganizerItemParent parentDetail = d->m_idToItemHash.value(akey: occurrenceId).detail(detailType: QOrganizerItemDetail::TypeParent); | 
| 572 |                         if (!parentDetail.isEmpty() && exceptionDates.contains(t: parentDetail.originalDate())) | 
| 573 |                             occurrenceIds.removeOne(t: occurrenceId); | 
| 574 |                     } | 
| 575 |                 } | 
| 576 |                 foreach (const QOrganizerItemId &occurrenceId, occurrenceIds) | 
| 577 |                     removeItem(organizeritemId: occurrenceId, changeSet, error: &occurrenceError); | 
| 578 |             } | 
| 579 |         } | 
| 580 |     } else { | 
| 581 |         // id does not exist; if not zero, fail. | 
| 582 |         if (!theOrganizerItemId.isNull()) { | 
| 583 |             // the ID is not empty, and it doesn't identify an existing organizer item in our database either. | 
| 584 |             *error = QOrganizerManager::DoesNotExistError; | 
| 585 |             return false; | 
| 586 |         } | 
| 587 |         /* New organizer item */ | 
| 588 |         QOrganizerItemTimestamp ts = theOrganizerItem->detail(detailType: QOrganizerItemDetail::TypeTimestamp); | 
| 589 |         ts.setLastModified(QDateTime::currentDateTime()); | 
| 590 |         ts.setCreated(ts.lastModified()); | 
| 591 |         theOrganizerItem->saveDetail(detail: &ts); | 
| 592 |  | 
| 593 |         if (!fixOccurrenceReferences(item: theOrganizerItem, error)) { | 
| 594 |             return false; | 
| 595 |         } | 
| 596 |         // set the guid if not set | 
| 597 |         if (theOrganizerItem->guid().isEmpty()) | 
| 598 |             theOrganizerItem->setGuid(QUuid::createUuid().toString()); | 
| 599 |  | 
| 600 |         // if we're saving an exception occurrence, we need to add it's original date as an exdate to the parent. | 
| 601 |         QOrganizerItemId parentId; | 
| 602 |         if (theOrganizerItem->type() == QOrganizerItemType::TypeEventOccurrence | 
| 603 |             || theOrganizerItem->type() == QOrganizerItemType::TypeTodoOccurrence) { | 
| 604 |             // update the event or the todo by adding an EX-DATE which corresponds to the original date of the occurrence being saved. | 
| 605 |             QOrganizerItemParent origin = theOrganizerItem->detail(detailType: QOrganizerItemDetail::TypeParent); | 
| 606 |             parentId = origin.parentId(); | 
| 607 |  | 
| 608 |             // for occurrences, if given a null collection id, save it in the same collection as the parent. | 
| 609 |             // otherwise, ensure that the parent is in the same collection.  You cannot save an exception to a different collection than the parent. | 
| 610 |             if (targetCollectionId.isNull()) { | 
| 611 |                 targetCollectionId = d->m_itemsInCollectionsHash.key(avalue: parentId); | 
| 612 |                 if (targetCollectionId.isNull()) { | 
| 613 |                     *error = QOrganizerManager::UnspecifiedError; // this should never occur; parent should _always_ be in a collection. | 
| 614 |                     return false; | 
| 615 |                 } | 
| 616 |             } else if (!d->m_itemsInCollectionsHash.values(akey: targetCollectionId).contains(t: parentId)) { | 
| 617 |                 // nope, the specified collection doesn't contain the parent.  error. | 
| 618 |                 *error = QOrganizerManager::InvalidCollectionError; | 
| 619 |                 return false; | 
| 620 |             } | 
| 621 |  | 
| 622 |             QMap<int, QOrganizerManager::Error> tempErrorMap; | 
| 623 |             QOrganizerManager::Error tempError = QOrganizerManager::NoError; | 
| 624 |             const QList<QOrganizerItem> candidates = items(itemIds: QList<QOrganizerItemId>() << parentId, fetchHint: QOrganizerItemFetchHint(), errorMap: &tempErrorMap, error: &tempError); | 
| 625 |             if (tempError != QOrganizerManager::NoError || candidates.isEmpty()) { | 
| 626 |                 *error = tempError != QOrganizerManager::NoError ? tempError : QOrganizerManager::UnspecifiedError; | 
| 627 |                 return false; | 
| 628 |             } | 
| 629 |             QOrganizerItem parentItem = candidates.first(); | 
| 630 |             QDate originalDate = origin.originalDate(); | 
| 631 |             QOrganizerItemRecurrence recurrence = parentItem.detail(detailType: QOrganizerItemDetail::TypeRecurrence); | 
| 632 |             QSet<QDate> currentExceptionDates = recurrence.exceptionDates(); | 
| 633 |             if (!currentExceptionDates.contains(value: originalDate)) { | 
| 634 |                 currentExceptionDates << originalDate; | 
| 635 |                 recurrence.setExceptionDates(currentExceptionDates); | 
| 636 |                 parentItem.saveDetail(detail: &recurrence); | 
| 637 |                 d->m_idToItemHash.insert(akey: parentId, avalue: parentItem); // replacement insert | 
| 638 |                 changeSet.insertChangedItem(itemId: parentId, typesChanged: detailMask); // is this correct?  it's an exception, so change parent? | 
| 639 |             } | 
| 640 |         } | 
| 641 |  | 
| 642 |         // if target collection id is null, set to default id. | 
| 643 |         if (targetCollectionId.isNull()) | 
| 644 |             targetCollectionId = defaultCollectionId(); | 
| 645 |  | 
| 646 |         // update the organizer item - set its ID | 
| 647 |         theOrganizerItemId = itemId(localId: QByteArray(reinterpret_cast<const char *>(&d->m_nextOrganizerItemId), sizeof(quint32))); | 
| 648 |         ++(d->m_nextOrganizerItemId); | 
| 649 |         theOrganizerItem->setId(theOrganizerItemId); | 
| 650 |         // finally, add the organizer item to our internal lists and return | 
| 651 |         theOrganizerItem->setCollectionId(targetCollectionId); | 
| 652 |         d->m_idToItemHash.insert(akey: theOrganizerItemId, avalue: *theOrganizerItem);  // add organizer item to hash | 
| 653 |         if (!parentId.isNull()) { | 
| 654 |             // if it was an occurrence, we need to add it to the children hash. | 
| 655 |             d->m_parentIdToChildIdHash.insert(akey: parentId, avalue: theOrganizerItemId); | 
| 656 |         } | 
| 657 |         d->m_itemsInCollectionsHash.insert(akey: targetCollectionId, avalue: theOrganizerItemId); | 
| 658 |         changeSet.insertAddedItem(itemId: theOrganizerItemId); | 
| 659 |     } | 
| 660 |  | 
| 661 |     *error = QOrganizerManager::NoError;     // successful. | 
| 662 |     return true; | 
| 663 | } | 
| 664 |  | 
| 665 | /*! | 
| 666 |  * For Occurrence type items, ensure the ParentId and the Guid are set consistently.  Returns | 
| 667 |  * false and sets \a error on error, returns true otherwise. | 
| 668 |  */ | 
| 669 | bool QOrganizerItemMemoryEngine::fixOccurrenceReferences(QOrganizerItem* theItem, QOrganizerManager::Error* error) | 
| 670 | { | 
| 671 |     if (theItem->type() == QOrganizerItemType::TypeEventOccurrence | 
| 672 |             || theItem->type() == QOrganizerItemType::TypeTodoOccurrence) { | 
| 673 |         const QString guid = theItem->guid(); | 
| 674 |         QOrganizerItemParent instanceOrigin = theItem->detail(detailType: QOrganizerItemDetail::TypeParent); | 
| 675 |         if (!instanceOrigin.originalDate().isValid()) { | 
| 676 |             *error = QOrganizerManager::InvalidOccurrenceError; | 
| 677 |             return false; | 
| 678 |         } | 
| 679 |         QOrganizerItemId parentId = instanceOrigin.parentId(); | 
| 680 |         if (!guid.isEmpty()) { | 
| 681 |             if (!parentId.isNull()) { | 
| 682 |                 QOrganizerManager::Error tempError; | 
| 683 |                 QMap<int, QOrganizerManager::Error> tempErrorMap; | 
| 684 |                 QOrganizerItem parentItem = items(itemIds: QList<QOrganizerItemId>() << parentId, fetchHint: QOrganizerItemFetchHint(), errorMap: &tempErrorMap, error: &tempError).at(i: 0); | 
| 685 |                 if (guid != parentItem.guid() | 
| 686 |                         || !typesAreRelated(occurrenceType: theItem->type(), parentType: parentItem.type())) { | 
| 687 |                     // parentId and guid are both set and inconsistent, or the parent is the wrong | 
| 688 |                     // type | 
| 689 |                     *error = QOrganizerManager::InvalidOccurrenceError; | 
| 690 |                     return false; | 
| 691 |                 } | 
| 692 |             } else { | 
| 693 |                 // guid set but not parentId | 
| 694 |                 // find an item with the given guid | 
| 695 |                 foreach (const QOrganizerItem& item, d->m_idToItemHash) { | 
| 696 |                     if (item.guid() == guid) { | 
| 697 |                         parentId = item.id(); | 
| 698 |                         break; | 
| 699 |                     } | 
| 700 |                 } | 
| 701 |                 if (parentId.isNull()) { | 
| 702 |                     // couldn't find an item with the given guid | 
| 703 |                     *error = QOrganizerManager::InvalidOccurrenceError; | 
| 704 |                     return false; | 
| 705 |                 } | 
| 706 |                 QOrganizerManager::Error tempError; | 
| 707 |                 QMap<int, QOrganizerManager::Error> tempErrorMap; | 
| 708 |                 QOrganizerItem parentItem = items(itemIds: QList<QOrganizerItemId>() << parentId, fetchHint: QOrganizerItemFetchHint(), errorMap: &tempErrorMap, error: &tempError).at(i: 0); | 
| 709 |                 if (!typesAreRelated(occurrenceType: theItem->type(), parentType: parentItem.type())) { | 
| 710 |                     // the parent is the wrong type | 
| 711 |                     *error = QOrganizerManager::InvalidOccurrenceError; | 
| 712 |                     return false; | 
| 713 |                 } | 
| 714 |                 // found a matching item - set the parentId of the occurrence | 
| 715 |                 QOrganizerItemParent origin = theItem->detail(detailType: QOrganizerItemDetail::TypeParent); | 
| 716 |                 origin.setParentId(parentId); | 
| 717 |                 theItem->saveDetail(detail: &origin); | 
| 718 |             } | 
| 719 |         } else if (!parentId.isNull()) { | 
| 720 |             QOrganizerManager::Error tempError; | 
| 721 |             QMap<int, QOrganizerManager::Error> tempErrorMap; | 
| 722 |             QOrganizerItem parentItem = items(itemIds: QList<QOrganizerItemId>() << parentId, fetchHint: QOrganizerItemFetchHint(), errorMap: &tempErrorMap, error: &tempError).at(i: 0); | 
| 723 |             if (parentItem.guid().isEmpty() | 
| 724 |                     || !typesAreRelated(occurrenceType: theItem->type(), parentType: parentItem.type())) { | 
| 725 |                 // found the matching item but it has no guid, or it isn't the right type | 
| 726 |                 *error = QOrganizerManager::InvalidOccurrenceError; | 
| 727 |                 return false; | 
| 728 |             } | 
| 729 |             theItem->setGuid(parentItem.guid()); | 
| 730 |         } else { | 
| 731 |             // neither parentId or guid is supplied | 
| 732 |             *error = QOrganizerManager::InvalidOccurrenceError; | 
| 733 |             return false; | 
| 734 |         } | 
| 735 |     } | 
| 736 |     return true; | 
| 737 | } | 
| 738 |  | 
| 739 | /*! | 
| 740 |  * Returns true if and only if \a occurrenceType is the "Occurrence" version of \a parentType. | 
| 741 |  */ | 
| 742 | bool QOrganizerItemMemoryEngine::typesAreRelated(QOrganizerItemType::ItemType occurrenceType, QOrganizerItemType::ItemType parentType) | 
| 743 | { | 
| 744 |     return ((parentType == QOrganizerItemType::TypeEvent | 
| 745 |                 && occurrenceType == QOrganizerItemType::TypeEventOccurrence) | 
| 746 |             || (parentType == QOrganizerItemType::TypeTodo | 
| 747 |                 && occurrenceType == QOrganizerItemType::TypeTodoOccurrence)); | 
| 748 | } | 
| 749 |  | 
| 750 | bool QOrganizerItemMemoryEngine::storeItems(QList<QOrganizerItem>* organizeritems, const QList<QOrganizerItemDetail::DetailType> &detailMask, | 
| 751 |                                             QMap<int, QOrganizerManager::Error>* errorMap, QOrganizerManager::Error* error) | 
| 752 | { | 
| 753 |     Q_ASSERT(errorMap); | 
| 754 |  | 
| 755 |     errorMap->clear(); | 
| 756 |  | 
| 757 |     if (!organizeritems) { | 
| 758 |         *error = QOrganizerManager::BadArgumentError; | 
| 759 |         return false; | 
| 760 |     } | 
| 761 |  | 
| 762 |     QOrganizerItemChangeSet changeSet; | 
| 763 |     QOrganizerItem current; | 
| 764 |     QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 765 |     for (int i = 0; i < organizeritems->count(); i++) { | 
| 766 |         current = organizeritems->at(i); | 
| 767 |         if (!storeItem(theOrganizerItem: ¤t, changeSet, detailMask, error)) { | 
| 768 |             operationError = *error; | 
| 769 |             errorMap->insert(akey: i, avalue: operationError); | 
| 770 |         } else { | 
| 771 |             (*organizeritems)[i] = current; | 
| 772 |         } | 
| 773 |     } | 
| 774 |  | 
| 775 |     *error = operationError; | 
| 776 |     d->emitSharedSignals(cs: &changeSet); | 
| 777 |     // return false if some error occurred | 
| 778 |     return (*error == QOrganizerManager::NoError); | 
| 779 | } | 
| 780 |  | 
| 781 | /*! \reimp | 
| 782 | */ | 
| 783 | bool QOrganizerItemMemoryEngine::saveItems(QList<QOrganizerItem> *items, const QList<QOrganizerItemDetail::DetailType> &detailMask, | 
| 784 |                                            QMap<int, QOrganizerManager::Error> *errorMap, QOrganizerManager::Error *error) | 
| 785 | { | 
| 786 |     // TODO should the default implementation do the right thing, or return false? | 
| 787 |     if (detailMask.isEmpty()) { | 
| 788 |         // Non partial, just pass it on | 
| 789 |         return storeItems(organizeritems: items, detailMask, errorMap, error); | 
| 790 |     } else { | 
| 791 |         // Partial item save. | 
| 792 |         // Basically | 
| 793 |  | 
| 794 |         // Need to: | 
| 795 |         // 1) fetch existing items | 
| 796 |         // 2) strip out details in definitionMask for existing items | 
| 797 |         // 3) copy the details from the passed in list for existing items | 
| 798 |         // 4) for any new items, copy the masked details to a blank item | 
| 799 |         // 5) save the modified ones | 
| 800 |         // 6) update the id of any new items | 
| 801 |         // 7) transfer any errors from saving to errorMap | 
| 802 |  | 
| 803 |         QList<QOrganizerItemId> existingItemIds; | 
| 804 |  | 
| 805 |         // Error conditions: | 
| 806 |         // 1) bad id passed in (can't fetch) | 
| 807 |         // 2) bad fetch (can't save partial update) | 
| 808 |         // 3) bad save error | 
| 809 |         // all of which needs to be returned in the error map | 
| 810 |  | 
| 811 |         QHash<int, int> existingIdMap; // items index to existingItems index | 
| 812 |  | 
| 813 |         // Try to figure out which of our arguments are new items | 
| 814 |         for (int i = 0; i < items->count(); i++) { | 
| 815 |             // See if there's a itemId that's not from this manager | 
| 816 |             const QOrganizerItem item = items->at(i); | 
| 817 |             if (item.id().managerUri() == managerUri()) { | 
| 818 |                 if (!item.id().isNull()) { | 
| 819 |                     existingIdMap.insert(akey: i, avalue: existingItemIds.count()); | 
| 820 |                     existingItemIds.append(t: item.id()); | 
| 821 |                 } else { | 
| 822 |                     // Strange. it's just a new item | 
| 823 |                 } | 
| 824 |             } else if (!item.id().managerUri().isEmpty() || !item.id().isNull()) { | 
| 825 |                 // Hmm, error (wrong manager) | 
| 826 |                 errorMap->insert(akey: i, avalue: QOrganizerManager::DoesNotExistError); | 
| 827 |             } // else new item | 
| 828 |         } | 
| 829 |  | 
| 830 |         // Now fetch the existing items | 
| 831 |         QMap<int, QOrganizerManager::Error> fetchErrors; | 
| 832 |         QOrganizerManager::Error fetchError = QOrganizerManager::NoError; | 
| 833 |         QList<QOrganizerItem> existingItems = this->itemsForExport(ids: existingItemIds, fetchHint: QOrganizerItemFetchHint(), errorMap: &fetchErrors, error: &fetchError); | 
| 834 |  | 
| 835 |         // Prepare the list to save | 
| 836 |         QList<QOrganizerItem> itemsToSave; | 
| 837 |         QList<int> savedToOriginalMap; // itemsToSave index to items index | 
| 838 |  | 
| 839 |         for (int i = 0; i < items->count(); i++) { | 
| 840 |             // See if this is an existing item or a new one | 
| 841 |             const int fetchedIdx = existingIdMap.value(akey: i, adefaultValue: -1); | 
| 842 |             QOrganizerItem itemToSave; | 
| 843 |             if (fetchedIdx >= 0) { | 
| 844 |                 // See if we had an error | 
| 845 |                 if (fetchErrors[fetchedIdx] != QOrganizerManager::NoError) { | 
| 846 |                     errorMap->insert(akey: i, avalue: fetchErrors[fetchedIdx]); | 
| 847 |                     continue; | 
| 848 |                 } | 
| 849 |  | 
| 850 |                 // Existing item we should have fetched | 
| 851 |                 itemToSave = existingItems.at(i: fetchedIdx); | 
| 852 |  | 
| 853 |                 // QOrganizerItemData::removeOnly() is not exported, so we can only do this... | 
| 854 |                 foreach (QOrganizerItemDetail::DetailType mask, detailMask) { | 
| 855 |                     QList<QOrganizerItemDetail> details(itemToSave.details(detailType: mask)); | 
| 856 |                     foreach (QOrganizerItemDetail detail, details) | 
| 857 |                         itemToSave.removeDetail(detail: &detail); | 
| 858 |                 } | 
| 859 |             } else if (errorMap->contains(akey: i)) { | 
| 860 |                 // A bad argument.  Leave it out of the itemsToSave list | 
| 861 |                 continue; | 
| 862 |             } else { | 
| 863 |                 // new item | 
| 864 |                 itemToSave.setType(items->at(i).type()); | 
| 865 |             } | 
| 866 |  | 
| 867 |             // Now copy in the details from the arguments | 
| 868 |             const QOrganizerItem& item = items->at(i); | 
| 869 |  | 
| 870 |             // Perhaps this could do this directly rather than through saveDetail | 
| 871 |             // but that would duplicate the checks for display label etc | 
| 872 |             foreach (QOrganizerItemDetail::DetailType name, detailMask) { | 
| 873 |                 QList<QOrganizerItemDetail> details = item.details(detailType: name); | 
| 874 |                 foreach (QOrganizerItemDetail detail, details) | 
| 875 |                     itemToSave.saveDetail(detail: &detail); | 
| 876 |             } | 
| 877 |             savedToOriginalMap.append(t: i); | 
| 878 |             itemsToSave.append(t: itemToSave); | 
| 879 |         } | 
| 880 |  | 
| 881 |         // Now save them | 
| 882 |         QMap<int, QOrganizerManager::Error> saveErrors; | 
| 883 |         QOrganizerManager::Error saveError = QOrganizerManager::NoError; | 
| 884 |         storeItems(organizeritems: &itemsToSave, detailMask, errorMap: &saveErrors, error: &saveError); | 
| 885 |         // Now update the passed in arguments, where necessary | 
| 886 |  | 
| 887 |         // Update IDs of the items list | 
| 888 |         for (int i = 0; i < itemsToSave.count(); i++) { | 
| 889 |             (*items)[savedToOriginalMap[i]].setId(itemsToSave[i].id()); | 
| 890 |         } | 
| 891 |         // Populate the errorMap with the errorMap of the attempted save | 
| 892 |         QMap<int, QOrganizerManager::Error>::iterator it(saveErrors.begin()); | 
| 893 |         while (it != saveErrors.end()) { | 
| 894 |             if (it.value() != QOrganizerManager::NoError) { | 
| 895 |                 errorMap->insert(akey: savedToOriginalMap[it.key()], avalue: it.value()); | 
| 896 |             } | 
| 897 |             it++; | 
| 898 |         } | 
| 899 |         return errorMap->isEmpty(); | 
| 900 |     } | 
| 901 | } | 
| 902 |  | 
| 903 | /*! Removes the organizer item identified by the given \a organizeritemId, storing any error to \a error and | 
| 904 |     filling the \a changeSet with ids of changed organizer items as required | 
| 905 | */ | 
| 906 | bool QOrganizerItemMemoryEngine::removeItem(const QOrganizerItemId& organizeritemId, QOrganizerItemChangeSet& changeSet, QOrganizerManager::Error* error) | 
| 907 | { | 
| 908 |     QHash<QOrganizerItemId, QOrganizerItem>::const_iterator hashIterator = d->m_idToItemHash.find(akey: organizeritemId); | 
| 909 |     if (hashIterator == d->m_idToItemHash.constEnd()) { | 
| 910 |         *error = QOrganizerManager::DoesNotExistError; | 
| 911 |         return false; | 
| 912 |     } | 
| 913 |  | 
| 914 |     // if it is a child item, remove itself from the children hash | 
| 915 |     QOrganizerItem thisItem = hashIterator.value(); | 
| 916 |     QOrganizerItemParent parentDetail = thisItem.detail(detailType: QOrganizerItemDetail::TypeParent); | 
| 917 |     if (!parentDetail.parentId().isNull()) { | 
| 918 |         d->m_parentIdToChildIdHash.remove(key: parentDetail.parentId(), value: organizeritemId); | 
| 919 |     } | 
| 920 |  | 
| 921 |     // if it is a parent item, remove any children. | 
| 922 |     QList<QOrganizerItemId> childrenIds = d->m_parentIdToChildIdHash.values(akey: organizeritemId); | 
| 923 |     foreach (const QOrganizerItemId& childId, childrenIds) { | 
| 924 |         // remove the child occurrence from our lists. | 
| 925 |         d->m_idToItemHash.remove(akey: childId); | 
| 926 |         d->m_itemsInCollectionsHash.remove(key: d->m_itemsInCollectionsHash.key(avalue: childId), value: childId); | 
| 927 |         changeSet.insertRemovedItem(itemId: childId); | 
| 928 |     } | 
| 929 |  | 
| 930 |     // remove the organizer item from the lists. | 
| 931 |     d->m_idToItemHash.remove(akey: organizeritemId); | 
| 932 |     d->m_parentIdToChildIdHash.remove(akey: organizeritemId); | 
| 933 |     d->m_itemsInCollectionsHash.remove(key: d->m_itemsInCollectionsHash.key(avalue: organizeritemId), value: organizeritemId); | 
| 934 |     *error = QOrganizerManager::NoError; | 
| 935 |  | 
| 936 |     changeSet.insertRemovedItem(itemId: organizeritemId); | 
| 937 |     return true; | 
| 938 | } | 
| 939 |  | 
| 940 | /*! Removes the organizer item occurrence identified by the given \a organizeritem. Removing a generated occurrence means | 
| 941 |     adding a new exception date to parent items exception date list. Stores any error to \a error and | 
| 942 |     fills the \a changeSet with ids of changed organizer items as required | 
| 943 | */ | 
| 944 | bool QOrganizerItemMemoryEngine::removeOccurrence(const QOrganizerItem &organizeritem, QOrganizerItemChangeSet &changeSet, QOrganizerManager::Error *error) | 
| 945 | { | 
| 946 |     QOrganizerItemParent parentDetail = organizeritem.detail(detailType: QOrganizerItemDetail::TypeParent); | 
| 947 |     if (parentDetail.parentId().isNull()) { | 
| 948 |         *error = QOrganizerManager::InvalidOccurrenceError; | 
| 949 |         return false; | 
| 950 |     } | 
| 951 |  | 
| 952 |     QHash<QOrganizerItemId, QOrganizerItem>::const_iterator hashIterator = d->m_idToItemHash.find(akey: parentDetail.parentId()); | 
| 953 |     if (hashIterator == d->m_idToItemHash.constEnd()) { | 
| 954 |         *error = QOrganizerManager::InvalidOccurrenceError; | 
| 955 |         return false; | 
| 956 |     } else { | 
| 957 |         QOrganizerItem parentItem = hashIterator.value(); | 
| 958 |         QOrganizerItemRecurrence recurrenceDetail = parentItem.detail(detailType: QOrganizerItemDetail::TypeRecurrence); | 
| 959 |         QSet<QDate> exceptionDates = recurrenceDetail.exceptionDates(); | 
| 960 |         exceptionDates.insert(value: parentDetail.originalDate()); | 
| 961 |         recurrenceDetail.setExceptionDates(exceptionDates); | 
| 962 |         parentItem.saveDetail(detail: &recurrenceDetail); | 
| 963 |         d->m_idToItemHash.insert(akey: parentDetail.parentId(), avalue: parentItem); | 
| 964 |         changeSet.insertChangedItem(itemId: parentDetail.parentId(), typesChanged: QList<QOrganizerItemDetail::DetailType>()); | 
| 965 |     } | 
| 966 |     *error = QOrganizerManager::NoError; | 
| 967 |     return true; | 
| 968 | } | 
| 969 |  | 
| 970 | /*! \reimp | 
| 971 | */ | 
| 972 | bool QOrganizerItemMemoryEngine::removeItems(const QList<QOrganizerItemId> &itemIds, QMap<int, QOrganizerManager::Error> *errorMap, | 
| 973 |                                              QOrganizerManager::Error *error) | 
| 974 | { | 
| 975 |     Q_ASSERT(errorMap); | 
| 976 |  | 
| 977 |     if (itemIds.count() == 0) { | 
| 978 |         *error = QOrganizerManager::BadArgumentError; | 
| 979 |         return false; | 
| 980 |     } | 
| 981 |  | 
| 982 |     QOrganizerItemChangeSet changeSet; | 
| 983 |     QOrganizerItemId current; | 
| 984 |     QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 985 |     for (int i = 0; i < itemIds.count(); i++) { | 
| 986 |         current = itemIds.at(i); | 
| 987 |         if (!removeItem(organizeritemId: current, changeSet, error)) { | 
| 988 |             operationError = *error; | 
| 989 |             errorMap->insert(akey: i, avalue: operationError); | 
| 990 |         } | 
| 991 |     } | 
| 992 |  | 
| 993 |     *error = operationError; | 
| 994 |     d->emitSharedSignals(cs: &changeSet); | 
| 995 |  | 
| 996 |     // return false if some errors occurred | 
| 997 |     return (*error == QOrganizerManager::NoError); | 
| 998 | } | 
| 999 |  | 
| 1000 | /*! \reimp | 
| 1001 | */ | 
| 1002 | bool QOrganizerItemMemoryEngine::removeItems(const QList<QOrganizerItem> *items, QMap<int, QOrganizerManager::Error> *errorMap, QOrganizerManager::Error *error) | 
| 1003 | { | 
| 1004 |     Q_ASSERT(errorMap); | 
| 1005 |     if (items->count() == 0) { | 
| 1006 |         *error = QOrganizerManager::BadArgumentError; | 
| 1007 |         return false; | 
| 1008 |     } | 
| 1009 |  | 
| 1010 |     QOrganizerItemChangeSet changeSet; | 
| 1011 |     QOrganizerItem current; | 
| 1012 |     QSet<QOrganizerItemId> removedParentIds; | 
| 1013 |     QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1014 |     for (int i = 0; i < items->count(); i++) { | 
| 1015 |         current = items->at(i); | 
| 1016 |         QOrganizerManager::Error tempError = QOrganizerManager::NoError; | 
| 1017 |         if ((current.type() == QOrganizerItemType::TypeEventOccurrence | 
| 1018 |              || current.type() == QOrganizerItemType::TypeTodoOccurrence) | 
| 1019 |                 && current.id().isNull()) { | 
| 1020 |             // this is a generated occurrence, modify parent items exception dates | 
| 1021 |             QOrganizerItemParent parentDetail = current.detail(detailType: QOrganizerItemDetail::TypeParent); | 
| 1022 |             if (removedParentIds.isEmpty() || !removedParentIds.contains(value: parentDetail.parentId())) | 
| 1023 |                 removeOccurrence(organizeritem: current, changeSet, error: &tempError); | 
| 1024 |         } else { | 
| 1025 |             removeItem(organizeritemId: current.id(), changeSet, error: &tempError); | 
| 1026 |             if (tempError == QOrganizerManager::NoError && itemHasReccurence(oi: current)) | 
| 1027 |                 removedParentIds.insert(value: current.id()); | 
| 1028 |         } | 
| 1029 |         if (tempError != QOrganizerManager::NoError) { | 
| 1030 |             errorMap->insert(akey: i, avalue: tempError); | 
| 1031 |             operationError = tempError; | 
| 1032 |         } | 
| 1033 |     } | 
| 1034 |  | 
| 1035 |     *error = operationError; | 
| 1036 |     d->emitSharedSignals(cs: &changeSet); | 
| 1037 |  | 
| 1038 |     // return false if some errors occurred | 
| 1039 |     return (*error == QOrganizerManager::NoError); | 
| 1040 | } | 
| 1041 |  | 
| 1042 | QOrganizerCollectionId QOrganizerItemMemoryEngine::defaultCollectionId() const | 
| 1043 | { | 
| 1044 |     const uint id(QOrganizerItemMemoryEngineData::DefaultCollectionLocalId); | 
| 1045 |     return collectionId(localId: QByteArray(reinterpret_cast<const char *>(&id), sizeof(uint))); | 
| 1046 | } | 
| 1047 |  | 
| 1048 | QOrganizerCollection QOrganizerItemMemoryEngine::collection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) | 
| 1049 | { | 
| 1050 |     if (d->m_idToCollectionHash.contains(akey: collectionId)) { | 
| 1051 |         *error = QOrganizerManager::NoError; | 
| 1052 |         return d->m_idToCollectionHash.value(akey: collectionId); | 
| 1053 |     } | 
| 1054 |  | 
| 1055 |     *error = QOrganizerManager::DoesNotExistError; | 
| 1056 |     return QOrganizerCollection(); | 
| 1057 | } | 
| 1058 |  | 
| 1059 | QList<QOrganizerCollection> QOrganizerItemMemoryEngine::collections(QOrganizerManager::Error* error) | 
| 1060 | { | 
| 1061 |     Q_ASSERT(!d->m_idToCollectionHash.isEmpty()); | 
| 1062 |     *error = QOrganizerManager::NoError; | 
| 1063 |     return d->m_idToCollectionHash.values(); | 
| 1064 | } | 
| 1065 |  | 
| 1066 | bool QOrganizerItemMemoryEngine::saveCollection(QOrganizerCollection* collection, QOrganizerManager::Error* error) | 
| 1067 | { | 
| 1068 |     QOrganizerCollectionId collectionId = collection->id(); | 
| 1069 |  | 
| 1070 |     QOrganizerCollectionChangeSet cs; | 
| 1071 |     if (d->m_idToCollectionHash.contains(akey: collectionId)) { | 
| 1072 |         // this collection already exists.  update our internal list | 
| 1073 |         // if the collection has been modified. | 
| 1074 |         if (d->m_idToCollectionHash.value(akey: collectionId) == *collection) { | 
| 1075 |             *error = QOrganizerManager::NoError; | 
| 1076 |             return true; | 
| 1077 |         } | 
| 1078 |  | 
| 1079 |         cs.insertChangedCollection(collectionId); | 
| 1080 |     } else { | 
| 1081 |         // this must be a new collection.  check that the id is null. | 
| 1082 |         if (!collectionId.isNull() && collectionId.managerUri() != d->m_managerUri) { | 
| 1083 |             // nope, this collection belongs in another manager, or has been deleted. | 
| 1084 |             *error = QOrganizerManager::DoesNotExistError; | 
| 1085 |             return false; | 
| 1086 |         } | 
| 1087 |  | 
| 1088 |         // this is a new collection with a null id; create a new id, add it to our list. | 
| 1089 |         collectionId = this->collectionId(localId: QByteArray(reinterpret_cast<const char *>(&d->m_nextOrganizerCollectionId), sizeof(quint32))); | 
| 1090 |         ++(d->m_nextOrganizerCollectionId); | 
| 1091 |         collection->setId(collectionId); | 
| 1092 |         cs.insertAddedCollection(collectionId); | 
| 1093 |     } | 
| 1094 |  | 
| 1095 |     d->m_idToCollectionHash.insert(akey: collectionId, avalue: *collection); | 
| 1096 |     d->emitSharedSignals(cs: &cs); | 
| 1097 |     *error = QOrganizerManager::NoError; | 
| 1098 |     return true; | 
| 1099 | } | 
| 1100 |  | 
| 1101 | bool QOrganizerItemMemoryEngine::removeCollection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) | 
| 1102 | { | 
| 1103 |     if (collectionId == defaultCollectionId()) { | 
| 1104 |         // attempting to remove the default collection.  this is not allowed in the memory engine. | 
| 1105 |         *error = QOrganizerManager::PermissionsError; | 
| 1106 |         return false; | 
| 1107 |     } | 
| 1108 |  | 
| 1109 |     // try to find the collection to remove it (and the items it contains) | 
| 1110 |     if (d->m_idToCollectionHash.contains(akey: collectionId)) { | 
| 1111 |         // found the collection to remove.  remove the items in the collection. | 
| 1112 |         const QList<QOrganizerItemId> itemsToRemove = d->m_itemsInCollectionsHash.values(akey: collectionId); | 
| 1113 |         if (!itemsToRemove.isEmpty()) { | 
| 1114 |             QMap<int, QOrganizerManager::Error> errorMap; | 
| 1115 |             if (!removeItems(itemIds: itemsToRemove, errorMap: &errorMap, error)) { | 
| 1116 |                 // without transaction support, we can't back out.  but the operation should fail. | 
| 1117 |                 return false; | 
| 1118 |             } | 
| 1119 |         } | 
| 1120 |  | 
| 1121 |         // now remove the collection from our lists. | 
| 1122 |         d->m_idToCollectionHash.remove(akey: collectionId); | 
| 1123 |         d->m_itemsInCollectionsHash.remove(akey: collectionId); | 
| 1124 |         QOrganizerCollectionChangeSet cs; | 
| 1125 |         cs.insertRemovedCollection(collectionId); | 
| 1126 |         d->emitSharedSignals(cs: &cs); | 
| 1127 |         *error = QOrganizerManager::NoError; | 
| 1128 |         return true; | 
| 1129 |     } | 
| 1130 |  | 
| 1131 |     // the collection doesn't exist... | 
| 1132 |     *error = QOrganizerManager::DoesNotExistError; | 
| 1133 |     return false; | 
| 1134 | } | 
| 1135 |  | 
| 1136 | /*! \reimp | 
| 1137 | */ | 
| 1138 | void QOrganizerItemMemoryEngine::requestDestroyed(QOrganizerAbstractRequest* req) | 
| 1139 | { | 
| 1140 |     Q_UNUSED(req); | 
| 1141 | } | 
| 1142 |  | 
| 1143 | /*! \reimp | 
| 1144 | */ | 
| 1145 | bool QOrganizerItemMemoryEngine::startRequest(QOrganizerAbstractRequest* req) | 
| 1146 | { | 
| 1147 |     updateRequestState(request: req, state: QOrganizerAbstractRequest::ActiveState); | 
| 1148 |     performAsynchronousOperation(request: req); | 
| 1149 |  | 
| 1150 |     return true; | 
| 1151 | } | 
| 1152 |  | 
| 1153 | /*! \reimp | 
| 1154 | */ | 
| 1155 | bool QOrganizerItemMemoryEngine::cancelRequest(QOrganizerAbstractRequest* req) | 
| 1156 | { | 
| 1157 |     Q_UNUSED(req); // we can't cancel since we complete immediately | 
| 1158 |     return false; | 
| 1159 | } | 
| 1160 |  | 
| 1161 | /*! \reimp | 
| 1162 | */ | 
| 1163 | bool QOrganizerItemMemoryEngine::waitForRequestFinished(QOrganizerAbstractRequest* req, int msecs) | 
| 1164 | { | 
| 1165 |     // in our implementation, we always complete any operation we start. | 
| 1166 |     Q_UNUSED(msecs); | 
| 1167 |     Q_UNUSED(req); | 
| 1168 |  | 
| 1169 |     return true; | 
| 1170 | } | 
| 1171 |  | 
| 1172 | QList<QOrganizerItemDetail::DetailType> QOrganizerItemMemoryEngine::supportedItemDetails(QOrganizerItemType::ItemType itemType) const | 
| 1173 | { | 
| 1174 |     QList<QOrganizerItemDetail::DetailType> supportedDetails; | 
| 1175 |     supportedDetails << QOrganizerItemDetail::TypeItemType | 
| 1176 |                      << QOrganizerItemDetail::TypeGuid | 
| 1177 |                      << QOrganizerItemDetail::TypeTimestamp | 
| 1178 |                      << QOrganizerItemDetail::TypeDisplayLabel | 
| 1179 |                      << QOrganizerItemDetail::TypeDescription | 
| 1180 |                      << QOrganizerItemDetail::TypeComment | 
| 1181 |                      << QOrganizerItemDetail::TypeTag | 
| 1182 |                      << QOrganizerItemDetail::TypeClassification | 
| 1183 |                      << QOrganizerItemDetail::TypeExtendedDetail; | 
| 1184 |  | 
| 1185 |     if (itemType == QOrganizerItemType::TypeEvent) { | 
| 1186 |         supportedDetails << QOrganizerItemDetail::TypeRecurrence | 
| 1187 |                          << QOrganizerItemDetail::TypeEventTime | 
| 1188 |                          << QOrganizerItemDetail::TypePriority | 
| 1189 |                          << QOrganizerItemDetail::TypeLocation | 
| 1190 |                          << QOrganizerItemDetail::TypeReminder | 
| 1191 |                          << QOrganizerItemDetail::TypeAudibleReminder | 
| 1192 |                          << QOrganizerItemDetail::TypeEmailReminder | 
| 1193 |                          << QOrganizerItemDetail::TypeVisualReminder; | 
| 1194 |     } else if (itemType == QOrganizerItemType::TypeTodo) { | 
| 1195 |         supportedDetails << QOrganizerItemDetail::TypeRecurrence | 
| 1196 |                          << QOrganizerItemDetail::TypeTodoTime | 
| 1197 |                          << QOrganizerItemDetail::TypePriority | 
| 1198 |                          << QOrganizerItemDetail::TypeTodoProgress | 
| 1199 |                          << QOrganizerItemDetail::TypeReminder | 
| 1200 |                          << QOrganizerItemDetail::TypeAudibleReminder | 
| 1201 |                          << QOrganizerItemDetail::TypeEmailReminder | 
| 1202 |                          << QOrganizerItemDetail::TypeVisualReminder; | 
| 1203 |     } else if (itemType == QOrganizerItemType::TypeEventOccurrence) { | 
| 1204 |         supportedDetails << QOrganizerItemDetail::TypeParent | 
| 1205 |                          << QOrganizerItemDetail::TypeEventTime | 
| 1206 |                          << QOrganizerItemDetail::TypePriority | 
| 1207 |                          << QOrganizerItemDetail::TypeLocation | 
| 1208 |                          << QOrganizerItemDetail::TypeReminder | 
| 1209 |                          << QOrganizerItemDetail::TypeAudibleReminder | 
| 1210 |                          << QOrganizerItemDetail::TypeEmailReminder | 
| 1211 |                          << QOrganizerItemDetail::TypeVisualReminder; | 
| 1212 |     } else if (itemType == QOrganizerItemType::TypeTodoOccurrence) { | 
| 1213 |         supportedDetails << QOrganizerItemDetail::TypeParent | 
| 1214 |                          << QOrganizerItemDetail::TypeTodoTime | 
| 1215 |                          << QOrganizerItemDetail::TypePriority | 
| 1216 |                          << QOrganizerItemDetail::TypeTodoProgress | 
| 1217 |                          << QOrganizerItemDetail::TypeReminder | 
| 1218 |                          << QOrganizerItemDetail::TypeAudibleReminder | 
| 1219 |                          << QOrganizerItemDetail::TypeEmailReminder | 
| 1220 |                          << QOrganizerItemDetail::TypeVisualReminder; | 
| 1221 |     } else if (itemType == QOrganizerItemType::TypeJournal) { | 
| 1222 |         supportedDetails << QOrganizerItemDetail::TypeJournalTime; | 
| 1223 |     } else if (itemType == QOrganizerItemType::TypeNote) { | 
| 1224 |         // nothing ;) | 
| 1225 |     } else { | 
| 1226 |         supportedDetails.clear(); | 
| 1227 |     } | 
| 1228 |  | 
| 1229 |     return supportedDetails; | 
| 1230 | } | 
| 1231 |  | 
| 1232 | /*! | 
| 1233 |  * This slot is called some time after an asynchronous request is started. | 
| 1234 |  * It performs the required operation, sets the result and returns. | 
| 1235 |  */ | 
| 1236 | void QOrganizerItemMemoryEngine::performAsynchronousOperation(QOrganizerAbstractRequest *currentRequest) | 
| 1237 | { | 
| 1238 |     // store up changes, and emit signals once at the end of the (possibly batch) operation. | 
| 1239 |     QOrganizerItemChangeSet changeSet; | 
| 1240 |  | 
| 1241 |     // Now perform the active request and emit required signals. | 
| 1242 |     Q_ASSERT(currentRequest->state() == QOrganizerAbstractRequest::ActiveState); | 
| 1243 |     switch (currentRequest->type()) { | 
| 1244 |         case QOrganizerAbstractRequest::ItemFetchRequest: | 
| 1245 |         { | 
| 1246 |             QOrganizerItemFetchRequest* r = static_cast<QOrganizerItemFetchRequest*>(currentRequest); | 
| 1247 |             QOrganizerItemFilter filter = r->filter(); | 
| 1248 |             QList<QOrganizerItemSortOrder> sorting = r->sorting(); | 
| 1249 |             QOrganizerItemFetchHint fetchHint = r->fetchHint(); | 
| 1250 |             QDateTime startDate = r->startDate(); | 
| 1251 |             QDateTime endDate = r->endDate(); | 
| 1252 |  | 
| 1253 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1254 |             QList<QOrganizerItem> requestedOrganizerItems = items(filter, startDateTime: startDate, endDateTime: endDate, maxCount: -1, sortOrders: sorting, fetchHint, error: &operationError); | 
| 1255 |  | 
| 1256 |             // update the request with the results. | 
| 1257 |             if (!requestedOrganizerItems.isEmpty() || operationError != QOrganizerManager::NoError) | 
| 1258 |                 updateItemFetchRequest(request: r, result: requestedOrganizerItems, error: operationError, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1259 |             else | 
| 1260 |                 updateRequestState(request: currentRequest, state: QOrganizerAbstractRequest::FinishedState); | 
| 1261 |         } | 
| 1262 |         break; | 
| 1263 |  | 
| 1264 |     case QOrganizerAbstractRequest::ItemFetchByIdRequest: { | 
| 1265 |         QOrganizerItemFetchByIdRequest* r = static_cast<QOrganizerItemFetchByIdRequest*>(currentRequest); | 
| 1266 |         // fetch hint cannot be used in memory backend | 
| 1267 |  | 
| 1268 |         QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1269 |         QMap<int, QOrganizerManager::Error> errorMap; | 
| 1270 |  | 
| 1271 |         QList<QOrganizerItem> requestedOrganizerItems; | 
| 1272 |  | 
| 1273 |         for (int i = 0; i < r->ids().size(); i++) { | 
| 1274 |             QOrganizerItem item = d->m_idToItemHash.value(akey: r->ids().at(i), adefaultValue: QOrganizerItem()); | 
| 1275 |             requestedOrganizerItems.append(t: item); | 
| 1276 |             if (item.isEmpty()) | 
| 1277 |                 errorMap.insert(akey: i, avalue: QOrganizerManager::DoesNotExistError); | 
| 1278 |         } | 
| 1279 |  | 
| 1280 |         // update the request with the results. | 
| 1281 |         if (!requestedOrganizerItems.isEmpty() || operationError != QOrganizerManager::NoError || !errorMap.isEmpty()) | 
| 1282 |             QOrganizerManagerEngine::updateItemFetchByIdRequest(request: r, result: requestedOrganizerItems, error: operationError, errorMap, QOrganizerAbstractRequest::FinishedState); | 
| 1283 |         else | 
| 1284 |             updateRequestState(request: currentRequest, state: QOrganizerAbstractRequest::FinishedState); | 
| 1285 |     } | 
| 1286 |     break; | 
| 1287 |  | 
| 1288 |         case QOrganizerAbstractRequest::ItemFetchForExportRequest: | 
| 1289 |         { | 
| 1290 |             QOrganizerItemFetchForExportRequest* r = static_cast<QOrganizerItemFetchForExportRequest*>(currentRequest); | 
| 1291 |             QOrganizerItemFilter filter = r->filter(); | 
| 1292 |             QList<QOrganizerItemSortOrder> sorting = r->sorting(); | 
| 1293 |             QOrganizerItemFetchHint fetchHint = r->fetchHint(); | 
| 1294 |             QDateTime startDate = r->startDate(); | 
| 1295 |             QDateTime endDate = r->endDate(); | 
| 1296 |  | 
| 1297 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1298 |             QList<QOrganizerItem> requestedOrganizerItems = itemsForExport(startDateTime: startDate, endDateTime: endDate, filter, sortOrders: sorting, fetchHint, error: &operationError); | 
| 1299 |  | 
| 1300 |             // update the request with the results. | 
| 1301 |             if (!requestedOrganizerItems.isEmpty() || operationError != QOrganizerManager::NoError) | 
| 1302 |                 updateItemFetchForExportRequest(request: r, result: requestedOrganizerItems, error: operationError, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1303 |             else | 
| 1304 |                 updateRequestState(request: currentRequest, state: QOrganizerAbstractRequest::FinishedState); | 
| 1305 |         } | 
| 1306 |         break; | 
| 1307 |  | 
| 1308 |         case QOrganizerAbstractRequest::ItemOccurrenceFetchRequest: | 
| 1309 |         { | 
| 1310 |             QOrganizerItemOccurrenceFetchRequest* r = static_cast<QOrganizerItemOccurrenceFetchRequest*>(currentRequest); | 
| 1311 |             QOrganizerItem parentItem(r->parentItem()); | 
| 1312 |             QDateTime startDate(r->startDate()); | 
| 1313 |             QDateTime endDate(r->endDate()); | 
| 1314 |             int countLimit = r->maxOccurrences(); | 
| 1315 |             QOrganizerItemFetchHint fetchHint = r->fetchHint(); | 
| 1316 |  | 
| 1317 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1318 |             QList<QOrganizerItem> requestedOrganizerItems = itemOccurrences(parentItem, startDateTime: startDate, endDateTime: endDate, maxCount: countLimit, fetchHint, error: &operationError); | 
| 1319 |  | 
| 1320 |             // update the request with the results. | 
| 1321 |             if (!requestedOrganizerItems.isEmpty() || operationError != QOrganizerManager::NoError) | 
| 1322 |                 updateItemOccurrenceFetchRequest(request: r, result: requestedOrganizerItems, error: operationError, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1323 |             else | 
| 1324 |                 updateRequestState(request: currentRequest, state: QOrganizerAbstractRequest::FinishedState); | 
| 1325 |         } | 
| 1326 |         break; | 
| 1327 |  | 
| 1328 |  | 
| 1329 |         case QOrganizerAbstractRequest::ItemIdFetchRequest: | 
| 1330 |         { | 
| 1331 |             QOrganizerItemIdFetchRequest* r = static_cast<QOrganizerItemIdFetchRequest*>(currentRequest); | 
| 1332 |             QOrganizerItemFilter filter = r->filter(); | 
| 1333 |             QList<QOrganizerItemSortOrder> sorting = r->sorting(); | 
| 1334 |             QDateTime startDate = r->startDate(); | 
| 1335 |             QDateTime endDate = r->endDate(); | 
| 1336 |  | 
| 1337 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1338 |             QList<QOrganizerItemId> requestedOrganizerItemIds = itemIds(filter, startDateTime: startDate, endDateTime: endDate, sortOrders: sorting, error: &operationError); | 
| 1339 |  | 
| 1340 |             if (!requestedOrganizerItemIds.isEmpty() || operationError != QOrganizerManager::NoError) | 
| 1341 |                 updateItemIdFetchRequest(request: r, result: requestedOrganizerItemIds, error: operationError, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1342 |             else | 
| 1343 |                 updateRequestState(request: currentRequest, state: QOrganizerAbstractRequest::FinishedState); | 
| 1344 |         } | 
| 1345 |         break; | 
| 1346 |  | 
| 1347 |         case QOrganizerAbstractRequest::ItemSaveRequest: | 
| 1348 |         { | 
| 1349 |             QOrganizerItemSaveRequest* r = static_cast<QOrganizerItemSaveRequest*>(currentRequest); | 
| 1350 |             QList<QOrganizerItem> organizeritems = r->items(); | 
| 1351 |  | 
| 1352 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1353 |             QMap<int, QOrganizerManager::Error> errorMap; | 
| 1354 |             saveItems(items: &organizeritems, detailMask: r->detailMask(), errorMap: &errorMap, error: &operationError); | 
| 1355 |  | 
| 1356 |             updateItemSaveRequest(request: r, result: organizeritems, error: operationError, errorMap, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1357 |         } | 
| 1358 |         break; | 
| 1359 |  | 
| 1360 |         case QOrganizerAbstractRequest::ItemRemoveRequest: | 
| 1361 |         { | 
| 1362 |             QOrganizerItemRemoveRequest* r = static_cast<QOrganizerItemRemoveRequest*>(currentRequest); | 
| 1363 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1364 |             QList<QOrganizerItem> organizeritemsToRemove = r->items(); | 
| 1365 |             QSet<QOrganizerItemId> removedParentIds; | 
| 1366 |             QMap<int, QOrganizerManager::Error> errorMap; | 
| 1367 |  | 
| 1368 |             for (int i = 0; i < organizeritemsToRemove.size(); i++) { | 
| 1369 |                 QOrganizerItem item = organizeritemsToRemove[i]; | 
| 1370 |                 QOrganizerManager::Error tempError = QOrganizerManager::NoError; | 
| 1371 |                 if ((item.type() == QOrganizerItemType::TypeEventOccurrence | 
| 1372 |                      || item.type() == QOrganizerItemType::TypeTodoOccurrence) | 
| 1373 |                         && item.id().isNull()) { | 
| 1374 |                     // this is a generated occurrence, modify parent items exception dates | 
| 1375 |                     QOrganizerItemParent parentDetail = item.detail(detailType: QOrganizerItemDetail::TypeParent); | 
| 1376 |                     if (removedParentIds.isEmpty() || !removedParentIds.contains(value: parentDetail.parentId())) | 
| 1377 |                         removeOccurrence(organizeritem: item, changeSet, error: &tempError); | 
| 1378 |                 } else { | 
| 1379 |                     removeItem(organizeritemId: item.id(), changeSet, error: &tempError); | 
| 1380 |                     if (tempError == QOrganizerManager::NoError && itemHasReccurence(oi: item)) | 
| 1381 |                         removedParentIds.insert(value: item.id()); | 
| 1382 |                 } | 
| 1383 |                 if (tempError != QOrganizerManager::NoError) { | 
| 1384 |                     errorMap.insert(akey: i, avalue: tempError); | 
| 1385 |                     operationError = tempError; | 
| 1386 |                 } | 
| 1387 |             } | 
| 1388 |             if (!errorMap.isEmpty() || operationError != QOrganizerManager::NoError) | 
| 1389 |                 updateItemRemoveRequest(request: r, error: operationError, errorMap, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1390 |             else | 
| 1391 |                 updateRequestState(request: currentRequest, state: QOrganizerAbstractRequest::FinishedState); | 
| 1392 |         } | 
| 1393 |         break; | 
| 1394 |  | 
| 1395 |         case QOrganizerAbstractRequest::ItemRemoveByIdRequest: | 
| 1396 |         { | 
| 1397 |             QOrganizerItemRemoveByIdRequest* r = static_cast<QOrganizerItemRemoveByIdRequest*>(currentRequest); | 
| 1398 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1399 |             QList<QOrganizerItemId> organizeritemsToRemove = r->itemIds(); | 
| 1400 |             QMap<int, QOrganizerManager::Error> errorMap; | 
| 1401 |  | 
| 1402 |             for (int i = 0; i < organizeritemsToRemove.size(); i++) { | 
| 1403 |                 QOrganizerManager::Error tempError = QOrganizerManager::NoError; | 
| 1404 |                 removeItem(organizeritemId: organizeritemsToRemove.at(i), changeSet, error: &tempError); | 
| 1405 |  | 
| 1406 |                 if (tempError != QOrganizerManager::NoError) { | 
| 1407 |                     errorMap.insert(akey: i, avalue: tempError); | 
| 1408 |                     operationError = tempError; | 
| 1409 |                 } | 
| 1410 |             } | 
| 1411 |  | 
| 1412 |             if (!errorMap.isEmpty() || operationError != QOrganizerManager::NoError) | 
| 1413 |                 updateItemRemoveByIdRequest(request: r, error: operationError, errorMap, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1414 |             else | 
| 1415 |                 updateRequestState(request: currentRequest, state: QOrganizerAbstractRequest::FinishedState); | 
| 1416 |         } | 
| 1417 |         break; | 
| 1418 |  | 
| 1419 |         case QOrganizerAbstractRequest::CollectionFetchRequest: | 
| 1420 |         { | 
| 1421 |             QOrganizerCollectionFetchRequest* r = static_cast<QOrganizerCollectionFetchRequest*>(currentRequest); | 
| 1422 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1423 |             QList<QOrganizerCollection> requestedOrganizerCollections = collections(error: &operationError); | 
| 1424 |  | 
| 1425 |             // update the request with the results. | 
| 1426 |             updateCollectionFetchRequest(request: r, result: requestedOrganizerCollections, error: operationError, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1427 |         } | 
| 1428 |         break; | 
| 1429 |  | 
| 1430 |         case QOrganizerAbstractRequest::CollectionSaveRequest: | 
| 1431 |         { | 
| 1432 |             QOrganizerCollectionSaveRequest* r = static_cast<QOrganizerCollectionSaveRequest*>(currentRequest); | 
| 1433 |             QList<QOrganizerCollection> collections = r->collections(); | 
| 1434 |             QList<QOrganizerCollection> retn; | 
| 1435 |  | 
| 1436 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1437 |             QMap<int, QOrganizerManager::Error> errorMap; | 
| 1438 |             for (int i = 0; i < collections.size(); ++i) { | 
| 1439 |                 QOrganizerManager::Error tempError = QOrganizerManager::NoError; | 
| 1440 |                 QOrganizerCollection curr = collections.at(i); | 
| 1441 |                 if (!saveCollection(collection: &curr, error: &tempError)) { | 
| 1442 |                     errorMap.insert(akey: i, avalue: tempError); | 
| 1443 |                     operationError = tempError; | 
| 1444 |                 } | 
| 1445 |                 retn.append(t: curr); | 
| 1446 |             } | 
| 1447 |  | 
| 1448 |             updateCollectionSaveRequest(request: r, result: retn, error: operationError, errorMap, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1449 |         } | 
| 1450 |         break; | 
| 1451 |  | 
| 1452 |         case QOrganizerAbstractRequest::CollectionRemoveRequest: | 
| 1453 |         { | 
| 1454 |             // removes the collections identified in the list of ids. | 
| 1455 |             QOrganizerCollectionRemoveRequest* r = static_cast<QOrganizerCollectionRemoveRequest*>(currentRequest); | 
| 1456 |             QOrganizerManager::Error operationError = QOrganizerManager::NoError; | 
| 1457 |             QList<QOrganizerCollectionId> collectionsToRemove = r->collectionIds(); | 
| 1458 |             QMap<int, QOrganizerManager::Error> errorMap; | 
| 1459 |  | 
| 1460 |             for (int i = 0; i < collectionsToRemove.size(); i++) { | 
| 1461 |                 QOrganizerManager::Error tempError = QOrganizerManager::NoError; | 
| 1462 |                 removeCollection(collectionId: collectionsToRemove.at(i), error: &tempError); | 
| 1463 |  | 
| 1464 |                 if (tempError != QOrganizerManager::NoError) { | 
| 1465 |                     errorMap.insert(akey: i, avalue: tempError); | 
| 1466 |                     operationError = tempError; | 
| 1467 |                 } | 
| 1468 |             } | 
| 1469 |  | 
| 1470 |             if (!errorMap.isEmpty() || operationError != QOrganizerManager::NoError) | 
| 1471 |                 updateCollectionRemoveRequest(request: r, error: operationError, errorMap, newState: QOrganizerAbstractRequest::FinishedState); | 
| 1472 |             else | 
| 1473 |                 updateRequestState(request: currentRequest, state: QOrganizerAbstractRequest::FinishedState); | 
| 1474 |         } | 
| 1475 |         break; | 
| 1476 |  | 
| 1477 |         default: // unknown request type. | 
| 1478 |         break; | 
| 1479 |     } | 
| 1480 |  | 
| 1481 |     // now emit any signals we have to emit | 
| 1482 |     d->emitSharedSignals(cs: &changeSet); | 
| 1483 | } | 
| 1484 |  | 
| 1485 | #include "moc_qorganizeritemmemorybackend_p.cpp" | 
| 1486 |  | 
| 1487 | QT_END_NAMESPACE_ORGANIZER | 
| 1488 |  |