1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Merge branch 'master' into chatlog_v3_1

Conflicts:
	qtox.pro
	res.qrc
	src/widget/form/chatform.cpp
	src/widget/form/chatform.h
	src/widget/form/genericchatform.cpp
	src/widget/tool/chatactions/chataction.h
	src/widget/tool/chatactions/messageaction.cpp
	src/widget/widget.cpp
	ui/chatArea/innerStyle.css
This commit is contained in:
krepa098 2015-02-02 14:31:44 +01:00
commit 52f220c9e4
90 changed files with 5586 additions and 1587 deletions

View File

@ -230,45 +230,18 @@ The following steps assume that MinGW is installed at "C:\MinGW". If you decided
###Setting up Path
Add MinGW/MSYS binaries to the system path to make them globally accessible.
Add MinGW/MSYS/CMake binaries to the system path to make them globally accessible.
Open Control Panel -> System and Security -> System -> Advanced system settings -> Environment Variables...
In the second box search for the PATH variable and press Edit...
The input box "Variable value:" should already contain some directories. Each directory is separated with a semicolon.
Extend the input box by adding ";C:\MinGW\bin;C:\MinGW\msys\1.0\bin". The very first semicolon must only be added if it is missing.
Extend the input box by adding ";C:\MinGW\bin;C:\MinGW\msys\1.0\bin;C:\Program Files (x86)\CMake 2.8\bin".
The very first semicolon must only be added if it is missing. CMake may be added by installer automatically.
###Cloning the Repository
Clone the repository (https://github.com/tux3/qTox.git) with your preferred Git client. [SmartGit](http://www.syntevo.com/smartgit/) is very nice for this task.
The following steps assume that you cloned the repository at "C:\qTox". If you decided to choose another location, replace corresponding parts.
###Tox Core
[jenkins.libtoxcore.so](http://jenkins.libtoxcore.so/job/libtoxcore-win32-i686/lastSuccessfulBuild/artifact/libtoxcore-win32-i686.zip)
provides a prebuild package of Tox Core. Download this package and extract its content to "C:\qTox\libs". You may have to create the directory "libs".
If you prefer to compile Tox Core on your own follow the instructions at https://github.com/irungentoo/toxcore/blob/master/INSTALL.md#windows
###OpenCV
Unfortunately there are no prebuild packages for OpenCV compiled with MinGW. Thus, you have to create your own.
First of all download and install the most recent version of CMake from
[cmake.org](http://www.cmake.org/cmake/resources/software.html).
Afterwards download OpenCV in version 2.4.9 from [sourceforge.net](http://sourceforge.net/projects/opencvlibrary/files/opencv-unix/2.4.9/opencv-2.4.9.zip/download) and extract the content of the source archive to "C:\qTox\libs". Furthermore, create a new directory named "opencv-build" in "C:\qTox\libs".
Now you should have the two directories "opencv-2.4.9" and "opencv-build" inside your "C:\qTox\libs" directory.
Run CMake (cmake-gui) and set up the input boxes "Where is the source code:" and "Where to build the binaries" with "C:\qTox\libs\opencv-2.4.9" and "C:\qTox\libs\opencv-build". Press configure and choose "MSYS Makefiles" in the drop down menu with "Use default native compilers". To start initial configuration press Finish. Given that qTox only needs some components of OpenCV it's recommended to disable not required modules. Furthermore, this will decrease compilation time of OpenCV dramatically. Each module begins with "BUILD_opencv_" and can be disabled by deselecting its entry. Use the "Search" input box for convenience. Disable all modules except of "core", "highgui" and "imgproc" (highgui depends on imgproc and will automatically be disabled if imgproc is disabled). For maximum performance search for "CMAKE_BUILD_TYPE" and set this value to "Release". Finally, make sure "CMAKE_INSTALL_PREFIX" points to "C:\qTox\libs\opencv-build\install" (should be by default). To update the configuration press Configure again. To generate the Makefiles press Generate.
Open a new command prompt within "C:\qTox\libs\opencv-build" (HINT: Use shift + right click -> "Open command window here" on the directory within Windows Explorer). Compile and install OpenCV with the following command. It's not recommended to use -j for multicore compilation, because it freezes the terminal from time to time.
```bash
make
make install
```
After OpenCV was successfully installed to "C:\qTox\libs\opencv-build\install" copy the dlls "libopencv_core249.dll", "libopencv_highgui249.dll" and "libopencv_imgproc249.dll" located at "C:\qTox\libs\opencv-build\install\x86\mingw\bin" to "C:\qTox\libs\lib". Afterwards copy the content of the directory "C:\qTox\libs\opencv-build\install\include" to "C:\qTox\libs\include". Finally, you have to patch the file "C:\qTox\libs\include\opencv2\opencv.hpp" because it includes all modules of OpenCV regardless of your configuration. Open this file with your preferred text editor and remove all includes except of "opencv2/core/core_c.h", "opencv2/core/core.hpp", "opencv2/imgproc/imgproc_c.h", "opencv2/imgproc/imgproc.hpp", "opencv2/highgui/highgui_c.h" and "opencv2/highgui/highgui.hpp". OpenCV is now ready to use. Feel free to delete the directories "opencv-2.4.9" and "opencv-build", but you don't need to.
###OpenAL Soft
As for OpenCV there are no prebuild packages of OpenAL Softe compiled with MinGW, but the installation process is very similar to OpenCV. Download the most recent source archive of OpenAL Soft from [http://kcat.strangesoft.net](http://kcat.strangesoft.net/openal.html#download). Extract its content to "C:\qTox\libs". Besides the source folder itself you'll find the file "pax_global_header". It is not required and can be deleted. Create the directory "openal-build" next to source folder. Now you should have the two directories "openal-soft-x.y.z" where x.y.z is the version of OpenAL and "openal-build" inside your "C:\qTox\libs" directory. Run CMake (cmake-gui) and setup the source and build location. Run the initial configuration and use "MSYS Makefiles" with "Use default native compilers". The only thing you need to configure is "CMAKE_INSTALL_PREFIX" which does not point to "C:\qTox\libs\openal-build\install" by default. Configure the project and generate the Makefiles. Compile and install OpenAL Soft with:
```bash
make
make install
```
Copy the dll "OpenAL32.dll" located at "C:\qTox\libs\openal-build\install\bin" to "C:\qTox\libs\lib". Finally, copy the directory "AL" located at "C:\qTox\libs\openal-build\install\include" to "C:\qTox\libs\include". Unlike OpenCV you don't need to patch any files. Feel free to delete the directories "openal-soft-x.y.z" and "openal-build", but you don't need to.
### Getting dependencies
Run bootstrap.bat in cloned C:\qTox directory
Script will download rest of dependencies compile them and put to appropriate directories.

View File

@ -20,9 +20,10 @@ qTox is a powerful Tox client that tries to follow the Tox design guidelines whi
This client runs on Windows, Linux and Mac natively.<br/>
<a href="http://207.12.89.155:8080/job/qTox-win64-nsis/lastSuccessfulBuild/artifact/setup-qtox64.exe">Windows 64 bit download</a><br/>
<a href="http://207.12.89.155:8080/job/qTox-win32-nsis/lastSuccessfulBuild/artifact/setup-qtox32.exe">Windows 32 bit download (for older hardware)</a><br/>
<a href="https://dist-build.tox.im/qtox.dmg">Mac OS X download </a><br/>
<a href="https://tux3-dev.tox.im/jenkins/job/qTox-win64-nsis/lastSuccessfulBuild/artifact/setup-qtox64.exe">Windows 64 bit download</a><br/>
<a href="https://tux3-dev.tox.im/jenkins/job/qTox-win32-nsis/lastSuccessfulBuild/artifact/setup-qtox32.exe">Windows 32 bit download (for older hardware)</a><br/>
<a href="https://jenkins.libtoxcore.so/job/qTox%20OS%20X/lastSuccessfulBuild/artifact/qtox.dmg">Mac OS X download </a><br/>
<a href="https://jenkins.libtoxcore.so/job/qTox-linux-amd64/lastSuccessfulBuild/artifact/qt/qtox.xz">Linux binary download</a><br/>
<a href="https://jenkins.libtoxcore.so/user/tux3/my-views/view/qTox/job/qTox-Linux-pkg/lastSuccessfulBuild/artifact/">Linux packages</a><br/>
@ -37,6 +38,6 @@ This client runs on Windows, Linux and Mac natively.<br/>
##Developer overview:
[GitStats](http://104.219.184.93/index.html)<br/>
[GitStats](https://tux3-dev.tox.im/)<br/>
[Mac & Linux jenkins](https://jenkins.libtoxcore.so/user/tux3/my-views/view/qTox/)<br/>
[Windows jenkins](http://104.219.184.93:8080)<br/>
[Windows jenkins](https://tux3-dev.tox.im/jenkins)<br/>

View File

@ -1,4 +1,4 @@
@mkdir %~dp0libs
%~dp0tools\wget --no-check-certificate http://jenkins.libtoxcore.so/job/libtoxcore-win32-i686/lastSuccessfulBuild/artifact/libtoxcore-win32-i686.zip -O %~dp0libs\libtoxcore-latest.zip
%~dp0tools\unzip -o %~dp0libs\libtoxcore-latest.zip -d %~dp0libs\
@del %~dp0libs\libtoxcore-latest.zip
@echo off
sh bootstrap.sh
@pause

View File

@ -1,5 +1,12 @@
#!/bin/bash
WINDOWS_VERSION=$(cmd.exe /c ver 2>/dev/null | grep "Microsoft Windows")
if [ ! -z "$WINDOWS_VERSION" ]; then
cd windows
./bootstrap.sh
exit $?
fi
################ parameters ################
# directory where the script is located
SCRIPT_DIR=$( cd $(dirname $0); pwd -P)
@ -67,7 +74,7 @@ while [ $# -ge 1 ] ; do
echo ""
fi
# print help
# print help
echo "Use this script to install/update libsodium and libtoxcore in ${INSTALL_DIR}"
echo ""
echo "usage:"

View File

@ -14,6 +14,17 @@ else
INCLUDE_DIR="$2/include/"
fi
WINDOWS_VERSION=$(cmd.exe /c ver 2>/dev/null | grep "Microsoft Windows")
if [ ! -z "$WINDOWS_VERSION" ]; then
EXT="dll"
BIN_DIR="$2/bin/"
STATIC_EXT="$EXT.a"
else
BIN_DIR=$LIB_DIR
EXT="so"
STATIC_EXT="a"
fi
echo "Cloning filter_audio from GitHub.com"
git clone https://github.com/irungentoo/filter_audio.git $SOURCE_DIR
@ -22,16 +33,21 @@ cd $SOURCE_DIR
gcc -c -fPIC filter_audio.c aec/*.c agc/*.c ns/*.c other/*.c -lm -lpthread
echo "Creating shared object file"
gcc *.o -shared -o libfilteraudio.so
gcc *.o -shared -o libfilteraudio.$EXT -Wl,--out-implib,libfilteraudio.$STATIC_EXT
echo "Cleaning up"
rm *.o
muhcmd="cp libfilteraudio.so $LIB_DIR"
muhcmd="cp libfilteraudio.$EXT $BIN_DIR"
[ -z "$2" ] && muhcmd="sudo $muhcmd"
echo "Installing libfilteraudio.so with $muhcmd"
$muhcmd
muhcmd="cp libfilteraudio.$STATIC_EXT $LIB_DIR"
[ -z "$2" ] && muhcmd="sudo $muhcmd"
echo "Installing libfilteraudio.$STATIC_EXT with $muhcmd"
$muhcmd
muhcmd="cp *.h $INCLUDE_DIR"
[ -z "$2" ] && muhcmd="sudo $muhcmd"
echo "Installing include files with $muhcmd"

View File

@ -54,6 +54,23 @@ TIMESTAMP = $$system($1 2>null||echo 0||a;rm null;date +%s||echo 0) # I'm so sor
DEFINES += TIMESTAMP=$$TIMESTAMP
DEFINES += LOG_TO_FILE
contains(ENABLE_SYSTRAY_UNITY_BACKEND, YES) {
DEFINES += ENABLE_SYSTRAY_UNITY_BACKEND
INCLUDEPATH += "/usr/include/libappindicator-0.1"
INCLUDEPATH += "/usr/include/gtk-2.0"
INCLUDEPATH += "/usr/include/glib-2.0"
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/glib-2.0/include"
INCLUDEPATH += "/usr/include/cairo"
INCLUDEPATH += "/usr/include/pango-1.0"
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/gtk-2.0/include"
INCLUDEPATH += "/usr/include/gdk-pixbuf-2.0"
INCLUDEPATH += "/usr/include/atk-1.0"
INCLUDEPATH += "/usr/include/libdbusmenu-glib-0.4"
LIBS += -lgobject-2.0 -lappindicator -lgtk-x11-2.0
}
contains(DISABLE_PLATFORM_EXT, YES) {
} else {
@ -75,9 +92,9 @@ contains(JENKINS,YES) {
# Rules for Windows, Mac OSX, and Linux
win32 {
RC_FILE = windows/qtox.rc
LIBS += -liphlpapi -L$$PWD/libs/lib -lsodium -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lvpx -lpthread
LIBS += -L$$PWD/libs/lib -lopencv_core248 -lopencv_highgui248 -lopencv_imgproc248 -lOpenAL32 -lopus
LIBS += -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -ljpeg -ltiff -lpng -ljasper -lIlmImf -lHalf -lws2_32 -lz
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
contains(DEFINES, QTOX_FILTER_AUDIO) {
contains(STATICPKG, YES) {
@ -195,7 +212,9 @@ HEADERS += src/widget/form/addfriendform.h \
src/chatlog/content/notificationicon.h \
src/chatlog/content/timestamp.h \
src/chatlog/documentcache.h \
src/chatlog/pixmapcache.h
src/chatlog/pixmapcache.h \
src/widget/callconfirmwidget.h \
src/widget/systemtrayicon.h \
SOURCES += \
src/widget/form/addfriendform.cpp \
@ -268,7 +287,9 @@ SOURCES += \
src/chatlog/content/notificationicon.cpp \
src/chatlog/content/timestamp.cpp \
src/chatlog/documentcache.cpp \
src/chatlog/pixmapcache.cpp
src/chatlog/pixmapcache.cpp \
src/widget/callconfirmwidget.cpp \
src/widget/systemtrayicon.cpp
contains(DEFINES, QTOX_FILTER_AUDIO) {
HEADERS += src/audiofilterer.h

View File

@ -124,6 +124,7 @@
<file>translations/fi.qm</file>
<file>translations/fr.qm</file>
<file>translations/it.qm</file>
<file>translations/lt.qm</file>
<file>translations/mannol.qm</file>
<file>translations/pirate.qm</file>
<file>translations/pl.qm</file>
@ -226,5 +227,8 @@
<file>ui/fileTransferInstance/arrow_white.svg</file>
<file>ui/fileTransferInstance/browse.svg</file>
<file>ui/fileTransferInstance/filetransferWidget.css</file>
<file>ui/acceptCall/acceptCall.png</file>
<file>ui/rejectCall/rejectCall.png</file>
<file>ui/volButton/volButtonDisabled.png</file>
</qresource>
</RCC>

View File

@ -6,8 +6,8 @@ if which apt-get; then
elif which pacman; then
sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss
elif which yum; then
yum groupinstall "Development Tools"
yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel libXScrnSaver-devel
sudo yum groupinstall "Development Tools"
sudo yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel libXScrnSaver-devel
else
echo "Unknown package manager, attempting to compile anyways"
fi

View File

@ -41,6 +41,7 @@ ALCdevice* Audio::alInDev{nullptr};
ALCdevice* Audio::alOutDev{nullptr};
ALCcontext* Audio::alContext{nullptr};
ALuint Audio::alMainSource{0};
float Audio::outputVolume{1.0};
void audioDebugLog(QString msg)
{
@ -64,6 +65,18 @@ Audio& Audio::getInstance()
return *instance;
}
Audio::~Audio()
{
qDebug() << "Deleting Audio";
audioThread->exit(0);
audioThread->wait();
if (audioThread->isRunning())
audioThread->terminate();
delete audioThread;
delete audioInLock;
delete audioOutLock;
}
void Audio::suscribeInput()
{
if (!alInDev)
@ -249,7 +262,10 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data,
return;
if (!call.alSources.contains(peer))
{
alGenSources(1, &call.alSources[peer]);
alSourcef(call.alSources[peer], AL_GAIN, outputVolume);
}
playAudioBuffer(call.alSources[peer], data, samples, channels, sample_rate);
}
@ -289,6 +305,7 @@ void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, u
ALint state;
alGetSourcei(alSource, AL_SOURCE_STATE, &state);
alSourcef(alSource, AL_GAIN, outputVolume);
if (state != AL_PLAYING)
alSourcePlay(alSource);
}

View File

@ -73,9 +73,11 @@ public:
static QThread* audioThread;
static ALCcontext* alContext;
static ALuint alMainSource;
static float outputVolume;
private:
explicit Audio()=default;
~Audio();
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate);
private:

View File

@ -65,9 +65,13 @@ const QString AutoUpdater::checkURI = AutoUpdater::updateServer+"/qtox/"+AutoUpd
const QString AutoUpdater::flistURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/flist";
const QString AutoUpdater::filesURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/files/";
bool AutoUpdater::abortFlag{false};
std::atomic_bool AutoUpdater::isDownloadingUpdate{false};
bool AutoUpdater::isUpdateAvailable()
{
if (isDownloadingUpdate)
return false;
VersionInfo newVersion = getUpdateVersion();
if (newVersion.timestamp <= TIMESTAMP
|| newVersion.versionString.isEmpty() || newVersion.versionString == GIT_VERSION)
@ -280,26 +284,33 @@ bool AutoUpdater::downloadUpdate()
if (platform.isEmpty())
return false;
bool expectFalse = false;
if (!isDownloadingUpdate.compare_exchange_strong(expectFalse,true))
return false;
// Get a list of files to update
QByteArray newFlistData = getUpdateFlist();
QList<UpdateFileMeta> newFlist = parseFlist(newFlistData);
QList<UpdateFileMeta> diff = genUpdateDiff(newFlist);
if (abortFlag)
{
isDownloadingUpdate = false;
return false;
}
qDebug() << "AutoUpdater: Need to update "<<diff.size()<<" files";
// Create an empty directory to download updates into
QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/";
QDir updateDir(updateDirStr);
if (updateDir.exists())
updateDir.removeRecursively();
QDir().mkdir(updateDirStr);
if (!updateDir.exists())
QDir().mkdir(updateDirStr);
updateDir = QDir(updateDirStr);
if (!updateDir.exists())
{
qWarning() << "AutoUpdater::downloadUpdate: Can't create update directory, aborting...";
isDownloadingUpdate = false;
return false;
}
@ -308,6 +319,7 @@ bool AutoUpdater::downloadUpdate()
if (!newFlistFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
qWarning() << "AutoUpdater::downloadUpdate: Can't save new flist file, aborting...";
isDownloadingUpdate = false;
return false;
}
newFlistFile.write(newFlistData);
@ -317,7 +329,19 @@ bool AutoUpdater::downloadUpdate()
for (UpdateFileMeta fileMeta : diff)
{
if (abortFlag)
{
isDownloadingUpdate = false;
return false;
}
// Skip files we already have
QFile fileFile(updateDirStr+fileMeta.installpath);
if (fileFile.open(QIODevice::ReadOnly) && fileFile.size() == (qint64)fileMeta.size)
{
qDebug() << "AutoUpdater: Skipping already downloaded file '"+fileMeta.installpath+"'";
fileFile.close();
continue;
}
qDebug() << "AutoUpdater: Downloading '"+fileMeta.installpath+"' ...";
@ -329,10 +353,14 @@ bool AutoUpdater::downloadUpdate()
// Download
UpdateFile file = getUpdateFile(fileMeta);
if (abortFlag)
{
isDownloadingUpdate = false;
return false;
}
if (file.data.isNull())
{
qWarning() << "AutoUpdater::downloadUpdate: Error downloading a file, aborting...";
isDownloadingUpdate = false;
return false;
}
@ -341,20 +369,24 @@ bool AutoUpdater::downloadUpdate()
file.data.size(), key) != 0)
{
qCritical() << "AutoUpdater: downloadUpdate: RECEIVED FORGED FILE, aborting...";
isDownloadingUpdate = false;
return false;
}
// Save
QFile fileFile(updateDirStr+fileMeta.installpath);
if (!fileFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
qWarning() << "AutoUpdater::downloadUpdate: Can't save new update file, aborting...";
isDownloadingUpdate = false;
return false;
}
fileFile.write(file.data);
fileFile.close();
}
qDebug() << "AutoUpdater::downloadUpdate: The update is ready, it'll be installed on the next restart";
isDownloadingUpdate = false;
return true;
}
@ -364,6 +396,9 @@ bool AutoUpdater::isLocalUpdateReady()
if (platform.isEmpty())
return false;
if (isDownloadingUpdate)
return false;
// Check that there's an update dir in the first place, valid or not
QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/";
QDir updateDir(updateDirStr);
@ -380,22 +415,15 @@ bool AutoUpdater::isLocalUpdateReady()
QList<UpdateFileMeta> updateFlist = parseFlist(updateFlistData);
QList<UpdateFileMeta> diff = genUpdateDiff(updateFlist);
// If the update wasn't downloaded correctly, redownload it
// We don't check signatures to not block qTox too long, the updater will do it anyway
// Check that we have every file
for (UpdateFileMeta fileMeta : diff)
{
if (!QFile::exists(updateDirStr+fileMeta.installpath))
{
QtConcurrent::run(&AutoUpdater::downloadUpdate);
return false;
}
QFile f(updateDirStr+fileMeta.installpath);
if (f.size() != (int64_t)fileMeta.size)
{
QtConcurrent::run(&AutoUpdater::downloadUpdate);
return false;
}
}
return true;
@ -446,6 +474,9 @@ fail:
void AutoUpdater::checkUpdatesAsyncInteractive()
{
if (isDownloadingUpdate)
return;
QtConcurrent::run(&AutoUpdater::checkUpdatesAsyncInteractiveWorker);
}
@ -454,7 +485,12 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker()
if (!isUpdateAvailable())
return;
if (Widget::getInstance()->askMsgboxQuestion(QObject::tr("Update", "The title of a message box"),
// If there's already an update dir, resume updating, otherwise ask the user
QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/";
QDir updateDir(updateDirStr);
if ((updateDir.exists() && QFile(updateDirStr+"flist").exists())
|| Widget::getInstance()->askMsgboxQuestion(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.")))
{
downloadUpdate();
@ -464,4 +500,5 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker()
void AutoUpdater::abortUpdates()
{
abortFlag = true;
isDownloadingUpdate = false;
}

View File

@ -21,9 +21,10 @@
#include <QString>
#include <QList>
#include <sodium.h>
#include <atomic>
/// For now we only support auto updates on Windows, although extending it is not a technical issue.
/// Linux and Mac users are expected to use their package managers or update manually through official channels.
/// For now we only support auto updates on Windows and OS X, although extending it is not a technical issue.
/// Linux users are expected to use their package managers or update manually through official channels.
#ifdef Q_OS_WIN
#define AUTOUPDATE_ENABLED 1
#elif defined(Q_OS_OSX)
@ -122,6 +123,7 @@ private:
static const QString updaterBin; ///< Path to the qtox-updater binary
static unsigned char key[];
static bool abortFlag; ///< If true, try to abort everything.
static std::atomic_bool isDownloadingUpdate; ///< We'll pretend there's no new update available if we're already updating
};
#endif // AUTOUPDATE_H

View File

@ -84,7 +84,7 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
}
// OpenAL init
QString outDevDescr = Settings::getInstance().getOutDev(); ;
QString outDevDescr = Settings::getInstance().getOutDev();
Audio::openOutput(outDevDescr);
QString inDevDescr = Settings::getInstance().getInDev();
Audio::openInput(inDevDescr);
@ -92,9 +92,19 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
Core::~Core()
{
qDebug() << "Deleting Core";
clearPassword(Core::ptMain);
clearPassword(Core::ptHistory);
toxTimer->stop();
coreThread->exit(0);
while (coreThread->isRunning())
{
qApp->processEvents();
coreThread->wait(500);
}
if (tox)
{
toxav_kill(toxav);
@ -355,7 +365,7 @@ void Core::bootstrapDht()
}
static int j = qrand() % listSize;
qDebug() << "Core: Bootstraping to the DHT ...";
qDebug() << "Core: Bootstrapping to the DHT ...";
int i=0;
while (i < 2) // i think the more we bootstrap, the more we jitter because the more we overwrite nodes
@ -363,10 +373,10 @@ void Core::bootstrapDht()
const Settings::DhtServer& dhtServer = dhtServerList[j % listSize];
if (tox_bootstrap_from_address(tox, dhtServer.address.toLatin1().data(),
dhtServer.port, CUserId(dhtServer.userId).data()) == 1)
qDebug() << QString("Core: Bootstraping from ")+dhtServer.name+QString(", addr ")+dhtServer.address.toLatin1().data()
qDebug() << QString("Core: Bootstrapping from ")+dhtServer.name+QString(", addr ")+dhtServer.address.toLatin1().data()
+QString(", port ")+QString().setNum(dhtServer.port);
else
qDebug() << "Core: Error bootstraping from "+dhtServer.name;
qDebug() << "Core: Error bootstrapping from "+dhtServer.name;
j++;
i++;
@ -731,7 +741,7 @@ void Core::acceptFriendRequest(const QString& userId)
void Core::requestFriendship(const QString& friendAddress, const QString& message)
{
const QString userId = friendAddress.mid(0, TOX_CLIENT_ID_SIZE * 2);
const QString userId = friendAddress.mid(0, TOX_PUBLIC_KEY_SIZE * 2);
if (hasFriendWithAddress(friendAddress))
{
@ -1240,8 +1250,6 @@ bool Core::loadConfiguration(QString path)
// tox core is already decrypted
if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs())
{
bool error = true;
// get salt
QFile file(HistoryKeeper::getHistoryPath());
file.open(QIODevice::ReadOnly);
@ -1255,6 +1263,7 @@ bool Core::loadConfiguration(QString path)
}
else
{
bool error = true;
do
{
while (!pwsaltedkeys[ptHistory])
@ -1294,6 +1303,9 @@ bool Core::loadConfiguration(QString path)
void Core::saveConfiguration()
{
if (QThread::currentThread() != coreThread)
return (void) QMetaObject::invokeMethod(this, "saveConfiguration");
QString dir = Settings::getSettingsDirPath();
QDir directory(dir);
if (!directory.exists() && !directory.mkpath(directory.absolutePath())) {
@ -1320,6 +1332,9 @@ void Core::saveConfiguration()
void Core::saveConfiguration(const QString& path)
{
if (QThread::currentThread() != coreThread)
return (void) QMetaObject::invokeMethod(this, "saveConfiguration", Q_ARG(const QString&, path));
if (!tox)
{
qWarning() << "Core::saveConfiguration: Tox not started, aborting!";
@ -1329,27 +1344,28 @@ void Core::saveConfiguration(const QString& path)
Settings::getInstance().save();
QSaveFile configurationFile(path);
if (!configurationFile.open(QIODevice::WriteOnly)) {
if (!configurationFile.open(QIODevice::WriteOnly))
{
qCritical() << "File " << path << " cannot be opened";
return;
}
qDebug() << "Core: writing tox_save to " << path;
uint32_t fileSize; bool encrypt = Settings::getInstance().getEncryptTox();
uint32_t fileSize;
bool encrypt = Settings::getInstance().getEncryptTox();
if (encrypt)
fileSize = tox_encrypted_size(tox);
else
fileSize = tox_size(tox);
if (fileSize > 0 && fileSize <= INT32_MAX) {
if (fileSize > 0 && fileSize <= INT32_MAX)
{
uint8_t *data = new uint8_t[fileSize];
if (encrypt)
{
if (!pwsaltedkeys[ptMain])
{
// probably zero chance event
Widget::getInstance()->showWarningMsgBox(tr("NO Password"), tr("Will be saved without encryption!"));
tox_save(tox, data);
}
@ -1364,7 +1380,9 @@ void Core::saveConfiguration(const QString& path)
}
}
else
{
tox_save(tox, data);
}
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
configurationFile.commit();
@ -1420,7 +1438,7 @@ void Core::loadFriends()
// assuming there are not that many friends to fill up the whole stack
int32_t *ids = new int32_t[friendCount];
tox_get_friendlist(tox, ids, friendCount);
uint8_t clientId[TOX_CLIENT_ID_SIZE];
uint8_t clientId[TOX_PUBLIC_KEY_SIZE];
for (int32_t i = 0; i < static_cast<int32_t>(friendCount); ++i) {
if (tox_get_client_id(tox, ids[i], clientId) == 0) {
emit friendAdded(ids[i], CUserId::toString(clientId));
@ -1481,7 +1499,7 @@ ToxID Core::getGroupPeerToxID(int groupId, int peerId) const
{
ToxID peerToxID;
uint8_t rawID[TOX_CLIENT_ID_SIZE];
uint8_t rawID[TOX_PUBLIC_KEY_SIZE];
int res = tox_group_peer_pubkey(tox, groupId, peerId, rawID);
if (res == -1)
{
@ -1687,14 +1705,14 @@ bool Core::hasFriendWithAddress(const QString &addr) const
return false;
}
QString pubkey = addr.left(TOX_CLIENT_ID_SIZE * 2);
QString pubkey = addr.left(TOX_PUBLIC_KEY_SIZE * 2);
return hasFriendWithPublicKey(pubkey);
}
bool Core::hasFriendWithPublicKey(const QString &pubkey) const
{
// Valid length check
if (pubkey.length() != (TOX_CLIENT_ID_SIZE * 2))
if (pubkey.length() != (TOX_PUBLIC_KEY_SIZE * 2))
{
return false;
}
@ -1727,9 +1745,9 @@ bool Core::hasFriendWithPublicKey(const QString &pubkey) const
QString Core::getFriendAddress(int friendNumber) const
{
// If we don't know the full address of the client, return just the id, otherwise get the full address
uint8_t rawid[TOX_CLIENT_ID_SIZE];
uint8_t rawid[TOX_PUBLIC_KEY_SIZE];
tox_get_client_id(tox, friendNumber, rawid);
QByteArray data((char*)rawid,TOX_CLIENT_ID_SIZE);
QByteArray data((char*)rawid,TOX_PUBLIC_KEY_SIZE);
QString id = data.toHex().toUpper();
QString addr = Settings::getInstance().getFriendAdress(id);

View File

@ -64,9 +64,6 @@ public:
bool hasFriendWithPublicKey(const QString &pubkey) const; ///< Check if we have a friend by public key
int joinGroupchat(int32_t friendNumber, uint8_t type, const uint8_t* pubkey,uint16_t length) const; ///< Accept a groupchat invite
void quitGroupChat(int groupId) const; ///< Quit a groupchat
void saveConfiguration();
void saveConfiguration(const QString& path);
QString getIDString() const; ///< Get the 12 first characters of our Tox ID
@ -88,6 +85,9 @@ public slots:
void bootstrapDht(); ///< Connects us to the Tox network
void switchConfiguration(const QString& profile); ///< Load a different profile and restart the core
void saveConfiguration();
void saveConfiguration(const QString& path);
void acceptFriendRequest(const QString& userId);
void requestFriendship(const QString& friendAddress, const QString& message);
void groupInviteFriend(int friendId, int groupId);
@ -117,6 +117,7 @@ public slots:
void pauseResumeFileRecv(int friendId, int fileNum);
void answerCall(int callId);
void rejectCall(int callId);
void hangupCall(int callId);
void startCall(int friendId, bool video=false);
void cancelCall(int callId, int friendId);

View File

@ -26,7 +26,7 @@
ToxCall Core::calls[TOXAV_MAX_CALLS];
#ifdef QTOX_FILTER_AUDIO
AudioFilterer * Core::filterer[TOXAV_MAX_CALLS] { nullptr};
AudioFilterer * Core::filterer[TOXAV_MAX_CALLS] {nullptr};
#endif
const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4};
uint8_t* Core::videobuf;
@ -159,6 +159,13 @@ void Core::hangupCall(int callId)
toxav_hangup(toxav, callId);
}
void Core::rejectCall(int callId)
{
qDebug() << QString("Core: rejecting call %1").arg(callId);
calls[callId].active = false;
toxav_reject(toxav, callId, nullptr);
}
void Core::startCall(int friendId, bool video)
{
int callId;
@ -201,7 +208,7 @@ void Core::cancelCall(int callId, int friendId)
{
qDebug() << QString("Core: Cancelling call with %1").arg(friendId);
calls[callId].active = false;
toxav_cancel(toxav, callId, friendId, 0);
toxav_cancel(toxav, callId, friendId, nullptr);
}
void Core::cleanupCall(int callId)
@ -245,10 +252,11 @@ void Core::sendCallAudio(int callId, ToxAv* toxav)
const int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000 * av_DefaultSettings.audio_channels;
const int bufsize = framesize * 2 * av_DefaultSettings.audio_channels;
uint8_t buf[bufsize], dest[bufsize];
uint8_t buf[bufsize];
if (Audio::tryCaptureSamples(buf, framesize))
{
uint8_t dest[bufsize];
int r;
if ((r = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize)) < 0)
{
@ -551,6 +559,7 @@ void Core::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, un
ALint state;
alGetSourcei(alSource, AL_SOURCE_STATE, &state);
alSourcef(alSource, AL_GAIN, Audio::outputVolume);
if (state != AL_PLAYING)
{
alSourcePlay(alSource);
@ -618,9 +627,8 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
if (Audio::tryCaptureSamples(buf, framesize))
{
int r;
if ((r = toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf,
framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate)) < 0)
if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf,
framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate) < 0)
{
qDebug() << "Core: toxav_group_send_audio error";
groupCalls[groupId].sendAudioTimer->start();

View File

@ -33,6 +33,13 @@ bool ToxFile::open(bool write)
return write ? file->open(QIODevice::ReadWrite) : file->open(QIODevice::ReadOnly);
}
ToxID::ToxID(const ToxID& other)
{
publicKey = other.publicKey;
noSpam = other.noSpam;
checkSum = other.checkSum;
}
QString ToxID::toString() const
{
return publicKey + noSpam + checkSum;

View File

@ -16,6 +16,9 @@ enum class Status : int {Online = 0, Away, Busy, Offline};
struct ToxID
{
ToxID()=default;
ToxID(const ToxID& other);
QString publicKey;
QString noSpam;
QString checkSum;

View File

@ -23,12 +23,11 @@
#include "src/misc/settings.h"
Friend::Friend(int FriendId, const ToxID &UserId)
: friendId(FriendId)
: userName{Core::getInstance()->getPeerName(UserId)},
userID{UserId}, friendId{FriendId}
{
hasNewEvents = 0;
friendStatus = Status::Offline;
userID = UserId;
userName = Core::getInstance()->getPeerName(UserId);
if (userName.size() == 0)
userName = UserId.publicKey;

View File

@ -27,7 +27,9 @@ struct Friend
{
public:
Friend(int FriendId, const ToxID &UserId);
Friend(const Friend& other)=delete;
~Friend();
Friend& operator=(const Friend& other)=delete;
void setName(QString name);
void setAlias(QString name);

View File

@ -63,10 +63,10 @@ private:
void updateAliases();
QPair<int, ChatType> getChatID(const QString &id_str, ChatType ct);
int getAliasID(const QString &id_str);
QString wrapMessage(const QString &str);
QString unWrapMessage(const QString &str);
ChatType convertToChatType(int);
static QString wrapMessage(const QString &str);
static QString unWrapMessage(const QString &str);
static ChatType convertToChatType(int);
GenericDdInterface *db;
QMap<QString, int> aliases;

View File

@ -66,12 +66,24 @@ int main(int argc, char *argv[])
parser.addHelpOption();
parser.addVersionOption();
parser.addPositionalArgument("uri", QObject::tr("Tox URI to parse"));
parser.addOption(QCommandLineOption("P", QObject::tr("Starts new instance and loads specified profile."), QObject::tr("profile")));
parser.addOption(QCommandLineOption("p", QObject::tr("Starts new instance and loads specified profile."), QObject::tr("profile")));
parser.process(a);
Settings::getInstance(); // Build our Settings singleton as soon as QApplication is ready, not before
if (parser.isSet("P"))
Settings::getInstance().setCurrentProfile(parser.value("P"));
if (parser.isSet("p"))
{
QString profile = parser.value("p");
if (QDir(Settings::getSettingsDirPath()).exists(profile + ".tox"))
{
qDebug() << "Setting profile to" << profile;
Settings::getInstance().setCurrentProfile(profile);
}
else
{
qWarning() << "Error: -p profile" << profile + ".tox" << "doesn't exist";
return EXIT_FAILURE;
}
}
sodium_init(); // For the auto-updater
@ -82,7 +94,7 @@ int main(int argc, char *argv[])
if (logfile.open(QIODevice::Append))
{
logFile->setDevice(&logfile);
*logFile << QDateTime::currentDateTime().toString("\nyyyy-dd-MM HH:mm:ss' file logger starting\n'");
*logFile << QDateTime::currentDateTime().toString("\nyyyy-MM-dd HH:mm:ss' file logger starting\n'");
qInstallMessageHandler(myMessageHandler);
}
else
@ -156,7 +168,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
}
else if (!ipc.isCurrentOwner() && !parser.isSet("P"))
else if (!ipc.isCurrentOwner() && !parser.isSet("p"))
{
time_t event = ipc.postEvent("$activate");
ipc.waitUntilProcessed(event);
@ -165,6 +177,7 @@ int main(int argc, char *argv[])
}
// Run
a.setQuitOnLastWindowClosed(false);
Widget* w = Widget::getInstance();
int errorcode = a.exec();

View File

@ -36,7 +36,7 @@ uint8_t* CData::data()
return cData;
}
uint16_t CData::size()
uint16_t CData::size() const
{
return cDataSize;
}
@ -56,7 +56,7 @@ uint16_t CData::fromString(const QString& data, uint8_t* cData)
// CUserId
const uint16_t CUserId::SIZE{TOX_CLIENT_ID_SIZE};
const uint16_t CUserId::SIZE{TOX_PUBLIC_KEY_SIZE};
CUserId::CUserId(const QString &userId) :
CData(userId, SIZE < userId.size() ? userId.size() : SIZE)

View File

@ -24,11 +24,13 @@ class CData
{
public:
uint8_t* data();
uint16_t size();
uint16_t size() const;
protected:
explicit CData(const QString& data, uint16_t byteSize);
CData(const CData& other)=delete;
virtual ~CData();
CData& operator=(const CData& other)=delete;
static QString toString(const uint8_t* cData, const uint16_t cDataSize);

View File

@ -46,7 +46,7 @@ uint8_t* CString::data()
return cString;
}
uint16_t CString::size()
uint16_t CString::size() const
{
return cStringSize;
}

View File

@ -31,7 +31,7 @@ public:
~CString();
uint8_t* data();
uint16_t size();
uint16_t size() const;
static QString toString(const uint8_t* cMessage, const uint16_t cMessageSize);
static uint16_t fromString(const QString& message, uint8_t* cMessage);

View File

@ -29,12 +29,7 @@
#include <QList>
#include <QStyleFactory>
#ifdef Q_OS_LINUX
#define SHOW_SYSTEM_TRAY_DEFAULT (bool) false
#else // OS is not linux
#define SHOW_SYSTEM_TRAY_DEFAULT (bool) true
#endif
const QString Settings::OLDFILENAME = "settings.ini";
const QString Settings::FILENAME = "qtox.ini";
@ -136,12 +131,15 @@ void Settings::load()
currentProfile = s.value("currentProfile", "").toString();
autoAwayTime = s.value("autoAwayTime", 10).toInt();
checkUpdates = s.value("checkUpdates", false).toBool();
showWindow = s.value("showWindow", true).toBool();
showInFront = s.value("showInFront", false).toBool();
groupAlwaysNotify = s.value("groupAlwaysNotify", false).toBool();
fauxOfflineMessaging = s.value("fauxOfflineMessaging", true).toBool();
autoSaveEnabled = s.value("autoSaveEnabled", false).toBool();
globalAutoAcceptDir = s.value("globalAutoAcceptDir",
QStandardPaths::locate(QStandardPaths::HomeLocation, QString(), QStandardPaths::LocateDirectory)
).toString();
compactLayout = s.value("compactLayout", false).toBool();
s.endGroup();
s.beginGroup("Advanced");
@ -201,6 +199,10 @@ void Settings::load()
filterAudio = s.value("filterAudio", false).toBool();
s.endGroup();
s.beginGroup("Video");
camVideoRes = s.value("camVideoRes",QSize()).toSize();
s.endGroup();
// Read the embedded DHT bootsrap nodes list if needed
if (dhtServerList.isEmpty())
{
@ -289,8 +291,11 @@ void Settings::save(QString path, bool writeFriends)
s.setValue("currentProfile", currentProfile);
s.setValue("autoAwayTime", autoAwayTime);
s.setValue("checkUpdates", checkUpdates);
s.setValue("showWindow", showWindow);
s.setValue("showInFront", showInFront);
s.setValue("groupAlwaysNotify", groupAlwaysNotify);
s.setValue("fauxOfflineMessaging", fauxOfflineMessaging);
s.setValue("compactLayout", compactLayout);
s.setValue("autoSaveEnabled", autoSaveEnabled);
s.setValue("globalAutoAcceptDir", globalAutoAcceptDir);
s.endGroup();
@ -344,6 +349,10 @@ void Settings::save(QString path, bool writeFriends)
s.setValue("filterAudio", filterAudio);
s.endGroup();
s.beginGroup("Video");
s.setValue("camVideoRes",camVideoRes);
s.endGroup();
if (!writeFriends || currentProfile.isEmpty()) // Core::switchConfiguration
return;
@ -561,7 +570,17 @@ bool Settings::getShowInFront() const
void Settings::setShowInFront(bool newValue)
{
showInFront = newValue;
showInFront = newValue;
}
bool Settings::getGroupAlwaysNotify() const
{
return groupAlwaysNotify;
}
void Settings::setGroupAlwaysNotify(bool newValue)
{
groupAlwaysNotify = newValue;
}
QString Settings::getTranslation() const
@ -854,6 +873,16 @@ void Settings::setCheckUpdates(bool newValue)
checkUpdates = newValue;
}
bool Settings::getShowWindow() const
{
return showWindow;
}
void Settings::setShowWindow(bool newValue)
{
showWindow = newValue;
}
QByteArray Settings::getSplitterState() const
{
return splitterState;
@ -914,6 +943,16 @@ void Settings::setFilterAudio(bool newValue)
filterAudio = newValue;
}
QSize Settings::getCamVideoRes() const
{
return camVideoRes;
}
void Settings::setCamVideoRes(QSize newValue)
{
camVideoRes = newValue;
}
QString Settings::getFriendAdress(const QString &publicKey) const
{
QString key = ToxID::fromString(publicKey).publicKey;
@ -986,6 +1025,17 @@ void Settings::setFauxOfflineMessaging(bool value)
fauxOfflineMessaging = value;
}
bool Settings::getCompactLayout() const
{
return compactLayout;
}
void Settings::setCompactLayout(bool value)
{
compactLayout = value;
emit compactLayoutChanged();
}
int Settings::getThemeColor() const
{
return themeColor;

View File

@ -115,9 +115,15 @@ public:
bool getCheckUpdates() const;
void setCheckUpdates(bool newValue);
bool getShowWindow() const;
void setShowWindow(bool newValue);
bool getShowInFront() const;
void setShowInFront(bool newValue);
bool getGroupAlwaysNotify() const;
void setGroupAlwaysNotify(bool newValue);
QPixmap getSavedAvatar(const QString& ownerId);
void saveAvatar(QPixmap& pic, const QString& ownerId);
@ -133,6 +139,9 @@ public:
bool getFilterAudio() const;
void setFilterAudio(bool newValue);
QSize getCamVideoRes() const;
void setCamVideoRes(QSize newValue);
// Assume all widgets have unique names
// Don't use it to save every single thing you want to save, use it
// for some general purpose widgets, such as MainWindows or Splitters,
@ -223,6 +232,9 @@ public:
bool getFauxOfflineMessaging() const;
void setFauxOfflineMessaging(bool value);
bool getCompactLayout() const;
void setCompactLayout(bool compact);
public:
void save(bool writeFriends = true);
void save(QString path, bool writeFriends = true);
@ -246,6 +258,7 @@ private:
bool dontShowDhtDialog;
bool fauxOfflineMessaging;
bool compactLayout;
bool enableIPv6;
QString translation;
static bool makeToxPortable;
@ -255,7 +268,9 @@ private:
bool lightTrayIcon;
bool useEmoticons;
bool checkUpdates;
bool showWindow;
bool showInFront;
bool groupAlwaysNotify;
bool forceTCP;
@ -305,6 +320,9 @@ private:
QString outDev;
bool filterAudio;
// Video
QSize camVideoRes;
struct friendProp
{
QString alias;
@ -323,6 +341,7 @@ signals:
void smileyPackChanged();
void emojiFontChanged();
void timestampFormatChanged();
void compactLayoutChanged();
};
#endif // SETTINGS_HPP

View File

@ -130,7 +130,8 @@ bool SmileyPack::load(const QString& filename)
while (!stringElement.isNull())
{
QString emoticon = stringElement.text();
QString emoticon = stringElement.text()
.replace("<","&lt;").replace(">","&gt;");
filenameTable.insert(emoticon, file);
cacheSmiley(file); // preload all smileys

View File

@ -22,7 +22,7 @@
uint32_t Platform::getIdleTime()
{
LASTINPUTINFO info = { 0 };
LASTINPUTINFO info = { 0, 0 };
info.cbSize = sizeof(info);
if (GetLastInputInfo(&info))
return GetTickCount() - info.dwTime;

View File

@ -221,8 +221,8 @@ fallbackOnTox1:
toxIdStr = queryTox1(record, silent);
#elif TOX1_ASK_FALLBACK
QMessageBox::StandardButton btn = QMessageBox::warning(nullptr, "qTox", tr("It appears that qTox has to use the old tox1 protocol.\n\
Unfortunately tox1 is not secure. Should it be used anyway?"), QMessageBox::Ok|QMessageBox::No, QMessageBox::No);
if (btn == QMessageBox::Ok)
Unfortunately tox1 is not secure. Should it be used anyway?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
if (btn == QMessageBox::Yes)
queryTox1(record, silent);
#endif
return toxIdStr;

View File

@ -99,7 +99,7 @@ double Camera::getProp(Camera::Prop prop)
return worker->getProp(int(prop));
}
void Camera::onNewFrameAvailable(const VideoFrame frame)
void Camera::onNewFrameAvailable(const VideoFrame &frame)
{
emit frameAvailable(frame);

View File

@ -21,7 +21,7 @@
#include <QList>
#include <QMutex>
#include "vpx/vpx_image.h"
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "src/video/videosource.h"
class CameraWorker;
@ -79,7 +79,7 @@ private:
CameraWorker* worker;
private slots:
void onNewFrameAvailable(const VideoFrame frame);
void onNewFrameAvailable(const VideoFrame& frame);
};

View File

@ -29,6 +29,12 @@ CameraWorker::CameraWorker(int index)
qRegisterMetaType<QList<QSize>>();
}
CameraWorker::~CameraWorker()
{
if (clock)
delete clock;
}
void CameraWorker::onStart()
{
clock = new QTimer(this);
@ -112,7 +118,7 @@ void CameraWorker::_probeResolutions()
//qDebug() << "PROBING:" << res << " got " << w << h;
if (!resolutions.contains(QSize(w,h)))
if (w>0 && h>0 && !resolutions.contains(QSize(w,h)))
resolutions.append(QSize(w,h));
}

View File

@ -24,7 +24,7 @@
#include <QQueue>
#include <QSize>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "videosource.h"
class QTimer;
@ -34,6 +34,7 @@ class CameraWorker : public QObject
Q_OBJECT
public:
CameraWorker(int index);
~CameraWorker();
void doWork();
void suspend();
@ -48,7 +49,7 @@ public slots:
signals:
void started();
void newFrameAvailable(const VideoFrame frame);
void newFrameAvailable(const VideoFrame& frame);
void resProbingFinished(QList<QSize> res);
void propProbingFinished(int prop, double val);

View File

@ -19,7 +19,7 @@
#include "videosource.h"
class vpx_image;
struct vpx_image;
class NetVideoSource : public VideoSource
{

View File

@ -32,7 +32,7 @@ public:
virtual void unsubscribe() = 0;
signals:
void frameAvailable(const VideoFrame frame);
void frameAvailable(const VideoFrame& frame);
};

View File

@ -0,0 +1,93 @@
#include "callconfirmwidget.h"
#include "widget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QDialogButtonBox>
#include <QPainter>
#include <QPaintEvent>
#include <QRect>
#include <QPalette>
CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor) :
QWidget(Widget::getInstance()), anchor(Anchor),
rectW{120}, rectH{85},
spikeW{30}, spikeH{15},
roundedFactor{20},
rectRatio{static_cast<qreal>(rectH)/static_cast<qreal>(rectW)}
{
setWindowFlags(Qt::SubWindow);
QPalette palette;
palette.setColor(QPalette::WindowText, Qt::white);
setPalette(palette);
QVBoxLayout *layout = new QVBoxLayout(this);
QLabel *callLabel = new QLabel(QObject::tr("Incoming call..."), this);
callLabel->setAlignment(Qt::AlignHCenter);
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal, this);
QPushButton *accept = new QPushButton(this), *reject = new QPushButton(this);
accept->setFlat(true);
reject->setFlat(true);
accept->setStyleSheet("QPushButton{border:none;}");
reject->setStyleSheet("QPushButton{border:none;}");
accept->setIcon(QIcon(":/ui/acceptCall/acceptCall.png"));
reject->setIcon(QIcon(":/ui/rejectCall/rejectCall.png"));
accept->setIconSize(accept->size());
reject->setIconSize(reject->size());
buttonBox->addButton(accept, QDialogButtonBox::AcceptRole);
buttonBox->addButton(reject, QDialogButtonBox::RejectRole);
connect(buttonBox, &QDialogButtonBox::accepted, this, &CallConfirmWidget::accepted);
connect(buttonBox, &QDialogButtonBox::rejected, this, &CallConfirmWidget::rejected);
connect(Widget::getInstance(), &Widget::resized, this, &CallConfirmWidget::reposition);
layout->setMargin(12);
layout->addSpacing(spikeH);
layout->addWidget(callLabel);
layout->addWidget(buttonBox);
setFixedSize(rectW,rectH+spikeH);
reposition();
}
void CallConfirmWidget::reposition()
{
Widget* w = Widget::getInstance();
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
int xOverflow=0;
if (pos.x() + rectW > w->width())
xOverflow = pos.x() + rectW - w->width();
pos.rx() -= xOverflow;
mainRect = {0,spikeH,rectW,rectH};
brush = QBrush(QColor(65,65,65));
spikePoly = QPolygon({{(rectW-spikeW)/2+xOverflow,spikeH},
{rectW/2+xOverflow,0},
{(rectW+spikeW)/2+xOverflow,spikeH}});
move(pos);
update();
}
void CallConfirmWidget::paintEvent(QPaintEvent*)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(brush);
painter.setPen(Qt::NoPen);
painter.drawRoundRect(mainRect, roundedFactor*rectRatio, roundedFactor);
painter.drawPolygon(spikePoly);
}
void CallConfirmWidget::showEvent(QShowEvent*)
{
reposition();
update();
}

View File

@ -0,0 +1,45 @@
#ifndef CALLCONFIRMWIDGET_H
#define CALLCONFIRMWIDGET_H
#include <QWidget>
#include <QRect>
#include <QPolygon>
#include <QBrush>
class QPaintEvent;
class QShowEvent;
/// This is a widget with dialog buttons to accept/reject a call
/// It tracks the position of another widget called the anchor
/// and looks like a bubble at the bottom of that widget.
class CallConfirmWidget : public QWidget
{
Q_OBJECT
public:
explicit CallConfirmWidget(const QWidget *Anchor);
signals:
void accepted();
void rejected();
protected:
virtual void paintEvent(QPaintEvent* event) override;
virtual void showEvent(QShowEvent * event) override;
protected slots:
void reposition(); ///< Recalculate our positions to track the anchor
private:
const QWidget* anchor; ///< The widget we're going to be tracking
QRect mainRect;
QPolygon spikePoly;
QBrush brush;
const int rectW, rectH;
const int spikeW, spikeH;
const int roundedFactor; ///< By how much are the corners of the main rect rounded
const qreal rectRatio; ///< Used to correct the rounding factors on non-square rects
};
#endif // CALLCONFIRMWIDGET_H

View File

@ -98,8 +98,8 @@ void AddFriendForm::onSendTriggered()
if (Settings::getInstance().getProxyType() != ProxyType::ptNone)
{
QMessageBox::StandardButton btn = QMessageBox::warning(main, "qTox", tr("qTox needs to use the Tox DNS, but can't do it through a proxy.\n\
Ignore the proxy and connect to the Internet directly?"), QMessageBox::Ok|QMessageBox::No, QMessageBox::No);
if (btn != QMessageBox::Ok)
Ignore the proxy and connect to the Internet directly?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
if (btn != QMessageBox::Yes)
return;
}

View File

@ -24,13 +24,20 @@
#include <QDragEnterEvent>
#include <QBitmap>
#include "chatform.h"
#include "src/historykeeper.h"
#include "src/widget/form/loadhistorydialog.h"
#include "src/core.h"
#include "src/friend.h"
#include "src/filetransferinstance.h"
#include "src/historykeeper.h"
#include "src/misc/style.h"
#include "src/misc/settings.h"
#include "src/misc/cstring.h"
#include "src/widget/callconfirmwidget.h"
#include "src/widget/friendwidget.h"
#include "src/widget/netcamview.h"
#include "src/widget/chatareawidget.h"
#include "src/widget/form/loadhistorydialog.h"
#include "src/widget/tool/chattextedit.h"
#include "src/core.h"
#include "src/widget/tool/chatactions/filetransferaction.h"
#include "src/widget/widget.h"
#include "src/widget/maskablepixmapwidget.h"
#include "src/widget/croppinglabel.h"
@ -56,8 +63,13 @@ ChatForm::ChatForm(Friend* chatFriend)
statusMessageLabel->setFont(Style::getFont(Style::Medium));
statusMessageLabel->setMinimumHeight(Style::getFont(Style::Medium).pixelSize());
callConfirm = nullptr;
typingTimer.setSingleShot(true);
netcam = new NetCamView();
timer = nullptr;
callDurationTimer = nullptr;
disableCallButtonsTimer = nullptr;
chatWidget->setTypingNotification(ChatMessage::createTypingNotification());
@ -80,6 +92,7 @@ ChatForm::ChatForm(Friend* chatFriend)
connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle()));
connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
connect(this, SIGNAL(chatAreaCleared()), this, SLOT(clearReciepts()));
connect(&typingTimer, &QTimer::timeout, this, [=]{Core::getInstance()->sendTyping(f->getFriendID(), false);});
connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig) {
if (text != orig) emit aliasChanged(text);
} );
@ -90,6 +103,7 @@ ChatForm::ChatForm(Friend* chatFriend)
ChatForm::~ChatForm()
{
delete netcam;
delete callConfirm;
}
void ChatForm::setStatusMessage(QString newMessage)
@ -132,6 +146,8 @@ void ChatForm::onSendTriggered()
rec = Core::getInstance()->sendMessage(f->getFriendID(), qt_msg);
registerReceipt(rec, id, ma);
msgEdit->setLastMessage(msg); //set last message only when sending it
}
msgEdit->clear();
@ -145,11 +161,10 @@ void ChatForm::onTextEditChanged()
else
isNowTyping = msgEdit->toPlainText().length() > 0;
if (isTyping != isNowTyping)
{
isTyping = isNowTyping;
Core::getInstance()->sendTyping(f->getFriendID(), isTyping);
}
if (isNowTyping)
typingTimer.start(3000);
Core::getInstance()->sendTyping(f->getFriendID(), isNowTyping);
}
void ChatForm::onAttachClicked()
@ -245,20 +260,30 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
videoButton->disconnect();
if (video)
{
callConfirm = new CallConfirmWidget(videoButton);
if (isVisible())
callConfirm->show();
connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered);
connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered);
callButton->setObjectName("grey");
callButton->style()->polish(callButton);
videoButton->setObjectName("yellow");
videoButton->style()->polish(videoButton);
connect(videoButton, SIGNAL(clicked()), this, SLOT(onAnswerCallTriggered()));
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onAnswerCallTriggered);
}
else
{
callConfirm = new CallConfirmWidget(callButton);
if (isVisible())
callConfirm->show();
connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered);
connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered);
callButton->setObjectName("yellow");
callButton->style()->polish(callButton);
videoButton->setObjectName("grey");
videoButton->style()->polish(videoButton);
connect(callButton, SIGNAL(clicked()), this, SLOT(onAnswerCallTriggered()));
connect(callButton, &QPushButton::clicked, this, &ChatForm::onAnswerCallTriggered);
}
callButton->style()->polish(callButton);
videoButton->style()->polish(videoButton);
insertChatMessage(ChatMessage::createChatInfoMessage(tr("%1 calling").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime()));
@ -287,49 +312,38 @@ void ChatForm::onAvStart(int FriendId, int CallId, bool video)
if (video)
{
callButton->setObjectName("grey");
callButton->style()->polish(callButton);
videoButton->setObjectName("red");
videoButton->style()->polish(videoButton);
connect(videoButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered()));
connect(videoButton, SIGNAL(clicked()),
this, SLOT(onHangupCallTriggered()));
netcam->show(Core::getInstance()->getVideoSourceFromCall(CallId), f->getDisplayedName());
}
else
{
callButton->setObjectName("red");
callButton->style()->polish(callButton);
videoButton->setObjectName("grey");
videoButton->style()->polish(videoButton);
connect(callButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered()));
connect(callButton, SIGNAL(clicked()),
this, SLOT(onHangupCallTriggered()));
}
callButton->style()->polish(callButton);
videoButton->style()->polish(videoButton);
startCounter();
}
void ChatForm::onAvCancel(int FriendId, int)
{
if (FriendId != f->getFriendID())
return;
qDebug() << "onAvCancel";
delete callConfirm;
callConfirm = nullptr;
stopCounter();
audioInputFlag = false;
audioOutputFlag = false;
micButton->setObjectName("green");
micButton->style()->polish(micButton);
volButton->setObjectName("green");
volButton->style()->polish(volButton);
callButton->disconnect();
videoButton->disconnect();
callButton->setObjectName("green");
callButton->style()->polish(callButton);
videoButton->setObjectName("green");
videoButton->style()->polish(videoButton);
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
enableCallButtons();
netcam->hide();
@ -343,21 +357,11 @@ void ChatForm::onAvEnd(int FriendId, int)
qDebug() << "onAvEnd";
audioInputFlag = false;
audioOutputFlag = false;
micButton->setObjectName("green");
micButton->style()->polish(micButton);
volButton->setObjectName("green");
volButton->style()->polish(volButton);
callButton->disconnect();
videoButton->disconnect();
callButton->setObjectName("green");
callButton->style()->polish(callButton);
videoButton->setObjectName("green");
videoButton->style()->polish(videoButton);
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
netcam->hide();
stopCounter();
@ -379,7 +383,8 @@ void ChatForm::onAvRinging(int FriendId, int CallId, bool video)
callButton->style()->polish(callButton);
videoButton->setObjectName("yellow");
videoButton->style()->polish(videoButton);
connect(videoButton, SIGNAL(clicked()), this, SLOT(onCancelCallTriggered()));
connect(videoButton, SIGNAL(clicked()),
this, SLOT(onCancelCallTriggered()));
}
else
{
@ -387,7 +392,8 @@ void ChatForm::onAvRinging(int FriendId, int CallId, bool video)
callButton->style()->polish(callButton);
videoButton->setObjectName("grey");
videoButton->style()->polish(videoButton);
connect(callButton, SIGNAL(clicked()), this, SLOT(onCancelCallTriggered()));
connect(callButton, SIGNAL(clicked()),
this, SLOT(onCancelCallTriggered()));
}
addSystemInfoMessage(tr("Calling to %1").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime());
@ -431,22 +437,10 @@ void ChatForm::onAvEnding(int FriendId, int)
qDebug() << "onAvEnding";
audioInputFlag = false;
audioOutputFlag = false;
micButton->setObjectName("green");
micButton->style()->polish(micButton);
volButton->setObjectName("green");
volButton->style()->polish(volButton);
callButton->disconnect();
videoButton->disconnect();
callButton->setObjectName("green");
callButton->style()->polish(callButton);
callButton->disconnect();
videoButton->setObjectName("green");
videoButton->style()->polish(videoButton);
videoButton->disconnect();
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
netcam->hide();
@ -460,23 +454,11 @@ void ChatForm::onAvRequestTimeout(int FriendId, int)
qDebug() << "onAvRequestTimeout";
audioInputFlag = false;
audioOutputFlag = false;
micButton->setObjectName("green");
micButton->style()->polish(micButton);
volButton->setObjectName("green");
volButton->style()->polish(volButton);
callButton->disconnect();
videoButton->disconnect();
callButton->setObjectName("green");
callButton->style()->polish(callButton);
callButton->disconnect();
videoButton->setObjectName("green");
videoButton->style()->polish(videoButton);
videoButton->disconnect();
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
netcam->hide();
}
@ -487,22 +469,10 @@ void ChatForm::onAvPeerTimeout(int FriendId, int)
qDebug() << "onAvPeerTimeout";
audioInputFlag = false;
audioOutputFlag = false;
micButton->setObjectName("green");
micButton->style()->polish(micButton);
volButton->setObjectName("green");
volButton->style()->polish(volButton);
callButton->disconnect();
videoButton->disconnect();
callButton->setObjectName("green");
callButton->style()->polish(callButton);
callButton->disconnect();
videoButton->setObjectName("green");
videoButton->style()->polish(videoButton);
videoButton->disconnect();
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
netcam->hide();
}
@ -514,22 +484,10 @@ void ChatForm::onAvRejected(int FriendId, int)
qDebug() << "onAvRejected";
audioInputFlag = false;
audioOutputFlag = false;
micButton->setObjectName("green");
micButton->style()->polish(micButton);
volButton->setObjectName("green");
volButton->style()->polish(volButton);
callButton->disconnect();
videoButton->disconnect();
callButton->setObjectName("green");
callButton->style()->polish(callButton);
callButton->disconnect();
videoButton->setObjectName("green");
videoButton->style()->polish(videoButton);
videoButton->disconnect();
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
insertChatMessage(ChatMessage::createChatInfoMessage(tr("Call rejected"), ChatMessage::INFO, QDateTime::currentDateTime()));
@ -557,6 +515,12 @@ void ChatForm::onAnswerCallTriggered()
{
qDebug() << "onAnswerCallTriggered";
if (callConfirm)
{
delete callConfirm;
callConfirm = nullptr;
}
audioInputFlag = true;
audioOutputFlag = true;
emit answerCall(callId);
@ -569,16 +533,32 @@ void ChatForm::onHangupCallTriggered()
audioInputFlag = false;
audioOutputFlag = false;
emit hangupCall(callId);
micButton->setObjectName("green");
micButton->style()->polish(micButton);
volButton->setObjectName("green");
volButton->style()->polish(volButton);
enableCallButtons();
}
void ChatForm::onRejectCallTriggered()
{
qDebug() << "onRejectCallTriggered";
if (callConfirm)
{
delete callConfirm;
callConfirm = nullptr;
}
audioInputFlag = false;
audioOutputFlag = false;
emit rejectCall(callId);
enableCallButtons();
}
void ChatForm::onCallTriggered()
{
qDebug() << "onCallTriggered";
audioInputFlag = true;
audioOutputFlag = true;
callButton->disconnect();
@ -589,7 +569,7 @@ void ChatForm::onCallTriggered()
void ChatForm::onVideoCallTriggered()
{
qDebug() << "onVideoCallTriggered";
audioInputFlag = true;
audioOutputFlag = true;
callButton->disconnect();
@ -604,37 +584,80 @@ void ChatForm::onAvCallFailed(int FriendId)
qDebug() << "onAvCallFailed";
audioInputFlag = false;
audioOutputFlag = false;
callButton->disconnect();
videoButton->disconnect();
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
delete callConfirm;
callConfirm = nullptr;
enableCallButtons();
}
void ChatForm::onCancelCallTriggered()
{
qDebug() << "onCancelCallTriggered";
enableCallButtons();
netcam->hide();
emit cancelCall(callId, f->getFriendID());
}
void ChatForm::enableCallButtons()
{
qDebug() << "enableCallButtons";
audioInputFlag = false;
audioOutputFlag = false;
micButton->setObjectName("grey");
micButton->style()->polish(micButton);
micButton->disconnect();
volButton->setObjectName("grey");
volButton->style()->polish(volButton);
volButton->disconnect();
callButton->setObjectName("grey");
callButton->style()->polish(callButton);
callButton->disconnect();
videoButton->setObjectName("grey");
videoButton->style()->polish(videoButton);
videoButton->disconnect();
if(disableCallButtonsTimer == nullptr)
{
disableCallButtonsTimer = new QTimer();
connect(disableCallButtonsTimer, SIGNAL(timeout()),
this, SLOT(onEnableCallButtons()));
disableCallButtonsTimer->start(1500); // 1.5sec
qDebug() << "timer started!!";
}
}
void ChatForm::onEnableCallButtons()
{
qDebug() << "onEnableCallButtons";
audioInputFlag = false;
audioOutputFlag = false;
micButton->setObjectName("green");
micButton->style()->polish(micButton);
volButton->setObjectName("green");
volButton->style()->polish(volButton);
callButton->disconnect();
videoButton->disconnect();
callButton->setObjectName("green");
callButton->style()->polish(callButton);
callButton->disconnect();
videoButton->setObjectName("green");
videoButton->style()->polish(videoButton);
videoButton->disconnect();
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
netcam->hide();
emit cancelCall(callId, f->getFriendID());
connect(callButton, SIGNAL(clicked()),
this, SLOT(onCallTriggered()));
connect(videoButton, SIGNAL(clicked()),
this, SLOT(onVideoCallTriggered()));
connect(micButton, SIGNAL(clicked()),
this, SLOT(onMicMuteToggle()));
connect(volButton, SIGNAL(clicked()),
this, SLOT(onVolMuteToggle()));
disableCallButtonsTimer->stop();
delete disableCallButtonsTimer;
disableCallButtonsTimer = nullptr;
}
void ChatForm::onMicMuteToggle()
@ -695,6 +718,20 @@ void ChatForm::dropEvent(QDropEvent *ev)
{
QFileInfo info(url.path());
QFile file(info.absoluteFilePath());
if (!file.exists() || !file.open(QIODevice::ReadOnly))
{
QMessageBox::warning(this, tr("File not read"), tr("qTox wasn't able to open %1").arg(info.fileName()));
continue;
}
if (file.isSequential())
{
QMessageBox::critical(0, tr("Bad Idea"), tr("You're trying to send a special (sequential) file, that's not going to work!"));
file.close();
continue;
}
file.close();
if (info.exists())
Core::getInstance()->sendFile(f->getFriendID(), info.fileName(), info.absoluteFilePath(), info.size());
}
@ -805,11 +842,11 @@ void ChatForm::onLoadHistory()
void ChatForm::startCounter()
{
if (!timer)
if (!callDurationTimer)
{
timer = new QTimer();
connect(timer, SIGNAL(timeout()), this, SLOT(updateTime()));
timer->start(1000);
callDurationTimer = new QTimer();
connect(callDurationTimer, SIGNAL(timeout()), this, SLOT(onUpdateTime()));
callDurationTimer->start(1000);
timeElapsed.start();
callDuration->show();
}
@ -817,19 +854,20 @@ void ChatForm::startCounter()
void ChatForm::stopCounter()
{
if (timer)
if (callDurationTimer)
{
addSystemInfoMessage(tr("Call with %1 ended. %2").arg(f->getDisplayedName(),secondsToDHMS(timeElapsed.elapsed()/1000)),
ChatMessage::INFO, QDateTime::currentDateTime());
timer->stop();
callDurationTimer->stop();
callDuration->setText("");
callDuration->hide();
timer = nullptr;
delete timer;
delete callDurationTimer;
callDurationTimer = nullptr;
}
}
void ChatForm::updateTime()
void ChatForm::onUpdateTime()
{
callDuration->setText(secondsToDHMS(timeElapsed.elapsed()/1000));
}
@ -915,3 +953,17 @@ void ChatForm::deliverOfflineMsgs()
registerReceipt(rec, iter.key(), iter.value());
}
}
void ChatForm::show(Ui::MainWindow &ui)
{
GenericChatForm::show(ui);
if (callConfirm)
callConfirm->show();
}
void ChatForm::hideEvent(QHideEvent*)
{
if (callConfirm)
callConfirm->hide();
}

View File

@ -19,15 +19,19 @@
#include "genericchatform.h"
#include "src/corestructs.h"
#include <QSet>
#include <QLabel>
#include <QTimer>
#include <QElapsedTimer>
#include <QSet>
struct Friend;
class FileTransferInstance;
class NetCamView;
class QPixmap;
class CallConfirmWidget;
class QHideEvent;
class QMoveEvent;
class ChatForm : public GenericChatForm
{
@ -41,6 +45,8 @@ public:
void dischargeReceipt(int receipt);
void setFriendTyping(bool isTyping);
virtual void show(Ui::MainWindow &ui);
signals:
void sendFile(int32_t friendId, QString, QString, long long);
void startCall(int friendId);
@ -48,6 +54,7 @@ signals:
void answerCall(int callId);
void hangupCall(int callId);
void cancelCall(int callId, int friendId);
void rejectCall(int callId);
void micMuteToggle(int callId);
void volMuteToggle(int callId);
void aliasChanged(const QString& alias);
@ -83,15 +90,18 @@ private slots:
void onAnswerCallTriggered();
void onHangupCallTriggered();
void onCancelCallTriggered();
void onRejectCallTriggered();
void onFileSendFailed(int FriendId, const QString &fname);
void onLoadHistory();
void updateTime();
void onUpdateTime();
void onEnableCallButtons();
protected:
// drag & drop
void dragEnterEvent(QDragEnterEvent* ev);
void dropEvent(QDropEvent* ev);
void registerReceipt(int receipt, int messageID, ChatMessage::Ptr msg);
virtual void hideEvent(QHideEvent* event);
private:
Friend* f;
@ -99,7 +109,9 @@ private:
NetCamView* netcam;
int callId;
QLabel *callDuration;
QTimer *timer;
QTimer *callDurationTimer;
QTimer typingTimer;
QTimer *disableCallButtonsTimer;
QElapsedTimer timeElapsed;
QHash<uint, FileTransferInstance*> ftransWidgets;
@ -109,6 +121,8 @@ private:
QHash<int, int> receipts;
QMap<int, ChatMessage::Ptr> undeliveredMsgs;
bool isTyping;
CallConfirmWidget *callConfirm;
void enableCallButtons();
};
#endif // CHATFORM_H

View File

@ -55,9 +55,12 @@ GenericChatForm::GenericChatForm(QWidget *parent)
*mainFootLayout = new QHBoxLayout();
QVBoxLayout *mainLayout = new QVBoxLayout(),
*footButtonsSmall = new QVBoxLayout(),
*volMicLayout = new QVBoxLayout();
headTextLayout = new QVBoxLayout();
*footButtonsSmall = new QVBoxLayout(),
*micButtonsLayout = new QVBoxLayout();
QGridLayout *buttonsLayout = new QGridLayout();
headTextLayout = new QVBoxLayout();
chatWidget = new ChatLog(this);
chatWidget->setBusyNotification(ChatMessage::createBusyNotification());
@ -123,28 +126,37 @@ GenericChatForm::GenericChatForm(QWidget *parent)
mainFootLayout->addWidget(sendButton);
mainFootLayout->setSpacing(0);
headTextLayout->addStretch();
headTextLayout->addStretch();
headTextLayout->addWidget(nameLabel);
volMicLayout->addWidget(micButton, Qt::AlignTop);
volMicLayout->addSpacing(2);
volMicLayout->addWidget(volButton, Qt::AlignBottom);
headTextLayout->addStretch();
micButtonsLayout->setSpacing(0);
micButtonsLayout->addWidget(micButton, Qt::AlignTop | Qt::AlignRight);
micButtonsLayout->addSpacing(4);
micButtonsLayout->addWidget(volButton, Qt::AlignTop | Qt::AlignRight);
buttonsLayout->addLayout(micButtonsLayout, 0, 0, 2, 1, Qt::AlignTop | Qt::AlignRight);
buttonsLayout->addWidget(callButton, 0, 1, 2, 1, Qt::AlignTop);
buttonsLayout->addWidget(videoButton, 0, 2, 2, 1, Qt::AlignTop);
buttonsLayout->setVerticalSpacing(0);
buttonsLayout->setHorizontalSpacing(4);
headLayout->addWidget(avatar, Qt::AlignTop | Qt::AlignLeft);
headLayout->addSpacing(5);
headLayout->addLayout(headTextLayout, Qt::AlignTop | Qt::AlignAbsolute);
headLayout->addLayout(buttonsLayout, Qt::AlignTop | Qt::AlignRight);
headWidget->setLayout(headLayout);
headLayout->addWidget(avatar);
headLayout->addSpacing(5);
headLayout->addLayout(headTextLayout);
headLayout->addLayout(volMicLayout);
headLayout->addWidget(callButton);
headLayout->addSpacing(3);
headLayout->addWidget(videoButton);
headLayout->setSpacing(0);
//Fix for incorrect layouts on OS X as per
//https://bugreports.qt-project.org/browse/QTBUG-14591
sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
micButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
volButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
callButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
videoButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
menu.addActions(chatWidget->actions());
menu.addSeparator();

View File

@ -30,6 +30,7 @@ AdvancedForm::AdvancedForm() :
bodyUI->dbLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
bodyUI->dbLabel->setOpenExternalLinks(true);
bodyUI->cbMakeToxPortable->setChecked(Settings::getInstance().getMakeToxPortable());
bodyUI->syncTypeComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
bodyUI->syncTypeComboBox->addItems({tr("FULL - very safe, slowest (recommended)"),
tr("NORMAL - almost as safe as FULL, about 20% faster than FULL"),
@ -38,6 +39,7 @@ AdvancedForm::AdvancedForm() :
int index = 2 - static_cast<int>(Settings::getInstance().getDbSyncType());
bodyUI->syncTypeComboBox->setCurrentIndex(index);
connect(bodyUI->cbMakeToxPortable, &QCheckBox::stateChanged, this, &AdvancedForm::onMakeToxPortableUpdated);
connect(bodyUI->syncTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onDbSyncTypeUpdated()));
connect(bodyUI->resetButton, SIGNAL(clicked()), this, SLOT(resetToDefault()));
}
@ -47,6 +49,11 @@ AdvancedForm::~AdvancedForm()
delete bodyUI;
}
void AdvancedForm::onMakeToxPortableUpdated()
{
Settings::getInstance().setMakeToxPortable(bodyUI->cbMakeToxPortable->isChecked());
}
void AdvancedForm::onDbSyncTypeUpdated()
{
int index = 2 - bodyUI->syncTypeComboBox->currentIndex();

View File

@ -33,6 +33,7 @@ public:
virtual ~AdvancedForm();
private slots:
void onMakeToxPortableUpdated();
void onDbSyncTypeUpdated();
void resetToDefault();

View File

@ -29,6 +29,16 @@
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="cbMakeToxPortable">
<property name="toolTip">
<string extracomment="describes makeToxPortable checkbox">Save settings to the working directory instead of the usual conf dir</string>
</property>
<property name="text">
<string>Make Tox portable</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="warningLabel">
<property name="text">

View File

@ -47,6 +47,7 @@ AVForm::AVForm() :
connect(bodyUI->outDevCombobox, qcomboboxIndexChanged, this, &AVForm::onOutDevChanged);
connect(bodyUI->filterAudio, SIGNAL(toggled(bool)), this, SLOT(onFilterAudioToggled(bool)));
connect(bodyUI->rescanButton, &QPushButton::clicked, this, [=](){getAudioInDevices(); getAudioOutDevices();});
bodyUI->playbackSlider->setValue(100);
}
AVForm::~AVForm()
@ -67,6 +68,10 @@ void AVForm::present()
Camera::getInstance()->probeProp(Camera::HUE);
Camera::getInstance()->probeResolutions();
bodyUI->videoModescomboBox->blockSignals(true);
bodyUI->videoModescomboBox->addItem(tr("Initializing Camera..."));
bodyUI->videoModescomboBox->blockSignals(false);
}
void AVForm::on_ContrastSlider_sliderMoved(int position)
@ -89,9 +94,11 @@ void AVForm::on_HueSlider_sliderMoved(int position)
Camera::getInstance()->setProp(Camera::HUE, position / 100.0);
}
void AVForm::on_videoModescomboBox_activated(int index)
void AVForm::on_videoModescomboBox_currentIndexChanged(int index)
{
Camera::getInstance()->setResolution(bodyUI->videoModescomboBox->itemData(index).toSize());
QSize res = bodyUI->videoModescomboBox->itemData(index).toSize();
Settings::getInstance().setCamVideoRes(res);
Camera::getInstance()->setResolution(res);
}
void AVForm::onPropProbingFinished(Camera::Prop prop, double val)
@ -117,11 +124,25 @@ void AVForm::onPropProbingFinished(Camera::Prop prop, double val)
void AVForm::onResProbingFinished(QList<QSize> res)
{
QSize savedRes = Settings::getInstance().getCamVideoRes();
int savedResIndex = -1;
bodyUI->videoModescomboBox->clear();
for (QSize r : res)
bodyUI->videoModescomboBox->blockSignals(true);
for (int i=0; i<res.size(); ++i)
{
QSize& r = res[i];
bodyUI->videoModescomboBox->addItem(QString("%1x%2").arg(QString::number(r.width()),QString::number(r.height())), r);
if (r == savedRes)
savedResIndex = i;
}
//reset index, otherwise cameras with only one resolution won't get initialized
bodyUI->videoModescomboBox->setCurrentIndex(-1);
bodyUI->videoModescomboBox->blockSignals(false);
bodyUI->videoModescomboBox->setCurrentIndex(bodyUI->videoModescomboBox->count()-1);
if (savedResIndex != -1)
bodyUI->videoModescomboBox->setCurrentIndex(savedResIndex);
else
bodyUI->videoModescomboBox->setCurrentIndex(bodyUI->videoModescomboBox->count()-1);
}
void AVForm::hideEvent(QHideEvent *)
@ -129,36 +150,47 @@ void AVForm::hideEvent(QHideEvent *)
bodyUI->CamVideoSurface->setSource(nullptr);
}
void AVForm::showEvent(QShowEvent *)
{
bodyUI->CamVideoSurface->setSource(Camera::getInstance());
}
void AVForm::getAudioInDevices()
{
QString settingsInDev = Settings::getInstance().getInDev();
bool inDevFound = false;
int inDevIndex = 0;
bodyUI->inDevCombobox->clear();
const ALchar *pDeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
if (pDeviceList)
{
//prevent currentIndexChanged to be fired while adding items
bodyUI->inDevCombobox->blockSignals(true);
while (*pDeviceList)
{
int len = strlen(pDeviceList);
QString inDev = QString::fromLocal8Bit(pDeviceList,len);
#ifdef Q_OS_WIN32
QString inDev = QString::fromUtf8(pDeviceList,len);
#else
QString inDev = QString::fromLocal8Bit(pDeviceList,len);
#endif
bodyUI->inDevCombobox->addItem(inDev);
if (settingsInDev == inDev)
{
bodyUI->inDevCombobox->setCurrentIndex(bodyUI->inDevCombobox->count()-1);
inDevFound = true;
inDevIndex = bodyUI->inDevCombobox->count()-1;
}
pDeviceList += len+1;
}
//addItem changes currentIndex -> reset
bodyUI->inDevCombobox->setCurrentIndex(-1);
bodyUI->inDevCombobox->blockSignals(false);
}
if (!inDevFound)
Settings::getInstance().setInDev(bodyUI->inDevCombobox->itemText(0));
bodyUI->inDevCombobox->setCurrentIndex(inDevIndex);
}
void AVForm::getAudioOutDevices()
{
QString settingsOutDev = Settings::getInstance().getOutDev();
bool outDevFound = false;
int outDevIndex = 0;
bodyUI->outDevCombobox->clear();
const ALchar *pDeviceList;
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
@ -167,22 +199,28 @@ void AVForm::getAudioOutDevices()
pDeviceList = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
if (pDeviceList)
{
//prevent currentIndexChanged to be fired while adding items
bodyUI->outDevCombobox->blockSignals(true);
while (*pDeviceList)
{
int len = strlen(pDeviceList);
QString outDev = QString::fromLocal8Bit(pDeviceList,len);
#ifdef Q_OS_WIN32
QString outDev = QString::fromUtf8(pDeviceList,len);
#else
QString outDev = QString::fromLocal8Bit(pDeviceList,len);
#endif
bodyUI->outDevCombobox->addItem(outDev);
if (settingsOutDev == outDev)
{
bodyUI->outDevCombobox->setCurrentIndex(bodyUI->outDevCombobox->count()-1);
outDevFound = true;
outDevIndex = bodyUI->outDevCombobox->count()-1;
}
pDeviceList += len+1;
}
//addItem changes currentIndex -> reset
bodyUI->outDevCombobox->setCurrentIndex(-1);
bodyUI->outDevCombobox->blockSignals(false);
}
if (!outDevFound)
Settings::getInstance().setOutDev(bodyUI->outDevCombobox->itemText(0));
bodyUI->outDevCombobox->setCurrentIndex(outDevIndex);
}
void AVForm::onInDevChanged(const QString &deviceDescriptor)
@ -201,3 +239,28 @@ void AVForm::onFilterAudioToggled(bool filterAudio)
{
Settings::getInstance().setFilterAudio(filterAudio);
}
void AVForm::on_HueSlider_valueChanged(int value)
{
Camera::getInstance()->setProp(Camera::HUE, value / 100.0);
}
void AVForm::on_BrightnessSlider_valueChanged(int value)
{
Camera::getInstance()->setProp(Camera::BRIGHTNESS, value / 100.0);
}
void AVForm::on_SaturationSlider_valueChanged(int value)
{
Camera::getInstance()->setProp(Camera::SATURATION, value / 100.0);
}
void AVForm::on_ContrastSlider_valueChanged(int value)
{
Camera::getInstance()->setProp(Camera::CONTRAST, value / 100.0);
}
void AVForm::on_playbackSlider_valueChanged(int value)
{
Audio::getInstance().outputVolume = value / 100.0;
}

View File

@ -47,18 +47,28 @@ private slots:
void on_SaturationSlider_sliderMoved(int position);
void on_BrightnessSlider_sliderMoved(int position);
void on_HueSlider_sliderMoved(int position);
void on_videoModescomboBox_activated(int index);
void on_videoModescomboBox_currentIndexChanged(int index);
// audio
void onInDevChanged(const QString& deviceDescriptor);
void onOutDevChanged(const QString& deviceDescriptor);
void onFilterAudioToggled(bool filterAudio);
void on_playbackSlider_valueChanged(int value);
// camera
void onPropProbingFinished(Camera::Prop prop, double val);
void onResProbingFinished(QList<QSize> res);
virtual void hideEvent(QHideEvent*);
virtual void showEvent(QShowEvent*);
void on_HueSlider_valueChanged(int value);
void on_BrightnessSlider_valueChanged(int value);
void on_SaturationSlider_valueChanged(int value);
void on_ContrastSlider_valueChanged(int value);
private:
Ui::AVSettings *bodyUI;

View File

@ -60,6 +60,9 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="toolTip">
<string>Use slider to set volume of your speakers.</string>
</property>
</widget>
</item>
<item row="7" column="1">
@ -67,6 +70,10 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="toolTip">
<string>Use slider to set volume of your microphone.
WARNING: slider is not supposed to work yet.</string>
</property>
</widget>
</item>
<item row="1" column="0">
@ -101,6 +108,9 @@
<property name="text">
<string>Filter audio</string>
</property>
<property name="toolTip">
<string>Filter sound from your microphone, so that people hearing you would get better sound.</string>
</property>
</widget>
</item>
</layout>
@ -122,6 +132,13 @@
<property name="text">
<string>Resolution</string>
</property>
<property name="toolTip">
<string>Set resolution of your camera.
The higher values, the better video quality your friends may get.
Note though that with better video quality there is needed better internet connection.
Sometimes your connection may not be good enough to handle higher video quality,
which may lead to problems with video calls.</string>
</property>
</widget>
</item>
<item row="0" column="1">
@ -132,6 +149,13 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Set resolution of your camera.
The higher values, the better video quality your friends may get.
Note though that with better video quality there is needed better internet connection.
Sometimes your connection may not be good enough to handle higher video quality,
which may lead to problems with video calls.</string>
</property>
</widget>
</item>
<item row="1" column="0">

View File

@ -30,8 +30,8 @@
#include "src/autoupdate.h"
static QStringList locales = {"bg", "de", "en", "es", "fr", "it", "mannol", "pirate", "pl", "pt", "ru", "fi", "sv", "uk"};
static QStringList langs = {"Български", "Deutsch", "English", "Español", "Français", "Italiano", "mannol", "Pirate", "Polski", "Português", "Русский", "Suomi", "Svenska", "Українська"};
static QStringList locales = {"bg", "de", "en", "es", "fr", "it", "lt", "mannol", "pirate", "pl", "pt", "ru", "fi", "sv", "uk"};
static QStringList langs = {"Български", "Deutsch", "English", "Español", "Français", "Italiano", "Lietuvių", "mannol", "Pirate", "Polski", "Português", "Русский", "Suomi", "Svenska", "Українська"};
static QStringList timeFormats = {"hh:mm AP", "hh:mm", "hh:mm:ss AP", "hh:mm:ss"};
@ -45,16 +45,14 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
bodyUI->checkUpdates->setVisible(AUTOUPDATE_ENABLED);
bodyUI->checkUpdates->setChecked(Settings::getInstance().getCheckUpdates());
bodyUI->trayBehavior->addStretch();
bodyUI->cbEnableIPv6->setChecked(Settings::getInstance().getEnableIPv6());
for (int i = 0; i < langs.size(); i++)
bodyUI->transComboBox->insertItem(i, langs[i]);
bodyUI->transComboBox->setCurrentIndex(locales.indexOf(Settings::getInstance().getTranslation()));
bodyUI->cbMakeToxPortable->setChecked(Settings::getInstance().getMakeToxPortable());
bool showSystemTray = Settings::getInstance().getShowSystemTray();
bodyUI->showSystemTray->setChecked(showSystemTray);
bodyUI->startInTray->setChecked(Settings::getInstance().getAutostartInTray());
bodyUI->startInTray->setEnabled(showSystemTray);
@ -68,8 +66,11 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
bodyUI->useEmoticons->setChecked(Settings::getInstance().getUseEmoticons());
bodyUI->autoacceptFiles->setChecked(Settings::getInstance().getAutoSaveEnabled());
bodyUI->autoSaveFilesDir->setText(Settings::getInstance().getGlobalAutoAcceptDir());
bodyUI->showWindow->setChecked(Settings::getInstance().getShowWindow());
bodyUI->showInFront->setChecked(Settings::getInstance().getShowInFront());
bodyUI->groupAlwaysNotify->setChecked(Settings::getInstance().getGroupAlwaysNotify());
bodyUI->cbFauxOfflineMessaging->setChecked(Settings::getInstance().getFauxOfflineMessaging());
bodyUI->cbCompactLayout->setChecked(Settings::getInstance().getCompactLayout());
for (auto entry : SmileyPack::listSmileyPacks())
{
@ -117,7 +118,6 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
//general
connect(bodyUI->checkUpdates, &QCheckBox::stateChanged, this, &GeneralForm::onCheckUpdateChanged);
connect(bodyUI->transComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onTranslationUpdated()));
connect(bodyUI->cbMakeToxPortable, &QCheckBox::stateChanged, this, &GeneralForm::onMakeToxPortableUpdated);
connect(bodyUI->showSystemTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetShowSystemTray);
connect(bodyUI->startInTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetAutostartInTray);
connect(bodyUI->closeToTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetCloseToTray);
@ -125,7 +125,9 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
connect(bodyUI->lightTrayIcon, &QCheckBox::stateChanged, this, &GeneralForm::onSetLightTrayIcon);
connect(bodyUI->statusChanges, &QCheckBox::stateChanged, this, &GeneralForm::onSetStatusChange);
connect(bodyUI->autoAwaySpinBox, SIGNAL(editingFinished()), this, SLOT(onAutoAwayChanged()));
connect(bodyUI->showWindow, &QCheckBox::stateChanged, this, &GeneralForm::onShowWindowChanged);
connect(bodyUI->showInFront, &QCheckBox::stateChanged, this, &GeneralForm::onSetShowInFront);
connect(bodyUI->groupAlwaysNotify, &QCheckBox::stateChanged, this, &GeneralForm::onSetGroupAlwaysNotify);
connect(bodyUI->autoacceptFiles, &QCheckBox::stateChanged, this, &GeneralForm::onAutoAcceptFileChange);
if (bodyUI->autoacceptFiles->isChecked())
connect(bodyUI->autoSaveFilesDir, SIGNAL(clicked()), this, SLOT(onAutoSaveDirChange()));
@ -144,6 +146,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
connect(bodyUI->proxyPort, SIGNAL(valueChanged(int)), this, SLOT(onProxyPortEdited(int)));
connect(bodyUI->reconnectButton, &QPushButton::clicked, this, &GeneralForm::onReconnectClicked);
connect(bodyUI->cbFauxOfflineMessaging, &QCheckBox::stateChanged, this, &GeneralForm::onFauxOfflineMessaging);
connect(bodyUI->cbCompactLayout, &QCheckBox::stateChanged, this, &GeneralForm::onCompactLayout);
#ifndef QTOX_PLATFORM_EXT
bodyUI->autoAwayLabel->setEnabled(false); // these don't seem to change the appearance of the widgets,
@ -167,11 +170,6 @@ void GeneralForm::onTranslationUpdated()
Widget::getInstance()->setTranslation();
}
void GeneralForm::onMakeToxPortableUpdated()
{
Settings::getInstance().setMakeToxPortable(bodyUI->cbMakeToxPortable->isChecked());
}
void GeneralForm::onSetShowSystemTray()
{
Settings::getInstance().setShowSystemTray(bodyUI->showSystemTray->isChecked());
@ -330,9 +328,19 @@ void GeneralForm::onCheckUpdateChanged()
Settings::getInstance().setCheckUpdates(bodyUI->checkUpdates->isChecked());
}
void GeneralForm::onShowWindowChanged()
{
Settings::getInstance().setShowWindow(bodyUI->showWindow->isChecked());
}
void GeneralForm::onSetShowInFront()
{
Settings::getInstance().setShowInFront(bodyUI->showInFront->isChecked());
Settings::getInstance().setShowInFront(bodyUI->showInFront->isChecked());
}
void GeneralForm::onSetGroupAlwaysNotify()
{
Settings::getInstance().setGroupAlwaysNotify(bodyUI->groupAlwaysNotify->isChecked());
}
void GeneralForm::onFauxOfflineMessaging()
@ -340,6 +348,12 @@ void GeneralForm::onFauxOfflineMessaging()
Settings::getInstance().setFauxOfflineMessaging(bodyUI->cbFauxOfflineMessaging->isChecked());
}
void GeneralForm::onCompactLayout()
{
Settings::getInstance().setCompactLayout(bodyUI->cbCompactLayout->isChecked());
emit parent->compactToggled(bodyUI->cbCompactLayout->isChecked());
}
void GeneralForm::onThemeColorChanged(int)
{
int index = bodyUI->themeColorCBox->currentIndex();

View File

@ -33,7 +33,6 @@ public:
private slots:
void onEnableIPv6Updated();
void onTranslationUpdated();
void onMakeToxPortableUpdated();
void onSetShowSystemTray();
void onSetAutostartInTray();
void onSetCloseToTray();
@ -54,8 +53,11 @@ private slots:
void onAutoAcceptFileChange();
void onAutoSaveDirChange();
void onCheckUpdateChanged();
void onShowWindowChanged();
void onSetShowInFront();
void onSetGroupAlwaysNotify();
void onFauxOfflineMessaging();
void onCompactLayout();
void onThemeColorChanged(int);
private:

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>545</width>
<width>573</width>
<height>644</height>
</rect>
</property>
@ -34,16 +34,16 @@
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>-173</y>
<width>511</width>
<height>797</height>
<y>0</y>
<width>544</width>
<height>785</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,0,0">
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,0,0,1">
<property name="spacing">
<number>9</number>
</property>
@ -57,14 +57,14 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="transLayout">
<layout class="QHBoxLayout" name="generalLayout">
<item>
<widget class="QLabel" name="transLabel">
<property name="toolTip">
<string>The translation may not load until qTox restarts.</string>
</property>
<property name="text">
<string>Translation</string>
<string>Language:</string>
</property>
</widget>
</item>
@ -81,27 +81,30 @@
</property>
</widget>
</item>
<item>
<spacer name="generalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="cbMakeToxPortable">
<property name="toolTip">
<string extracomment="describes makeToxPortable checkbox">Save settings to the working directory instead of the usual conf dir</string>
</property>
<property name="text">
<string>Make Tox portable</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="trayGroup">
<property name="title">
<string>System tray integration</string>
<string>System tray</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="showSystemTray">
<property name="text">
@ -109,103 +112,131 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="trayBehavior">
<item>
<widget class="QCheckBox" name="startInTray">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start in tray</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="closeToTray">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Close to tray</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="minimizeToTray">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Minimize to tray</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="lightTrayIcon">
<property name="text">
<string>Light icon</string>
</property>
<property name="toolTip">
<string comment="toolTip for light icon setting">Enable light tray icon.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="startInTray">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start in tray</string>
</property>
<property name="toolTip">
<string comment="toolTip for Start in tray setting">qTox will start minimized in tray.</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="closeToTray">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Close to tray</string>
</property>
<property name="toolTip">
<string comment="toolTip for close to tray setting">After pressing close (X) qTox will minimize to tray,
instead of closing itself.</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="minimizeToTray">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Minimize to tray</string>
</property>
<property name="toolTip">
<string comment="toolTip for minimize to tray setting">After pressing minimize (_) qTox will minimize itself to tray,
instead of system taskbar.</string>
</property>
</widget>
</item>
<item row="2" column="3">
<spacer name="traySpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="statusChanges">
<property name="text">
<string>Show contacts' status changes</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkUpdates">
<property name="text">
<string>Check for updates on startup (unstable)</string>
<string>Check for updates on startup</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showInFront">
<property name="text">
<string>Focus qTox when a message is received</string>
<layout class="QGridLayout" name="autoLayout">
<property name="topMargin">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbFauxOfflineMessaging">
<property name="text">
<string>Faux offline messaging</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item alignment="Qt::AlignLeft">
<widget class="QLabel" name="autoAwayLabel">
<property name="toolTip">
<string>Provided in minutes</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<item row="1" column="1">
<widget class="QLabel" name="autoSaveFilesDirLabel">
<property name="text">
<string>Auto away after (0 to disable)</string>
<string>Save to:</string>
</property>
<property name="toolTip">
<string>Set where files will be saved.</string>
</property>
</widget>
</item>
<item>
<item row="1" column="0">
<widget class="QCheckBox" name="autoacceptFiles">
<property name="toolTip">
<string comment="autoaccept cb tooltip">You can set this on a per-friend basis by right clicking them.</string>
</property>
<property name="text">
<string>Autoaccept files</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="autoSaveFilesDir">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>PushButton</string>
</property>
<property name="toolTip">
<string>Set where files will be saved.</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QSpinBox" name="autoAwaySpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -230,46 +261,116 @@
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="fileLayout">
<item>
<widget class="QCheckBox" name="autoacceptFiles">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="autoAwayLabel">
<property name="toolTip">
<string comment="autoaccept cb tooltip">You can set this on a per-friend basis by right clicking them.</string>
<string>Your status is changed to Away after set period of inactivity.</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Autoaccept files</string>
<string>Auto away after (0 to disable):</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="chatGroupBox">
<property name="title">
<string>Chat</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0">
<item row="3" column="0">
<widget class="QCheckBox" name="groupAlwaysNotify">
<property name="toolTip">
<string comment="toolTip for Group chat always notify">Always notify about new messages in groupchats.</string>
</property>
<property name="text">
<string>Group chats always notify</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="statusChanges">
<property name="text">
<string>Show contacts' status changes</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>On new message:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="onMessageLayout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="showWindow">
<property name="toolTip">
<string comment="tooltip for Show window setting">Show qTox's window when you receive new message.</string>
</property>
<property name="text">
<string>Show window</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="saveFilesHLayout">
<item>
<widget class="QLabel" name="autoSaveFilesDirLabel">
<property name="text">
<string>Save files in</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="autoSaveFilesDir">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
<widget class="QCheckBox" name="showInFront">
<property name="toolTip">
<string comment="toolTip for Focus window setting">Focus qTox when you receive message.</string>
</property>
<property name="text">
<string>Focus window</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="2">
<spacer name="chatSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="cbFauxOfflineMessaging">
<property name="toolTip">
<string comment="toolTip for Faux offline messaging setting">Messages you are trying to send to your friends when they are not online
will be sent to them when they will appear online to you.</string>
</property>
<property name="text">
<string>Faux offline messaging</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="cbCompactLayout">
<property name="toolTip">
<string comment="toolTip for compact layout setting">Your contact list will be shown in compact mode.
qTox's restart needed.</string>
</property>
<property name="text">
<string>Compact contact list</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -306,7 +407,7 @@
<item row="0" column="0">
<widget class="QLabel" name="smileyPackLabel">
<property name="text">
<string extracomment="Text on smiley pack label">Smiley Pack</string>
<string extracomment="Text on smiley pack label">Smiley Pack:</string>
</property>
</widget>
</item>
@ -378,47 +479,13 @@
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="styleLabel">
<widget class="QLabel" name="emoticonSizeLabel">
<property name="text">
<string>Style</string>
<string>Emoticon size:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="styleBrowser">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="themeColorLabel">
<property name="text">
<string>Theme color</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="themeColorCBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="emoticonSizeLabel">
<property name="text">
<string>Emoticon size</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="emoticonSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -443,10 +510,44 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="styleLabel">
<property name="text">
<string>Style:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="styleBrowser">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="themeColorLabel">
<property name="text">
<string>Theme color:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="themeColorCBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="timestampLabel">
<property name="text">
<string>Timestamp format</string>
<string>Timestamp format:</string>
</property>
</widget>
</item>
@ -478,32 +579,76 @@
<number>9</number>
</property>
<item>
<widget class="QCheckBox" name="cbEnableIPv6">
<property name="text">
<string extracomment="Text on a checkbox to enable IPv6">Enable IPv6 (recommended)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbEnableUDP">
<property name="toolTip">
<string extracomment="force tcp checkbox tooltip">Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary.</string>
</property>
<property name="text">
<string extracomment="Text on checkbox to disable UDP">Enable UDP (recommended)</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="proxyLayout_2">
<layout class="QHBoxLayout" name="coreProtocolLayout">
<item>
<widget class="QLabel" name="label">
<widget class="QCheckBox" name="cbEnableUDP">
<property name="toolTip">
<string extracomment="force tcp checkbox tooltip">Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary.</string>
</property>
<property name="text">
<string>Proxy type</string>
<string extracomment="Text on checkbox to disable UDP">Enable UDP (recommended)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbEnableIPv6">
<property name="text">
<string extracomment="Text on a checkbox to enable IPv6">Enable IPv6 (recommended)</string>
</property>
</widget>
</item>
<item>
<spacer name="protocolSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="proxyLayout">
<item row="2" column="3">
<widget class="QSpinBox" name="proxyPort">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="proxyTypeLabel">
<property name="text">
<string>Proxy type:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="proxyAddrLabel">
<property name="text">
<string extracomment="Text on proxy addr label">Address:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="proxyAddr"/>
</item>
<item row="2" column="2">
<widget class="QLabel" name="proxyPortLabel">
<property name="text">
<string extracomment="Text on proxy port label">Port</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QComboBox" name="proxyType">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -530,40 +675,6 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="proxyLayout">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="proxyAddrLabel">
<property name="text">
<string extracomment="Text on proxy addr label">Address</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="proxyAddr"/>
</item>
<item>
<widget class="QLabel" name="proxyPortLabel">
<property name="text">
<string extracomment="Text on proxy port label">Port</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="proxyPort">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="reconnectButton">
<property name="text">
@ -630,5 +741,21 @@
</hint>
</hints>
</connection>
<connection>
<sender>showWindow</sender>
<signal>toggled(bool)</signal>
<receiver>showInFront</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>329</x>
<y>349</y>
</hint>
<hint type="destinationlabel">
<x>451</x>
<y>349</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -46,9 +46,9 @@ IdentityForm::IdentityForm() :
bodyUI->toxGroup->layout()->addWidget(toxId);
timer.setInterval(1000);
timer.setInterval(750);
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout, this, [=]() {bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text().replace("", ""));});
connect(&timer, &QTimer::timeout, this, [=]() {bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text().replace("", "")); hasCheck = false;});
connect(bodyUI->toxIdLabel, SIGNAL(clicked()), this, SLOT(copyIdClicked()));
connect(toxId, SIGNAL(clicked()), this, SLOT(copyIdClicked()));
@ -89,7 +89,11 @@ void IdentityForm::copyIdClicked()
QApplication::clipboard()->setText(txt);
toxId->setCursorPosition(0);
bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text() + "");
if (!hasCheck)
{
bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text() + "");
hasCheck = true;
}
timer.start();
}

View File

@ -74,6 +74,7 @@ private:
Ui::IdentitySettings* bodyUI;
Core* core;
QTimer timer;
bool hasCheck = false;
ClickableTE* toxId;
};

View File

@ -81,6 +81,10 @@
<property name="title">
<string>Tox ID</string>
</property>
<property name="toolTip">
<string comment="Tox ID tooltip">This bunch of characters tells other Tox clients how to contact you.
Share it with your friends to communicate.</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="CroppingLabel" name="toxIdLabel">
@ -109,6 +113,9 @@
</item>
<item>
<widget class="QComboBox" name="profiles">
<property name="toolTip">
<string comment="toolTip for currently set profile">Currently selected profile.</string>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -124,7 +131,7 @@
<item>
<widget class="QPushButton" name="loadButton">
<property name="toolTip">
<string comment="tooltip">Switching profiles is disabled during calls</string>
<string comment="tooltip for loading profile button">Load selected profile and switch to it.</string>
</property>
<property name="text">
<string comment="load profile button">Load</string>
@ -136,6 +143,9 @@
<property name="text">
<string comment="rename profile button">Rename</string>
</property>
<property name="toolTip">
<string comment="tooltip for renaming profile button">Rename selected profile.</string>
</property>
</widget>
</item>
<item>
@ -143,12 +153,16 @@
<property name="text">
<string comment="export profile button">Export</string>
</property>
<property name="toolTip">
<string comment="tooltip for profile exporting button">Allows you to export your Tox profile to a file.
Profile does not contain your history.</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="toolTip">
<string comment="delete profile button tooltip">This is useful to remain safe on public computers</string>
<string comment="delete profile button tooltip">Delete selected profile.</string>
</property>
<property name="text">
<string comment="delete profile button">Delete</string>
@ -164,12 +178,15 @@
<property name="text">
<string comment="import profile button">Import a profile</string>
</property>
<property name="toolTip">
<string comment="tooltip for importing profile button">Import Tox profile from a .tox file.</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="newButton">
<property name="toolTip">
<string comment="tooltip">Switching profiles is disabled during calls</string>
<string comment="tooltip for creating new Tox ID button">Create new Tox ID and switch to it.</string>
</property>
<property name="text">
<string comment="new profile button">New Tox ID</string>

View File

@ -44,7 +44,7 @@
<item>
<widget class="QCheckBox" name="cbTypingNotification">
<property name="toolTip">
<string extracomment="Your friends will be able to see when you are typing."/>
<string comment="tooltip for typing notifications setting">Your friends will be able to see when you are typing.</string>
</property>
<property name="text">
<string>Send Typing Notifications</string>
@ -54,7 +54,8 @@
<item>
<widget class="QCheckBox" name="cbKeepHistory">
<property name="toolTip">
<string extracomment="History keeping still under developing. Log format changin is possible."/>
<string comment="toolTip for Keep History setting">History keeping is still in development.
Save format changes are possible, which may result in data loss.</string>
</property>
<property name="text">
<string>Keep History (unstable)</string>
@ -101,6 +102,12 @@
<property name="title">
<string>Nospam</string>
</property>
<property name="toolTip">
<string comment="toolTip for nospam">Nospam is part of your Tox ID.
It is there to help you change your Tox ID when you feel like you are getting too much spam friend requests.
When you change nospam, your current contacts still can communicate with you,
but new contacts need to know your new Tox ID to be able to add you.</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">

View File

@ -42,15 +42,15 @@ public:
void show(Ui::MainWindow &ui);
void setBodyHeadStyle(QString style);
signals:
void setShowSystemTray(bool newValue);
void compactToggled(bool compact);
private slots:
void onTabChanged(int);
signals:
void setShowSystemTray(bool newValue);
private:
QWidget *head, *body; // keep the others private
IdentityForm *ifrm;
QWidget *head, *body;
QTabWidget *settingsWidgets;
QLabel *nameLabel, *imgLabel;
};

View File

@ -28,7 +28,8 @@
const QString TabCompleter::nickSuffix = QString(": ");
TabCompleter::TabCompleter(ChatTextEdit* msgEdit, Group* group)
: QObject(msgEdit), msgEdit(msgEdit), group(group), enabled(false)
: QObject{msgEdit}, msgEdit{msgEdit}, group{group},
enabled{false}, lastCompletionLength{0}
{
}
@ -70,9 +71,11 @@ void TabCompleter::complete()
if (nextCompletion != completionMap.end()) {
// clear previous completion
for (int i = 0; i < lastCompletionLength; i++) {
msgEdit->keyPressEvent(new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier));
}
auto cur = msgEdit->textCursor();
cur.setPosition(cur.selectionEnd());
msgEdit->setTextCursor(cur);
for (int i = 0; i < lastCompletionLength; i++)
msgEdit->textCursor().deletePreviousChar();
// insert completion
msgEdit->insertPlainText(*nextCompletion);
@ -95,7 +98,6 @@ void TabCompleter::complete()
}
}
void TabCompleter::reset()
{
enabled = false;

View File

@ -40,7 +40,7 @@ public slots:
private:
struct SortableString {
inline SortableString(const QString &n) { contents = n; }
inline SortableString(const QString &n) : contents{n} {}
bool operator<(const SortableString &other) const;
QString contents;
};

View File

@ -16,6 +16,7 @@
#include "genericchatroomwidget.h"
#include "src/misc/style.h"
#include "src/misc/settings.h"
#include "maskablepixmapwidget.h"
#include "croppinglabel.h"
#include <QMouseEvent>
@ -24,17 +25,17 @@
GenericChatroomWidget::GenericChatroomWidget(QWidget *parent)
: QFrame(parent)
{
setFixedHeight(55);
setLayout(&layout);
layout.setSpacing(0);
layout.setMargin(0);
textLayout.setSpacing(0);
textLayout.setMargin(0);
setLayoutDirection(Qt::LeftToRight); // parent might have set Qt::RightToLeft
setProperty("compact", Settings::getInstance().getCompactLayout());
// avatar
avatar = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.png");
if (property("compact").toBool())
{
avatar = new MaskablePixmapWidget(this, QSize(20,20), ":/img/avatar_mask.png");
}
else
{
avatar = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.png");
}
// status text
statusMessageLabel = new CroppingLabel(this);
@ -44,24 +45,65 @@ GenericChatroomWidget::GenericChatroomWidget(QWidget *parent)
nameLabel = new CroppingLabel(this);
nameLabel->setObjectName("name");
textLayout.addStretch();
textLayout.addWidget(nameLabel);
textLayout.addWidget(statusMessageLabel);
textLayout.addStretch();
layout.addSpacing(20);
layout.addWidget(avatar);
layout.addSpacing(10);
layout.addLayout(&textLayout);
layout.addSpacing(10);
layout.addWidget(&statusPic);
layout.addSpacing(10);
layout.activate();
onCompactChanged(property("compact").toBool());
setProperty("active", false);
setStyleSheet(Style::getStylesheet(":/ui/chatroomWidgets/genericChatroomWidget.css"));
}
void GenericChatroomWidget::onCompactChanged(bool _compact)
{
delete textLayout; // has to be first, deleted by layout
delete layout;
setProperty("compact", _compact);
layout = new QHBoxLayout;
textLayout = new QVBoxLayout;
setLayout(layout);
layout->setSpacing(0);
layout->setMargin(0);
textLayout->setSpacing(0);
textLayout->setMargin(0);
setLayoutDirection(Qt::LeftToRight); // parent might have set Qt::RightToLeft
// avatar
if (property("compact").toBool())
{
setFixedHeight(25);
avatar->setSize(QSize(20,20));
layout->addSpacing(18);
layout->addWidget(avatar);
layout->addSpacing(5);
layout->addWidget(nameLabel);
layout->addWidget(statusMessageLabel);
layout->addSpacing(5);
layout->addWidget(&statusPic);
layout->addSpacing(5);
layout->activate();
}
else
{
setFixedHeight(55);
avatar->setSize(QSize(40,40));
textLayout->addStretch();
textLayout->addWidget(nameLabel);
textLayout->addWidget(statusMessageLabel);
textLayout->addStretch();
layout->addSpacing(20);
layout->addWidget(avatar);
layout->addSpacing(10);
layout->addLayout(textLayout);
layout->addSpacing(10);
layout->addWidget(&statusPic);
layout->addSpacing(10);
layout->activate();
}
Style::repolish(this);
}
bool GenericChatroomWidget::isActive()
{
return property("active").toBool();
@ -102,3 +144,14 @@ void GenericChatroomWidget::reloadTheme()
{
setStyleSheet(Style::getStylesheet(":/ui/chatroomWidgets/genericChatroomWidget.css"));
}
bool GenericChatroomWidget::isCompact() const
{
return compact;
}
void GenericChatroomWidget::setCompact(bool compact)
{
this->compact = compact;
Style::repolish(this);
}

View File

@ -53,18 +53,25 @@ public:
void reloadTheme();
bool isCompact() const;
void setCompact(bool compact);
Q_PROPERTY(bool compact READ isCompact WRITE setCompact)
signals:
void chatroomWidgetClicked(GenericChatroomWidget* widget);
public slots:
void onCompactChanged(bool compact);
protected:
QColor lastColor;
QHBoxLayout layout;
QVBoxLayout textLayout;
QHBoxLayout* layout = nullptr;
QVBoxLayout* textLayout = nullptr;
MaskablePixmapWidget* avatar;
QLabel statusPic;
CroppingLabel *nameLabel, *statusMessageLabel;
CroppingLabel* nameLabel, * statusMessageLabel;
bool compact;
friend class Style; ///< To update our stylesheets
};

View File

@ -99,16 +99,9 @@ void GroupWidget::updateStatusLight()
Group *g = GroupList::findGroup(groupId);
if (!g->getEventFlag())
{
statusPic.setPixmap(QPixmap(":img/status/dot_online.png"));
}
else
{
if (!g->getMentionedFlag())
statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png"));
else
statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png"));
}
statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png"));
}
void GroupWidget::setChatForm(Ui::MainWindow &ui)

View File

@ -19,16 +19,11 @@
MaskablePixmapWidget::MaskablePixmapWidget(QWidget *parent, QSize size, QString maskName)
: QWidget(parent)
, renderTarget(size)
, maskName(maskName)
, backgroundColor(Qt::white)
, clickable(false)
{
setFixedSize(size);
QPixmap pmapMask = QPixmap(maskName);
if (!pmapMask.isNull())
mask = QPixmap(maskName).scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
setSize(size);
}
void MaskablePixmapWidget::autopickBackground()
@ -63,16 +58,11 @@ void MaskablePixmapWidget::autopickBackground()
QColor color = QColor::fromRgb(r,g,b);
backgroundColor = QColor::fromRgb(0xFFFFFF ^ color.rgb());
manualColor = false;
update();
}
void MaskablePixmapWidget::setBackground(QColor color)
{
backgroundColor = color;
update();
}
void MaskablePixmapWidget::setClickable(bool clickable)
{
this->clickable = clickable;
@ -87,9 +77,10 @@ void MaskablePixmapWidget::setPixmap(const QPixmap &pmap, QColor background)
{
if (!pmap.isNull())
{
unscaled = pmap;
pixmap = pmap.scaled(width(), height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
backgroundColor = background;
manualColor = true;
update();
}
}
@ -98,25 +89,44 @@ void MaskablePixmapWidget::setPixmap(const QPixmap &pmap)
{
if (!pmap.isNull())
{
unscaled = pmap;
pixmap = pmap.scaled(width(), height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
autopickBackground();
update();
}
}
QPixmap MaskablePixmapWidget::getPixmap() const
{
return renderTarget;
return *renderTarget;
}
void MaskablePixmapWidget::setSize(QSize size)
{
setFixedSize(size);
delete renderTarget;
renderTarget = new QPixmap(size);
QPixmap pmapMask = QPixmap(maskName);
if (!pmapMask.isNull())
mask = pmapMask.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
if (!unscaled.isNull())
{
pixmap = unscaled.scaled(width(), height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
if (!manualColor)
autopickBackground();
update();
}
}
void MaskablePixmapWidget::paintEvent(QPaintEvent *)
{
renderTarget.fill(Qt::transparent);
renderTarget->fill(Qt::transparent);
QPoint offset((width() - pixmap.size().width())/2,(height() - pixmap.size().height())/2); // centering the pixmap
QPainter painter(&renderTarget);
QPainter painter(renderTarget);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.fillRect(0,0,width(),height(),backgroundColor);
painter.drawPixmap(offset,pixmap);
@ -125,7 +135,7 @@ void MaskablePixmapWidget::paintEvent(QPaintEvent *)
painter.end();
painter.begin(this);
painter.drawPixmap(0,0,renderTarget);
painter.drawPixmap(0,0,*renderTarget);
}
void MaskablePixmapWidget::mousePressEvent(QMouseEvent*)

View File

@ -26,11 +26,11 @@ public:
MaskablePixmapWidget(QWidget *parent, QSize size, QString maskName = QString());
void autopickBackground();
void setBackground(QColor color);
void setClickable(bool clickable);
void setPixmap(const QPixmap &pmap, QColor background);
void setPixmap(const QPixmap &pmap);
QPixmap getPixmap() const;
void setSize(QSize size);
signals:
void clicked();
@ -40,13 +40,12 @@ protected:
virtual void mousePressEvent(QMouseEvent *);
private:
QPixmap pixmap;
QPixmap mask;
QPixmap renderTarget;
QPixmap pixmap, mask, unscaled; // a lot of memory...
QPixmap* renderTarget = nullptr; // pointer to dynamically call the constructor
QSize size;
QString maskName;
QColor backgroundColor;
bool clickable;
bool clickable, manualColor = false;
};
#endif // MASKABLEPIXMAPWIDGET_H

View File

@ -0,0 +1,162 @@
#include "systemtrayicon.h"
#include <QString>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QFile>
#include <QDebug>
#include "src/misc/settings.h"
SystemTrayIcon::SystemTrayIcon()
{
QString desktop = getenv("XDG_CURRENT_DESKTOP");
if (false);
#ifdef ENABLE_SYSTRAY_UNITY_BACKEND
else if (desktop.toLower() == "unity")
{
QString settingsDir = Settings::getSettingsDirPath();
QFile iconFile(settingsDir+"/icon.png");
if (iconFile.open(QIODevice::Truncate | QIODevice::WriteOnly))
{
QFile resIconFile(":/img/icon.png");
if (resIconFile.open(QIODevice::ReadOnly))
iconFile.write(resIconFile.readAll());
resIconFile.close();
iconFile.close();
}
backendType = SystrayBackendType::Unity;
unityMenu = gtk_menu_new();
unityIndicator = app_indicator_new_with_path(
"qTox",
"icon",
APP_INDICATOR_CATEGORY_APPLICATION_STATUS,
settingsDir.toStdString().c_str()
);
app_indicator_set_menu(unityIndicator, GTK_MENU(unityMenu));
}
#endif
else
{
qtIcon = new QSystemTrayIcon;
backendType = SystrayBackendType::Qt;
connect(qtIcon, &QSystemTrayIcon::activated, this, &SystemTrayIcon::activated);
}
}
QString SystemTrayIcon::extractIconToFile(QIcon icon, QString name)
{
QString iconPath;
(void) icon;
(void) name;
#ifdef ENABLE_SYSTRAY_UNITY_BACKEND
iconPath = Settings::getSettingsDirPath()+"/"+name+".png";
QSize iconSize = icon.actualSize(QSize{64,64});
icon.pixmap(iconSize).save(iconPath);
#endif
return iconPath;
}
void SystemTrayIcon::setContextMenu(QMenu* menu)
{
if (false);
#ifdef ENABLE_SYSTRAY_UNITY_BACKEND
else if (backendType == SystrayBackendType::Unity)
{
for (QAction* a : menu->actions())
{
gtk_image_menu_item_new();
QString aText = a->text().replace('&',"");
GtkWidget* item;
if (a->isSeparator())
item = gtk_menu_item_new();
else if (a->icon().isNull())
item = gtk_menu_item_new_with_label(aText.toStdString().c_str());
else
{
QString iconPath = extractIconToFile(a->icon(),"iconmenu"+a->icon().name());
GtkWidget* image = gtk_image_new_from_file(iconPath.toStdString().c_str());
item = gtk_image_menu_item_new_with_label(aText.toStdString().c_str());
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item),TRUE);
}
gtk_menu_shell_append(GTK_MENU_SHELL(unityMenu), item);
void (*callback)(GtkMenu*, gpointer data) = [](GtkMenu*, gpointer a)
{
((QAction*)a)->activate(QAction::Trigger);
};
g_signal_connect(item, "activate", G_CALLBACK(callback), a);
gtk_widget_show(item);
}
app_indicator_set_menu(unityIndicator, GTK_MENU(unityMenu));
DbusmenuServer *menuServer;
DbusmenuMenuitem *rootMenuItem;
g_object_get(unityIndicator, "dbus-menu-server", &menuServer, NULL);
g_object_get(menuServer, "root-node", &rootMenuItem, NULL);
void (*callback)(DbusmenuMenuitem *, gpointer) =
[](DbusmenuMenuitem *, gpointer data)
{
((SystemTrayIcon*)data)->activated(QSystemTrayIcon::Unknown);
};
g_signal_connect(rootMenuItem, "about-to-show", G_CALLBACK(callback), this);
}
#endif
else
{
qtIcon->setContextMenu(menu);
}
}
void SystemTrayIcon::show()
{
setVisible(true);
}
void SystemTrayIcon::hide()
{
setVisible(false);
}
void SystemTrayIcon::setVisible(bool newState)
{
if (false);
#ifdef ENABLE_SYSTRAY_UNITY_BACKEND
else if (backendType == SystrayBackendType::Unity)
{
if (newState)
app_indicator_set_status(unityIndicator, APP_INDICATOR_STATUS_ACTIVE);
else
app_indicator_set_status(unityIndicator, APP_INDICATOR_STATUS_PASSIVE);
}
#endif
else
{
if (newState)
qtIcon->show();
else
qtIcon->hide();
}
}
void SystemTrayIcon::setIcon(QIcon &&icon)
{
if (false);
#ifdef ENABLE_SYSTRAY_UNITY_BACKEND
else if (backendType == SystrayBackendType::Unity)
{
// Alternate file names or appindicator will not reload the icon
if (app_indicator_get_icon(unityIndicator) == QString("icon2"))
{
extractIconToFile(icon,"icon");
app_indicator_set_icon_full(unityIndicator, "icon","qtox");
}
else
{
extractIconToFile(icon,"icon2");
app_indicator_set_icon_full(unityIndicator, "icon2","qtox");
}
}
#endif
else
{
qtIcon->setIcon(icon);
}
}

View File

@ -0,0 +1,36 @@
#ifndef SYSTEMTRAYICON_H
#define SYSTEMTRAYICON_H
#include "systemtrayicon_private.h"
#include <QObject>
class QSystemTrayIcon;
class QMenu;
class SystemTrayIcon : public QObject
{
Q_OBJECT
public:
SystemTrayIcon();
void setContextMenu(QMenu* menu);
void show();
void hide();
void setVisible(bool);
void setIcon(QIcon&& icon);
signals:
void activated(QSystemTrayIcon::ActivationReason);
private:
QString extractIconToFile(QIcon icon, QString name="icon");
private:
SystrayBackendType backendType;
QSystemTrayIcon* qtIcon;
#ifdef ENABLE_SYSTRAY_UNITY_BACKEND
AppIndicator *unityIndicator;
GtkWidget *unityMenu;
#endif
};
#endif // SYSTEMTRAYICON_H

View File

@ -0,0 +1,35 @@
#ifndef SYSTEMTRAYICON_PRIVATE_H
#define SYSTEMTRAYICON_PRIVATE_H
#include <QSystemTrayIcon>
#ifdef ENABLE_SYSTRAY_UNITY_BACKEND
#ifdef signals
#undef signals
#endif
extern "C" {
#include <libappindicator/app-indicator.h>
#include <gtk/gtk.h>
#include <libdbusmenu-glib/server.h>
}
#define signals public
#endif
enum class SystrayBackendType
{
Qt,
#ifdef ENABLE_SYSTRAY_UNITY_BACKEND
Unity
#endif
};
union SystrayBackend
{
QSystemTrayIcon *qt;
#ifdef ENABLE_SYSTRAY_UNITY_BACKEND
AppIndicator *unity;
#endif
};
#endif // SYSTEMTRAYICON_PRIVATE_H

View File

@ -25,17 +25,26 @@ ChatTextEdit::ChatTextEdit(QWidget *parent) :
}
void ChatTextEdit::keyPressEvent(QKeyEvent * event)
{
{
int key = event->key();
if ((key == Qt::Key_Enter || key == Qt::Key_Return) && !(event->modifiers() && Qt::ShiftModifier))
if ((key == Qt::Key_Enter || key == Qt::Key_Return) && !(event->modifiers() & Qt::ShiftModifier))
emit enterPressed();
else if (key == Qt::Key_Tab)
emit tabPressed();
else if (key == Qt::Key_Backspace) // because of the backspace() hack in tabber, we can't emit on these
QTextEdit::keyPressEvent(event);
else if (key == Qt::Key_Up && this->toPlainText().isEmpty())
{
this->setText(lastMessage);
this->setFocus();
this->moveCursor(QTextCursor::MoveOperation::End,QTextCursor::MoveMode::MoveAnchor);
}
else
{
emit keyPressed();
QTextEdit::keyPressEvent(event);
}
}
void ChatTextEdit::setLastMessage(QString lm)
{
lastMessage = lm;
}

View File

@ -25,14 +25,15 @@ class ChatTextEdit : public QTextEdit
public:
explicit ChatTextEdit(QWidget *parent = 0);
virtual void keyPressEvent(QKeyEvent * event) override;
void setLastMessage(QString lm);
signals:
void enterPressed();
void tabPressed();
void keyPressed();
public slots:
private:
QString lastMessage;
};
#endif // CHATTEXTEDIT_H

View File

@ -26,6 +26,7 @@
FriendRequestDialog::FriendRequestDialog(QWidget *parent, const QString &userId, const QString &message) :
QDialog(parent)
{
setAttribute(Qt::WA_QuitOnClose, false);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowTitle(tr("Friend request","Title of the window to aceept/deny a friend request"));

View File

@ -22,15 +22,17 @@
#include <QDebug>
VideoSurface::VideoSurface(QWidget* parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers | QGL::SingleBuffer), parent)
, source(nullptr)
: QGLWidget(QGLFormat(QGL::SingleBuffer), parent)
, source{nullptr}
, pbo{nullptr, nullptr}
, textureId(0)
, pboAllocSize(0)
, hasSubscribed(false)
, pboIndex(0)
, bgrProgramm{nullptr}
, yuvProgramm{nullptr}
, textureId{0}
, pboAllocSize{0}
, hasSubscribed{false}
, pboIndex{0}
{
setAutoBufferSwap(false);
}
VideoSurface::VideoSurface(VideoSource *source, QWidget* parent)
@ -65,8 +67,8 @@ void VideoSurface::setSource(VideoSource *src)
void VideoSurface::initializeGL()
{
QGLWidget::initializeGL();
qDebug() << "VideoSurface: Init";
// pbo
pbo[0] = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer);
pbo[0]->setUsagePattern(QOpenGLBuffer::StreamDraw);
@ -180,14 +182,6 @@ void VideoSurface::paintGL()
pbo[nextPboIndex]->release();
}
// render pbo
static float values[] = {
-1, -1,
1, -1,
-1, 1,
1, 1
};
// background
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
@ -220,6 +214,14 @@ void VideoSurface::paintGL()
if (programm)
{
// render pbo
static float values[] = {
-1, -1,
1, -1,
-1, 1,
1, 1
};
programm->bind();
programm->setAttributeArray(0, GL_FLOAT, values, 2);
programm->enableAttributeArray(0);
@ -259,7 +261,7 @@ void VideoSurface::unsubscribe()
}
}
void VideoSurface::onNewFrameAvailable(const VideoFrame newFrame)
void VideoSurface::onNewFrameAvailable(const VideoFrame& newFrame)
{
mutex.lock();
frame = newFrame;

View File

@ -44,7 +44,7 @@ protected:
void unsubscribe();
private slots:
void onNewFrameAvailable(const VideoFrame newFrame);
void onNewFrameAvailable(const VideoFrame &newFrame);
private:
VideoSource* source;

View File

@ -36,6 +36,7 @@
#include "src/autoupdate.h"
#include "src/audio.h"
#include "src/platform/timer.h"
#include "systemtrayicon.h"
#include <QMessageBox>
#include <QDebug>
#include <QFile>
@ -56,8 +57,7 @@ void toxActivateEventHandler(const QByteArray& data)
{
if (data != "$activate")
return;
Widget::getInstance()->show();
Widget::getInstance()->activateWindow();
Widget::getInstance()->forceShow();
}
Widget *Widget::instance{nullptr};
@ -85,7 +85,7 @@ void Widget::init()
if (QSystemTrayIcon::isSystemTrayAvailable())
{
icon = new QSystemTrayIcon(this);
icon = new SystemTrayIcon;
updateTrayIcon();
trayMenu = new QMenu;
@ -113,7 +113,8 @@ void Widget::init()
this,
SLOT(onIconClick(QSystemTrayIcon::ActivationReason)));
if (Settings::getInstance().getShowSystemTray()){
if (Settings::getInstance().getShowSystemTray())
{
icon->show();
if (Settings::getInstance().getAutostartInTray() == false)
this->show();
@ -214,7 +215,7 @@ void Widget::init()
addFriendForm = new AddFriendForm;
settingsWidget = new SettingsWidget();
connect(settingsWidget, SIGNAL(setShowSystemTray(bool)), this, SLOT(onSetShowSystemTray(bool)));
connect(settingsWidget, &SettingsWidget::setShowSystemTray, this, &Widget::onSetShowSystemTray);
connect(core, &Core::connected, this, &Widget::onConnected);
connect(core, &Core::disconnected, this, &Widget::onDisconnected);
@ -314,16 +315,16 @@ void Widget::updateTrayIcon()
Widget::~Widget()
{
qDebug() << "Deleting Widget";
core->saveConfiguration();
coreThread->exit();
coreThread->wait(500); // In case of deadlock (can happen with QtAudio/PA bugs)
if (!coreThread->isFinished())
coreThread->terminate();
AutoUpdater::abortUpdates();
delete core;
icon->hide();
hideMainForms();
delete settingsWidget;
delete addFriendForm;
delete filesForm;
delete idleTimer;
FriendList::clear();
GroupList::clear();
@ -359,6 +360,7 @@ void Widget::closeEvent(QCloseEvent *event)
{
saveWindowGeometry();
saveSplitterGeometry();
qApp->exit(0);
QWidget::closeEvent(event);
}
}
@ -378,6 +380,8 @@ void Widget::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
saveWindowGeometry();
emit resized();
}
QString Widget::detectProfile()
@ -569,6 +573,13 @@ void Widget::setWindowTitle(const QString& title)
QMainWindow::setWindowTitle("qTox - " + title);
}
void Widget::forceShow()
{
hide(); // Workaround to force minimized window to be restored
show();
activateWindow();
}
void Widget::onAddClicked()
{
hideMainForms();
@ -591,21 +602,38 @@ void Widget::onTransferClicked()
void Widget::onIconClick(QSystemTrayIcon::ActivationReason reason)
{
switch (reason) {
switch (reason)
{
case QSystemTrayIcon::Trigger:
if (this->isHidden() == true)
{
this->show();
this->activateWindow();
if (isHidden())
{
show();
activateWindow();
}
else if (isMinimized())
{
forceShow();
}
else
{
hide();
}
break;
}
else
this->hide();
case QSystemTrayIcon::DoubleClick:
case QSystemTrayIcon::DoubleClick:
forceShow();
break;
case QSystemTrayIcon::MiddleClick:
hide();
break;
case QSystemTrayIcon::Unknown:
if (isHidden())
forceShow();
break;
default:
;
break;
}
}
@ -633,15 +661,18 @@ void Widget::hideMainForms()
void Widget::onUsernameChanged(const QString& newUsername, const QString& oldUsername)
{
ui->nameLabel->setText(oldUsername); // restore old username until Core tells us to set it
ui->nameLabel->setToolTip(oldUsername); // for overlength names
setUsername(oldUsername); // restore old username until Core tells us to set it
core->setUsername(newUsername);
}
void Widget::setUsername(const QString& username)
{
ui->nameLabel->setText(username);
ui->nameLabel->setToolTip(username); // for overlength names
ui->nameLabel->setToolTip(username); // for overlength names
QString sanename = username;
sanename.remove(QRegExp("[\\t\\n\\v\\f\\r\\x0000]"));
nameMention = QRegExp("\\b" + QRegExp::escape(username) + "\\b", Qt::CaseInsensitive);
sanitizedNameMention = QRegExp("\\b" + QRegExp::escape(sanename) + "\\b", Qt::CaseInsensitive);
}
void Widget::onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage)
@ -665,6 +696,10 @@ void Widget::addFriend(int friendId, const QString &userId)
QLayout* layout = contactListWidget->getFriendLayout(Status::Offline);
layout->addWidget(newfriend->getFriendWidget());
if (Settings::getInstance().getEnableLogging())
newfriend->getChatForm()->loadHistory(QDateTime::currentDateTime().addDays(-7), true);
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)));
connect(newfriend->getFriendWidget(), SIGNAL(copyFriendIdToClipboard(int)), this, SLOT(copyFriendIdToClipboard(int)));
@ -674,6 +709,7 @@ void Widget::addFriend(int friendId, const QString &userId)
connect(newfriend->getChatForm(), SIGNAL(sendFile(int32_t, QString, QString, long long)), core, SLOT(sendFile(int32_t, QString, QString, long long)));
connect(newfriend->getChatForm(), SIGNAL(answerCall(int)), core, SLOT(answerCall(int)));
connect(newfriend->getChatForm(), SIGNAL(hangupCall(int)), core, SLOT(hangupCall(int)));
connect(newfriend->getChatForm(), SIGNAL(rejectCall(int)), core, SLOT(rejectCall(int)));
connect(newfriend->getChatForm(), SIGNAL(startCall(int)), core, SLOT(startCall(int)));
connect(newfriend->getChatForm(), SIGNAL(startVideoCall(int,bool)), core, SLOT(startCall(int,bool)));
connect(newfriend->getChatForm(), SIGNAL(cancelCall(int,int)), core, SLOT(cancelCall(int,int)));
@ -803,25 +839,11 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message, bool
QDateTime timestamp = QDateTime::currentDateTime();
f->getChatForm()->addMessage(f->getToxID(), message, isAction, timestamp, true);
if (isAction)
HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, "/me " + message, f->getToxID().publicKey, timestamp, true);
else
HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, message, f->getToxID().publicKey, timestamp, true);
if (activeChatroomWidget != nullptr)
{
if ((static_cast<GenericChatroomWidget*>(f->getFriendWidget()) != activeChatroomWidget) || isMinimized() || !isActiveWindow())
{
f->setEventFlag(true);
newMessageAlert(f->getFriendWidget());
}
}
else
{
f->setEventFlag(true);
newMessageAlert(f->getFriendWidget());
}
HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, isAction ? "/me " + message : message,
f->getToxID().publicKey, timestamp, true);
f->setEventFlag(static_cast<GenericChatroomWidget*>(f->getFriendWidget()) != activeChatroomWidget);
newMessageAlert(f->getFriendWidget());
f->getFriendWidget()->updateStatusLight();
}
@ -836,17 +858,22 @@ void Widget::onReceiptRecieved(int friendId, int receipt)
void Widget::newMessageAlert(GenericChatroomWidget* chat)
{
bool inactiveWindow = isMinimized() || !isActiveWindow();
if (!inactiveWindow && activeChatroomWidget != nullptr && chat == activeChatroomWidget)
return;
QApplication::alert(this);
static QFile sndFile(":audio/notification.pcm");
if ((isMinimized() || !isActiveWindow()) && Settings::getInstance().getShowInFront())
if (Settings::getInstance().getShowWindow())
{
this->show();
showNormal();
activateWindow();
emit chat->chatroomWidgetClicked(chat);
show();
if (inactiveWindow && Settings::getInstance().getShowInFront())
setWindowState(Qt::WindowActive);
}
static QFile sndFile(":audio/notification.pcm");
static QByteArray sndData;
if (sndData.isEmpty())
{
sndFile.open(QIODevice::ReadOnly);
@ -950,24 +977,21 @@ void Widget::onGroupMessageReceived(int groupnumber, int peernumber, const QStri
return;
ToxID author = Core::getInstance()->getGroupPeerToxID(groupnumber, peernumber);
QString name = core->getUsername();
bool targeted = (!author.isMine()) && message.contains(name, Qt::CaseInsensitive);
bool targeted = !author.isMine() && (message.contains(nameMention) || message.contains(sanitizedNameMention));
if (targeted && !isAction)
g->getChatForm()->addAlertMessage(author, message, QDateTime::currentDateTime());
else
g->getChatForm()->addMessage(author, message, isAction, QDateTime::currentDateTime(), true);
if ((static_cast<GenericChatroomWidget*>(g->getGroupWidget()) != activeChatroomWidget) || isMinimized() || !isActiveWindow())
{
g->setEventFlag(true);
if (targeted)
{
newMessageAlert(g->getGroupWidget());
g->setMentionedFlag(true); // useful for highlighting line or desktop notifications
}
g->getGroupWidget()->updateStatusLight();
}
g->setEventFlag(static_cast<GenericChatroomWidget*>(g->getGroupWidget()) != activeChatroomWidget);
if (targeted || Settings::getInstance().getGroupAlwaysNotify())
newMessageAlert(g->getGroupWidget());
if (targeted)
g->setMentionedFlag(true); // useful for highlighting line or desktop notifications
g->getGroupWidget()->updateStatusLight();
}
void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Change)

View File

@ -44,6 +44,7 @@ class FriendListWidget;
class MaskablePixmapWidget;
class QTimer;
class QTranslator;
class SystemTrayIcon;
class Widget : public QMainWindow
{
@ -81,6 +82,7 @@ public:
public slots:
void onSettingsClicked();
void setWindowTitle(const QString& title);
void forceShow();
signals:
void friendRequestAccepted(const QString& userId);
@ -90,6 +92,7 @@ signals:
void usernameChanged(const QString& username);
void statusMessageChanged(const QString& statusMessage);
void changeProfile(const QString& profile);
void resized();
private slots:
void onConnected();
@ -147,7 +150,7 @@ private:
void saveSplitterGeometry();
QString askProfiles();
QString detectProfile();
QSystemTrayIcon *icon;
SystemTrayIcon *icon;
QMenu *trayMenu;
QAction *statusOnline,
*statusAway,
@ -171,6 +174,7 @@ private:
Status beforeDisconnect = Status::Offline;
QTimer* idleTimer;
QTranslator* translator;
QRegExp nameMention, sanitizedNameMention;
};
void toxActivateEventHandler(const QByteArray& data);

467
translations/de.ts vendored

File diff suppressed because it is too large Load Diff

628
translations/fr.ts vendored

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@ TRANSLATIONS = translations/es.ts \
translations/fi.ts \
translations/fr.ts \
translations/it.ts \
translations/lt.ts \
translations/mannol.ts \
translations/pirate.ts \
translations/pl.ts \

714
translations/it.ts vendored

File diff suppressed because it is too large Load Diff

1579
translations/lt.ts vendored Normal file

File diff suppressed because it is too large Load Diff

765
translations/pl.ts vendored

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -30,3 +30,8 @@ div.alert_name {
background-color: @orange;
font: @bigBold;
}
a {
color: blue;
}

View File

@ -1,5 +1,5 @@
GenericChatroomWidget
{
GenericChatroomWidget
{
background-color: @themeMedium;
}
@ -8,31 +8,55 @@ GenericChatroomWidget[active="true"]
background-color: @white;
}
GenericChatroomWidget[active="false"]:hover
{
background-color: @themeLight;
}
GenericChatroomWidget[active="false"]:hover
{
background-color: @themeLight;
}
GenericChatroomWidget[active="true"] > QLabel#status
GenericChatroomWidget[active="true"][compact="true"] > QLabel#status
{
font: @small;
color: @mediumGrey;
}
GenericChatroomWidget[active="false"][compact="true"] > QLabel#status
{
font: @small;
color: @lightGrey;
}
GenericChatroomWidget[active="true"][compact="true"] > QLabel#name
{
font: @medium;
color: @darkGrey;
}
GenericChatroomWidget[active="false"][compact="true"] > QLabel#name
{
font: @medium;
color: @white;
}
GenericChatroomWidget[active="true"][compact="false"] > QLabel#status
{
font: @medium;
color: @mediumGrey;
}
GenericChatroomWidget[active="false"] > QLabel#status
GenericChatroomWidget[active="false"][compact="false"] > QLabel#status
{
font: @medium;
color: @lightGrey;
}
GenericChatroomWidget[active="true"] > QLabel#name
GenericChatroomWidget[active="true"][compact="false"] > QLabel#name
{
font: @big;
color: @darkGrey;
}
GenericChatroomWidget[active="false"] > QLabel#name
GenericChatroomWidget[active="false"][compact="false"] > QLabel#name
{
font: @big;
color: @white;
}
}

View File

@ -4,8 +4,8 @@ QPushButton#green
background-image: url(":/ui/micButton/micButton.png");
background-repeat: none;
border: none;
width: 25px;
height: 20px;
width: 22px;
height: 18px;
}
QPushButton#green:hover
@ -19,8 +19,8 @@ QPushButton#red
background-image: url(":/ui/micButton/micButtonPressed.png");
background-repeat: none;
border: none;
width: 25px;
height: 20px;
width: 22px;
height: 18px;
}
QPushButton#grey
@ -29,8 +29,8 @@ QPushButton#grey
background-image: url(":/ui/micButton/micButtonDisabled.png");
background-repeat: none;
border: none;
width: 25px;
height: 20px;
width: 22px;
height: 18px;
}
QPushButton:focus {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -1,4 +1,4 @@
QWidget
QWidget .QLabel, QWidget .QLineEdit
{
color: black;
background: white;

View File

@ -4,7 +4,7 @@ QPushButton#green
background-image: url(":/ui/volButton/volButton.png");
background-repeat: none;
border: none;
width: 25px;
width: 22px;
height: 18px;
}
@ -19,7 +19,17 @@ QPushButton#red
background-image: url(":/ui/volButton/volButtonPressed.png");
background-repeat: none;
border: none;
width: 25px;
width: 22px;
height: 18px;
}
QPushButton#grey
{
background-color: transparent;
background-image: url(":/ui/volButton/volButtonDisabled.png");
background-repeat: none;
border: none;
width: 22px;
height: 18px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

View File

@ -58,6 +58,7 @@ Widget::~Widget()
void Widget::setProgress(int value)
{
ui->progress->setValue(value);
ui->progress->repaint();
qApp->processEvents();
}
@ -126,7 +127,7 @@ void Widget::update()
setProgress(5);
/// 2. Check the update (5-50%)
float checkProgressStep = 45/diff.size();
float checkProgressStep = 45.0/(float)diff.size();
float checkProgress = 5;
for (UpdateFileMeta fileMeta : diff)
{
@ -152,21 +153,23 @@ void Widget::update()
setProgress(50);
/// 3. Install the update (50-95%)
float installProgressStep = 45/diff.size();
float installProgressStep = 45.0/(float)diff.size();
float installProgress = 50;
for (UpdateFileMeta fileMeta : diff)
{
// Backup old files
if (QFile(fileMeta.installpath).exists())
{
QFile(fileMeta.installpath+".bak").remove();
QFile(fileMeta.installpath).rename(fileMeta.installpath+".bak");
backups.append(fileMeta.installpath);
}
// Install new ones
QDir().mkpath(QFileInfo(fileMeta.installpath).absolutePath());
QFile fileFile(updateDirStr+fileMeta.installpath);
if (!fileFile.copy(fileMeta.installpath))
fatalError(tr("Unable to copy the update's files."));
fatalError(tr("Unable to copy the update's files from ")+(updateDirStr+fileMeta.installpath)+" to "+fileMeta.installpath);
installProgress += installProgressStep;
setProgress(installProgress);
}

109
windows/bootstrap.sh Normal file
View File

@ -0,0 +1,109 @@
#!/bin/sh
QTOX_DIR=`pwd`/..
if [ -d $QTOX_DIR/libs ]; then
echo "Remove ./libs and redownload/recompile dependencies?"
read -p "m/a/N (missing/all/no): " yn
yn=$(echo $yn | tr "[:upper:]" "[:lower:]")
if [ "$yn" == "a" ]; then
rm -rf $QTOX_DIR/libs
elif [ "$yn" == "n" ]; then
exit -1
elif [ "$yn" != "m" ]; then
exit -1
fi
fi
mkdir -p $QTOX_DIR/libs
cd $QTOX_DIR/libs
## toxcore
if [ ! -f "libtoxcore-win32-i686.zip" ]; then
wget --no-check-certificate http://jenkins.libtoxcore.so/job/libtoxcore-win32-i686/lastSuccessfulBuild/artifact/libtoxcore-win32-i686.zip
rm -rf include/tox
fi
if [ ! -d "include/tox" ]; then
$QTOX_DIR/tools/unzip -o libtoxcore-win32-i686.zip -d ./
fi
## filter_audio
if [ ! -f "bin/libfilteraudio.dll" ]; then
$QTOX_DIR/install_libfilteraudio.sh $QTOX_DIR/libs/filter_audio $QTOX_DIR/libs
fi
## OpenAL
if [ ! -f "openal-soft-1.16.0.tar.bz2" ]; then
wget http://kcat.strangesoft.net/openal-releases/openal-soft-1.16.0.tar.bz2
rm -rf openal-soft-1.16.0
fi
if [ ! -d "openal-soft-1.16.0" ]; then
tar -xvf openal-soft-1.16.0.tar.bz2
rm bin/OpenAL32.dll
fi
if [ ! -f "bin/OpenAL32.dll" ]; then
pushd openal-soft-1.16.0/build
cmake -G "MSYS Makefiles" -DQT_QMAKE_EXECUTABLE=NOTFOUND -DCMAKE_BUILD_TYPE=Release -DALSOFT_REQUIRE_DSOUND=NO -DCMAKE_INSTALL_PREFIX=$QTOX_DIR/libs ..
make
make install
popd
fi
## opencv
if [ ! -f "opencv-2.4.9.tar.gz" ]; then
wget --no-check-certificate https://github.com/Itseez/opencv/archive/2.4.9.tar.gz -O opencv-2.4.9.tar.gz
rm -rf opencv-2.4.9
fi
if [ ! -d "opencv-2.4.9" ]; then
tar -xvf opencv-2.4.9.tar.gz
rm bin/libopencv_core249.dll
fi
if [ ! -f "bin/libopencv_core249.dll" ]; then
mkdir opencv-2.4.9/build
pushd opencv-2.4.9/build
cmake -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$QTOX_DIR/libs \
-DBUILD_opencv_apps=NO \
-DBUILD_opencv_calib3d=NO \
-DBUILD_opencv_contrib=NO \
-DBUILD_opencv_core=YES \
-DBUILD_opencv_features2d=NO \
-DBUILD_opencv_flann=NO \
-DBUILD_opencv_gpu=NO \
-DBUILD_opencv_highgui=YES \
-DBUILD_opencv_imgproc=YES \
-DBUILD_opencv_legacy=NO \
-DBUILD_opencv_ml=NO \
-DBUILD_opencv_nonfree=NO \
-DBUILD_opencv_objdetect=NO \
-DBUILD_opencv_ocl=NO \
-DBUILD_opencv_photo=NO \
-DBUILD_opencv_stiching=NO \
-DBUILD_opencv_superres=NO \
-DBUILD_opencv_ts=NO \
-DBUILD_opencv_video=NO \
-DBUILD_opencv_videostab=NO \
-DBUILD_opencv_world=NO \
-DWITH_QT=NO \
-DBUILD_EXAMPLES=NO \
..
make
make install
for arch in x86 x64
do
if [ -d $QTOX_DIR/libs/$arch/mingw ]; then
mv $QTOX_DIR/libs/$arch/mingw/bin/* $QTOX_DIR/libs/bin/
mv $QTOX_DIR/libs/$arch/mingw/lib/* $QTOX_DIR/libs/lib/
rm -rf $QTOX_DIR/libs/$arch
fi
done
popd
fi
echo **Done**

357
windows/qtox64.nsi Normal file
View File

@ -0,0 +1,357 @@
###################
#META
###################
!define APP_NAME "qTox"
!define COMP_NAME "Tox"
!define WEB_SITE "https://github.com/tux3/qTox"
!define VERSION "1.0.0.0"
!define DESCRIPTION "qTox Installer"
!define COPYRIGHT "The Tox Project"
!define INSTALLER_NAME "setup-qtox.exe"
!define MAIN_APP_EXE "bin\qtox.exe"
!define INSTALL_TYPE "SetShellVarContext current"
!define REG_ROOT "HKCU"
!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\qtox.exe"
!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}"
!define REG_START_MENU "Start Menu Folder"
var SM_Folder
Name "${APP_NAME}"
Caption "${APP_NAME}"
OutFile "${INSTALLER_NAME}"
BrandingText "${APP_NAME}"
InstallDir "$PROGRAMFILES64\${APP_NAME}"
SetCompressor /SOLID lzma
VIProductVersion "${VERSION}"
VIAddVersionKey "ProductName" "${APP_NAME}"
VIAddVersionKey "CompanyName" "${COMP_NAME}"
VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
VIAddVersionKey "FileDescription" "${DESCRIPTION}"
VIAddVersionKey "FileVersion" "${VERSION}"
##############
#UNINSTALL LOG
##############
;AddItem macro
!macro AddItem Path
FileWrite $UninstLog "${Path}$\r$\n"
!macroend
;File macro
!macro File FileName
IfFileExists "$OUTDIR\${FileName}" +2
FileWrite $UninstLog "$OUTDIR\${FileName}$\r$\n"
File "${FileName}"
!macroend
;CreateShortcut macro
!macro CreateShortcut FilePath FilePointer Pamameters Icon IconIndex
FileWrite $UninstLog "${FilePath}$\r$\n"
CreateShortcut "${FilePath}" "${FilePointer}" "${Pamameters}" "${Icon}" "${IconIndex}"
!macroend
;Copy files macro
!macro CopyFiles SourcePath DestPath
IfFileExists "${DestPath}" +2
FileWrite $UninstLog "${DestPath}$\r$\n"
CopyFiles "${SourcePath}" "${DestPath}"
!macroend
;Rename macro
!macro Rename SourcePath DestPath
IfFileExists "${DestPath}" +2
FileWrite $UninstLog "${DestPath}$\r$\n"
Rename "${SourcePath}" "${DestPath}"
!macroend
;CreateDirectory macro
!macro CreateDirectory Path
CreateDirectory "${Path}"
FileWrite $UninstLog "${Path}$\r$\n"
!macroend
;SetOutPath macro
!macro SetOutPath Path
SetOutPath "${Path}"
FileWrite $UninstLog "${Path}$\r$\n"
!macroend
;WriteUninstaller macro
!macro WriteUninstaller Path
WriteUninstaller "${Path}"
FileWrite $UninstLog "${Path}$\r$\n"
!macroend
;WriteIniStr macro
!macro WriteIniStr IniFile SectionName EntryName NewValue
IfFileExists "${IniFile}" +2
FileWrite $UninstLog "${IniFile}$\r$\n"
WriteIniStr "${IniFile}" "${SectionName}" "${EntryName}" "${NewValue}"
!macroend
;WriteRegStr macro
!macro WriteRegStr RegRoot UnInstallPath Key Value
FileWrite $UninstLog "${RegRoot} ${UnInstallPath}$\r$\n"
WriteRegStr "${RegRoot}" "${UnInstallPath}" "${Key}" "${Value}"
!macroend
;WriteRegDWORD macro
!macro WriteRegDWORD RegRoot UnInstallPath Key Value
FileWrite $UninstLog "${RegRoot} ${UnInstallPath}$\r$\n"
WriteRegDWORD "${RegRoot}" "${UnInstallPath}" "${Key}" "${Value}"
!macroend
;BackupFile macro
!macro BackupFile FILE_DIR FILE BACKUP_TO
IfFileExists "${BACKUP_TO}\*.*" +2
CreateDirectory "${BACKUP_TO}"
IfFileExists "${FILE_DIR}\${FILE}" 0 +2
Rename "${FILE_DIR}\${FILE}" "${BACKUP_TO}\${FILE}"
!macroend
;RestoreFile macro
!macro RestoreFile BUP_DIR FILE RESTORE_TO
IfFileExists "${BUP_DIR}\${FILE}" 0 +2
Rename "${BUP_DIR}\${FILE}" "${RESTORE_TO}\${FILE}"
!macroend
;BackupFiles macro
!macro BackupFiles FILE_DIR FILE BACKUP_TO
IfFileExists "${BACKUP_TO}\*.*" +2
CreateDirectory "22222"
IfFileExists "${FILE_DIR}\${FILE}" 0 +7
FileWrite $UninstLog "${FILE_DIR}\${FILE}$\r$\n"
FileWrite $UninstLog "${BACKUP_TO}\${FILE}$\r$\n"
FileWrite $UninstLog "FileBackup$\r$\n"
Rename "${FILE_DIR}\${FILE}" "${BACKUP_TO}\${FILE}"
SetOutPath "${FILE_DIR}"
File "${FILE}" #After the Original file is backed up write the new file.
!macroend
;RestoreFiles macro
!macro RestoreFiles BUP_FILE RESTORE_FILE
IfFileExists "${BUP_FILE}" 0 +2
CopyFiles "${BUP_FILE}" "${RESTORE_FILE}"
!macroend
###################
#PREPARE UNINST LOG
###################
;Set the name of the uninstall log
!define UninstLog "uninstall.log"
Var UninstLog
;Uninstall log file missing.
LangString UninstLogMissing ${LANG_ENGLISH} "${UninstLog} not found!$\r$\nUninstallation cannot proceed!"
;AddItem macro
!define AddItem "!insertmacro AddItem"
;BackupFile macro
!define BackupFile "!insertmacro BackupFile"
;BackupFiles macro
!define BackupFiles "!insertmacro BackupFiles"
;Copy files macro
!define CopyFiles "!insertmacro CopyFiles"
;CreateDirectory macro
!define CreateDirectory "!insertmacro CreateDirectory"
;CreateShortcut macro
!define CreateShortcut "!insertmacro CreateShortcut"
;File macro
!define File "!insertmacro File"
;Rename macro
!define Rename "!insertmacro Rename"
;RestoreFile macro
!define RestoreFile "!insertmacro RestoreFile"
;RestoreFiles macro
!define RestoreFiles "!insertmacro RestoreFiles"
;SetOutPath macro
!define SetOutPath "!insertmacro SetOutPath"
;WriteRegDWORD macro
!define WriteRegDWORD "!insertmacro WriteRegDWORD"
;WriteRegStr macro
!define WriteRegStr "!insertmacro WriteRegStr"
;WriteUninstaller macro
!define WriteUninstaller "!insertmacro WriteUninstaller"
Section -openlogfile
CreateDirectory "$INSTDIR"
IfFileExists "$INSTDIR\${UninstLog}" +3
FileOpen $UninstLog "$INSTDIR\${UninstLog}" w
Goto +4
SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL
FileOpen $UninstLog "$INSTDIR\${UninstLog}" a
FileSeek $UninstLog 0 END
SectionEnd
##############
#MODERN UI
##############
!include "MUI.nsh"
!define MUI_ABORTWARNING
!define MUI_FINISHPAGE_NOAUTOCLOSE
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!ifdef REG_START_MENU
!define MUI_STARTMENUPAGE_NODISABLE
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "qTox"
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${REG_ROOT}"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "${UNINSTALL_PATH}"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${REG_START_MENU}"
!insertmacro MUI_PAGE_STARTMENU Application $SM_Folder
!endif
!insertmacro MUI_PAGE_INSTFILES
Function finishpageaction
${CreateShortcut} "$DESKTOP\qTox.lnk" "$INSTDIR\${MAIN_APP_EXE}" "" "" ""
FunctionEnd
!define MUI_FINISHPAGE_SHOWREADME ""
!define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut"
!define MUI_FINISHPAGE_SHOWREADME_FUNCTION finishpageaction
!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAIN_APP_EXE}"
!define MUI_FINISHPAGE_LINK "Find qTox on GitHub"
!define MUI_FINISHPAGE_LINK_LOCATION "https://github.com/tux3/qTox"
!insertmacro MUI_PAGE_FINISH
!define MUI_UNABORTWARNING
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
!insertmacro MUI_LANGUAGE "English"
#################
#INSTALL
#################
Section "Install"
# Install files
${SetOutPath} "$INSTDIR"
${WriteUninstaller} "uninstall.exe"
${CreateDirectory} "bin"
${SetOutPath} "$INSTDIR\bin"
${File} "qtox\*.*"
${CreateDirectory} "imageformats"
${SetOutPath} "$INSTDIR\bin\imageformats"
${File} "qtox\imageformats\*.*"
${SetOutPath} "$INSTDIR\bin"
${CreateDirectory} "platforms"
${SetOutPath} "$INSTDIR\bin\platforms"
${File} "qtox\platforms\*.*"
${SetOutPath} "$INSTDIR\bin"
${CreateDirectory} "sqldrivers"
${SetOutPath} "$INSTDIR\bin\sqldrivers"
${File} "qtox\sqldrivers\*.*"
${SetOutPath} "$INSTDIR\bin"
# Create shortcuts
${CreateDirectory} "$SMPROGRAMS\qTox"
${CreateShortCut} "$SMPROGRAMS\qTox\qTox.lnk" "$INSTDIR\${MAIN_APP_EXE}" "" "" ""
${CreateShortCut} "$SMPROGRAMS\qTox\Uninstall qTox.lnk" "$INSTDIR\uninstall.exe" "" "" ""
# Write setup/app info into the registry
${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}"
${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "Path" "$INSTDIR\bin\"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\uninstall.exe"
# Register the tox: protocol
${WriteRegStr} HKCR "tox" "" "URL:tox Protocol"
${WriteRegStr} HKCR "tox" "URL Protocol" ""
${WriteRegStr} HKCR "tox\shell\open\command" "" "$INSTDIR\${MAIN_APP_EXE} %1"
# Register the .tox file associations
${WriteRegStr} "HKCR" "Applications\qtox.exe\SupportedTypes" ".tox" ""
${WriteRegStr} HKCR ".tox" "" "toxsave"
${WriteRegStr} HKCR "toxsave" "" "Tox save file"
${WriteRegStr} HKCR "toxsave\DefaultIcon" "" "$INSTDIR\${MAIN_APP_EXE}"
${WriteRegStr} HKCR "toxsave\shell\open\command" "" "$INSTDIR\${MAIN_APP_EXE} %1"
SectionEnd
################
#UNINSTALL
################
Section Uninstall
;If there's no uninstall log, we'll try anyway to clean what we can
IfFileExists "$INSTDIR\${UninstLog}" +3
Goto noLog
Push $R0
Push $R1
Push $R2
SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL
FileOpen $UninstLog "$INSTDIR\${UninstLog}" r
StrCpy $R1 -1
GetLineCount:
ClearErrors
FileRead $UninstLog $R0
IntOp $R1 $R1 + 1
StrCpy $R0 $R0 -2
Push $R0
IfErrors 0 GetLineCount
Pop $R0
LoopRead:
StrCmp $R1 0 LoopDone
Pop $R0
IfFileExists "$R0\*.*" 0 +3
RMDir $R0 #is dir
Goto +9
IfFileExists $R0 0 +3
Delete $R0 #is file
Goto +6
StrCmp $R0 "${REG_ROOT} ${REG_APP_PATH}" 0 +3
DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" #is Reg Element
Goto +3
StrCmp $R0 "${REG_ROOT} ${UNINSTALL_PATH}" 0 +2
DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" #is Reg Element
IntOp $R1 $R1 - 1
Goto LoopRead
LoopDone:
FileClose $UninstLog
Delete "$INSTDIR\${UninstLog}"
noLog:
Delete /REBOOTOK "$INSTDIR\uninstall.exe"
RMDir /r /REBOOTOK "$INSTDIR\bin"
RMDir /REBOOTOK "$INSTDIR"
Pop $R2
Pop $R1
Pop $R0
;Remove start menu entries
RMDir /r /REBOOTOK "$SMPROGRAMS\qTox"
;Remove registry keys
DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}"
DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}"
DeleteRegKey HKCR "Applications\qtox.exe"
DeleteRegKey HKCR ".tox"
DeleteRegKey HKCR "tox"
DeleteRegKey HKCR "toxsave"
SectionEnd