Akonadi Contacts

contactviewer.cpp
1 /*
2  This file is part of Akonadi Contact.
3 
4  Copyright (c) 2009 Tobias Koenig <[email protected]>
5 
6  This library is free software; you can redistribute it and/or modify it
7  under the terms of the GNU Library General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or (at your
9  option) any later version.
10 
11  This library is distributed in the hope that it will be useful, but WITHOUT
12  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14  License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to the
18  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  02110-1301, USA.
20 */
21 
22 #include "contactviewer.h"
23 
24 #include "contactmetadataakonadi_p.h"
25 #include "attributes/contactmetadataattribute_p.h"
26 #include "customfieldmanager_p.h"
27 #include "standardcontactformatter.h"
28 #include "textbrowser_p.h"
29 
30 #include <KIOCore/kio/transferjob.h>
31 #include <collection.h>
32 #include <collectionfetchjob.h>
33 #include <entitydisplayattribute.h>
34 #include <item.h>
35 #include <itemfetchscope.h>
36 #include <kcontacts/addressee.h>
37 #include <KColorScheme>
38 #include <KConfigGroup>
39 #include <KLocalizedString>
40 #include <KStringHandler>
41 
42 #include <QVBoxLayout>
43 #include <QIcon>
44 #include <QUrlQuery>
45 #include <prison/Prison>
46 #include <kcontacts/vcardconverter.h>
47 
48 using namespace Akonadi;
49 
50 class Q_DECL_HIDDEN ContactViewer::Private
51 {
52 public:
53  Private(ContactViewer *parent)
54  : mParent(parent)
55  {
56  mStandardContactFormatter = new StandardContactFormatter;
57  mContactFormatter = mStandardContactFormatter;
58  KConfig config(QStringLiteral("akonadi_contactrc"));
59  KConfigGroup group(&config, QStringLiteral("View"));
60  mShowQRCode = group.readEntry("QRCodes", true);
62  }
63 
64  ~Private()
65  {
66  delete mStandardContactFormatter;
67  delete mQRCode;
68  }
69 
70  void updateView(const QVariantList &localCustomFieldDescriptions = QVariantList(), const QString &addressBookName = QString())
71  {
72  static QPixmap defaultPixmap = QIcon::fromTheme(QStringLiteral("user-identity")).pixmap(QSize(100, 100));
73  static QPixmap defaultMapPixmap = QIcon::fromTheme(QStringLiteral("map-symbolic")).pixmap(QSize(16, 16));
74  static QPixmap defaultSmsPixmap = QIcon::fromTheme(KContacts::Impp::serviceIcon(QStringLiteral("sms"))).pixmap(QSize(16, 16));
75 
76  mParent->setWindowTitle(i18nc("@title:window", "Contact %1", mCurrentContact.assembledName()));
77 
78  if (mCurrentContact.photo().isIntern()) {
79  mBrowser->document()->addResource(QTextDocument::ImageResource,
80  QUrl(QStringLiteral("contact_photo")),
81  mCurrentContact.photo().data());
82  } else if (!mCurrentContact.photo().url().isEmpty()) {
83  QByteArray imageData;
84  QImage image;
85  KIO::TransferJob *job = KIO::get(QUrl(mCurrentContact.photo().url()), KIO::NoReload);
87  [&imageData](KIO::Job *, const QByteArray &data) {
88  imageData.append(data);
89  });
90  if (job->exec()) {
91  if (image.loadFromData(imageData)) {
92  mBrowser->document()->addResource(QTextDocument::ImageResource,
93  QUrl(QStringLiteral("contact_photo")),
94  image);
95  } else {
96  mBrowser->document()->addResource(QTextDocument::ImageResource,
97  QUrl(QStringLiteral("contact_photo")),
98  defaultPixmap);
99  }
100  } else {
101  mBrowser->document()->addResource(QTextDocument::ImageResource,
102  QUrl(QStringLiteral("contact_photo")),
103  defaultPixmap);
104  }
105  } else {
106  mBrowser->document()->addResource(QTextDocument::ImageResource,
107  QUrl(QStringLiteral("contact_photo")),
108  defaultPixmap);
109  }
110 
111  if (mCurrentContact.logo().isIntern()) {
112  mBrowser->document()->addResource(QTextDocument::ImageResource,
113  QUrl(QStringLiteral("contact_logo")),
114  mCurrentContact.logo().data());
115  } else if (!mCurrentContact.logo().url().isEmpty()) {
116  QByteArray imageData;
117  QImage image;
118  KIO::TransferJob *job = KIO::get(QUrl(mCurrentContact.logo().url()), KIO::NoReload);
120  [&imageData](KIO::Job *, const QByteArray &data) {
121  imageData.append(data);
122  });
123  if (job->exec()) {
124  if (image.loadFromData(imageData)) {
125  mBrowser->document()->addResource(QTextDocument::ImageResource,
126  QUrl(QStringLiteral("contact_logo")),
127  image);
128  }
129  }
130  }
131 
132  mBrowser->document()->addResource(QTextDocument::ImageResource,
133  QUrl(QStringLiteral("map_icon")),
134  defaultMapPixmap);
135 
136  mBrowser->document()->addResource(QTextDocument::ImageResource,
137  QUrl(QStringLiteral("sms_icon")),
138  defaultSmsPixmap);
139 
140  if (mShowQRCode) {
141  KContacts::VCardConverter converter;
142  KContacts::Addressee addr(mCurrentContact);
143  addr.setPhoto(KContacts::Picture());
144  addr.setLogo(KContacts::Picture());
145  const QString data = QString::fromUtf8(converter.createVCard(addr));
146  if (mQRCode) {
147  mQRCode->setData(data);
148  mBrowser->document()->addResource(QTextDocument::ImageResource,
149  QUrl(QStringLiteral("qrcode")),
150  mQRCode->toImage(QSizeF(50, 50)));
151  }
152  }
153 
154  // merge local and global custom field descriptions
155  QVector<QVariantMap> customFieldDescriptions;
156  const CustomField::List globalCustomFields = CustomFieldManager::globalCustomFieldDescriptions();
157  customFieldDescriptions.reserve(localCustomFieldDescriptions.count() + globalCustomFields.count());
158  for (const QVariant &entry : qAsConst(localCustomFieldDescriptions)) {
159  customFieldDescriptions << entry.toMap();
160  }
161 
162  for (const CustomField &field : qAsConst(globalCustomFields)) {
163  QVariantMap description;
164  description.insert(QStringLiteral("key"), field.key());
165  description.insert(QStringLiteral("title"), field.title());
166 
167  customFieldDescriptions << description;
168  }
169 
170  KContacts::Addressee contact(mCurrentContact);
171  if (!addressBookName.isEmpty()) {
172  contact.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("AddressBook"), addressBookName);
173  }
174 
175  mContactFormatter->setContact(contact);
176  mContactFormatter->setCustomFieldDescriptions(customFieldDescriptions);
177 
178  mBrowser->setHtml(mContactFormatter->toHtml());
179  }
180 
181  void slotUrlClicked(const QUrl &url)
182  {
183  const QUrlQuery query(url);
184  const QString urlScheme(url.scheme());
185  if (urlScheme == QLatin1String("http")
186  || urlScheme == QLatin1String("https")) {
187  Q_EMIT mParent->urlClicked(url);
188  } else if (urlScheme == QLatin1String("phone")) {
189  const int pos = query.queryItemValue(QStringLiteral("index")).toInt();
190 
191  const KContacts::PhoneNumber::List numbers = mCurrentContact.phoneNumbers();
192  if (pos < numbers.count()) {
193  Q_EMIT mParent->phoneNumberClicked(numbers.at(pos));
194  }
195  } else if (urlScheme == QLatin1String("sms")) {
196  const int pos = query.queryItemValue(QStringLiteral("index")).toInt();
197 
198  const KContacts::PhoneNumber::List numbers = mCurrentContact.phoneNumbers();
199  if (pos < numbers.count()) {
200  Q_EMIT mParent->smsClicked(numbers.at(pos));
201  }
202  } else if (urlScheme == QLatin1String("address")) {
203  const int pos = query.queryItemValue(QStringLiteral("index")).toInt();
204 
205  const KContacts::Address::List addresses = mCurrentContact.addresses();
206  if (pos < addresses.count()) {
207  Q_EMIT mParent->addressClicked(addresses.at(pos));
208  }
209  } else if (urlScheme == QLatin1String("mailto")) {
211 
212  // remove the 'mailto:' and split into name and email address
213  KContacts::Addressee::parseEmailAddress(url.path(), name, address);
214 
215  Q_EMIT mParent->emailClicked(name, address);
216  }
217  }
218 
219  void slotParentCollectionFetched(KJob *job)
220  {
221  mParentCollectionFetchJob = nullptr;
222 
223  QString addressBookName;
224 
225  if (!job->error()) {
226  CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *>(job);
227  if (!fetchJob->collections().isEmpty()) {
228  const Collection collection = fetchJob->collections().at(0);
229  addressBookName = collection.displayName();
230  }
231  }
232 
233  // load the local meta data of the item
234  ContactMetaDataAkonadi metaData;
235  metaData.load(mCurrentItem);
236 
237  updateView(metaData.customFieldDescriptions(), addressBookName);
238  }
239 
240  QMetaObject::Connection mCollectionFetchJobConnection;
241  KContacts::Addressee mCurrentContact;
242  Item mCurrentItem;
243  ContactViewer *mParent = nullptr;
244  TextBrowser *mBrowser = nullptr;
245  AbstractContactFormatter *mContactFormatter = nullptr;
246  AbstractContactFormatter *mStandardContactFormatter = nullptr;
247  CollectionFetchJob *mParentCollectionFetchJob = nullptr;
248  Prison::AbstractBarcode *mQRCode = nullptr;
249  bool mShowQRCode = true;
250 };
251 
253  : QWidget(parent)
254  , d(new Private(this))
255 {
256  QVBoxLayout *layout = new QVBoxLayout(this);
257  layout->setContentsMargins(0, 0, 0, 0);
258 
259  d->mBrowser = new TextBrowser;
260 
261  connect(d->mBrowser, &TextBrowser::anchorClicked,
262  this, [this](const QUrl &url) {
263  d->slotUrlClicked(url);
264  });
265 
266  layout->addWidget(d->mBrowser);
267 
268  // always fetch full payload for contacts
272 }
273 
275 {
276  delete d;
277 }
278 
279 Akonadi::Item ContactViewer::contact() const
280 {
281  return ItemMonitor::item();
282 }
283 
285 {
286  return d->mCurrentContact;
287 }
288 
290 {
291  if (formatter == nullptr) {
292  d->mContactFormatter = d->mStandardContactFormatter;
293  } else {
294  d->mContactFormatter = formatter;
295  delete d->mStandardContactFormatter;
296  d->mStandardContactFormatter = nullptr;
297  }
298 }
299 
300 void ContactViewer::setContact(const Akonadi::Item &contact)
301 {
302  ItemMonitor::setItem(contact);
303 }
304 
306 {
307  d->mCurrentContact = contact;
308 
309  d->updateView();
310 }
311 
312 void ContactViewer::itemChanged(const Item &contactItem)
313 {
314  if (!contactItem.hasPayload<KContacts::Addressee>()) {
315  return;
316  }
317 
318  d->mCurrentItem = contactItem;
319  d->mCurrentContact = contactItem.payload<KContacts::Addressee>();
320 
321  // stop any running fetch job
322  if (d->mParentCollectionFetchJob) {
323  disconnect(d->mCollectionFetchJobConnection);
324  delete d->mParentCollectionFetchJob;
325  d->mParentCollectionFetchJob = nullptr;
326  }
327 
328  d->mParentCollectionFetchJob = new CollectionFetchJob(contactItem.parentCollection(), CollectionFetchJob::Base, this);
329  d->mCollectionFetchJobConnection = connect(d->mParentCollectionFetchJob, &CollectionFetchJob::result, this, [this](KJob *job) {d->slotParentCollectionFetched(job);});
330 }
331 
332 void ContactViewer::itemRemoved()
333 {
334  d->mBrowser->clear();
335 }
336 
338 {
339  itemChanged(d->mCurrentItem);
340 }
341 
343 {
344  if (d->mShowQRCode != b) {
345  d->mShowQRCode = b;
346  updateView();
347  }
348 }
349 
350 bool ContactViewer::showQRCode() const
351 {
352  return d->mShowQRCode;
353 }
354 
355 #include "moc_contactviewer.cpp"
QLayout * layout() const const
void fetchAttribute(const QByteArray &type, bool fetch=true)
bool loadFromData(const uchar *data, int len, const char *format)
void setContentsMargins(int left, int top, int right, int bottom)
Collection::List collections() const
QString name(const QVariant &location)
Akonadi::Item contact() const
Returns the contact that is currently displayed.
QString displayName() const
std::optional< QSqlQuery > query(const QString &queryStatement)
KContacts::Addressee rawContact() const
Returns the raw contact that is currently displayed.
static void parseEmailAddress(const QString &rawEmail, QString &fullName, QString &email)
ItemFetchScope & fetchScope()
bool exec()
void insert(int i, T &&value)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
void fetchFullPayload(bool fetch=true)
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
KSharedConfigPtr config()
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
ContactViewer(QWidget *parent=nullptr)
Creates a new contact viewer.
QString fromUtf8(const char *str, int size)
PostalAddress address(const QVariant &location)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
~ContactViewer() override
Destroys the contact viewer.
void setContact(const Akonadi::Item &contact)
Sets the contact that shall be displayed in the viewer.
void setRawContact(const KContacts::Addressee &contact)
Sets the raw contact object that shall be displayed in the viewer.
QString path(QUrl::ComponentFormattingOptions options) const const
PRISON_EXPORT Prison::AbstractBarcode * createBarcode(BarcodeType type)
void setContactFormatter(AbstractContactFormatter *formatter)
Sets the contact formatter that should be used for formatting the contact.
QString scheme() const const
Item item() const
QByteArray & append(char ch)
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
void reserve(int size)
A class that formats a contact as HTML code.
void setAncestorRetrieval(AncestorRetrieval ancestorDepth)
const T & at(int i) const const
void data(KIO::Job *job, const QByteArray &data)
A viewer component for contacts in Akonadi.
Definition: contactviewer.h:73
void setItem(const Item &item)
bool isEmpty() const const
int count(const T &value) const const
QIcon fromTheme(const QString &name)
void result(KJob *job)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
The interface for all contact formatters.
int error() const
QString serviceIcon() const
QByteArray createVCard(const Addressee &addr, Version version=v3_0) const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Aug 5 2020 23:05:57 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.