contactviewer.cpp 13.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
    This file is part of Akonadi Contact.

    Copyright (c) 2009 Tobias Koenig <tokoe@kde.org>

    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 "contactviewer.h"

24
#include "contactmetadataakonadi_p.h"
Laurent Montel's avatar
Laurent Montel committed
25
#include "attributes/contactmetadataattribute_p.h"
26
#include "customfieldmanager_p.h"
27
#include "standardcontactformatter.h"
28
#include "textbrowser_p.h"
29

30
#include "editor/im/improtocols.h"
Laurent Montel's avatar
Laurent Montel committed
31
#include <KIOCore/kio/transferjob.h>
32
33
34
35
36
#include <collection.h>
#include <collectionfetchjob.h>
#include <entitydisplayattribute.h>
#include <item.h>
#include <itemfetchscope.h>
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
37
#include <kcontacts/addressee.h>
38
#include <kcolorscheme.h>
39
#include <kconfiggroup.h>
Laurent Montel's avatar
Laurent Montel committed
40
#include <KLocalizedString>
41
42
#include <kstringhandler.h>

43
#include <QVBoxLayout>
Laurent Montel's avatar
Laurent Montel committed
44
#include <QIcon>
45
#include <QUrlQuery>
46
#ifdef HAVE_PRISON
47
#include <prison/Prison>
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
48
#include <kcontacts/vcardconverter.h>
49
50
#endif // HAVE_PRISON

51
52
using namespace Akonadi;

