~ubuntu-branches/ubuntu/quantal/kdepim/quantal

« back to all changes in this revision

Viewing changes to kalarm/akonadimodel.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2011-12-15 14:17:51 UTC
  • mto: This revision was merged to the branch mainline in revision 193.
  • Revision ID: package-import@ubuntu.com-20111215141751-bmhdpiwl23wd9w26
Tags: upstream-4.7.90
ImportĀ upstreamĀ versionĀ 4.7.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifdef __GNUC__
 
2
#warning Update a calendar format, enable the calendar, copy old version on top -> crash
 
3
#warning Add a directory resource containing 2 alarm types, right click on it -> crash
 
4
#warning Set default template calendar to read-only -> crash
 
5
#warning Set default archived calendar to read-only, then read-write -> now "other format"
 
6
#warning Create new email template, get valgrind uninitialised errors from serialize()
 
7
#endif
1
8
/*
2
9
 *  akonadimodel.cpp  -  KAlarm calendar file access using Akonadi
3
10
 *  Program:  kalarm
19
26
 */
20
27
 
21
28
#include "akonadimodel.h"
22
 
#include "alarmtext.h"
23
29
#include "autoqpointer.h"
24
30
#include "calendarmigrator.h"
25
 
#include "collectionattribute.h"
26
 
#include "compatibilityattribute.h"
27
 
#include "eventattribute.h"
 
31
#include "mainwindow.h"
28
32
#include "messagebox.h"
29
33
#include "preferences.h"
30
34
#include "synchtimer.h"
 
35
#include "kalarmsettings.h"
31
36
#include "kalarmdirsettings.h"
32
37
 
 
38
#include <kalarmcal/alarmtext.h>
 
39
#include <kalarmcal/collectionattribute.h>
 
40
#include <kalarmcal/compatibilityattribute.h>
 
41
#include <kalarmcal/eventattribute.h>
 
42
 
33
43
#include <akonadi/agentfilterproxymodel.h>
34
44
#include <akonadi/agentinstancecreatejob.h>
35
45
#include <akonadi/agentmanager.h>
47
57
#include <akonadi/itemfetchscope.h>
48
58
 
49
59
#include <klocale.h>
 
60
#include <kcolorutils.h>
50
61
 
51
62
#include <QApplication>
52
63
#include <QFileInfo>
53
64
#include <QTimer>
54
65
 
55
66
using namespace Akonadi;
56
 
using KAlarm::CollectionAttribute;
57
 
using KAlarm::CompatibilityAttribute;
58
 
using KAlarm::EventAttribute;
 
67
using namespace KAlarmCal;
59
68
 
60
69
static Collection::Rights writableRights = Collection::CanChangeItem | Collection::CanCreateItem | Collection::CanDeleteItem;
61
70
 
98
107
    monitor->setCollectionMonitored(Collection::root());
99
108
    monitor->setResourceMonitored("akonadi_kalarm_resource");
100
109
    monitor->setResourceMonitored("akonadi_kalarm_dir_resource");
101
 
    monitor->setMimeTypeMonitored(KAlarm::MIME_ACTIVE);
102
 
    monitor->setMimeTypeMonitored(KAlarm::MIME_ARCHIVED);
103
 
    monitor->setMimeTypeMonitored(KAlarm::MIME_TEMPLATE);
 
110
    monitor->setMimeTypeMonitored(KAlarmCal::MIME_ACTIVE);
 
111
    monitor->setMimeTypeMonitored(KAlarmCal::MIME_ARCHIVED);
 
112
    monitor->setMimeTypeMonitored(KAlarmCal::MIME_TEMPLATE);
104
113
    monitor->itemFetchScope().fetchFullPayload();
105
114
    monitor->itemFetchScope().fetchAttribute<EventAttribute>();
106
115
 
121
130
#ifdef __GNUC__
122
131
#warning Only want to monitor collection properties, not content, when this becomes possible
123
132
#endif
124
 
    connect(monitor, SIGNAL(collectionChanged(const Akonadi::Collection&, const QSet<QByteArray>&)), SLOT(slotCollectionChanged(const Akonadi::Collection&, const QSet<QByteArray>&)));
125
 
    connect(monitor, SIGNAL(collectionRemoved(const Akonadi::Collection&)), SLOT(slotCollectionRemoved(const Akonadi::Collection&)));
 
133
    connect(monitor, SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)), SLOT(slotCollectionChanged(Akonadi::Collection,QSet<QByteArray>)));
 
134
    connect(monitor, SIGNAL(collectionRemoved(Akonadi::Collection)), SLOT(slotCollectionRemoved(Akonadi::Collection)));
 
135
    connect(CalendarMigrator::instance(), SIGNAL(creating(QString,bool)), SLOT(slotCollectionBeingCreated(QString,bool)));
126
136
    MinuteTimer::connect(this, SLOT(slotUpdateTimeTo()));
127
 
    Preferences::connect(SIGNAL(archivedColourChanged(const QColor&)), this, SLOT(slotUpdateArchivedColour(const QColor&)));
128
 
    Preferences::connect(SIGNAL(disabledColourChanged(const QColor&)), this, SLOT(slotUpdateDisabledColour(const QColor&)));
129
 
    Preferences::connect(SIGNAL(holidaysChanged(const KHolidays::HolidayRegion&)), this, SLOT(slotUpdateHolidays()));
