blob: 32b15fe1cc9fa6213a46fd00a85bc9c09c6e2c28 [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
361lrc::api::ConversationModel::ConversationQueue::const_iterator
362Utils::getConversationFromUid(const std::string& uid, const lrc::api::ConversationModel& model) {
363 return std::find_if(model.allFilteredConversations().begin(), model.allFilteredConversations().end(),
364 [&](const lrc::api::conversation::Info& conv) {
365 return uid == conv.uid;
366 });
367}
368
369lrc::api::ConversationModel::ConversationQueue::const_iterator
370Utils::getConversationFromUri(const std::string& uri, const lrc::api::ConversationModel& model) {
371 return std::find_if(model.allFilteredConversations().begin(), model.allFilteredConversations().end(),
372 [&](const lrc::api::conversation::Info& conv) {
373 return uri == conv.participants[0];
374 });
375}
376
377bool
378Utils::isInteractionGenerated(const lrc::api::interaction::Type& type)
379{
380 return type == lrc::api::interaction::Type::CALL ||
381 type == lrc::api::interaction::Type::CONTACT;
382}
383
384bool
385Utils::isContactValid(const std::string& contactUid, const lrc::api::ConversationModel& model)
386{
387 auto contact = model.owner.contactModel->getContact(contactUid);
388 return (contact.profileInfo.type == lrc::api::profile::Type::PENDING ||
389 contact.profileInfo.type == lrc::api::profile::Type::TEMPORARY ||
390 contact.profileInfo.type == lrc::api::profile::Type::RING ||
391 contact.profileInfo.type == lrc::api::profile::Type::SIP) &&
392 !contact.profileInfo.uri.empty();
393}
394
395QImage
396Utils::conversationPhoto(const std::string & convUid, const lrc::api::account::Info& accountInfo)
397{
398 auto& convModel = accountInfo.conversationModel;
399 auto conversation = Utils::getConversationFromUid(convUid, *convModel);
400 if (conversation == (*convModel).allFilteredConversations().end()) {
401 return QImage();
402 }
403
404 QVariant var = GlobalInstances::pixmapManipulator().decorationRole(*conversation, accountInfo);
405 return var.value<QImage>();
406}
Andreas Traczyk43c08232018-10-31 13:42:09 -0400407
408QByteArray
409Utils::QByteArrayFromFile(const QString& filename)
410{
411 QFile file(filename);
412 if (file.open(QIODevice::ReadOnly)) {
413 return file.readAll();
414 } else {
415 qDebug() << "can't open file";
416 return QByteArray();
417 }
Andreas Traczyk6ace34f2018-12-14 14:31:23 -0500418}
419
420QPixmap
421Utils::generateTintedPixmap(const QString& filename, QColor color)
422{
423 QPixmap px(filename);
424 QImage tmpImage = px.toImage();
425 for (int y = 0; y < tmpImage.height(); y++) {
426 for (int x = 0; x < tmpImage.width(); x++) {
427 color.setAlpha(tmpImage.pixelColor(x, y).alpha());
428 tmpImage.setPixelColor(x, y, color);
429 }
430 }
431 return QPixmap::fromImage(tmpImage);
Andreas Traczyk6b5ad3e2019-01-02 17:04:36 -0500432}
433
434std::string
435Utils::getConversationFromCallId(const std::string& callId)
436{
437 auto convModel = LRCInstance::getCurrentConversationModel();
438 auto conversations = convModel->allFilteredConversations();
439 std::string convUid;
440 for (auto conversation : conversations) {
441 if (conversation.callId == callId) {
442 return conversation.uid;
443 }
444 }
445 return "";
Andreas Traczyk43c08232018-10-31 13:42:09 -0400446}