aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLibravatar keshavbhatt <keshavnrj@gmail.com>2021-04-05 02:11:25 +0530
committerLibravatar keshavbhatt <keshavnrj@gmail.com>2021-04-05 02:11:25 +0530
commit9ea334d08f42f3c362e86499dc3c0ed658bb428c (patch)
tree85eb15c519bcff8a5e3c4f8406f068b7c412b789 /src
parente79b447b31ad9ab1ed42fd232f8789fad38d780b (diff)
downloadwhatsie-9ea334d08f42f3c362e86499dc3c0ed658bb428c.tar.gz
whatsie-9ea334d08f42f3c362e86499dc3c0ed658bb428c.zip
src init
Diffstat (limited to 'src')
-rw-r--r--src/WhatsApp.pro87
-rw-r--r--src/certificateerrordialog.ui133
-rw-r--r--src/common.h7
-rw-r--r--src/downloadmanagerwidget.cpp44
-rw-r--r--src/downloadmanagerwidget.h83
-rw-r--r--src/downloadmanagerwidget.ui123
-rw-r--r--src/downloadwidget.cpp108
-rw-r--r--src/downloadwidget.h88
-rw-r--r--src/downloadwidget.ui80
-rw-r--r--src/elidedlabel.cpp49
-rw-r--r--src/elidedlabel.h49
-rw-r--r--src/icons.qrc147
-rw-r--r--src/icons/account-pin-circle-line.pngbin0 -> 3204 bytes
-rw-r--r--src/icons/anticlockwise-fill.pngbin0 -> 769 bytes
-rw-r--r--src/icons/anticlockwise-line.pngbin0 -> 842 bytes
-rw-r--r--src/icons/app/icon-128.pngbin0 -> 6064 bytes
-rw-r--r--src/icons/app/icon-16.pngbin0 -> 1286 bytes
-rw-r--r--src/icons/app/icon-256.pngbin0 -> 12301 bytes
-rw-r--r--src/icons/app/icon-32.pngbin0 -> 1777 bytes
-rw-r--r--src/icons/app/icon-512.pngbin0 -> 22868 bytes
-rw-r--r--src/icons/app/icon-64.pngbin0 -> 3308 bytes
-rwxr-xr-xsrc/icons/app/resize.sh155
-rw-r--r--src/icons/app/whatsapp-message.svg94
-rw-r--r--src/icons/app/whatsapp.svg81
-rw-r--r--src/icons/archive-drawer-line.pngbin0 -> 589 bytes
-rw-r--r--src/icons/arrow-down-s-line.pngbin0 -> 647 bytes
-rw-r--r--src/icons/arrow-go-back-line.pngbin0 -> 996 bytes
-rw-r--r--src/icons/arrow-go-forward-line.pngbin0 -> 1040 bytes
-rw-r--r--src/icons/arrow-left-circle-line.pngbin0 -> 2101 bytes
-rw-r--r--src/icons/arrow-left-line.pngbin0 -> 578 bytes
-rw-r--r--src/icons/arrow-left-s-line.pngbin0 -> 513 bytes
-rw-r--r--src/icons/arrow-right-circle-line.pngbin0 -> 2119 bytes
-rw-r--r--src/icons/arrow-right-line.pngbin0 -> 538 bytes
-rw-r--r--src/icons/arrow-right-s-line.pngbin0 -> 500 bytes
-rw-r--r--src/icons/arrow-up-s-line.pngbin0 -> 610 bytes
-rw-r--r--src/icons/blur-off-line.pngbin0 -> 1348 bytes
-rw-r--r--src/icons/camera-off-line.pngbin0 -> 1786 bytes
-rw-r--r--src/icons/categories/art-and-design.pngbin0 -> 1413 bytes
-rw-r--r--src/icons/categories/books-and-reference.pngbin0 -> 680 bytes
-rw-r--r--src/icons/categories/development.pngbin0 -> 1383 bytes
-rw-r--r--src/icons/categories/devices-and-iot.pngbin0 -> 790 bytes
-rw-r--r--src/icons/categories/education.pngbin0 -> 502 bytes
-rw-r--r--src/icons/categories/entertainment.pngbin0 -> 1696 bytes
-rw-r--r--src/icons/categories/featured.pngbin0 -> 1565 bytes
-rw-r--r--src/icons/categories/finance.pngbin0 -> 893 bytes
-rw-r--r--src/icons/categories/games.pngbin0 -> 1085 bytes
-rw-r--r--src/icons/categories/health-and-fitness.pngbin0 -> 584 bytes
-rw-r--r--src/icons/categories/music-and-audio.pngbin0 -> 1237 bytes
-rw-r--r--src/icons/categories/news-and-weather.pngbin0 -> 761 bytes
-rw-r--r--src/icons/categories/personalisation.pngbin0 -> 819 bytes
-rw-r--r--src/icons/categories/photo-and-video.pngbin0 -> 856 bytes
-rw-r--r--src/icons/categories/productivity.pngbin0 -> 1579 bytes
-rw-r--r--src/icons/categories/science.pngbin0 -> 1489 bytes
-rw-r--r--src/icons/categories/security.pngbin0 -> 906 bytes
-rw-r--r--src/icons/categories/server-and-cloud.pngbin0 -> 498 bytes
-rw-r--r--src/icons/categories/social.pngbin0 -> 1364 bytes
-rw-r--r--src/icons/categories/utilities.pngbin0 -> 684 bytes
-rw-r--r--src/icons/clipboard-line.pngbin0 -> 590 bytes
-rw-r--r--src/icons/clockwise-fill.pngbin0 -> 768 bytes
-rw-r--r--src/icons/clockwise-line.pngbin0 -> 848 bytes
-rw-r--r--src/icons/close-fill.pngbin0 -> 732 bytes
-rw-r--r--src/icons/compare-image-line.pngbin0 -> 1821 bytes
-rw-r--r--src/icons/crop-line.pngbin0 -> 528 bytes
-rw-r--r--src/icons/delete-bin-3-line.pngbin0 -> 645 bytes
-rw-r--r--src/icons/delete-bin-5-line.pngbin0 -> 679 bytes
-rw-r--r--src/icons/disabled-arrow-left-circle-line.pngbin0 -> 2156 bytes
-rw-r--r--src/icons/disabled-arrow-right-circle-line.pngbin0 -> 2150 bytes
-rw-r--r--src/icons/download-line.pngbin0 -> 945 bytes
-rw-r--r--src/icons/drag-move-fill.pngbin0 -> 1064 bytes
-rw-r--r--src/icons/error-warning-line.pngbin0 -> 2384 bytes
-rw-r--r--src/icons/eye-line.pngbin0 -> 2650 bytes
-rw-r--r--src/icons/facebook-box-line.pngbin0 -> 770 bytes
-rw-r--r--src/icons/facebook-line.pngbin0 -> 583 bytes
-rw-r--r--src/icons/file-copy-line.pngbin0 -> 732 bytes
-rw-r--r--src/icons/file-text-line.pngbin0 -> 649 bytes
-rw-r--r--src/icons/file-unknow-line.pngbin0 -> 850 bytes
-rw-r--r--src/icons/filter-2-line.pngbin0 -> 965 bytes
-rw-r--r--src/icons/focus-3-line.pngbin0 -> 2186 bytes
-rw-r--r--src/icons/folder-download-line.pngbin0 -> 953 bytes
-rw-r--r--src/icons/folder-open-line.pngbin0 -> 1009 bytes
-rw-r--r--src/icons/fullscreen-line.pngbin0 -> 425 bytes
-rw-r--r--src/icons/funds-line.pngbin0 -> 1629 bytes
-rw-r--r--src/icons/fx-line.pngbin0 -> 1174 bytes
-rw-r--r--src/icons/grid-line.pngbin0 -> 515 bytes
-rw-r--r--src/icons/home-8-line.pngbin0 -> 911 bytes
-rw-r--r--src/icons/icon-512.xcfbin0 -> 410837 bytes
-rw-r--r--src/icons/image-2-line.pngbin0 -> 1234 bytes
-rw-r--r--src/icons/information-fill.pngbin0 -> 1557 bytes
-rw-r--r--src/icons/information-line.pngbin0 -> 2396 bytes
-rw-r--r--src/icons/link-unlink-m.pngbin0 -> 2829 bytes
-rw-r--r--src/icons/links-line.pngbin0 -> 2656 bytes
-rw-r--r--src/icons/loader-2-fill.pngbin0 -> 1343 bytes
-rw-r--r--src/icons/lock-2-fill.pngbin0 -> 1074 bytes
-rw-r--r--src/icons/lock-line.pngbin0 -> 960 bytes
-rw-r--r--src/icons/lock-unlock-line.pngbin0 -> 885 bytes
-rw-r--r--src/icons/magic-line.pngbin0 -> 2259 bytes
-rw-r--r--src/icons/mail-add-line.pngbin0 -> 1321 bytes
-rw-r--r--src/icons/mail-line.pngbin0 -> 892 bytes
-rw-r--r--src/icons/map-pin-line.pngbin0 -> 2555 bytes
-rw-r--r--src/icons/movie-2-line.pngbin0 -> 2821 bytes
-rw-r--r--src/icons/music-2-line.pngbin0 -> 1534 bytes
-rw-r--r--src/icons/others/Histogram.pngbin0 -> 291 bytes
-rw-r--r--src/icons/others/Omonitor.pngbin0 -> 13176 bytes
-rw-r--r--src/icons/others/greendot.pngbin0 -> 7109 bytes
-rw-r--r--src/icons/others/monitor.pngbin0 -> 3922 bytes
-rw-r--r--src/icons/others/snapcraft.pngbin0 -> 1602 bytes
-rw-r--r--src/icons/others/snapcraft.svg59
-rw-r--r--src/icons/others/tt_tbc_head.pngbin0 -> 83727 bytes
-rw-r--r--src/icons/others/wall_placeholder_180.jpgbin0 -> 5155 bytes
-rw-r--r--src/icons/pause-line.pngbin0 -> 325 bytes
-rw-r--r--src/icons/paypal-line.pngbin0 -> 2582 bytes
-rw-r--r--src/icons/picture-in-picture-line.pngbin0 -> 651 bytes
-rw-r--r--src/icons/play-fill.pngbin0 -> 415 bytes
-rw-r--r--src/icons/play-line.pngbin0 -> 828 bytes
-rw-r--r--src/icons/printer-line.pngbin0 -> 754 bytes
-rw-r--r--src/icons/questionnaire-line.pngbin0 -> 1145 bytes
-rw-r--r--src/icons/record-circle-line.pngbin0 -> 2603 bytes
-rw-r--r--src/icons/refresh-line.pngbin0 -> 2000 bytes
-rw-r--r--src/icons/repeat-2-fill.pngbin0 -> 823 bytes
-rw-r--r--src/icons/save-line.pngbin0 -> 502 bytes
-rw-r--r--src/icons/scissors-cut-line.pngbin0 -> 1715 bytes
-rw-r--r--src/icons/screenshot-2-line.pngbin0 -> 1185 bytes
-rw-r--r--src/icons/search-2-line.pngbin0 -> 2339 bytes
-rw-r--r--src/icons/setting-line.pngbin0 -> 1494 bytes
-rw-r--r--src/icons/shape-2-line.pngbin0 -> 510 bytes
-rw-r--r--src/icons/shape-line.pngbin0 -> 1226 bytes
-rw-r--r--src/icons/share-line.pngbin0 -> 2102 bytes
-rw-r--r--src/icons/shopping-cart-line.pngbin0 -> 1183 bytes
-rw-r--r--src/icons/sip-line.pngbin0 -> 1171 bytes
-rw-r--r--src/icons/split-cells-horizontal.pngbin0 -> 626 bytes
-rw-r--r--src/icons/split-cells-vertical.pngbin0 -> 610 bytes
-rw-r--r--src/icons/star-line.pngbin0 -> 2554 bytes
-rw-r--r--src/icons/stop-line.pngbin0 -> 471 bytes
-rw-r--r--src/icons/swap-box-line.pngbin0 -> 929 bytes
-rw-r--r--src/icons/terminal-box-line.pngbin0 -> 665 bytes
-rw-r--r--src/icons/texture.pngbin0 -> 2417 bytes
-rw-r--r--src/icons/tiktok-downloader.pngbin0 -> 76834 bytes
-rw-r--r--src/icons/time-line.pngbin0 -> 2398 bytes
-rw-r--r--src/icons/translate-2.pngbin0 -> 2002 bytes
-rw-r--r--src/icons/twitter-line.pngbin0 -> 1518 bytes
-rw-r--r--src/icons/volume-mute-line.pngbin0 -> 1249 bytes
-rw-r--r--src/icons/volume-up-line.pngbin0 -> 2042 bytes
-rw-r--r--src/icons/white/stop-line.pngbin0 -> 437 bytes
-rw-r--r--src/icons/white/white_arrow-left-line.pngbin0 -> 538 bytes
-rw-r--r--src/icons/white/white_arrow-right-line.pngbin0 -> 504 bytes
-rw-r--r--src/icons/white/white_picture-in-picture-line.pngbin0 -> 560 bytes
-rw-r--r--src/icons/white/white_play-fill.pngbin0 -> 415 bytes
-rw-r--r--src/icons/white/white_record-circle-line.pngbin0 -> 2055 bytes
-rw-r--r--src/icons/white/white_scissors-cut-line.pngbin0 -> 1715 bytes
-rw-r--r--src/icons/white/white_shape-2-line.pngbin0 -> 468 bytes
-rw-r--r--src/icons/white/white_sip-line.pngbin0 -> 999 bytes
-rw-r--r--src/icons/white/white_terminal-box-line.pngbin0 -> 601 bytes
-rw-r--r--src/icons/youtube-line.pngbin0 -> 1901 bytes
-rw-r--r--src/icons/zoom-in-fill.pngbin0 -> 1077 bytes
-rw-r--r--src/icons/zoom-in-line.pngbin0 -> 1453 bytes
-rw-r--r--src/icons/zoom-out-fill.pngbin0 -> 1005 bytes
-rw-r--r--src/icons/zoom-out-line.pngbin0 -> 1421 bytes
-rw-r--r--src/main.cpp45
-rw-r--r--src/mainwindow.cpp551
-rw-r--r--src/mainwindow.h107
-rw-r--r--src/notificationpopup.h144
-rw-r--r--src/passworddialog.ui121
-rw-r--r--src/requestinterceptor.h41
-rw-r--r--src/settingswidget.cpp189
-rw-r--r--src/settingswidget.h66
-rw-r--r--src/settingswidget.ui354
-rw-r--r--src/utils.cpp162
-rw-r--r--src/utils.h85
-rw-r--r--src/webenginepage.cpp252
-rw-r--r--src/webenginepage.h49
-rw-r--r--src/widgets/scrolltext/scrolltext.cpp179
-rw-r--r--src/widgets/scrolltext/scrolltext.h53
172 files changed, 3785 insertions, 0 deletions
diff --git a/src/WhatsApp.pro b/src/WhatsApp.pro
new file mode 100644
index 0000000..95e6b33
--- /dev/null
+++ b/src/WhatsApp.pro
@@ -0,0 +1,87 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2020-03-26T13:53:21
+#
+#-------------------------------------------------
+
+QT += core widgets webengine webenginewidgets
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+
+TARGET = whatsie
+TEMPLATE = app
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Refer to the documentation for the
+# deprecated API to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# No debug output in release mode
+CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+CONFIG += c++11
+
+# Set program version
+VERSION = 1.0
+DEFINES += VERSIONSTR=\\\"$${VERSION}\\\"
+
+SOURCES += \
+ downloadmanagerwidget.cpp \
+ downloadwidget.cpp \
+ elidedlabel.cpp \
+ main.cpp \
+ mainwindow.cpp \
+ settingswidget.cpp \
+ utils.cpp \
+ webenginepage.cpp \
+ widgets/scrolltext/scrolltext.cpp
+
+RESOURCES += \
+ icons.qrc
+
+HEADERS += \
+ common.h \
+ downloadmanagerwidget.h \
+ downloadwidget.h \
+ elidedlabel.h \
+ mainwindow.h \
+ notificationpopup.h \
+ requestinterceptor.h \
+ settingswidget.h \
+ utils.h \
+ webenginepage.h \
+ widgets/scrolltext/scrolltext.h
+
+
+# Default rules for deployment.
+isEmpty(PREFIX){
+ PREFIX = /usr
+}
+
+BINDIR = $$PREFIX/bin
+DATADIR = $$PREFIX/share
+
+target.path = $$BINDIR
+
+icon.files = icons/linguist.png
+icon.path = $$DATADIR/icons/hicolor/512x512/apps/
+
+desktop.files = linguist.desktop
+desktop.path = $$DATADIR/applications/
+
+INSTALLS += target icon desktop
+
+FORMS += \
+ certificateerrordialog.ui \
+ downloadmanagerwidget.ui \
+ downloadwidget.ui \
+ passworddialog.ui \
+ settingswidget.ui
+
diff --git a/src/certificateerrordialog.ui b/src/certificateerrordialog.ui
new file mode 100644
index 0000000..a14a784
--- /dev/null
+++ b/src/certificateerrordialog.ui
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CertificateErrorDialog</class>
+ <widget class="QDialog" name="CertificateErrorDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>370</width>
+ <height>141</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>20</number>
+ </property>
+ <property name="rightMargin">
+ <number>20</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="m_iconLabel">
+ <property name="text">
+ <string>Icon</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_errorLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Error</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_infoLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>If you wish so, you may continue with an unverified certificate. Accepting an unverified certificate mean you may not be connected with the host you tried to connect to.
+
+Do you wish to override the security check and continue ? </string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::No|QDialogButtonBox::Yes</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>CertificateErrorDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>CertificateErrorDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..261ee64
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,7 @@
+#ifndef COMMON_H
+#define COMMON_H
+#include <QString>
+
+QString defaultUserAgentStr = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.4389.114 Safari/537.36";
+
+#endif // COMMON_H
diff --git a/src/downloadmanagerwidget.cpp b/src/downloadmanagerwidget.cpp
new file mode 100644
index 0000000..1a0fa27
--- /dev/null
+++ b/src/downloadmanagerwidget.cpp
@@ -0,0 +1,44 @@
+#include "downloadmanagerwidget.h"
+
+#include "downloadwidget.h"
+
+#include <QFileDialog>
+#include <QWebEngineDownloadItem>
+
+DownloadManagerWidget::DownloadManagerWidget(QWidget *parent)
+ : QWidget(parent)
+ , m_numDownloads(0)
+{
+ setupUi(this);
+}
+
+void DownloadManagerWidget::downloadRequested(QWebEngineDownloadItem *download)
+{
+ //Q_ASSERT(download && download->state() == QWebEngineDownloadItem::DownloadRequested);
+
+ QString path = QFileDialog::getSaveFileName(this, tr("Save as"), download->path(),tr("Any file (*)"),nullptr,QFileDialog::DontUseNativeDialog);
+ if (path.isEmpty())
+ return;
+
+ download->setPath(path);
+ download->accept();
+ add(new DownloadWidget(download));
+
+ show();
+}
+
+void DownloadManagerWidget::add(DownloadWidget *downloadWidget)
+{
+ connect(downloadWidget, &DownloadWidget::removeClicked, this, &DownloadManagerWidget::remove);
+ m_itemsLayout->insertWidget(0, downloadWidget, 0, Qt::AlignTop);
+ if (m_numDownloads++ == 0)
+ m_zeroItemsLabel->hide();
+}
+
+void DownloadManagerWidget::remove(DownloadWidget *downloadWidget)
+{
+ m_itemsLayout->removeWidget(downloadWidget);
+ downloadWidget->deleteLater();
+ if (--m_numDownloads == 0)
+ m_zeroItemsLabel->show();
+}
diff --git a/src/downloadmanagerwidget.h b/src/downloadmanagerwidget.h
new file mode 100644
index 0000000..cca092b
--- /dev/null
+++ b/src/downloadmanagerwidget.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DOWNLOADMANAGERWIDGET_H
+#define DOWNLOADMANAGERWIDGET_H
+
+#include "ui_downloadmanagerwidget.h"
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QWebEngineDownloadItem;
+QT_END_NAMESPACE
+
+class DownloadWidget;
+
+// Displays a list of downloads.
+class DownloadManagerWidget final : public QWidget, public Ui::DownloadManagerWidget
+{
+ Q_OBJECT
+public:
+ explicit DownloadManagerWidget(QWidget *parent = nullptr);
+
+ // Prompts user with a "Save As" dialog. If the user doesn't cancel it, then
+ // the QWebEngineDownloadItem will be accepted and the DownloadManagerWidget
+ // will be shown on the screen.
+ void downloadRequested(QWebEngineDownloadItem *webItem);
+
+private:
+ void add(DownloadWidget *downloadWidget);
+ void remove(DownloadWidget *downloadWidget);
+
+ int m_numDownloads;
+};
+
+#endif // DOWNLOADMANAGERWIDGET_H
diff --git a/src/downloadmanagerwidget.ui b/src/downloadmanagerwidget.ui
new file mode 100644
index 0000000..8b815d0
--- /dev/null
+++ b/src/downloadmanagerwidget.ui
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DownloadManagerWidget</class>
+ <widget class="QWidget" name="DownloadManagerWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>452</width>
+ <height>250</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>400</width>
+ <height>250</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Downloads</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="m_topLevelLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetNoConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QScrollArea" name="m_scrollArea">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAsNeeded</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="m_items">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>450</width>
+ <height>248</height>
+ </rect>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="m_itemsLayout">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="m_zeroItemsLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">color: palette(shadow)</string>
+ </property>
+ <property name="text">
+ <string>No downloads</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/downloadwidget.cpp b/src/downloadwidget.cpp
new file mode 100644
index 0000000..005ea1e
--- /dev/null
+++ b/src/downloadwidget.cpp
@@ -0,0 +1,108 @@
+#include "downloadwidget.h"
+
+#include <QFileInfo>
+#include <QUrl>
+#include <QWebEngineDownloadItem>
+
+DownloadWidget::DownloadWidget(QWebEngineDownloadItem *download, QWidget *parent)
+ : QFrame(parent)
+ , m_download(download)
+ , m_timeAdded(QTime::currentTime())
+{
+ setupUi(this);
+ m_dstName->setText(QFileInfo(m_download->path()).fileName());
+ m_srcUrl->setText(m_download->url().toDisplayString());
+
+ connect(m_cancelButton, &QPushButton::clicked,
+ [this](bool) {
+ if (m_download->state() == QWebEngineDownloadItem::DownloadInProgress)
+ m_download->cancel();
+ else
+ emit removeClicked(this);
+ });
+
+ connect(m_download, &QWebEngineDownloadItem::downloadProgress,
+ this, &DownloadWidget::updateWidget);
+
+ connect(m_download, &QWebEngineDownloadItem::stateChanged,
+ this, &DownloadWidget::updateWidget);
+
+ updateWidget();
+}
+
+inline QString DownloadWidget::withUnit(qreal bytes)
+{
+ if (bytes < (1 << 10))
+ return tr("%L1 B").arg(bytes);
+ else if (bytes < (1 << 20))
+ return tr("%L1 KiB").arg(bytes / (1 << 10), 0, 'f', 2);
+ else if (bytes < (1 << 30))
+ return tr("%L1 MiB").arg(bytes / (1 << 20), 0, 'f', 2);
+ else
+ return tr("%L1 GiB").arg(bytes / (1 << 30), 0, 'f', 2);
+}
+
+void DownloadWidget::updateWidget()
+{
+ qreal totalBytes = m_download->totalBytes();
+ qreal receivedBytes = m_download->receivedBytes();
+ qreal bytesPerSecond = receivedBytes / m_timeAdded.elapsed() * 1000;
+
+ auto state = m_download->state();
+ switch (state) {
+ case QWebEngineDownloadItem::DownloadRequested:
+ Q_UNREACHABLE();
+ break;
+ case QWebEngineDownloadItem::DownloadInProgress:
+ if (totalBytes >= 0) {
+ m_progressBar->setValue(qRound(100 * receivedBytes / totalBytes));
+ m_progressBar->setDisabled(false);
+ m_progressBar->setFormat(
+ tr("%p% - %1 of %2 downloaded - %3/s")
+ .arg(withUnit(receivedBytes))
+ .arg(withUnit(totalBytes))
+ .arg(withUnit(bytesPerSecond)));
+ } else {
+ m_progressBar->setValue(0);
+ m_progressBar->setDisabled(false);
+ m_progressBar->setFormat(
+ tr("unknown size - %1 downloaded - %2/s")
+ .arg(withUnit(receivedBytes))
+ .arg(withUnit(bytesPerSecond)));
+ }
+ break;
+ case QWebEngineDownloadItem::DownloadCompleted:
+ m_progressBar->setValue(100);
+ m_progressBar->setDisabled(true);
+ m_progressBar->setFormat(
+ tr("completed - %1 downloaded - %2/s")
+ .arg(withUnit(receivedBytes))
+ .arg(withUnit(bytesPerSecond)));
+ break;
+ case QWebEngineDownloadItem::DownloadCancelled:
+ m_progressBar->setValue(0);
+ m_progressBar->setDisabled(true);
+ m_progressBar->setFormat(
+ tr("cancelled - %1 downloaded - %2/s")
+ .arg(withUnit(receivedBytes))
+ .arg(withUnit(bytesPerSecond)));
+ break;
+ case QWebEngineDownloadItem::DownloadInterrupted:
+ m_progressBar->setValue(0);
+ m_progressBar->setDisabled(true);
+ m_progressBar->setFormat(
+ tr("interrupted: %1")
+ .arg(m_download->interruptReasonString()));
+ break;
+ }
+
+ if (state == QWebEngineDownloadItem::DownloadInProgress) {
+ static QIcon cancelIcon(QStringLiteral(":/icons/stop-line.png"));
+ m_cancelButton->setIcon(cancelIcon);
+ m_cancelButton->setToolTip(tr("Stop downloading"));
+ } else {
+ static QIcon removeIcon(QStringLiteral(":/icons/close-fill.png"));
+ m_cancelButton->setIcon(removeIcon);
+ m_cancelButton->setToolTip(tr("Remove from list"));
+ }
+}
diff --git a/src/downloadwidget.h b/src/downloadwidget.h
new file mode 100644
index 0000000..521d996
--- /dev/null
+++ b/src/downloadwidget.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DOWNLOADWIDGET_H
+#define DOWNLOADWIDGET_H
+
+#include "ui_downloadwidget.h"
+#include "elidedlabel.h"
+
+
+#include <QFrame>
+#include <QTime>
+
+QT_BEGIN_NAMESPACE
+class QWebEngineDownloadItem;
+QT_END_NAMESPACE
+
+// Displays one ongoing or finished download (QWebEngineDownloadItem).
+class DownloadWidget final : public QFrame, public Ui::DownloadWidget
+{
+ Q_OBJECT
+public:
+ // Precondition: The QWebEngineDownloadItem has been accepted.
+ explicit DownloadWidget(QWebEngineDownloadItem *download, QWidget *parent = nullptr);
+
+signals:
+ // This signal is emitted when the user indicates that they want to remove
+ // this download from the downloads list.
+ void removeClicked(DownloadWidget *self);
+
+private slots:
+ void updateWidget();
+
+private:
+ QString withUnit(qreal bytes);
+
+ QWebEngineDownloadItem *m_download;
+ QTime m_timeAdded;
+};
+
+#endif // DOWNLOADWIDGET_H
diff --git a/src/downloadwidget.ui b/src/downloadwidget.ui
new file mode 100644
index 0000000..d405b8f
--- /dev/null
+++ b/src/downloadwidget.ui
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DownloadWidget</class>
+ <widget class="QFrame" name="DownloadWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>526</width>
+ <height>97</height>
+ </rect>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QGridLayout" name="m_topLevelLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinAndMaxSize</enum>
+ </property>
+ <item row="1" column="0" colspan="2">
+ <widget class="ElidedLabel" name="m_srcUrl">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="m_cancelButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="ElidedLabel" name="m_dstName">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QProgressBar" name="m_progressBar">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="value">
+ <number>24</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ElidedLabel</class>
+ <extends>QLabel</extends>
+ <header>elidedlabel.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/elidedlabel.cpp b/src/elidedlabel.cpp
new file mode 100644
index 0000000..6c14af4
--- /dev/null
+++ b/src/elidedlabel.cpp
@@ -0,0 +1,49 @@
+#include "elidedlabel.h"
+
+#include <QDebug>
+#include <QPainter>
+#include <QResizeEvent>
+#include <QStyle>
+
+ElidedLabel::ElidedLabel(QWidget* parent, Qt::WindowFlags f)
+ : QLabel(parent, f), m_elide_mode(Qt::ElideRight) {
+ setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
+}
+
+ElidedLabel::ElidedLabel(const QString& txt, QWidget* parent, Qt::WindowFlags f)
+ : QLabel(txt, parent, f), m_elide_mode(Qt::ElideRight) {
+ setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
+}
+
+ElidedLabel::ElidedLabel(const QString& txt, Qt::TextElideMode elideMode, QWidget* parent, Qt::WindowFlags f)
+ : QLabel(txt, parent, f), m_elide_mode(elideMode) {
+ setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
+
+}
+
+void ElidedLabel::setText(const QString& txt) {
+ QLabel::setText(txt);
+ cacheElidedText(geometry().width());
+ setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
+}
+
+
+void ElidedLabel::cacheElidedText(int w) {
+ m_cached_elided_text = fontMetrics().elidedText(text(), m_elide_mode, w, (buddy() == nullptr)? 0 : Qt::TextShowMnemonic);
+}
+
+void ElidedLabel::resizeEvent(QResizeEvent* e) {
+ QLabel::resizeEvent(e);
+ cacheElidedText(e->size().width());
+}
+
+void ElidedLabel::paintEvent(QPaintEvent* e) {
+ if(m_elide_mode == Qt::ElideNone) {
+ QLabel::paintEvent(e);
+ } else {
+ QPainter p(this);
+ p.drawText(0, 0, geometry().width(), geometry().height(),
+ QStyle::visualAlignment(text().isRightToLeft()? Qt::RightToLeft : Qt::LeftToRight, alignment()) | ((buddy() == nullptr)? 0 : Qt::TextShowMnemonic),
+ m_cached_elided_text);
+ }
+}
diff --git a/src/elidedlabel.h b/src/elidedlabel.h
new file mode 100644
index 0000000..1ce5210
--- /dev/null
+++ b/src/elidedlabel.h
@@ -0,0 +1,49 @@
+#pragma once
+#include <QLabel>
+#include <QPainter>
+#include <QResizeEvent>
+#include <QString>
+#include <QFont>
+
+// A label that elides its text when not enough geometry is available to show all of the text.
+// Currently only capable of one-line.
+class ElidedLabel : public QLabel {
+ Q_OBJECT
+
+private:
+ Qt::TextElideMode m_elide_mode;
+ QString m_cached_elided_text;
+
+
+public:
+ ElidedLabel(QWidget* parent = NULL, Qt::WindowFlags f = 0);
+ ElidedLabel(const QString& txt, QWidget* parent = NULL, Qt::WindowFlags f = 0);
+ ElidedLabel(const QString& txt, Qt::TextElideMode elideMode = Qt::ElideRight, QWidget* parent = NULL, Qt::WindowFlags f = 0);
+
+public:
+ // Set the elide mode used for displaying text.
+ inline void setElideMode(Qt::TextElideMode elideMode) {
+ m_elide_mode = elideMode;
+ updateGeometry();
+ }
+
+ // Get the elide mode currently used to display text.
+ inline Qt::TextElideMode elideMode() const {
+ return m_elide_mode;
+ }
+
+
+
+public: // QLabel overrides
+ void setText(const QString&); // note: not virtual so no polymorphism ...
+
+
+protected: // QLabel overrides
+ virtual void paintEvent(QPaintEvent*) override;
+ virtual void resizeEvent(QResizeEvent*) override;
+
+protected:
+ // Cache the elided text so as to not recompute it every paint event
+ void cacheElidedText(int w);
+
+};
diff --git a/src/icons.qrc b/src/icons.qrc
new file mode 100644
index 0000000..5a346e3
--- /dev/null
+++ b/src/icons.qrc
@@ -0,0 +1,147 @@
+<RCC>
+ <qresource prefix="/">
+ <file>icons/texture.png</file>
+ <file>icons/arrow-left-circle-line.png</file>
+ <file>icons/refresh-line.png</file>
+ <file>icons/arrow-right-circle-line.png</file>
+ <file>icons/disabled-arrow-left-circle-line.png</file>
+ <file>icons/disabled-arrow-right-circle-line.png</file>
+ <file>icons/download-line.png</file>
+ <file>icons/image-2-line.png</file>
+ <file>icons/setting-line.png</file>
+ <file>icons/map-pin-line.png</file>
+ <file>icons/folder-open-line.png</file>
+ <file>icons/paypal-line.png</file>
+ <file>icons/links-line.png</file>
+ <file>icons/star-line.png</file>
+ <file>icons/folder-download-line.png</file>
+ <file>icons/lock-2-fill.png</file>
+ <file>icons/questionnaire-line.png</file>
+ <file>icons/volume-up-line.png</file>
+ <file>icons/swap-box-line.png</file>
+ <file>icons/clipboard-line.png</file>
+ <file>icons/close-fill.png</file>
+ <file>icons/archive-drawer-line.png</file>
+ <file>icons/file-copy-line.png</file>
+ <file>icons/translate-2.png</file>
+ <file>icons/magic-line.png</file>
+ <file>icons/twitter-line.png</file>
+ <file>icons/mail-add-line.png</file>
+ <file>icons/file-text-line.png</file>
+ <file>icons/facebook-box-line.png</file>
+ <file>icons/stop-line.png</file>
+ <file>icons/loader-2-fill.png</file>
+ <file>icons/share-line.png</file>
+ <file>icons/error-warning-line.png</file>
+ <file>icons/play-line.png</file>
+ <file>icons/time-line.png</file>
+ <file>icons/pause-line.png</file>
+ <file>icons/focus-3-line.png</file>
+ <file>icons/arrow-right-s-line.png</file>
+ <file>icons/arrow-left-s-line.png</file>
+ <file>icons/drag-move-fill.png</file>
+ <file>icons/volume-mute-line.png</file>
+ <file>icons/scissors-cut-line.png</file>
+ <file>icons/play-fill.png</file>
+ <file>icons/screenshot-2-line.png</file>
+ <file>icons/search-2-line.png</file>
+ <file>icons/youtube-line.png</file>
+ <file>icons/terminal-box-line.png</file>
+ <file>icons/sip-line.png</file>
+ <file>icons/shape-2-line.png</file>
+ <file>icons/record-circle-line.png</file>
+ <file>icons/repeat-2-fill.png</file>
+ <file>icons/camera-off-line.png</file>
+ <file>icons/link-unlink-m.png</file>
+ <file>icons/eye-line.png</file>
+ <file>icons/information-fill.png</file>
+ <file>icons/information-line.png</file>
+ <file>icons/delete-bin-5-line.png</file>
+ <file>icons/delete-bin-3-line.png</file>
+ <file>icons/movie-2-line.png</file>
+ <file>icons/music-2-line.png</file>
+ <file>icons/file-unknow-line.png</file>
+ <file>icons/arrow-left-line.png</file>
+ <file>icons/arrow-right-line.png</file>
+ <file>icons/filter-2-line.png</file>
+ <file>icons/arrow-up-s-line.png</file>
+ <file>icons/arrow-down-s-line.png</file>
+ <file>icons/account-pin-circle-line.png</file>
+ <file>icons/shopping-cart-line.png</file>
+ <file>icons/picture-in-picture-line.png</file>
+ <file>icons/home-8-line.png</file>
+ <file>icons/white/white_scissors-cut-line.png</file>
+ <file>icons/white/white_play-fill.png</file>
+ <file>icons/white/white_terminal-box-line.png</file>
+ <file>icons/white/white_shape-2-line.png</file>
+ <file>icons/white/white_sip-line.png</file>
+ <file>icons/white/white_record-circle-line.png</file>
+ <file>icons/white/stop-line.png</file>
+ <file>icons/white/white_arrow-right-line.png</file>
+ <file>icons/white/white_arrow-left-line.png</file>
+ <file>icons/white/white_picture-in-picture-line.png</file>
+ <file>icons/others/monitor.png</file>
+ <file>icons/others/wall_placeholder_180.jpg</file>
+ <file>icons/categories/devices-and-iot.png</file>
+ <file>icons/categories/social.png</file>
+ <file>icons/categories/server-and-cloud.png</file>
+ <file>icons/categories/security.png</file>
+ <file>icons/categories/productivity.png</file>
+ <file>icons/categories/photo-and-video.png</file>
+ <file>icons/categories/art-and-design.png</file>
+ <file>icons/categories/personalisation.png</file>
+ <file>icons/categories/news-and-weather.png</file>
+ <file>icons/categories/music-and-audio.png</file>
+ <file>icons/categories/health-and-fitness.png</file>
+ <file>icons/categories/games.png</file>
+ <file>icons/categories/finance.png</file>
+ <file>icons/categories/featured.png</file>
+ <file>icons/categories/entertainment.png</file>
+ <file>icons/categories/education.png</file>
+ <file>icons/categories/science.png</file>
+ <file>icons/categories/development.png</file>
+ <file>icons/categories/books-and-reference.png</file>
+ <file>icons/categories/utilities.png</file>
+ <file>icons/others/snapcraft.png</file>
+ <file>icons/mail-line.png</file>
+ <file>icons/app/icon-256.png</file>
+ <file>icons/app/icon-128.png</file>
+ <file>icons/app/icon-64.png</file>
+ <file>icons/app/icon-32.png</file>
+ <file>icons/app/icon-16.png</file>
+ <file>icons/app/icon-512.png</file>
+ <file>icons/app/resize.sh</file>
+ <file>icons/others/greendot.png</file>
+ <file>icons/anticlockwise-line.png</file>
+ <file>icons/arrow-go-back-line.png</file>
+ <file>icons/arrow-go-forward-line.png</file>
+ <file>icons/clockwise-line.png</file>
+ <file>icons/crop-line.png</file>
+ <file>icons/facebook-line.png</file>
+ <file>icons/printer-line.png</file>
+ <file>icons/save-line.png</file>
+ <file>icons/grid-line.png</file>
+ <file>icons/shape-line.png</file>
+ <file>icons/compare-image-line.png</file>
+ <file>icons/fx-line.png</file>
+ <file>icons/zoom-out-line.png</file>
+ <file>icons/zoom-out-fill.png</file>
+ <file>icons/zoom-in-line.png</file>
+ <file>icons/zoom-in-fill.png</file>
+ <file>icons/split-cells-vertical.png</file>
+ <file>icons/split-cells-horizontal.png</file>
+ <file>icons/fullscreen-line.png</file>
+ <file>icons/clockwise-fill.png</file>
+ <file>icons/anticlockwise-fill.png</file>
+ <file>icons/others/Histogram.png</file>
+ <file>icons/tiktok-downloader.png</file>
+ <file>icons/icon-512.xcf</file>
+ <file>icons/blur-off-line.png</file>
+ <file>icons/lock-line.png</file>
+ <file>icons/lock-unlock-line.png</file>
+ <file>icons/funds-line.png</file>
+ <file>icons/others/tt_tbc_head.png</file>
+ <file>icons/app/whatsapp-message.svg</file>
+ <file>icons/app/whatsapp.svg</file>
+ </qresource>
+</RCC>
diff --git a/src/icons/account-pin-circle-line.png b/src/icons/account-pin-circle-line.png
new file mode 100644
index 0000000..1e70a3d
--- /dev/null
+++ b/src/icons/account-pin-circle-line.png
Binary files differ
diff --git a/src/icons/anticlockwise-fill.png b/src/icons/anticlockwise-fill.png
new file mode 100644
index 0000000..908a15a
--- /dev/null
+++ b/src/icons/anticlockwise-fill.png
Binary files differ
diff --git a/src/icons/anticlockwise-line.png b/src/icons/anticlockwise-line.png
new file mode 100644
index 0000000..8cd98de
--- /dev/null
+++ b/src/icons/anticlockwise-line.png
Binary files differ
diff --git a/src/icons/app/icon-128.png b/src/icons/app/icon-128.png
new file mode 100644
index 0000000..f625123
--- /dev/null
+++ b/src/icons/app/icon-128.png
Binary files differ
diff --git a/src/icons/app/icon-16.png b/src/icons/app/icon-16.png
new file mode 100644
index 0000000..e3ada9b
--- /dev/null
+++ b/src/icons/app/icon-16.png
Binary files differ
diff --git a/src/icons/app/icon-256.png b/src/icons/app/icon-256.png
new file mode 100644
index 0000000..2e5155d
--- /dev/null
+++ b/src/icons/app/icon-256.png
Binary files differ
diff --git a/src/icons/app/icon-32.png b/src/icons/app/icon-32.png
new file mode 100644
index 0000000..8b47514
--- /dev/null
+++ b/src/icons/app/icon-32.png
Binary files differ
diff --git a/src/icons/app/icon-512.png b/src/icons/app/icon-512.png
new file mode 100644
index 0000000..7974220
--- /dev/null
+++ b/src/icons/app/icon-512.png
Binary files differ
diff --git a/src/icons/app/icon-64.png b/src/icons/app/icon-64.png
new file mode 100644
index 0000000..175c8fb
--- /dev/null
+++ b/src/icons/app/icon-64.png
Binary files differ
diff --git a/src/icons/app/resize.sh b/src/icons/app/resize.sh
new file mode 100755
index 0000000..a2fd54b
--- /dev/null
+++ b/src/icons/app/resize.sh
@@ -0,0 +1,155 @@
+#!/bin/bash
+
+# Edit the settings below:
+
+# Output sizes -
+# Please note the format: each size is wrapped with quotes, width and height are separated with space.
+output=("16 16" "32 32" "64 64" "128 128" "256 256" "512 512" )
+
+# If you frequently use the same source file (f.e. "~/Desktop/src.jpg"),
+# set it in "default_src"
+default_src="";
+
+# If you frequently use the same destination
+# (f.e. "~/Desktop/Some_folder/%.jpg"), set it in "default_dst"
+# Destination must include "%", it will be replaced by output size, f.e. "800x600"
+default_dst="%.png";
+
+# Add signature?
+default_sign='n'
+
+# If you frequently use the same signature file (f.e. "~/Desktop/sig.png"),
+# set it in "default_sig"
+default_sig="";
+
+# Gravity is for cropping left/right edges for different proportions (center, east, west)
+default_gravity="center"
+
+# Output JPG quality: maximum is 100 (recommended)
+quality=100
+
+# ======
+# Do not edit below.
+# ======
+# Welcome to Smashing resizer!
+# Written by Vlad Gerasimov from http://www.vladstudio.com
+#
+# This script takes one "source" image and saves it in different sizes.
+#
+# Requires:
+# * imagemagick - http://www.imagemagick.org/
+# * python - I'm sure your PC already has it!
+
+
+
+# Unfortunately, bash is awful at math operations.
+# We'll create a simple function that handles math for us.
+# Example: $(math 2 * 2)
+
+function math(){
+ echo $(python -c "from __future__ import division; print $@")
+}
+
+
+
+# To make our script short and nice, here is the "save()" function.
+# We'll use it to save each size.
+
+function save(){
+
+ # read target width and height from function parameters
+ local dst_w=${1}
+ local dst_h=${2}
+
+ # calculate ratio
+ local ratio=$(math $dst_w/$dst_h);
+
+ # calculate "intermediate" width and height
+ local inter_w=$(math "int(round($src_h*$ratio))")
+ local inter_h=${src_h}
+
+ # which size we're saving now
+ #local size="${dst_w}x${dst_h}"
+ local size="${dst_w}"
+ echo "Saving ${size}..."
+
+ #crop intermediate image (with target ratio)
+ convert ${src} -gravity ${gravity} -crop ${inter_w}x${inter_h}+0+0 +repage temp.psd
+
+ # apply signature
+ if [ "${sign}" == "y" ]; then
+ convert temp.psd ${sig} -gravity southeast -geometry ${sig_w}x${sig_h}+24+48 -composite temp.psd
+ fi
+
+ ## For best quality, I resize image 80%, sharpen 3 times, then repeat.
+
+ # setup resize filter and unsharp parameters (calculated through trial and error)
+ local arguments="-interpolate bicubic -filter Lagrange"
+ local unsharp="-unsharp 0.4x0.4+0.4+0.008"
+
+ # scale 80%, sharpen, repeat until less than 150% of target size
+ local current_w=${dst_w}
+
+ # pardon moi for such ugly while condition!
+ while [ $(math "${current_w}/${dst_w} > 1.5") = "True" ]; do
+ current_w=$(math ${current_w}\*0\.80)
+ current_w=$(math "int(round(${current_w}))")
+ arguments="${arguments} -resize 80% +repage ${unsharp} ${unsharp} ${unsharp} "
+ done
+
+ # final resize
+ arguments="${arguments} -resize ${dst_w}x${dst_h}! +repage ${unsharp} ${unsharp} ${unsharp} -density 72x72 +repage"
+
+ # final convert! resize, sharpen, save
+ convert temp.psd ${arguments} -quality ${quality} ${dst/\%/${size}}
+
+}
+
+# Ask for source image, or use default value
+echo "Enter path to source image, or hit Enter to keep default value (${default_src}): "
+read src
+src=${src:-${default_src}}
+
+# ask for destination path, or use default value
+echo "Enter destination path, or hit Enter to keep default value (${default_dst})."
+echo "must include % symbol, it will be replaced by output size, f.e. '800x600'"
+read dst
+dst=${dst:-${default_dst}}
+
+# Ask for signature image, or use default value
+echo "Apply signature? (hit Enter for 'yes' or type 'n' for 'no') "
+read sign
+sign=${sign:-${default_sign}}
+
+# Ask for signature image, or use default value
+echo "Enter path to signature image, or hit Enter to keep default value (${default_sig}): "
+read sig
+sig=${sig:-${default_sig}}
+
+# ask for gravity, or use default value
+echo "Enter gravity for cropping left/right edges (center, east, west), or hit Enter to keep default value (${default_gravity}): "
+read gravity
+gravity=${gravity:-${default_gravity}}
+
+
+# detect source image width and height
+src_w=$(identify -format "%w" "${src}")
+src_h=$(identify -format "%h" "${src}")
+
+# detect signature width and height
+if [ "${sign}" == "y" ]; then
+ sig_w=$(identify -format "%w" "${sig}")
+ sig_h=$(identify -format "%h" "${sig}")
+fi
+
+# loop throught output sizes and save each size
+for i in "${output[@]}"
+do
+ save ${i}
+done
+
+# Delete temporary file
+rm temp.psd
+
+# Done!
+echo "Done!"
diff --git a/src/icons/app/whatsapp-message.svg b/src/icons/app/whatsapp-message.svg
new file mode 100644
index 0000000..7f56fa4
--- /dev/null
+++ b/src/icons/app/whatsapp-message.svg
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="100%"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
+ version="1.1"
+ viewBox="0 0 24 24"
+ width="100%"
+ xml:space="preserve"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="whatsapp.svg"><metadata
+ id="metadata19"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs17"><filter
+ inkscape:label="Fill Background"
+ inkscape:menu="Fill and Transparency"
+ inkscape:menu-tooltip="Adds a colorizable opaque background"
+ style="color-interpolation-filters:sRGB;"
+ id="filter843"><feFlood
+ result="result1"
+ flood-color="rgb(255,0,0)"
+ flood-opacity="1"
+ id="feFlood845" /><feImage
+ xlink:href=""
+ result="result2"
+ id="feImage847" /><feBlend
+ result="result4"
+ in2="result1"
+ mode="multiply"
+ id="feBlend849" /><feMerge
+ result="result3"
+ id="feMerge851"><feMergeNode
+ in="result1"
+ id="feMergeNode853" /><feMergeNode
+ in="SourceGraphic"
+ id="feMergeNode855" /></feMerge></filter></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1311"
+ inkscape:window-height="744"
+ id="namedview15"
+ showgrid="false"
+ inkscape:zoom="8.4606523"
+ inkscape:cx="9.4540046"
+ inkscape:cy="19.836975"
+ inkscape:window-x="55"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" /><rect
+ height="24"
+ id="Artboard1"
+ style="fill:none;"
+ width="24"
+ x="0"
+ y="0" /><g
+ id="g5"><g
+ id="g7"><path
+ d="M4.018,17.048c-0.96,-1.484 -1.518,-3.253 -1.518,-5.151c0,-5.243 4.257,-9.5 9.5,-9.5c5.243,0 9.5,4.257 9.5,9.5c0,5.243 -4.257,9.5 -9.5,9.5c-1.777,0 -3.44,-0.489 -4.863,-1.339l-4.637,1.545l1.518,-4.555Z"
+ style="fill:#f3f3f3;"
+ id="path9" /><path
+ d="M5.795,16.304c-0.886,-1.244 -1.407,-2.765 -1.407,-4.407c0,-4.201 3.411,-7.612 7.612,-7.612c4.201,0 7.612,3.411 7.612,7.612c0,4.201 -3.411,7.611 -7.612,7.611c-1.59,0 -3.066,-0.488 -4.288,-1.323l-2.862,0.954l0.945,-2.835Z"
+ style="fill:#00a82d;"
+ id="path11" /></g><path
+ d="M9.714,13.873c-1.124,-1.374 -1.874,-3.056 -2.109,-4.88c-0.063,-0.508 0.106,-1.018 0.461,-1.387c0.355,-0.369 0.858,-0.558 1.368,-0.515l0.049,0.005c0,0 0.561,0.15 0.868,0.233c0.122,0.033 0.219,0.124 0.26,0.243c0.138,0.41 0.464,1.373 0.618,1.826c0.05,0.147 0.004,0.31 -0.114,0.41c-0.233,0.196 -0.618,0.52 -0.858,0.723c-0.129,0.109 -0.17,0.29 -0.1,0.443c0.279,0.608 0.635,1.176 1.057,1.69c0.434,0.502 0.933,0.949 1.485,1.327c0.14,0.095 0.325,0.085 0.454,-0.024c0.241,-0.202 0.626,-0.526 0.858,-0.722c0.119,-0.1 0.287,-0.117 0.424,-0.043c0.42,0.228 1.314,0.712 1.694,0.918c0.111,0.06 0.185,0.172 0.196,0.297c0.029,0.317 0.083,0.895 0.083,0.895l-0.004,0.049c-0.044,0.51 -0.315,0.974 -0.739,1.261c-0.424,0.288 -0.955,0.368 -1.445,0.22c-1.772,-0.545 -3.313,-1.581 -4.479,-2.937l-0.027,-0.032Z"
+ style="fill:#f3f3f3;"
+ id="path13" /></g><path
+ style="fill:#ff0000;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1.41420996;stroke-dasharray:none;stroke-opacity:1"
+ id="path904"
+ sodipodi:type="arc"
+ sodipodi:cx="17.079062"
+ sodipodi:cy="7.2755213"
+ sodipodi:rx="2.6593695"
+ sodipodi:ry="2.6593695"
+ sodipodi:start="0"
+ sodipodi:end="6.2744587"
+ sodipodi:open="true"
+ d="M 19.738431,7.2755213 A 2.6593695,2.6593695 0 0 1 17.084863,9.9348844 2.6593695,2.6593695 0 0 1 14.419717,7.2871249 2.6593695,2.6593695 0 0 1 17.061656,4.6162088 2.6593695,2.6593695 0 0 1 19.73833,7.2523143"
+ inkscape:transform-center-x="-0.23638866"
+ inkscape:transform-center-y="-0.70916464" /></svg> \ No newline at end of file
diff --git a/src/icons/app/whatsapp.svg b/src/icons/app/whatsapp.svg
new file mode 100644
index 0000000..0b81505
--- /dev/null
+++ b/src/icons/app/whatsapp.svg
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="100%"
+ style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
+ version="1.1"
+ viewBox="0 0 24 24"
+ width="100%"
+ xml:space="preserve"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="whatsapp.svg"><metadata
+ id="metadata19"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+ id="defs17"><filter
+ inkscape:label="Fill Background"
+ inkscape:menu="Fill and Transparency"
+ inkscape:menu-tooltip="Adds a colorizable opaque background"
+ style="color-interpolation-filters:sRGB;"
+ id="filter843"><feFlood
+ result="result1"
+ flood-color="rgb(255,0,0)"
+ flood-opacity="1"
+ id="feFlood845" /><feImage
+ xlink:href=""
+ result="result2"
+ id="feImage847" /><feBlend
+ result="result4"
+ in2="result1"
+ mode="multiply"
+ id="feBlend849" /><feMerge
+ result="result3"
+ id="feMerge851"><feMergeNode
+ in="result1"
+ id="feMergeNode853" /><feMergeNode
+ in="SourceGraphic"
+ id="feMergeNode855" /></feMerge></filter></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1311"
+ inkscape:window-height="744"
+ id="namedview15"
+ showgrid="false"
+ inkscape:zoom="8.4606523"
+ inkscape:cx="-10.8163"
+ inkscape:cy="19.836975"
+ inkscape:window-x="55"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" /><rect
+ height="24"
+ id="Artboard1"
+ style="fill:none;"
+ width="24"
+ x="0"
+ y="0" /><g
+ id="g5"><g
+ id="g7"><path
+ d="M4.018,17.048c-0.96,-1.484 -1.518,-3.253 -1.518,-5.151c0,-5.243 4.257,-9.5 9.5,-9.5c5.243,0 9.5,4.257 9.5,9.5c0,5.243 -4.257,9.5 -9.5,9.5c-1.777,0 -3.44,-0.489 -4.863,-1.339l-4.637,1.545l1.518,-4.555Z"
+ style="fill:#f3f3f3;"
+ id="path9" /><path
+ d="M5.795,16.304c-0.886,-1.244 -1.407,-2.765 -1.407,-4.407c0,-4.201 3.411,-7.612 7.612,-7.612c4.201,0 7.612,3.411 7.612,7.612c0,4.201 -3.411,7.611 -7.612,7.611c-1.59,0 -3.066,-0.488 -4.288,-1.323l-2.862,0.954l0.945,-2.835Z"
+ style="fill:#00a82d;"
+ id="path11" /></g><path
+ d="M9.714,13.873c-1.124,-1.374 -1.874,-3.056 -2.109,-4.88c-0.063,-0.508 0.106,-1.018 0.461,-1.387c0.355,-0.369 0.858,-0.558 1.368,-0.515l0.049,0.005c0,0 0.561,0.15 0.868,0.233c0.122,0.033 0.219,0.124 0.26,0.243c0.138,0.41 0.464,1.373 0.618,1.826c0.05,0.147 0.004,0.31 -0.114,0.41c-0.233,0.196 -0.618,0.52 -0.858,0.723c-0.129,0.109 -0.17,0.29 -0.1,0.443c0.279,0.608 0.635,1.176 1.057,1.69c0.434,0.502 0.933,0.949 1.485,1.327c0.14,0.095 0.325,0.085 0.454,-0.024c0.241,-0.202 0.626,-0.526 0.858,-0.722c0.119,-0.1 0.287,-0.117 0.424,-0.043c0.42,0.228 1.314,0.712 1.694,0.918c0.111,0.06 0.185,0.172 0.196,0.297c0.029,0.317 0.083,0.895 0.083,0.895l-0.004,0.049c-0.044,0.51 -0.315,0.974 -0.739,1.261c-0.424,0.288 -0.955,0.368 -1.445,0.22c-1.772,-0.545 -3.313,-1.581 -4.479,-2.937l-0.027,-0.032Z"
+ style="fill:#f3f3f3;"
+ id="path13" /></g></svg> \ No newline at end of file
diff --git a/src/icons/archive-drawer-line.png b/src/icons/archive-drawer-line.png
new file mode 100644
index 0000000..c770801
--- /dev/null
+++ b/src/icons/archive-drawer-line.png
Binary files differ
diff --git a/src/icons/arrow-down-s-line.png b/src/icons/arrow-down-s-line.png
new file mode 100644
index 0000000..ab85daa
--- /dev/null
+++ b/src/icons/arrow-down-s-line.png
Binary files differ
diff --git a/src/icons/arrow-go-back-line.png b/src/icons/arrow-go-back-line.png
new file mode 100644
index 0000000..1081e0a
--- /dev/null
+++ b/src/icons/arrow-go-back-line.png
Binary files differ
diff --git a/src/icons/arrow-go-forward-line.png b/src/icons/arrow-go-forward-line.png
new file mode 100644
index 0000000..ef65a0a
--- /dev/null
+++ b/src/icons/arrow-go-forward-line.png
Binary files differ
diff --git a/src/icons/arrow-left-circle-line.png b/src/icons/arrow-left-circle-line.png
new file mode 100644
index 0000000..4d9d808
--- /dev/null
+++ b/src/icons/arrow-left-circle-line.png
Binary files differ
diff --git a/src/icons/arrow-left-line.png b/src/icons/arrow-left-line.png
new file mode 100644
index 0000000..411f8d5
--- /dev/null
+++ b/src/icons/arrow-left-line.png
Binary files differ
diff --git a/src/icons/arrow-left-s-line.png b/src/icons/arrow-left-s-line.png
new file mode 100644
index 0000000..6c34c72
--- /dev/null
+++ b/src/icons/arrow-left-s-line.png
Binary files differ
diff --git a/src/icons/arrow-right-circle-line.png b/src/icons/arrow-right-circle-line.png
new file mode 100644
index 0000000..a993751
--- /dev/null
+++ b/src/icons/arrow-right-circle-line.png
Binary files differ
diff --git a/src/icons/arrow-right-line.png b/src/icons/arrow-right-line.png
new file mode 100644
index 0000000..9085577
--- /dev/null
+++ b/src/icons/arrow-right-line.png
Binary files differ
diff --git a/src/icons/arrow-right-s-line.png b/src/icons/arrow-right-s-line.png
new file mode 100644
index 0000000..6c1aaa5
--- /dev/null
+++ b/src/icons/arrow-right-s-line.png
Binary files differ
diff --git a/src/icons/arrow-up-s-line.png b/src/icons/arrow-up-s-line.png
new file mode 100644
index 0000000..04b0500
--- /dev/null
+++ b/src/icons/arrow-up-s-line.png
Binary files differ
diff --git a/src/icons/blur-off-line.png b/src/icons/blur-off-line.png
new file mode 100644
index 0000000..1d20104
--- /dev/null
+++ b/src/icons/blur-off-line.png
Binary files differ
diff --git a/src/icons/camera-off-line.png b/src/icons/camera-off-line.png
new file mode 100644
index 0000000..f748ce5
--- /dev/null
+++ b/src/icons/camera-off-line.png
Binary files differ
diff --git a/src/icons/categories/art-and-design.png b/src/icons/categories/art-and-design.png
new file mode 100644
index 0000000..706dcdd
--- /dev/null
+++ b/src/icons/categories/art-and-design.png
Binary files differ
diff --git a/src/icons/categories/books-and-reference.png b/src/icons/categories/books-and-reference.png
new file mode 100644
index 0000000..9684a3c
--- /dev/null
+++ b/src/icons/categories/books-and-reference.png
Binary files differ
diff --git a/src/icons/categories/development.png b/src/icons/categories/development.png
new file mode 100644
index 0000000..b81758b
--- /dev/null
+++ b/src/icons/categories/development.png
Binary files differ
diff --git a/src/icons/categories/devices-and-iot.png b/src/icons/categories/devices-and-iot.png
new file mode 100644
index 0000000..b81ee28
--- /dev/null
+++ b/src/icons/categories/devices-and-iot.png
Binary files differ
diff --git a/src/icons/categories/education.png b/src/icons/categories/education.png
new file mode 100644
index 0000000..5d474c4
--- /dev/null
+++ b/src/icons/categories/education.png
Binary files differ
diff --git a/src/icons/categories/entertainment.png b/src/icons/categories/entertainment.png
new file mode 100644
index 0000000..53bf12b
--- /dev/null
+++ b/src/icons/categories/entertainment.png
Binary files differ
diff --git a/src/icons/categories/featured.png b/src/icons/categories/featured.png
new file mode 100644
index 0000000..0bc0e29
--- /dev/null
+++ b/src/icons/categories/featured.png
Binary files differ
diff --git a/src/icons/categories/finance.png b/src/icons/categories/finance.png
new file mode 100644
index 0000000..c6edb26
--- /dev/null
+++ b/src/icons/categories/finance.png
Binary files differ
diff --git a/src/icons/categories/games.png b/src/icons/categories/games.png
new file mode 100644
index 0000000..2578a5f
--- /dev/null
+++ b/src/icons/categories/games.png
Binary files differ
diff --git a/src/icons/categories/health-and-fitness.png b/src/icons/categories/health-and-fitness.png
new file mode 100644
index 0000000..bf36890
--- /dev/null
+++ b/src/icons/categories/health-and-fitness.png
Binary files differ
diff --git a/src/icons/categories/music-and-audio.png b/src/icons/categories/music-and-audio.png
new file mode 100644
index 0000000..a226a08
--- /dev/null
+++ b/src/icons/categories/music-and-audio.png
Binary files differ
diff --git a/src/icons/categories/news-and-weather.png b/src/icons/categories/news-and-weather.png
new file mode 100644
index 0000000..97b5fe2
--- /dev/null
+++ b/src/icons/categories/news-and-weather.png
Binary files differ
diff --git a/src/icons/categories/personalisation.png b/src/icons/categories/personalisation.png
new file mode 100644
index 0000000..352226f
--- /dev/null
+++ b/src/icons/categories/personalisation.png
Binary files differ
diff --git a/src/icons/categories/photo-and-video.png b/src/icons/categories/photo-and-video.png
new file mode 100644
index 0000000..554a3cb
--- /dev/null
+++ b/src/icons/categories/photo-and-video.png
Binary files differ
diff --git a/src/icons/categories/productivity.png b/src/icons/categories/productivity.png
new file mode 100644
index 0000000..0bb33f4
--- /dev/null
+++ b/src/icons/categories/productivity.png
Binary files differ
diff --git a/src/icons/categories/science.png b/src/icons/categories/science.png
new file mode 100644
index 0000000..069e83a
--- /dev/null
+++ b/src/icons/categories/science.png
Binary files differ
diff --git a/src/icons/categories/security.png b/src/icons/categories/security.png
new file mode 100644
index 0000000..22132a5
--- /dev/null
+++ b/src/icons/categories/security.png
Binary files differ
diff --git a/src/icons/categories/server-and-cloud.png b/src/icons/categories/server-and-cloud.png
new file mode 100644
index 0000000..0a69247
--- /dev/null
+++ b/src/icons/categories/server-and-cloud.png
Binary files differ
diff --git a/src/icons/categories/social.png b/src/icons/categories/social.png
new file mode 100644
index 0000000..39e5478
--- /dev/null
+++ b/src/icons/categories/social.png
Binary files differ
diff --git a/src/icons/categories/utilities.png b/src/icons/categories/utilities.png
new file mode 100644
index 0000000..95a642d
--- /dev/null
+++ b/src/icons/categories/utilities.png
Binary files differ
diff --git a/src/icons/clipboard-line.png b/src/icons/clipboard-line.png
new file mode 100644
index 0000000..18ba32d
--- /dev/null
+++ b/src/icons/clipboard-line.png
Binary files differ
diff --git a/src/icons/clockwise-fill.png b/src/icons/clockwise-fill.png
new file mode 100644
index 0000000..75a577d
--- /dev/null
+++ b/src/icons/clockwise-fill.png
Binary files differ
diff --git a/src/icons/clockwise-line.png b/src/icons/clockwise-line.png
new file mode 100644
index 0000000..6bf7aca
--- /dev/null
+++ b/src/icons/clockwise-line.png
Binary files differ
diff --git a/src/icons/close-fill.png b/src/icons/close-fill.png
new file mode 100644
index 0000000..63fbd29
--- /dev/null
+++ b/src/icons/close-fill.png
Binary files differ
diff --git a/src/icons/compare-image-line.png b/src/icons/compare-image-line.png
new file mode 100644
index 0000000..f080138
--- /dev/null
+++ b/src/icons/compare-image-line.png
Binary files differ
diff --git a/src/icons/crop-line.png b/src/icons/crop-line.png
new file mode 100644
index 0000000..edf9098
--- /dev/null
+++ b/src/icons/crop-line.png
Binary files differ
diff --git a/src/icons/delete-bin-3-line.png b/src/icons/delete-bin-3-line.png
new file mode 100644
index 0000000..bf62d84
--- /dev/null
+++ b/src/icons/delete-bin-3-line.png
Binary files differ
diff --git a/src/icons/delete-bin-5-line.png b/src/icons/delete-bin-5-line.png
new file mode 100644
index 0000000..947a403
--- /dev/null
+++ b/src/icons/delete-bin-5-line.png
Binary files differ
diff --git a/src/icons/disabled-arrow-left-circle-line.png b/src/icons/disabled-arrow-left-circle-line.png
new file mode 100644
index 0000000..67769ff
--- /dev/null
+++ b/src/icons/disabled-arrow-left-circle-line.png
Binary files differ
diff --git a/src/icons/disabled-arrow-right-circle-line.png b/src/icons/disabled-arrow-right-circle-line.png
new file mode 100644
index 0000000..078e72a
--- /dev/null
+++ b/src/icons/disabled-arrow-right-circle-line.png
Binary files differ
diff --git a/src/icons/download-line.png b/src/icons/download-line.png
new file mode 100644
index 0000000..a8f6cec
--- /dev/null
+++ b/src/icons/download-line.png
Binary files differ
diff --git a/src/icons/drag-move-fill.png b/src/icons/drag-move-fill.png
new file mode 100644
index 0000000..1e8b211
--- /dev/null
+++ b/src/icons/drag-move-fill.png
Binary files differ
diff --git a/src/icons/error-warning-line.png b/src/icons/error-warning-line.png
new file mode 100644
index 0000000..9c6e522
--- /dev/null
+++ b/src/icons/error-warning-line.png
Binary files differ
diff --git a/src/icons/eye-line.png b/src/icons/eye-line.png
new file mode 100644
index 0000000..c856fa5
--- /dev/null
+++ b/src/icons/eye-line.png
Binary files differ
diff --git a/src/icons/facebook-box-line.png b/src/icons/facebook-box-line.png
new file mode 100644
index 0000000..c3c9a35
--- /dev/null
+++ b/src/icons/facebook-box-line.png
Binary files differ
diff --git a/src/icons/facebook-line.png b/src/icons/facebook-line.png
new file mode 100644
index 0000000..e0a5205
--- /dev/null
+++ b/src/icons/facebook-line.png
Binary files differ
diff --git a/src/icons/file-copy-line.png b/src/icons/file-copy-line.png
new file mode 100644
index 0000000..f07fbcb
--- /dev/null
+++ b/src/icons/file-copy-line.png
Binary files differ
diff --git a/src/icons/file-text-line.png b/src/icons/file-text-line.png
new file mode 100644
index 0000000..93be6b6
--- /dev/null
+++ b/src/icons/file-text-line.png
Binary files differ
diff --git a/src/icons/file-unknow-line.png b/src/icons/file-unknow-line.png
new file mode 100644
index 0000000..30ceffc
--- /dev/null
+++ b/src/icons/file-unknow-line.png
Binary files differ
diff --git a/src/icons/filter-2-line.png b/src/icons/filter-2-line.png
new file mode 100644
index 0000000..04b127c
--- /dev/null
+++ b/src/icons/filter-2-line.png
Binary files differ
diff --git a/src/icons/focus-3-line.png b/src/icons/focus-3-line.png
new file mode 100644
index 0000000..acd8380
--- /dev/null
+++ b/src/icons/focus-3-line.png
Binary files differ
diff --git a/src/icons/folder-download-line.png b/src/icons/folder-download-line.png
new file mode 100644
index 0000000..a971c93
--- /dev/null
+++ b/src/icons/folder-download-line.png
Binary files differ
diff --git a/src/icons/folder-open-line.png b/src/icons/folder-open-line.png
new file mode 100644
index 0000000..7049366
--- /dev/null
+++ b/src/icons/folder-open-line.png
Binary files differ
diff --git a/src/icons/fullscreen-line.png b/src/icons/fullscreen-line.png
new file mode 100644
index 0000000..788249d
--- /dev/null
+++ b/src/icons/fullscreen-line.png
Binary files differ
diff --git a/src/icons/funds-line.png b/src/icons/funds-line.png
new file mode 100644
index 0000000..3c38303
--- /dev/null
+++ b/src/icons/funds-line.png
Binary files differ
diff --git a/src/icons/fx-line.png b/src/icons/fx-line.png
new file mode 100644
index 0000000..e443c06
--- /dev/null
+++ b/src/icons/fx-line.png
Binary files differ
diff --git a/src/icons/grid-line.png b/src/icons/grid-line.png
new file mode 100644
index 0000000..dae2987
--- /dev/null
+++ b/src/icons/grid-line.png
Binary files differ
diff --git a/src/icons/home-8-line.png b/src/icons/home-8-line.png
new file mode 100644
index 0000000..5811472
--- /dev/null
+++ b/src/icons/home-8-line.png
Binary files differ
diff --git a/src/icons/icon-512.xcf b/src/icons/icon-512.xcf
new file mode 100644
index 0000000..b9275f6
--- /dev/null
+++ b/src/icons/icon-512.xcf
Binary files differ
diff --git a/src/icons/image-2-line.png b/src/icons/image-2-line.png
new file mode 100644
index 0000000..f1c2494
--- /dev/null
+++ b/src/icons/image-2-line.png
Binary files differ
diff --git a/src/icons/information-fill.png b/src/icons/information-fill.png
new file mode 100644
index 0000000..ddcd05d
--- /dev/null
+++ b/src/icons/information-fill.png
Binary files differ
diff --git a/src/icons/information-line.png b/src/icons/information-line.png
new file mode 100644
index 0000000..8336ada
--- /dev/null
+++ b/src/icons/information-line.png
Binary files differ
diff --git a/src/icons/link-unlink-m.png b/src/icons/link-unlink-m.png
new file mode 100644
index 0000000..9a4c56e
--- /dev/null
+++ b/src/icons/link-unlink-m.png
Binary files differ
diff --git a/src/icons/links-line.png b/src/icons/links-line.png
new file mode 100644
index 0000000..c0e3eaf
--- /dev/null
+++ b/src/icons/links-line.png
Binary files differ
diff --git a/src/icons/loader-2-fill.png b/src/icons/loader-2-fill.png
new file mode 100644
index 0000000..044be77
--- /dev/null
+++ b/src/icons/loader-2-fill.png
Binary files differ
diff --git a/src/icons/lock-2-fill.png b/src/icons/lock-2-fill.png
new file mode 100644
index 0000000..86b7d7c
--- /dev/null
+++ b/src/icons/lock-2-fill.png
Binary files differ
diff --git a/src/icons/lock-line.png b/src/icons/lock-line.png
new file mode 100644
index 0000000..6370adc
--- /dev/null
+++ b/src/icons/lock-line.png
Binary files differ
diff --git a/src/icons/lock-unlock-line.png b/src/icons/lock-unlock-line.png
new file mode 100644
index 0000000..d2bf8e9
--- /dev/null
+++ b/src/icons/lock-unlock-line.png
Binary files differ
diff --git a/src/icons/magic-line.png b/src/icons/magic-line.png
new file mode 100644
index 0000000..2422682
--- /dev/null
+++ b/src/icons/magic-line.png
Binary files differ
diff --git a/src/icons/mail-add-line.png b/src/icons/mail-add-line.png
new file mode 100644
index 0000000..d04eba5
--- /dev/null
+++ b/src/icons/mail-add-line.png
Binary files differ
diff --git a/src/icons/mail-line.png b/src/icons/mail-line.png
new file mode 100644
index 0000000..c2a13cc
--- /dev/null
+++ b/src/icons/mail-line.png
Binary files differ
diff --git a/src/icons/map-pin-line.png b/src/icons/map-pin-line.png
new file mode 100644
index 0000000..159ca5b
--- /dev/null
+++ b/src/icons/map-pin-line.png
Binary files differ
diff --git a/src/icons/movie-2-line.png b/src/icons/movie-2-line.png
new file mode 100644
index 0000000..dbfdf7f
--- /dev/null
+++ b/src/icons/movie-2-line.png
Binary files differ
diff --git a/src/icons/music-2-line.png b/src/icons/music-2-line.png
new file mode 100644
index 0000000..d2edf8c
--- /dev/null
+++ b/src/icons/music-2-line.png
Binary files differ
diff --git a/src/icons/others/Histogram.png b/src/icons/others/Histogram.png
new file mode 100644
index 0000000..d2bddb3
--- /dev/null
+++ b/src/icons/others/Histogram.png
Binary files differ
diff --git a/src/icons/others/Omonitor.png b/src/icons/others/Omonitor.png
new file mode 100644
index 0000000..f1c1a64
--- /dev/null
+++ b/src/icons/others/Omonitor.png
Binary files differ
diff --git a/src/icons/others/greendot.png b/src/icons/others/greendot.png
new file mode 100644
index 0000000..be914c6
--- /dev/null
+++ b/src/icons/others/greendot.png
Binary files differ
diff --git a/src/icons/others/monitor.png b/src/icons/others/monitor.png
new file mode 100644
index 0000000..7ba4759
--- /dev/null
+++ b/src/icons/others/monitor.png
Binary files differ
diff --git a/src/icons/others/snapcraft.png b/src/icons/others/snapcraft.png
new file mode 100644
index 0000000..da50180
--- /dev/null
+++ b/src/icons/others/snapcraft.png
Binary files differ
diff --git a/src/icons/others/snapcraft.svg b/src/icons/others/snapcraft.svg
new file mode 100644
index 0000000..a14c440
--- /dev/null
+++ b/src/icons/others/snapcraft.svg
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ aria-hidden="true"
+ focusable="false"
+ width="1em"
+ height="1em"
+ style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"
+ preserveAspectRatio="xMidYMid meet"
+ viewBox="0 0 24 24"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="snapcraft.svg">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1311"
+ inkscape:window-height="744"
+ id="namedview6"
+ showgrid="false"
+ inkscape:zoom="9.8333333"
+ inkscape:cx="12"
+ inkscape:cy="12"
+ inkscape:window-x="55"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <path
+ d="M13.804 13.367V5.69l5.292 2.362l-5.292 5.315zM3.701 23.514l6.49-12.22l2.847 2.843L3.7 23.514zM0 .486l13.355 4.848v8.484L0 .486zm21.803 4.848H14.11L24 9.748z"
+ fill="#626262"
+ id="path4"
+ style="fill-opacity:1;fill:#9fa0a4" />
+</svg>
diff --git a/src/icons/others/tt_tbc_head.png b/src/icons/others/tt_tbc_head.png
new file mode 100644
index 0000000..20cac2c
--- /dev/null
+++ b/src/icons/others/tt_tbc_head.png
Binary files differ
diff --git a/src/icons/others/wall_placeholder_180.jpg b/src/icons/others/wall_placeholder_180.jpg
new file mode 100644
index 0000000..d696b5f
--- /dev/null
+++ b/src/icons/others/wall_placeholder_180.jpg
Binary files differ
diff --git a/src/icons/pause-line.png b/src/icons/pause-line.png
new file mode 100644
index 0000000..4be8f55
--- /dev/null
+++ b/src/icons/pause-line.png
Binary files differ
diff --git a/src/icons/paypal-line.png b/src/icons/paypal-line.png
new file mode 100644
index 0000000..f6ac01d
--- /dev/null
+++ b/src/icons/paypal-line.png
Binary files differ
diff --git a/src/icons/picture-in-picture-line.png b/src/icons/picture-in-picture-line.png
new file mode 100644
index 0000000..4416bfd
--- /dev/null
+++ b/src/icons/picture-in-picture-line.png
Binary files differ
diff --git a/src/icons/play-fill.png b/src/icons/play-fill.png
new file mode 100644
index 0000000..1ead6ba
--- /dev/null
+++ b/src/icons/play-fill.png
Binary files differ
diff --git a/src/icons/play-line.png b/src/icons/play-line.png
new file mode 100644
index 0000000..9561499
--- /dev/null
+++ b/src/icons/play-line.png
Binary files differ
diff --git a/src/icons/printer-line.png b/src/icons/printer-line.png
new file mode 100644
index 0000000..fed36d3
--- /dev/null
+++ b/src/icons/printer-line.png
Binary files differ
diff --git a/src/icons/questionnaire-line.png b/src/icons/questionnaire-line.png
new file mode 100644
index 0000000..9c1fcc6
--- /dev/null
+++ b/src/icons/questionnaire-line.png
Binary files differ
diff --git a/src/icons/record-circle-line.png b/src/icons/record-circle-line.png
new file mode 100644
index 0000000..ad66257
--- /dev/null
+++ b/src/icons/record-circle-line.png
Binary files differ
diff --git a/src/icons/refresh-line.png b/src/icons/refresh-line.png
new file mode 100644
index 0000000..c105522
--- /dev/null
+++ b/src/icons/refresh-line.png
Binary files differ
diff --git a/src/icons/repeat-2-fill.png b/src/icons/repeat-2-fill.png
new file mode 100644
index 0000000..f32602a
--- /dev/null
+++ b/src/icons/repeat-2-fill.png
Binary files differ
diff --git a/src/icons/save-line.png b/src/icons/save-line.png
new file mode 100644
index 0000000..b0206e9
--- /dev/null
+++ b/src/icons/save-line.png
Binary files differ
diff --git a/src/icons/scissors-cut-line.png b/src/icons/scissors-cut-line.png
new file mode 100644
index 0000000..3ee54d7
--- /dev/null
+++ b/src/icons/scissors-cut-line.png
Binary files differ
diff --git a/src/icons/screenshot-2-line.png b/src/icons/screenshot-2-line.png
new file mode 100644
index 0000000..567ce04
--- /dev/null
+++ b/src/icons/screenshot-2-line.png
Binary files differ
diff --git a/src/icons/search-2-line.png b/src/icons/search-2-line.png
new file mode 100644
index 0000000..453acd1
--- /dev/null
+++ b/src/icons/search-2-line.png
Binary files differ
diff --git a/src/icons/setting-line.png b/src/icons/setting-line.png
new file mode 100644
index 0000000..fcbd74c
--- /dev/null
+++ b/src/icons/setting-line.png
Binary files differ
diff --git a/src/icons/shape-2-line.png b/src/icons/shape-2-line.png
new file mode 100644
index 0000000..a230b14
--- /dev/null
+++ b/src/icons/shape-2-line.png
Binary files differ
diff --git a/src/icons/shape-line.png b/src/icons/shape-line.png
new file mode 100644
index 0000000..d5944fb
--- /dev/null
+++ b/src/icons/shape-line.png
Binary files differ
diff --git a/src/icons/share-line.png b/src/icons/share-line.png
new file mode 100644
index 0000000..22bf75d
--- /dev/null
+++ b/src/icons/share-line.png
Binary files differ
diff --git a/src/icons/shopping-cart-line.png b/src/icons/shopping-cart-line.png
new file mode 100644
index 0000000..219b634
--- /dev/null
+++ b/src/icons/shopping-cart-line.png
Binary files differ
diff --git a/src/icons/sip-line.png b/src/icons/sip-line.png
new file mode 100644
index 0000000..16aeb6c
--- /dev/null
+++ b/src/icons/sip-line.png
Binary files differ
diff --git a/src/icons/split-cells-horizontal.png b/src/icons/split-cells-horizontal.png
new file mode 100644
index 0000000..b180049
--- /dev/null
+++ b/src/icons/split-cells-horizontal.png
Binary files differ
diff --git a/src/icons/split-cells-vertical.png b/src/icons/split-cells-vertical.png
new file mode 100644
index 0000000..c741af8
--- /dev/null
+++ b/src/icons/split-cells-vertical.png
Binary files differ
diff --git a/src/icons/star-line.png b/src/icons/star-line.png
new file mode 100644
index 0000000..9f9a328
--- /dev/null
+++ b/src/icons/star-line.png
Binary files differ
diff --git a/src/icons/stop-line.png b/src/icons/stop-line.png
new file mode 100644
index 0000000..2bda682
--- /dev/null
+++ b/src/icons/stop-line.png
Binary files differ
diff --git a/src/icons/swap-box-line.png b/src/icons/swap-box-line.png
new file mode 100644
index 0000000..0f270b2
--- /dev/null
+++ b/src/icons/swap-box-line.png
Binary files differ
diff --git a/src/icons/terminal-box-line.png b/src/icons/terminal-box-line.png
new file mode 100644
index 0000000..3e66e7d
--- /dev/null
+++ b/src/icons/terminal-box-line.png
Binary files differ
diff --git a/src/icons/texture.png b/src/icons/texture.png
new file mode 100644
index 0000000..12bae83
--- /dev/null
+++ b/src/icons/texture.png
Binary files differ
diff --git a/src/icons/tiktok-downloader.png b/src/icons/tiktok-downloader.png
new file mode 100644
index 0000000..7ca80f6
--- /dev/null
+++ b/src/icons/tiktok-downloader.png
Binary files differ
diff --git a/src/icons/time-line.png b/src/icons/time-line.png
new file mode 100644
index 0000000..590aa72
--- /dev/null
+++ b/src/icons/time-line.png
Binary files differ
diff --git a/src/icons/translate-2.png b/src/icons/translate-2.png
new file mode 100644
index 0000000..10a2c39
--- /dev/null
+++ b/src/icons/translate-2.png
Binary files differ
diff --git a/src/icons/twitter-line.png b/src/icons/twitter-line.png
new file mode 100644
index 0000000..4e5dad9
--- /dev/null
+++ b/src/icons/twitter-line.png
Binary files differ
diff --git a/src/icons/volume-mute-line.png b/src/icons/volume-mute-line.png
new file mode 100644
index 0000000..1877d6c
--- /dev/null
+++ b/src/icons/volume-mute-line.png
Binary files differ
diff --git a/src/icons/volume-up-line.png b/src/icons/volume-up-line.png
new file mode 100644
index 0000000..3473479
--- /dev/null
+++ b/src/icons/volume-up-line.png
Binary files differ
diff --git a/src/icons/white/stop-line.png b/src/icons/white/stop-line.png
new file mode 100644
index 0000000..2cd918c
--- /dev/null
+++ b/src/icons/white/stop-line.png
Binary files differ
diff --git a/src/icons/white/white_arrow-left-line.png b/src/icons/white/white_arrow-left-line.png
new file mode 100644
index 0000000..1ab063b
--- /dev/null
+++ b/src/icons/white/white_arrow-left-line.png
Binary files differ
diff --git a/src/icons/white/white_arrow-right-line.png b/src/icons/white/white_arrow-right-line.png
new file mode 100644
index 0000000..e203184
--- /dev/null
+++ b/src/icons/white/white_arrow-right-line.png
Binary files differ
diff --git a/src/icons/white/white_picture-in-picture-line.png b/src/icons/white/white_picture-in-picture-line.png
new file mode 100644
index 0000000..ee4dc9a
--- /dev/null
+++ b/src/icons/white/white_picture-in-picture-line.png
Binary files differ
diff --git a/src/icons/white/white_play-fill.png b/src/icons/white/white_play-fill.png
new file mode 100644
index 0000000..1ead6ba
--- /dev/null
+++ b/src/icons/white/white_play-fill.png
Binary files differ
diff --git a/src/icons/white/white_record-circle-line.png b/src/icons/white/white_record-circle-line.png
new file mode 100644
index 0000000..91b34d5
--- /dev/null
+++ b/src/icons/white/white_record-circle-line.png
Binary files differ
diff --git a/src/icons/white/white_scissors-cut-line.png b/src/icons/white/white_scissors-cut-line.png
new file mode 100644
index 0000000..3ee54d7
--- /dev/null
+++ b/src/icons/white/white_scissors-cut-line.png
Binary files differ
diff --git a/src/icons/white/white_shape-2-line.png b/src/icons/white/white_shape-2-line.png
new file mode 100644
index 0000000..6be2c99
--- /dev/null
+++ b/src/icons/white/white_shape-2-line.png
Binary files differ
diff --git a/src/icons/white/white_sip-line.png b/src/icons/white/white_sip-line.png
new file mode 100644
index 0000000..cefb4e3
--- /dev/null
+++ b/src/icons/white/white_sip-line.png
Binary files differ
diff --git a/src/icons/white/white_terminal-box-line.png b/src/icons/white/white_terminal-box-line.png
new file mode 100644
index 0000000..f98536f
--- /dev/null
+++ b/src/icons/white/white_terminal-box-line.png
Binary files differ
diff --git a/src/icons/youtube-line.png b/src/icons/youtube-line.png
new file mode 100644
index 0000000..96a7b80
--- /dev/null
+++ b/src/icons/youtube-line.png
Binary files differ
diff --git a/src/icons/zoom-in-fill.png b/src/icons/zoom-in-fill.png
new file mode 100644
index 0000000..aa40dfd
--- /dev/null
+++ b/src/icons/zoom-in-fill.png
Binary files differ
diff --git a/src/icons/zoom-in-line.png b/src/icons/zoom-in-line.png
new file mode 100644
index 0000000..21c13e1
--- /dev/null
+++ b/src/icons/zoom-in-line.png
Binary files differ
diff --git a/src/icons/zoom-out-fill.png b/src/icons/zoom-out-fill.png
new file mode 100644
index 0000000..39f173f
--- /dev/null
+++ b/src/icons/zoom-out-fill.png
Binary files differ
diff --git a/src/icons/zoom-out-line.png b/src/icons/zoom-out-line.png
new file mode 100644
index 0000000..8d5168b
--- /dev/null
+++ b/src/icons/zoom-out-line.png
Binary files differ
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..5971c92
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,45 @@
+#include <QApplication>
+#include <QtWidgets>
+#include <QtWebEngine>
+#include <QWebEngineProfile>
+#include <QWebEngineSettings>
+#include <QSettings>
+#include <QDebug>
+
+#include <mainwindow.h>
+#include "common.h"
+
+extern QString defaultUserAgentStr;
+
+int main(int argc, char *argv[])
+{
+
+ //argv[argc++] = "--single-process";
+
+ QApplication app(argc, argv);
+ app.setWindowIcon(QIcon(":/icons/app/icon-256.png"));
+
+ QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ static const char ENV_VAR_QT_DEVICE_PIXEL_RATIO[] = "QT_DEVICE_PIXEL_RATIO";
+ if (!qEnvironmentVariableIsSet(ENV_VAR_QT_DEVICE_PIXEL_RATIO)
+ && !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR")
+ && !qEnvironmentVariableIsSet("QT_SCALE_FACTOR")
+ && !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) {
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ }
+
+ QApplication::setApplicationName("WhatSie");
+ QApplication::setOrganizationName("org.keshavnrj.ubuntu");
+ QApplication::setApplicationVersion(VERSIONSTR);
+
+ QtWebEngine::initialize();
+
+ QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
+ QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, true);
+ QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
+
+ MainWindow window;
+ window.show();
+
+ return app.exec();
+}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
new file mode 100644
index 0000000..38ecbe6
--- /dev/null
+++ b/src/mainwindow.cpp
@@ -0,0 +1,551 @@
+#include "mainwindow.h"
+
+
+extern QString defaultUserAgentStr;
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent),
+ notificationsTitleRegExp("^\\([1-9]\\d*\\).*"),
+ trayIconRead(":/icons/app/whatsapp.svg"),
+ trayIconUnread(":/icons/app/whatsapp-message.svg")
+{
+ setWindowTitle(QApplication::applicationName());
+ setWindowIcon(QIcon(":/icons/app/icon-256.png"));
+ setMinimumWidth(800);
+ setMinimumHeight(600);
+ readSettings();
+
+ createActions();
+ createStatusBar();
+ createTrayIcon();
+ createWebEngine();
+
+ init_settingWidget();
+
+ lightPalette = qApp->palette();
+ updateWindowTheme();
+
+ // quit application if the download manager window is the only remaining window
+ m_downloadManagerWidget.setAttribute(Qt::WA_QuitOnClose, false);
+
+
+}
+
+void MainWindow::updatePageTheme()
+{
+ QString webPageTheme = "web"; //implies light
+ QString windowTheme = settings.value("windowTheme","light").toString();
+ if(windowTheme == "dark"){
+ webPageTheme = "web dark";
+ }
+ if(webEngine && webEngine->page()){
+ webEngine->page()->runJavaScript(
+ "document.querySelector('body').className='"+webPageTheme+"';",
+ [](const QVariant &result){
+ qDebug() << "Value is: " << result.toString() << endl;
+ }
+ );
+ }
+}
+
+void MainWindow::updateWindowTheme()
+{
+ if(settings.value("windowTheme","light").toString() == "dark")
+ {
+ //TODO make dark palette match whatsapp dark theme
+ qApp->setStyle(QStyleFactory::create("fusion"));
+ QPalette palette;
+ palette.setColor(QPalette::Window,QColor("#131C21")); //whatsapp dark color
+ palette.setColor(QPalette::WindowText,Qt::white);
+ palette.setColor(QPalette::Disabled,QPalette::WindowText,QColor(127,127,127));
+ palette.setColor(QPalette::Base,QColor(42,42,42));
+ palette.setColor(QPalette::AlternateBase,QColor(66,66,66));
+ palette.setColor(QPalette::ToolTipBase,Qt::white);
+ palette.setColor(QPalette::ToolTipText,QColor(53,53,53));
+ palette.setColor(QPalette::Text,Qt::white);
+ palette.setColor(QPalette::Disabled,QPalette::Text,QColor(127,127,127));
+ palette.setColor(QPalette::Dark,QColor(35,35,35));
+ palette.setColor(QPalette::Shadow,QColor(20,20,20));
+ palette.setColor(QPalette::Button,QColor(53,53,53));
+ palette.setColor(QPalette::ButtonText,Qt::white);
+ palette.setColor(QPalette::Disabled,QPalette::ButtonText,QColor(127,127,127));
+ palette.setColor(QPalette::BrightText,Qt::red);
+ palette.setColor(QPalette::Link,QColor("skyblue"));
+ palette.setColor(QPalette::Highlight,QColor(49,106,150));
+ palette.setColor(QPalette::Disabled,QPalette::Highlight,QColor(80,80,80));
+ palette.setColor(QPalette::HighlightedText,Qt::white);
+ palette.setColor(QPalette::Disabled,QPalette::HighlightedText,QColor(127,127,127));
+ qApp->setPalette(palette);
+ this->webEngine->setStyleSheet("QWebEngineView{background:#131C21;}"); //whatsapp dark color
+ }
+ else{
+ qApp->setPalette(lightPalette);
+ this->update();
+ }
+}
+
+void MainWindow::handleCookieAdded(const QNetworkCookie &cookie)
+{
+ qDebug() << cookie.toRawForm() << "\n\n\n";
+}
+
+void MainWindow::readSettings()
+{
+ restoreGeometry(settings.value("geometry").toByteArray());
+ restoreState(settings.value("windowState").toByteArray());
+}
+
+void MainWindow::init_settingWidget()
+{
+ if(settingsWidget == nullptr)
+ {
+ settingsWidget = new SettingsWidget(this,webEngine->page()->profile()->cachePath()
+ ,webEngine->page()->profile()->persistentStoragePath());
+ settingsWidget->setWindowTitle(QApplication::applicationName()+" | Settings");
+ settingsWidget->setWindowFlags(Qt::Dialog);
+
+ connect(settingsWidget,SIGNAL(updateWindowTheme()),this,SLOT(updateWindowTheme()));
+ connect(settingsWidget,SIGNAL(updatePageTheme()),this,SLOT(updatePageTheme()));
+ connect(settingsWidget,&SettingsWidget::muteToggled,[=](const bool checked)
+ {
+ this->toggleMute(checked);
+ });
+ connect(settingsWidget,&SettingsWidget::userAgentChanged,[=](QString userAgentStr)
+ {
+ if(webEngine->page()->profile()->httpUserAgent() != userAgentStr)
+ {
+ settings.setValue("useragent",userAgentStr);
+ this->updateSettingsUserAgentWidget();
+ this->askToReloadPage();
+ }
+ });
+ connect(settingsWidget,&SettingsWidget::autoPlayMediaToggled,[=](bool checked)
+ { QWebEngineProfile *profile = QWebEngineProfile::defaultProfile();
+
+ auto* webSettings = profile->settings();
+ webSettings->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,checked);
+
+ this->webEngine->page()->profile()->settings()->setAttribute(
+ QWebEngineSettings::PlaybackRequiresUserGesture,
+ checked);
+ });
+ }
+}
+
+void MainWindow::showSettings()
+{
+ if(webEngine == nullptr){
+ QMessageBox::critical(this,QApplication::applicationName()+"| Error",
+ "Unable to initialize settings module.\nIs webengine initialized?");
+ return;
+ }
+ if(!settingsWidget->isVisible())
+ {
+ this->updateSettingsUserAgentWidget();
+ settingsWidget->refresh();
+ settingsWidget->showNormal();
+ }
+}
+
+void MainWindow::updateSettingsUserAgentWidget()
+{
+ settingsWidget->updateDefaultUAButton(this->webEngine->page()->profile()->httpUserAgent());
+}
+
+void MainWindow::askToReloadPage()
+{
+ QMessageBox msgBox;
+ msgBox.setWindowTitle(QApplication::applicationName()+" | Action required");
+ msgBox.setInformativeText("Page needs to be reloaded to continue.");
+ msgBox.setStandardButtons(QMessageBox::Ok);
+ msgBox.exec();
+ this->doAppReload();
+}
+
+void MainWindow::showAbout()
+{
+ QDialog *aboutDialog = new QDialog(this,Qt::Dialog);
+ aboutDialog->setWindowModality(Qt::WindowModal);
+ QVBoxLayout *layout = new QVBoxLayout;
+ QLabel *message = new QLabel(aboutDialog);
+ layout->addWidget(message);
+ connect(message,&QLabel::linkActivated,[=](const QString linkStr){
+ if(linkStr.contains("about_qt")){
+ qApp->aboutQt();
+ }else{
+ QDesktopServices::openUrl(QUrl(linkStr));
+ }
+ });
+ aboutDialog->setLayout(layout);
+ aboutDialog->setAttribute(Qt::WA_DeleteOnClose,true);
+ aboutDialog->show();
+
+ QString mes =
+ "<p align='center' style=' margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'><img src=':/icons/app/icon-64.png' /></p>"
+ "<p align='center' style='-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'><br /></p>"
+ "<p align='center' style=' margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'>Designed and Developed</p>"
+ "<p align='center' style=' margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'>by <span style=' font-weight:600;'>Keshav Bhatt</span> &lt;keshavnrj@gmail.com&gt;</p>"
+ "<p align='center' style=' margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'>Website: https://ktechpit.com</p>"
+ "<p align='center' style=' margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'>Runtime: <a href='http://about_qt'>Qt Toolkit</a></p>"
+ "<p align='center' style=' margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'>Version: "+QApplication::applicationVersion()+"</p>"
+ "<p align='center' style='-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'><br /></p>"
+ "<p align='center' style=' margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'><a href='https://snapcraft.io/search?q=keshavnrj'>More Apps</p>"
+ "<p align='center' style='-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;'><br /></p>";
+
+ QGraphicsOpacityEffect *eff = new QGraphicsOpacityEffect(this);
+ message->setGraphicsEffect(eff);
+ QPropertyAnimation *a = new QPropertyAnimation(eff,"opacity");
+ a->setDuration(1000);
+ a->setStartValue(0);
+ a->setEndValue(1);
+ a->setEasingCurve(QEasingCurve::InCurve);
+ a->start(QPropertyAnimation::DeleteWhenStopped);
+ message->setText(mes);
+ message->show();
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ settings.setValue("geometry", saveGeometry());
+ settings.setValue("windowState", saveState());
+
+ if(QSystemTrayIcon::isSystemTrayAvailable() && settings.value("closeButtonActionCombo",0).toInt() == 0){
+ this->hide();
+ event->ignore();
+ notify(QApplication::applicationName(),"Application is minimized to system tray.");
+ return;
+ }
+ event->accept();
+ qApp->quit();
+ QMainWindow::closeEvent(event);
+}
+
+void MainWindow::notify(QString title,QString message)
+{
+ if(settings.value("disableNotificationPopups",false).toBool() == true){
+ return;
+ }
+ auto popup = new NotificationPopup(webEngine);
+ connect(popup,&NotificationPopup::notification_clicked,[=](){
+ if(windowState()==Qt::WindowMinimized || windowState()!=Qt::WindowActive){
+ activateWindow();
+ raise();
+ show();
+ }
+ });
+ popup->adjustSize();
+ popup->present(title,message,QPixmap(":/icons/app/icon-64.png"));
+}
+
+void MainWindow::createActions()
+{
+
+ minimizeAction = new QAction(tr("Mi&nimize to tray"), this);
+ connect(minimizeAction, &QAction::triggered, this, &QWidget::hide);
+ addAction(minimizeAction);
+
+ restoreAction = new QAction(tr("&Restore"), this);
+ connect(restoreAction, &QAction::triggered, this, &QWidget::showNormal);
+ addAction(restoreAction);
+
+ reloadAction = new QAction(tr("Re&load"), this);
+ reloadAction->setShortcut(Qt::Key_F5);
+ connect(reloadAction, &QAction::triggered, this, &MainWindow::doReload);
+ addAction(reloadAction);
+
+ settingsAction = new QAction(tr("Settings"), this);
+ connect(settingsAction, &QAction::triggered, this, &MainWindow::showSettings);
+
+
+ aboutAction = new QAction(tr("About"), this);
+ connect(aboutAction, &QAction::triggered, this, &MainWindow::showAbout);
+
+ quitAction = new QAction(tr("&Quit"), this);
+ quitAction->setShortcut(QKeySequence(Qt::Modifier::CTRL + Qt::Key_Q));
+ connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit);
+ addAction(quitAction);
+}
+
+void MainWindow::createStatusBar()
+{
+ QStatusBar *statusBar = new QStatusBar(this);
+ setStatusBar(statusBar);
+ statusBar->hide();
+ this->statusBar = statusBar;
+}
+
+void MainWindow::createTrayIcon()
+{
+ trayIconMenu = new QMenu(this);
+ trayIconMenu->addAction(minimizeAction);
+ trayIconMenu->addAction(restoreAction);
+ trayIconMenu->addSeparator();
+ trayIconMenu->addAction(reloadAction);
+ trayIconMenu->addAction(settingsAction);
+ trayIconMenu->addAction(aboutAction);
+ trayIconMenu->addSeparator();
+ trayIconMenu->addAction(quitAction);
+
+ trayIcon = new QSystemTrayIcon(trayIconRead, this);
+ trayIcon->setContextMenu(trayIconMenu);
+
+ trayIcon->show();
+
+ connect(trayIcon, &QSystemTrayIcon::messageClicked,
+ this, &MainWindow::messageClicked);
+ connect(trayIcon, &QSystemTrayIcon::activated,
+ this, &MainWindow::iconActivated);
+}
+
+void MainWindow::init_globalWebProfile()
+{
+
+ QWebEngineProfile *profile = QWebEngineProfile::defaultProfile();
+ auto* webSettings = profile->settings();
+ webSettings->setAttribute(QWebEngineSettings::AutoLoadImages, true);
+ webSettings->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
+ webSettings->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
+ webSettings->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
+ webSettings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
+ webSettings->setAttribute(QWebEngineSettings::XSSAuditingEnabled, true);
+ webSettings->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, true);
+ webSettings->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, false);
+ webSettings->setAttribute(QWebEngineSettings::DnsPrefetchEnabled,true);
+ webSettings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled ,true);
+ webSettings->setAttribute(QWebEngineSettings::LinksIncludedInFocusChain, false);
+ webSettings->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false);
+ //webSettings->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, true);
+
+ // QObject::connect(
+ // QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested,
+ // &m_downloadManagerWidget, &DownloadManagerWidget::downloadRequested);
+
+ QWebEngineSettings::defaultSettings()->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture,
+ true);
+
+}
+
+void MainWindow::createWebEngine()
+{
+ init_globalWebProfile();
+
+ QSizePolicy widgetSize;
+ widgetSize.setHorizontalPolicy(QSizePolicy::Expanding);
+ widgetSize.setVerticalPolicy(QSizePolicy::Expanding);
+ widgetSize.setHorizontalStretch(1);
+ widgetSize.setVerticalStretch(1);
+
+ QWebEngineView *webEngine = new QWebEngineView(this);
+ setCentralWidget(webEngine);
+ webEngine->setSizePolicy(widgetSize);
+ webEngine->show();
+
+ this->webEngine = webEngine;
+
+
+ connect(webEngine, &QWebEngineView::titleChanged,
+ this, &MainWindow::handleWebViewTitleChanged);
+ connect(webEngine, &QWebEngineView::loadStarted,
+ this, &MainWindow::handleLoadStarted);
+ connect(webEngine, &QWebEngineView::loadProgress,
+ this, &MainWindow::handleLoadProgress);
+ connect(webEngine, &QWebEngineView::loadFinished,
+ this, &MainWindow::handleLoadFinished);
+ connect(webEngine, &QWebEngineView::renderProcessTerminated,
+ [this](QWebEnginePage::RenderProcessTerminationStatus termStatus, int statusCode) {
+ QString status;
+ switch (termStatus) {
+ case QWebEnginePage::NormalTerminationStatus:
+ status = tr("Render process normal exit");
+ break;
+ case QWebEnginePage::AbnormalTerminationStatus:
+ status = tr("Render process abnormal exit");
+ break;
+ case QWebEnginePage::CrashedTerminationStatus:
+ status = tr("Render process crashed");
+ break;
+ case QWebEnginePage::KilledTerminationStatus:
+ status = tr("Render process killed");
+ break;
+ }
+ QMessageBox::StandardButton btn = QMessageBox::question(window(), status,
+ tr("Render process exited with code: %1\n"
+ "Do you want to reload the page ?").arg(statusCode));
+ if (btn == QMessageBox::Yes)
+ QTimer::singleShot(0, [this] { this->webEngine->reload(); });
+ });
+
+ QWebEngineCookieStore *browser_cookie_store = webEngine->page()->profile()->cookieStore();
+ connect( browser_cookie_store, &QWebEngineCookieStore::cookieAdded, this, &MainWindow::handleCookieAdded );
+
+ createWebPage(false);
+}
+
+void MainWindow::createWebPage(bool offTheRecord)
+{
+ if (offTheRecord && !m_otrProfile) {
+ m_otrProfile.reset(new QWebEngineProfile);
+ }
+
+ auto profile = offTheRecord ? m_otrProfile.get() : QWebEngineProfile::defaultProfile();
+
+ profile->setHttpUserAgent(settings.value("useragent",defaultUserAgentStr).toString());
+
+ auto popup = new NotificationPopup(webEngine);
+ connect(popup,&NotificationPopup::notification_clicked,[=](){
+ if(windowState()==Qt::WindowMinimized || windowState()!=Qt::WindowActive){
+ activateWindow();
+ raise();
+ show();
+ }
+ });
+ profile->setNotificationPresenter([=] (std::unique_ptr<QWebEngineNotification> notification)
+ {
+ if(settings.value("disableNotificationPopups",false).toBool() == true){
+ return;
+ }
+ popup->present(notification);
+ });
+
+ QWebEnginePage *page = new WebEnginePage(profile,webEngine);
+ if(settings.value("windowTheme","light").toString() == "dark"){
+ page->setBackgroundColor(QColor("#F0F0F0")); //whatsapp bg color
+ }
+ webEngine->setPage(page);
+ //page should be set parent of profile to prevent
+ //Release of profile requested but WebEnginePage still not deleted. Expect troubles !
+ profile->setParent(page);
+ //RequestInterceptor *interceptor = new RequestInterceptor(profile);
+ //profile->setUrlRequestInterceptor(interceptor);
+ page->setUrl(QUrl("https://web.whatsapp.com/"));
+
+ connect(profile, &QWebEngineProfile::downloadRequested,
+ &m_downloadManagerWidget, &DownloadManagerWidget::downloadRequested);
+
+ connect(webEngine->page(), SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)),
+ this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest)));
+}
+
+void MainWindow::fullScreenRequested(QWebEngineFullScreenRequest request)
+{
+ if (request.toggleOn())
+ {
+ webEngine->showFullScreen();
+ this->showFullScreen();
+ request.accept();
+ } else {
+ webEngine->showNormal();
+ this->showNormal();
+ request.accept();
+ }
+}
+
+void MainWindow::handleWebViewTitleChanged(QString title)
+{
+ setWindowTitle(title);
+
+ if (notificationsTitleRegExp.exactMatch(title))
+ {
+ trayIcon->setIcon(trayIconUnread);
+ setWindowIcon(trayIconUnread);
+ }
+ else
+ {
+ trayIcon->setIcon(trayIconRead);
+ setWindowIcon(trayIconRead);
+ }
+}
+
+void MainWindow::handleLoadStarted()
+{
+ statusBar->show();
+}
+
+void MainWindow::handleLoadFinished(bool loaded)
+{
+ if(loaded){
+ updatePageTheme();
+ }
+}
+
+void MainWindow::handleLoadProgress(int progress)
+{
+ if (progress >= 100)
+ {
+ statusBar->hide();
+ }
+ else
+ {
+ statusBar->showMessage("Loading "+QString::number(progress)+"%");
+ }
+}
+
+//unused direct method to download file without having entry in download manager
+void MainWindow::handleDownloadRequested(QWebEngineDownloadItem *download)
+{
+ QFileDialog dialog;
+ dialog.setParent(this);
+ dialog.setAcceptMode(QFileDialog::AcceptMode::AcceptSave);
+ dialog.setFileMode(QFileDialog::FileMode::AnyFile);
+ QString suggestedFileName = QUrl(download->path()).fileName();
+ dialog.selectFile(suggestedFileName);
+
+ if (dialog.exec() && dialog.selectedFiles().size() > 0)
+ {
+ download->setPath(dialog.selectedFiles().at(0));
+ download->accept();
+ }
+}
+
+void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
+{
+ Q_UNUSED(reason);
+ if (isVisible()) {
+ hide();
+ } else {
+ showNormal();
+ }
+}
+
+void MainWindow::messageClicked()
+{
+ if (isVisible()) {
+ hide();
+ } else {
+ showNormal();
+ }
+}
+
+void MainWindow::doAppReload()
+{
+ if(this->webEngine->page()){
+ this->webEngine->page()->disconnect();
+ }
+ createWebPage(false);
+}
+
+void MainWindow::doReload()
+{
+ this->webEngine->triggerPageAction(QWebEnginePage::ReloadAndBypassCache, false);
+}
+
+void MainWindow::toggleMute(const bool checked)
+{
+ this->webEngine->page()->setAudioMuted(checked);
+}
+
+// get value of page theme when page is loaded
+QString MainWindow::getPageTheme()
+{
+ static QString theme = "web"; //implies light
+ if(webEngine && webEngine->page())
+ {
+ webEngine->page()->runJavaScript(
+ "document.querySelector('body').className;",
+ [](const QVariant &result){
+ theme = result.toString();
+ }
+ );
+ }
+ return theme;
+}
diff --git a/src/mainwindow.h b/src/mainwindow.h
new file mode 100644
index 0000000..367d18a
--- /dev/null
+++ b/src/mainwindow.h
@@ -0,0 +1,107 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QAction>
+#include <QApplication>
+#include <QFileDialog>
+#include <QGraphicsOpacityEffect>
+#include <QIcon>
+#include <QMainWindow>
+#include <QMenu>
+#include <QMessageBox>
+#include <QProgressBar>
+#include <QRegExp>
+#include <QSettings>
+#include <QStatusBar>
+#include <QStyle>
+#include <QStyleFactory>
+#include <QSystemTrayIcon>
+#include <QWebEngineCookieStore>
+#include <QWebEngineFullScreenRequest>
+#include <QWebEngineProfile>
+#include <QWebEngineView>
+#include <QWebEngineSettings>
+
+#include "notificationpopup.h"
+#include "requestinterceptor.h"
+#include "settingswidget.h"
+#include "webenginepage.h"
+
+#include "downloadmanagerwidget.h"
+
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+public:
+ explicit MainWindow(QWidget *parent = nullptr);
+public slots:
+ void updateWindowTheme();
+ void updatePageTheme();
+protected:
+ void closeEvent(QCloseEvent *event) override;
+
+private:
+ QPalette lightPalette;
+ void createActions();
+ void createStatusBar();
+ void createTrayIcon();
+ void createWebEngine();
+
+ QSettings settings;
+
+ QRegExp notificationsTitleRegExp;
+ QIcon trayIconRead;
+ QIcon trayIconUnread;
+
+ QAction *reloadAction;
+ QAction *minimizeAction;
+ QAction *restoreAction;
+ QAction *aboutAction;
+ QAction *settingsAction;
+ QAction *quitAction;
+
+ QMenu *trayIconMenu;
+ QSystemTrayIcon *trayIcon;
+
+ QWebEngineView *webEngine;
+ QStatusBar *statusBar;
+
+
+ SettingsWidget * settingsWidget = nullptr;
+
+ //void reload();
+
+ DownloadManagerWidget m_downloadManagerWidget;
+ QScopedPointer<QWebEngineProfile> m_otrProfile;
+
+
+private slots:
+
+ void readSettings();
+ void handleWebViewTitleChanged(QString title);
+ void handleLoadStarted();
+ void handleLoadProgress(int progress);
+ void handleDownloadRequested(QWebEngineDownloadItem *download);
+ void iconActivated(QSystemTrayIcon::ActivationReason reason);
+ void messageClicked();
+ void doReload();
+ void showAbout();
+ void notify(QString title, QString message);
+ void showSettings();
+ void handleCookieAdded(const QNetworkCookie &cookie);
+ void handleLoadFinished(bool loaded);
+
+ QString getPageTheme();
+ void toggleMute(const bool checked);
+ void doAppReload();
+ void askToReloadPage();
+ void updateSettingsUserAgentWidget();
+ void fullScreenRequested(QWebEngineFullScreenRequest request);
+
+ void createWebPage(bool offTheRecord =false);
+ void init_settingWidget();
+ void init_globalWebProfile();
+};
+
+#endif // MAINWINDOW_H
diff --git a/src/notificationpopup.h b/src/notificationpopup.h
new file mode 100644
index 0000000..2246129
--- /dev/null
+++ b/src/notificationpopup.h
@@ -0,0 +1,144 @@
+#ifndef NOTIFICATIONPOPUP_H
+#define NOTIFICATIONPOPUP_H
+
+#pragma once
+
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QMouseEvent>
+#include <QPushButton>
+#include <QSpacerItem>
+#include <QTimer>
+#include <QVBoxLayout>
+#include <QWebEngineNotification>
+#include <QPropertyAnimation>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QDebug>
+#include "widgets/scrolltext/scrolltext.h"
+
+#include <memory>
+
+class NotificationPopup : public QWidget
+{
+ Q_OBJECT
+
+ QLabel m_icon, m_title; ScrollText m_message;
+ std::unique_ptr<QWebEngineNotification> notification;
+
+public:
+ NotificationPopup(QWidget *parent) : QWidget(parent)
+ {
+ setWindowFlags(Qt::ToolTip);
+ auto rootLayout = new QHBoxLayout(this);
+
+ rootLayout->addWidget(&m_icon);
+
+ auto bodyLayout = new QVBoxLayout;
+ rootLayout->addLayout(bodyLayout);
+
+ auto titleLayout = new QHBoxLayout;
+ bodyLayout->addLayout(titleLayout);
+
+ titleLayout->addWidget(&m_title);
+ titleLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
+
+ auto close = new QPushButton(tr("Close"));
+ titleLayout->addWidget(close);
+ connect(close, &QPushButton::clicked, this, &NotificationPopup::onClosed);
+
+ bodyLayout->addWidget(&m_message);
+ adjustSize();
+ }
+
+ void present(QString title,QString message,const QPixmap image)
+ {
+ m_title.setText("<b>" + title + "</b>");
+ m_message.setText(message);
+ m_icon.setPixmap(image.scaledToHeight(m_icon.height(),Qt::SmoothTransformation));
+
+ this->adjustSize();
+ qApp->processEvents();
+
+ int x = QApplication::desktop()->geometry().width()-(this->width()+10);
+ int y = 40;
+
+
+ QTimer::singleShot(10000,this,[=](){
+ onClosed();
+ });
+
+ QPropertyAnimation *a = new QPropertyAnimation(this,"pos");
+ a->setDuration(200);
+ a->setStartValue(QApplication::desktop()->mapToGlobal(QPoint(x+this->width(),y)));
+ a->setEndValue(QApplication::desktop()->mapToGlobal(QPoint(x,y)));
+ a->setEasingCurve(QEasingCurve::Linear);
+ a->start(QPropertyAnimation::DeleteWhenStopped);
+
+ this->show();
+
+ }
+
+ void present(std::unique_ptr<QWebEngineNotification> &newNotification)
+ {
+ if (notification) {
+ notification->close();
+ notification.reset();
+ }
+
+ notification.swap(newNotification);
+
+ m_title.setText("<b>" + notification->title() + "</b>");
+ m_message.setText(notification->message());
+ m_icon.setPixmap(QPixmap::fromImage(notification->icon()).scaledToHeight(m_icon.height()));
+
+ //show();
+ notification->show();
+
+ connect(notification.get(), &QWebEngineNotification::closed, this, &NotificationPopup::onClosed);
+ QTimer::singleShot(10000, notification.get(), [&] () { onClosed(); });
+
+ this->adjustSize();
+ qApp->processEvents();
+
+ int x = QApplication::desktop()->geometry().width()-(this->width()+10);
+ int y = 40;
+
+ QPropertyAnimation *a = new QPropertyAnimation(this,"pos");
+ a->setDuration(200);
+ a->setStartValue(QApplication::desktop()->mapToGlobal(QPoint(x+this->width(),y)));
+ a->setEndValue(QApplication::desktop()->mapToGlobal(QPoint(x,y)));
+ a->setEasingCurve(QEasingCurve::Linear);
+ a->start(QPropertyAnimation::DeleteWhenStopped);
+ this->show();
+ }
+
+protected slots:
+ void onClosed()
+ {
+ hide();
+ if(notification){
+ notification->close();
+ notification.reset();
+ }else{
+ this->deleteLater();
+ }
+ }
+
+protected:
+ void mouseReleaseEvent(QMouseEvent *event) override
+ {
+ QWidget::mouseReleaseEvent(event);
+ if (event->button() == Qt::LeftButton) {
+ qDebug()<<"noti clicked";
+ emit notification_clicked();
+ if(notification )
+ notification->click();
+ onClosed();
+ }
+ }
+signals:
+ void notification_clicked();
+};
+
+#endif // NOTIFICATIONPOPUP_H
diff --git a/src/passworddialog.ui b/src/passworddialog.ui
new file mode 100644
index 0000000..7f9bf99
--- /dev/null
+++ b/src/passworddialog.ui
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PasswordDialog</class>
+ <widget class="QDialog" name="PasswordDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>399</width>
+ <height>148</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Authentication Required</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" columnstretch="0,0" columnminimumwidth="0,0">
+ <item row="0" column="0">
+ <widget class="QLabel" name="m_iconLabel">
+ <property name="text">
+ <string>Icon</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="m_infoLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Info</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="userLabel">
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="m_userNameLineEdit"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="passwordLabel">
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="m_passwordLineEdit">
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <zorder>userLabel</zorder>
+ <zorder>m_userNameLineEdit</zorder>
+ <zorder>passwordLabel</zorder>
+ <zorder>m_passwordLineEdit</zorder>
+ <zorder>buttonBox</zorder>
+ <zorder>m_iconLabel</zorder>
+ <zorder>m_infoLabel</zorder>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>PasswordDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>PasswordDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/requestinterceptor.h b/src/requestinterceptor.h
new file mode 100644
index 0000000..95f2d48
--- /dev/null
+++ b/src/requestinterceptor.h
@@ -0,0 +1,41 @@
+#ifndef REQUESTINTERCEPTOR_H
+#define REQUESTINTERCEPTOR_H
+#include <QWebEnginePage>
+#include <QDebug>
+#include <QObject>
+#include <QWebEngineView>
+#include <QApplication>
+#include <QWebEngineUrlRequestInfo>
+#include <QWebEngineUrlRequestInterceptor>
+#include <QSettings>
+
+class RequestInterceptor : public QWebEngineUrlRequestInterceptor
+{
+ Q_OBJECT
+
+signals:
+ void blocked(QString adUrl);
+private:
+ QSettings m_settings;
+
+public:
+ RequestInterceptor(QObject *parent = nullptr) : QWebEngineUrlRequestInterceptor(parent)
+ {
+ }
+
+ void interceptRequest(QWebEngineUrlRequestInfo &info)
+ {
+ QString reqUrlStr = info.requestUrl().toString();
+
+// if(reqUrlStr.contains("mms-type=video")|| reqUrlStr.contains("stream/video?key")){
+// if(m_settings.value("disableVideos",false).toBool()){
+// info.block(true);
+// qDebug()<<"INTERCEPTOR: Blocked video - "<<reqUrlStr;
+// }
+// }else{
+// qDebug()<<"INTERCEPTOR:"<<reqUrlStr;
+// }
+ qDebug()<<"INTERCEPTOR:"<<reqUrlStr;
+ }
+};
+#endif // REQUESTINTERCEPTOR_H
diff --git a/src/settingswidget.cpp b/src/settingswidget.cpp
new file mode 100644
index 0000000..7f9a257
--- /dev/null
+++ b/src/settingswidget.cpp
@@ -0,0 +1,189 @@
+#include "settingswidget.h"
+#include "ui_settingswidget.h"
+
+#include <QMessageBox>
+
+extern QString defaultUserAgentStr;
+
+SettingsWidget::SettingsWidget(QWidget *parent, QString engineCachePath, QString enginePersistentStoragePath) :
+ QWidget(parent),
+ ui(new Ui::SettingsWidget)
+{
+ ui->setupUi(this);
+
+ ui->disableVideosCheckBox->hide();//impl later
+
+ this->engineCachePath = engineCachePath;
+ this->enginePersistentStoragePath = enginePersistentStoragePath;
+
+ ui->closeButtonActionComboBox->setCurrentIndex(settings.value("closeButtonActionCombo",0).toInt());
+ ui->notificationCheckBox->setChecked(settings.value("disableNotificationPopups",false).toBool());
+ ui->muteAudioCheckBox->setChecked(settings.value("muteAudio",false).toBool());
+ ui->autoPlayMediaCheckBox->setChecked(settings.value("autoPlayMedia",false).toBool());
+ ui->themeComboBox->setCurrentText(utils::toCamelCase(settings.value("windowTheme","light").toString()));
+ ui->userAgentLineEdit->setText(settings.value("useragent",defaultUserAgentStr).toString());
+
+ applyThemeQuirks();
+
+ ui->setUserAgent->setEnabled(false);
+}
+
+SettingsWidget::~SettingsWidget()
+{
+ delete ui;
+}
+
+void SettingsWidget::refresh()
+{
+ ui->cacheSize->setText(utils::refreshCacheSize(cachePath()));
+ ui->cookieSize->setText(utils::refreshCacheSize(persistentStoragePath()));
+}
+
+void SettingsWidget::updateDefaultUAButton(const QString engineUA)
+{
+ bool isDefault = QString::compare(engineUA,defaultUserAgentStr,Qt::CaseInsensitive) == 0;
+ ui->defaultUserAgentButton->setEnabled(!isDefault);
+
+ if(ui->userAgentLineEdit->text().trimmed().isEmpty()){
+ ui->userAgentLineEdit->setText(engineUA);
+ }
+}
+
+
+QString SettingsWidget::cachePath()
+{
+ return engineCachePath;
+}
+
+QString SettingsWidget::persistentStoragePath()
+{
+ return enginePersistentStoragePath;
+}
+
+void SettingsWidget::on_deleteCache_clicked()
+{
+ QMessageBox msgBox;
+ msgBox.setText("This will delete app cache! Application cache makes application load faster.");
+ msgBox.setIconPixmap(QPixmap(":/icons/information-line.png").scaled(42,42,Qt::KeepAspectRatio,Qt::SmoothTransformation));
+
+ msgBox.setInformativeText("Delete Application cache ?");
+ msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+ msgBox.setDefaultButton(QMessageBox::No);
+ int ret = msgBox.exec();
+ switch (ret) {
+ case QMessageBox::Yes:{
+ utils::delete_cache(this->cachePath());
+ refresh();
+ break;
+ }
+ case QMessageBox::No:
+ break;
+ }
+}
+
+void SettingsWidget::on_deletePersistentData_clicked()
+{
+ QMessageBox msgBox;
+ msgBox.setText("This will delete app Persistent Data ! Persistent data includes persistent cookies, HTML5 local storage, and visited links.");
+ msgBox.setIconPixmap(QPixmap(":/icons/information-line.png").scaled(42,42,Qt::KeepAspectRatio,Qt::SmoothTransformation));
+ msgBox.setInformativeText("Delete Application Cookies ?");
+ msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+ msgBox.setDefaultButton(QMessageBox::No);
+ int ret = msgBox.exec();
+ switch (ret) {
+ case QMessageBox::Yes:{
+ utils::delete_cache(this->persistentStoragePath());
+ refresh();
+ break;
+ }
+ case QMessageBox::No:
+ break;
+ }
+}
+
+void SettingsWidget::on_notificationCheckBox_toggled(bool checked)
+{
+ settings.setValue("disableNotificationPopups",checked);
+}
+
+void SettingsWidget::on_themeComboBox_currentTextChanged(const QString &arg1)
+{
+ applyThemeQuirks();
+ settings.setValue("windowTheme",QString(arg1).toLower());
+ emit updateWindowTheme();
+ emit updatePageTheme();
+}
+
+
+void SettingsWidget::applyThemeQuirks()
+{
+ //little quirks
+ if(QString::compare(ui->themeComboBox->currentText(),"dark",Qt::CaseInsensitive)==0)
+ {
+ ui->label_7->setStyleSheet("color:#c2c5d1;padding: 0px 8px 0px 8px;background:transparent;");
+ }else{
+ ui->label_7->setStyleSheet("color:#1e1f21;padding: 0px 8px 0px 8px;background:transparent;");
+ }
+}
+
+
+void SettingsWidget::on_muteAudioCheckBox_toggled(bool checked)
+{
+ settings.setValue("muteAudio",checked);
+ emit muteToggled(checked);
+}
+
+void SettingsWidget::on_autoPlayMediaCheckBox_toggled(bool checked)
+{
+ settings.setValue("autoPlayMedia",checked);
+ emit autoPlayMediaToggled(checked);
+}
+
+void SettingsWidget::on_defaultUserAgentButton_clicked()
+{
+ ui->userAgentLineEdit->setText(defaultUserAgentStr);
+ emit userAgentChanged(ui->userAgentLineEdit->text());
+}
+
+void SettingsWidget::on_userAgentLineEdit_textChanged(const QString &arg1)
+{
+ bool isDefault = QString::compare(arg1.trimmed(),defaultUserAgentStr,Qt::CaseInsensitive) == 0;
+ bool isPrevious= QString::compare(arg1.trimmed(),settings.value("useragent",defaultUserAgentStr).toString(),Qt::CaseInsensitive) == 0;
+
+ if(isDefault == false && arg1.trimmed().isEmpty()==false)
+ {
+ ui->defaultUserAgentButton->setEnabled(false);
+ ui->setUserAgent->setEnabled(false);
+ }
+ if(isPrevious == false && arg1.trimmed().isEmpty() == false)
+ {
+ ui->setUserAgent->setEnabled(true);
+ ui->defaultUserAgentButton->setEnabled(true);
+ }
+ if(isPrevious){
+ ui->defaultUserAgentButton->setEnabled(true);
+ ui->setUserAgent->setEnabled(false);
+ }
+}
+
+void SettingsWidget::on_setUserAgent_clicked()
+{
+ if(ui->userAgentLineEdit->text().trimmed().isEmpty()){
+ QMessageBox::information(this,QApplication::applicationName()+"| Error",
+ "Cannot set an empty UserAgent String.");
+ return;
+ }
+ emit userAgentChanged(ui->userAgentLineEdit->text());
+}
+
+
+
+void SettingsWidget::on_disableVideosCheckBox_toggled(bool checked)
+{
+ settings.setValue("disableVideos",checked);
+}
+
+void SettingsWidget::on_closeButtonActionComboBox_currentIndexChanged(int index)
+{
+ settings.setValue("closeButtonActionCombo",index);
+}
diff --git a/src/settingswidget.h b/src/settingswidget.h
new file mode 100644
index 0000000..a21a410
--- /dev/null
+++ b/src/settingswidget.h
@@ -0,0 +1,66 @@
+#ifndef SETTINGSWIDGET_H
+#define SETTINGSWIDGET_H
+
+#include <QWidget>
+#include <QSettings>
+#include "utils.h"
+
+
+
+namespace Ui {
+class SettingsWidget;
+}
+
+class SettingsWidget : public QWidget
+{
+ Q_OBJECT
+
+signals:
+ void updateWindowTheme();
+ void updatePageTheme();
+ void muteToggled(const bool checked);
+ void autoPlayMediaToggled(const bool checked);
+ void userAgentChanged(QString userAgentStr);
+
+public:
+ explicit SettingsWidget(QWidget *parent = nullptr,QString engineCachePath = "",
+ QString enginePersistentStoragePath = "");
+ ~SettingsWidget();
+
+public slots:
+ void refresh();
+ void updateDefaultUAButton(const QString engineUA);
+private slots:
+ QString cachePath();
+ QString persistentStoragePath();
+
+ void on_deleteCache_clicked();
+
+ void on_deletePersistentData_clicked();
+
+ void on_notificationCheckBox_toggled(bool checked);
+
+ void on_themeComboBox_currentTextChanged(const QString &arg1);
+
+ void applyThemeQuirks();
+ void on_muteAudioCheckBox_toggled(bool checked);
+
+ void on_defaultUserAgentButton_clicked();
+
+ void on_userAgentLineEdit_textChanged(const QString &arg1);
+
+ void on_setUserAgent_clicked();
+
+ void on_autoPlayMediaCheckBox_toggled(bool checked);
+
+ void on_disableVideosCheckBox_toggled(bool checked);
+
+ void on_closeButtonActionComboBox_currentIndexChanged(int index);
+
+private:
+ Ui::SettingsWidget *ui;
+ QString engineCachePath,enginePersistentStoragePath;
+ QSettings settings;
+};
+
+#endif // SETTINGSWIDGET_H
diff --git a/src/settingswidget.ui b/src/settingswidget.ui
new file mode 100644
index 0000000..7318e5c
--- /dev/null
+++ b/src/settingswidget.ui
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SettingsWidget</class>
+ <widget class="QWidget" name="SettingsWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>539</width>
+ <height>447</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignTop">
+ <widget class="QWidget" name="headerWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background-color: qlineargradient(spread:reflect, x1:0, y1:1, x2:1, y2:1, stop:0 rgba(63, 129, 216, 255), stop:0.328358 rgba(63, 129, 216, 111), stop:0.61194 rgba(63, 129, 216, 48), stop:0.895522 rgba(63, 129, 216, 15));
+</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <item>
+ <widget class="QLabel" name="label_6">
+ <property name="styleSheet">
+ <string notr="true">background:transparent;
+padding: 8px 0px 8px 8px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="icons.qrc">:/icons/app/icon-64.png</pixmap>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_7">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>16</pointsize>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QLabel{
+padding: 0px 8px 0px 8px;
+background:transparent;
+}</string>
+ </property>
+ <property name="text">
+ <string>Application Settings</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="verticalWidget" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox_8">
+ <property name="title">
+ <string>General settings</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="3" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Default close button action</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="closeButtonActionComboBox">
+ <item>
+ <property name="text">
+ <string>Minimize to tray</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Quit Application</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="4" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Theme</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="themeComboBox">
+ <item>
+ <property name="text">
+ <string>Dark</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Light</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="5" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>User Agent</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="userAgentLineEdit"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="setUserAgent">
+ <property name="text">
+ <string>Set</string>
+ </property>
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/categories/utilities.png</normaloff>:/icons/categories/utilities.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="defaultUserAgentButton">
+ <property name="text">
+ <string>Default</string>
+ </property>
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/refresh-line.png</normaloff>:/icons/refresh-line.png</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="notificationCheckBox">
+ <property name="text">
+ <string>Disable Notifications PopUp</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="muteAudioCheckBox">
+ <property name="text">
+ <string>Mute Audio</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="autoPlayMediaCheckBox">
+ <property name="text">
+ <string>Disable Auto Playback of Media</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="disableVideosCheckBox">
+ <property name="text">
+ <string>Disable loading videos</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_7">
+ <property name="title">
+ <string>Application Storage and Other Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="2">
+ <widget class="QPushButton" name="deleteCache">
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/delete-bin-3-line.png</normaloff>:/icons/delete-bin-3-line.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Application Cache</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="cacheSize">
+ <property name="text">
+ <string>-</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <italic>false</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>Property</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_13">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <italic>false</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>Option</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_12">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <italic>false</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>Size</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Persistent data includes persistent cookies, HTML5 local storage, and visited links.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Persistent data</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="cookieSize">
+ <property name="text">
+ <string>-</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QPushButton" name="deletePersistentData">
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/delete-bin-3-line.png</normaloff>:/icons/delete-bin-3-line.png</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="icons.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/utils.cpp b/src/utils.cpp
new file mode 100644
index 0000000..543b837
--- /dev/null
+++ b/src/utils.cpp
@@ -0,0 +1,162 @@
+#include "utils.h"
+#include <QDateTime>
+#include <QRegularExpression>
+
+utils::utils(QObject *parent) : QObject(parent)
+{
+ setParent(parent);
+}
+
+utils::~utils()
+{
+ this->deleteLater();
+}
+
+//calculate dir size
+quint64 utils::dir_size(const QString & directory)
+{
+ quint64 sizex = 0;
+ QFileInfo str_info(directory);
+ if (str_info.isDir())
+ {
+ QDir dir(directory);
+ QFileInfoList list = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot);
+ for (int i = 0; i < list.size(); ++i)
+ {
+ QFileInfo fileInfo = list.at(i);
+ if(fileInfo.isDir())
+ {
+ sizex += dir_size(fileInfo.absoluteFilePath());
+ }
+ else{
+ sizex += fileInfo.size();
+ }
+ }
+ }
+ return sizex;
+}
+
+//get the size of cache folder in human readble format
+QString utils::refreshCacheSize(const QString cache_dir)
+{
+ qint64 cache_size = dir_size(cache_dir);
+ QString cache_unit;
+ if(cache_size > 1024*1024*1024)
+ {
+ cache_size = cache_size/(1024*1024*1024);
+ cache_unit = " GB";
+ }
+ if(cache_size > 1024*1024)
+ {
+ cache_size = cache_size/(1024*1024);
+ cache_unit = " MB";
+ }
+ else if(cache_size > 1024)
+ {
+ cache_size = cache_size/(1024);
+ cache_unit = " kB";
+ }
+ else
+ {
+ cache_unit = " B";
+ }
+ return QString::number(cache_size) + cache_unit;
+}
+
+bool utils::delete_cache(const QString cache_dir)
+{
+ bool deleted = QDir(cache_dir).removeRecursively();
+ QDir(cache_dir).mkpath(cache_dir);
+ return deleted;
+}
+
+//returns string with first letter capitalized
+QString utils::toCamelCase(const QString& s)
+{
+ QStringList parts = s.split(' ', QString::SkipEmptyParts);
+ for (int i = 0; i < parts.size(); ++i)
+ parts[i].replace(0, 1, parts[i][0].toUpper());
+ return parts.join(" ");
+}
+
+QString utils::generateRandomId(int length){
+
+ QString str = QUuid::createUuid().toString();
+ str.remove(QRegularExpression("{|}|-"));
+ if(str.length()<length){
+ while(str.length() != length){
+ int required_char = length - str.length();
+ str = str + str.append(genRand(required_char));
+ }
+ }
+ if(str.length()>length){
+ while(str.length() != length){
+ str = str.remove(str.length()-1,1);
+ }
+ }
+ return str;
+}
+
+QString utils::genRand(int length)
+{
+ QDateTime cd = QDateTime::currentDateTime();
+ const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"+QString::number(cd.currentMSecsSinceEpoch()).remove(QRegExp("[^a-zA-Z\\d\\s]")));
+
+ const int randomStringLength = length;
+ QString randomString;
+ qsrand(cd.toTime_t());
+ for(int i=0; i<randomStringLength; ++i)
+ {
+ int index = qrand() % possibleCharacters.length();
+ QChar nextChar = possibleCharacters.at(index);
+ randomString.append(nextChar);
+ }
+ randomString = randomString.trimmed().simplified().remove(" ");
+ return randomString;
+}
+
+QString utils::convertSectoDay(qint64 secs)
+{
+ int day = secs / (24 * 3600);
+
+ secs = secs % (24 * 3600);
+ int hour = secs / 3600;
+
+ secs %= 3600;
+ int minutes = secs / 60 ;
+
+ secs %= 60;
+ int seconds = secs;
+
+ QString days = QString::number(day) + " " + "days " + QString::number(hour)
+ + " " + "hours " + QString::number(minutes) + " "
+ + "minutes " + QString::number(seconds) + " "
+ + "seconds ";
+ return days;
+}
+
+//static on demand path maker
+QString utils::returnPath(QString pathname)
+{
+ QString _data_path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
+ if(!QDir(_data_path+"/"+pathname).exists()){
+ QDir d(_data_path+"/"+pathname);
+ d.mkpath(_data_path+"/"+pathname);
+ }
+ return _data_path+"/"+pathname+"/";
+}
+
+
+QString utils::htmlToPlainText(QString str){
+ QString out;
+ QTextDocument text;
+ text.setHtml(str);
+ out = text.toPlainText();
+ text.deleteLater();
+ return out .replace("\\\"","'")
+ .replace("&amp;","&")
+ .replace("&gt;",">")
+ .replace("&lt;","<")
+ .replace("&#39;","'");
+}
+
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..9d2520f
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,85 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <QObject>
+#include <QDebug>
+#include <QFileInfo>
+#include <QStandardPaths>
+#include <QDir>
+#include <QTextDocument>
+#include <QUuid>
+
+class utils : public QObject
+{
+ Q_OBJECT
+
+public:
+ utils(QObject* parent=0);
+ virtual ~utils();
+public slots:
+ static QString refreshCacheSize(const QString cache_dir);
+ static bool delete_cache(const QString cache_dir);
+ static QString toCamelCase(const QString &s);
+ static QString generateRandomId(int length);
+ static QString genRand(int length);
+ static QString convertSectoDay(qint64 secs);
+ static QString returnPath(QString pathname);
+
+ static QString EncodeXML ( const QString& encodeMe ){
+
+ QString temp;
+ int length = encodeMe.size();
+ for (int index = 0; index < length; index++)
+ {
+ QChar character(encodeMe.at(index));
+
+ switch (character.unicode())
+ {
+ case '&':
+ temp += "&amp;"; break;
+
+ case '\'':
+ temp += "&apos;"; break;
+
+ case '"':
+ temp += "&quot;"; break;
+
+ case '<':
+ temp += "&lt;"; break;
+
+ case '>':
+ temp += "&gt;"; break;
+
+ default:
+ temp += character;
+ break;
+ }
+ }
+
+ return temp;
+ }
+
+ static QString DecodeXML ( const QString& decodeMe ) {
+
+ QString temp(decodeMe);
+
+ temp.replace("&amp;", "&");
+ temp.replace("&apos;", "'");
+ temp.replace("&quot;", "\"");
+ temp.replace("&lt;", "<");
+ temp.replace("&gt;", ">");
+
+ return temp;
+ }
+
+ static QString htmlToPlainText(QString str);
+
+private slots:
+ //use refreshCacheSize
+ static quint64 dir_size(const QString &directory);
+
+
+
+};
+
+#endif // UTILS_H
diff --git a/src/webenginepage.cpp b/src/webenginepage.cpp
new file mode 100644
index 0000000..4478d6d
--- /dev/null
+++ b/src/webenginepage.cpp
@@ -0,0 +1,252 @@
+#include "webenginepage.h"
+
+#include <QIcon>
+#include <QStyle>
+
+
+WebEnginePage::WebEnginePage(QWebEngineProfile *profile, QObject *parent)
+ : QWebEnginePage(profile, parent)
+{
+ // Connect signals and slots
+ connect(this, &QWebEnginePage::featurePermissionRequested,
+ this, &WebEnginePage::handleFeaturePermissionRequested);
+ connect(this, &QWebEnginePage::loadFinished,
+ this, &WebEnginePage::handleLoadFinished);
+ profile->setHttpUserAgent(profile->httpUserAgent().replace("QtWebEngine/5.13.0",""));
+ connect(this, &QWebEnginePage::authenticationRequired, this, &WebEnginePage::handleAuthenticationRequired);
+ connect(this, &QWebEnginePage::featurePermissionRequested, this, &WebEnginePage::handleFeaturePermissionRequested);
+ connect(this, &QWebEnginePage::proxyAuthenticationRequired, this, &WebEnginePage::handleProxyAuthenticationRequired);
+ connect(this, &QWebEnginePage::registerProtocolHandlerRequested, this, &WebEnginePage::handleRegisterProtocolHandlerRequested);
+
+#if !defined(QT_NO_SSL) || QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+ connect(this, &QWebEnginePage::selectClientCertificate, this, &WebEnginePage::handleSelectClientCertificate);
+#endif
+}
+
+bool WebEnginePage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame)
+{
+ qDebug() << "Navigation request: [" + url.toDisplayString() + "] " + type;
+
+ if (QWebEnginePage::NavigationType::NavigationTypeLinkClicked == type)
+ {
+ QDesktopServices::openUrl(url);
+ return false;
+ }
+
+ return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame);
+}
+
+QWebEnginePage *WebEnginePage::createWindow(QWebEnginePage::WebWindowType type)
+{
+ Q_UNUSED(type);
+ return new WebEnginePage(this->profile());
+}
+
+
+inline QString questionForFeature(QWebEnginePage::Feature feature)
+{
+ switch (feature) {
+ case QWebEnginePage::Geolocation:
+ return WebEnginePage::tr("Allow %1 to access your location information?");
+ case QWebEnginePage::MediaAudioCapture:
+ return WebEnginePage::tr("Allow %1 to access your microphone?");
+ case QWebEnginePage::MediaVideoCapture:
+ return WebEnginePage::tr("Allow %1 to access your webcam?");
+ case QWebEnginePage::MediaAudioVideoCapture:
+ return WebEnginePage::tr("Allow %1 to access your microphone and webcam?");
+ case QWebEnginePage::MouseLock:
+ return WebEnginePage::tr("Allow %1 to lock your mouse cursor?");
+ case QWebEnginePage::DesktopVideoCapture:
+ return WebEnginePage::tr("Allow %1 to capture video of your desktop?");
+ case QWebEnginePage::DesktopAudioVideoCapture:
+ return WebEnginePage::tr("Allow %1 to capture audio and video of your desktop?");
+ case QWebEnginePage::Notifications:
+ return WebEnginePage::tr("Allow %1 to show notification on your desktop?");
+ }
+ return QString();
+}
+
+void WebEnginePage::handleFeaturePermissionRequested(const QUrl &securityOrigin, Feature feature)
+{
+ QString title = tr("Permission Request");
+ QString question = questionForFeature(feature).arg(securityOrigin.host());
+ if (!question.isEmpty() && QMessageBox::question(view()->window(), title, question) == QMessageBox::Yes)
+ setFeaturePermission(securityOrigin, feature, PermissionGrantedByUser);
+ else
+ setFeaturePermission(securityOrigin, feature, PermissionDeniedByUser);
+}
+
+void WebEnginePage::handleLoadFinished(bool ok)
+{
+ Q_UNUSED(ok);
+ setFeaturePermission(
+ QUrl("https://web.whatsapp.com/"),
+ QWebEnginePage::Feature::Notifications,
+ QWebEnginePage::PermissionPolicy::PermissionGrantedByUser
+ );
+ setFeaturePermission(
+ QUrl("https://web.whatsapp.com/"),
+ QWebEnginePage::Feature::MediaAudioCapture,
+ QWebEnginePage::PermissionPolicy::PermissionGrantedByUser
+ );
+ setFeaturePermission(
+ QUrl("https://web.whatsapp.com/"),
+ QWebEnginePage::Feature::MediaVideoCapture,
+ QWebEnginePage::PermissionPolicy::PermissionGrantedByUser
+ );
+ setFeaturePermission(
+ QUrl("https://web.whatsapp.com/"),
+ QWebEnginePage::Feature::MediaAudioVideoCapture,
+ QWebEnginePage::PermissionPolicy::PermissionGrantedByUser
+ );
+}
+
+void WebEnginePage::fullScreenRequestedByPage(QWebEngineFullScreenRequest request)
+{
+ qDebug()<<"Fullscreen";
+ request.accept();
+}
+
+QStringList WebEnginePage::chooseFiles(QWebEnginePage::FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes)
+{
+ qDebug()<<mode<<oldFiles<<acceptedMimeTypes;
+ QFileDialog::FileMode dialogMode;
+ if(mode == QWebEnginePage::FileSelectOpen) {
+ dialogMode = QFileDialog::ExistingFile;
+ } else {
+ dialogMode = QFileDialog::ExistingFiles;
+ }
+
+ QFileDialog* dialog = new QFileDialog();
+
+ dialog->setFileMode(dialogMode);
+ dialog->setOption(QFileDialog::DontUseNativeDialog,true);
+
+ QStringList mimeFilters = acceptedMimeTypes;
+
+ if(acceptedMimeTypes.contains("image/*")){
+ foreach(QByteArray mime,QImageReader::supportedImageFormats()){
+ mimeFilters.append("image/"+mime);
+ }
+ }
+
+ dialog->setMimeTypeFilters(mimeFilters);
+
+ QStringList selectedFiles;
+ if(dialog->exec()) {
+ selectedFiles = dialog->selectedFiles();
+ }
+
+ return selectedFiles;
+}
+
+bool WebEnginePage::certificateError(const QWebEngineCertificateError &error)
+{
+ QWidget *mainWindow = view()->window();
+ if (error.isOverridable()) {
+ QDialog dialog(mainWindow);
+ dialog.setModal(true);
+ dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ Ui::CertificateErrorDialog certificateDialog;
+ certificateDialog.setupUi(&dialog);
+ certificateDialog.m_iconLabel->setText(QString());
+ QIcon icon(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, mainWindow));
+ certificateDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32));
+ certificateDialog.m_errorLabel->setText(error.errorDescription());
+ dialog.setWindowTitle(tr("Certificate Error"));
+ return dialog.exec() == QDialog::Accepted;
+ }
+
+ QMessageBox::critical(mainWindow, tr("Certificate Error"), error.errorDescription());
+ return false;
+}
+
+void WebEnginePage::handleAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth)
+{
+ QWidget *mainWindow = view()->window();
+ QDialog dialog(mainWindow);
+ dialog.setModal(true);
+ dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+ Ui::PasswordDialog passwordDialog;
+ passwordDialog.setupUi(&dialog);
+
+ passwordDialog.m_iconLabel->setText(QString());
+ QIcon icon(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, nullptr, mainWindow));
+ passwordDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32));
+
+ QString introMessage(tr("Enter username and password for \"%1\" at %2")
+ .arg(auth->realm()).arg(requestUrl.toString().toHtmlEscaped()));
+ passwordDialog.m_infoLabel->setText(introMessage);
+ passwordDialog.m_infoLabel->setWordWrap(true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ auth->setUser(passwordDialog.m_userNameLineEdit->text());
+ auth->setPassword(passwordDialog.m_passwordLineEdit->text());
+ } else {
+ // Set authenticator null if dialog is cancelled
+ *auth = QAuthenticator();
+ }
+}
+
+void WebEnginePage::handleProxyAuthenticationRequired(const QUrl &, QAuthenticator *auth, const QString &proxyHost)
+{
+ QWidget *mainWindow = view()->window();
+ QDialog dialog(mainWindow);
+ dialog.setModal(true);
+ dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+ Ui::PasswordDialog passwordDialog;
+ passwordDialog.setupUi(&dialog);
+
+ passwordDialog.m_iconLabel->setText(QString());
+ QIcon icon(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, nullptr, mainWindow));
+ passwordDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32));
+
+ QString introMessage = tr("Connect to proxy \"%1\" using:");
+ introMessage = introMessage.arg(proxyHost.toHtmlEscaped());
+ passwordDialog.m_infoLabel->setText(introMessage);
+ passwordDialog.m_infoLabel->setWordWrap(true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ auth->setUser(passwordDialog.m_userNameLineEdit->text());
+ auth->setPassword(passwordDialog.m_passwordLineEdit->text());
+ } else {
+ // Set authenticator null if dialog is cancelled
+ *auth = QAuthenticator();
+ }
+}
+
+//! [registerProtocolHandlerRequested]
+void WebEnginePage::handleRegisterProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest request)
+{
+ auto answer = QMessageBox::question(
+ view()->window(),
+ tr("Permission Request"),
+ tr("Allow %1 to open all %2 links?")
+ .arg(request.origin().host())
+ .arg(request.scheme()));
+ if (answer == QMessageBox::Yes)
+ request.accept();
+ else
+ request.reject();
+}
+//! [registerProtocolHandlerRequested]
+
+#if !defined(QT_NO_SSL) || QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+void WebEnginePage::handleSelectClientCertificate(QWebEngineClientCertificateSelection selection)
+{
+ // Just select one.
+ selection.select(selection.certificates().at(0));
+
+ qDebug() << __FUNCTION__;
+ for(QSslCertificate cert : selection.certificates()) {
+ qDebug() << cert;
+ selection.select(cert); // select the first available cert
+ break;
+ }
+ qDebug() << selection.host();
+}
+#endif
+
+
diff --git a/src/webenginepage.h b/src/webenginepage.h
new file mode 100644
index 0000000..7ec1a69
--- /dev/null
+++ b/src/webenginepage.h
@@ -0,0 +1,49 @@
+#ifndef WEBENGINEPAGE_H
+#define WEBENGINEPAGE_H
+
+
+#include <QFileDialog>
+#include <QWebEngineFullScreenRequest>
+#include <QWebEngineNotification>
+#include <QWebEngineProfile>
+
+#include <QWebEnginePage>
+#include <QDesktopServices>
+#include <QMessageBox>
+#include <QImageReader>
+#include <QWebEngineCertificateError>
+#include <QAuthenticator>
+
+#include <QWebEngineRegisterProtocolHandlerRequest>
+#include <QWebEngineFullScreenRequest>
+
+#include "ui_certificateerrordialog.h"
+#include "ui_passworddialog.h"
+
+class WebEnginePage : public QWebEnginePage
+{
+ Q_OBJECT
+public:
+ WebEnginePage(QWebEngineProfile *profile, QObject *parent = nullptr);
+
+protected:
+ bool acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) override;
+ QWebEnginePage* createWindow(QWebEnginePage::WebWindowType type) override;
+ bool certificateError(const QWebEngineCertificateError &error) override;
+ QStringList chooseFiles(FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes);
+
+public slots:
+ void handleFeaturePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature);
+ void handleLoadFinished(bool ok);
+
+private slots:
+ void handleAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth);
+ void handleProxyAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth, const QString &proxyHost);
+ void handleRegisterProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest request);
+#if !defined(QT_NO_SSL) || QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+ void handleSelectClientCertificate(QWebEngineClientCertificateSelection clientCertSelection);
+#endif
+ void fullScreenRequestedByPage(QWebEngineFullScreenRequest request);
+};
+
+#endif // WEBENGINEPAGE_H
diff --git a/src/widgets/scrolltext/scrolltext.cpp b/src/widgets/scrolltext/scrolltext.cpp
new file mode 100644
index 0000000..00696af
--- /dev/null
+++ b/src/widgets/scrolltext/scrolltext.cpp
@@ -0,0 +1,179 @@
+#include "scrolltext.h"
+#include <QPainter>
+#include <QHoverEvent>
+
+ScrollText::ScrollText(QWidget *parent) :
+ QWidget(parent), scrollPos(0)
+{
+
+ staticText.setTextFormat(Qt::PlainText);
+
+ setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
+
+ setMinimumHeight(fontMetrics().height()+10);
+ setMaximumHeight(this->minimumHeight()+6);
+
+ leftMargin = 0; //height() / 3;
+
+ setSeparator(" ");
+
+ connect(&timer, SIGNAL(timeout()), this, SLOT(timer_timeout()));
+ timer.setInterval(50);
+}
+
+QString ScrollText::text() const
+{
+ return _text;
+}
+
+void ScrollText::setText(QString text)
+{
+ _text = text;
+ updateText();
+ update();
+}
+
+QString ScrollText::separator() const
+{
+ return _separator;
+}
+
+void ScrollText::setSeparator(QString separator)
+{
+ _separator = separator;
+ updateText();
+ update();
+}
+
+void ScrollText::setLeftMargin(int pixels)
+{
+ leftMargin = pixels;
+ update();
+}
+
+void ScrollText::pause()
+{
+ if(scrollEnabled){
+ timer.stop();
+ }
+}
+
+void ScrollText::resume()
+{
+ if(scrollEnabled){
+ timer.start();
+ scrolledOnce = false;
+ }
+}
+
+void ScrollText::updateText()
+{
+ scrolledOnce = false;
+ timer.stop();
+
+ singleTextWidth = fontMetrics().horizontalAdvance(_text);
+// scrollEnabled = true;
+ scrollEnabled = (singleTextWidth > width() - leftMargin);
+
+ if(scrollEnabled)
+ {
+ scrollPos = -64;
+ staticText.setText(_text + _separator);
+ timer.start();
+ }
+ else{
+ staticText.setText(_text);
+ }
+ staticText.prepare(QTransform(), font());
+ //wholeTextSize = QSize(fontMetrics().width(staticText.text()), fontMetrics().height());
+ wholeTextSize = QSize(fontMetrics().horizontalAdvance(staticText.text()), fontMetrics().height());
+
+}
+
+void ScrollText::paintEvent(QPaintEvent*)
+{
+ QPainter p(this);
+
+ if(scrollEnabled)
+ {
+ buffer.fill(qRgba(0, 0, 0, 0));
+ QPainter pb(&buffer);
+ pb.setPen(p.pen());
+ pb.setFont(p.font());
+
+ int x = qMin(-scrollPos, 0) + leftMargin;
+ while(x < width())
+ {
+ pb.drawStaticText(QPointF(x, (height() - wholeTextSize.height()) / 2) + QPoint(2, 2), staticText);
+ x += wholeTextSize.width();
+ }
+
+ //Apply Alpha Channel
+ pb.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+ pb.setClipRect(width() - 15, 0, 15, height());
+ pb.drawImage(0, 0, alphaChannel);
+ pb.setClipRect(0, 0, 15, height());
+ //initial situation: don't apply alpha channel in the left half of the image at all; apply it more and more until scrollPos gets positive
+ if(scrollPos < 0)
+ pb.setOpacity((qreal)(qMax(-8, scrollPos) + 8) / 8.0);
+ pb.drawImage(0, 0, alphaChannel);
+ p.drawImage(0, 0, buffer);
+ }
+ else
+ {
+ p.drawText(QRectF(0, 0, width(), height()), Qt::AlignCenter, text());
+// p.drawStaticText(QPointF(leftMargin, (height() - wholeTextSize.height()) / 2), staticText);
+ }
+}
+
+void ScrollText::resizeEvent(QResizeEvent*)
+{
+ //When the widget is resized, we need to update the alpha channel.
+
+ alphaChannel = QImage(size(), QImage::Format_ARGB32_Premultiplied);
+ buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);
+
+ //Create Alpha Channel:
+ if(width() > 64)
+ {
+ //create first scanline
+ QRgb* scanline1 = (QRgb*)alphaChannel.scanLine(0);
+ for(int x = 1; x < 16; ++x)
+ scanline1[x - 1] = scanline1[width() - x] = qRgba(0, 0, 0, x << 4);
+ for(int x = 15; x < width() - 15; ++x)
+ scanline1[x] = qRgb(0, 0, 0);
+ //copy scanline to the other ones
+ for(int y = 1; y < height(); ++y)
+ memcpy(alphaChannel.scanLine(y), (uchar*)scanline1, width() * 4);
+ }
+ else
+ alphaChannel.fill(qRgb(0, 0, 0));
+
+
+ //Update scrolling state
+ bool newScrollEnabled = (singleTextWidth > width() - leftMargin);
+ if(newScrollEnabled != scrollEnabled)
+ updateText();
+}
+
+void ScrollText::timer_timeout()
+{
+ scrollPos = (scrollPos + 2)
+ % wholeTextSize.width();
+ pauseAfterOneRotation(scrollPos);
+ update();
+}
+
+void ScrollText::pauseAfterOneRotation(int scrollPos)
+{
+ if(scrolledOnce == false && scrollPos+2==wholeTextSize.width()){
+ scrolledOnce = true;
+ }
+ if(scrolledOnce){
+ pause();
+ }
+}
+
+
+
+
diff --git a/src/widgets/scrolltext/scrolltext.h b/src/widgets/scrolltext/scrolltext.h
new file mode 100644
index 0000000..a660615
--- /dev/null
+++ b/src/widgets/scrolltext/scrolltext.h
@@ -0,0 +1,53 @@
+#ifndef SCROLLTEXT_H
+#define SCROLLTEXT_H
+
+#include <QWidget>
+#include <QStaticText>
+#include <QTimer>
+
+
+class ScrollText : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(QString separator READ separator WRITE setSeparator)
+
+public:
+ explicit ScrollText(QWidget *parent = 0);
+
+public slots:
+ QString text() const;
+ void setText(QString text);
+
+ QString separator() const;
+ void setSeparator(QString separator);
+ void setLeftMargin(int pixels);
+ void pause();
+ void resume();
+
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+
+private:
+ void updateText();
+ QString _text;
+ QString _separator;
+ QStaticText staticText;
+ int singleTextWidth;
+ QSize wholeTextSize;
+ int leftMargin;
+ bool scrollEnabled;
+ int scrollPos;
+ QImage alphaChannel;
+ QImage buffer;
+ QTimer timer;
+ bool scrolledOnce = false;
+
+private slots:
+ virtual void timer_timeout();
+ void pauseAfterOneRotation(int scrollPos);
+};
+
+#endif // SCROLLTEXT_H