1 /*
2     Copyright (C) 2014 Aseman
3     http://aseman.co
4 
5     This project is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This project is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #define UNITY_LIGHT (p->desktop->desktopSession()==AsemanDesktopTools::Unity && !p->desktop->titleBarIsDark())
20 #define UNITY_ICON_PATH(NUM) "/tmp/aseman-telegram-client-trayicon" + QString::number(NUM) + (lowLevelDarkSystemTray()?"-dark":"-light") + ".png"
21 #define SYSTRAY_ICON (lowLevelDarkSystemTray()?":/qml/Cutegram/files/systray-dark.png":":/qml/Cutegram/files/systray.png")
22 #define SYSTRAY_ICON_STATUS QString("General/statusIconStyle_%1").arg(AsemanDesktopTools::desktopSession())
23 
24 #include "cutegram.h"
25 #include "asemantools/asemanquickview.h"
26 #include "asemantools/asemancalendarconverter.h"
27 #include "asemantools/asemandesktoptools.h"
28 #include "asemantools/asemandevices.h"
29 #include "asemantools/asemanapplication.h"
30 #include "asemantools/asemanlistrecord.h"
31 #include "emoticonsmodel.h"
32 #include "contributorsmodel.h"
33 #include "themeitem.h"
34 #include "texttohtmlconverter.h"
35 #include "cutegramdialog.h"
36 #include "textemojiwrapper.h"
37 #include "cutegramencrypter.h"
38 #include "stickerfilemanager.h"
39 #include "emojis.h"
40 #include "unitysystemtray.h"
41 #include "pasteanalizer.h"
42 #include "cutegramenums.h"
43 #include "authsaver.h"
44 
45 #include <QPointer>
46 #include <QQmlContext>
47 #include <QQmlEngine>
48 #include <QtQml>
49 #include <QDebug>
50 #include <QImageWriter>
51 #include <QTextDocument>
52 #include <QImageReader>
53 #include <QSystemTrayIcon>
54 #include <QGuiApplication>
55 #include <QMenu>
56 #include <QAction>
57 #include <QPainter>
58 #include <QPainterPath>
59 #include <QDesktopServices>
60 #include <QMimeDatabase>
61 #include <QTimer>
62 
63 #include <telegramqml.h>
64 #include <core/settings.h>
65 #include <userdata.h>
66 
67 #ifdef Q_OS_WIN
68 #include <QtWin>
69 #endif
70 
71 class CutegramPrivate
72 {
73 public:
74     QString defaultHostAddress;
75     int defaultHostPort;
76     int defaultHostDcId;
77     int appId;
78     QString appHash;
79 
80     QPointer<AsemanQuickView> viewer;
81     int sysTrayCounter;
82     int startupOption;
83     int statusIconStyle;
84     bool notification;
85     bool emojiOnHover;
86     bool minimumDialogs;
87     bool showLastMessage;
88     bool darkSystemTray;
89     bool closingState;
90     bool cutegramSubscribe;
91     bool kWallet;
92     bool nativeTitleBar;
93     bool autoEmojis;
94     bool smoothScroll;
95     bool sendByCtrlEnter;
96 
97     QTextDocument *doc;
98 
99     QSystemTrayIcon *sysTray;
100     UnitySystemTray *unityTray;
101 
102     AsemanDesktopTools *desktop;
103 
104     QTranslator *translator;
105     QString translationsPath;
106     QString themesPath;
107     QString emojisThemesPath;
108 
109     QHash<QString,QVariant> languages;
110     QHash<QString,QLocale> locales;
111     QString language;
112 
113     QString background;
114     QString messageAudio;
115     QString masterColor;
116     QFont font;
117 
118     QPalette mainPalette;
119     QColor highlightColor;
120 
121     QMimeDatabase mdb;
122 
123     QStringList themes;
124     QString theme;
125     QPointer<QQmlComponent> currentThemeComponent;
126     QPointer<ThemeItem> currentTheme;
127 
128     QStringList emojisThemes;
129     QString emojisTheme;
130 
131     QStringList searchEngines;
132     QString searchEngine;
133 
134     CutegramEncrypter encrypter;
135     QTimer *save_timer;
136 };
137 
Cutegram(QObject * parent)138 Cutegram::Cutegram(QObject *parent) :
139     QObject(parent)
140 {
141     QFont default_font;
142     int startupOption = StartupAutomatic;
143 #ifdef Q_OS_MACX
144     startupOption = StartupVisible;
145     default_font.setPointSize(9);
146 #endif
147 #ifdef Q_OS_WIN
148     default_font.setPointSize(10);
149 #endif
150 
151     p = new CutegramPrivate;
152     p->defaultHostAddress = "149.154.167.50";
153     p->defaultHostPort = 443;
154     p->defaultHostDcId = 2;
155     p->appId = 13682;
156     p->appHash = "de37bcf00f4688de900510f4f87384bb";
157     p->doc = new QTextDocument(this);
158     p->desktop = new AsemanDesktopTools(this);
159     p->sysTray = 0;
160     p->unityTray = 0;
161     p->sysTrayCounter = 0;
162     p->closingState = false;
163     p->highlightColor = AsemanApplication::settings()->value("General/lastHighlightColor", p->mainPalette.highlight().color().name() ).toString();
164     p->startupOption = AsemanApplication::settings()->value("General/startupOption", startupOption).toInt();
165     p->statusIconStyle = AsemanApplication::settings()->value(SYSTRAY_ICON_STATUS, static_cast<int>(StatusIconAutomatic) ).toInt();
166     p->notification = AsemanApplication::settings()->value("General/notification", true ).toBool();
167     p->emojiOnHover = AsemanApplication::settings()->value("General/emojiOnHover", true ).toBool();
168     p->minimumDialogs = AsemanApplication::settings()->value("General/minimumDialogs", false ).toBool();
169     p->showLastMessage = AsemanApplication::settings()->value("General/showLastMessage", false ).toBool();
170     p->cutegramSubscribe = AsemanApplication::settings()->value("General/cutegramSubscribe", true ).toBool();
171     p->kWallet = AsemanApplication::settings()->value("General/kWallet", true ).toBool();
172     p->nativeTitleBar = AsemanApplication::settings()->value("General/nativeTitleBar", allowNativeTitleBar() ).toBool();
173     p->autoEmojis = AsemanApplication::settings()->value("General/autoEmojis", true ).toBool();
174     p->smoothScroll = AsemanApplication::settings()->value("General/smoothScroll", true ).toBool();
175     p->sendByCtrlEnter = AsemanApplication::settings()->value("General/sendByCtrlEnter", false ).toBool();
176     p->darkSystemTray = AsemanApplication::settings()->value("General/darkSystemTray", UNITY_LIGHT ).toBool();
177     p->background = AsemanApplication::settings()->value("General/background").toString();
178     p->masterColor = AsemanApplication::settings()->value("General/masterColor").toString();
179     p->messageAudio = AsemanApplication::settings()->value("General/messageAudio","files/new_msg.ogg").toString();
180     p->font = AsemanApplication::settings()->value("General/font", default_font).value<QFont>();
181     p->translator = new QTranslator(this);
182     p->theme = AsemanApplication::settings()->value("General/theme","Abrisham.qml").toString();
183     p->emojisTheme = AsemanApplication::settings()->value("General/emojisTheme", "twitter").toString();
184     p->searchEngine = AsemanApplication::settings()->value("General/searchEngine","https://duckduckgo.com/?q=").toString();
185 
186     p->searchEngines = QStringList() << "https://duckduckgo.com/?q=" << "https://google.com/?q=" << "https://bing.com/?q=";
187 
188 #ifdef Q_OS_ANDROID
189     p->translationsPath = "assets:/files/translations";
190     p->themesPath = "assets:/themes";
191 #else
192     p->translationsPath = AsemanDevices::resourcePath() + "/files/translations/";
193     p->themesPath = AsemanDevices::resourcePath() + "/themes/";
194     p->emojisThemesPath = AsemanDevices::resourcePath() + "/emojis/";
195 #endif
196 
197     p->save_timer = new QTimer(this);
198     p->save_timer->setSingleShot(true);
199     p->save_timer->setInterval(1000);
200 
201     p->themes = QDir(p->themesPath).entryList( QStringList()<<"*.qml" ,QDir::Files, QDir::Name);
202 
203     p->emojisThemes = QDir(p->emojisThemesPath).entryList( QStringList() ,QDir::Dirs|QDir::NoDotAndDotDot, QDir::Name);
204 
205     QDir().mkpath(personalStickerDirectory());
206 
207     qmlRegisterType<StickerFileManager>("Cutegram", 1, 0, "StickerFileManager");
208     qmlRegisterType<ContributorsModel>("Cutegram", 1, 0, "ContributorsModel");
209     qmlRegisterType<CutegramEnums>("Cutegram", 1, 0, "CutegramEnums");
210     qmlRegisterType<ThemeItem>("Cutegram", 1, 0, "CutegramTheme");
211     qmlRegisterType<TextEmojiWrapper>("Cutegram", 1, 0, "TextEmojiWrapper");
212     qmlRegisterType<Emojis>("Cutegram", 1, 0, "Emojis");
213     qmlRegisterType<EmoticonsModel>("Cutegram", 1, 0, "EmoticonsModel");
214     qmlRegisterType<CutegramDialog>("Cutegram", 1, 0, "CutegramDialog");
215     qmlRegisterType<PasteAnalizer>("Cutegram", 1, 0, "PasteAnalizer");
216     qmlRegisterType<TextToHtmlConverter>("Cutegram", 1, 0, "TextToHtmlConverter");
217 
218     init_languages();
219 
220     connect(p->save_timer, SIGNAL(timeout()), SLOT(savePosition()));
221 }
222 
imageSize(const QString & pt)223 QSize Cutegram::imageSize(const QString &pt)
224 {
225     QString path = pt;
226     if(path.left(AsemanDevices::localFilesPrePath().length()) == AsemanDevices::localFilesPrePath())
227         path = path.mid(AsemanDevices::localFilesPrePath().length());
228     if(path.isEmpty())
229         return QSize();
230 
231     QImageReader img(path);
232     return img.size();
233 }
234 
filsIsImage(const QString & pt)235 bool Cutegram::filsIsImage(const QString &pt)
236 {
237     QString path = pt;
238     if(path.left(AsemanDevices::localFilesPrePath().length()) == AsemanDevices::localFilesPrePath())
239         path = path.mid(AsemanDevices::localFilesPrePath().length());
240     if(path.isEmpty())
241         return false;
242 
243     return p->mdb.mimeTypeForFile(path).name().toLower().contains("image");
244 }
245 
htmlWidth(const QString & txt)246 qreal Cutegram::htmlWidth(const QString &txt)
247 {
248     p->doc->setHtml(txt);
249     return p->doc->size().width() + 10;
250 }
251 
deleteFile(const QString & pt)252 void Cutegram::deleteFile(const QString &pt)
253 {
254     QString path = pt;
255     if(path.left(AsemanDevices::localFilesPrePath().length()) == AsemanDevices::localFilesPrePath())
256         path = path.mid(AsemanDevices::localFilesPrePath().length());
257     if(path.isEmpty())
258         return;
259 
260     QFile::remove(path);
261 }
262 
storeMessage(const QString & msg)263 QString Cutegram::storeMessage(const QString &msg)
264 {
265     const QString &path = AsemanApplication::tempPath() + "/" + QDateTime::currentDateTime().toString("ddd MMMM d yy - hh mm") + ".txt";
266     QFile file(path);
267     if(!file.open(QFile::WriteOnly))
268         return QString();
269 
270     file.write(msg.toUtf8());
271     file.close();
272 
273     return path;
274 }
275 
getTimeString(const QDateTime & dt)276 QString Cutegram::getTimeString(const QDateTime &dt)
277 {
278     if( QDate::currentDate() == dt.date() ) // TODAY
279         return dt.toString("HH:mm");
280     else
281     if( dt.date().daysTo(QDate::currentDate()) < 7 )
282         return dt.toString("ddd HH:mm");
283     else
284     if( dt.date().year() == QDate::currentDate().year() )
285         return dt.toString("dd MMM");
286     else
287         return dt.toString("dd MMM yy");
288 }
289 
showMenu(const QStringList & actions,QPoint point)290 int Cutegram::showMenu(const QStringList &actions, QPoint point)
291 {
292     if( point.isNull() )
293         point = QCursor::pos();
294 
295     QMenu menu;
296     QList<QAction*> pointers;
297     for( int i=0; i<actions.count(); i++ )
298         pointers << menu.addAction(actions.value(i));
299 
300     QAction *res = menu.exec(point);
301     return pointers.indexOf(res);
302 }
303 
setDefaultHostAddress(const QString & host)304 void Cutegram::setDefaultHostAddress(const QString &host)
305 {
306     if(p->defaultHostAddress == host)
307         return;
308 
309     p->defaultHostAddress = host;
310     emit defaultHostAddressChanged();
311 }
312 
defaultHostAddress() const313 QString Cutegram::defaultHostAddress() const
314 {
315     return p->defaultHostAddress;
316 }
317 
setDefaultHostPort(int port)318 void Cutegram::setDefaultHostPort(int port)
319 {
320     if(p->defaultHostPort == port)
321         return;
322 
323     p->defaultHostPort = port;
324     emit defaultHostPortChanged();
325 }
326 
defaultHostPort() const327 int Cutegram::defaultHostPort() const
328 {
329     return p->defaultHostPort;
330 }
331 
setDefaultHostDcId(int dcId)332 void Cutegram::setDefaultHostDcId(int dcId)
333 {
334     if(p->defaultHostDcId == dcId)
335         return;
336 
337     p->defaultHostDcId = dcId;
338     emit defaultHostDcIdChanged();
339 }
340 
defaultHostDcId() const341 int Cutegram::defaultHostDcId() const
342 {
343     return p->defaultHostDcId;
344 }
345 
setAppId(int appId)346 void Cutegram::setAppId(int appId)
347 {
348     if(p->appId == appId)
349         return;
350 
351     p->appId = appId;
352     emit appIdChanged();
353 }
354 
appId() const355 int Cutegram::appId() const
356 {
357     return p->appId;
358 }
359 
setAppHash(const QString & appHash)360 void Cutegram::setAppHash(const QString &appHash)
361 {
362     if(p->appHash == appHash)
363         return;
364 
365     p->appHash = appHash;
366     emit appHashChanged();
367 }
368 
appHash() const369 QString Cutegram::appHash() const
370 {
371     return p->appHash;
372 }
373 
start(bool forceVisible)374 void Cutegram::start(bool forceVisible)
375 {
376     if( p->viewer )
377         return;
378 
379     p->viewer = new AsemanQuickView();
380     p->viewer->engine()->rootContext()->setContextProperty( "Cutegram", this );
381     p->viewer->setColor(QColor(0,0,0,0));
382     p->viewer->setFlags(Qt::Window|
383                         (nativeTitleBar()?Qt::FramelessWindowHint|Qt::NoDropShadowWindowHint:
384                                           Qt::Widget)|
385                         Qt::WindowTitleHint|
386                         Qt::WindowSystemMenuHint|
387                         Qt::WindowMinMaxButtonsHint|
388                         Qt::WindowCloseButtonHint|
389                         Qt::WindowFullscreenButtonHint);
390 
391     init_theme();
392 
393     p->viewer->setSource(QUrl(QStringLiteral("qrc:/qml/Cutegram/main.qml")));
394 
395     QPoint point = AsemanApplication::settings()->value("General/position").toPoint();
396     if(!point.isNull())
397         p->viewer->setPosition(point);
398 
399 #ifdef Q_OS_WIN
400     QtWin::extendFrameIntoClientArea(p->viewer,-1,-1,-1,-1);
401 #endif
402 
403     switch(startupOption())
404     {
405     case StartupAutomatic:
406         if(AsemanApplication::settings()->value("General/lastWindowState",true).toBool())
407             p->viewer->show();
408         break;
409 
410     case StartupVisible:
411         p->viewer->show();
412         break;
413 
414     case StartupHide:
415         break;
416     }
417 
418     if(forceVisible)
419         p->viewer->show();
420 
421     p->viewer->installEventFilter(this);
422     init_systray();
423 }
424 
restart()425 void Cutegram::restart()
426 {
427     QProcess::startDetached(QCoreApplication::applicationFilePath(), QStringList()<<"--force");
428     quit();
429 }
430 
logout(const QString & phone)431 void Cutegram::logout(const QString &phone)
432 {
433     const QString &home = AsemanApplication::homePath();
434     const QString &ppath = home + "/" + phone;
435     QFile::remove(ppath + "/config");
436     QFile::remove(ppath + "/secret");
437     QFile::remove(ppath + "/database.db");
438     QFile::remove(ppath + "/database.db-journal");
439 
440     Settings::clearAuth(home, phone);
441 
442     restart();
443 }
444 
close()445 void Cutegram::close()
446 {
447     p->viewer->close();
448 }
449 
quit()450 void Cutegram::quit()
451 {
452     p->closingState = true;
453     emit closingStateChanged();
454 
455     QGuiApplication::quit();
456 }
457 
contact()458 void Cutegram::contact()
459 {
460     QDesktopServices::openUrl(QUrl("http://aseman.co/en/contact-us/cutegram/"));
461 }
462 
aboutAseman()463 void Cutegram::aboutAseman()
464 {
465     emit aboutAsemanRequest();
466     active();
467 }
468 
about()469 void Cutegram::about()
470 {
471     p->viewer->root()->setProperty("aboutMode", true);
472     active();
473 }
474 
configure()475 void Cutegram::configure()
476 {
477     emit configureRequest();
478     active();
479 }
480 
incomingAppMessage(const QString & msg)481 void Cutegram::incomingAppMessage(const QString &msg)
482 {
483     AsemanListRecord record(msg.toUtf8());
484     const QList<QByteArray> &list = record.toQByteArrayList();
485 
486     foreach(const QByteArray &data, list)
487     {
488         QString msgPart = data;
489         if( msgPart == "show" )
490         {
491             active();
492         }
493         else
494         if(msgPart.left(5) == "tg://")
495         {
496             const QString &tgMsg = msgPart.mid(5);
497             const int index = tgMsg.indexOf("?");
498             if(index == -1)
499             {
500                 const QString &cmd = tgMsg;
501                 Q_UNUSED(cmd)
502                 return;
503             }
504 
505             const QString &cmd = tgMsg.left(index);
506             const QStringList &args = tgMsg.mid(index+1).split("?", QString::SkipEmptyParts);
507             if(cmd.toLower() == "addstickers")
508             {
509                 QString set;
510                 foreach(const QString &arg, args)
511                 {
512                     const int index = arg.indexOf("=");
513                     if(index < 0)
514                         continue;
515 
516                     const QString &argName = arg.left(index);
517                     if(argName == "set")
518                         set = arg.mid(index+1);
519                 }
520 
521                 installSticker(set);
522             }
523 
524         }
525     }
526 }
527 
active()528 void Cutegram::active()
529 {
530 #ifdef Q_OS_WIN
531     QtWin::extendFrameIntoClientArea(p->viewer,-1,-1,-1,-1);
532 #endif
533     p->viewer->show();
534     p->viewer->requestActivate();
535 }
536 
addToPersonal(const QString & src)537 void Cutegram::addToPersonal(const QString &src)
538 {
539     QString file = src;
540     if( file.left(AsemanDevices::localFilesPrePath().length()) == AsemanDevices::localFilesPrePath() )
541         file = file.mid(AsemanDevices::localFilesPrePath().length());
542 
543     QFile::copy(file, personalStickerDirectory() + "/" + QFileInfo(src).baseName() + ".webp");
544 }
545 
installSticker(const QString & shortName)546 void Cutegram::installSticker(const QString &shortName)
547 {
548     QVariant shortNameVar = shortName;
549     QMetaObject::invokeMethod(p->viewer->root(), "installSticker", Q_ARG(QVariant, shortNameVar));
550     active();
551 }
552 
savePosition(bool force)553 void Cutegram::savePosition(bool force)
554 {
555     p->save_timer->stop();
556     if(!force)
557         p->save_timer->start();
558     else
559     if(p->viewer)
560         AsemanApplication::settings()->setValue("General/position", p->viewer->position());
561 
562 }
563 
setSysTrayCounter(int count,bool force)564 void Cutegram::setSysTrayCounter(int count, bool force)
565 {
566     if( count == p->sysTrayCounter && !force )
567         return;
568 
569     const QImage & img = generateIcon( QImage(SYSTRAY_ICON), count );
570     if( p->sysTray )
571     {
572         p->sysTray->setIcon( QPixmap::fromImage(img) );
573     }
574     else
575     if( p->unityTray )
576     {
577         QString path = UNITY_ICON_PATH(count);
578         QFile::remove(path);
579         QImageWriter writer(path);
580         writer.write(img);
581         p->unityTray->setIcon(path);
582     }
583 
584     p->sysTrayCounter = count;
585     emit sysTrayCounterChanged();
586 }
587 
sysTrayCounter() const588 int Cutegram::sysTrayCounter() const
589 {
590     return p->sysTrayCounter;
591 }
592 
eventFilter(QObject * o,QEvent * e)593 bool Cutegram::eventFilter(QObject *o, QEvent *e)
594 {
595     if( o == p->viewer )
596     {
597         switch( static_cast<int>(e->type()) )
598         {
599         case QEvent::Move:
600             savePosition(false);
601             break;
602         }
603     }
604 
605     return QObject::eventFilter(o,e);
606 }
607 
systray_action(QSystemTrayIcon::ActivationReason act)608 void Cutegram::systray_action(QSystemTrayIcon::ActivationReason act)
609 {
610     switch( static_cast<int>(act) )
611     {
612     case QSystemTrayIcon::Trigger:
613         if( p->viewer->isVisible() && p->viewer->isActive() )
614             p->viewer->hide();
615         else
616         {
617             active();
618         }
619         break;
620     }
621 }
622 
init_systray()623 void Cutegram::init_systray()
624 {
625     if( p->desktop->desktopSession() == AsemanDesktopTools::Unity || p->desktop->desktopSession() == AsemanDesktopTools::GnomeFallBack )
626     {
627         QFile::remove(UNITY_ICON_PATH(0));
628         QFile::copy(SYSTRAY_ICON,UNITY_ICON_PATH(0));
629 
630         p->unityTray = new UnitySystemTray( QCoreApplication::applicationName(), UNITY_ICON_PATH(0) );
631         if( !p->unityTray->pntr() )
632             QGuiApplication::setQuitOnLastWindowClosed(true);
633 
634         p->unityTray->addMenu( tr("Show"), this, "active" );
635         p->unityTray->addMenu( tr("Configure"), this, "configure" );
636         p->unityTray->addMenu( tr("Contact"), this, "contact" );
637         p->unityTray->addMenu( tr("About"), this, "about" );
638         p->unityTray->addMenu( tr("About Aseman"), this, "aboutAseman" );
639         p->unityTray->addMenu( tr("Quit"), this, "quit" );
640     }
641     if( !p->unityTray || !p->unityTray->pntr() )
642     {
643         p->sysTray = new QSystemTrayIcon( QIcon(SYSTRAY_ICON), this );
644         p->sysTray->setContextMenu(contextMenu());
645 #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
646         p->sysTray->setToolTip(tr("Cutegram"));
647 #endif
648 #ifndef Q_OS_MAC
649         p->sysTray->show();
650 #endif
651 
652         connect( p->sysTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SLOT(systray_action(QSystemTrayIcon::ActivationReason)) );
653     }
654 }
655 
contextMenu()656 QMenu *Cutegram::contextMenu()
657 {
658     QMenu *menu = new QMenu();
659     menu->move( QCursor::pos() );
660 
661     menu->addAction( tr("Show"), this, SLOT(active()) );
662     menu->addSeparator();
663     menu->addAction( tr("Configure"), this, SLOT(configure()) );
664     menu->addAction( tr("Contact"), this, SLOT(contact()) );
665     menu->addSeparator();
666     menu->addAction( tr("About"), this, SLOT(about()) );
667     menu->addAction( tr("About Aseman"), this, SLOT(aboutAseman()) );
668     menu->addSeparator();
669     menu->addAction( tr("Exit"), this, SLOT(quit()) );
670 
671     return menu;
672 }
673 
generateIcon(const QImage & img,int count)674 QImage Cutegram::generateIcon(const QImage &img, int count)
675 {
676     QImage res = img;
677     if( count == 0 )
678         return img;
679 
680     QRect rct;
681     rct.setX( img.width()/5 );
682     rct.setWidth( 4*img.width()/5 );
683     rct.setY( img.height()-rct.width() );
684     rct.setHeight( rct.width() );
685 
686     QPainterPath path;
687     path.addEllipse(rct);
688 
689     QPainter painter(&res);
690     painter.setRenderHint( QPainter::Antialiasing , true );
691     painter.fillPath( path, QColor("#ff0000") );
692     painter.setPen("#333333");
693     painter.drawPath( path );
694     painter.setPen("#ffffff");
695     painter.drawText( rct, Qt::AlignCenter | Qt::AlignHCenter, QString::number(count) );
696 
697     return res;
698 }
699 
languages()700 QStringList Cutegram::languages()
701 {
702     QStringList res = p->languages.keys();
703     res.sort();
704     return res;
705 }
706 
setLanguage(const QString & lang)707 void Cutegram::setLanguage(const QString &lang)
708 {
709     if( p->language == lang )
710         return;
711 
712     QGuiApplication::removeTranslator(p->translator);
713     p->translator->load(p->languages.value(lang).toString(),"languages");
714     QGuiApplication::installTranslator(p->translator);
715     p->language = lang;
716 
717     AsemanApplication::settings()->setValue("General/Language",lang);
718 
719     emit languageChanged();
720     emit languageDirectionChanged();
721 }
722 
language() const723 QString Cutegram::language() const
724 {
725     return p->language;
726 }
727 
closingState() const728 bool Cutegram::closingState() const
729 {
730     return p->closingState;
731 }
732 
setStartupOption(int opt)733 void Cutegram::setStartupOption(int opt)
734 {
735     if(opt == p->startupOption)
736         return;
737 
738     p->startupOption = opt;
739     AsemanApplication::settings()->setValue("General/startupOption", opt);
740     emit startupOptionChanged();
741 }
742 
startupOption() const743 int Cutegram::startupOption() const
744 {
745     return p->startupOption;
746 }
747 
setNotification(bool stt)748 void Cutegram::setNotification(bool stt)
749 {
750     if(p->notification == stt)
751         return;
752 
753     p->notification = stt;
754     AsemanApplication::settings()->setValue("General/notification", stt);
755     emit notificationChanged();
756 }
757 
notification() const758 bool Cutegram::notification() const
759 {
760     return p->notification;
761 }
762 
setEmojiOnHover(bool stt)763 void Cutegram::setEmojiOnHover(bool stt)
764 {
765     if(p->emojiOnHover == stt)
766         return;
767 
768     p->emojiOnHover = stt;
769     AsemanApplication::settings()->setValue("General/emojiOnHover", stt);
770     emit emojiOnHoverChanged();
771 }
772 
emojiOnHover() const773 bool Cutegram::emojiOnHover() const
774 {
775     return p->emojiOnHover;
776 }
777 
setMinimumDialogs(bool stt)778 void Cutegram::setMinimumDialogs(bool stt)
779 {
780     if(p->minimumDialogs == stt)
781         return;
782 
783     p->minimumDialogs = stt;
784     AsemanApplication::settings()->setValue("General/minimumDialogs", stt);
785     emit minimumDialogsChanged();
786 }
787 
minimumDialogs() const788 bool Cutegram::minimumDialogs() const
789 {
790     return p->minimumDialogs;
791 }
792 
setShowLastMessage(bool stt)793 void Cutegram::setShowLastMessage(bool stt)
794 {
795     if(p->showLastMessage == stt)
796         return;
797 
798     p->showLastMessage = stt;
799     AsemanApplication::settings()->setValue("General/showLastMessage", stt);
800     emit showLastMessageChanged();
801 }
802 
showLastMessage() const803 bool Cutegram::showLastMessage() const
804 {
805     return p->showLastMessage;
806 }
807 
setDarkSystemTray(bool stt)808 void Cutegram::setDarkSystemTray(bool stt)
809 {
810     if(p->darkSystemTray == stt)
811         return;
812 
813     p->darkSystemTray = stt;
814     AsemanApplication::settings()->setValue("General/darkSystemTray", stt);
815 
816     setSysTrayCounter(sysTrayCounter(), true);
817     emit darkSystemTrayChanged();
818 }
819 
darkSystemTray() const820 bool Cutegram::darkSystemTray() const
821 {
822     return p->darkSystemTray;
823 }
824 
setSmoothScroll(bool stt)825 void Cutegram::setSmoothScroll(bool stt)
826 {
827     if(p->smoothScroll == stt)
828         return;
829 
830     p->smoothScroll = stt;
831     AsemanApplication::settings()->setValue("General/smoothScroll", stt);
832 
833     emit smoothScrollChanged();
834 }
835 
smoothScroll() const836 bool Cutegram::smoothScroll() const
837 {
838     return p->smoothScroll;
839 }
840 
setKWallet(bool stt)841 void Cutegram::setKWallet(bool stt)
842 {
843     if(p->kWallet == stt)
844         return;
845 
846     p->kWallet = stt;
847     AsemanApplication::settings()->setValue("General/kWallet", stt);
848 
849     emit kWalletChanged();
850 }
851 
kWallet() const852 bool Cutegram::kWallet() const
853 {
854     return p->kWallet;
855 }
856 
setNativeTitleBar(bool stt)857 void Cutegram::setNativeTitleBar(bool stt)
858 {
859     if(p->nativeTitleBar == stt)
860         return;
861 
862     p->nativeTitleBar = stt;
863     AsemanApplication::settings()->setValue("General/nativeTitleBar", stt);
864 
865     emit nativeTitleBarChanged();
866 }
867 
nativeTitleBar() const868 bool Cutegram::nativeTitleBar() const
869 {
870     return p->nativeTitleBar;
871 }
872 
allowNativeTitleBar() const873 bool Cutegram::allowNativeTitleBar() const
874 {
875 #ifdef Q_OS_MACX
876 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
877     switch(static_cast<int>(QSysInfo::macVersion()))
878     {
879     case QSysInfo::MV_YOSEMITE:
880     case QSysInfo::MV_ELCAPITAN:
881         return true;
882         break;
883     }
884 #endif
885 #endif
886     return false;
887 }
888 
setBackground(const QString & background)889 void Cutegram::setBackground(const QString &background)
890 {
891     if(p->background == background)
892         return;
893 
894     p->background = background;
895     AsemanApplication::settings()->setValue("General/background", background);
896     emit backgroundChanged();
897 }
898 
background() const899 QString Cutegram::background() const
900 {
901     return p->background;
902 }
903 
setMessageAudio(const QString & file)904 void Cutegram::setMessageAudio(const QString &file)
905 {
906     if(p->messageAudio == file)
907         return;
908 
909     p->messageAudio = file;
910     AsemanApplication::settings()->setValue("General/messageAudio", file);
911     emit messageAudioChanged();
912 }
913 
messageAudio() const914 QString Cutegram::messageAudio() const
915 {
916     return p->messageAudio;
917 }
918 
setMasterColor(const QString & color)919 void Cutegram::setMasterColor(const QString &color)
920 {
921     if(p->masterColor == color)
922         return;
923 
924     p->masterColor = color;
925     AsemanApplication::settings()->setValue("General/masterColor", color);
926 
927     emit masterColorChanged();
928     emit highlightColorChanged();
929 }
930 
masterColor() const931 QString Cutegram::masterColor() const
932 {
933     return p->masterColor;
934 }
935 
highlightColor() const936 QColor Cutegram::highlightColor() const
937 {
938     return p->masterColor.isEmpty()? p->highlightColor : QColor(p->masterColor);
939 }
940 
setHighlightColor(const QColor & color)941 void Cutegram::setHighlightColor(const QColor &color)
942 {
943     if(p->highlightColor == color)
944         return;
945 
946     p->highlightColor = color;
947     AsemanApplication::settings()->setValue("General/lastHighlightColor", color.name());
948 
949     emit highlightColorChanged();
950 }
951 
setFont(const QFont & font)952 void Cutegram::setFont(const QFont &font)
953 {
954     if(p->font == font)
955         return;
956 
957     p->font = font;
958     AsemanApplication::settings()->setValue("General/font", font);
959     emit fontChanged();
960 }
961 
font() const962 QFont Cutegram::font() const
963 {
964     return p->font;
965 }
966 
setAsemanSubscribe(bool stt)967 void Cutegram::setAsemanSubscribe(bool stt)
968 {
969     if(p->cutegramSubscribe == stt)
970         return;
971 
972     p->cutegramSubscribe = stt;
973     AsemanApplication::settings()->setValue("General/cutegramSubscribe", stt);
974 
975     emit cutegramSubscribeChanged();
976 }
977 
cutegramSubscribe() const978 bool Cutegram::cutegramSubscribe() const
979 {
980     return p->cutegramSubscribe;
981 }
982 
setAutoEmojis(bool stt)983 void Cutegram::setAutoEmojis(bool stt)
984 {
985     if(p->autoEmojis == stt)
986         return;
987 
988     p->autoEmojis = stt;
989     AsemanApplication::settings()->setValue("General/autoEmojis", stt);
990 
991     emit autoEmojisChanged();
992 }
993 
autoEmojis() const994 bool Cutegram::autoEmojis() const
995 {
996     return p->autoEmojis;
997 }
998 
setSendByCtrlEnter(bool stt)999 void Cutegram::setSendByCtrlEnter(bool stt)
1000 {
1001     if(p->sendByCtrlEnter == stt)
1002         return;
1003 
1004     p->sendByCtrlEnter = stt;
1005     AsemanApplication::settings()->setValue("General/sendByCtrlEnter", stt);
1006 
1007     emit sendByCtrlEnterChanged();
1008 }
1009 
sendByCtrlEnter() const1010 bool Cutegram::sendByCtrlEnter() const
1011 {
1012     return p->sendByCtrlEnter;
1013 }
1014 
setStatusIconStyle(int style)1015 void Cutegram::setStatusIconStyle(int style)
1016 {
1017     if(p->statusIconStyle == style)
1018         return;
1019 
1020     p->statusIconStyle = style;
1021     AsemanApplication::settings()->setValue(SYSTRAY_ICON_STATUS, style);
1022 
1023     emit cutegramSubscribeChanged();
1024 
1025     setSysTrayCounter(sysTrayCounter(), true);
1026 }
1027 
statusIconStyle()1028 int Cutegram::statusIconStyle()
1029 {
1030     return p->statusIconStyle;
1031 }
1032 
themes() const1033 QStringList Cutegram::themes() const
1034 {
1035     return p->themes;
1036 }
1037 
setTheme(const QString & theme)1038 void Cutegram::setTheme(const QString &theme)
1039 {
1040     if(p->theme == theme)
1041         return;
1042 
1043     p->theme = theme;
1044     AsemanApplication::settings()->setValue("General/theme",p->theme);
1045     init_theme();
1046 
1047     emit themeChanged();
1048 }
1049 
theme() const1050 QString Cutegram::theme() const
1051 {
1052     return p->theme;
1053 }
1054 
currentTheme()1055 ThemeItem *Cutegram::currentTheme()
1056 {
1057     return p->currentTheme;
1058 }
1059 
emojisThemes() const1060 QStringList Cutegram::emojisThemes() const
1061 {
1062     return p->emojisThemes;
1063 }
1064 
setEmojisTheme(const QString & theme)1065 void Cutegram::setEmojisTheme(const QString& theme)
1066 {
1067     if (p->emojisTheme == theme)
1068         return;
1069 
1070     p->emojisTheme = theme;
1071     AsemanApplication::settings()->setValue("General/emojisTheme", p->emojisTheme);
1072 
1073     emit emojisThemeChanged();
1074 }
1075 
emojisTheme() const1076 QString Cutegram::emojisTheme() const
1077 {
1078     return p->emojisTheme;
1079 }
1080 
searchEngines() const1081 QStringList Cutegram::searchEngines() const
1082 {
1083     return p->searchEngines;
1084 }
1085 
setSearchEngine(const QString & se)1086 void Cutegram::setSearchEngine(const QString &se)
1087 {
1088     if(p->searchEngine == se)
1089         return;
1090 
1091     p->searchEngine = se;
1092     AsemanApplication::settings()->setValue("General/searchEngine",p->searchEngine);
1093     init_theme();
1094 
1095     emit searchEngineChanged();
1096 }
1097 
searchEngine() const1098 QString Cutegram::searchEngine() const
1099 {
1100     return p->searchEngine;
1101 }
1102 
personalStickerDirectory() const1103 QString Cutegram::personalStickerDirectory() const
1104 {
1105     return AsemanApplication::homePath() + "/stickers/Personal";
1106 }
1107 
encrypter()1108 DatabaseAbstractEncryptor *Cutegram::encrypter()
1109 {
1110     return &p->encrypter;
1111 }
1112 
setEncrypterKey(const QString & key)1113 void Cutegram::setEncrypterKey(const QString &key)
1114 {
1115     p->encrypter.setKey(key);
1116 }
1117 
isLoggedIn(const QString & phone) const1118 bool Cutegram::isLoggedIn(const QString &phone) const
1119 {
1120     const QString &home = AsemanApplication::homePath();
1121     const QString &ppath = home + "/" + phone;
1122     return QFile::exists(ppath + "/auth");
1123 }
1124 
normalizeText(const QString & text) const1125 QString Cutegram::normalizeText(const QString &text) const
1126 {
1127     if(text.isEmpty())
1128         return text;
1129 
1130     return text[0].toUpper() + text.mid(1);
1131 }
1132 
init_languages()1133 void Cutegram::init_languages()
1134 {
1135     QDir dir(p->translationsPath);
1136     QStringList languages = dir.entryList( QDir::Files );
1137     if( !languages.contains("lang-en.qm") )
1138         languages.prepend("lang-en.qm");
1139 
1140     for( int i=0 ; i<languages.size() ; i++ )
1141     {
1142         QString locale_str = languages[i];
1143         locale_str.truncate( locale_str.lastIndexOf('.') );
1144         locale_str.remove( 0, locale_str.indexOf('-') + 1 );
1145 
1146         QLocale locale(locale_str);
1147 
1148         QString  lang = QString("%1 (%2)").arg(QLocale::languageToString(locale.language()), QLocale::countryToString(locale.country()));
1149         QVariant data = p->translationsPath + "/" + languages[i];
1150 
1151         p->languages.insert( lang, data );
1152         p->locales.insert( lang , locale );
1153 
1154         if( lang == AsemanApplication::settings()->value("General/Language","English (UnitedStates)").toString() )
1155             setLanguage( lang );
1156     }
1157 }
1158 
init_theme()1159 void Cutegram::init_theme()
1160 {
1161     if(p->currentThemeComponent)
1162         p->currentThemeComponent->deleteLater();
1163     if(p->currentTheme)
1164         p->currentTheme->deleteLater();
1165 
1166     p->currentThemeComponent = new QQmlComponent(p->viewer->engine(), p->themesPath + "/" + p->theme);
1167     p->currentTheme = static_cast<ThemeItem*>(p->currentThemeComponent->create());
1168     if(!p->currentTheme)
1169         qDebug() << p->currentThemeComponent->errorString();
1170 
1171     emit currentThemeChanged();
1172 }
1173 
lowLevelDarkSystemTray()1174 bool Cutegram::lowLevelDarkSystemTray()
1175 {
1176     switch(p->statusIconStyle)
1177     {
1178     case StatusIconAutomatic:
1179         return darkSystemTray();
1180         break;
1181 
1182     case StatusIconDark:
1183         return true;
1184         break;
1185 
1186     case StatusIconLight:
1187         return false;
1188         break;
1189     }
1190 
1191     return darkSystemTray();
1192 }
1193 
intListToVariantList(const QList<qint32> & list)1194 QVariantList Cutegram::intListToVariantList(const QList<qint32> &list)
1195 {
1196     QVariantList res;
1197     foreach(const qint32 u, list)
1198         res << u;
1199 
1200     return res;
1201 }
1202 
variantListToIntList(const QVariantList & list)1203 QList<qint32> Cutegram::variantListToIntList(const QVariantList &list)
1204 {
1205     QList<qint32> res;
1206     foreach(const QVariant &l, list)
1207         res << l.toInt();
1208 
1209     return res;
1210 }
1211 
~Cutegram()1212 Cutegram::~Cutegram()
1213 {
1214     delete p;
1215 }
1216