53
class Q_DECL_HIDDEN ContactViewer::Private
54
{
Guy Maurel's avatar
Guy Maurel committed
55
56
57
public:
    Private(ContactViewer *parent)
        : mParent(parent)
Laurent Montel's avatar
Laurent Montel committed
58
        , mBrowser(nullptr)
Laurent Montel's avatar
Laurent Montel committed
59
        , mParentCollectionFetchJob(nullptr)
60
    {
Guy Maurel's avatar
Guy Maurel committed
61
62
        mStandardContactFormatter = new StandardContactFormatter;
        mContactFormatter = mStandardContactFormatter;
63
64
65
        KConfig config(QStringLiteral("akonadi_contactrc"));
        KConfigGroup group(&config, QStringLiteral("View"));
        mShowQRCode = group.readEntry("QRCodes", true);
66
#ifdef HAVE_PRISON
67
68
        mQRCode = Prison::createBarcode(Prison::QRCode);
        mDataMatrix = Prison::createBarcode(Prison::DataMatrix);
69
#endif // HAVE_PRISON
70
71
72
73
    }

    ~Private()
    {
Guy Maurel's avatar
Guy Maurel committed
74
        delete mStandardContactFormatter;
Sune Stolborg Vuorela's avatar
Sune Stolborg Vuorela committed
75
#ifdef HAVE_PRISON
Guy Maurel's avatar
Guy Maurel committed
76
77
        delete mQRCode;
        delete mDataMatrix;
Sune Stolborg Vuorela's avatar
Sune Stolborg Vuorela committed
78
#endif // HAVE_PRISON
79
80
    }

Guy Maurel's avatar
Guy Maurel committed
81
    void updateView(const QVariantList &localCustomFieldDescriptions = QVariantList(), const QString &addressBookName = QString())
82
    {
Laurent Montel's avatar
Laurent Montel committed
83
        static QPixmap defaultPixmap = QIcon::fromTheme(QStringLiteral("user-identity")).pixmap(QSize(100, 100));
Laurent Montel's avatar
Laurent Montel committed
84
85
        static QPixmap defaultMapPixmap = QIcon::fromTheme(QStringLiteral("document-open-remote")).pixmap(QSize(16, 16));
        static QPixmap defaultSmsPixmap = QIcon::fromTheme(IMProtocols::self()->icon(QStringLiteral("messaging/sms"))).pixmap(QSize(16, 16));
Guy Maurel's avatar
Guy Maurel committed
86
87
88
89
90

        mParent->setWindowTitle(i18n("Contact %1", mCurrentContact.assembledName()));

        if (mCurrentContact.photo().isIntern()) {
            mBrowser->document()->addResource(QTextDocument::ImageResource,
Laurent Montel's avatar
Laurent Montel committed
91
                                              QUrl(QStringLiteral("contact_photo")),
Guy Maurel's avatar
Guy Maurel committed
92
                                              mCurrentContact.photo().data());
93
        } else if (!mCurrentContact.photo().url().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
94
95
96
97
            QByteArray imageData;
            QImage image;
            KIO::TransferJob *job = KIO::get(QUrl(mCurrentContact.photo().url()), KIO::NoReload);
            QObject::connect(job, &KIO::TransferJob::data,
Laurent Montel's avatar
Laurent Montel committed
98
99
100
            [&imageData](KIO::Job *, const QByteArray & data) {
                imageData.append(data);
            });
Laurent Montel's avatar
Laurent Montel committed
101
102
103
            if (job->exec()) {
                if (image.loadFromData(imageData)) {
                    mBrowser->document()->addResource(QTextDocument::ImageResource,
Laurent Montel's avatar
Laurent Montel committed
104
                                                      QUrl(QStringLiteral("contact_photo")),
Laurent Montel's avatar
Laurent Montel committed
105
106
107
108
109
110
111
112
113
114
115
116
                                                      image);
                } else {
                    mBrowser->document()->addResource(QTextDocument::ImageResource,
                                                      QUrl(QStringLiteral("contact_photo")),
                                                      defaultPixmap);
                }
            } else {
                mBrowser->document()->addResource(QTextDocument::ImageResource,
                                                  QUrl(QStringLiteral("contact_photo")),
                                                  defaultPixmap);

            }
Guy Maurel's avatar
Guy Maurel committed
117
118
        } else {
            mBrowser->document()->addResource(QTextDocument::ImageResource,
Laurent Montel's avatar
Laurent Montel committed
119
                                              QUrl(QStringLiteral("contact_photo")),
Guy Maurel's avatar
Guy Maurel committed
120
121
122
123
124
                                              defaultPixmap);
        }

        if (mCurrentContact.logo().isIntern()) {
            mBrowser->document()->addResource(QTextDocument::ImageResource,
Laurent Montel's avatar
Laurent Montel committed
125
                                              QUrl(QStringLiteral("contact_logo")),
Guy Maurel's avatar
Guy Maurel committed
126
                                              mCurrentContact.logo().data());
127
        } else if (!mCurrentContact.logo().url().isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
128
129
130
131
            QByteArray imageData;
            QImage image;
            KIO::TransferJob *job = KIO::get(QUrl(mCurrentContact.logo().url()), KIO::NoReload);
            QObject::connect(job, &KIO::TransferJob::data,
Laurent Montel's avatar
Laurent Montel committed
132
133
134
            [&imageData](KIO::Job *, const QByteArray & data) {
                imageData.append(data);
            });
Laurent Montel's avatar
Laurent Montel committed
135
136
137
138
139
140
141
            if (job->exec()) {
                if (image.loadFromData(imageData)) {
                    mBrowser->document()->addResource(QTextDocument::ImageResource,
                                                      QUrl(QStringLiteral("contact_logo")),
                                                      image);
                }
            }
Guy Maurel's avatar
Guy Maurel committed
142
143
144
        }

        mBrowser->document()->addResource(QTextDocument::ImageResource,
Laurent Montel's avatar
Laurent Montel committed
145
                                          QUrl(QStringLiteral("map_icon")),
Laurent Montel's avatar
Laurent Montel committed
146
                                          defaultMapPixmap);
Guy Maurel's avatar
Guy Maurel committed
147
148

        mBrowser->document()->addResource(QTextDocument::ImageResource,
Laurent Montel's avatar
Laurent Montel committed
149
                                          QUrl(QStringLiteral("sms_icon")),
Laurent Montel's avatar
Laurent Montel committed
150
                                          defaultSmsPixmap);
151

152
#ifdef HAVE_PRISON
153
        if (mShowQRCode) {
Guy Maurel's avatar
Guy Maurel committed
154
155
156
157
158
159
160
161
            KContacts::VCardConverter converter;
            KContacts::Addressee addr(mCurrentContact);
            addr.setPhoto(KContacts::Picture());
            addr.setLogo(KContacts::Picture());
            const QString data = QString::fromUtf8(converter.createVCard(addr));
            mQRCode->setData(data);
            mDataMatrix->setData(data);
            mBrowser->document()->addResource(QTextDocument::ImageResource,
Laurent Montel's avatar
Laurent Montel committed
162
                                              QUrl(QStringLiteral("qrcode")),
Guy Maurel's avatar
Guy Maurel committed
163
164
                                              mQRCode->toImage(QSizeF(50, 50)));
            mBrowser->document()->addResource(QTextDocument::ImageResource,
Laurent Montel's avatar
Laurent Montel committed
165
                                              QUrl(QStringLiteral("datamatrix")),
Guy Maurel's avatar
Guy Maurel committed
166
167
                                              mDataMatrix->toImage(QSizeF(50, 50)));
        }
168
169
#endif // HAVE_PRISON

Guy Maurel's avatar
Guy Maurel committed
170
        // merge local and global custom field descriptions
Laurent Montel's avatar
Laurent Montel committed
171
        QVector<QVariantMap> customFieldDescriptions;
Sergio Martins's avatar
Sergio Martins committed
172
173
        const CustomField::List globalCustomFields = CustomFieldManager::globalCustomFieldDescriptions();
        customFieldDescriptions.reserve(localCustomFieldDescriptions.count() + globalCustomFields.count());
Laurent Montel's avatar
Laurent Montel committed
174
        for (const QVariant &entry : qAsConst(localCustomFieldDescriptions)) {
Guy Maurel's avatar
Guy Maurel committed
175
176
            customFieldDescriptions << entry.toMap();
        }
177

Laurent Montel's avatar
Laurent Montel committed
178
        for (const CustomField &field : qAsConst(globalCustomFields)) {
Guy Maurel's avatar
Guy Maurel committed
179
            QVariantMap description;
Laurent Montel's avatar
Laurent Montel committed
180
181
            description.insert(QStringLiteral("key"), field.key());
            description.insert(QStringLiteral("title"), field.title());
182

Guy Maurel's avatar
Guy Maurel committed
183
184
            customFieldDescriptions << description;
        }
185

Guy Maurel's avatar
Guy Maurel committed
186
187
        KContacts::Addressee contact(mCurrentContact);
        if (!addressBookName.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
188
            contact.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("AddressBook"), addressBookName);
Guy Maurel's avatar
Guy Maurel committed
189
        }
190

Guy Maurel's avatar
Guy Maurel committed
191
192
        mContactFormatter->setContact(contact);
        mContactFormatter->setCustomFieldDescriptions(customFieldDescriptions);
193

Guy Maurel's avatar
Guy Maurel committed
194
        mBrowser->setHtml(mContactFormatter->toHtml());
195
196
    }