130
 
    Preferences::connect(SIGNAL(workTimeChanged(const QTime&, const QTime&, const QBitArray&)), this, SLOT(slotUpdateWorkingHours()));
 
137
    Preferences::connect(SIGNAL(archivedColourChanged(QColor)), this, SLOT(slotUpdateArchivedColour(QColor)));
 
138
    Preferences::connect(SIGNAL(disabledColourChanged(QColor)), this, SLOT(slotUpdateDisabledColour(QColor)));
 
139
    Preferences::connect(SIGNAL(holidaysChanged(KHolidays::HolidayRegion)), this, SLOT(slotUpdateHolidays()));
 
140
    Preferences::connect(SIGNAL(workTimeChanged(QTime,QTime,QBitArray)), this, SLOT(slotUpdateWorkingHours()));
131
141
 
132
 
    connect(this, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(slotRowsInserted(const QModelIndex&, int, int)));
133
 
    connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), SLOT(slotRowsAboutToBeRemoved(const QModelIndex&, int, int)));
134
 
    connect(monitor, SIGNAL(itemChanged(const Akonadi::Item&, const QSet<QByteArray>&)), SLOT(slotMonitoredItemChanged(const Akonadi::Item&, const QSet<QByteArray>&)));
 
142
    connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotRowsInserted(QModelIndex,int,int)));
 
143
    connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
 
144
    connect(monitor, SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>)), SLOT(slotMonitoredItemChanged(Akonadi::Item,QSet<QByteArray>)));
135
145
 
136
146
    // Check whether there are any KAlarm resources configured
137
147
    bool found = false;
176
186
        case ValueRole:
177
187
        case StatusRole:
178
188
        case AlarmActionsRole:
179
 
        case AlarmActionRole:
 
189
        case AlarmSubActionRole:
180
190
        case EnabledRole:
 
191
        case EnabledTypesRole:
181
192
        case CommandErrorRole:
182
193
        case BaseColourRole:
183
194
        case AlarmTypeRole:
195
206
        {
196
207
            case Qt::DisplayRole:
197
208
                return displayName_p(collection);
198
 
            case EnabledRole:
 
209
            case EnabledTypesRole:
199
210
                if (!collection.hasAttribute<CollectionAttribute>())
200
211
                    return 0;
201
212
                return static_cast<int>(collection.attribute<CollectionAttribute>()->enabled());
210
221
                break;
211
222
            }
212
223
            case Qt::ForegroundRole:
213
 
            {
214
 
                QStringList mimeTypes = collection.contentMimeTypes();
215
 
                if (mimeTypes.contains(KAlarm::MIME_ACTIVE))
216
 
                    return (collection.rights() & writableRights) == writableRights ? Qt::black : Qt::darkGray;
217
 
                if (mimeTypes.contains(KAlarm::MIME_ARCHIVED))
218
 
                    return (collection.rights() & writableRights) == writableRights ? Qt::darkGreen : Qt::green;
219
 
                if (mimeTypes.contains(KAlarm::MIME_TEMPLATE))
220
 
                    return (collection.rights() & writableRights) == writableRights ? Qt::darkBlue : Qt::blue;
221
 
                break;
222
 
            }
 
224
                return foregroundColor(collection, collection.contentMimeTypes());
223
225
            case Qt::ToolTipRole:
224
 
                return tooltip(collection, KAlarm::CalEvent::ALL);
 
226
                return tooltip(collection, CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE);
225
227
            case AlarmTypeRole:
226
228
                return static_cast<int>(types(collection));
227
229
            case IsStandardRole:
229
231
                ||  !isCompatible(collection))
230
232
                    return 0;
231
233
                return static_cast<int>(collection.attribute<CollectionAttribute>()->standard());
 
234
            case KeepFormatRole:
 
235
                if (!collection.hasAttribute<CollectionAttribute>())
 
236
                    return false;
 
237
                return collection.attribute<CollectionAttribute>()->keepFormat();
232
238
            default:
233
239
                break;
234
240
        }
