akonadimodel.h 16.6 KB
Newer Older
1 2 3
/*
 *  akonadimodel.h  -  KAlarm calendar file access using Akonadi
 *  Program:  kalarm
David Jarvie's avatar
David Jarvie committed
4
 *  Copyright © 2010-2014 by David Jarvie <djarvie@kde.org>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef AKONADIMODEL_H
#define AKONADIMODEL_H

24 25
#include "eventid.h"

Laurent Montel's avatar
Laurent Montel committed
26 27
#include <kalarmcal/kacalendar.h>
#include <kalarmcal/kaevent.h>
28

Laurent Montel's avatar
Laurent Montel committed
29
#include <AkonadiCore/entitytreemodel.h>
Laurent Montel's avatar
Laurent Montel committed
30
#include <AkonadiCore/servermanager.h>
31 32

#include <QSize>
33 34 35 36
#include <QColor>
#include <QMap>
#include <QQueue>

37 38 39 40 41
namespace Akonadi
{
class ChangeRecorder;
}

42 43 44
class QPixmap;
class KJob;

45
using namespace KAlarmCal;
46

47 48 49 50 51

class AkonadiModel : public Akonadi::EntityTreeModel
{
        Q_OBJECT
    public:
52
        enum Change { Added, Deleted, Invalidated, Enabled, ReadOnly, AlarmTypes, WrongType, Location, Colour };
53 54 55 56 57 58 59 60
        enum {   // data columns
            // Item columns
            TimeColumn = 0, TimeToColumn, RepeatColumn, ColourColumn, TypeColumn, TextColumn,
            TemplateNameColumn,
            ColumnCount
        };
        enum {   // additional data roles
            // Collection roles
61
            EnabledTypesRole = UserRole, // alarm types which are enabled for the collection
62
            BaseColourRole,            // background colour ignoring collection colour
63
            AlarmTypeRole,             // OR of event types which collection contains
64
            IsStandardRole,            // OR of event types which collection is standard for
65
            KeepFormatRole,            // user has chosen not to update collection's calendar storage format
66
            // Item roles
67
            EnabledRole,               // true for enabled alarm, false for disabled
68 69
            StatusRole,                // KAEvent::ACTIVE/ARCHIVED/TEMPLATE
            AlarmActionsRole,          // KAEvent::Actions
70
            AlarmSubActionRole,        // KAEvent::Action
71 72 73 74 75 76 77 78 79
            ValueRole,                 // numeric value
            SortRole,                  // the value to use for sorting
            CommandErrorRole           // last command execution error for alarm (per user)
        };

        /** Struct containing a KAEvent and its parent Collection. */
        struct Event
        {
            Event(const KAEvent& e, const Akonadi::Collection& c) : event(e), collection(c) {}
80 81
            EventId eventId() const       { return EventId(collection.id(), event.id()); }
            bool    isConsistent() const  { return event.collectionId() == collection.id(); }
82 83 84 85 86 87 88
            KAEvent             event;
            Akonadi::Collection collection;
        };
        typedef QList<Event> EventList;

        static AkonadiModel* instance();

David Jarvie's avatar
David Jarvie committed
89 90
        ~AkonadiModel();

91
        /** Return the display name for a collection. */
92
        QString displayName(Akonadi::Collection&) const;
93 94
        /** Return the storage type (file/directory/URL etc.) for a collection. */
        QString storageType(const Akonadi::Collection&) const;
95 96
        /** Get the foreground color for a collection, based on specified mime types. */
        static QColor foregroundColor(const Akonadi::Collection&, const QStringList& mimeTypes);
97 98 99
        /** Set the background color for a collection and its alarms. */
        void setBackgroundColor(Akonadi::Collection&, const QColor&);
        /** Get the background color for a collection and its alarms. */
100
        QColor backgroundColor(Akonadi::Collection&) const;
101 102
        /** Get the tooltip for a collection. The collection's enabled status is
         *  evaluated for specified alarm types. */
103
        QString tooltip(const Akonadi::Collection&, CalEvent::Types) const;
104 105 106
        /** Return the read-only status tooltip for a collection.
          * A null string is returned if the collection is fully writable. */
        static QString readOnlyTooltip(const Akonadi::Collection&);
107 108 109 110 111 112

        /** To be called when the command error status of an alarm has changed,
         *  to set in the Akonadi database and update the visual command error indications.
         */
        void updateCommandError(const KAEvent&);

