diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
new file mode 100644
index 000000000..f8e2ee1c8
--- /dev/null
+++ b/android/AndroidManifest.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qtox.pro b/qtox.pro
index 7de0f7811..3c95c22ba 100644
--- a/qtox.pro
+++ b/qtox.pro
@@ -69,6 +69,23 @@ contains(ENABLE_SYSTRAY_UNITY_BACKEND, YES) {
LIBS += -lgobject-2.0 -lappindicator -lgtk-x11-2.0
}
+android {
+ ANDROID_TOOLCHAIN=/opt/android/toolchain-r9d-17/
+ INCLUDEPATH += $$ANDROID_TOOLCHAIN/include/
+ LIBS += -L$$ANDROID_TOOLCHAIN/lib
+
+ DISABLE_PLATFORM_EXT=YES
+ DISABLE_FILTER_AUDIO=YES
+
+ ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
+ contains(ANDROID_TARGET_ARCH,armeabi) {
+ ANDROID_EXTRA_LIBS = \
+ $$ANDROID_TOOLCHAIN/lib/libopenal.so \
+ $$ANDROID_TOOLCHAIN/lib/libsodium.so
+ }
+}
+
+
contains(DISABLE_PLATFORM_EXT, YES) {
} else {
@@ -90,7 +107,7 @@ contains(JENKINS,YES) {
# Rules for Windows, Mac OSX, and Linux
win32 {
RC_FILE = windows/qtox.rc
- LIBS += -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lsodium -lvpx -lpthread
+ LIBS += -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lsodium -lvpx -lpthread
LIBS += -L$$PWD/libs/lib -lopencv_core249 -lopencv_highgui249 -lopencv_imgproc249 -lOpenAL32 -lopus
LIBS += -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -lws2_32 -liphlpapi -lz
@@ -110,31 +127,38 @@ win32 {
contains(DEFINES, QTOX_PLATFORM_EXT) { LIBS += -framework IOKit -framework CoreFoundation }
contains(DEFINES, QTOX_FILTER_AUDIO) { LIBS += -lfilteraudio }
} else {
- # If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
- contains(STATICPKG, YES) {
- target.path = /usr/bin
- INSTALLS += target
- LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic
- LIBS += -Wl,-Bstatic -ljpeg -ltiff -lpng -ljasper -lIlmImf -lIlmThread -lIex -ldc1394 -lraw1394 -lHalf -lz -llzma -ljbig
- LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0
+ android {
+ LIBS += -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns
+ LIBS += -lopencv_videoio -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc -lopencv_androidcamera
+ LIBS += -llibjpeg -llibwebp -llibpng -llibtiff -llibjasper -lIlmImf -lopencv_core
+ LIBS += -lopus -lvpx -lsodium -lopenal
} else {
- LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lvpx -lsodium -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
- }
-
- contains(DEFINES, QTOX_PLATFORM_EXT) {
- LIBS += -lX11 -lXss
- }
-
- contains(DEFINES, QTOX_FILTER_AUDIO) {
+ # If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
contains(STATICPKG, YES) {
- LIBS += -Wl,-Bstatic -lfilteraudio
+ target.path = /usr/bin
+ INSTALLS += target
+ LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic
+ LIBS += -Wl,-Bstatic -ljpeg -ltiff -lpng -ljasper -lIlmImf -lIlmThread -lIex -ldc1394 -lraw1394 -lHalf -lz -llzma -ljbig
+ LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0
} else {
- LIBS += -lfilteraudio
+ LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lvpx -lsodium -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
}
- }
- contains(JENKINS, YES) {
- LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxdns.a ./libs/lib/libtoxencryptsave.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a ./libs/lib/libfilteraudio.a /usr/lib/libopencv_core.so /usr/lib/libopencv_highgui.so /usr/lib/libopencv_imgproc.so -lopenal -lX11 -lXss -s
+ contains(DEFINES, QTOX_PLATFORM_EXT) {
+ LIBS += -lX11 -lXss
+ }
+
+ contains(DEFINES, QTOX_FILTER_AUDIO) {
+ contains(STATICPKG, YES) {
+ LIBS += -Wl,-Bstatic -lfilteraudio
+ } else {
+ LIBS += -lfilteraudio
+ }
+ }
+
+ contains(JENKINS, YES) {
+ LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxdns.a ./libs/lib/libtoxencryptsave.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a ./libs/lib/libfilteraudio.a /usr/lib/libopencv_core.so /usr/lib/libopencv_highgui.so /usr/lib/libopencv_imgproc.so -lopenal -lX11 -lXss -s
+ }
}
}
}
@@ -206,7 +230,10 @@ HEADERS += src/widget/form/addfriendform.h \
src/audio.h \
src/widget/callconfirmwidget.h \
src/widget/systemtrayicon.h \
- src/widget/systemtrayicon_private.h
+ src/widget/systemtrayicon_private.h \
+ src/nexus.h \
+ src/widget/gui.h \
+ src/widget/androidgui.h
SOURCES += \
src/widget/form/addfriendform.cpp \
@@ -275,7 +302,10 @@ SOURCES += \
src/widget/form/settings/advancedform.cpp \
src/audio.cpp \
src/widget/callconfirmwidget.cpp \
- src/widget/systemtrayicon.cpp
+ src/widget/systemtrayicon.cpp \
+ src/nexus.cpp \
+ src/widget/gui.cpp \
+ src/widget/androidgui.cpp
contains(DEFINES, QTOX_FILTER_AUDIO) {
HEADERS += src/audiofilterer.h
diff --git a/src/autoupdate.cpp b/src/autoupdate.cpp
index 9c4fcc666..5f7b54f97 100644
--- a/src/autoupdate.cpp
+++ b/src/autoupdate.cpp
@@ -19,6 +19,7 @@
#include "src/misc/serialize.h"
#include "src/misc/settings.h"
#include "src/widget/widget.h"
+#include "src/widget/gui.h"
#include
#include
#include
@@ -490,7 +491,7 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker()
QDir updateDir(updateDirStr);
if ((updateDir.exists() && QFile(updateDirStr+"flist").exists())
- || Widget::getInstance()->askQuestion(QObject::tr("Update", "The title of a message box"),
+ || GUI::askQuestion(QObject::tr("Update", "The title of a message box"),
QObject::tr("An update is available, do you want to download it now?\nIt will be installed when qTox restarts."), true, false))
{
downloadUpdate();
diff --git a/src/core.cpp b/src/core.cpp
index 4b82a1d5a..610077fda 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -15,10 +15,11 @@
*/
#include "core.h"
+#include "nexus.h"
#include "misc/cdata.h"
#include "misc/cstring.h"
#include "misc/settings.h"
-#include "widget/widget.h"
+#include "widget/gui.h"
#include "historykeeper.h"
#include "src/audio.h"
@@ -106,6 +107,7 @@ Core::~Core()
{
qDebug() << "Deleting Core";
+ saveConfiguration();
toxTimer->stop();
coreThread->exit(0);
while (coreThread->isRunning())
@@ -128,7 +130,7 @@ Core::~Core()
Core* Core::getInstance()
{
- return Widget::getInstance()->getCore();
+ return Nexus::getCore();
}
void Core::make_tox()
@@ -223,6 +225,8 @@ void Core::make_tox()
void Core::start()
{
+ qDebug() << "Core: Starting up";
+
make_tox();
qsrand(time(nullptr));
@@ -255,7 +259,6 @@ void Core::start()
{
setStatusMessage(tr("Toxing on qTox")); // this also solves the not updating issue
setUsername(tr("qTox User"));
- QMetaObject::invokeMethod(Widget::getInstance(), "onSettingsClicked"); // update ui with new profile
}
tox_callback_friend_request(tox, onFriendRequest, this);
@@ -1202,8 +1205,7 @@ bool Core::loadConfiguration(QString path)
{
configurationFile.close();
- QString profile;
- QMetaObject::invokeMethod(Widget::getInstance(), "askProfiles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, profile));
+ QString profile = Settings::getInstance().askProfiles();
if (!profile.isEmpty())
{
@@ -1281,7 +1283,7 @@ void Core::switchConfiguration(const QString& profile)
saveCurrentInformation(); // part of a hack, see core.h
ready = false;
- Widget::getInstance()->setEnabledThreadsafe(false);
+ GUI::setEnabled(false);
clearPassword(ptMain);
clearPassword(ptHistory);
@@ -1301,7 +1303,7 @@ void Core::switchConfiguration(const QString& profile)
start();
if (isReady())
- Widget::getInstance()->setEnabledThreadsafe(true);
+ GUI::setEnabled(true);
}
void Core::loadFriends()
diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp
index 00e4d1a52..3f64fe7b3 100644
--- a/src/coreencryption.cpp
+++ b/src/coreencryption.cpp
@@ -19,7 +19,7 @@
/* was permanently moved here to handle encryption */
#include "core.h"
-#include "src/widget/widget.h"
+#include "src/widget/gui.h"
#include
#include
#include "src/misc/settings.h"
@@ -165,7 +165,7 @@ QByteArray Core::getSaltFromFile(QString filename)
bool Core::loadEncryptedSave(QByteArray& data)
{
if (!Settings::getInstance().getEncryptTox())
- Widget::getInstance()->showWarningMsgBox(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless."));
+ GUI::showWarning(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless."));
int error = -1;
QString a(tr("Please enter the password for the %1 profile.", "used in load() when no pw is already set").arg(Settings::getInstance().getCurrentProfile()));
@@ -190,7 +190,7 @@ bool Core::loadEncryptedSave(QByteArray& data)
do
{
- QString pw = Widget::getInstance()->passwordDialog(tr("Change profile"), dialogtxt);
+ QString pw = GUI::passwordDialog(tr("Change profile"), dialogtxt);
if (pw.isEmpty())
{
@@ -216,7 +216,7 @@ void Core::checkEncryptedHistory()
QByteArray salt = getSaltFromFile(path);
if (exists && salt.size() == 0)
{ // maybe we should handle this better
- Widget::getInstance()->showWarningMsgBox(tr("Encrypted chat history"), tr("No encrypted chat history file found, or it was corrupted.\nHistory will be disabled!"));
+ GUI::showWarning(tr("Encrypted chat history"), tr("No encrypted chat history file found, or it was corrupted.\nHistory will be disabled!"));
Settings::getInstance().setEncryptLogs(false);
Settings::getInstance().setEnableLogging(false);
HistoryKeeper::resetInstance();
@@ -252,7 +252,7 @@ void Core::checkEncryptedHistory()
bool error = true;
do
{
- QString pw = Widget::getInstance()->passwordDialog(tr("Disable chat history"), dialogtxt);
+ QString pw = GUI::passwordDialog(tr("Disable chat history"), dialogtxt);
if (pw.isEmpty())
{
@@ -295,7 +295,7 @@ void Core::saveConfiguration(const QString& path)
else
fileSize = tox_size(tox);
- if (fileSize > 0 && fileSize <= INT32_MAX) {
+ if (fileSize > 0 && fileSize <= std::numeric_limits::max()) {
uint8_t *data = new uint8_t[fileSize];
if (encrypt)
@@ -303,7 +303,7 @@ void Core::saveConfiguration(const QString& path)
if (!pwsaltedkeys[ptMain])
{
// probably zero chance event
- Widget::getInstance()->showWarningMsgBox(tr("NO Password"), tr("Encryption is enabled, but there is no password! Encryption will be disabled."));
+ GUI::showWarning(tr("NO Password"), tr("Encryption is enabled, but there is no password! Encryption will be disabled."));
Settings::getInstance().setEncryptTox(false);
tox_save(tox, data);
}
diff --git a/src/friend.cpp b/src/friend.cpp
index 856e2a58c..52f675cf5 100644
--- a/src/friend.cpp
+++ b/src/friend.cpp
@@ -18,7 +18,7 @@
#include "friendlist.h"
#include "widget/friendwidget.h"
#include "widget/form/chatform.h"
-#include "widget/widget.h"
+#include "widget/gui.h"
#include "src/core.h"
#include "src/misc/settings.h"
@@ -52,7 +52,7 @@ void Friend::setName(QString name)
chatForm->setName(name);
if (widget->isActive())
- Widget::getInstance()->setWindowTitle(name);
+ GUI::setWindowTitle(name);
}
}
@@ -65,7 +65,7 @@ void Friend::setAlias(QString name)
chatForm->setName(dispName);
if (widget->isActive())
- Widget::getInstance()->setWindowTitle(dispName);
+ GUI::setWindowTitle(dispName);
}
void Friend::setStatusMessage(QString message)
diff --git a/src/group.cpp b/src/group.cpp
index a75d933a2..c54601c44 100644
--- a/src/group.cpp
+++ b/src/group.cpp
@@ -20,7 +20,7 @@
#include "friendlist.h"
#include "friend.h"
#include "core.h"
-#include "widget/widget.h"
+#include "widget/gui.h"
#include
#include
@@ -90,7 +90,7 @@ void Group::setName(const QString& name)
chatForm->setName(name);
if (widget->isActive())
- Widget::getInstance()->setWindowTitle(name);
+ GUI::setWindowTitle(name);
}
void Group::regeneratePeerList()
diff --git a/src/main.cpp b/src/main.cpp
index ad8c23342..43395a9f4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -16,6 +16,7 @@
#include "widget/widget.h"
#include "misc/settings.h"
+#include "src/nexus.h"
#include "src/ipc.h"
#include "src/widget/toxuri.h"
#include "src/widget/toxsave.h"
@@ -70,6 +71,7 @@ int main(int argc, char *argv[])
parser.process(a);
Settings::getInstance(); // Build our Settings singleton as soon as QApplication is ready, not before
+
if (parser.isSet("p"))
{
QString profile = parser.value("p");
@@ -121,6 +123,9 @@ int main(int argc, char *argv[])
AutoUpdater::installLocalUpdate(); ///< NORETURN
#endif
+ Nexus::getInstance().start();
+
+#ifndef Q_OS_ANDROID
// Inter-process communication
IPC ipc;
ipc.registerEventHandler(&toxURIEventHandler);
@@ -175,17 +180,18 @@ int main(int argc, char *argv[])
if (!ipc.isCurrentOwner())
return EXIT_SUCCESS;
}
+#endif
// Run
a.setQuitOnLastWindowClosed(false);
- Widget* w = Widget::getInstance();
int errorcode = a.exec();
- delete w;
#ifdef LOG_TO_FILE
delete logFile;
logFile = nullptr;
#endif
+ Nexus::destroyInstance();
+
return errorcode;
}
diff --git a/src/misc/settings.cpp b/src/misc/settings.cpp
index 196202865..96b2b2c6e 100644
--- a/src/misc/settings.cpp
+++ b/src/misc/settings.cpp
@@ -18,6 +18,8 @@
#include "smileypack.h"
#include "src/corestructs.h"
#include "src/misc/db/plaindb.h"
+#include "src/core.h"
+#include "src/widget/gui.h"
#include
#include
@@ -65,6 +67,70 @@ void Settings::switchProfile(const QString& profile)
resetInstance();
}
+QString Settings::detectProfile()
+{
+ QDir dir(getSettingsDirPath());
+ QString path, profile = getCurrentProfile();
+ path = dir.filePath(profile + Core::TOX_EXT);
+ QFile file(path);
+ if (profile.isEmpty() || !file.exists())
+ {
+ setCurrentProfile("");
+#if 1 // deprecation attempt
+ // if the last profile doesn't exist, fall back to old "data"
+ path = dir.filePath(Core::CONFIG_FILE_NAME);
+ QFile file(path);
+ if (file.exists())
+ return path;
+ else if (QFile(path = dir.filePath("tox_save")).exists()) // also import tox_save if no data
+ return path;
+ else
+#endif
+ {
+ profile = askProfiles();
+ if (profile.isEmpty())
+ return "";
+ else
+ {
+ switchProfile(profile);
+ return dir.filePath(profile + Core::TOX_EXT);
+ }
+ }
+ }
+ else
+ return path;
+}
+
+QList Settings::searchProfiles()
+{
+ QList out;
+ QDir dir(getSettingsDirPath());
+ dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
+ dir.setNameFilters(QStringList("*.tox"));
+ for (QFileInfo file : dir.entryInfoList())
+ out += file.completeBaseName();
+ return out;
+}
+
+QString Settings::askProfiles()
+{ // TODO: allow user to create new Tox ID, even if a profile already exists
+ QList profiles = searchProfiles();
+ if (profiles.empty()) return "";
+ bool ok;
+ QString profile = GUI::itemInputDialog(nullptr,
+ tr("Choose a profile"),
+ tr("Please choose which identity to use"),
+ profiles,
+ 0, // which slot to start on
+ false, // if the user can enter their own input
+ &ok);
+ if (!ok) // user cancelled
+ return "";
+ else
+ return profile;
+}
+
+
void Settings::load()
{
if (loaded)
diff --git a/src/misc/settings.h b/src/misc/settings.h
index 391fe1c58..550b79840 100644
--- a/src/misc/settings.h
+++ b/src/misc/settings.h
@@ -33,6 +33,9 @@ public:
static Settings& getInstance();
static void resetInstance();
void switchProfile(const QString& profile);
+ QString detectProfile();
+ QList searchProfiles();
+ QString askProfiles();
~Settings() = default;
void executeSettingsDialog(QWidget* parent);
diff --git a/src/misc/style.cpp b/src/misc/style.cpp
index ad6f93c7b..ea1318fd7 100644
--- a/src/misc/style.cpp
+++ b/src/misc/style.cpp
@@ -16,10 +16,7 @@
#include "style.h"
#include "settings.h"
-
-#include "src/widget/widget.h"
-#include "ui_mainwindow.h"
-#include "src/widget/genericchatroomwidget.h"
+#include "src/widget/gui.h"
#include
#include
@@ -198,11 +195,9 @@ void Style::setThemeColor(QColor color)
dict["@themeMediumDark"] = getColor(ThemeMediumDark).name();
dict["@themeMedium"] = getColor(ThemeMedium).name();
dict["@themeLight"] = getColor(ThemeLight).name();
-
- applyTheme();
}
void Style::applyTheme()
{
- Widget::getInstance()->reloadTheme();
+ GUI::reloadTheme();
}
diff --git a/src/nexus.cpp b/src/nexus.cpp
new file mode 100644
index 000000000..9c80bcfcd
--- /dev/null
+++ b/src/nexus.cpp
@@ -0,0 +1,142 @@
+#include "nexus.h"
+#include "core.h"
+#include "misc/settings.h"
+#include "video/camera.h"
+#include "widget/gui.h"
+#include
+#include
+
+#ifdef Q_OS_ANDROID
+#include
+#else
+#include
+#endif
+
+static Nexus* nexus{nullptr};
+
+Nexus::Nexus(QObject *parent) :
+ QObject(parent),
+ core{nullptr},
+ coreThread{nullptr},
+ widget{nullptr},
+ androidgui{nullptr},
+ started{false}
+{
+}
+
+Nexus::~Nexus()
+{
+ delete core;
+ delete coreThread;
+#ifdef Q_OS_ANDROID
+ delete androidgui;
+#else
+ delete widget;
+#endif
+}
+
+void Nexus::start()
+{
+ if (started)
+ return;
+ qDebug() << "Nexus: Starting up";
+
+ // Setup the environment
+ qRegisterMetaType("Status");
+ qRegisterMetaType("vpx_image");
+ qRegisterMetaType("uint8_t");
+ qRegisterMetaType("uint16_t");
+ qRegisterMetaType("const int16_t*");
+ qRegisterMetaType("int32_t");
+ qRegisterMetaType("int64_t");
+ qRegisterMetaType("QPixmap");
+ qRegisterMetaType("ToxFile");
+ qRegisterMetaType("ToxFile::FileDirection");
+ qRegisterMetaType("Core::PasswordType");
+
+ // Create Core
+ QString profilePath = Settings::getInstance().detectProfile();
+ coreThread = new QThread(this);
+ coreThread->setObjectName("qTox Core");
+ core = new Core(Camera::getInstance(), coreThread, profilePath);
+ core->moveToThread(coreThread);
+ connect(coreThread, &QThread::started, core, &Core::start);
+
+ // Start GUI
+#ifdef Q_OS_ANDROID
+ androidgui = new AndroidGUI;
+ androidgui->show();
+#else
+ widget = Widget::getInstance();
+#endif
+ GUI::getInstance();
+
+ // Connections
+#ifndef Q_OS_ANDROID
+ connect(core, &Core::connected, widget, &Widget::onConnected);
+ connect(core, &Core::disconnected, widget, &Widget::onDisconnected);
+ connect(core, &Core::failedToStart, widget, &Widget::onFailedToStartCore);
+ connect(core, &Core::badProxy, widget, &Widget::onBadProxyCore);
+ connect(core, &Core::statusSet, widget, &Widget::onStatusSet);
+ connect(core, &Core::usernameSet, widget, &Widget::setUsername);
+ connect(core, &Core::statusMessageSet, widget, &Widget::setStatusMessage);
+ connect(core, &Core::selfAvatarChanged, widget, &Widget::onSelfAvatarLoaded);
+ connect(core, &Core::friendAdded, widget, &Widget::addFriend);
+ connect(core, &Core::failedToAddFriend, widget, &Widget::addFriendFailed);
+ connect(core, &Core::friendUsernameChanged, widget, &Widget::onFriendUsernameChanged);
+ connect(core, &Core::friendStatusChanged, widget, &Widget::onFriendStatusChanged);
+ connect(core, &Core::friendStatusMessageChanged, widget, &Widget::onFriendStatusMessageChanged);
+ connect(core, &Core::friendRequestReceived, widget, &Widget::onFriendRequestReceived);
+ connect(core, &Core::friendMessageReceived, widget, &Widget::onFriendMessageReceived);
+ connect(core, &Core::receiptRecieved, widget, &Widget::onReceiptRecieved);
+ connect(core, &Core::groupInviteReceived, widget, &Widget::onGroupInviteReceived);
+ connect(core, &Core::groupMessageReceived, widget, &Widget::onGroupMessageReceived);
+ connect(core, &Core::groupNamelistChanged, widget, &Widget::onGroupNamelistChanged);
+ connect(core, &Core::groupTitleChanged, widget, &Widget::onGroupTitleChanged);
+ connect(core, &Core::emptyGroupCreated, widget, &Widget::onEmptyGroupCreated);
+ connect(core, &Core::avInvite, widget, &Widget::playRingtone);
+ connect(core, &Core::blockingClearContacts, widget, &Widget::clearContactsList, Qt::BlockingQueuedConnection);
+ connect(core, &Core::friendTypingChanged, widget, &Widget::onFriendTypingChanged);
+
+ connect(core, SIGNAL(messageSentResult(int,QString,int)), widget, SLOT(onMessageSendResult(int,QString,int)));
+ connect(core, SIGNAL(groupSentResult(int,QString,int)), widget, SLOT(onGroupSendResult(int,QString,int)));
+
+ connect(widget, &Widget::statusSet, core, &Core::setStatus);
+ connect(widget, &Widget::friendRequested, core, &Core::requestFriendship);
+ connect(widget, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest);
+ connect(widget, &Widget::changeProfile, core, &Core::switchConfiguration);
+#endif
+
+ // Start Core
+ coreThread->start();
+
+ started = true;
+}
+
+Nexus& Nexus::getInstance()
+{
+ if (!nexus)
+ nexus = new Nexus;
+ return *nexus;
+}
+
+void Nexus::destroyInstance()
+{
+ delete nexus;
+ nexus = nullptr;
+}
+
+Core* Nexus::getCore()
+{
+ return getInstance().core;
+}
+
+AndroidGUI* Nexus::getAndroidGUI()
+{
+ return getInstance().androidgui;
+}
+
+Widget* Nexus::getDesktopGUI()
+{
+ return getInstance().widget;
+}
diff --git a/src/nexus.h b/src/nexus.h
new file mode 100644
index 000000000..f198f21cc
--- /dev/null
+++ b/src/nexus.h
@@ -0,0 +1,38 @@
+#ifndef NEXUS_H
+#define NEXUS_H
+
+#include
+
+class QThread;
+class Core;
+class Widget;
+class AndroidGUI;
+
+/// This class is in charge of connecting various systems together
+/// and forwarding signals appropriately to the right objects
+/// It is in charge of starting the GUI and the Core
+class Nexus : public QObject
+{
+ Q_OBJECT
+public:
+ void start(); ///< Will initialise the systems (GUI, Core, ...)
+
+ static Nexus& getInstance();
+ static void destroyInstance();
+ static Core* getCore(); ///< Will return 0 if not started
+ static AndroidGUI* getAndroidGUI(); ///< Will return 0 if not started
+ static Widget* getDesktopGUI(); ///< Will return 0 if not started
+
+private:
+ explicit Nexus(QObject *parent = 0);
+ ~Nexus();
+
+private:
+ Core* core;
+ QThread* coreThread;
+ Widget* widget;
+ AndroidGUI* androidgui;
+ bool started;
+};
+
+#endif // NEXUS_H
diff --git a/src/widget/androidgui.cpp b/src/widget/androidgui.cpp
new file mode 100644
index 000000000..7ae41d983
--- /dev/null
+++ b/src/widget/androidgui.cpp
@@ -0,0 +1,13 @@
+#include "androidgui.h"
+#include
+
+AndroidGUI::AndroidGUI(QWidget *parent) :
+ QWidget(parent)
+{
+ l = new QLabel("qTox Android", this);
+}
+
+AndroidGUI::~AndroidGUI()
+{
+ delete l;
+}
diff --git a/src/widget/androidgui.h b/src/widget/androidgui.h
new file mode 100644
index 000000000..853483ce2
--- /dev/null
+++ b/src/widget/androidgui.h
@@ -0,0 +1,19 @@
+#ifndef ANDROIDGUI_H
+#define ANDROIDGUI_H
+
+#include
+
+class QLabel;
+
+class AndroidGUI : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit AndroidGUI(QWidget *parent = 0);
+ ~AndroidGUI();
+
+private:
+ QLabel* l;
+};
+
+#endif // ANDROIDGUI_H
diff --git a/src/widget/callconfirmwidget.cpp b/src/widget/callconfirmwidget.cpp
index 03cc7912f..a5663ec1a 100644
--- a/src/widget/callconfirmwidget.cpp
+++ b/src/widget/callconfirmwidget.cpp
@@ -1,5 +1,6 @@
#include "callconfirmwidget.h"
-#include "widget.h"
+#include "gui.h"
+#include
#include
#include
#include
@@ -11,7 +12,7 @@
#include
CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor) :
- QWidget(Widget::getInstance()), anchor(Anchor),
+ QWidget(GUI::getMainWidget()), anchor(Anchor),
rectW{120}, rectH{85},
spikeW{30}, spikeH{15},
roundedFactor{20},
@@ -43,7 +44,7 @@ CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor) :
connect(buttonBox, &QDialogButtonBox::accepted, this, &CallConfirmWidget::accepted);
connect(buttonBox, &QDialogButtonBox::rejected, this, &CallConfirmWidget::rejected);
- connect(Widget::getInstance(), &Widget::resized, this, &CallConfirmWidget::reposition);
+ connect(&GUI::getInstance(), &GUI::resized, this, &CallConfirmWidget::reposition);
layout->setMargin(12);
layout->addSpacing(spikeH);
@@ -56,7 +57,7 @@ CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor) :
void CallConfirmWidget::reposition()
{
- Widget* w = Widget::getInstance();
+ QWidget* w = GUI::getMainWidget();
QPoint pos = anchor->mapToGlobal({(anchor->width()-rectW)/2,anchor->height()})-w->mapToGlobal({0,0});
// We don't want the widget to overflow past the right of the screen
diff --git a/src/widget/form/settings/avform.cpp b/src/widget/form/settings/avform.cpp
index 3c170821e..d3cefe8a7 100644
--- a/src/widget/form/settings/avform.cpp
+++ b/src/widget/form/settings/avform.cpp
@@ -27,6 +27,10 @@
#include
#endif
+#ifndef ALC_ALL_DEVICES_SPECIFIER
+#define ALC_ALL_DEVICES_SPECIFIER ALC_DEVICE_SPECIFIER
+#endif
+
AVForm::AVForm() :
GenericForm(tr("Audio/Video"), QPixmap(":/img/settings/av.png"))
{
diff --git a/src/widget/form/settings/generalform.cpp b/src/widget/form/settings/generalform.cpp
index 333d9a561..4aca50b3b 100644
--- a/src/widget/form/settings/generalform.cpp
+++ b/src/widget/form/settings/generalform.cpp
@@ -359,4 +359,5 @@ void GeneralForm::onThemeColorChanged(int)
int index = bodyUI->themeColorCBox->currentIndex();
Settings::getInstance().setThemeColor(index);
Style::setThemeColor(index);
+ Style::applyTheme();
}
diff --git a/src/widget/form/settings/identityform.cpp b/src/widget/form/settings/identityform.cpp
index 8d067b606..a856af1b5 100644
--- a/src/widget/form/settings/identityform.cpp
+++ b/src/widget/form/settings/identityform.cpp
@@ -111,7 +111,7 @@ void IdentityForm::present()
toxId->setText(Core::getInstance()->getSelfId().toString());
toxId->setCursorPosition(0);
bodyUI->profiles->clear();
- for (QString profile : Widget::searchProfiles())
+ for (QString profile : Settings::getInstance().searchProfiles())
bodyUI->profiles->addItem(profile);
QString current = Settings::getInstance().getCurrentProfile();
if (current != "")
diff --git a/src/widget/gui.cpp b/src/widget/gui.cpp
new file mode 100644
index 000000000..d6b8a7dc0
--- /dev/null
+++ b/src/widget/gui.cpp
@@ -0,0 +1,290 @@
+#include "gui.h"
+#include "src/nexus.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef Q_OS_ANDROID
+#include "androidgui.h"
+#else
+#include "widget.h"
+#endif
+
+GUI::GUI(QObject *parent) :
+ QObject(parent)
+{
+ assert(QThread::currentThread() == qApp->thread());
+
+#ifndef Q_OS_ANDROID
+ assert(Nexus::getDesktopGUI());
+ connect(Nexus::getDesktopGUI(), &Widget::resized, this, &GUI::resized);
+#endif
+}
+
+GUI& GUI::getInstance()
+{
+ static GUI gui;
+ return gui;
+}
+
+// Implementation of the public clean interface
+
+void GUI::setEnabled(bool state)
+{
+ if (QThread::currentThread() == qApp->thread())
+ {
+ getInstance()._setEnabled(state);
+ }
+ else
+ {
+ QMetaObject::invokeMethod(&getInstance(), "_setEnabled", Qt::BlockingQueuedConnection,
+ Q_ARG(bool, state));
+ }
+}
+
+void GUI::setWindowTitle(const QString& title)
+{
+ if (QThread::currentThread() == qApp->thread())
+ {
+ getInstance()._setWindowTitle(title);
+ }
+ else
+ {
+ QMetaObject::invokeMethod(&getInstance(), "_setWindowTitle", Qt::BlockingQueuedConnection,
+ Q_ARG(const QString&, title));
+ }
+}
+
+void GUI::reloadTheme()
+{
+ if (QThread::currentThread() == qApp->thread())
+ {
+ getInstance()._reloadTheme();
+ }
+ else
+ {
+ QMetaObject::invokeMethod(&getInstance(), "_reloadTheme", Qt::BlockingQueuedConnection);
+ }
+}
+
+void GUI::showWarning(const QString& title, const QString& msg)
+{
+ if (QThread::currentThread() == qApp->thread())
+ {
+ getInstance()._showWarning(title, msg);
+ }
+ else
+ {
+ QMetaObject::invokeMethod(&getInstance(), "_showWarning", Qt::BlockingQueuedConnection,
+ Q_ARG(const QString&, title), Q_ARG(const QString&, msg));
+ }
+}
+
+void GUI::showInfo(const QString& title, const QString& msg)
+{
+ if (QThread::currentThread() == qApp->thread())
+ {
+ getInstance()._showInfo(title, msg);
+ }
+ else
+ {
+ QMetaObject::invokeMethod(&getInstance(), "_showInfo", Qt::BlockingQueuedConnection,
+ Q_ARG(const QString&, title), Q_ARG(const QString&, msg));
+ }
+}
+
+bool GUI::askQuestion(const QString& title, const QString& msg,
+ bool defaultAns, bool warning)
+{
+ if (QThread::currentThread() == qApp->thread())
+ {
+ return getInstance()._askQuestion(title, msg, defaultAns, warning);
+ }
+ else
+ {
+ bool ret;
+ QMetaObject::invokeMethod(&getInstance(), "_askQuestion", Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(bool, ret),
+ Q_ARG(const QString&, title), Q_ARG(const QString&, msg),
+ Q_ARG(bool, defaultAns), Q_ARG(bool, warning));
+ return ret;
+ }
+}
+
+QString GUI::itemInputDialog(QWidget * parent, const QString & title,
+ const QString & label, const QStringList & items,
+ int current, bool editable, bool * ok,
+ Qt::WindowFlags flags,
+ Qt::InputMethodHints hints)
+{
+ if (QThread::currentThread() == qApp->thread())
+ {
+ return getInstance()._itemInputDialog(parent, title, label, items, current, editable, ok, flags, hints);
+ }
+ else
+ {
+ QString r;
+ QMetaObject::invokeMethod(&getInstance(), "_itemInputDialog", Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(QString, r),
+ Q_ARG(QWidget*, parent), Q_ARG(const QString&, title),
+ Q_ARG(const QString&,label), Q_ARG(const QStringList&, items),
+ Q_ARG(int, current), Q_ARG(bool, editable), Q_ARG(bool*, ok),
+ Q_ARG(Qt::WindowFlags, flags), Q_ARG(Qt::InputMethodHints, hints));
+ return r;
+ }
+}
+
+QString GUI::passwordDialog(const QString& cancel, const QString& body)
+{
+ if (QThread::currentThread() == qApp->thread())
+ {
+ return getInstance()._passwordDialog(cancel, body);
+ }
+ else
+ {
+ QString r;
+ QMetaObject::invokeMethod(&getInstance(), "_passwordDialog", Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(QString, r),
+ Q_ARG(const QString&, cancel), Q_ARG(const QString&, body));
+ return r;
+ }
+}
+
+// Private implementations
+
+void GUI::_setEnabled(bool state)
+{
+#ifdef Q_OS_ANDROID
+ Nexus::getAndroidGUI()->setEnabled(state);
+#else
+ Nexus::getDesktopGUI()->setEnabled(state);
+#endif
+}
+
+void GUI::_setWindowTitle(const QString& title)
+{
+ if (title.isEmpty())
+ getMainWidget()->setWindowTitle("qTox");
+ else
+ getMainWidget()->setWindowTitle("qTox - " +title);
+}
+
+void GUI::_reloadTheme()
+{
+#ifndef Q_OS_ANDROID
+ Nexus::getDesktopGUI()->reloadTheme();
+#endif
+}
+
+void GUI::_showWarning(const QString& title, const QString& msg)
+{
+ QMessageBox::warning(getMainWidget(), title, msg);
+}
+
+void GUI::_showInfo(const QString& title, const QString& msg)
+{
+ QMessageBox::information(getMainWidget(), title, msg);
+}
+
+bool GUI::_askQuestion(const QString& title, const QString& msg,
+ bool defaultAns, bool warning)
+{
+ if (warning)
+ {
+ QMessageBox::StandardButton def = QMessageBox::Cancel;
+ if (defaultAns)
+ def = QMessageBox::Ok;
+ return QMessageBox::warning(getMainWidget(), title, msg, QMessageBox::Ok | QMessageBox::Cancel, def) == QMessageBox::Ok;
+ }
+ else
+ {
+ QMessageBox::StandardButton def = QMessageBox::No;
+ if (defaultAns)
+ def = QMessageBox::Yes;
+ return QMessageBox::question(getMainWidget(), title, msg, QMessageBox::Yes | QMessageBox::No, def) == QMessageBox::Yes;
+ }
+}
+
+QString GUI::_itemInputDialog(QWidget * parent, const QString & title,
+ const QString & label, const QStringList & items,
+ int current, bool editable, bool * ok,
+ Qt::WindowFlags flags,
+ Qt::InputMethodHints hints)
+{
+ return QInputDialog::getItem(parent, title, label, items, current, editable, ok, flags, hints);
+}
+
+QString GUI::_passwordDialog(const QString& cancel, const QString& body)
+{
+ // we use a hack. It is considered that closing the dialog without explicitly clicking
+ // disable history is confusing. But we can't distinguish between clicking the cancel
+ // button and closing the dialog. So instead, we reverse the Ok and Cancel roles,
+ // so that nothing but explicitly clicking disable history closes the dialog
+ QString ret;
+ QInputDialog dialog;
+ dialog.setWindowTitle(tr("Enter your password"));
+ dialog.setOkButtonText(cancel);
+ dialog.setCancelButtonText(tr("Decrypt"));
+ dialog.setInputMode(QInputDialog::TextInput);
+ dialog.setTextEchoMode(QLineEdit::Password);
+ dialog.setLabelText(body);
+
+ // problem with previous hack: the default button is disable history, not decrypt.
+ // use another hack to reverse the default buttons.
+ // http://www.qtcentre.org/threads/49924-Change-property-of-QInputDialog-button
+ QList l = dialog.findChildren();
+ if (!l.isEmpty())
+ {
+ QPushButton* ok = l.first()->button(QDialogButtonBox::Ok);
+ QPushButton* cancel = l.first()->button(QDialogButtonBox::Cancel);
+ if (ok && cancel)
+ {
+ ok->setAutoDefault(false);
+ ok->setDefault(false);
+ cancel->setAutoDefault(true);
+ cancel->setDefault(true);
+ }
+ else
+ qWarning() << "PasswordDialog: Missing button!";
+ }
+ else
+ qWarning() << "PasswordDialog: No QDialogButtonBox!";
+
+ // using similar code, set QLabels to wrap
+ for (auto* label : dialog.findChildren())
+ label->setWordWrap(true);
+
+ while (true)
+ {
+ int val = dialog.exec();
+ if (val == QDialog::Accepted)
+ return QString();
+ else
+ {
+ ret = dialog.textValue();
+ if (!ret.isEmpty())
+ return ret;
+ }
+ dialog.setTextValue("");
+ dialog.setLabelText(body + "\n\n" + tr("You must enter a non-empty password:"));
+ }
+}
+
+// Other
+
+QWidget* GUI::getMainWidget()
+{
+ QWidget* maingui{nullptr};
+#ifdef Q_OS_ANDROID
+ maingui = Nexus::getAndroidGUI();
+#else
+ maingui = Nexus::getDesktopGUI();
+#endif
+ return maingui;
+}
diff --git a/src/widget/gui.h b/src/widget/gui.h
new file mode 100644
index 000000000..70cc73a7c
--- /dev/null
+++ b/src/widget/gui.h
@@ -0,0 +1,72 @@
+#ifndef GUI_H
+#define GUI_H
+
+#include
+
+class QWidget;
+
+/// Abstracts the GUI from the target backend (AndroidGUI, DesktopGUI, ...)
+/// All the functions exposed here are thread-safe
+/// Prefer calling this class to calling a GUI backend directly
+class GUI : public QObject
+{
+ Q_OBJECT
+public:
+ static GUI& getInstance();
+ /// Returns the main QWidget* of the application
+ static QWidget* getMainWidget();
+ /// Will enable or disable the GUI.
+ /// A disabled GUI can't be interacted with by the user
+ static void setEnabled(bool state);
+ /// Change the title of the main window
+ /// This is usually always visible to the user
+ static void setWindowTitle(const QString& title);
+ /// Reloads the application theme and redraw the window
+ static void reloadTheme();
+ /// Show a warning to the user, for example in a message box
+ static void showWarning(const QString& title, const QString& msg);
+ /// Show some text to the user, for example in a message box
+ static void showInfo(const QString& title, const QString& msg);
+ /// Asks the user a question, for example in a message box.
+ /// If warning is true, we will use a special warning style.
+ /// Returns the answer.
+ static bool askQuestion(const QString& title, const QString& msg,
+ bool defaultAns = false, bool warning = true);
+ /// Asks the user to input text and returns the answer.
+ /// The interface is equivalent to QInputDialog::getItem()
+ static QString itemInputDialog(QWidget * parent, const QString & title,
+ const QString & label, const QStringList & items,
+ int current = 0, bool editable = true, bool * ok = 0,
+ Qt::WindowFlags flags = 0,
+ Qt::InputMethodHints hints = Qt::ImhNone);
+ /// Asks the user to answer a password
+ /// cancel is the text on the cancel button and body
+ /// is descriptive text that will be shown to the user
+ static QString passwordDialog(const QString& cancel, const QString& body);
+
+signals:
+ /// Emitted when the GUI is resized on supported platforms
+ /// Guaranteed to work on desktop platforms
+ void resized();
+
+private:
+ explicit GUI(QObject *parent = 0);
+
+ // Private implementation, those must be called from the GUI thread
+private slots:
+ void _setEnabled(bool state);
+ void _setWindowTitle(const QString& title);
+ void _reloadTheme();
+ void _showWarning(const QString& title, const QString& msg);
+ void _showInfo(const QString& title, const QString& msg);
+ bool _askQuestion(const QString& title, const QString& msg,
+ bool defaultAns = false, bool warning = true);
+ QString _itemInputDialog(QWidget * parent, const QString & title,
+ const QString & label, const QStringList & items,
+ int current = 0, bool editable = true, bool * ok = 0,
+ Qt::WindowFlags flags = 0,
+ Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
+ QString _passwordDialog(const QString& cancel, const QString& body);
+};
+
+#endif // GUI_H
diff --git a/src/widget/systemtrayicon.cpp b/src/widget/systemtrayicon.cpp
index a31929d3e..e33c0a22c 100644
--- a/src/widget/systemtrayicon.cpp
+++ b/src/widget/systemtrayicon.cpp
@@ -42,6 +42,11 @@ SystemTrayIcon::SystemTrayIcon()
}
}
+SystemTrayIcon::~SystemTrayIcon()
+{
+ qDebug() << "Deleting SystemTrayIcon";
+}
+
QString SystemTrayIcon::extractIconToFile(QIcon icon, QString name)
{
QString iconPath;
diff --git a/src/widget/systemtrayicon.h b/src/widget/systemtrayicon.h
index aa5120cd0..540d96285 100644
--- a/src/widget/systemtrayicon.h
+++ b/src/widget/systemtrayicon.h
@@ -12,6 +12,7 @@ class SystemTrayIcon : public QObject
Q_OBJECT
public:
SystemTrayIcon();
+ ~SystemTrayIcon();
void setContextMenu(QMenu* menu);
void show();
void hide();
diff --git a/src/widget/toxsave.cpp b/src/widget/toxsave.cpp
index 9d14879db..8340b1e95 100644
--- a/src/widget/toxsave.cpp
+++ b/src/widget/toxsave.cpp
@@ -15,7 +15,7 @@
*/
#include "toxsave.h"
-#include "widget.h"
+#include "gui.h"
#include "src/core.h"
#include "src/misc/settings.h"
#include
@@ -53,20 +53,19 @@ void handleToxSave(const QString& path)
if (info.suffix() != "tox")
{
- QMessageBox::warning(Widget::getInstance(),
- QObject::tr("Ignoring non-Tox file", "popup title"),
- QObject::tr("Warning: you've chosen a file that is not a Tox save file; ignoring.", "popup text"));
+ GUI::showWarning(QObject::tr("Ignoring non-Tox file", "popup title"),
+ QObject::tr("Warning: you've chosen a file that is not a Tox save file; ignoring.", "popup text"));
return;
}
QString profilePath = QDir(Settings::getSettingsDirPath()).filePath(profile + Core::TOX_EXT);
- if (QFileInfo(profilePath).exists() && !Widget::getInstance()->askQuestion(QObject::tr("Profile already exists", "import confirm title"),
+ if (QFileInfo(profilePath).exists() && !GUI::askQuestion(QObject::tr("Profile already exists", "import confirm title"),
QObject::tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile)))
return;
QFile::copy(path, profilePath);
// no good way to update the ui from here... maybe we need a Widget:refreshUi() function...
// such a thing would simplify other code as well I believe
- QMessageBox::information(Widget::getInstance(), QObject::tr("Profile imported"), QObject::tr("%1.tox was successfully imported").arg(profile));
+ GUI::showInfo(QObject::tr("Profile imported"), QObject::tr("%1.tox was successfully imported").arg(profile));
}
diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp
index eb3999504..ce7c42ee1 100644
--- a/src/widget/widget.cpp
+++ b/src/widget/widget.cpp
@@ -36,6 +36,8 @@
#include "src/audio.h"
#include "src/platform/timer.h"
#include "systemtrayicon.h"
+#include "src/nexus.h"
+#include
#include
#include
#include
@@ -53,6 +55,12 @@
#include
#include
+#ifdef Q_OS_ANDROID
+#define IS_ON_DESKTOP_GUI 0
+#else
+#define IS_ON_DESKTOP_GUI 1
+#endif
+
void toxActivateEventHandler(const QByteArray& data)
{
if (data != "$activate")
@@ -64,11 +72,11 @@ Widget *Widget::instance{nullptr};
Widget::Widget(QWidget *parent)
: QMainWindow(parent),
+ icon{nullptr},
ui(new Ui::MainWindow),
activeChatroomWidget{nullptr},
eventFlag(false),
- eventIcon(false),
- icon{nullptr}
+ eventIcon(false)
{
translator = new QTranslator;
setTranslation();
@@ -116,6 +124,9 @@ void Widget::init()
this,
SLOT(onIconClick(QSystemTrayIcon::ActivationReason)));
+ icon->show();
+ icon->hide();
+
if (Settings::getInstance().getShowSystemTray())
{
icon->show();
@@ -124,7 +135,6 @@ void Widget::init()
}
else
this->show();
-
}
else
{
@@ -193,68 +203,16 @@ void Widget::init()
ui->statusButton->setEnabled(false);
Style::setThemeColor(Settings::getInstance().getThemeColor());
- Style::applyTheme();
-
- qRegisterMetaType("Status");
- qRegisterMetaType("vpx_image");
- qRegisterMetaType("uint8_t");
- qRegisterMetaType("uint16_t");
- qRegisterMetaType("const int16_t*");
- qRegisterMetaType("int32_t");
- qRegisterMetaType("int64_t");
- qRegisterMetaType("QPixmap");
- qRegisterMetaType("ToxFile");
- qRegisterMetaType("ToxFile::FileDirection");
- qRegisterMetaType("Core::PasswordType");
-
- QString profilePath = detectProfile();
- coreThread = new QThread(this);
- coreThread->setObjectName("qTox Core");
- core = new Core(Camera::getInstance(), coreThread, profilePath);
- core->moveToThread(coreThread);
- connect(coreThread, &QThread::started, core, &Core::start);
+ reloadTheme();
filesForm = new FilesForm();
addFriendForm = new AddFriendForm;
settingsWidget = new SettingsWidget();
- connect(settingsWidget, &SettingsWidget::setShowSystemTray, this, &Widget::onSetShowSystemTray);
-
- connect(core, &Core::connected, this, &Widget::onConnected);
- connect(core, &Core::disconnected, this, &Widget::onDisconnected);
- connect(core, &Core::failedToStart, this, &Widget::onFailedToStartCore);
- connect(core, &Core::badProxy, this, &Widget::onBadProxyCore);
- connect(core, &Core::statusSet, this, &Widget::onStatusSet);
- connect(core, &Core::usernameSet, this, &Widget::setUsername);
- connect(core, &Core::statusMessageSet, this, &Widget::setStatusMessage);
- connect(core, &Core::selfAvatarChanged, this, &Widget::onSelfAvatarLoaded);
+ Core* core = Nexus::getCore();
connect(core, SIGNAL(fileDownloadFinished(const QString&)), filesForm, SLOT(onFileDownloadComplete(const QString&)));
connect(core, SIGNAL(fileUploadFinished(const QString&)), filesForm, SLOT(onFileUploadComplete(const QString&)));
- connect(core, &Core::friendAdded, this, &Widget::addFriend);
- connect(core, &Core::failedToAddFriend, this, &Widget::addFriendFailed);
- connect(core, &Core::friendUsernameChanged, this, &Widget::onFriendUsernameChanged);
- connect(core, &Core::friendStatusChanged, this, &Widget::onFriendStatusChanged);
- connect(core, &Core::friendStatusMessageChanged, this, &Widget::onFriendStatusMessageChanged);
- connect(core, &Core::friendRequestReceived, this, &Widget::onFriendRequestReceived);
- connect(core, &Core::friendMessageReceived, this, &Widget::onFriendMessageReceived);
- connect(core, &Core::receiptRecieved, this, &Widget::onReceiptRecieved);
- connect(core, &Core::groupInviteReceived, this, &Widget::onGroupInviteReceived);
- connect(core, &Core::groupMessageReceived, this, &Widget::onGroupMessageReceived);
- connect(core, &Core::groupNamelistChanged, this, &Widget::onGroupNamelistChanged);
- connect(core, &Core::groupTitleChanged, this, &Widget::onGroupTitleChanged);
- connect(core, &Core::emptyGroupCreated, this, &Widget::onEmptyGroupCreated);
- connect(core, &Core::avInvite, this, &Widget::playRingtone);
- connect(core, &Core::blockingClearContacts, this, &Widget::clearContactsList, Qt::BlockingQueuedConnection);
- connect(core, &Core::friendTypingChanged, this, &Widget::onFriendTypingChanged);
-
- connect(core, SIGNAL(messageSentResult(int,QString,int)), this, SLOT(onMessageSendResult(int,QString,int)));
- connect(core, SIGNAL(groupSentResult(int,QString,int)), this, SLOT(onGroupSendResult(int,QString,int)));
-
- connect(this, &Widget::statusSet, core, &Core::setStatus);
- connect(this, &Widget::friendRequested, core, &Core::requestFriendship);
- connect(this, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest);
- connect(this, &Widget::changeProfile, core, &Core::switchConfiguration);
-
+ connect(settingsWidget, &SettingsWidget::setShowSystemTray, this, &Widget::onSetShowSystemTray);
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(onAddClicked()));
connect(ui->groupButton, SIGNAL(clicked()), this, SLOT(onGroupClicked()));
connect(ui->transferButton, SIGNAL(clicked()), this, SLOT(onTransferClicked()));
@@ -270,8 +228,6 @@ void Widget::init()
connect(timer, &QTimer::timeout, this, &Widget::onUserAwayCheck);
connect(timer, &QTimer::timeout, this, &Widget::onEventIconTick);
- coreThread->start();
-
addFriendForm->show(*ui);
#if (AUTOUPDATE_ENABLED)
@@ -317,10 +273,8 @@ void Widget::updateTrayIcon()
Widget::~Widget()
{
- qDebug() << "Deleting Widget";
- core->saveConfiguration();
+ qDebug() << "Widget: Deleting Widget";
AutoUpdater::abortUpdates();
- delete core;
icon->hide();
hideMainForms();
delete settingsWidget;
@@ -338,6 +292,8 @@ Widget::~Widget()
Widget* Widget::getInstance()
{
+ assert(IS_ON_DESKTOP_GUI); // Widget must only be used on Desktop platforms
+
if (!instance)
{
instance = new Widget();
@@ -346,11 +302,6 @@ Widget* Widget::getInstance()
return instance;
}
-QThread* Widget::getCoreThread()
-{
- return coreThread;
-}
-
void Widget::closeEvent(QCloseEvent *event)
{
if (Settings::getInstance().getShowSystemTray() && Settings::getInstance().getCloseToTray() == true)
@@ -386,72 +337,9 @@ void Widget::resizeEvent(QResizeEvent *event)
emit resized();
}
-QString Widget::detectProfile()
-{
- QDir dir(Settings::getSettingsDirPath());
- QString path, profile = Settings::getInstance().getCurrentProfile();
- path = dir.filePath(profile + Core::TOX_EXT);
- QFile file(path);
- if (profile.isEmpty() || !file.exists())
- {
- Settings::getInstance().setCurrentProfile("");
-#if 1 // deprecation attempt
- // if the last profile doesn't exist, fall back to old "data"
- path = dir.filePath(Core::CONFIG_FILE_NAME);
- QFile file(path);
- if (file.exists())
- return path;
- else if (QFile(path = dir.filePath("tox_save")).exists()) // also import tox_save if no data
- return path;
- else
-#endif
- {
- profile = askProfiles();
- if (profile.isEmpty())
- return "";
- else
- {
- Settings::getInstance().switchProfile(profile);
- return dir.filePath(profile + Core::TOX_EXT);
- }
- }
- }
- else
- return path;
-}
-
-QList Widget::searchProfiles()
-{
- QList out;
- QDir dir(Settings::getSettingsDirPath());
- dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
- dir.setNameFilters(QStringList("*.tox"));
- for (QFileInfo file : dir.entryInfoList())
- out += file.completeBaseName();
- return out;
-}
-
-QString Widget::askProfiles()
-{ // TODO: allow user to create new Tox ID, even if a profile already exists
- QList profiles = searchProfiles();
- if (profiles.empty()) return "";
- bool ok;
- QString profile = QInputDialog::getItem(this,
- tr("Choose a profile"),
- tr("Please choose which identity to use"),
- profiles,
- 0, // which slot to start on
- false, // if the user can enter their own input
- &ok);
- if (!ok) // user cancelled
- return "";
- else
- return profile;
-}
-
QString Widget::getUsername()
{
- return core->getUsername();
+ return Nexus::getCore()->getUsername();
}
void Widget::onAvatarClicked()
@@ -495,7 +383,7 @@ void Widget::onAvatarClicked()
return;
}
- core->setAvatar(TOX_AVATAR_FORMAT_PNG, bytes);
+ Nexus::getCore()->setAvatar(TOX_AVATAR_FORMAT_PNG, bytes);
}
void Widget::onSelfAvatarLoaded(const QPixmap& pic)
@@ -591,7 +479,7 @@ void Widget::onAddClicked()
void Widget::onGroupClicked()
{
- core->createGroup();
+ Nexus::getCore()->createGroup();
}
void Widget::onTransferClicked()
@@ -664,7 +552,7 @@ void Widget::hideMainForms()
void Widget::onUsernameChanged(const QString& newUsername, const QString& oldUsername)
{
setUsername(oldUsername); // restore old username until Core tells us to set it
- core->setUsername(newUsername);
+ Nexus::getCore()->setUsername(newUsername);
}
void Widget::setUsername(const QString& username)
@@ -681,7 +569,7 @@ void Widget::onStatusMessageChanged(const QString& newStatusMessage, const QStri
{
ui->statusLabel->setText(oldStatusMessage); // restore old status message until Core tells us to set it
ui->statusLabel->setToolTip(oldStatusMessage); // for overlength messsages
- core->setStatusMessage(newStatusMessage);
+ Nexus::getCore()->setStatusMessage(newStatusMessage);
}
void Widget::setStatusMessage(const QString &statusMessage)
@@ -707,6 +595,7 @@ void Widget::addFriend(int friendId, const QString &userId)
if (Settings::getInstance().getEnableLogging())
newfriend->getChatForm()->loadHistory(QDateTime::currentDateTime().addDays(-7), true);
+ Core* core = Nexus::getCore();
connect(settingsWidget, &SettingsWidget::compactToggled, newfriend->getFriendWidget(), &GenericChatroomWidget::onCompactChanged);
connect(newfriend->getFriendWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*)));
connect(newfriend->getFriendWidget(), SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int)));
@@ -928,7 +817,7 @@ void Widget::removeFriend(Friend* f, bool fake)
onAddClicked();
}
FriendList::removeFriend(f->getFriendID(), fake);
- core->removeFriend(f->getFriendID(), fake);
+ Nexus::getCore()->removeFriend(f->getFriendID(), fake);
delete f;
if (ui->mainHead->layout()->isEmpty())
onAddClicked();
@@ -959,7 +848,7 @@ void Widget::copyFriendIdToClipboard(int friendId)
if (f != nullptr)
{
QClipboard *clipboard = QApplication::clipboard();
- clipboard->setText(core->getFriendAddress(f->getFriendID()), QClipboard::Clipboard);
+ clipboard->setText(Nexus::getCore()->getFriendAddress(f->getFriendID()), QClipboard::Clipboard);
}
}
@@ -967,7 +856,7 @@ void Widget::onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray in
{
if (type == TOX_GROUPCHAT_TYPE_TEXT || type == TOX_GROUPCHAT_TYPE_AV)
{
- int groupId = core->joinGroupchat(friendId, type, (uint8_t*)invite.data(), invite.length());
+ int groupId = Nexus::getCore()->joinGroupchat(friendId, type, (uint8_t*)invite.data(), invite.length());
if (groupId < 0)
{
qWarning() << "Widget::onGroupInviteReceived: Unable to accept group invite";
@@ -1014,7 +903,7 @@ void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Cha
g = createGroup(groupnumber);
}
- QString name = core->getGroupPeerName(groupnumber, peernumber);
+ QString name = Nexus::getCore()->getGroupPeerName(groupnumber, peernumber);
TOX_CHAT_CHANGE change = static_cast(Change);
if (change == TOX_CHAT_CHANGE_PEER_ADD)
{
@@ -1033,7 +922,7 @@ void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Cha
//g->chatForm->addSystemInfoMessage(tr("%1 has left the chat").arg(name), "silver");
}
else if (change == TOX_CHAT_CHANGE_PEER_NAME) // core overwrites old name before telling us it changed...
- g->updatePeer(peernumber,core->getGroupPeerName(groupnumber, peernumber));
+ g->updatePeer(peernumber,Nexus::getCore()->getGroupPeerName(groupnumber, peernumber));
}
void Widget::onGroupTitleChanged(int groupnumber, const QString& author, const QString& title)
@@ -1056,7 +945,7 @@ void Widget::removeGroup(Group* g, bool fake)
onAddClicked();
}
GroupList::removeGroup(g->getGroupId(), fake);
- core->removeGroup(g->getGroupId(), fake);
+ Nexus::getCore()->removeGroup(g->getGroupId(), fake);
delete g;
if (ui->mainHead->layout()->isEmpty())
onAddClicked();
@@ -1070,11 +959,6 @@ void Widget::removeGroup(int groupId)
removeGroup(GroupList::findGroup(groupId));
}
-Core *Widget::getCore()
-{
- return core;
-}
-
Group *Widget::createGroup(int groupId)
{
Group* g = GroupList::findGroup(groupId);
@@ -1090,6 +974,7 @@ Group *Widget::createGroup(int groupId)
layout->addWidget(newgroup->getGroupWidget());
newgroup->getGroupWidget()->updateStatusLight();
+ Core* core = Nexus::getCore();
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*)));
connect(newgroup->getGroupWidget(), SIGNAL(removeGroup(int)), this, SLOT(removeGroup(int)));
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newgroup->getChatForm(), SLOT(focusInput()));
@@ -1174,17 +1059,17 @@ void Widget::onEventIconTick()
void Widget::setStatusOnline()
{
- core->setStatus(Status::Online);
+ Nexus::getCore()->setStatus(Status::Online);
}
void Widget::setStatusAway()
{
- core->setStatus(Status::Away);
+ Nexus::getCore()->setStatus(Status::Away);
}
void Widget::setStatusBusy()
{
- core->setStatus(Status::Busy);
+ Nexus::getCore()->setStatus(Status::Busy);
}
void Widget::onMessageSendResult(int friendId, const QString& message, int messageId)
diff --git a/src/widget/widget.h b/src/widget/widget.h
index 0a8307cbe..f43a301f7 100644
--- a/src/widget/widget.h
+++ b/src/widget/widget.h
@@ -54,14 +54,11 @@ public:
explicit Widget(QWidget *parent = 0);
void setCentralWidget(QWidget *widget, const QString &widgetName);
QString getUsername();
- Core* getCore();
- QThread* getCoreThread();
Camera* getCamera();
static Widget* getInstance();
void newMessageAlert(GenericChatroomWidget* chat);
bool isFriendWidgetCurActiveWidget(Friend* f);
bool getIsWindowMinimized();
- static QList searchProfiles();
void clearContactsList();
void setTranslation();
void updateTrayIcon();
@@ -69,7 +66,6 @@ public:
Q_INVOKABLE void setEnabledThreadsafe(bool enabled);
Q_INVOKABLE bool askQuestion(const QString& title, const QString& msg, bool defaultAns = false, bool warning = true);
Q_INVOKABLE QString passwordDialog(const QString& cancel, const QString& body);
- Q_INVOKABLE QString askProfiles();
// hooray for threading hacks
~Widget();
@@ -86,6 +82,29 @@ public slots:
void onSettingsClicked();
void setWindowTitle(const QString& title);
void forceShow();
+ void onConnected();
+ void onDisconnected();
+ void onStatusSet(Status status);
+ void onFailedToStartCore();
+ void onBadProxyCore();
+ void onSelfAvatarLoaded(const QPixmap &pic);
+ void setUsername(const QString& username);
+ void setStatusMessage(const QString &statusMessage);
+ void addFriend(int friendId, const QString& userId);
+ void addFriendFailed(const QString& userId, const QString& errorInfo = QString());
+ void onFriendStatusChanged(int friendId, Status status);
+ void onFriendStatusMessageChanged(int friendId, const QString& message);
+ void onFriendUsernameChanged(int friendId, const QString& username);
+ void onFriendMessageReceived(int friendId, const QString& message, bool isAction);
+ void onFriendRequestReceived(const QString& userId, const QString& message);
+ void onReceiptRecieved(int friendId, int receipt);
+ void onEmptyGroupCreated(int groupId);
+ void onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray invite);
+ void onGroupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
+ void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
+ void onGroupTitleChanged(int groupnumber, const QString& author, const QString& title);
+ void playRingtone();
+ void onFriendTypingChanged(int friendId, bool isTyping);
signals:
void friendRequestAccepted(const QString& userId);
@@ -98,34 +117,13 @@ signals:
void resized();
private slots:
- void onConnected();
- void onDisconnected();
- void onStatusSet(Status status);
void onAddClicked();
void onGroupClicked();
void onTransferClicked();
- void onFailedToStartCore();
- void onBadProxyCore();
void onAvatarClicked();
- void onSelfAvatarLoaded(const QPixmap &pic);
void onUsernameChanged(const QString& newUsername, const QString& oldUsername);
void onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage);
- void setUsername(const QString& username);
- void setStatusMessage(const QString &statusMessage);
- void addFriend(int friendId, const QString& userId);
- void addFriendFailed(const QString& userId, const QString& errorInfo = QString());
- void onFriendStatusChanged(int friendId, Status status);
- void onFriendStatusMessageChanged(int friendId, const QString& message);
- void onFriendUsernameChanged(int friendId, const QString& username);
void onChatroomWidgetClicked(GenericChatroomWidget *);
- void onFriendMessageReceived(int friendId, const QString& message, bool isAction);
- void onFriendRequestReceived(const QString& userId, const QString& message);
- void onReceiptRecieved(int friendId, int receipt);
- void onEmptyGroupCreated(int groupId);
- void onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray invite);
- void onGroupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
- void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
- void onGroupTitleChanged(int groupnumber, const QString& author, const QString& title);
void removeFriend(int friendId);
void copyFriendIdToClipboard(int friendId);
void removeGroup(int groupId);
@@ -134,11 +132,9 @@ private slots:
void setStatusBusy();
void onMessageSendResult(int friendId, const QString& message, int messageId);
void onGroupSendResult(int groupId, const QString& message, int result);
- void playRingtone();
void onIconClick(QSystemTrayIcon::ActivationReason);
void onUserAwayCheck();
void onEventIconTick();
- void onFriendTypingChanged(int friendId, bool isTyping);
void onSetShowSystemTray(bool newValue);
void onSplitterMoved(int pos, int index);
@@ -151,7 +147,6 @@ private:
void removeGroup(Group* g, bool fake = false);
void saveWindowGeometry();
void saveSplitterGeometry();
- QString detectProfile();
SystemTrayIcon *icon;
QMenu *trayMenu;
QAction *statusOnline,
@@ -162,8 +157,6 @@ private:
Ui::MainWindow *ui;
QSplitter *centralLayout;
QPoint dragPosition;
- Core* core;
- QThread* coreThread;
AddFriendForm* addFriendForm;
SettingsWidget* settingsWidget;
FilesForm* filesForm;