blob: cb80f03dc6600f53ef7e2aeb230ae111b4d12865 [file] [log] [blame]
Edric Milaret4bba46d2015-04-29 16:33:38 -04001/***************************************************************************
Sébastien Blin68abac92019-01-02 17:41:31 -05002 * Copyright (C) 2015-2019 by Savoir-faire Linux *
Edric Milaret4bba46d2015-04-29 16:33:38 -04003 * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
Andreas Traczykb8b13ba2018-08-21 16:30:16 -04004 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> *
Isa Nanic601de1d2018-10-23 11:37:26 -04005 * Author: Isa Nanic <isa.nanic@savoirfairelinux.com *
Edric Milaret4bba46d2015-04-29 16:33:38 -04006 * *
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 "utils.h"
22
Edric Milaret36587362016-02-04 12:30:52 -050023#ifdef Q_OS_WIN
Edric Milaret3ba22ca2016-03-01 13:28:01 -050024#include <windows.h>
Edric Milaretb37aa1f2015-07-09 16:39:04 -040025#include <lmcons.h>
26#include <shobjidl.h>
27#include <shlguid.h>
28#include <shlobj.h>
29#include <shlwapi.h>
30#endif
31
Edric Milaret3ba22ca2016-03-01 13:28:01 -050032//Qt
33#include <QObject>
34#include <QErrorMessage>
Edric Milaret25236d92016-03-28 09:40:58 -040035#include <QPainter>
Olivier SOLDANOc1ca2df2017-09-06 16:01:46 -040036#include <QStackedWidget>
37#include <QPropertyAnimation>
Andreas Traczykb8b13ba2018-08-21 16:30:16 -040038#include <QApplication>
Andreas Traczyk43c08232018-10-31 13:42:09 -040039#include <QFile>
Andreas Traczykb8b13ba2018-08-21 16:30:16 -040040
41#include "globalinstances.h"
42#include "pixbufmanipulator.h"
43
44#include "globalsystemtray.h"
Andreas Traczyk6b5ad3e2019-01-02 17:04:36 -050045#include "lrcinstance.h"
Edric Milaret3ba22ca2016-03-01 13:28:01 -050046
Edric Milaret4bba46d2015-04-29 16:33:38 -040047bool
Edric Milaret1eca0292015-06-29 12:03:36 -040048Utils::CreateStartupLink()
49{
Edric Milaret36587362016-02-04 12:30:52 -050050#ifdef Q_OS_WIN
Edric Milaret465a3142015-06-02 15:02:52 -040051 TCHAR szPath[MAX_PATH];
52 GetModuleFileName(NULL, szPath, MAX_PATH);
Edric Milaret4bba46d2015-04-29 16:33:38 -040053
Edric Milaret465a3142015-06-02 15:02:52 -040054 std::wstring programPath(szPath);
Edric Milaret4bba46d2015-04-29 16:33:38 -040055
Edric Milaret465a3142015-06-02 15:02:52 -040056 TCHAR startupPath[MAX_PATH];
57 SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath);
58
59 std::wstring linkPath(startupPath);
Sébastien Blincba5b522018-12-10 12:48:36 -050060 linkPath += TEXT("\\Jami.lnk");
Edric Milaret4bba46d2015-04-29 16:33:38 -040061
62 return Utils::CreateLink(programPath.c_str(), linkPath.c_str());
Edric Milaretb37aa1f2015-07-09 16:39:04 -040063#else
64 return true;
65#endif
Edric Milaret4bba46d2015-04-29 16:33:38 -040066}
67
68bool
69Utils::CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink) {
Edric Milaret36587362016-02-04 12:30:52 -050070#ifdef Q_OS_WIN
Edric Milaret4bba46d2015-04-29 16:33:38 -040071 HRESULT hres;
72 IShellLink* psl;
73
74 hres = CoCreateInstance(CLSID_ShellLink, NULL,
75 CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
76 if (SUCCEEDED(hres))
77 {
78 IPersistFile* ppf;
79 psl->SetPath(lpszPathObj);
Edric Milaret90bd5a82015-06-08 10:07:59 -040080 psl->SetArguments(TEXT("--minimized"));
Edric Milaret4bba46d2015-04-29 16:33:38 -040081
82 hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
83 if (SUCCEEDED(hres))
84 {
85 hres = ppf->Save(lpszPathLink, TRUE);
86 ppf->Release();
87 }
88 psl->Release();
89 }
90 return hres;
Edric Milaretb37aa1f2015-07-09 16:39:04 -040091#else
Edric Milaret36587362016-02-04 12:30:52 -050092 Q_UNUSED(lpszPathObj)
93 Q_UNUSED(lpszPathLink)
Edric Milaretb37aa1f2015-07-09 16:39:04 -040094 return true;
95#endif
Edric Milaret4bba46d2015-04-29 16:33:38 -040096}
97
98void
99Utils::DeleteStartupLink() {
Edric Milaret36587362016-02-04 12:30:52 -0500100#ifdef Q_OS_WIN
Edric Milaret465a3142015-06-02 15:02:52 -0400101 TCHAR startupPath[MAX_PATH];
102 SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath);
Edric Milaret4bba46d2015-04-29 16:33:38 -0400103
Edric Milaret465a3142015-06-02 15:02:52 -0400104 std::wstring linkPath(startupPath);
Sébastien Blincba5b522018-12-10 12:48:36 -0500105 linkPath += TEXT("\\Jami.lnk");
Edric Milaret4bba46d2015-04-29 16:33:38 -0400106
107 DeleteFile(linkPath.c_str());
Edric Milaretb37aa1f2015-07-09 16:39:04 -0400108#endif
Edric Milaret4bba46d2015-04-29 16:33:38 -0400109}
110
111bool
112Utils::CheckStartupLink() {
Edric Milaret36587362016-02-04 12:30:52 -0500113#ifdef Q_OS_WIN
Edric Milaret465a3142015-06-02 15:02:52 -0400114 TCHAR startupPath[MAX_PATH];
115 SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath);
Edric Milaret4bba46d2015-04-29 16:33:38 -0400116
Edric Milaret465a3142015-06-02 15:02:52 -0400117 std::wstring linkPath(startupPath);
Sébastien Blincba5b522018-12-10 12:48:36 -0500118 linkPath += TEXT("\\Jami.lnk");
Edric Milaret4bba46d2015-04-29 16:33:38 -0400119 return PathFileExists(linkPath.c_str());
Edric Milaretb37aa1f2015-07-09 16:39:04 -0400120#else
121 return true;
122#endif
Edric Milaret4bba46d2015-04-29 16:33:38 -0400123}
124
Edric Milaret031c3052015-04-29 18:14:18 -0400125QString
126Utils::GetRingtonePath() {
Edric Milaret36587362016-02-04 12:30:52 -0500127#ifdef Q_OS_WIN
Edric Milaret031c3052015-04-29 18:14:18 -0400128 TCHAR workingDirectory[MAX_PATH];
129 GetCurrentDirectory(MAX_PATH, workingDirectory);
130
131 QString ringtonePath = QString::fromWCharArray(workingDirectory);
Andreas Traczyk3d0276c2019-01-02 12:00:07 -0500132 ringtonePath += QStringLiteral("\\ringtones\\default.opus");
Edric Milaret031c3052015-04-29 18:14:18 -0400133 return ringtonePath;
Edric Milaretb37aa1f2015-07-09 16:39:04 -0400134#else
135 return QString("/usr/local");
136#endif
Edric Milaret031c3052015-04-29 18:14:18 -0400137}
138
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400139QString
140Utils::GenGUID() {
Edric Milaret36587362016-02-04 12:30:52 -0500141#ifdef Q_OS_WIN
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400142 GUID gidReference;
143 wchar_t *str;
144 HRESULT hCreateGuid = CoCreateGuid(&gidReference);
145 if (hCreateGuid == S_OK) {
146 StringFromCLSID(gidReference, &str);
147 auto gStr = QString::fromWCharArray(str);
148 return gStr.remove("{").remove("}").toLower();
149 }
150 else
Edric Milaret36587362016-02-04 12:30:52 -0500151 return QString();
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400152#else
153 return QString("");
154#endif
155}
156
157QString
158Utils::GetISODate() {
Edric Milaret36587362016-02-04 12:30:52 -0500159#ifdef Q_OS_WIN
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400160 SYSTEMTIME lt;
161 GetSystemTime(&lt);
162 return QString("%1-%2-%3T%4:%5:%6Z").arg(lt.wYear).arg(lt.wMonth,2,10,QChar('0')).arg(lt.wDay,2,10,QChar('0'))
163 .arg(lt.wHour,2,10,QChar('0')).arg(lt.wMinute,2,10,QChar('0')).arg(lt.wSecond,2,10,QChar('0'));
164#else
Edric Milaret36587362016-02-04 12:30:52 -0500165 return QString();
166#endif
167}
168
169QString
170Utils::GetCurrentUserName() {
171#ifdef Q_OS_WIN
172 wchar_t username[UNLEN+1];
173 DWORD username_len = UNLEN+1;
174 GetUserName(username, &username_len);
Edric Milaret25236d92016-03-28 09:40:58 -0400175 return QString::fromWCharArray(username, username_len-1);
Edric Milaret36587362016-02-04 12:30:52 -0500176#else
177 return QString();
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400178#endif
179}
180
Edric Milaret4097d2f2016-02-09 14:41:50 -0500181void
182Utils::InvokeMailto(const QString& subject,
183 const QString& body,
184 const QString& attachement) {
Edric Milaret3ba22ca2016-03-01 13:28:01 -0500185#ifdef Q_OS_WIN
186 HKEY hKey;
187 LONG lRes = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"mailto", 0, KEY_READ, &hKey);
188 if (lRes != ERROR_FILE_NOT_FOUND) {
189 auto addr = QString("mailto:?subject=%1&body=%2").arg(subject).arg(body);
190 if (not attachement.isEmpty())
191 addr += QString("&attachement=%1").arg(attachement);
192 ShellExecute(nullptr, L"open", addr.toStdWString().c_str(), NULL, NULL, SW_SHOWNORMAL);
193 } else {
194 QErrorMessage errorMessage;
195 errorMessage.showMessage(QObject::tr("No default mail client found"));
196 }
197#endif
Edric Milaret4097d2f2016-02-09 14:41:50 -0500198}
Edric Milaret25236d92016-03-28 09:40:58 -0400199
200QImage
201Utils::getCirclePhoto(const QImage original, int sizePhoto)
202{
203 QImage target(sizePhoto, sizePhoto, QImage::Format_ARGB32_Premultiplied);
204 target.fill(Qt::transparent);
205
206 QPainter painter(&target);
207 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
208 painter.setBrush(QBrush(Qt::white));
209 auto scaledPhoto = original
210 .scaled(sizePhoto, sizePhoto, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)
211 .convertToFormat(QImage::Format_ARGB32_Premultiplied);
212 int margin = 0;
213 if (scaledPhoto.width() > sizePhoto) {
214 margin = (scaledPhoto.width() - sizePhoto) / 2;
215 }
216 painter.drawEllipse(0, 0, sizePhoto, sizePhoto);
217 painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
218 painter.drawImage(0, 0, scaledPhoto, margin, 0);
219 return target;
220}
Olivier SOLDANOc1ca2df2017-09-06 16:01:46 -0400221
222void
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400223Utils::setStackWidget(QStackedWidget* stack, QWidget* widget)
Olivier SOLDANOc1ca2df2017-09-06 16:01:46 -0400224{
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400225 if (stack->indexOf(widget) != -1 && stack->currentWidget() != widget) {
Olivier SOLDANOc1ca2df2017-09-06 16:01:46 -0400226 stack->setCurrentWidget(widget);
Olivier SOLDANOc1ca2df2017-09-06 16:01:46 -0400227 }
228}
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400229
230void Utils::showSystemNotification(QWidget* widget, const QString & message, long delay)
231{
Sébastien Blincba5b522018-12-10 12:48:36 -0500232 GlobalSystemTray::instance().showMessage("Jami", message);
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400233 QApplication::alert(widget, delay);
234}
235
236// new lrc helpers
237
238inline std::string
239removeEndlines(const std::string& str)
240{
241 std::string trimmed(str);
242 trimmed.erase(std::remove(trimmed.begin(), trimmed.end(), '\n'), trimmed.end());
243 trimmed.erase(std::remove(trimmed.begin(), trimmed.end(), '\r'), trimmed.end());
244 return trimmed;
245}
246
247std::string
248Utils::bestIdForConversation(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model)
249{
250 auto contact = model.owner.contactModel->getContact(conv.participants[0]);
251 if (!contact.registeredName.empty()) {
252 return removeEndlines(contact.registeredName);
253 }
254 return removeEndlines(contact.profileInfo.uri);
255}
256
257std::string
258Utils::bestIdForAccount(const lrc::api::account::Info& account)
259{
260 if (!account.registeredName.empty()) {
261 return removeEndlines(account.registeredName);
262 }
263 return removeEndlines(account.profileInfo.uri);
264}
265
266std::string
267Utils::bestNameForAccount(const lrc::api::account::Info& account)
268{
269 if (account.profileInfo.alias.empty()) {
270 return bestIdForAccount(account);
271 }
272 return account.profileInfo.alias;
273}
274
275std::string
276Utils::bestIdForContact(const lrc::api::contact::Info& contact)
277{
278 if (!contact.registeredName.empty()) {
279 return removeEndlines(contact.registeredName);
280 }
281 return removeEndlines(contact.profileInfo.uri);
282}
283
284std::string
285Utils::bestNameForContact(const lrc::api::contact::Info& contact)
286{
287 auto alias = removeEndlines(contact.profileInfo.alias);
288 if (alias.length() == 0) {
289 return bestIdForContact(contact);
290 }
291 return alias;
292}
293
294std::string
295Utils::bestNameForConversation(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model)
296{
297 auto contact = model.owner.contactModel->getContact(conv.participants[0]);
298 auto alias = removeEndlines(contact.profileInfo.alias);
299 if (alias.length() == 0) {
300 return bestIdForConversation(conv, model);
301 }
302 return alias;
303}
304
Isa Nanic601de1d2018-10-23 11:37:26 -0400305// returns empty string if only infoHash is available, second best identifier otherwise
306std::string
307Utils::secondBestNameForAccount(const lrc::api::account::Info& account)
308{
309 auto alias = removeEndlines(account.profileInfo.alias);
310 auto registeredName = removeEndlines(account.registeredName);
311 auto infoHash = account.profileInfo.uri;
312
313 if (!alias.length() == 0) { // if alias exists
314 if (!registeredName.length() == 0) { // if registeredName exists
315 return registeredName;
316 }
317 else {
318 return infoHash;
319 }
320 }
321 else {
322 if (!registeredName.length() == 0) { // if registeredName exists
323 return infoHash;
324 }
325 else {
326 return "";
327 }
328 }
329}
330
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400331lrc::api::profile::Type
332Utils::profileType(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model)
333{
334 try {
335 auto contact = model.owner.contactModel->getContact(conv.participants[0]);
336 return contact.profileInfo.type;
337 }
338 catch (...) {
339 return lrc::api::profile::Type::INVALID;
340 }
341}
342
343std::string
344Utils::formatTimeString(const std::time_t& timestamp)
345{
346 std::time_t now = std::time(nullptr);
347 char interactionDay[64];
348 char nowDay[64];
349 std::strftime(interactionDay, sizeof(interactionDay), "%D", std::localtime(&timestamp));
350 std::strftime(nowDay, sizeof(nowDay), "%D", std::localtime(&now));
351 if (std::string(interactionDay) == std::string(nowDay)) {
352 char interactionTime[64];
353 std::strftime(interactionTime, sizeof(interactionTime), "%R", std::localtime(&timestamp));
354 return interactionTime;
355 }
356 else {
357 return interactionDay;
358 }
359}
360
Andreas Traczyk2771e2e2019-01-08 17:44:33 -0500361const lrc::api::conversation::Info*
362Utils::getConversationPtrFromUid(const std::string& uid, const lrc::api::ConversationModel& model) {
363 auto currentConversation = Utils::getConversationFromUid(LRCInstance::getSelectedConvUid(), model);
364 auto conversations = model.allFilteredConversations();
365 if (currentConversation == conversations.end()) {
366 return nullptr;
367 }
368 return &(conversations[std::distance(model.allFilteredConversations().begin(), currentConversation)]);
369}
370
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400371lrc::api::ConversationModel::ConversationQueue::const_iterator
372Utils::getConversationFromUid(const std::string& uid, const lrc::api::ConversationModel& model) {
373 return std::find_if(model.allFilteredConversations().begin(), model.allFilteredConversations().end(),
374 [&](const lrc::api::conversation::Info& conv) {
375 return uid == conv.uid;
376 });
377}
378
379lrc::api::ConversationModel::ConversationQueue::const_iterator
380Utils::getConversationFromUri(const std::string& uri, const lrc::api::ConversationModel& model) {
381 return std::find_if(model.allFilteredConversations().begin(), model.allFilteredConversations().end(),
382 [&](const lrc::api::conversation::Info& conv) {
383 return uri == conv.participants[0];
384 });
385}
386
387bool
388Utils::isInteractionGenerated(const lrc::api::interaction::Type& type)
389{
390 return type == lrc::api::interaction::Type::CALL ||
391 type == lrc::api::interaction::Type::CONTACT;
392}
393
394bool
395Utils::isContactValid(const std::string& contactUid, const lrc::api::ConversationModel& model)
396{
397 auto contact = model.owner.contactModel->getContact(contactUid);
398 return (contact.profileInfo.type == lrc::api::profile::Type::PENDING ||
399 contact.profileInfo.type == lrc::api::profile::Type::TEMPORARY ||
400 contact.profileInfo.type == lrc::api::profile::Type::RING ||
401 contact.profileInfo.type == lrc::api::profile::Type::SIP) &&
402 !contact.profileInfo.uri.empty();
403}
404
405QImage
406Utils::conversationPhoto(const std::string & convUid, const lrc::api::account::Info& accountInfo)
407{
408 auto& convModel = accountInfo.conversationModel;
409 auto conversation = Utils::getConversationFromUid(convUid, *convModel);
410 if (conversation == (*convModel).allFilteredConversations().end()) {
411 return QImage();
412 }
413
414 QVariant var = GlobalInstances::pixmapManipulator().decorationRole(*conversation, accountInfo);
415 return var.value<QImage>();
416}
Andreas Traczyk43c08232018-10-31 13:42:09 -0400417
418QByteArray
419Utils::QByteArrayFromFile(const QString& filename)
420{
421 QFile file(filename);
422 if (file.open(QIODevice::ReadOnly)) {
423 return file.readAll();
424 } else {
425 qDebug() << "can't open file";
426 return QByteArray();
427 }
Andreas Traczyk6ace34f2018-12-14 14:31:23 -0500428}
429
430QPixmap
431Utils::generateTintedPixmap(const QString& filename, QColor color)
432{
433 QPixmap px(filename);
434 QImage tmpImage = px.toImage();
435 for (int y = 0; y < tmpImage.height(); y++) {
436 for (int x = 0; x < tmpImage.width(); x++) {
437 color.setAlpha(tmpImage.pixelColor(x, y).alpha());
438 tmpImage.setPixelColor(x, y, color);
439 }
440 }
441 return QPixmap::fromImage(tmpImage);
Andreas Traczyk6b5ad3e2019-01-02 17:04:36 -0500442}
443
444std::string
445Utils::getConversationFromCallId(const std::string& callId)
446{
447 auto convModel = LRCInstance::getCurrentConversationModel();
448 auto conversations = convModel->allFilteredConversations();
449 std::string convUid;
450 for (auto conversation : conversations) {
451 if (conversation.callId == callId) {
452 return conversation.uid;
453 }
454 }
455 return "";
Andreas Traczyk43c08232018-10-31 13:42:09 -0400456}