Laurent Montel's avatar
Laurent Montel committed
113 114
        QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
        bool setData(const QModelIndex&, const QVariant& value, int role) Q_DECL_OVERRIDE;
115

116
        /** Refresh the specified collection instance with up to date data. */
117
        bool refresh(Akonadi::Collection&) const;
118
        /** Refresh the specified item instance with up to date data. */
119
        bool refresh(Akonadi::Item&) const;
120

121 122 123 124 125 126 127
        QModelIndex         collectionIndex(Akonadi::Collection::Id id) const
                                        { return collectionIndex(Akonadi::Collection(id)); }
        QModelIndex         collectionIndex(const Akonadi::Collection&) const;
        Akonadi::Collection collectionById(Akonadi::Collection::Id) const;
        Akonadi::Collection collectionForItem(Akonadi::Item::Id) const;
        Akonadi::Collection collection(const KAEvent& e) const   { return collectionForItem(e.itemId()); }

128 129 130 131 132
        /** Remove a collection from Akonadi. The calendar file is not removed.
         *  @return true if a removal job has been scheduled.
         */
        bool removeCollection(const Akonadi::Collection&);

133 134 135 136 137 138
        /** Reload a collection's data from Akonadi storage (not from the backend). */
        bool reloadCollection(const Akonadi::Collection&);

        /** Reload all collections' data from Akonadi storage (not from the backend). */
        void reload();

139 140 141
        /** Return whether calendar migration/creation at initialisation has completed. */
        bool isMigrationCompleted() const;

142 143
        bool isCollectionBeingDeleted(Akonadi::Collection::Id) const;

144 145 146 147 148
        QModelIndex         itemIndex(Akonadi::Item::Id id) const
                                        { return itemIndex(Akonadi::Item(id)); }
        QModelIndex         itemIndex(const Akonadi::Item&) const;
        Akonadi::Item       itemById(Akonadi::Item::Id) const;

149 150 151
        /** Return the alarm with the specified unique identifier.
         *  @return the event, or invalid event if no such event exists.
         */
Laurent Montel's avatar
Laurent Montel committed
152
        KAEvent event(const Akonadi::Item& item) const  { return event(item, QModelIndex(), Q_NULLPTR); }
153 154 155 156
        KAEvent event(Akonadi::Item::Id) const;
        KAEvent event(const QModelIndex&) const;
        using QObject::event;   // prevent warning about hidden virtual method

157
        /** Return an event's model index, based on its itemId() value. */
158
        QModelIndex eventIndex(const KAEvent&);
159 160 161 162
        /** Search for an event's item ID. This method ignores any itemId() value
         *  contained in the KAEvent. The collectionId() is used if available.
         */
        Akonadi::Item::Id findItemId(const KAEvent&);
163 164 165

#if 0
        /** Return all events in a collection, optionally of a specified type. */
166
        KAEvent::List events(Akonadi::Collection&, CalEvent::Type = CalEvent::EMPTY) const;
167 168 169
#endif

        bool  addEvent(KAEvent&, Akonadi::Collection&);
170
        bool  addEvents(const KAEvent::List&, Akonadi::Collection&);
171 172 173 174 175
        bool  updateEvent(KAEvent& event);
        bool  updateEvent(Akonadi::Item::Id oldId, KAEvent& newEvent);
        bool  deleteEvent(const KAEvent& event);
        bool  deleteEvent(Akonadi::Item::Id itemId);

176 177 178
        /** Check whether a collection is stored in the current KAlarm calendar format. */
        static bool isCompatible(const Akonadi::Collection&);

179 180 181
        /** Return whether a collection is fully writable, i.e. with
         *  create/delete/change rights and compatible with the current KAlarm
         *  calendar format.
182 183 184 185
         *
         *  @return 1 = fully writable,
         *          0 = writable except that backend calendar is in an old KAlarm format,
         *         -1 = read-only or incompatible format.
186
         */
187
        static int isWritable(const Akonadi::Collection&);
188 189 190 191 192

        /** Return whether a collection is fully writable, i.e. with
         *  create/delete/change rights and compatible with the current KAlarm
         *  calendar format.
         *
193
         *  @param format  Updated to contain the backend calendar storage format.
194 195
         *                 If read-only, = KACalendar::Current;
         *                 if unknown format, = KACalendar::Incompatible;
196 197 198
         *                 otherwise = the backend calendar storage format.
         *  @return 1 = fully writable,
         *          0 = writable except that backend calendar is in an old KAlarm format,
199
         *         -1 = read-only (if @p compat == KACalendar::Current), or
200
         *              incompatible format otherwise.
201
         */
