blob: f558670912a09b29b1d58ee5544cad42c7b01988 [file] [log] [blame]
Edric Milareta0ebd062016-01-13 12:18:23 -05001/***************************************************************************
Anthony Léonard2fde81d2017-04-17 10:06:55 -04002 * Copyright (C) 2015-2017 by Savoir-faire Linux *
Edric Milareta0ebd062016-01-13 12:18:23 -05003 * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
Olivier SOLDANO47aa97f2017-04-04 10:40:00 -04004 * Author: Anthony Léonard <anthony.leonard@savoirfairelinux.com> *
5 * Author: Olivier Soldano <olivier.soldano@savoirfairelinux.com> *
Edric Milareta0ebd062016-01-13 12:18:23 -05006 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 3 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19 **************************************************************************/
20
21#include "pixbufmanipulator.h"
22
Edric Milaret25236d92016-03-28 09:40:58 -040023#include <QSize>
24#include <QMetaType>
25#include <QImage>
26#include <QIODevice>
27#include <QByteArray>
28#include <QBuffer>
Anthony Léonard3d920d82017-07-31 13:51:16 -040029#include <QPainter>
Edric Milareta0ebd062016-01-13 12:18:23 -050030
Edric Milaret25236d92016-03-28 09:40:58 -040031#include "person.h"
32#include "call.h"
33#include "contactmethod.h"
34#include "profilemodel.h"
35#include "profile.h"
36
37#include "utils.h"
Anthony Léonard3d920d82017-07-31 13:51:16 -040038#include "ringthemeutils.h"
Edric Milaret25236d92016-03-28 09:40:58 -040039#undef interface
40
Guillaume Roguez79cab802017-08-25 09:59:13 -040041static const QSize IMAGE_SIZE {48, 48};
42
43//
44// Generate a QImage representing a dummy user avatar, when user doesn't provide it.
45// Current rendering is a flat colored circle with a centered letter.
46// The color of the letter is computed from the circle color to be visible whaterver be the circle color.
47//
48// \param color circle color
49// \param letter centerer letter
50//
51static QImage
52fallbackAvatar(const QSize size, const char color, const char letter)
53{
54 // We start with a transparent avatar
55 QImage avatar(size, QImage::Format_ARGB32);
56 avatar.fill(Qt::transparent);
57
58 // We pick a color based on the passed character
59 QColor avColor = RingTheme::avatarColors_[color % 16];
60
61 // We draw a circle with this color
62 QPainter painter(&avatar);
63 painter.setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform);
64 painter.setPen(Qt::transparent);
65 painter.setBrush(avColor);
66 painter.drawEllipse(avatar.rect());
67
68 // Then we paint a letter in the circle
69 QFont segoeFont("Segoe UI", avatar.height()/2); // We use Segoe UI as recommended by Windows guidelines
70 painter.setFont(segoeFont);
71 painter.setPen(Qt::white);
72 QRect textRect = avatar.rect();
73 textRect.moveTop(textRect.top()-(avatar.height()/20)); // Empirical value that seems to correct centering nicely
74 painter.drawText(textRect, QString(letter), QTextOption(Qt::AlignCenter));
75
76 return avatar;
77}
78
79//
80// Alias on fallbackAvatar
81//
82// \param color a QString where the first character is converted to latin1 and used as color
83// \param string a QString where the first character is converted to uppercase-latin1 and used as letter
84//
85static inline QImage
86fallbackAvatar(const QSize size, const QString& color_str, const QString& letter_str)
87{
88 return fallbackAvatar(size, color_str.at(0).toLatin1(), letter_str.at(0).toUpper().toLatin1());
89}
90
91
Edric Milaret25236d92016-03-28 09:40:58 -040092/*Namespace Interfaces collide with QBuffer*/
93QByteArray QImageToByteArray(QImage image)
94{
95 QByteArray ba;
96 QBuffer buffer(&ba);
97 buffer.open(QIODevice::WriteOnly);
98 image.save(&buffer, "PNG");
99 return ba;
100}
Edric Milareta0ebd062016-01-13 12:18:23 -0500101
Edric Milareta0ebd062016-01-13 12:18:23 -0500102QImage
103PixbufManipulator::scaleAndFrame(const QImage photo, const QSize& size)
104{
105 return photo.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
106}
107
108QVariant
109PixbufManipulator::callPhoto(Call* c, const QSize& size, bool displayPresence)
110{
Olivier SOLDANOad0fabb2016-11-25 09:08:01 -0500111 if (!c || c->type() == Call::Type::CONFERENCE){
Anthony Léonard3d920d82017-07-31 13:51:16 -0400112 return QVariant::fromValue(fallbackAvatar(size,
Guillaume Roguez79cab802017-08-25 09:59:13 -0400113 c->peerContactMethod()->uri().userinfo(),
114 c->peerContactMethod()->bestName()));
Olivier SOLDANOad0fabb2016-11-25 09:08:01 -0500115 }
Edric Milareta0ebd062016-01-13 12:18:23 -0500116 return callPhoto(c->peerContactMethod(), size, displayPresence);
117}
118
119QVariant
120PixbufManipulator::callPhoto(const ContactMethod* n, const QSize& size, bool displayPresence)
121{
122 if (n && n->contact()) {
123 return contactPhoto(n->contact(), size, displayPresence);
124 } else {
Guillaume Roguez79cab802017-08-25 09:59:13 -0400125 return QVariant::fromValue(fallbackAvatar(size, n->uri().userinfo(), n->bestName()));
Edric Milareta0ebd062016-01-13 12:18:23 -0500126 }
127}
128
129QVariant
130PixbufManipulator::contactPhoto(Person* c, const QSize& size, bool displayPresence)
131{
132 Q_UNUSED(displayPresence);
133
134 /**
135 * try to get the photo
136 * otherwise use the fallback avatar
137 */
138
139 QImage photo;
140
Olivier SOLDANOad0fabb2016-11-25 09:08:01 -0500141 if (c->photo().isValid()){
Edric Milareta0ebd062016-01-13 12:18:23 -0500142 photo = c->photo().value<QImage>();
Olivier SOLDANOad0fabb2016-11-25 09:08:01 -0500143 } else {
Anthony Léonard3d920d82017-07-31 13:51:16 -0400144 photo = fallbackAvatar(size,
Guillaume Roguez79cab802017-08-25 09:59:13 -0400145 c->phoneNumbers().at(0)->uri().userinfo(),
146 c->phoneNumbers().at(0)->bestName());
Olivier SOLDANOad0fabb2016-11-25 09:08:01 -0500147 }
Edric Milareta0ebd062016-01-13 12:18:23 -0500148 return QVariant::fromValue(scaleAndFrame(photo, size));
149}
150
151QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& type)
152{
Edric Milaret25236d92016-03-28 09:40:58 -0400153 QImage avatar;
154 QByteArray ba = type.toLatin1();
155 const char* c_str2 = ba.data();
156 if (avatar.loadFromData(data.fromBase64(data), c_str2))
157 return Utils::getCirclePhoto(avatar, avatar.size().width());
Guillaume Roguez79cab802017-08-25 09:59:13 -0400158 return fallbackAvatar(IMAGE_SIZE, '?', '?');
Edric Milareta0ebd062016-01-13 12:18:23 -0500159}
160
161QVariant
162PixbufManipulator::numberCategoryIcon(const QVariant& p, const QSize& size, bool displayPresence, bool isPresent)
163{
164 Q_UNUSED(p)
165 Q_UNUSED(size)
166 Q_UNUSED(displayPresence)
167 Q_UNUSED(isPresent)
168 return QVariant();
169}
170
171QVariant
172PixbufManipulator::securityIssueIcon(const QModelIndex& index)
173{
174 Q_UNUSED(index)
175 return QVariant();
176}
177
Edric Milaret25236d92016-03-28 09:40:58 -0400178
179
Edric Milareta0ebd062016-01-13 12:18:23 -0500180QByteArray
181PixbufManipulator::toByteArray(const QVariant& pxm)
182{
Edric Milaret25236d92016-03-28 09:40:58 -0400183 auto image = pxm.value<QImage>();
184 QByteArray ba = QImageToByteArray(image);
185 return ba;
Edric Milareta0ebd062016-01-13 12:18:23 -0500186}
187
188QVariant
Olivier SOLDANO41e61ab2017-08-04 16:47:16 -0400189PixbufManipulator::collectionIcon(const CollectionInterface* colItf, PixmapManipulatorI::CollectionIconHint hint) const
Edric Milareta0ebd062016-01-13 12:18:23 -0500190{
Olivier SOLDANO41e61ab2017-08-04 16:47:16 -0400191 Q_UNUSED(colItf)
Edric Milareta0ebd062016-01-13 12:18:23 -0500192 Q_UNUSED(hint)
193 return QVariant();
194}
195QVariant
196PixbufManipulator::securityLevelIcon(const SecurityEvaluationModel::SecurityLevel level) const
197{
198 Q_UNUSED(level)
199 return QVariant();
200}
201QVariant
202PixbufManipulator::historySortingCategoryIcon(const CategorizedHistoryModel::SortedProxy::Categories cat) const
203{
204 Q_UNUSED(cat)
205 return QVariant();
206}
207QVariant
208PixbufManipulator::contactSortingCategoryIcon(const CategorizedContactModel::SortedProxy::Categories cat) const
209{
210 Q_UNUSED(cat)
211 return QVariant();
212}
213QVariant
214PixbufManipulator::userActionIcon(const UserActionElement& state) const
215{
216 Q_UNUSED(state)
217 return QVariant();
218}
219
220QVariant PixbufManipulator::decorationRole(const QModelIndex& index)
221{
222 Q_UNUSED(index)
223 return QVariant();
224}
225
226QVariant PixbufManipulator::decorationRole(const Call* c)
227{
Edric Milareta13b48c2016-01-18 14:42:35 -0500228 QImage photo;
Edric Milareta0ebd062016-01-13 12:18:23 -0500229 if (c && c->peerContactMethod()
230 && c->peerContactMethod()->contact()
231 && c->peerContactMethod()->contact()->photo().isValid()) {
Edric Milareta13b48c2016-01-18 14:42:35 -0500232 photo = c->peerContactMethod()->contact()->photo().value<QImage>();
Edric Milareta0ebd062016-01-13 12:18:23 -0500233 }
Edric Milareta13b48c2016-01-18 14:42:35 -0500234 else
Guillaume Roguez79cab802017-08-25 09:59:13 -0400235 photo = fallbackAvatar(IMAGE_SIZE,
236 c->peerContactMethod()->uri().userinfo(),
237 c->peerContactMethod()->bestName());
238 return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
Edric Milareta0ebd062016-01-13 12:18:23 -0500239}
240
241QVariant PixbufManipulator::decorationRole(const ContactMethod* cm)
242{
Edric Milareta13b48c2016-01-18 14:42:35 -0500243 QImage photo;
Edric Milareta0ebd062016-01-13 12:18:23 -0500244 if (cm && cm->contact() && cm->contact()->photo().isValid())
Edric Milareta13b48c2016-01-18 14:42:35 -0500245 photo = cm->contact()->photo().value<QImage>();
Olivier SOLDANO6c46d352017-08-24 10:54:55 -0400246 else if (cm){
Guillaume Roguez79cab802017-08-25 09:59:13 -0400247 photo = fallbackAvatar(IMAGE_SIZE,
248 cm->uri().userinfo(),
249 cm->bestName());
Olivier SOLDANO6c46d352017-08-24 10:54:55 -0400250 } else {
Guillaume Roguez79cab802017-08-25 09:59:13 -0400251 photo = fallbackAvatar(IMAGE_SIZE, '?', '?');
Olivier SOLDANO6c46d352017-08-24 10:54:55 -0400252 }
253
Guillaume Roguez79cab802017-08-25 09:59:13 -0400254 return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
Edric Milareta0ebd062016-01-13 12:18:23 -0500255}
256
257QVariant PixbufManipulator::decorationRole(const Person* p)
258{
Edric Milareta13b48c2016-01-18 14:42:35 -0500259 QImage photo;
Edric Milareta0ebd062016-01-13 12:18:23 -0500260 if (p && p->photo().isValid())
Edric Milareta13b48c2016-01-18 14:42:35 -0500261 photo = p->photo().value<QImage>();
262 else
Guillaume Roguez79cab802017-08-25 09:59:13 -0400263 photo = fallbackAvatar(IMAGE_SIZE,
264 p->phoneNumbers().at(0)->uri().userinfo(),
265 p->phoneNumbers().at(0)->bestName());
266 return QVariant::fromValue(scaleAndFrame(photo, IMAGE_SIZE));
Edric Milareta0ebd062016-01-13 12:18:23 -0500267}
268
Edric Milareta7cf8652016-04-19 15:37:51 -0400269QVariant PixbufManipulator::decorationRole(const Account* acc)
270{
271 Q_UNUSED(acc)
Edric Milaret25236d92016-03-28 09:40:58 -0400272 return Utils::getCirclePhoto(ProfileModel::instance().
Guillaume Roguez79cab802017-08-25 09:59:13 -0400273 selectedProfile()->person()->photo().value<QImage>(),
274 IMAGE_SIZE.width());
Anthony Léonard3d920d82017-07-31 13:51:16 -0400275}