Guy Maurel's avatar
Guy Maurel committed
197
    void slotMailClicked(const QString &, const QString &email)
198
    {
Guy Maurel's avatar
Guy Maurel committed
199
        QString name, address;
200

Guy Maurel's avatar
Guy Maurel committed
201
202
        // remove the 'mailto:' and split into name and email address
        KContacts::Addressee::parseEmailAddress(email.mid(7), name, address);
203

Laurent Montel's avatar
Laurent Montel committed
204
        Q_EMIT mParent->emailClicked(name, address);
205
206
    }

Guy Maurel's avatar
Guy Maurel committed
207
    void slotUrlClicked(const QUrl &url)
208
    {
209
        const QUrlQuery query(url);
Guy Maurel's avatar
Guy Maurel committed
210
211
        const QString urlScheme(url.scheme());
        if (urlScheme == QLatin1String("http") ||
Laurent Montel's avatar
Laurent Montel committed
212
                urlScheme == QLatin1String("https")) {
Laurent Montel's avatar
Laurent Montel committed
213
            Q_EMIT mParent->urlClicked(url);
Guy Maurel's avatar
Guy Maurel committed
214
        } else if (urlScheme == QLatin1String("phone")) {
215
            const int pos = query.queryItemValue(QStringLiteral("index")).toInt();
Guy Maurel's avatar
Guy Maurel committed
216
217
218

            const KContacts::PhoneNumber::List numbers = mCurrentContact.phoneNumbers();
            if (pos < numbers.count()) {
Laurent Montel's avatar
Laurent Montel committed
219
                Q_EMIT mParent->phoneNumberClicked(numbers.at(pos));
Guy Maurel's avatar
Guy Maurel committed
220
221
            }
        } else if (urlScheme == QLatin1String("sms")) {
222
            const int pos = query.queryItemValue(QStringLiteral("index")).toInt();
Guy Maurel's avatar
Guy Maurel committed
223
224
225

            const KContacts::PhoneNumber::List numbers = mCurrentContact.phoneNumbers();
            if (pos < numbers.count()) {
Laurent Montel's avatar
Laurent Montel committed
226
                Q_EMIT mParent->smsClicked(numbers.at(pos));
Guy Maurel's avatar
Guy Maurel committed
227
228
            }
        } else if (urlScheme == QLatin1String("address")) {
229
            const int pos = query.queryItemValue(QStringLiteral("index")).toInt();
Guy Maurel's avatar
Guy Maurel committed
230
231
232

            const KContacts::Address::List addresses = mCurrentContact.addresses();
            if (pos < addresses.count()) {
Laurent Montel's avatar
Laurent Montel committed
233
                Q_EMIT mParent->addressClicked(addresses.at(pos));
Guy Maurel's avatar
Guy Maurel committed
234
235
236
237
238
239
240
            }
        } else if (urlScheme == QLatin1String("mailto")) {
            QString name, address;

            // remove the 'mailto:' and split into name and email address
            KContacts::Addressee::parseEmailAddress(url.path(), name, address);

Laurent Montel's avatar
Laurent Montel committed
241
            Q_EMIT mParent->emailClicked(name, address);
242
        }
243
244
    }