202
        static int isWritable(const Akonadi::Collection&, KACalendar::Compat& format);
203

204
        static CalEvent::Types types(const Akonadi::Collection&);
205 206 207

        static QSize iconSize()  { return mIconSize; }

Laurent Montel's avatar
Laurent Montel committed
208
    Q_SIGNALS:
209 210 211
        /** Signal emitted when a collection has been added to the model. */
        void collectionAdded(const Akonadi::Collection&);

212 213 214 215 216
        /** Signal emitted when a collection's enabled or read-only status has changed.
         *  @param inserted  true if the reason for the change is that the collection
         *                   has been inserted into the model
         */
        void collectionStatusChanged(const Akonadi::Collection&, AkonadiModel::Change, const QVariant& newValue, bool inserted);
217 218 219 220 221 222 223 224 225 226

        /** Signal emitted when events have been added to the model. */
        void eventsAdded(const AkonadiModel::EventList&);

        /** Signal emitted when events are about to be removed from the model. */
        void eventsToBeRemoved(const AkonadiModel::EventList&);

        /** Signal emitted when an event in the model has changed. */
        void eventChanged(const AkonadiModel::Event&);

227
        /** Signal emitted when Akonadi has completed a collection modification.
228 229 230
         *  @param id      Akonadi ID for the collection
         *  @param status  true if successful, false if error
         */
231 232 233 234 235 236 237
        void collectionModified(Akonadi::Collection::Id, bool status = true);

        /** Signal emitted when Akonadi has completed a collection deletion.
         *  @param id      Akonadi ID for the collection
         *  @param status  true if successful, false if error
         */
        void collectionDeleted(Akonadi::Collection::Id, bool status = true);
238 239 240 241 242 243 244 245

        /** Signal emitted when Akonadi has completed an item creation, update
         *  or deletion.
         *  @param id      Akonadi ID for the item
         *  @param status  true if successful, false if error
         */
        void itemDone(Akonadi::Item::Id, bool status = true);

246 247 248
        /** Signal emitted when calendar migration/creation has completed. */
        void migrationCompleted();

249 250 251
        /** Signal emitted when the Akonadi server has stopped. */
        void serverStopped();

252
    protected:
Laurent Montel's avatar
Laurent Montel committed
253 254
        QVariant entityHeaderData(int section, Qt::Orientation, int role, HeaderGroup) const Q_DECL_OVERRIDE;
        int entityColumnCount(HeaderGroup) const Q_DECL_OVERRIDE;
255

Laurent Montel's avatar
Laurent Montel committed
256
    private Q_SLOTS:
257
        void checkResources(Akonadi::ServerManager::State);
258
        void slotMigrationCompleted();
259
        void slotCollectionChanged(const Akonadi::Collection& c, const QSet<QByteArray>& attrNames)
260
                       { setCollectionChanged(c, attrNames, false); }
261
        void slotCollectionRemoved(const Akonadi::Collection&);
262
        void slotCollectionBeingCreated(const QString& path, Akonadi::Collection::Id, bool finished);
263 264 265 266 267 268 269 270 271
        void slotUpdateTimeTo();
        void slotUpdateArchivedColour(const QColor&);
        void slotUpdateDisabledColour(const QColor&);
        void slotUpdateHolidays();
        void slotUpdateWorkingHours();
        void slotRowsInserted(const QModelIndex& parent, int start, int end);
        void slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end);
        void slotMonitoredItemChanged(const Akonadi::Item&, const QSet<QByteArray>&);
        void slotEmitEventChanged();
272
        void modifyCollectionJobDone(KJob*);
273
        void itemJobDone(KJob*);
Stephen Kelly's avatar
Stephen Kelly committed
274

275 276 277 278 279 280 281 282
    private:
        struct CalData   // data per collection
        {
            CalData()  : enabled(false) { }
            CalData(bool e, const QColor& c)  : colour(c), enabled(e) { }
            QColor colour;    // user selected color for the calendar
            bool   enabled;   // whether the collection is enabled
        };
