contactgroupeditor.cpp 10.2 KB
Newer Older
1
/*
Tobias Koenig's avatar
Tobias Koenig committed
2 3 4
    This file is part of Akonadi Contact.

    Copyright (c) 2007-2009 Tobias Koenig <tokoe@kde.org>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library 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 Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "contactgroupeditor.h"
Tobias Koenig's avatar
Tobias Koenig committed
23
#include "contactgroupeditor_p.h"
24

25
#include "autoqpointer_p.h"
26 27
#include "contactgroupmodel_p.h"
#include "contactgroupeditordelegate_p.h"
28
#include "waitingoverlay_p.h"
29

30 31 32 33 34 35 36 37
#include <collectiondialog.h>
#include <collectionfetchjob.h>
#include <itemcreatejob.h>
#include <itemfetchjob.h>
#include <itemfetchscope.h>
#include <itemmodifyjob.h>
#include <monitor.h>
#include <session.h>
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
38
#include <kcontacts/contactgroup.h>
Laurent Montel's avatar
Laurent Montel committed
39 40
#include <KLocalizedString>
#include <KMessageBox>
41 42
#include <KColorScheme>

Laurent Montel's avatar
Laurent Montel committed
43
#include <QTimer>
44
#include <QMessageBox>
45

Tobias Koenig's avatar
Tobias Koenig committed
46
using namespace Akonadi;
47

Guy Maurel's avatar
Guy Maurel committed
48 49
ContactGroupEditor::Private::Private(ContactGroupEditor *parent)
    : mParent(parent)
Laurent Montel's avatar
Laurent Montel committed
50
    , mMonitor(nullptr)
Guy Maurel's avatar
Guy Maurel committed
51
    , mReadOnly(false)
Laurent Montel's avatar
Laurent Montel committed
52
    , mGroupModel(nullptr)
53
{
Tobias Koenig's avatar
Tobias Koenig committed
54
}
55

Tobias Koenig's avatar
Tobias Koenig committed
56 57
ContactGroupEditor::Private::~Private()
{
Guy Maurel's avatar
Guy Maurel committed
58
    delete mMonitor;
59 60
}

Tobias Koenig's avatar
Tobias Koenig committed
61 62
void ContactGroupEditor::Private::adaptHeaderSizes()
{
Guy Maurel's avatar
Guy Maurel committed
63 64
    mGui.membersView->header()->setDefaultSectionSize(mGui.membersView->header()->width() / 2);
    mGui.membersView->header()->resizeSections(QHeaderView::Interactive);
Tobias Koenig's avatar
Tobias Koenig committed
65
}
66

Guy Maurel's avatar
Guy Maurel committed
67
void ContactGroupEditor::Private::itemFetchDone(KJob *job)
68
{
Guy Maurel's avatar
Guy Maurel committed
69 70 71 72 73 74 75 76 77 78 79 80
    if (job->error()) {
        return;
    }

    ItemFetchJob *fetchJob = qobject_cast<ItemFetchJob *>(job);
    if (!fetchJob) {
        return;
    }

    if (fetchJob->items().isEmpty()) {
        return;
    }
81

Sergio Martins's avatar
Sergio Martins committed
82
    mItem = fetchJob->items().at(0);
83

Guy Maurel's avatar
Guy Maurel committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
    mReadOnly = false;
    if (mMode == ContactGroupEditor::EditMode) {
        // if in edit mode we have to fetch the parent collection to find out
        // about the modify rights of the item

        Akonadi::CollectionFetchJob *collectionFetchJob = new Akonadi::CollectionFetchJob(mItem.parentCollection(),
                Akonadi::CollectionFetchJob::Base);
        mParent->connect(collectionFetchJob, SIGNAL(result(KJob*)),
                         SLOT(parentCollectionFetchDone(KJob*)));
    } else {
        const KContacts::ContactGroup group = mItem.payload<KContacts::ContactGroup>();
        loadContactGroup(group);

        setReadOnly(mReadOnly);

        QTimer::singleShot(0, mParent, SLOT(adaptHeaderSizes()));
    }
101 102
}

Guy Maurel's avatar
Guy Maurel committed
103
void ContactGroupEditor::Private::parentCollectionFetchDone(KJob *job)
104
{
Guy Maurel's avatar
Guy Maurel committed
105 106 107
    if (job->error()) {
        return;
    }
108

Guy Maurel's avatar
Guy Maurel committed
109 110 111 112
    Akonadi::CollectionFetchJob *fetchJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
    if (!fetchJob) {
        return;
    }
113

Sergio Martins's avatar
Sergio Martins committed
114
    const Akonadi::Collection parentCollection = fetchJob->collections().at(0);
Guy Maurel's avatar
Guy Maurel committed
115 116 117
    if (parentCollection.isValid()) {
        mReadOnly = !(parentCollection.rights() & Collection::CanChangeItem);
    }
118

Guy Maurel's avatar
Guy Maurel committed
119 120
    const KContacts::ContactGroup group = mItem.payload<KContacts::ContactGroup>();
    loadContactGroup(group);
121

Guy Maurel's avatar
Guy Maurel committed
122
    setReadOnly(mReadOnly);
123

Guy Maurel's avatar
Guy Maurel committed
124
    QTimer::singleShot(0, mParent, SLOT(adaptHeaderSizes()));
125 126
}

Guy Maurel's avatar
Guy Maurel committed
127
void ContactGroupEditor::Private::storeDone(KJob *job)
128
{
Guy Maurel's avatar
Guy Maurel committed
129
    if (job->error()) {
Laurent Montel's avatar
Laurent Montel committed
130
        Q_EMIT mParent->error(job->errorString());
Guy Maurel's avatar
Guy Maurel committed
131 132 133 134
        return;
    }

    if (mMode == EditMode) {
Laurent Montel's avatar
Laurent Montel committed
135
        Q_EMIT mParent->contactGroupStored(mItem);
Guy Maurel's avatar
Guy Maurel committed
136
    } else if (mMode == CreateMode) {
Laurent Montel's avatar
Laurent Montel committed
137
        Q_EMIT mParent->contactGroupStored(static_cast<ItemCreateJob *>(job)->item());
Guy Maurel's avatar
Guy Maurel committed
138
    }
139 140
}

Guy Maurel's avatar
Guy Maurel committed
141
void ContactGroupEditor::Private::itemChanged(const Item &item, const QSet<QByteArray> &)
142
{
Guy Maurel's avatar
Guy Maurel committed
143
    Q_UNUSED(item)
Guy Maurel's avatar
Guy Maurel committed
144
    AutoQPointer<QMessageBox> dlg = new QMessageBox(mParent);   //krazy:exclude=qclasses
145

Guy Maurel's avatar
Guy Maurel committed
146 147 148
    dlg->setInformativeText(i18n("The contact group has been changed by someone else.\nWhat should be done?"));
    dlg->addButton(i18n("Take over changes"), QMessageBox::AcceptRole);
    dlg->addButton(i18n("Ignore and Overwrite changes"), QMessageBox::RejectRole);
149

Guy Maurel's avatar
Guy Maurel committed
150 151 152 153
    if (dlg->exec() == QMessageBox::AcceptRole) {
        ItemFetchJob *job = new ItemFetchJob(mItem);
        job->fetchScope().fetchFullPayload();
        job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
154

Guy Maurel's avatar
Guy Maurel committed
155 156 157
        mParent->connect(job, SIGNAL(result(KJob*)), mParent, SLOT(itemFetchDone(KJob*)));
        new WaitingOverlay(job, mParent);
    }
158 159
}

Guy Maurel's avatar
Guy Maurel committed
160
void ContactGroupEditor::Private::loadContactGroup(const KContacts::ContactGroup &group)
161
{
Guy Maurel's avatar
Guy Maurel committed
162
    mGui.groupName->setText(group.name());
163

Guy Maurel's avatar
Guy Maurel committed
164
    mGroupModel->loadContactGroup(group);
165

Guy Maurel's avatar
Guy Maurel committed
166 167
    const QAbstractItemModel *model = mGui.membersView->model();
    mGui.membersView->setCurrentIndex(model->index(model->rowCount() - 1, 0));
168

Guy Maurel's avatar
Guy Maurel committed
169 170 171
    if (mMode == EditMode) {
        mGui.membersView->setFocus();
    }
172

Guy Maurel's avatar
Guy Maurel committed
173
    mGui.membersView->header()->resizeSections(QHeaderView::Stretch);
174 175
}

Guy Maurel's avatar
Guy Maurel committed
176
bool ContactGroupEditor::Private::storeContactGroup(KContacts::ContactGroup &group)
177
{
Guy Maurel's avatar
Guy Maurel committed
178 179 180 181
    if (mGui.groupName->text().isEmpty()) {
        KMessageBox::error(mParent, i18n("The name of the contact group must not be empty."));
        return false;
    }
182

Guy Maurel's avatar
Guy Maurel committed
183
    group.setName(mGui.groupName->text());
184

Guy Maurel's avatar
Guy Maurel committed
185 186 187 188
    if (!mGroupModel->storeContactGroup(group)) {
        KMessageBox::error(mParent, mGroupModel->lastErrorMessage());
        return false;
    }
189

Guy Maurel's avatar
Guy Maurel committed
190
    return true;
191 192 193 194
}

void ContactGroupEditor::Private::setupMonitor()
{
Guy Maurel's avatar
Guy Maurel committed
195 196 197
    delete mMonitor;
    mMonitor = new Monitor;
    mMonitor->ignoreSession(Session::defaultSession());
198

Guy Maurel's avatar
Guy Maurel committed
199 200
    connect(mMonitor, SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>)),
            mParent, SLOT(itemChanged(Akonadi::Item,QSet<QByteArray>)));
201 202
}

Guy Maurel's avatar
Guy Maurel committed
203
void ContactGroupEditor::Private::setReadOnly(bool readOnly)
204
{
Guy Maurel's avatar
Guy Maurel committed
205 206
    mGui.groupName->setReadOnly(readOnly);
    mGui.membersView->setEnabled(!readOnly);
207 208
}

Guy Maurel's avatar
Guy Maurel committed
209
ContactGroupEditor::ContactGroupEditor(Mode mode, QWidget *parent)
Guy Maurel's avatar
Guy Maurel committed
210 211
    : QWidget(parent)
    , d(new Private(this))
212
{
Guy Maurel's avatar
Guy Maurel committed
213 214
    d->mMode = mode;
    d->mGui.setupUi(this);
215

Guy Maurel's avatar
Guy Maurel committed
216
    d->mGui.membersView->setEditTriggers(QAbstractItemView::AllEditTriggers);
217

Guy Maurel's avatar
Guy Maurel committed
218 219 220
    d->mGroupModel = new ContactGroupModel(this);
    d->mGui.membersView->setModel(d->mGroupModel);
    d->mGui.membersView->setItemDelegate(new ContactGroupEditorDelegate(d->mGui.membersView, this));
221

Guy Maurel's avatar
Guy Maurel committed
222 223 224
    if (mode == CreateMode) {
        KContacts::ContactGroup dummyGroup;
        d->mGroupModel->loadContactGroup(dummyGroup);
225

Guy Maurel's avatar
Guy Maurel committed
226 227 228
        QTimer::singleShot(0, this, SLOT(adaptHeaderSizes()));
        QTimer::singleShot(0, d->mGui.groupName, SLOT(setFocus()));
    }
229

Guy Maurel's avatar
Guy Maurel committed
230
    d->mGui.membersView->header()->setStretchLastSection(true);
231
}
232 233 234

ContactGroupEditor::~ContactGroupEditor()
{
Guy Maurel's avatar
Guy Maurel committed
235
    delete d;
236 237
}

Guy Maurel's avatar
Guy Maurel committed
238
void ContactGroupEditor::loadContactGroup(const Akonadi::Item &item)
239
{
Guy Maurel's avatar
Guy Maurel committed
240 241 242
    if (d->mMode == CreateMode) {
        Q_ASSERT_X(false, "ContactGroupEditor::loadContactGroup", "You are calling loadContactGroup in CreateMode!");
    }
243

Guy Maurel's avatar
Guy Maurel committed
244 245 246
    ItemFetchJob *job = new ItemFetchJob(item);
    job->fetchScope().fetchFullPayload();
    job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
247

Guy Maurel's avatar
Guy Maurel committed
248
    connect(job, SIGNAL(result(KJob*)), SLOT(itemFetchDone(KJob*)));
249

Guy Maurel's avatar
Guy Maurel committed
250 251
    d->setupMonitor();
    d->mMonitor->setItemMonitored(item);
252

Guy Maurel's avatar
Guy Maurel committed
253
    new WaitingOverlay(job, this);
254 255 256 257
}

bool ContactGroupEditor::saveContactGroup()
{
Guy Maurel's avatar
Guy Maurel committed
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
    if (d->mMode == EditMode) {
        if (!d->mItem.isValid()) {
            return false;
        }

        if (d->mReadOnly) {
            return true;
        }

        KContacts::ContactGroup group = d->mItem.payload<KContacts::ContactGroup>();

        if (!d->storeContactGroup(group)) {
            return false;
        }

        d->mItem.setPayload<KContacts::ContactGroup>(group);

        ItemModifyJob *job = new ItemModifyJob(d->mItem);
        connect(job, SIGNAL(result(KJob*)), SLOT(storeDone(KJob*)));
    } else if (d->mMode == CreateMode) {
        if (!d->mDefaultCollection.isValid()) {
            const QStringList mimeTypeFilter(KContacts::ContactGroup::mimeType());

            AutoQPointer<CollectionDialog> dlg = new CollectionDialog(this);
            dlg->setMimeTypeFilter(mimeTypeFilter);
            dlg->setAccessRightsFilter(Collection::CanCreateItem);
Laurent Montel's avatar
Laurent Montel committed
284
            dlg->setWindowTitle(i18n("Select Address Book"));
Guy Maurel's avatar
Guy Maurel committed
285 286
            dlg->setDescription(i18n("Select the address book the new contact group shall be saved in:"));

Laurent Montel's avatar
Laurent Montel committed
287
            if (dlg->exec() == QDialog::Accepted) {
Guy Maurel's avatar
Guy Maurel committed
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
                setDefaultAddressBook(dlg->selectedCollection());
            } else {
                return false;
            }
        }

        KContacts::ContactGroup group;
        if (!d->storeContactGroup(group)) {
            return false;
        }

        Item item;
        item.setPayload<KContacts::ContactGroup>(group);
        item.setMimeType(KContacts::ContactGroup::mimeType());

        ItemCreateJob *job = new ItemCreateJob(item, d->mDefaultCollection);
        connect(job, SIGNAL(result(KJob*)), SLOT(storeDone(KJob*)));
305
    }
306

Guy Maurel's avatar
Guy Maurel committed
307
    return true;
308 309
}

Guy Maurel's avatar
Guy Maurel committed
310
void ContactGroupEditor::setContactGroupTemplate(const KContacts::ContactGroup &group)
311
{
Guy Maurel's avatar
Guy Maurel committed
312 313 314
    d->mGroupModel->loadContactGroup(group);
    d->mGui.membersView->header()->setDefaultSectionSize(d->mGui.membersView->header()->width() / 2);
    d->mGui.membersView->header()->resizeSections(QHeaderView::Interactive);
315 316
}

Guy Maurel's avatar
Guy Maurel committed
317
void ContactGroupEditor::setDefaultAddressBook(const Akonadi::Collection &collection)
318
{
Guy Maurel's avatar
Guy Maurel committed
319
    d->mDefaultCollection = collection;
320 321
}

322 323 324
void ContactGroupEditor::groupNameIsValid(bool isValid)
{
#ifndef QT_NO_STYLE_STYLESHEET
Guy Maurel's avatar
Guy Maurel committed
325 326 327 328
    QString styleSheet;
    if (!isValid) {
        const KColorScheme::BackgroundRole bgColorScheme(KColorScheme::NegativeBackground);
        KStatefulBrush bgBrush(KColorScheme::View, bgColorScheme);
Laurent Montel's avatar
Laurent Montel committed
329
        styleSheet = QStringLiteral("QLineEdit{ background-color:%1 }").
Guy Maurel's avatar
Guy Maurel committed
330 331 332
                     arg(bgBrush.brush(this).color().name());
    }
    d->mGui.groupName->setStyleSheet(styleSheet);
333 334 335
#endif
}

336
#include "moc_contactgroupeditor.cpp"