Guy Maurel's avatar
Guy Maurel committed
245
    void slotParentCollectionFetched(KJob *job)
246
    {
Laurent Montel's avatar
Laurent Montel committed
247
        mParentCollectionFetchJob = nullptr;
248

Guy Maurel's avatar
Guy Maurel committed
249
        QString addressBookName;
250

Guy Maurel's avatar
Guy Maurel committed
251
252
253
        if (!job->error()) {
            CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>(job);
            if (!fetchJob->collections().isEmpty()) {
Sergio Martins's avatar
Sergio Martins committed
254
                const Collection collection = fetchJob->collections().at(0);
Guy Maurel's avatar
Guy Maurel committed
255
256
                addressBookName = collection.displayName();
            }
257
258
        }

Guy Maurel's avatar
Guy Maurel committed
259
        // load the local meta data of the item
260
        ContactMetaDataAkonadi metaData;
Guy Maurel's avatar
Guy Maurel committed
261
        metaData.load(mCurrentItem);
262

Guy Maurel's avatar
Guy Maurel committed
263
        updateView(metaData.customFieldDescriptions(), addressBookName);
264
265
    }

266
    ContactViewer *mParent;
267
    TextBrowser *mBrowser;
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
268
    KContacts::Addressee mCurrentContact;
269
    Item mCurrentItem;
270
    AbstractContactFormatter *mContactFormatter;
271
    AbstractContactFormatter *mStandardContactFormatter;
272
    CollectionFetchJob *mParentCollectionFetchJob;
273
    bool mShowQRCode;
274
#ifdef HAVE_PRISON
275
276
    Prison::AbstractBarcode *mQRCode;
    Prison::AbstractBarcode *mDataMatrix;
277
#endif // HAVE_PRISON
278
279
};

Guy Maurel's avatar
Guy Maurel committed
280
281
282
ContactViewer::ContactViewer(QWidget *parent)
    : QWidget(parent)
    , d(new Private(this))
283
{
Guy Maurel's avatar
Guy Maurel committed
284
285
    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->setMargin(0);
286

Guy Maurel's avatar
Guy Maurel committed
287
    d->mBrowser = new TextBrowser;
288

Guy Maurel's avatar
Guy Maurel committed
289
290
    connect(d->mBrowser, SIGNAL(anchorClicked(QUrl)),
            this, SLOT(slotUrlClicked(QUrl)));
291

Guy Maurel's avatar
Guy Maurel committed
292
    layout->addWidget(d->mBrowser);
293

Guy Maurel's avatar
Guy Maurel committed
294
295
296
297
    // always fetch full payload for contacts
    fetchScope().fetchFullPayload();
    fetchScope().fetchAttribute<ContactMetaDataAttribute>();
    fetchScope().setAncestorRetrieval(ItemFetchScope::Parent);
298
299
300
301
}