283 284 285 286 287 288 289
        struct CollJobData   // collection data for jobs in progress
        {
            CollJobData() : id(-1) {}
            CollJobData(Akonadi::Collection::Id i, const QString& d) : id(i), displayName(d) {}
            Akonadi::Collection::Id id;
            QString                 displayName;
        };
290 291
        struct CollTypeData  // data for configuration dialog for collection creation job
        {
Laurent Montel's avatar
Laurent Montel committed
292
            CollTypeData() : parent(Q_NULLPTR), alarmType(CalEvent::EMPTY) {}
293
            CollTypeData(CalEvent::Type t, QWidget* p) : parent(p), alarmType(t) {}
294
            QWidget*               parent;
295
            CalEvent::Type alarmType;
296
        };
297 298

        AkonadiModel(Akonadi::ChangeRecorder*, QObject* parent);
299
        void      initCalendarMigrator();
300
        KAEvent   event(const Akonadi::Item&, const QModelIndex&, Akonadi::Collection*) const;
301
        void      signalDataChanged(bool (*checkFunc)(const Akonadi::Item&), int startColumn, int endColumn, const QModelIndex& parent);
302
        void      setCollectionChanged(const Akonadi::Collection&, const QSet<QByteArray>&, bool rowInserted);
303
        void      queueItemModifyJob(const Akonadi::Item&);
David Jarvie's avatar
David Jarvie committed
304
        void      checkQueuedItemModifyJob(const Akonadi::Item&);
305
#if 0
306
        void     getChildEvents(const QModelIndex& parent, CalEvent::Type, KAEvent::List&) const;
307
#endif
308
        QColor    backgroundColor_p(const Akonadi::Collection&) const;
309 310 311 312 313 314 315 316 317 318 319 320 321 322
        QString   repeatText(const KAEvent&) const;
        QString   repeatOrder(const KAEvent&) const;
        QPixmap*  eventIcon(const KAEvent&) const;
        QString   whatsThisText(int column) const;
        EventList eventList(const QModelIndex& parent, int start, int end);

        static AkonadiModel*  mInstance;
        static QPixmap* mTextIcon;
        static QPixmap* mFileIcon;
        static QPixmap* mCommandIcon;
        static QPixmap* mEmailIcon;
        static QPixmap* mAudioIcon;
        static QSize    mIconSize;
        static int      mTimeHourPos;   // position of hour within time string, or -1 if leading zeroes included
323 324

        Akonadi::ChangeRecorder* mMonitor;
325
        QMap<Akonadi::Collection::Id, CalEvent::Types> mCollectionAlarmTypes;  // last content mime types of each collection
326
        QMap<Akonadi::Collection::Id, Akonadi::Collection::Rights> mCollectionRights;  // last writable status of each collection
327
        QMap<Akonadi::Collection::Id, CalEvent::Types> mCollectionEnabled;  // last enabled mime types of each collection
David Jarvie's avatar
David Jarvie committed
328
        QMap<KJob*, CollJobData> mPendingCollectionJobs;  // pending collection creation/deletion jobs, with collection ID & name
329
        QMap<KJob*, CollTypeData> mPendingColCreateJobs;  // default alarm type for pending collection creation jobs
David Jarvie's avatar
David Jarvie committed
330
        QMap<KJob*, Akonadi::Item::Id> mPendingItemJobs;  // pending item creation/deletion jobs, with event ID
331
        QMap<Akonadi::Item::Id, Akonadi::Item> mItemModifyJobQueue;  // pending item modification jobs, invalid item = queue empty but job active
332 333
        QList<QString>     mCollectionsBeingCreated;  // path names of new collections being created by migrator
        QList<Akonadi::Collection::Id> mCollectionIdsBeingCreated;  // ids of new collections being created by migrator
David Jarvie's avatar
David Jarvie committed
334
        QList<Akonadi::Item::Id> mItemsBeingCreated;  // new items not fully initialised yet
335
        QList<Akonadi::Collection::Id> mCollectionsDeleting;  // collections currently being removed
336
        QList<Akonadi::Collection::Id> mCollectionsDeleted;   // collections recently removed
337
        QQueue<Event>   mPendingEventChanges;   // changed events with changedEvent() signal pending
338
        bool            mResourcesChecked;      // whether resource existence has been checked yet
339
        bool            mMigrating;             // currently migrating calendars
340 341 342 343
};

#endif // AKONADIMODEL_H

344
// vim: et sw=4: