blob: 56ffb9cb0cb8a040c8931ce110e263a0515af488 [file] [log] [blame]
Edric Milaret4bba46d2015-04-29 16:33:38 -04001/***************************************************************************
Andreas Traczykb8b13ba2018-08-21 16:30:16 -04002 * Copyright (C) 2015-2018 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>
39
40#include "globalinstances.h"
41#include "pixbufmanipulator.h"
42
43#include "globalsystemtray.h"
Edric Milaret3ba22ca2016-03-01 13:28:01 -050044
Edric Milaret4bba46d2015-04-29 16:33:38 -040045bool
Edric Milaret1eca0292015-06-29 12:03:36 -040046Utils::CreateStartupLink()
47{
Edric Milaret36587362016-02-04 12:30:52 -050048#ifdef Q_OS_WIN
Edric Milaret465a3142015-06-02 15:02:52 -040049 TCHAR szPath[MAX_PATH];
50 GetModuleFileName(NULL, szPath, MAX_PATH);
Edric Milaret4bba46d2015-04-29 16:33:38 -040051
Edric Milaret465a3142015-06-02 15:02:52 -040052 std::wstring programPath(szPath);
Edric Milaret4bba46d2015-04-29 16:33:38 -040053
Edric Milaret465a3142015-06-02 15:02:52 -040054 TCHAR startupPath[MAX_PATH];
55 SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath);
56
57 std::wstring linkPath(startupPath);
58 linkPath += TEXT("\\Ring.lnk");
Edric Milaret4bba46d2015-04-29 16:33:38 -040059
60 return Utils::CreateLink(programPath.c_str(), linkPath.c_str());
Edric Milaretb37aa1f2015-07-09 16:39:04 -040061#else
62 return true;
63#endif
Edric Milaret4bba46d2015-04-29 16:33:38 -040064}
65
66bool
67Utils::CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink) {
Edric Milaret36587362016-02-04 12:30:52 -050068#ifdef Q_OS_WIN
Edric Milaret4bba46d2015-04-29 16:33:38 -040069 HRESULT hres;
70 IShellLink* psl;
71
72 hres = CoCreateInstance(CLSID_ShellLink, NULL,
73 CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
74 if (SUCCEEDED(hres))
75 {
76 IPersistFile* ppf;
77 psl->SetPath(lpszPathObj);
Edric Milaret90bd5a82015-06-08 10:07:59 -040078 psl->SetArguments(TEXT("--minimized"));
Edric Milaret4bba46d2015-04-29 16:33:38 -040079
80 hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
81 if (SUCCEEDED(hres))
82 {
83 hres = ppf->Save(lpszPathLink, TRUE);
84 ppf->Release();
85 }
86 psl->Release();
87 }
88 return hres;
Edric Milaretb37aa1f2015-07-09 16:39:04 -040089#else
Edric Milaret36587362016-02-04 12:30:52 -050090 Q_UNUSED(lpszPathObj)
91 Q_UNUSED(lpszPathLink)
Edric Milaretb37aa1f2015-07-09 16:39:04 -040092 return true;
93#endif
Edric Milaret4bba46d2015-04-29 16:33:38 -040094}
95
96void
97Utils::DeleteStartupLink() {
Edric Milaret36587362016-02-04 12:30:52 -050098#ifdef Q_OS_WIN
Edric Milaret465a3142015-06-02 15:02:52 -040099 TCHAR startupPath[MAX_PATH];
100 SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath);
Edric Milaret4bba46d2015-04-29 16:33:38 -0400101
Edric Milaret465a3142015-06-02 15:02:52 -0400102 std::wstring linkPath(startupPath);
103 linkPath += TEXT("\\Ring.lnk");
Edric Milaret4bba46d2015-04-29 16:33:38 -0400104
105 DeleteFile(linkPath.c_str());
Edric Milaretb37aa1f2015-07-09 16:39:04 -0400106#endif
Edric Milaret4bba46d2015-04-29 16:33:38 -0400107}
108
109bool
110Utils::CheckStartupLink() {
Edric Milaret36587362016-02-04 12:30:52 -0500111#ifdef Q_OS_WIN
Edric Milaret465a3142015-06-02 15:02:52 -0400112 TCHAR startupPath[MAX_PATH];
113 SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath);
Edric Milaret4bba46d2015-04-29 16:33:38 -0400114
Edric Milaret465a3142015-06-02 15:02:52 -0400115 std::wstring linkPath(startupPath);
116 linkPath += TEXT("\\Ring.lnk");
Edric Milaret4bba46d2015-04-29 16:33:38 -0400117 return PathFileExists(linkPath.c_str());
Edric Milaretb37aa1f2015-07-09 16:39:04 -0400118#else
119 return true;
120#endif
Edric Milaret4bba46d2015-04-29 16:33:38 -0400121}
122
Edric Milaret031c3052015-04-29 18:14:18 -0400123QString
124Utils::GetRingtonePath() {
Edric Milaret36587362016-02-04 12:30:52 -0500125#ifdef Q_OS_WIN
Edric Milaret031c3052015-04-29 18:14:18 -0400126 TCHAR workingDirectory[MAX_PATH];
127 GetCurrentDirectory(MAX_PATH, workingDirectory);
128
129 QString ringtonePath = QString::fromWCharArray(workingDirectory);
Edric Milaretb1b00ce2016-02-03 14:10:05 -0500130 ringtonePath += QStringLiteral("\\ringtones\\default.wav");
Edric Milaret031c3052015-04-29 18:14:18 -0400131
132 return ringtonePath;
Edric Milaretb37aa1f2015-07-09 16:39:04 -0400133#else
134 return QString("/usr/local");
135#endif
Edric Milaret031c3052015-04-29 18:14:18 -0400136}
137
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400138QString
139Utils::GenGUID() {
Edric Milaret36587362016-02-04 12:30:52 -0500140#ifdef Q_OS_WIN
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400141 GUID gidReference;
142 wchar_t *str;
143 HRESULT hCreateGuid = CoCreateGuid(&gidReference);
144 if (hCreateGuid == S_OK) {
145 StringFromCLSID(gidReference, &str);
146 auto gStr = QString::fromWCharArray(str);
147 return gStr.remove("{").remove("}").toLower();
148 }
149 else
Edric Milaret36587362016-02-04 12:30:52 -0500150 return QString();
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400151#else
152 return QString("");
153#endif
154}
155
156QString
157Utils::GetISODate() {
Edric Milaret36587362016-02-04 12:30:52 -0500158#ifdef Q_OS_WIN
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400159 SYSTEMTIME lt;
160 GetSystemTime(&lt);
161 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'))
162 .arg(lt.wHour,2,10,QChar('0')).arg(lt.wMinute,2,10,QChar('0')).arg(lt.wSecond,2,10,QChar('0'));
163#else
Edric Milaret36587362016-02-04 12:30:52 -0500164 return QString();
165#endif
166}
167
168QString
169Utils::GetCurrentUserName() {
170#ifdef Q_OS_WIN
171 wchar_t username[UNLEN+1];
172 DWORD username_len = UNLEN+1;
173 GetUserName(username, &username_len);
Edric Milaret25236d92016-03-28 09:40:58 -0400174 return QString::fromWCharArray(username, username_len-1);
Edric Milaret36587362016-02-04 12:30:52 -0500175#else
176 return QString();
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400177#endif
178}
179
Edric Milaret4097d2f2016-02-09 14:41:50 -0500180void
181Utils::InvokeMailto(const QString& subject,
182 const QString& body,
183 const QString& attachement) {
Edric Milaret3ba22ca2016-03-01 13:28:01 -0500184#ifdef Q_OS_WIN
185 HKEY hKey;
186 LONG lRes = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"mailto", 0, KEY_READ, &hKey);
187 if (lRes != ERROR_FILE_NOT_FOUND) {
188 auto addr = QString("mailto:?subject=%1&body=%2").arg(subject).arg(body);
189 if (not attachement.isEmpty())
190 addr += QString("&attachement=%1").arg(attachement);
191 ShellExecute(nullptr, L"open", addr.toStdWString().c_str(), NULL, NULL, SW_SHOWNORMAL);
192 } else {
193 QErrorMessage errorMessage;
194 errorMessage.showMessage(QObject::tr("No default mail client found"));
195 }
196#endif
Edric Milaret4097d2f2016-02-09 14:41:50 -0500197}
Edric Milaret25236d92016-03-28 09:40:58 -0400198
199QImage
200Utils::getCirclePhoto(const QImage original, int sizePhoto)
201{
202 QImage target(sizePhoto, sizePhoto, QImage::Format_ARGB32_Premultiplied);
203 target.fill(Qt::transparent);
204
205 QPainter painter(&target);
206 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
207 painter.setBrush(QBrush(Qt::white));
208 auto scaledPhoto = original
209 .scaled(sizePhoto, sizePhoto, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)
210 .convertToFormat(QImage::Format_ARGB32_Premultiplied);
211 int margin = 0;
212 if (scaledPhoto.width() > sizePhoto) {
213 margin = (scaledPhoto.width() - sizePhoto) / 2;
214 }
215 painter.drawEllipse(0, 0, sizePhoto, sizePhoto);
216 painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
217 painter.drawImage(0, 0, scaledPhoto, margin, 0);
218 return target;
219}
Olivier SOLDANOc1ca2df2017-09-06 16:01:46 -0400220
221void
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400222Utils::setStackWidget(QStackedWidget* stack, QWidget* widget)
Olivier SOLDANOc1ca2df2017-09-06 16:01:46 -0400223{
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400224 if (stack->indexOf(widget) != -1 && stack->currentWidget() != widget) {
Olivier SOLDANOc1ca2df2017-09-06 16:01:46 -0400225 stack->setCurrentWidget(widget);
Olivier SOLDANOc1ca2df2017-09-06 16:01:46 -0400226 }
227}
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400228
229void Utils::showSystemNotification(QWidget* widget, const QString & message, long delay)
230{
231 GlobalSystemTray::instance().showMessage("Ring", message);
232 QApplication::alert(widget, delay);
233}
234
235// new lrc helpers
236
237inline std::string
238removeEndlines(const std::string& str)
239{
240 std::string trimmed(str);
241 trimmed.erase(std::remove(trimmed.begin(), trimmed.end(), '\n'), trimmed.end());
242 trimmed.erase(std::remove(trimmed.begin(), trimmed.end(), '\r'), trimmed.end());
243 return trimmed;
244}
245
246std::string
247Utils::bestIdForConversation(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model)
248{
249 auto contact = model.owner.contactModel->getContact(conv.participants[0]);
250 if (!contact.registeredName.empty()) {
251 return removeEndlines(contact.registeredName);
252 }
253 return removeEndlines(contact.profileInfo.uri);
254}
255
256std::string
257Utils::bestIdForAccount(const lrc::api::account::Info& account)
258{
259 if (!account.registeredName.empty()) {
260 return removeEndlines(account.registeredName);
261 }
262 return removeEndlines(account.profileInfo.uri);
263}
264
265std::string
266Utils::bestNameForAccount(const lrc::api::account::Info& account)
267{
268 if (account.profileInfo.alias.empty()) {
269 return bestIdForAccount(account);
270 }
271 return account.profileInfo.alias;
272}
273
274std::string
275Utils::bestIdForContact(const lrc::api::contact::Info& contact)
276{
277 if (!contact.registeredName.empty()) {
278 return removeEndlines(contact.registeredName);
279 }
280 return removeEndlines(contact.profileInfo.uri);
281}
282
283std::string
284Utils::bestNameForContact(const lrc::api::contact::Info& contact)
285{
286 auto alias = removeEndlines(contact.profileInfo.alias);
287 if (alias.length() == 0) {
288 return bestIdForContact(contact);
289 }
290 return alias;
291}
292
293std::string
294Utils::bestNameForConversation(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model)
295{
296 auto contact = model.owner.contactModel->getContact(conv.participants[0]);
297 auto alias = removeEndlines(contact.profileInfo.alias);
298 if (alias.length() == 0) {
299 return bestIdForConversation(conv, model);
300 }
301 return alias;
302}
303
Isa Nanic601de1d2018-10-23 11:37:26 -0400304// returns empty string if only infoHash is available, second best identifier otherwise
305std::string
306Utils::secondBestNameForAccount(const lrc::api::account::Info& account)
307{
308 auto alias = removeEndlines(account.profileInfo.alias);
309 auto registeredName = removeEndlines(account.registeredName);
310 auto infoHash = account.profileInfo.uri;
311
312 if (!alias.length() == 0) { // if alias exists
313 if (!registeredName.length() == 0) { // if registeredName exists
314 return registeredName;
315 }
316 else {
317 return infoHash;
318 }
319 }
320 else {
321 if (!registeredName.length() == 0) { // if registeredName exists
322 return infoHash;
323 }
324 else {
325 return "";
326 }
327 }
328}
329
Andreas Traczykb8b13ba2018-08-21 16:30:16 -0400330lrc::api::profile::Type
331Utils::profileType(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model)
332{
333 try {
334 auto contact = model.owner.contactModel->getContact(conv.participants[0]);
335 return contact.profileInfo.type;
336 }
337 catch (...) {
338 return lrc::api::profile::Type::INVALID;
339 }
340}
341
342std::string
343Utils::formatTimeString(const std::time_t& timestamp)
344{
345 std::time_t now = std::time(nullptr);
346 char interactionDay[64];
347 char nowDay[64];
348 std::strftime(interactionDay, sizeof(interactionDay), "%D", std::localtime(&timestamp));
349 std::strftime(nowDay, sizeof(nowDay), "%D", std::localtime(&now));
350 if (std::string(interactionDay) == std::string(nowDay)) {
351 char interactionTime[64];
352 std::strftime(interactionTime, sizeof(interactionTime), "%R", std::localtime(&timestamp));
353 return interactionTime;
354 }
355 else {
356 return interactionDay;
357 }
358}
359
360lrc::api::ConversationModel::ConversationQueue::const_iterator
361Utils::getConversationFromUid(const std::string& uid, const lrc::api::ConversationModel& model) {
362 return std::find_if(model.allFilteredConversations().begin(), model.allFilteredConversations().end(),
363 [&](const lrc::api::conversation::Info& conv) {
364 return uid == conv.uid;
365 });
366}
367
368lrc::api::ConversationModel::ConversationQueue::const_iterator
369Utils::getConversationFromUri(const std::string& uri, const lrc::api::ConversationModel& model) {
370 return std::find_if(model.allFilteredConversations().begin(), model.allFilteredConversations().end(),
371 [&](const lrc::api::conversation::Info& conv) {
372 return uri == conv.participants[0];
373 });
374}
375
376bool
377Utils::isInteractionGenerated(const lrc::api::interaction::Type& type)
378{
379 return type == lrc::api::interaction::Type::CALL ||
380 type == lrc::api::interaction::Type::CONTACT;
381}
382
383bool
384Utils::isContactValid(const std::string& contactUid, const lrc::api::ConversationModel& model)
385{
386 auto contact = model.owner.contactModel->getContact(contactUid);
387 return (contact.profileInfo.type == lrc::api::profile::Type::PENDING ||
388 contact.profileInfo.type == lrc::api::profile::Type::TEMPORARY ||
389 contact.profileInfo.type == lrc::api::profile::Type::RING ||
390 contact.profileInfo.type == lrc::api::profile::Type::SIP) &&
391 !contact.profileInfo.uri.empty();
392}
393
394QImage
395Utils::conversationPhoto(const std::string & convUid, const lrc::api::account::Info& accountInfo)
396{
397 auto& convModel = accountInfo.conversationModel;
398 auto conversation = Utils::getConversationFromUid(convUid, *convModel);
399 if (conversation == (*convModel).allFilteredConversations().end()) {
400 return QImage();
401 }
402
403 QVariant var = GlobalInstances::pixmapManipulator().decorationRole(*conversation, accountInfo);
404 return var.value<QImage>();
405}