ContactViewer::~ContactViewer()
{
Guy Maurel's avatar
Guy Maurel committed
302
    delete d;
303
304
305
306
}

Akonadi::Item ContactViewer::contact() const
{
Guy Maurel's avatar
Guy Maurel committed
307
    return ItemMonitor::item();
308
309
}

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
310
KContacts::Addressee ContactViewer::rawContact() const
311
{
Guy Maurel's avatar
Guy Maurel committed
312
    return d->mCurrentContact;
313
314
}

Guy Maurel's avatar
Guy Maurel committed
315
void ContactViewer::setContactFormatter(AbstractContactFormatter *formatter)
316
{
Laurent Montel's avatar
Laurent Montel committed
317
    if (formatter == nullptr) {
Guy Maurel's avatar
Guy Maurel committed
318
319
320
        d->mContactFormatter = d->mStandardContactFormatter;
    } else {
        d->mContactFormatter = formatter;
Laurent Montel's avatar
Laurent Montel committed
321
        delete d->mStandardContactFormatter;
Laurent Montel's avatar
Laurent Montel committed
322
        d->mStandardContactFormatter = nullptr;
Guy Maurel's avatar
Guy Maurel committed
323
    }
324
325
}

Guy Maurel's avatar
Guy Maurel committed
326
void ContactViewer::setContact(const Akonadi::Item &contact)
327
{
Guy Maurel's avatar
Guy Maurel committed
328
    ItemMonitor::setItem(contact);
329
330
}

Guy Maurel's avatar
Guy Maurel committed
331
void ContactViewer::setRawContact(const KContacts::Addressee &contact)
332
{
Guy Maurel's avatar
Guy Maurel committed
333
    d->mCurrentContact = contact;
334

Guy Maurel's avatar
Guy Maurel committed
335
    d->updateView();
336
337
}

Guy Maurel's avatar
Guy Maurel committed
338
void ContactViewer::itemChanged(const Item &contactItem)
339
{
Guy Maurel's avatar
Guy Maurel committed
340
341
342
343
344
345
346
347
348
349
350
    if (!contactItem.hasPayload<KContacts::Addressee>()) {
        return;
    }

    d->mCurrentItem = contactItem;
    d->mCurrentContact = contactItem.payload<KContacts::Addressee>();

    // stop any running fetch job
    if (d->mParentCollectionFetchJob) {
        disconnect(d->mParentCollectionFetchJob, SIGNAL(result(KJob*)), this, SLOT(slotParentCollectionFetched(KJob*)));
        delete d->mParentCollectionFetchJob;
Laurent Montel's avatar
Laurent Montel committed
351
        d->mParentCollectionFetchJob = nullptr;
Guy Maurel's avatar
Guy Maurel committed
352
353
354
355
    }

    d->mParentCollectionFetchJob = new CollectionFetchJob(contactItem.parentCollection(), CollectionFetchJob::Base, this);
    connect(d->mParentCollectionFetchJob, SIGNAL(result(KJob*)), SLOT(slotParentCollectionFetched(KJob*)));
356
357
358
359
}

void ContactViewer::itemRemoved()
{
Guy Maurel's avatar
Guy Maurel committed
360
    d->mBrowser->clear();
361
362
}

Laurent Montel's avatar
Laurent Montel committed
363
364
void ContactViewer::updateView()
{
365
    itemChanged(d->mCurrentItem);
Laurent Montel's avatar
Laurent Montel committed
366
367
}

368
369
370
371
372
373
374
375
376
377
378
379
380
void ContactViewer::setShowQRCode(bool b)
{
    if (d->mShowQRCode != b) {
        d->mShowQRCode = b;
        updateView();
    }
}

bool ContactViewer::showQRCode() const
{
    return d->mShowQRCode;
}

381
#include "moc_contactviewer.cpp"