240
246
        {
241
247
            // This is an Item row
242
248
            QString mime = item.mimeType();
243
 
            if ((mime != KAlarm::MIME_ACTIVE  &&  mime != KAlarm::MIME_ARCHIVED  &&  mime != KAlarm::MIME_TEMPLATE)
 
249
            if ((mime != KAlarmCal::MIME_ACTIVE  &&  mime != KAlarmCal::MIME_ARCHIVED  &&  mime != KAlarmCal::MIME_TEMPLATE)
244
250
            ||  !item.hasPayload<KAEvent>())
245
251
                return QVariant();
246
252
            switch (role)
247
253
            {
248
254
                case StatusRole:
249
255
                    // Mime type has a one-to-one relationship to event's category()
250
 
                    if (mime == KAlarm::MIME_ACTIVE)
251
 
                        return KAlarm::CalEvent::ACTIVE;
252
 
                    if (mime == KAlarm::MIME_ARCHIVED)
253
 
                        return KAlarm::CalEvent::ARCHIVED;
254
 
                    if (mime == KAlarm::MIME_TEMPLATE)
255
 
                        return KAlarm::CalEvent::TEMPLATE;
 
256
                    if (mime == KAlarmCal::MIME_ACTIVE)
 
257
                        return CalEvent::ACTIVE;
 
258
                    if (mime == KAlarmCal::MIME_ARCHIVED)
 
259
                        return CalEvent::ARCHIVED;
 
260
                    if (mime == KAlarmCal::MIME_TEMPLATE)
 
261
                        return CalEvent::TEMPLATE;
256
262
                    return QVariant();
257
263
                case CommandErrorRole:
258
264
                    if (!item.hasAttribute<EventAttribute>())
268
274
            if (!event.isValid())
269
275
                return QVariant();
270
276
            if (role == AlarmActionsRole)
271
 
                return event.actions();
272
 
            if (role == AlarmActionRole)
273
 
                return event.action();
 
277
                return event.actionTypes();
 
278
            if (role == AlarmSubActionRole)
 
279
                return event.actionSubType();
274
280
            bool calendarColour = false;
275
281
            switch (column)
276
282
            {
338
344
                    switch (role)
339
345
                    {
340
346
                        case Qt::BackgroundRole:
341
 
                            if (event.action() == KAEvent::MESSAGE
342
 
                            ||  event.action() == KAEvent::FILE
343
 
                            ||  (event.action() == KAEvent::COMMAND && event.commandDisplay()))
 
347
                        {
 
348
                            KAEvent::Actions type = event.actionTypes();
 
349
                            if (type & KAEvent::ACT_DISPLAY)
344
350
                                return event.bgColour();
345
 
                            if (event.action() == KAEvent::COMMAND)
 
351
                            if (type == KAEvent::ACT_COMMAND)
346
352
                            {
347
353
                                if (event.commandError() != KAEvent::CMD_NO_ERROR)
348
354
                                    return Qt::red;
349
355
                            }
350
356
                            break;
 
357
                        }
351
358
                        case Qt::ForegroundRole:
352
359
                            if (event.commandError() != KAEvent::CMD_NO_ERROR)
353
360
                            {
354
 
                                if (event.action() == KAEvent::COMMAND && !event.commandDisplay())
 
361
                                if (event.actionTypes() == KAEvent::ACT_COMMAND)
355
362
                                    return Qt::white;
356
363
                                QColor colour = Qt::red;
357
364
                                int r, g, b;
367
374
                            break;
368
375
                        case SortRole:
369
376
                        {
370
 
                            unsigned i = (event.action() == KAEvent::MESSAGE || event.action() == KAEvent::FILE)
371
 
                                       ? event.bgColour().rgb() : 0;
 
377
                            unsigned i = (event.actionTypes() == KAEvent::ACT_DISPLAY)
 
378
                                         ? event.bgColour().rgb() : 0;
372
379
                            return QString("%1").arg(i, 6, 10, QLatin1Char('0'));
373
380
                        }
374
381
                        default:
397
404
#endif
398
405
                            return QString();
399
406
                        case ValueRole:
400
 
                            return static_cast<int>(event.action());
 
407
                            return static_cast<int>(event.actionSubType());
401
408
                        case SortRole:
402
 
                            return QString("%1").arg(event.action(), 2, 10, QLatin1Char('0'));
 
409
                            return QString("%1").arg(event.actionSubType(), 2, 10, QLatin1Char('0'));
403
410
                    }
404
411
                    break;
405
412
                case TextColumn:
490
497
    {
491
498
        // This is a Collection row
492
499
        bool updateCollection = false;
 
500
        CollectionAttribute* attr = 0;
493
501
        switch (role)
494
502
        {
495
503
            case Qt::BackgroundRole:
496
504
            {
497
505
                QColor colour = value.value<QColor>();
498
 
                CollectionAttribute* attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
 
506
                attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
499
507
                if (attr->backgroundColor() == colour)
500
508
                    return true;   // no change
501
509
                attr->setBackgroundColor(colour);
502
510
                updateCollection = true;
503
511
                break;
504
512
            }
505
 
            case EnabledRole:
 
513
            case EnabledTypesRole:
506
514
            {
507
 
                KAlarm::CalEvent::Types types = static_cast<KAlarm::CalEvent::Types>(value.value<int>());
508
 
                CollectionAttribute* attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
 
515
                CalEvent::Types types = static_cast<CalEvent::Types>(value.value<int>());
 
516
                attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
509
517
                if (attr->enabled() == types)
510
518
                    return true;   // no change
511
519
                kDebug() << "Set enabled:" << types << ", was=" << attr->enabled();
517
525
                if (collection.hasAttribute<CollectionAttribute>()
518
526
                &&  isCompatible(collection))
519
527
                {
520
 
                    KAlarm::CalEvent::Types types = static_cast<KAlarm::CalEvent::Types>(value.value<int>());
521
 
CollectionAttribute* attr = collection.attribute<CollectionAttribute>();
 
528
                    CalEvent::Types types = static_cast<CalEvent::Types>(value.value<int>());
 
529
                    attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
522
530
kDebug()<<"Set standard:"<<types<<", was="<<attr->standard();
523
 
                    collection.attribute<CollectionAttribute>()->setStandard(types);
 
531
                    attr->setStandard(types);
524
532
                    updateCollection = true;
525
533
                }
526
534
                break;
 
535
            case KeepFormatRole:
 
536
            {
 
537
                bool keepFormat = value.value<bool>();
 
538
                attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
 
539
                if (attr->keepFormat() == keepFormat)
 
540
                    return true;   // no change
 
541
                attr->setKeepFormat(keepFormat);
 
542
                updateCollection = true;
 
543
                break;
 
544
            }
527
545
            default:
528
546
                break;
529
547
        }
530
548
        if (updateCollection)
531
549
        {
532
 
            CollectionModifyJob* job = new CollectionModifyJob(collection, this);
 
550
            // Update the CollectionAttribute value.
 
551
            // Note that we can't supply 'collection' to CollectionModifyJob since
 
552
            // that also contains the CompatibilityAttribute value, which is read-only
 
553
            // for applications. So create a new Collection instance and only set a
 
554
            // value for CollectionAttribute.
 
555
            Collection c(collection.id());
 
556
            CollectionAttribute* att = c.attribute<CollectionAttribute>(Entity::AddIfMissing);
 
557
            *att = *attr;
 
558
            CollectionModifyJob* job = new CollectionModifyJob(c, this);
533
559
            connect(job, SIGNAL(result(KJob*)), this, SLOT(modifyCollectionJobDone(KJob*)));
534
560
            return true;
535
561
        }
754
780
* Signal every minute that the time-to-alarm values have changed.
755
781
*/
756
782
static bool checkItem_isActive(const Item& item)
757
 
{ return item.mimeType() == KAlarm::MIME_ACTIVE; }
 
783
{ return item.mimeType() == KAlarmCal::MIME_ACTIVE; }
758
784
 
759
785
void AkonadiModel::slotUpdateTimeTo()
760
786
{
766
792
* Called when the colour used to display archived alarms has changed.
767
793
*/
768
794
static bool checkItem_isArchived(const Item& item)
769
 
{ return item.mimeType() == KAlarm::MIME_ARCHIVED; }
 
795
{ return item.mimeType() == KAlarmCal::MIME_ARCHIVED; }
770
796
 
771
797
void AkonadiModel::slotUpdateArchivedColour(const QColor&)
772
798
{
848
874
}
849
875
 
850
876
/******************************************************************************
 
877
* Return the foreground color for displaying a collection, based on the
 
878
* supplied mime types which it contains, and on whether it is fully writable.
 
879
*/
 
880
QColor AkonadiModel::foregroundColor(const Akonadi::Collection& collection, const QStringList& mimeTypes)
 
881
{
 
882
    QColor colour;
 
883
    if (mimeTypes.contains(KAlarmCal::MIME_ACTIVE))
 
884
        colour = KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText).color();
 
885
    else if (mimeTypes.contains(KAlarmCal::MIME_ARCHIVED))
 
886
        colour = Preferences::archivedColour();
 
887
    else if (mimeTypes.contains(KAlarmCal::MIME_TEMPLATE))
 
888
        colour = KColorScheme(QPalette::Active).foreground(KColorScheme::LinkText).color();
 
889
    if (colour.isValid()  &&  (collection.rights() & writableRights) != writableRights)
 
890
        return KColorUtils::lighten(colour, 0.25);
 
891
    return colour;
 
892
}
 
893
 
 
894
/******************************************************************************
851
895
* Set the background color for displaying the collection and its alarms.
852
896
*/
853
897
void AkonadiModel::setBackgroundColor(Collection& collection, const QColor& colour)
917
961
* Return a collection's tooltip text. The collection's enabled status is
918
962
* evaluated for specified alarm types.
919
963
*/
920
 
QString AkonadiModel::tooltip(const Collection& collection, KAlarm::CalEvent::Types types) const
 
964
QString AkonadiModel::tooltip(const Collection& collection, CalEvent::Types types) const
921
965
{
922
966
    QString name = '@' + displayName_p(collection);   // insert markers for stripping out name
923
967
    KUrl url = collection.remoteId();
925
969
    QString locn = url.pathOrUrl();
926
970
    bool inactive = !collection.hasAttribute<CollectionAttribute>()
927
971
                 || !(collection.attribute<CollectionAttribute>()->enabled() & types);
928
 
    bool writable = (collection.rights() & writableRights) == writableRights;
929
972
    QString disabled = i18nc("@info/plain", "Disabled");
930
 
    QString readonly = i18nc("@info/plain", "Read-only");
 
973
    QString readonly = readOnlyTooltip(collection);
 
974
    bool writable = readonly.isEmpty();
931
975
//if (!collection.hasAttribute<CollectionAttribute>()) { kDebug()<<"Tooltip: no collection attribute"; } else { kDebug()<<"Tooltip: enabled="<<collection.attribute<CollectionAttribute>()->enabled(); } //disabled="<<inactive;
932
976
    if (inactive  &&  !writable)
933
977
        return i18nc("@info:tooltip",
948
992
}
949
993
 
950
994
/******************************************************************************
 
995
* Return the read-only status tooltip for a collection.
 
996
* A null string is returned if the collection is fully writable.
 
997
*/
 
998
QString AkonadiModel::readOnlyTooltip(const Collection& collection)
 
999
{
 
1000
    KACalendar::Compat compat;
 
1001
    switch (AkonadiModel::isWritable(collection, compat))
 
1002
    {
 
1003
        case 1:
 
1004
            return QString();
 
1005
        case 0:
 
1006
            return i18nc("@info/plain", "Read-only (old format)");
 
1007
        default:
 
1008
            if (compat == KACalendar::Current)
 
1009
                return i18nc("@info/plain", "Read-only");
 
1010
            return i18nc("@info/plain", "Read-only (other format)");
 
1011
    }
 
1012
}
 
1013
 
 
1014
/******************************************************************************
951
1015
* Return the repetition text.
952
1016
*/
953
1017
QString AkonadiModel::repeatText(const KAEvent& event) const
1002
1066
*/
1003
1067
QPixmap* AkonadiModel::eventIcon(const KAEvent& event) const
1004
1068
{
1005
 
    switch (event.action())
 
1069
    switch (event.actionTypes())
1006
1070
    {
1007
 
        case KAAlarm::FILE:
1008
 
            return mFileIcon;
1009
 
        case KAAlarm::EMAIL:
 
1071
        case KAEvent::ACT_EMAIL:
1010
1072
            return mEmailIcon;
1011
 
        case KAAlarm::AUDIO:
 
1073
        case KAEvent::ACT_AUDIO:
1012
1074
            return mAudioIcon;
1013
 
        case KAAlarm::COMMAND:
1014
 
            if (!event.commandDisplay())
1015
 
                return mCommandIcon;
1016
 
            // fall through to MESSAGE
1017
 
        case KAAlarm::MESSAGE:
 
1075
        case KAEvent::ACT_COMMAND:
 
1076
            return mCommandIcon;
 
1077
        case KAEvent::ACT_DISPLAY:
 
1078
            if (event.actionSubType() == KAEvent::FILE)
 
1079
                return mFileIcon;
 
1080
            // fall through to ACT_DISPLAY_COMMAND
 
1081
        case KAEvent::ACT_DISPLAY_COMMAND:
1018
1082
        default:
1019
1083
            return mTextIcon;
1020
1084
    }
1047
1111
}
1048
1112
 
1049
1113
/******************************************************************************
1050
 
* Add a new collection. The user will be prompted to enter its configuration.
1051
 
*/
1052
 
AgentInstanceCreateJob* AkonadiModel::addCollection(KAlarm::CalEvent::Type type, QWidget* parent)
1053
 
{
1054
 
    // Use AutoQPointer to guard against crash on application exit while
1055
 
    // the dialogue is still open. It prevents double deletion (both on
1056
 
    // deletion of parent, and on return from this function).
1057
 
    AutoQPointer<AgentTypeDialog> dlg = new AgentTypeDialog(parent);
1058
 
    QString mimeType;
1059
 
    switch (type)
1060
 
    {
1061
 
        case KAlarm::CalEvent::ACTIVE:
1062
 
            mimeType = KAlarm::MIME_ACTIVE;
1063
 
            break;
1064
 
        case KAlarm::CalEvent::ARCHIVED:
1065
 
            mimeType = KAlarm::MIME_ARCHIVED;
1066
 
            break;
1067
 
        case KAlarm::CalEvent::TEMPLATE:
1068
 
            mimeType = KAlarm::MIME_TEMPLATE;
1069
 
            break;
1070
 
        default:
1071
 
            return 0;
1072
 
    }
1073
 
    dlg->agentFilterProxyModel()->addMimeTypeFilter(mimeType);
1074
 
    dlg->agentFilterProxyModel()->addCapabilityFilter(QLatin1String("Resource"));
1075
 
    if (dlg->exec() != QDialog::Accepted)
1076
 
        return 0;
1077
 
    const AgentType agentType = dlg->agentType();
1078
 
    if (!agentType.isValid())
1079
 
        return 0;
1080
 
    AgentInstanceCreateJob* job = new AgentInstanceCreateJob(agentType, parent);
1081
 
    if (agentType.identifier() == QLatin1String("akonadi_kalarm_dir_resource"))
1082
 
        mPendingColCreateJobs[job] = CollTypeData(type, parent);
1083
 
    else
1084
 
        job->configure(parent);    // cause the user to be prompted for configuration
1085
 
    connect(job, SIGNAL(result(KJob*)), SLOT(addCollectionJobDone(KJob*)));
1086
 
    job->start();
1087
 
    return job;
1088
 
}
1089
 
 
1090
 
/******************************************************************************
1091
 
* Called when an agent creation job has completed.
1092
 
* Checks for any error.
1093
 
*/
1094
 
void AkonadiModel::addCollectionJobDone(KJob* j)
1095
 
{
1096
 
    AgentInstanceCreateJob* job = static_cast<AgentInstanceCreateJob*>(j);
1097
 
    if (j->error())
1098
 
    {
1099
 
        kError() << "Failed to create new calendar resource:" << j->errorString();
1100
 
        KAMessageBox::error(MainWindow::mainMainWindow(), i18nc("@info", "%1<nl/>(%2)",
1101
 
                            i18nc("@info/plain", "Failed to create new calendar resource"), j->errorString()));
1102
 
        emit collectionAdded(job, false);
1103
 
    }
1104
 
    else
1105
 
    {
1106
 
        QMap<KJob*, CollTypeData>::iterator it = mPendingColCreateJobs.find(j);
1107
 
        if (it != mPendingColCreateJobs.end())
1108
 
        {
1109
 
            // Set the default alarm type for a directory resource config dialog
1110
 
            AgentInstance agent = static_cast<AgentInstanceCreateJob*>(job)->instance();
1111
 
            OrgKdeAkonadiKAlarmDirSettingsInterface *iface = new OrgKdeAkonadiKAlarmDirSettingsInterface("org.freedesktop.Akonadi.Resource." + agent.identifier(),
1112
 
                    "/Settings", QDBusConnection::sessionBus(), this);
1113
 
            if (!iface->isValid())
1114
 
                kError() << "Error creating D-Bus interface for KAlarmDir configuration.";
1115
 
            else
1116
 
            {
1117
 
                iface->setAlarmTypes(KAlarm::CalEvent::mimeTypes(it.value().alarmType));
1118
 
                iface->writeConfig();
1119
 
                agent.reconfigure();
1120
 
            }
1121
 
            agent.configure(it.value().parent);
1122
 
            delete iface;
1123
 
        }
1124
 
        emit collectionAdded(job, true);
1125
 
    }
1126
 
}
1127
 
 
1128
 
/******************************************************************************
1129
1114
* Remove a collection from Akonadi. The calendar file is not removed.
1130
1115
*/
1131
1116
bool AkonadiModel::removeCollection(const Akonadi::Collection& collection)
1217
1202
void AkonadiModel::modifyCollectionJobDone(KJob* j)
1218
1203
{
1219
1204
    Collection collection = static_cast<CollectionModifyJob*>(j)->collection();
 
1205
    Collection::Id id = collection.id();
1220
1206
    if (j->error())
1221
1207
    {
1222
 
        emit collectionModified(collection.id(), false);
1223
 
        QString errMsg = i18nc("@info", "Failed to update calendar <resource>%1</resource>.", displayName(collection));
1224
 
        kError() << errMsg << ":" << j->errorString();
1225
 
        KAMessageBox::error(MainWindow::mainMainWindow(), i18nc("@info", "%1<nl/>(%2)", errMsg, j->errorString()));
 
1208
        emit collectionModified(id, false);
 
1209
        if (mCollectionsDeleted.contains(id))
 
1210
            mCollectionsDeleted.removeAll(id);
 
1211
        else
 
1212
        {
 
1213
            QString errMsg = i18nc("@info", "Failed to update calendar <resource>%1</resource>.", displayName(collection));
 
1214
            kError() << "Id:" << collection.id() << errMsg << ":" << j->errorString();
 
1215
            KAMessageBox::error(MainWindow::mainMainWindow(), i18nc("@info", "%1<nl/>(%2)", errMsg, j->errorString()));
 
1216
        }
1226
1217
    }
1227
1218
    else
1228
 
        emit collectionModified(collection.id(), true);
 
1219
        emit collectionModified(id, true);
1229
1220
}
1230
1221
 
1231
1222
/******************************************************************************
1240
1231
/******************************************************************************
1241
1232
* Return all events of a given type belonging to a collection.
1242
1233
*/
1243
 
KAEvent::List AkonadiModel::events(Akonadi::Collection& collection, KAlarm::CalEvent::Type type) const
 
1234
KAEvent::List AkonadiModel::events(Akonadi::Collection& collection, CalEvent::Type type) const
1244
1235
{
1245
1236
    KAEvent::List list;
1246
1237
    QModelIndex ix = modelIndexForCollection(this, collection);
1252
1243
/******************************************************************************
1253
1244
* Recursive function to append all child Events with a given mime type.
1254
1245
*/
1255
 
void AkonadiModel::getChildEvents(const QModelIndex& parent, KAlarm::CalEvent::Type type, KAEvent::List& events) const
 
1246
void AkonadiModel::getChildEvents(const QModelIndex& parent, CalEvent::Type type, KAEvent::List& events) const
1256
1247
{
1257
1248
    for (int row = 0, count = rowCount(parent);  row < count;  ++row)
1258
1249
    {
1301
1292
/******************************************************************************
1302
1293
* Add an event to the default or a user-selected Collection.
1303
1294
*/
1304
 
AkonadiModel::Result AkonadiModel::addEvent(KAEvent* event, KAlarm::CalEvent::Type type, QWidget* promptParent, bool noPrompt)
 
1295
AkonadiModel::Result AkonadiModel::addEvent(KAEvent* event, CalEvent::Type type, QWidget* promptParent, bool noPrompt)
1305
1296
{
1306
1297
    kDebug() << event->id();
1307
1298
 
1335
1326
* Reply = true if item creation has been scheduled for all events,
1336
1327
*         false if at least one item creation failed to be scheduled.
1337
1328
*/
1338
 
bool AkonadiModel::addEvents(const QList<KAEvent*>& events, Collection& collection)
 
1329
bool AkonadiModel::addEvents(const KAEvent::List& events, Collection& collection)
1339
1330
{
1340
1331
    bool ok = true;
1341
1332
    for (int i = 0, count = events.count();  i < count;  ++i)
1593
1584
        {
1594
1585
            // A collection has been inserted
1595
1586
            kDebug() << "Collection" << collection.id() << collection.name();
 
1587
 
1596
1588
            QSet<QByteArray> attrs;
1597
1589
            attrs += CollectionAttribute::name();
1598
 
            setCollectionChanged(collection, attrs, false);
 
1590
            setCollectionChanged(collection, attrs, true);
1599
1591
            emit collectionAdded(collection);
 
1592
 
 
1593
            if (!mCollectionsBeingCreated.contains(collection.remoteId())
 
1594
            &&  (collection.rights() & writableRights) == writableRights)
 
1595
            {
 
1596
                // Update to current KAlarm format if necessary, and if the user agrees
 
1597
                CalendarMigrator::updateToCurrentFormat(collection, false, MainWindow::mainMainWindow());
 
1598
            }
1600
1599
        }
1601
1600
        else
1602
1601
        {
1650
1649
* Called when a monitored collection's properties or content have changed.
1651
1650
* Optionally emits a signal if properties of interest have changed.
1652
1651
*/
1653
 
void AkonadiModel::setCollectionChanged(const Collection& collection, const QSet<QByteArray>& attributeNames, bool signal)
 
1652
void AkonadiModel::setCollectionChanged(const Collection& collection, const QSet<QByteArray>& attributeNames, bool rowInserted)
1654
1653
{
1655
1654
    // Check for a read/write permission change
1656
1655
    Collection::Rights oldRights = mCollectionRights.value(collection.id(), Collection::AllRights);
1657
1656
    Collection::Rights newRights = collection.rights() & writableRights;
1658
1657
    if (newRights != oldRights)
1659
1658
    {
 
1659
        kDebug() << "Collection" << collection.id() << ": rights ->" << newRights;
1660
1660
        mCollectionRights[collection.id()] = newRights;
1661
 
        if (signal)
1662
 
            emit collectionStatusChanged(collection, ReadOnly, (newRights != writableRights));
 
1661
        emit collectionStatusChanged(collection, ReadOnly, (newRights != writableRights), rowInserted);
1663
1662
    }
1664
1663
 
1665
1664
    // Check for a change in content mime types
1666
1665
    // (e.g. when a collection is first created at startup).
1667
 
    KAlarm::CalEvent::Types oldAlarmTypes = mCollectionAlarmTypes.value(collection.id(), KAlarm::CalEvent::EMPTY);
1668
 
    KAlarm::CalEvent::Types newAlarmTypes = KAlarm::CalEvent::types(collection.contentMimeTypes());
 
1666
    CalEvent::Types oldAlarmTypes = mCollectionAlarmTypes.value(collection.id(), CalEvent::EMPTY);
 
1667
    CalEvent::Types newAlarmTypes = CalEvent::types(collection.contentMimeTypes());
1669
1668
    if (newAlarmTypes != oldAlarmTypes)
1670
1669
    {
1671
1670
        kDebug() << "Collection" << collection.id() << ": alarm types ->" << newAlarmTypes;
1672
1671
        mCollectionAlarmTypes[collection.id()] = newAlarmTypes;
1673
 
        if (signal)
1674
 
            emit collectionStatusChanged(collection, AlarmTypes, static_cast<int>(newAlarmTypes));
 
1672
        emit collectionStatusChanged(collection, AlarmTypes, static_cast<int>(newAlarmTypes), rowInserted);
1675
1673
    }
1676
1674
 
1677
1675
    // Check for the collection being enabled/disabled
1678
1676
    if (attributeNames.contains(CollectionAttribute::name()))
1679
1677
    {
1680
1678
        static bool first = true;
1681
 
kDebug()<<"COLLECTION ATTRIBUTE changed";
1682
 
        KAlarm::CalEvent::Types oldEnabled = mCollectionEnabled.value(collection.id(), KAlarm::CalEvent::EMPTY);
1683
 
        KAlarm::CalEvent::Types newEnabled = collection.hasAttribute<CollectionAttribute>() ? collection.attribute<CollectionAttribute>()->enabled() : KAlarm::CalEvent::EMPTY;
 
1679
        CalEvent::Types oldEnabled = mCollectionEnabled.value(collection.id(), CalEvent::EMPTY);
 
1680
        CalEvent::Types newEnabled = collection.hasAttribute<CollectionAttribute>() ? collection.attribute<CollectionAttribute>()->enabled() : CalEvent::EMPTY;
1684
1681
        if (first  ||  newEnabled != oldEnabled)
1685
1682
        {
1686
1683
            kDebug() << "Collection" << collection.id() << ": enabled ->" << newEnabled;
1687
1684
            first = false;
1688
1685
            mCollectionEnabled[collection.id()] = newEnabled;
1689
 
            if (signal)
1690
 
                emit collectionStatusChanged(collection, Enabled, static_cast<int>(newEnabled));
 
1686
            emit collectionStatusChanged(collection, Enabled, static_cast<int>(newEnabled), rowInserted);
1691
1687
        }
1692
1688
    }
 
1689
 
 
1690
    // Check for the backend calendar format changing
 
1691
    if (attributeNames.contains(CompatibilityAttribute::name()))
 
1692
    {
 
1693
        // Update to current KAlarm format if necessary, and if the user agrees
 
1694
        kDebug() << "CompatibilityAttribute";
 
1695
        Collection col(collection);
 
1696
        refresh(col);
 
1697
        CalendarMigrator::updateToCurrentFormat(col, false, MainWindow::mainMainWindow());
 
1698
    }
1693
1699
}
1694
1700
 
1695
1701
/******************************************************************************
1697
1703
*/
1698
1704
void AkonadiModel::slotCollectionRemoved(const Collection& collection)
1699
1705
{
1700
 
    kDebug() << collection.id();
1701
 
    mCollectionRights.remove(collection.id());
1702
 
    mCollectionsDeleting.removeAll(collection.id());
 
1706
    Collection::Id id = collection.id();
 
1707
    kDebug() << id;
 
1708
    mCollectionRights.remove(id);
 
1709
    mCollectionsDeleting.removeAll(id);
 
1710
    while (mCollectionsDeleted.count() > 20)   // don't let list grow indefinitely
 
1711
        mCollectionsDeleted.removeFirst();
 
1712
    mCollectionsDeleted << id;
 
1713
}
 
1714
 
 
1715
/******************************************************************************
 
1716
* Called when a collection creation is about to start, or has completed.
 
1717
*/
 
1718
void AkonadiModel::slotCollectionBeingCreated(const QString& path, bool finished)
 
1719
{
 
1720
    if (finished)
 
1721
        mCollectionsBeingCreated.removeAll(path);
 
1722
    else
 
1723
        mCollectionsBeingCreated << path;
1703
1724
}
1704
1725
 
1705
1726
/******************************************************************************
1824
1845
bool AkonadiModel::isCompatible(const Collection& collection)
1825
1846
{
1826
1847
    return collection.hasAttribute<CompatibilityAttribute>()
1827
 
       &&  collection.attribute<CompatibilityAttribute>()->compatibility() == KAlarm::Calendar::Current;
1828
 
}
1829
 
 
1830
 
KAlarm::CalEvent::Types AkonadiModel::types(const Collection& collection)
1831
 
{
1832
 
    KAlarm::CalEvent::Types types = 0;
1833
 
    QStringList mimeTypes = collection.contentMimeTypes();
1834
 
    if (mimeTypes.contains(KAlarm::MIME_ACTIVE))
1835
 
        types |= KAlarm::CalEvent::ACTIVE;
1836
 
    if (mimeTypes.contains(KAlarm::MIME_ARCHIVED))
1837
 
        types |= KAlarm::CalEvent::ARCHIVED;
1838
 
    if (mimeTypes.contains(KAlarm::MIME_TEMPLATE))
1839
 
        types |= KAlarm::CalEvent::TEMPLATE;
1840
 
    return types;
 
1848
       &&  collection.attribute<CompatibilityAttribute>()->compatibility() == KACalendar::Current;
1841
1849
}
1842
1850
 
1843
1851
/******************************************************************************
1844
 
* Check whether the alarm types in a calendar correspond with a collection's
1845
 
* mime types.
1846
 
* Reply = true if at least 1 alarm is the right type.
 
1852
* Return whether a collection is fully writable.
1847
1853
*/
1848
 
bool AkonadiModel::checkAlarmTypes(const Akonadi::Collection& collection, KCalCore::Calendar::Ptr& calendar)
1849
 
{
1850
 
    KAlarm::CalEvent::Types etypes = types(collection);
1851
 
    if (etypes)
1852
 
    {
1853
 
        bool have = false;
1854
 
        bool other = false;
1855
 
        const KCalCore::Event::List events = calendar->rawEvents();
1856
 
        for (int i = 0, iend = events.count();  i < iend;  ++i)
1857
 
        {
1858
 
            KAlarm::CalEvent::Type s = KAlarm::CalEvent::status(events[i]);
1859
 
            if (etypes & s)
1860
 
                have = true;
1861
 
            else
1862
 
                other = true;
1863
 
            if (have && other)
1864
 
                break;
1865
 
        }
1866
 
        if (!have  &&  other)
1867
 
            return false;   // contains only wrong alarm types
1868
 
    }
1869
 
    return true;
 
1854
int AkonadiModel::isWritable(const Akonadi::Collection& collection)
 
1855
{
 
1856
    KACalendar::Compat format;
 
1857
    return isWritable(collection, format);
 
1858
}
 
1859
 
 
1860
int AkonadiModel::isWritable(const Akonadi::Collection& collection, KACalendar::Compat& format)
 
1861
{
 
1862
    format = KACalendar::Incompatible;
 
1863
    if (!collection.isValid())
 
1864
        return -1;
 
1865
    Collection col = collection;
 
1866
    instance()->refresh(col);    // update with latest data
 
1867
    if ((col.rights() & writableRights) != writableRights)
 
1868
    {
 
1869
        format = KACalendar::Current;
 
1870
        return -1;
 
1871
    }
 
1872
    if (!col.hasAttribute<CompatibilityAttribute>())
 
1873
        return -1;
 
1874
    format = col.attribute<CompatibilityAttribute>()->compatibility();
 
1875
    switch (format)
 
1876
    {
 
1877
        case KACalendar::Current:
 
1878
            return 1;
 
1879
        case KACalendar::Converted:
 
1880
        case KACalendar::Convertible:
 
1881
            return 0;
 
1882
        default:
 
1883
            return -1;
 
1884
    }
 
1885
}
 
1886
 
 
1887
CalEvent::Types AkonadiModel::types(const Collection& collection)
 
1888
{
 
1889
    return CalEvent::types(collection.contentMimeTypes());
1870
1890
}
1871
1891
 
1872
1892
// vim: et sw=4: