blob: f3d78534090933535fee1c2156904c02392b53d7 [file] [log] [blame]
Sébastien Blin1f915762020-08-03 13:27:42 -04001/*
2 * Copyright (C) 2015-2020 by Savoir-faire Linux
3 * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
4 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
5 * Author: Isa Nanic <isa.nanic@savoirfairelinux.com>
6 * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
agsantos655d8e22020-08-10 17:36:47 -04007 * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
Sébastien Blin1f915762020-08-03 13:27:42 -04008 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#pragma once
24
25#include "version.h"
26
27#include <string>
28
29#include <QClipboard>
30#include <QCryptographicHash>
31#include <QDir>
32#include <QApplication>
33#include <QImage>
34#include <QItemDelegate>
35#include <QLabel>
36#include <QListWidget>
37#include <QMessageBox>
38#include <QQmlEngine>
39#include <QSettings>
40#include <QStackedWidget>
41#include <QStandardPaths>
42#include <QString>
43#include <QTextDocument>
44#include <QtGlobal>
45#include <QPainterPath>
46
47#ifdef Q_OS_WIN
48#include <ciso646>
49#include <windows.h>
50#undef ERROR
51#else
52#define LPCWSTR char *
53#endif
54
55#include "api/account.h"
56#include "api/contact.h"
57#include "api/contactmodel.h"
58#include "api/conversationmodel.h"
59
60static const QSize IMAGE_SIZE{128, 128};
61static float CURRENT_SCALING_RATIO{1.0};
62
63#ifdef BETA
64static constexpr bool isBeta = true;
65#else
66static constexpr bool isBeta = false;
67#endif
68
69namespace Utils {
70
71/*
Sébastien Blin1f915762020-08-03 13:27:42 -040072 * System.
73 */
74bool CreateStartupLink(const std::wstring &wstrAppName);
75void DeleteStartupLink(const std::wstring &wstrAppName);
76bool CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink);
77bool CheckStartupLink(const std::wstring &wstrAppName);
78void removeOldVersions();
79const char *WinGetEnv(const char *name);
80QString GetRingtonePath();
81QString GenGUID();
82QString GetISODate();
83void InvokeMailto(const QString &subject,
84 const QString &body,
85 const QString &attachement = QString());
86void setStackWidget(QStackedWidget *stack, QWidget *widget);
87void showSystemNotification(QWidget *widget,
88 const QString &message,
89 long delay = 5000,
90 const QString &triggeredAccountId = "");
91void showSystemNotification(QWidget *widget,
92 const QString &sender,
93 const QString &message,
94 long delay = 5000,
95 const QString &triggeredAccountId = "");
96QSize getRealSize(QScreen *screen);
97void forceDeleteAsync(const QString &path);
98QString getChangeLog();
99QString getProjectCredits();
100float getCurrentScalingRatio();
101void setCurrentScalingRatio(float ratio);
102
103/*
104 * Updates.
105 */
106void cleanUpdateFiles();
107void checkForUpdates(bool withUI, QWidget *parent = nullptr);
108void applyUpdates(bool updateToBeta, QWidget *parent = nullptr);
109
110/*
111 * Names.
112 */
113QString bestIdForConversation(const lrc::api::conversation::Info &conv,
114 const lrc::api::ConversationModel &model);
115QString bestIdForAccount(const lrc::api::account::Info &account);
116QString bestNameForAccount(const lrc::api::account::Info &account);
117QString bestIdForContact(const lrc::api::contact::Info &contact);
118QString bestNameForContact(const lrc::api::contact::Info &contact);
119QString bestNameForConversation(const lrc::api::conversation::Info &conv,
120 const lrc::api::ConversationModel &model);
121/*
122 * Returns empty string if only infoHash is available.
123 */
124QString secondBestNameForAccount(const lrc::api::account::Info &account);
125lrc::api::profile::Type profileType(const lrc::api::conversation::Info &conv,
126 const lrc::api::ConversationModel &model);
127
128/*
129 * Interactions.
130 */
131std::string formatTimeString(const std::time_t &timestamp);
132bool isInteractionGenerated(const lrc::api::interaction::Type &interaction);
133bool isContactValid(const QString &contactUid, const lrc::api::ConversationModel &model);
134bool getReplyMessageBox(QWidget *widget, const QString &title, const QString &text);
135
136/*
137 * Image.
138 */
139QString getContactImageString(const QString &accountId, const QString &uid);
140QImage getCirclePhoto(const QImage original, int sizePhoto);
141QImage conversationPhoto(const QString &convUid,
142 const lrc::api::account::Info &accountInfo,
143 bool filtered = false);
144QColor getAvatarColor(const QString &canonicalUri);
145QImage fallbackAvatar(const QSize size,
146 const QString &canonicalUriStr,
147 const QString &letterStr = QString());
148QImage fallbackAvatar(const QSize size, const std::string &alias, const std::string &uri);
149QByteArray QImageToByteArray(QImage image);
150QByteArray QByteArrayFromFile(const QString &filename);
151QPixmap generateTintedPixmap(const QString &filename, QColor color);
152QPixmap generateTintedPixmap(const QPixmap &pix, QColor color);
153QImage scaleAndFrame(const QImage photo, const QSize &size = IMAGE_SIZE);
154QImage accountPhoto(const lrc::api::account::Info &accountInfo, const QSize &size = IMAGE_SIZE);
155QImage cropImage(const QImage &img);
156QPixmap pixmapFromSvg(const QString &svg_resource, const QSize &size);
157QImage setupQRCode(QString ringID, int margin);
158
159/*
160 * Rounded corner.
161 */
162template<typename T>
163void
164fillRoundRectPath(QPainter &painter,
165 const T &brushType,
166 const QRect &rectToDraw,
167 qreal cornerRadius,
168 int xTransFormOffset = 0,
169 int yTransFormOffset = 0)
170{
171 QBrush brush(brushType);
172 brush.setTransform(QTransform::fromTranslate(rectToDraw.x() + xTransFormOffset,
173 rectToDraw.y() + yTransFormOffset));
174 QPainterPath painterPath;
175 painterPath.addRoundRect(rectToDraw, cornerRadius);
176 painter.fillPath(painterPath, brush);
177}
178
179/*
180 * Time.
181 */
182QString formattedTime(int seconds);
183
184/*
185 * Byte to human readable size.
186 */
187QString humanFileSize(qint64 fileSize);
188
189/*
190 * Device plug or unplug enum.
191 */
192enum class DevicePlugStatus { Plugged, Unplugged, Unchanged };
193
194class OneShotDisconnectConnection : public QObject
195{
196 Q_OBJECT
197
198public:
199 explicit OneShotDisconnectConnection(const QObject *sender,
200 const char *signal,
201 QMetaObject::Connection *connection,
202 QObject *parent = nullptr)
203 : QObject(parent)
204 {
205 connection_ = connection;
206 disconnectConnection_ = new QMetaObject::Connection;
207 *disconnectConnection_ = QObject::connect(sender,
208 signal,
209 this,
210 SLOT(slotOneShotDisconnectConnection()));
211 }
212 ~OneShotDisconnectConnection()
213 {
214 if (!connection_) {
215 delete connection_;
216 }
217 if (!disconnectConnection_) {
218 delete disconnectConnection_;
219 }
220 if (!this) {
221 delete this;
222 }
223 }
224
225public slots:
226 void
227 slotOneShotDisconnectConnection()
228 {
229 if (connection_) {
230 QObject::disconnect(*connection_);
231 delete connection_;
232 }
233 if (disconnectConnection_) {
234 QObject::disconnect(*disconnectConnection_);
235 delete disconnectConnection_;
236 }
237 delete this;
238 }
239
240private:
241 QMetaObject::Connection *connection_;
242 QMetaObject::Connection *disconnectConnection_;
243};
244
245template<typename Func1, typename Func2>
246void
247oneShotConnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender,
248 Func1 signal,
249 Func2 slot)
250{
251 QMetaObject::Connection *const connection = new QMetaObject::Connection;
252 *connection = QObject::connect(sender, signal, slot);
253 QMetaObject::Connection *const disconnectConnection = new QMetaObject::Connection;
254 *disconnectConnection = QObject::connect(sender, signal, [connection, disconnectConnection] {
255 if (connection) {
256 QObject::disconnect(*connection);
257 delete connection;
258 }
259 if (disconnectConnection) {
260 QObject::disconnect(*disconnectConnection);
261 delete disconnectConnection;
262 }
263 });
264}
265
266template<typename Func1, typename Func2>
267void
268oneShotConnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender,
269 Func1 signal,
270 const typename QtPrivate::FunctionPointer<Func2>::Object *receiver,
271 Func2 slot)
272{
273 QMetaObject::Connection *const connection = new QMetaObject::Connection;
274 *connection = QObject::connect(sender, signal, receiver, slot);
275 QMetaObject::Connection *const disconnectConnection = new QMetaObject::Connection;
276 *disconnectConnection = QObject::connect(sender, signal, [connection, disconnectConnection] {
277 if (connection) {
278 QObject::disconnect(*connection);
279 delete connection;
280 }
281 if (disconnectConnection) {
282 QObject::disconnect(*disconnectConnection);
283 delete disconnectConnection;
284 }
285 });
286}
287
288inline void
289oneShotConnect(const QObject *sender, const char *signal, const QObject *receiver, const char *slot)
290{
291 QMetaObject::Connection *const connection = new QMetaObject::Connection;
292 *connection = QObject::connect(sender, signal, receiver, slot);
293 OneShotDisconnectConnection *disconnectConnection = new OneShotDisconnectConnection(sender,
294 signal,
295 connection);
296 Q_UNUSED(disconnectConnection)
297}
298
299template<class T>
300class Blocker
301{
302 T *blocked;
303 bool previous;
304
305public:
306 Blocker(T *blocked)
307 : blocked(blocked)
308 , previous(blocked->blockSignals(true))
309 {}
310 ~Blocker() { blocked->blockSignals(previous); }
311 T *
312 operator->()
313 {
314 return blocked;
315 }
316};
317
318template<class T>
319inline Blocker<T>
320whileBlocking(T *blocked)
321{
322 return Blocker<T>(blocked);
323}
324
325template<typename T>
326void
327setElidedText(T *object,
328 const QString &text,
329 Qt::TextElideMode mode = Qt::ElideMiddle,
330 int padding = 32)
331{
332 QFontMetrics metrics(object->font());
333 QString clippedText = metrics.elidedText(text, mode, object->width() - padding);
334 object->setText(clippedText);
335}
336
337template<typename E>
338constexpr inline
339 typename std::enable_if<std::is_enum<E>::value, typename std::underlying_type<E>::type>::type
340 toUnderlyingValue(E e) noexcept
341{
342 return static_cast<typename std::underlying_type<E>::type>(e);
343}
344
345template<typename E, typename T>
346constexpr inline
347 typename std::enable_if<std::is_enum<E>::value && std::is_integral<T>::value, E>::type
348 toEnum(T value) noexcept
349{
350 return static_cast<E>(value);
351}
352} // namespace Utils
353
354class UtilsAdapter : public QObject
355{
356 Q_OBJECT
357public:
358 explicit UtilsAdapter(QObject *parent = nullptr)
359 : QObject(parent)
360 {
361 clipboard_ = QApplication::clipboard();
362 }
363 ~UtilsAdapter() {}
364
365 ///Singleton
366 static UtilsAdapter &instance();
367
368 Q_INVOKABLE const QString
369 getChangeLog()
370 {
371 return Utils::getChangeLog();
372 }
373
374 Q_INVOKABLE const QString
375 getProjectCredits()
376 {
377 return Utils::getProjectCredits();
378 }
379
380 Q_INVOKABLE const QString
381 getVersionStr()
382 {
383 return QString(VERSION_STRING);
384 }
385
386 Q_INVOKABLE void
387 setText(QString text)
388 {
389 clipboard_->setText(text, QClipboard::Clipboard);
390 }
391
392 Q_INVOKABLE const QString
393 qStringFromFile(const QString &filename)
394 {
395 return Utils::QByteArrayFromFile(filename);
396 }
397
398 Q_INVOKABLE const QString
399 getStyleSheet(const QString &name, const QString &source)
400 {
401 auto simplifiedCSS = source.simplified().replace("'", "\"");
402 QString s = QString::fromLatin1("(function() {"
403 " var node = document.createElement('style');"
404 " node.id = '%1';"
405 " node.innerHTML = '%2';"
406 " document.head.appendChild(node);"
407 "})()")
408 .arg(name)
409 .arg(simplifiedCSS);
410 return s;
411 }
412
413 Q_INVOKABLE const QString
414 getCachePath()
415 {
416 QDir dataDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
417 dataDir.cdUp();
418 return dataDir.absolutePath() + "/jami";
419 }
420 Q_INVOKABLE bool
421 createStartupLink()
422 {
423 return Utils::CreateStartupLink(L"Jami");
424 }
425 Q_INVOKABLE QString
426 GetRingtonePath()
427 {
428 return Utils::GetRingtonePath();
429 }
430 Q_INVOKABLE bool
431 checkStartupLink()
432 {
433 return Utils::CheckStartupLink(L"Jami");
434 }
435
436 Q_INVOKABLE const QString
437 getContactImageString(const QString &accountId, const QString &uid)
438 {
439 return Utils::getContactImageString(accountId, uid);
440 }
441
442 Q_INVOKABLE void removeConversation(const QString &accountId,
443 const QString &uid,
444 bool banContact = false);
445 Q_INVOKABLE void clearConversationHistory(const QString &accountId, const QString &uid);
446 Q_INVOKABLE void setConversationFilter(const QString &filter);
447 Q_INVOKABLE int getTotalUnreadMessages();
448 Q_INVOKABLE int getTotalPendingRequest();
449 Q_INVOKABLE const QString getBestName(const QString &accountId, const QString &uid);
450 Q_INVOKABLE const QString getBestId(const QString &accountId, const QString &uid);
451
452 Q_INVOKABLE const QString getCurrAccId();
Sébastien Blin214d9ad2020-08-13 13:05:17 -0400453 Q_INVOKABLE const QString getCurrConvId();
454 Q_INVOKABLE void makePermanentCurrentConv();
Sébastien Blin1f915762020-08-03 13:27:42 -0400455 Q_INVOKABLE const QStringList getCurrAccList();
456 Q_INVOKABLE int getAccountListSize();
457 Q_INVOKABLE void setCurrentCall(const QString &accountId, const QString &convUid);
458 Q_INVOKABLE void startPreviewing(bool force);
459 Q_INVOKABLE void stopPreviewing();
460 Q_INVOKABLE bool hasVideoCall();
461 Q_INVOKABLE const QString getCallId(const QString &accountId, const QString &convUid);
ababi76b94aa2020-08-24 17:46:30 +0200462 Q_INVOKABLE const QString getCallStatusStr(int statusInt);
Sébastien Blin1f915762020-08-03 13:27:42 -0400463 Q_INVOKABLE QString getStringUTF8(QString string);
464 Q_INVOKABLE bool validateRegNameForm(const QString &regName);
465 Q_INVOKABLE QString getRecordQualityString(int value);
466 Q_INVOKABLE QString getCurrentPath();
467 Q_INVOKABLE QString
468 stringSimplifier(QString input)
469 {
470 return input.simplified();
471 }
472
473 Q_INVOKABLE QString
474 toNativeSeparators(QString inputDir)
475 {
476 return QDir::toNativeSeparators(inputDir);
477 }
478
479 Q_INVOKABLE QString
480 toFileInfoName(QString inputFileName)
481 {
482 QFileInfo fi(inputFileName);
483 return fi.fileName();
484 }
485
486 Q_INVOKABLE QString
487 toFileAbsolutepath(QString inputFileName)
488 {
489 QFileInfo fi(inputFileName);
490 return fi.absolutePath();
491 }
492
493 Q_INVOKABLE QString
494 getAbsPath(QString path)
495 {
496#ifdef Q_OS_WIN
497 return path.replace("file:///", "").replace("\n", "").replace("\r", "");
498#else
499 return path.replace("file:///", "/").replace("\n", "").replace("\r", "");
500#endif
501 }
502
503 Q_INVOKABLE QString
504 getCroppedImageBase64FromFile(QString fileName, int size)
505 {
506 auto image = Utils::cropImage(QImage(fileName));
507 auto croppedImage = image.scaled(size,
508 size,
509 Qt::KeepAspectRatioByExpanding,
510 Qt::SmoothTransformation);
511 return QString::fromLatin1(Utils::QImageToByteArray(croppedImage).toBase64().data());
512 }
513
agsantos655d8e22020-08-10 17:36:47 -0400514 Q_INVOKABLE bool checkShowPluginsButton();
515
Sébastien Blin1f915762020-08-03 13:27:42 -0400516private:
517 QClipboard *clipboard_;
518};
Sébastien Blin1f915762020-08-03 13:27:42 -0400519Q_DECLARE_METATYPE(UtilsAdapter *)