Merge branch 'master' into profiles
Holy shit first big merge of my career, I'm mildly amazed it even compiles Conflicts: core.cpp widget/form/settingsform.cpp widget/form/settingsform.h widget/widget.cpp
68
README.md
|
@ -15,31 +15,73 @@ However, it is not a fork.
|
|||
- Tox DNS
|
||||
- Translations in various languages
|
||||
|
||||
<h2>Requirements</h2>
|
||||
<h2>Downloads</h2>
|
||||
|
||||
This client runs on Windows, Linux and Mac natively, but is not build regularly for Linux <br/>
|
||||
Linux users will have to compile the source code themselves if they want the latest updates.
|
||||
This client runs on Windows, Linux and Mac natively.<br/>
|
||||
|
||||
<a href="https://jenkins.libtoxcore.so/job/tux3-toxgui-win32/lastSuccessfulBuild/artifact/toxgui-win32.zip">Windows download</a><br/>
|
||||
<a href="https://jenkins.libtoxcore.so/job/ToxGUI%20OS%20X/lastSuccessfulBuild/artifact/toxgui.dmg">Mac download </a><br/>
|
||||
<a href="https://mega.co.nz/#!9l5B0QqZ!O2glB8XE_Tcf4zTub2WEk-_9Ra43RoeiFV-AQBKDZJU">Linux download (12st July 2014 20:30 GMT)</a><br/>
|
||||
Note that the Linux download has not been tested and may not be up to date.<br/>
|
||||
<a href="https://jenkins.libtoxcore.so/job/ToxGUI%20OS%20X/lastSuccessfulBuild/artifact/qtox.dmg">Mac download </a><br/>
|
||||
<a href="https://jenkins.libtoxcore.so/job/qTox-linux-amd64/">Linux download</a> (click "Last successful artifacts")<br/>
|
||||
|
||||
<h3>Screenshots</h3>
|
||||
<h5>Note: The screenshots may not always be up to date, but they should give a good idea of the general look and features</h5>
|
||||
<img src="http://i.imgur.com/mMUdr6u.png"/>
|
||||
<img src="http://i.imgur.com/66ARBGC.png"/>
|
||||
|
||||
<h3>Compiling</h3>
|
||||
Compiling toxgui requires Qt 5.2 with the Qt Multimedia module and a C++11 compatible compiler.
|
||||
It also requires the toxcore and toxav libraries.
|
||||
<h3>Compiling on GNU-Linux</h3>
|
||||
<h4>Acquiring dependencies</h4>
|
||||
Compiling qTox requires several dependencies, however these are easily installable
|
||||
with your system's package manager. The step-by-step instructions assume Debian-style apt, but
|
||||
it should be easy enough to get equivalent packages with yum or pacman.
|
||||
|
||||
To compile, first clone or download the qTox repository and open a terminal in the qTox folder.
|
||||
Then run the script bootstrap.sh (for Linux and Mac) or bootsrap.bat (for Windows) to download an up-to-date toxcore.
|
||||
And finally run the commands "qmake" and "make" to start building qTox.
|
||||
First, we need Qt 5.2 with a C++11 compatible compiler:
|
||||
```bash
|
||||
sudo apt-get install build-essential qt5-qmake qt5-default
|
||||
```
|
||||
|
||||
toxcore and toxav, the client-agnostic network code for Tox, has several dependencies
|
||||
of its own (see <a href="https://github.com/irungentoo/toxcore/blob/master/INSTALL.md#unix">its installation guide</a> for more details):
|
||||
```bash
|
||||
sudo apt-get install libtool autotools-dev automake checkinstall check git yasm libopus-dev libvpx-dev
|
||||
```
|
||||
|
||||
<h3>OSX Install Guide</h3>
|
||||
Finally, qTox itself requires OpenAL and OpenCV:
|
||||
```bash
|
||||
sudo apt-get install libopenal-dev libopencv-dev
|
||||
```
|
||||
|
||||
<h4>Compilation</h4>
|
||||
|
||||
Having acquired all the dependencies, the following commands should get and compile qTox:
|
||||
|
||||
```bash
|
||||
wget -O qtox.tgz https://github.com/tux3/qTox/archive/master.tar.gz
|
||||
tar xvf qtox.tgz
|
||||
cd qTox-master
|
||||
./bootstrap.sh # This will automagically download and compile libsodium, toxcore, and toxav
|
||||
qmake
|
||||
make # Should compile to "qtox"
|
||||
```
|
||||
|
||||
And that's it!
|
||||
|
||||
<h4>Building packages</h4>
|
||||
|
||||
qTox now has the experimental and probably-dodgy ability to package itself (in .deb
|
||||
form natively, and .rpm form with <a href="http://joeyh.name/code/alien/">alien</a>).
|
||||
|
||||
After installing the required dependencies, run `bootstrap.sh` and then run the
|
||||
`buildPackages.sh` script, found in the tools folder. It will automatically get the
|
||||
packages necessary for building .debs, so be prepared to type your password for sudo.
|
||||
|
||||
<h3>OSX Easy Install</h3>
|
||||
|
||||
Since https://github.com/ReDetection/homebrew-qtox you can easily install qtox with homebrew
|
||||
```bash
|
||||
brew install --HEAD ReDetection/qtox/qtox
|
||||
```
|
||||
|
||||
<h3>OSX Full Install Guide</h3>
|
||||
|
||||
<strong>This guide is intended for people who wish to use an existing or new ProjectTox-Core installation separate to the bundled installation with qTox, if you do not wish to use a separate installation you can skip to the section titled 'Final Steps'.</strong>
|
||||
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "audiobuffer.h"
|
||||
|
||||
AudioBuffer::AudioBuffer() :
|
||||
QIODevice(0)
|
||||
{
|
||||
open(QIODevice::ReadWrite);
|
||||
}
|
||||
|
||||
AudioBuffer::~AudioBuffer()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
qint64 AudioBuffer::readData(char *data, qint64 len)
|
||||
{
|
||||
qint64 total;
|
||||
bufferMutex.lock();
|
||||
try {
|
||||
total = qMin((qint64)buffer.size(), len);
|
||||
memcpy(data, buffer.constData(), total);
|
||||
buffer = buffer.mid(total);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
bufferMutex.unlock();
|
||||
return 0;
|
||||
}
|
||||
bufferMutex.unlock();
|
||||
return total;
|
||||
}
|
||||
|
||||
qint64 AudioBuffer::writeData(const char* data, qint64 len)
|
||||
{
|
||||
bufferMutex.lock();
|
||||
try {
|
||||
buffer.append(data, len);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
bufferMutex.unlock();
|
||||
return 0;
|
||||
}
|
||||
bufferMutex.unlock();
|
||||
return len;
|
||||
}
|
||||
|
||||
qint64 AudioBuffer::bytesAvailable() const
|
||||
{
|
||||
bufferMutex.lock();
|
||||
long long size = buffer.size() + QIODevice::bytesAvailable();
|
||||
bufferMutex.unlock();
|
||||
return size;
|
||||
}
|
||||
|
||||
qint64 AudioBuffer::bufferSize() const
|
||||
{
|
||||
bufferMutex.lock();
|
||||
long long size = buffer.size();
|
||||
bufferMutex.unlock();
|
||||
return size;
|
||||
}
|
||||
|
||||
void AudioBuffer::clear()
|
||||
{
|
||||
bufferMutex.lock();
|
||||
buffer.clear();
|
||||
bufferMutex.unlock();
|
||||
}
|
75
bootstrap.sh
|
@ -10,8 +10,10 @@ INSTALL_DIR=libs
|
|||
# just for convenience
|
||||
BASE_DIR=${SCRIPT_DIR}/${INSTALL_DIR}
|
||||
|
||||
SODIUM_VER=0.7.0
|
||||
|
||||
# directory names of cloned repositories
|
||||
SODIUM_DIR=libsodium-0.5.0
|
||||
SODIUM_DIR=libsodium-$SODIUM_VER
|
||||
TOX_CORE_DIR=libtoxcore-latest
|
||||
|
||||
# this boolean describes whether the installation of
|
||||
|
@ -19,6 +21,8 @@ TOX_CORE_DIR=libtoxcore-latest
|
|||
# the default value is 'false' and will be set to 'true'
|
||||
# if this script gets the parameter -t or --tox
|
||||
TOX_ONLY=false
|
||||
GLOBAL=false
|
||||
KEEP=false
|
||||
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
echo "internal error detected!"
|
||||
|
@ -41,9 +45,16 @@ fi
|
|||
|
||||
|
||||
########## check input parameters ##########
|
||||
if [ $# -ge 1 ] ; then
|
||||
while [ $# -ge 1 ] ; do
|
||||
if [ ${1} = "-t" -o ${1} = "--tox" ] ; then
|
||||
TOX_ONLY=true
|
||||
shift
|
||||
elif [ ${1} = "-g" -o ${1} = "--global" ] ; then
|
||||
GLOBAL=true
|
||||
shift
|
||||
elif [ ${1} = "-k" -o ${1} = "--keep" ]; then
|
||||
KEEP=true
|
||||
shift
|
||||
else
|
||||
if [ ${1} != "-h" -a ${1} != "--help" ] ; then
|
||||
echo "[ERROR] Unknown parameter \"${1}\""
|
||||
|
@ -54,21 +65,26 @@ if [ $# -ge 1 ] ; then
|
|||
echo "Use this script to install/update libsodium and libtoxcore in ${INSTALL_DIR}"
|
||||
echo ""
|
||||
echo "usage:"
|
||||
echo " ${0} [-t|--tox|-h|--help]"
|
||||
echo " ${0} [-t|--tox|-h|--help|-g|--global|-k|--keep]"
|
||||
echo ""
|
||||
echo "parameters:"
|
||||
echo " -h|--help: displays this help"
|
||||
echo " -t|--tox : only install/update libtoxcore"
|
||||
echo " requires an already installed libsodium"
|
||||
echo " -h|--help : displays this help"
|
||||
echo " -t|--tox : only install/update libtoxcore"
|
||||
echo " requires an already installed libsodium"
|
||||
echo " -g|--global: installs libtox* and libsodium globally"
|
||||
echo " (also disables local configure prefixes)"
|
||||
echo " -k|--keep : does not delete the build directories afterwards"
|
||||
echo ""
|
||||
echo "example usages:"
|
||||
echo " ${0} -- to install libsodium and libtoxcore"
|
||||
echo " ${0} -t -- to update already installed libtoxcore"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
echo "Tox only: $TOX_ONLY"
|
||||
echo "Global : $GLOBAL"
|
||||
echo "Keep : $KEEP"
|
||||
|
||||
############### prepare step ###############
|
||||
# create BASE_DIR directory if necessary
|
||||
|
@ -83,17 +99,29 @@ rm -rf ${BASE_DIR}/${TOX_CORE_DIR}
|
|||
|
||||
|
||||
############### install step ###############
|
||||
# clone current master of libsodium and switch to version 0.5.0
|
||||
# clone current master of libsodium and switch to version $SODIUM_VER
|
||||
# afterwards install libsodium to INSTALL_DIR
|
||||
# skip the installation if TOX_ONLY is true
|
||||
if [[ $TOX_ONLY = "false" ]]; then
|
||||
git clone git://github.com/jedisct1/libsodium.git ${BASE_DIR}/${SODIUM_DIR}
|
||||
pushd ${BASE_DIR}/${SODIUM_DIR}
|
||||
git checkout tags/0.5.0
|
||||
git checkout tags/$SODIUM_VER
|
||||
./autogen.sh
|
||||
./configure --prefix=${BASE_DIR}/
|
||||
|
||||
if [[ $GLOBAL = "false" ]]; then
|
||||
./configure --prefix=${BASE_DIR}/
|
||||
else
|
||||
./configure
|
||||
fi
|
||||
|
||||
make -j2 check
|
||||
make install
|
||||
|
||||
if [[ $GLOBAL = "false" ]]; then
|
||||
make install
|
||||
else
|
||||
sudo make install
|
||||
fi
|
||||
|
||||
popd
|
||||
fi
|
||||
|
||||
|
@ -103,14 +131,25 @@ fi
|
|||
git clone https://github.com/irungentoo/toxcore.git ${BASE_DIR}/${TOX_CORE_DIR}
|
||||
pushd ${BASE_DIR}/${TOX_CORE_DIR}
|
||||
./autogen.sh
|
||||
./configure --prefix=${BASE_DIR}/ --with-libsodium-headers=${BASE_DIR}/include --with-libsodium-libs=${BASE_DIR}/lib
|
||||
if [[ $GLOBAL = "false" ]]; then
|
||||
./configure --prefix=${BASE_DIR}/ --with-libsodium-headers=${BASE_DIR}/include --with-libsodium-libs=${BASE_DIR}/lib
|
||||
else
|
||||
./configure
|
||||
fi
|
||||
|
||||
make -j2
|
||||
make install
|
||||
|
||||
if [[ $GLOBAL = "false" ]]; then
|
||||
make install
|
||||
else
|
||||
sudo make install
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
|
||||
|
||||
############### cleanup step ###############
|
||||
# remove cloned repositories
|
||||
rm -rf ${BASE_DIR}/${SODIUM_DIR}
|
||||
rm -rf ${BASE_DIR}/${TOX_CORE_DIR}
|
||||
if [[ $KEEP = "false" ]]; then
|
||||
rm -rf ${BASE_DIR}/${SODIUM_DIR}
|
||||
rm -rf ${BASE_DIR}/${TOX_CORE_DIR}
|
||||
fi
|
||||
|
|
716
core.cpp
|
@ -20,6 +20,9 @@
|
|||
#include "settings.h"
|
||||
#include "widget/widget.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
|
@ -33,10 +36,6 @@ const QString Core::CONFIG_FILE_NAME = "data";
|
|||
const QString Core::TOX_EXT = ".tox";
|
||||
QList<ToxFile> Core::fileSendQueue;
|
||||
QList<ToxFile> Core::fileRecvQueue;
|
||||
ToxCall Core::calls[TOXAV_MAX_CALLS];
|
||||
const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4};
|
||||
uint8_t* Core::videobuf;
|
||||
int Core::videoBusyness;
|
||||
|
||||
Core::Core(Camera* cam, QThread *coreThread) :
|
||||
tox(nullptr), camera(cam)
|
||||
|
@ -48,8 +47,6 @@ Core::Core(Camera* cam, QThread *coreThread) :
|
|||
toxTimer->setSingleShot(true);
|
||||
//saveTimer = new QTimer(this);
|
||||
//saveTimer->start(TOX_SAVE_INTERVAL);
|
||||
//fileTimer = new QTimer(this);
|
||||
//fileTimer->start(TOX_FILE_INTERVAL);
|
||||
bootstrapTimer = new QTimer(this);
|
||||
bootstrapTimer->start(TOX_BOOTSTRAP_INTERVAL);
|
||||
connect(toxTimer, &QTimer::timeout, this, &Core::process);
|
||||
|
@ -63,11 +60,32 @@ Core::Core(Camera* cam, QThread *coreThread) :
|
|||
{
|
||||
calls[i].sendAudioTimer = new QTimer();
|
||||
calls[i].sendVideoTimer = new QTimer();
|
||||
calls[i].audioBuffer.moveToThread(coreThread);
|
||||
calls[i].sendAudioTimer->moveToThread(coreThread);
|
||||
calls[i].sendVideoTimer->moveToThread(coreThread);
|
||||
connect(calls[i].sendVideoTimer, &QTimer::timeout, [this,i](){sendCallVideo(i);});
|
||||
}
|
||||
|
||||
// OpenAL init
|
||||
alOutDev = alcOpenDevice(nullptr);
|
||||
if (!alOutDev)
|
||||
{
|
||||
qWarning() << "Core: Cannot open output audio device";
|
||||
}
|
||||
else
|
||||
{
|
||||
alContext=alcCreateContext(alOutDev,nullptr);
|
||||
if (!alcMakeContextCurrent(alContext))
|
||||
{
|
||||
qWarning() << "Core: Cannot create output audio context";
|
||||
alcCloseDevice(alOutDev);
|
||||
}
|
||||
else
|
||||
alGenSources(1, &alMainSource);
|
||||
}
|
||||
alInDev = alcCaptureOpenDevice(NULL,av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16,
|
||||
(av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) / 1000);
|
||||
if (!alInDev)
|
||||
qWarning() << "Core: Cannot open input audio device";
|
||||
}
|
||||
|
||||
Core::~Core()
|
||||
|
@ -83,6 +101,16 @@ Core::~Core()
|
|||
delete[] videobuf;
|
||||
videobuf=nullptr;
|
||||
}
|
||||
|
||||
if (alContext)
|
||||
{
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(alContext);
|
||||
}
|
||||
if (alOutDev)
|
||||
alcCloseDevice(alOutDev);
|
||||
if (alInDev)
|
||||
alcCaptureCloseDevice(alInDev);
|
||||
}
|
||||
|
||||
void Core::start()
|
||||
|
@ -93,12 +121,36 @@ void Core::start()
|
|||
qDebug() << "Core starting with IPv6 enabled";
|
||||
else
|
||||
qWarning() << "Core starting with IPv6 disabled. LAN discovery may not work properly.";
|
||||
tox = tox_new(enableIPv6);
|
||||
|
||||
Tox_Options toxOptions;
|
||||
toxOptions.ipv6enabled = enableIPv6;
|
||||
toxOptions.udp_disabled = 0;
|
||||
toxOptions.proxy_enabled = false;
|
||||
toxOptions.proxy_address[0] = 0;
|
||||
toxOptions.proxy_port = 0;
|
||||
|
||||
tox = tox_new(&toxOptions);
|
||||
if (tox == nullptr)
|
||||
{
|
||||
qCritical() << "Tox core failed to start";
|
||||
emit failedToStart();
|
||||
return;
|
||||
if (enableIPv6) // Fallback to IPv4
|
||||
{
|
||||
toxOptions.ipv6enabled = false;
|
||||
tox = tox_new(&toxOptions);
|
||||
if (tox == nullptr)
|
||||
{
|
||||
qCritical() << "Tox core failed to start";
|
||||
emit failedToStart();
|
||||
return;
|
||||
}
|
||||
else
|
||||
qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery may not work properly.";
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Tox core failed to start";
|
||||
emit failedToStart();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
toxav = toxav_new(tox, TOXAV_MAX_CALLS);
|
||||
|
@ -109,6 +161,8 @@ void Core::start()
|
|||
return;
|
||||
}
|
||||
|
||||
qsrand(time(nullptr));
|
||||
|
||||
// where do we find the data file?
|
||||
QString path;
|
||||
{ // read data from whose profile?
|
||||
|
@ -153,8 +207,8 @@ void Core::start()
|
|||
toxav_register_callstate_callback(toxav, onAvRequestTimeout, av_OnRequestTimeout, this);
|
||||
toxav_register_callstate_callback(toxav, onAvPeerTimeout, av_OnPeerTimeout, this);
|
||||
|
||||
toxav_register_audio_recv_callback(toxav, playCallAudio);
|
||||
toxav_register_video_recv_callback(toxav, playCallVideo);
|
||||
toxav_register_audio_recv_callback(toxav, playCallAudio, this);
|
||||
toxav_register_video_recv_callback(toxav, playCallVideo, this);
|
||||
|
||||
uint8_t friendAddress[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(tox, friendAddress);
|
||||
|
@ -261,7 +315,7 @@ void Core::onFileSendRequestCallback(Tox*, int32_t friendnumber, uint8_t filenum
|
|||
fileRecvQueue.append(file);
|
||||
emit static_cast<Core*>(core)->fileReceiveRequested(fileRecvQueue.last());
|
||||
}
|
||||
void Core::onFileControlCallback(Tox*, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
|
||||
void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
|
||||
uint8_t control_type, const uint8_t*, uint16_t, void *core)
|
||||
{
|
||||
ToxFile* file{nullptr};
|
||||
|
@ -292,12 +346,15 @@ void Core::onFileControlCallback(Tox*, int32_t friendnumber, uint8_t receive_sen
|
|||
qWarning("Core::onFileControlCallback: No such file in queue");
|
||||
return;
|
||||
}
|
||||
if (control_type == TOX_FILECONTROL_ACCEPT && receive_send == 1)
|
||||
if (receive_send == 1 && control_type == TOX_FILECONTROL_ACCEPT)
|
||||
{
|
||||
file->status = ToxFile::TRANSMITTING;
|
||||
emit static_cast<Core*>(core)->fileTransferAccepted(*file);
|
||||
qDebug() << "Core: File control callback, file accepted";
|
||||
file->sendFuture = QtConcurrent::run(sendAllFileData, static_cast<Core*>(core), file);
|
||||
file->sendTimer = new QTimer(static_cast<Core*>(core));
|
||||
connect(file->sendTimer, &QTimer::timeout, std::bind(sendAllFileData,static_cast<Core*>(core), file));
|
||||
file->sendTimer->setSingleShot(true);
|
||||
file->sendTimer->start(TOX_FILE_INTERVAL);
|
||||
}
|
||||
else if (receive_send == 1 && control_type == TOX_FILECONTROL_KILL)
|
||||
{
|
||||
|
@ -305,8 +362,26 @@ void Core::onFileControlCallback(Tox*, int32_t friendnumber, uint8_t receive_sen
|
|||
.arg(file->fileNum).arg(file->friendId);
|
||||
file->status = ToxFile::STOPPED;
|
||||
emit static_cast<Core*>(core)->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
|
||||
file->sendFuture.waitForFinished(); // Wait for sendAllFileData to return before deleting the ToxFile
|
||||
removeFileFromQueue(true, file->friendId, file->fileNum);
|
||||
// Wait for sendAllFileData to return before deleting the ToxFile, we MUST ensure this or we'll use after free
|
||||
if (file->sendTimer)
|
||||
{
|
||||
QThread::msleep(1);
|
||||
qApp->processEvents();
|
||||
if (file->sendTimer)
|
||||
{
|
||||
delete file->sendTimer;
|
||||
file->sendTimer = nullptr;
|
||||
}
|
||||
}
|
||||
removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum);
|
||||
}
|
||||
else if (receive_send == 1 && control_type == TOX_FILECONTROL_FINISHED)
|
||||
{
|
||||
qDebug() << QString("Core::onFileControlCallback: Transfer of file %1 to friend %2 is complete")
|
||||
.arg(file->fileNum).arg(file->friendId);
|
||||
file->status = ToxFile::STOPPED;
|
||||
emit static_cast<Core*>(core)->fileTransferFinished(*file);
|
||||
removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum);
|
||||
}
|
||||
else if (receive_send == 0 && control_type == TOX_FILECONTROL_KILL)
|
||||
{
|
||||
|
@ -314,7 +389,7 @@ void Core::onFileControlCallback(Tox*, int32_t friendnumber, uint8_t receive_sen
|
|||
.arg(file->fileNum).arg(file->friendId);
|
||||
file->status = ToxFile::STOPPED;
|
||||
emit static_cast<Core*>(core)->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::RECEIVING);
|
||||
removeFileFromQueue(false, file->friendId, file->fileNum);
|
||||
removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum);
|
||||
}
|
||||
else if (receive_send == 0 && control_type == TOX_FILECONTROL_FINISHED)
|
||||
{
|
||||
|
@ -322,7 +397,9 @@ void Core::onFileControlCallback(Tox*, int32_t friendnumber, uint8_t receive_sen
|
|||
.arg(file->fileNum).arg(file->friendId);
|
||||
file->status = ToxFile::STOPPED;
|
||||
emit static_cast<Core*>(core)->fileTransferFinished(*file);
|
||||
removeFileFromQueue(false, file->friendId, file->fileNum);
|
||||
// confirm receive is complete
|
||||
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0);
|
||||
removeFileFromQueue((bool)receive_send, file->friendId, file->fileNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -376,6 +453,19 @@ void Core::requestFriendship(const QString& friendAddress, const QString& messag
|
|||
if (friendId < 0) {
|
||||
emit failedToAddFriend(userId);
|
||||
} else {
|
||||
// Update our friendAddresses
|
||||
bool found=false;
|
||||
QList<QString>& friendAddresses = Settings::getInstance().friendAddresses;
|
||||
for (QString& addr : friendAddresses)
|
||||
{
|
||||
if (addr.toUpper().contains(friendAddress))
|
||||
{
|
||||
addr = friendAddress;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
friendAddresses.append(friendAddress);
|
||||
emit friendAdded(friendId, userId);
|
||||
}
|
||||
saveConfiguration();
|
||||
|
@ -445,7 +535,7 @@ void Core::pauseResumeFileSend(int friendId, int fileNum)
|
|||
}
|
||||
if (!file)
|
||||
{
|
||||
qWarning("Core::cancelFileSend: No such file in queue");
|
||||
qWarning("Core::pauseResumeFileSend: No such file in queue");
|
||||
return;
|
||||
}
|
||||
if (file->status == ToxFile::TRANSMITTING)
|
||||
|
@ -515,7 +605,7 @@ void Core::cancelFileSend(int friendId, int fileNum)
|
|||
file->status = ToxFile::STOPPED;
|
||||
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
|
||||
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
|
||||
file->sendFuture.waitForFinished(); // Wait until sendAllFileData returns before deleting
|
||||
while (file->sendTimer) QThread::msleep(1); // Wait until sendAllFileData returns before deleting
|
||||
removeFileFromQueue(true, friendId, fileNum);
|
||||
}
|
||||
|
||||
|
@ -696,25 +786,36 @@ void Core::onFileTransferFinished(ToxFile file)
|
|||
|
||||
void Core::bootstrapDht()
|
||||
{
|
||||
qDebug() << "Core: Bootstraping DHT";
|
||||
const Settings& s = Settings::getInstance();
|
||||
QList<Settings::DhtServer> dhtServerList = s.getDhtServerList();
|
||||
|
||||
static int j = 0;
|
||||
int i=0;
|
||||
int listSize = dhtServerList.size();
|
||||
while (i<5)
|
||||
static int j = qrand() % listSize, n=0;
|
||||
|
||||
// We couldn't connect after trying 6 different nodes, let's try something else
|
||||
if (n>3)
|
||||
{
|
||||
qDebug() << "Core: We're having trouble connecting to the DHT, slowing down";
|
||||
bootstrapTimer->setInterval(TOX_BOOTSTRAP_INTERVAL*(n-1));
|
||||
}
|
||||
else
|
||||
qDebug() << "Core: Connecting to the DHT ...";
|
||||
|
||||
int i=0;
|
||||
while (i < (2 - (n>3)))
|
||||
{
|
||||
const Settings::DhtServer& dhtServer = dhtServerList[j % listSize];
|
||||
if (tox_bootstrap_from_address(tox, dhtServer.address.toLatin1().data(),
|
||||
0, qToBigEndian(dhtServer.port), CUserId(dhtServer.userId).data()) == 1)
|
||||
qToBigEndian(dhtServer.port), CUserId(dhtServer.userId).data()) == 1)
|
||||
qDebug() << QString("Core: Bootstraping from ")+dhtServer.name+QString(", addr ")+dhtServer.address.toLatin1().data()
|
||||
+QString(", port ")+QString().setNum(dhtServer.port);
|
||||
else
|
||||
qDebug() << "Core: Error bootstraping from "+dhtServer.name;
|
||||
|
||||
tox_do(tox);
|
||||
j++;
|
||||
i++;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -825,7 +926,7 @@ void Core::saveConfiguration(const QString& path)
|
|||
qWarning() << "Core::saveConfiguration: Tox not started, aborting!";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QSaveFile configurationFile(path);
|
||||
if (!configurationFile.open(QIODevice::WriteOnly)) {
|
||||
qCritical() << "File " << path << " cannot be opened";
|
||||
|
@ -840,8 +941,10 @@ void Core::saveConfiguration(const QString& path)
|
|||
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
|
||||
configurationFile.commit();
|
||||
delete[] data;
|
||||
//configurationFile.close();
|
||||
}
|
||||
|
||||
qDebug() << "Core: writing settings";
|
||||
Settings::getInstance().save();
|
||||
}
|
||||
|
||||
void Core::loadFriends()
|
||||
|
@ -981,486 +1084,85 @@ void Core::removeFileFromQueue(bool sendQueue, int friendId, int fileId)
|
|||
|
||||
void Core::sendAllFileData(Core *core, ToxFile* file)
|
||||
{
|
||||
while (file->bytesSent < file->filesize)
|
||||
if (file->status == ToxFile::PAUSED)
|
||||
{
|
||||
if (file->status == ToxFile::PAUSED)
|
||||
{
|
||||
QThread::sleep(5);
|
||||
continue;
|
||||
}
|
||||
else if (file->status == ToxFile::STOPPED)
|
||||
{
|
||||
qWarning("Core::sendAllFileData: File is stopped");
|
||||
return;
|
||||
}
|
||||
emit core->fileTransferInfo(file->friendId, file->fileNum, file->filesize, file->bytesSent, ToxFile::SENDING);
|
||||
qApp->processEvents();
|
||||
long long chunkSize = tox_file_data_size(core->tox, file->friendId);
|
||||
if (chunkSize == -1)
|
||||
{
|
||||
qWarning("Core::fileHeartbeat: Error getting preffered chunk size, aborting file send");
|
||||
file->status = ToxFile::STOPPED;
|
||||
emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
|
||||
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
|
||||
removeFileFromQueue(true, file->friendId, file->fileNum);
|
||||
return;
|
||||
}
|
||||
qDebug() << "chunkSize: " << chunkSize;
|
||||
chunkSize = std::min(chunkSize, file->filesize);
|
||||
uint8_t* data = new uint8_t[chunkSize];
|
||||
file->file->seek(file->bytesSent);
|
||||
int readSize = file->file->read((char*)data, chunkSize);
|
||||
if (readSize == -1)
|
||||
{
|
||||
qWarning() << QString("Core::sendAllFileData: Error reading from file: %1").arg(file->file->errorString());
|
||||
delete[] data;
|
||||
QThread::msleep(5);
|
||||
continue;
|
||||
}
|
||||
else if (readSize == 0)
|
||||
{
|
||||
qWarning() << QString("Core::sendAllFileData: Nothing to read from file: %1").arg(file->file->errorString());
|
||||
delete[] data;
|
||||
QThread::msleep(5);
|
||||
continue;
|
||||
}
|
||||
if (tox_file_send_data(core->tox, file->friendId, file->fileNum, data, readSize) == -1)
|
||||
{
|
||||
//qWarning("Core::fileHeartbeat: Error sending data chunk");
|
||||
core->process();
|
||||
delete[] data;
|
||||
QThread::msleep(5);
|
||||
continue;
|
||||
}
|
||||
file->sendTimer->start(5+TOX_FILE_INTERVAL);
|
||||
return;
|
||||
}
|
||||
else if (file->status == ToxFile::STOPPED)
|
||||
{
|
||||
qWarning("Core::sendAllFileData: File is stopped");
|
||||
file->sendTimer->disconnect();
|
||||
delete file->sendTimer;
|
||||
file->sendTimer = nullptr;
|
||||
return;
|
||||
}
|
||||
emit core->fileTransferInfo(file->friendId, file->fileNum, file->filesize, file->bytesSent, ToxFile::SENDING);
|
||||
qApp->processEvents();
|
||||
long long chunkSize = tox_file_data_size(core->tox, file->friendId);
|
||||
if (chunkSize == -1)
|
||||
{
|
||||
qWarning("Core::fileHeartbeat: Error getting preffered chunk size, aborting file send");
|
||||
file->status = ToxFile::STOPPED;
|
||||
emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
|
||||
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
|
||||
removeFileFromQueue(true, file->friendId, file->fileNum);
|
||||
return;
|
||||
}
|
||||
//qDebug() << "chunkSize: " << chunkSize;
|
||||
chunkSize = std::min(chunkSize, file->filesize);
|
||||
uint8_t* data = new uint8_t[chunkSize];
|
||||
file->file->seek(file->bytesSent);
|
||||
int readSize = file->file->read((char*)data, chunkSize);
|
||||
if (readSize == -1)
|
||||
{
|
||||
qWarning() << QString("Core::sendAllFileData: Error reading from file: %1").arg(file->file->errorString());
|
||||
delete[] data;
|
||||
file->bytesSent += readSize;
|
||||
//qDebug() << QString("Core::fileHeartbeat: sent %1/%2 bytes").arg(file->bytesSent).arg(file->fileData.size());
|
||||
}
|
||||
qDebug("Core::fileHeartbeat: Transfer finished");
|
||||
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0);
|
||||
file->status = ToxFile::STOPPED;
|
||||
emit core->fileTransferFinished(*file);
|
||||
removeFileFromQueue(true, file->friendId, file->fileNum);
|
||||
}
|
||||
|
||||
void Core::onAvInvite(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV invite";
|
||||
file->status = ToxFile::STOPPED;
|
||||
emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
|
||||
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
|
||||
removeFileFromQueue(true, file->friendId, file->fileNum);
|
||||
return;
|
||||
}
|
||||
|
||||
int transType = toxav_get_peer_transmission_type(toxav, call_index, 0);
|
||||
if (transType == TypeVideo)
|
||||
else if (readSize == 0)
|
||||
{
|
||||
qDebug() << QString("Core: AV invite from %1 with video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avInvite(friendId, call_index, true);
|
||||
qWarning() << QString("Core::sendAllFileData: Nothing to read from file: %1").arg(file->file->errorString());
|
||||
delete[] data;
|
||||
file->status = ToxFile::STOPPED;
|
||||
emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
|
||||
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
|
||||
removeFileFromQueue(true, file->friendId, file->fileNum);
|
||||
return;
|
||||
}
|
||||
if (tox_file_send_data(core->tox, file->friendId, file->fileNum, data, readSize) == -1)
|
||||
{
|
||||
//qWarning("Core::fileHeartbeat: Error sending data chunk");
|
||||
//core->process();
|
||||
delete[] data;
|
||||
//QThread::msleep(1);
|
||||
file->sendTimer->start(1+TOX_FILE_INTERVAL);
|
||||
return;
|
||||
}
|
||||
delete[] data;
|
||||
file->bytesSent += readSize;
|
||||
//qDebug() << QString("Core::fileHeartbeat: sent %1/%2 bytes").arg(file->bytesSent).arg(file->fileData.size());
|
||||
|
||||
if (file->bytesSent < file->filesize)
|
||||
{
|
||||
file->sendTimer->start(TOX_FILE_INTERVAL);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV invite from %1 without video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avInvite(friendId, call_index, false);
|
||||
//qDebug("Core: File transfer finished");
|
||||
file->sendTimer->disconnect();
|
||||
delete file->sendTimer;
|
||||
file->sendTimer = nullptr;
|
||||
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0);
|
||||
//emit core->fileTransferFinished(*file);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::onAvStart(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV start";
|
||||
return;
|
||||
}
|
||||
|
||||
int transType = toxav_get_peer_transmission_type(toxav, call_index, 0);
|
||||
if (transType == TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: AV start from %1 with video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, true);
|
||||
emit static_cast<Core*>(core)->avStart(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV start from %1 without video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, false);
|
||||
emit static_cast<Core*>(core)->avStart(friendId, call_index, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::onAvCancel(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV cancel";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV cancel from %1").arg(friendId);
|
||||
|
||||
emit static_cast<Core*>(core)->avCancel(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvReject(void*, int32_t, void*)
|
||||
{
|
||||
qDebug() << "Core: AV reject";
|
||||
}
|
||||
|
||||
void Core::onAvEnd(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV end";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV end from %1").arg(friendId);
|
||||
|
||||
cleanupCall(call_index);
|
||||
|
||||
emit static_cast<Core*>(core)->avEnd(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvRinging(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV ringing";
|
||||
return;
|
||||
}
|
||||
|
||||
if (calls[call_index].videoEnabled)
|
||||
{
|
||||
qDebug() << QString("Core: AV ringing with %1 with video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avRinging(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV ringing with %1 without video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avRinging(friendId, call_index, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::onAvStarting(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV starting";
|
||||
return;
|
||||
}
|
||||
int transType = toxav_get_peer_transmission_type(toxav, call_index, 0);
|
||||
if (transType == TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: AV starting from %1 with video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, true);
|
||||
emit static_cast<Core*>(core)->avStarting(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV starting from %1 without video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, false);
|
||||
emit static_cast<Core*>(core)->avStarting(friendId, call_index, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::onAvEnding(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV ending";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV ending from %1").arg(friendId);
|
||||
|
||||
cleanupCall(call_index);
|
||||
|
||||
emit static_cast<Core*>(core)->avEnding(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvRequestTimeout(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV request timeout";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV request timeout with %1").arg(friendId);
|
||||
|
||||
cleanupCall(call_index);
|
||||
|
||||
emit static_cast<Core*>(core)->avRequestTimeout(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvPeerTimeout(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV peer timeout";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV peer timeout with %1").arg(friendId);
|
||||
|
||||
cleanupCall(call_index);
|
||||
|
||||
emit static_cast<Core*>(core)->avPeerTimeout(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvMediaChange(void*, int32_t, void*)
|
||||
{
|
||||
// HALP, PLS COMPLETE MEH
|
||||
}
|
||||
|
||||
void Core::answerCall(int callId)
|
||||
{
|
||||
int friendId = toxav_get_peer_id(toxav, callId, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV answer peer ID";
|
||||
return;
|
||||
}
|
||||
int transType = toxav_get_peer_transmission_type(toxav, callId, 0);
|
||||
if (transType == TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: answering call %1 with video").arg(callId);
|
||||
toxav_answer(toxav, callId, TypeVideo);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: answering call %1 without video").arg(callId);
|
||||
toxav_answer(toxav, callId, TypeAudio);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::hangupCall(int callId)
|
||||
{
|
||||
qDebug() << QString("Core: hanging up call %1").arg(callId);
|
||||
calls[callId].active = false;
|
||||
toxav_hangup(toxav, callId);
|
||||
}
|
||||
|
||||
void Core::startCall(int friendId, bool video)
|
||||
{
|
||||
int callId;
|
||||
if (video)
|
||||
{
|
||||
qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId);
|
||||
toxav_call(toxav, &callId, friendId, TypeVideo, TOXAV_RINGING_TIME);
|
||||
calls[callId].videoEnabled=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId);
|
||||
toxav_call(toxav, &callId, friendId, TypeAudio, TOXAV_RINGING_TIME);
|
||||
calls[callId].videoEnabled=false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled)
|
||||
{
|
||||
qDebug() << QString("Core: preparing call %1").arg(callId);
|
||||
calls[callId].callId = callId;
|
||||
calls[callId].friendId = friendId;
|
||||
calls[callId].codecSettings = av_DefaultSettings;
|
||||
calls[callId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
|
||||
calls[callId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
|
||||
calls[callId].videoEnabled = videoEnabled;
|
||||
toxav_prepare_transmission(toxav, callId, &calls[callId].codecSettings, videoEnabled);
|
||||
|
||||
// Prepare output
|
||||
QAudioFormat format;
|
||||
format.setSampleRate(calls[callId].codecSettings.audio_sample_rate);
|
||||
format.setChannelCount(calls[callId].codecSettings.audio_channels);
|
||||
format.setSampleSize(16);
|
||||
format.setCodec("audio/pcm");
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
if (!QAudioDeviceInfo::defaultOutputDevice().isFormatSupported(format))
|
||||
{
|
||||
calls[callId].audioOutput = nullptr;
|
||||
qWarning() << "Core: Raw audio format not supported by output backend, cannot play audio.";
|
||||
}
|
||||
else if (calls[callId].audioOutput==nullptr)
|
||||
{
|
||||
calls[callId].audioOutput = new QAudioOutput(format);
|
||||
calls[callId].audioOutput->setBufferSize(1900*30); // Make this bigger to get less underflows, but more latency
|
||||
calls[callId].audioOutput->start(&calls[callId].audioBuffer);
|
||||
int error = calls[callId].audioOutput->error();
|
||||
if (error != QAudio::NoError)
|
||||
{
|
||||
qWarning() << QString("Core: Error %1 when starting audio output").arg(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Start input
|
||||
if (!QAudioDeviceInfo::defaultInputDevice().isFormatSupported(format))
|
||||
{
|
||||
calls[callId].audioInput = nullptr;
|
||||
qWarning() << "Default input format not supported, cannot record audio";
|
||||
}
|
||||
else if (calls[callId].audioInput==nullptr)
|
||||
{
|
||||
calls[callId].audioInput = new QAudioInput(format);
|
||||
calls[callId].audioInputDevice = calls[callId].audioInput->start();
|
||||
}
|
||||
|
||||
// Go
|
||||
calls[callId].active = true;
|
||||
|
||||
if (calls[callId].audioInput != nullptr)
|
||||
{
|
||||
calls[callId].sendAudioTimer->setInterval(2);
|
||||
calls[callId].sendAudioTimer->setSingleShot(true);
|
||||
connect(calls[callId].sendAudioTimer, &QTimer::timeout, [=](){sendCallAudio(callId,toxav);});
|
||||
calls[callId].sendAudioTimer->start();
|
||||
}
|
||||
|
||||
if (calls[callId].videoEnabled)
|
||||
{
|
||||
calls[callId].sendVideoTimer->setInterval(50);
|
||||
calls[callId].sendVideoTimer->setSingleShot(true);
|
||||
calls[callId].sendVideoTimer->start();
|
||||
|
||||
Widget::getInstance()->getCamera()->suscribe();
|
||||
}
|
||||
}
|
||||
|
||||
void Core::cleanupCall(int callId)
|
||||
{
|
||||
qDebug() << QString("Core: cleaning up call %1").arg(callId);
|
||||
calls[callId].active = false;
|
||||
disconnect(calls[callId].sendAudioTimer,0,0,0);
|
||||
calls[callId].sendAudioTimer->stop();
|
||||
calls[callId].sendVideoTimer->stop();
|
||||
if (calls[callId].audioOutput != nullptr)
|
||||
{
|
||||
calls[callId].audioOutput->stop();
|
||||
}
|
||||
if (calls[callId].audioInput != nullptr)
|
||||
{
|
||||
calls[callId].audioInput->stop();
|
||||
}
|
||||
if (calls[callId].videoEnabled)
|
||||
Widget::getInstance()->getCamera()->unsuscribe();
|
||||
calls[callId].audioBuffer.clear();
|
||||
}
|
||||
|
||||
void Core::playCallAudio(ToxAv*, int32_t callId, int16_t *data, int length)
|
||||
{
|
||||
if (!calls[callId].active)
|
||||
return;
|
||||
calls[callId].audioBuffer.write((char*)data, length*2);
|
||||
int state = calls[callId].audioOutput->state();
|
||||
if (state != QAudio::ActiveState)
|
||||
{
|
||||
qDebug() << QString("Core: Audio state is %1").arg(state);
|
||||
calls[callId].audioOutput->start(&calls[callId].audioBuffer);
|
||||
}
|
||||
int error = calls[callId].audioOutput->error();
|
||||
if (error != QAudio::NoError)
|
||||
qWarning() << QString("Core::playCallAudio: Error: %1").arg(error);
|
||||
}
|
||||
|
||||
void Core::sendCallAudio(int callId, ToxAv* toxav)
|
||||
{
|
||||
if (!calls[callId].active)
|
||||
return;
|
||||
int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000;
|
||||
uint8_t buf[framesize*2], dest[framesize*2];
|
||||
int bytesReady = calls[callId].audioInput->bytesReady();
|
||||
if (bytesReady >= framesize*2)
|
||||
{
|
||||
calls[callId].audioInputDevice->read((char*)buf, framesize*2);
|
||||
int result = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize);
|
||||
if (result < 0)
|
||||
{
|
||||
qWarning() << QString("Core: Unable to prepare audio frame, error %1").arg(result);
|
||||
calls[callId].sendAudioTimer->start();
|
||||
return;
|
||||
}
|
||||
result = toxav_send_audio(toxav, callId, dest, result);
|
||||
if (result < 0)
|
||||
{
|
||||
qWarning() << QString("Core: Unable to send audio frame, error %1").arg(result);
|
||||
calls[callId].sendAudioTimer->start();
|
||||
return;
|
||||
}
|
||||
calls[callId].sendAudioTimer->start();
|
||||
}
|
||||
else
|
||||
calls[callId].sendAudioTimer->start();
|
||||
}
|
||||
|
||||
void Core::playCallVideo(ToxAv*, int32_t callId, vpx_image_t* img)
|
||||
{
|
||||
if (!calls[callId].active || !calls[callId].videoEnabled)
|
||||
return;
|
||||
|
||||
if (videoBusyness >= 1)
|
||||
qWarning() << "Core: playCallVideo: Busy, dropping current frame";
|
||||
else
|
||||
emit Widget::getInstance()->getCore()->videoFrameReceived(img);
|
||||
vpx_img_free(img);
|
||||
}
|
||||
|
||||
void Core::sendCallVideo(int callId)
|
||||
{
|
||||
if (!calls[callId].active || !calls[callId].videoEnabled)
|
||||
return;
|
||||
|
||||
vpx_image frame = camera->getLastVPXImage();
|
||||
if (frame.w && frame.h)
|
||||
{
|
||||
int result;
|
||||
if((result = toxav_prepare_video_frame(toxav, callId, videobuf, videobufsize, &frame)) < 0)
|
||||
{
|
||||
qDebug() << QString("Core: toxav_prepare_video_frame: error %1").arg(result);
|
||||
vpx_img_free(&frame);
|
||||
calls[callId].sendVideoTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
if((result = toxav_send_video(toxav, callId, (uint8_t*)videobuf, result)) < 0)
|
||||
qDebug() << QString("Core: toxav_send_video error: %1").arg(result);
|
||||
|
||||
vpx_img_free(&frame);
|
||||
}
|
||||
else
|
||||
qDebug("Core::sendCallVideo: Invalid frame (bad camera ?)");
|
||||
|
||||
calls[callId].sendVideoTimer->start();
|
||||
}
|
||||
|
||||
void Core::groupInviteFriend(int friendId, int groupId)
|
||||
{
|
||||
tox_invite_friend(tox, friendId, groupId);
|
||||
|
@ -1471,12 +1173,18 @@ void Core::createGroup()
|
|||
emit emptyGroupCreated(tox_add_groupchat(tox));
|
||||
}
|
||||
|
||||
void Core::increaseVideoBusyness()
|
||||
QString Core::getFriendAddress(int friendNumber) const
|
||||
{
|
||||
videoBusyness++;
|
||||
}
|
||||
// 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];
|
||||
tox_get_client_id(tox, friendNumber, rawid);
|
||||
QByteArray data((char*)rawid,TOX_CLIENT_ID_SIZE);
|
||||
QString id = data.toHex().toUpper();
|
||||
|
||||
void Core::decreaseVideoBusyness()
|
||||
{
|
||||
videoBusyness--;
|
||||
QList<QString>& friendAddresses = Settings::getInstance().friendAddresses;
|
||||
for (QString addr : friendAddresses)
|
||||
if (addr.toUpper().contains(id))
|
||||
return addr;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
|
49
core.h
|
@ -17,11 +17,17 @@
|
|||
#ifndef CORE_HPP
|
||||
#define CORE_HPP
|
||||
|
||||
#include "audiobuffer.h"
|
||||
|
||||
#include <tox/tox.h>
|
||||
#include <tox/toxav.h>
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
|
@ -30,16 +36,12 @@
|
|||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QByteArray>
|
||||
#include <QFuture>
|
||||
#include <QBuffer>
|
||||
#include <QAudioOutput>
|
||||
#include <QAudioInput>
|
||||
|
||||
#define TOXAV_MAX_CALLS 16
|
||||
#define GROUPCHAT_MAX_SIZE 32
|
||||
#define TOX_SAVE_INTERVAL 30*1000
|
||||
#define TOX_FILE_INTERVAL 20
|
||||
#define TOX_BOOTSTRAP_INTERVAL 10*1000
|
||||
#define TOX_FILE_INTERVAL 0
|
||||
#define TOX_BOOTSTRAP_INTERVAL 5*1000
|
||||
#define TOXAV_RINGING_TIME 15
|
||||
|
||||
// TODO: Put that in the settings
|
||||
|
@ -76,7 +78,7 @@ struct ToxFile
|
|||
ToxFile()=default;
|
||||
ToxFile(int FileNum, int FriendId, QByteArray FileName, QString FilePath, FileDirection Direction)
|
||||
: fileNum(FileNum), friendId(FriendId), fileName{FileName}, filePath{FilePath}, file{new QFile(filePath)},
|
||||
bytesSent{0}, filesize{0}, status{STOPPED}, direction{Direction} {}
|
||||
bytesSent{0}, filesize{0}, status{STOPPED}, direction{Direction}, sendTimer{nullptr} {}
|
||||
~ToxFile(){}
|
||||
void setFilePath(QString path) {filePath=path; file->setFileName(path);}
|
||||
bool open(bool write) {return write?file->open(QIODevice::ReadWrite):file->open(QIODevice::ReadOnly);}
|
||||
|
@ -90,22 +92,20 @@ struct ToxFile
|
|||
long long filesize;
|
||||
FileStatus status;
|
||||
FileDirection direction;
|
||||
QFuture<void> sendFuture;
|
||||
QTimer* sendTimer;
|
||||
};
|
||||
|
||||
struct ToxCall
|
||||
{
|
||||
public:
|
||||
AudioBuffer audioBuffer;
|
||||
QAudioOutput* audioOutput;
|
||||
QAudioInput* audioInput;
|
||||
QIODevice* audioInputDevice;
|
||||
ToxAvCodecSettings codecSettings;
|
||||
ToxAvCSettings codecSettings;
|
||||
QTimer *sendAudioTimer, *sendVideoTimer;
|
||||
int callId;
|
||||
int friendId;
|
||||
bool videoEnabled;
|
||||
bool active;
|
||||
bool muteMic;
|
||||
ALuint alSource;
|
||||
};
|
||||
|
||||
class Core : public QObject
|
||||
|
@ -120,6 +120,7 @@ public:
|
|||
int getGroupNumberPeers(int groupId) const;
|
||||
QString getGroupPeerName(int groupId, int peerId) const;
|
||||
QList<QString> getGroupPeerNames(int groupId) const;
|
||||
QString getFriendAddress(int friendNumber) const;
|
||||
int joinGroupchat(int32_t friendnumber, const uint8_t* friend_group_public_key) const;
|
||||
void quitGroupChat(int groupId) const;
|
||||
void dispatchVideoFrame(vpx_image img) const;
|
||||
|
@ -173,6 +174,8 @@ public slots:
|
|||
void startCall(int friendId, bool video=false);
|
||||
void cancelCall(int callId, int friendId);
|
||||
|
||||
void micMuteToggle(int callId);
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void disconnected();
|
||||
|
@ -239,7 +242,7 @@ signals:
|
|||
void avEnding(int friendId, int callIndex);
|
||||
void avRequestTimeout(int friendId, int callIndex);
|
||||
void avPeerTimeout(int friendId, int callIndex);
|
||||
void avMediaChange(int friendId, int callIndex);
|
||||
void avMediaChange(int friendId, int callIndex, bool videoEnabled);
|
||||
|
||||
void videoFrameReceived(vpx_image* frame);
|
||||
|
||||
|
@ -271,21 +274,22 @@ private:
|
|||
static void onAvEnding(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvRequestTimeout(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvPeerTimeout(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvMediaChange(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvMediaChange(void *toxav, int32_t call_index, void* core);
|
||||
|
||||
static void prepareCall(int friendId, int callId, ToxAv *toxav, bool videoEnabled);
|
||||
static void cleanupCall(int callId);
|
||||
static void playCallAudio(ToxAv *toxav, int32_t callId, int16_t *data, int length); // Callback
|
||||
static void playCallAudio(ToxAv *toxav, int32_t callId, int16_t *data, int samples, void *user_data); // Callback
|
||||
static void sendCallAudio(int callId, ToxAv* toxav);
|
||||
static void playCallVideo(ToxAv* toxav, int32_t callId, vpx_image_t* img);
|
||||
static void playAudioBuffer(int callId, int16_t *data, int samples, unsigned channels, int sampleRate);
|
||||
static void playCallVideo(ToxAv* toxav, int32_t callId, vpx_image_t* img, void *user_data);
|
||||
void sendCallVideo(int callId);
|
||||
|
||||
void checkConnection();
|
||||
void onBootstrapTimer();
|
||||
|
||||
void loadFriends();
|
||||
static void sendAllFileData(Core* core, ToxFile* file);
|
||||
|
||||
static void sendAllFileData(Core* core, ToxFile* file);
|
||||
static void removeFileFromQueue(bool sendQueue, int friendId, int fileId);
|
||||
|
||||
void checkLastOnline(int friendId);
|
||||
|
@ -307,6 +311,11 @@ private:
|
|||
static const int videobufsize;
|
||||
static uint8_t* videobuf;
|
||||
static int videoBusyness; // Used to know when to drop frames
|
||||
|
||||
static ALCdevice* alOutDev, *alInDev;
|
||||
static ALCcontext* alContext;
|
||||
public:
|
||||
static ALuint alMainSource;
|
||||
};
|
||||
|
||||
#endif // CORE_HPP
|
||||
|
|
540
coreav.cpp
Normal file
|
@ -0,0 +1,540 @@
|
|||
/*
|
||||
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
|
||||
|
||||
This file is part of Tox Qt GUI.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
#include "widget/widget.h"
|
||||
|
||||
ToxCall Core::calls[TOXAV_MAX_CALLS];
|
||||
const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4};
|
||||
uint8_t* Core::videobuf;
|
||||
int Core::videoBusyness;
|
||||
|
||||
ALCdevice* Core::alOutDev, *Core::alInDev;
|
||||
ALCcontext* Core::alContext;
|
||||
ALuint Core::alMainSource;
|
||||
|
||||
void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled)
|
||||
{
|
||||
qDebug() << QString("Core: preparing call %1").arg(callId);
|
||||
calls[callId].callId = callId;
|
||||
calls[callId].friendId = friendId;
|
||||
calls[callId].muteMic = false;
|
||||
// the following three lines are also now redundant from startCall, but are
|
||||
// necessary there for outbound and here for inbound
|
||||
calls[callId].codecSettings = av_DefaultSettings;
|
||||
calls[callId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
|
||||
calls[callId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
|
||||
calls[callId].videoEnabled = videoEnabled;
|
||||
toxav_prepare_transmission(toxav, callId, av_jbufdc, av_VADd, videoEnabled);
|
||||
|
||||
// Audio
|
||||
alGenSources(1, &calls[callId].alSource);
|
||||
alcCaptureStart(alInDev);
|
||||
|
||||
// Go
|
||||
calls[callId].active = true;
|
||||
calls[callId].sendAudioTimer->setInterval(5);
|
||||
calls[callId].sendAudioTimer->setSingleShot(true);
|
||||
connect(calls[callId].sendAudioTimer, &QTimer::timeout, [=](){sendCallAudio(callId,toxav);});
|
||||
calls[callId].sendAudioTimer->start();
|
||||
calls[callId].sendVideoTimer->setInterval(50);
|
||||
calls[callId].sendVideoTimer->setSingleShot(true);
|
||||
if (calls[callId].videoEnabled)
|
||||
{
|
||||
calls[callId].sendVideoTimer->start();
|
||||
Widget::getInstance()->getCamera()->suscribe();
|
||||
}
|
||||
}
|
||||
|
||||
void Core::onAvMediaChange(void* toxav, int32_t callId, void* core)
|
||||
{
|
||||
ToxAvCSettings settings;
|
||||
toxav_get_peer_csettings((ToxAv*)toxav, callId, 0, &settings);
|
||||
int friendId = toxav_get_peer_id((ToxAv*)toxav, callId, 0);
|
||||
|
||||
qWarning() << "Core: Received media change from friend "<<friendId;
|
||||
|
||||
if (settings.call_type == TypeAudio)
|
||||
{
|
||||
calls[callId].videoEnabled = false;
|
||||
calls[callId].sendVideoTimer->stop();
|
||||
Widget::getInstance()->getCamera()->unsuscribe();
|
||||
emit ((Core*)core)->avMediaChange(friendId, callId, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Widget::getInstance()->getCamera()->suscribe();
|
||||
calls[callId].videoEnabled = true;
|
||||
calls[callId].sendVideoTimer->start();
|
||||
emit ((Core*)core)->avMediaChange(friendId, callId, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::answerCall(int callId)
|
||||
{
|
||||
int friendId = toxav_get_peer_id(toxav, callId, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV answer peer ID";
|
||||
return;
|
||||
}
|
||||
|
||||
ToxAvCSettings* transSettings = new ToxAvCSettings;
|
||||
int err = toxav_get_peer_csettings(toxav, callId, 0, transSettings);
|
||||
if (err != ErrorNone)
|
||||
{
|
||||
qWarning() << "Core::answerCall: error getting call settings";
|
||||
delete transSettings;
|
||||
return;
|
||||
}
|
||||
|
||||
if (transSettings->call_type == TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: answering call %1 with video").arg(callId);
|
||||
toxav_answer(toxav, callId, transSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: answering call %1 without video").arg(callId);
|
||||
toxav_answer(toxav, callId, transSettings);
|
||||
}
|
||||
|
||||
delete transSettings;
|
||||
}
|
||||
|
||||
void Core::hangupCall(int callId)
|
||||
{
|
||||
qDebug() << QString("Core: hanging up call %1").arg(callId);
|
||||
calls[callId].active = false;
|
||||
toxav_hangup(toxav, callId);
|
||||
}
|
||||
|
||||
void Core::startCall(int friendId, bool video)
|
||||
{
|
||||
int callId;
|
||||
ToxAvCSettings cSettings = av_DefaultSettings;
|
||||
cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
|
||||
cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
|
||||
if (video)
|
||||
{
|
||||
qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId);
|
||||
cSettings.call_type = TypeVideo;
|
||||
toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME);
|
||||
calls[callId].videoEnabled=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId);
|
||||
cSettings.call_type = TypeAudio;
|
||||
toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME);
|
||||
calls[callId].videoEnabled=false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void Core::cleanupCall(int callId)
|
||||
{
|
||||
qDebug() << QString("Core: cleaning up call %1").arg(callId);
|
||||
calls[callId].active = false;
|
||||
disconnect(calls[callId].sendAudioTimer,0,0,0);
|
||||
calls[callId].sendAudioTimer->stop();
|
||||
calls[callId].sendVideoTimer->stop();
|
||||
if (calls[callId].videoEnabled)
|
||||
Widget::getInstance()->getCamera()->unsuscribe();
|
||||
alcCaptureStop(alInDev);
|
||||
}
|
||||
|
||||
void Core::playCallAudio(ToxAv* toxav, int32_t callId, int16_t *data, int samples, void *user_data)
|
||||
{
|
||||
Q_UNUSED(user_data);
|
||||
|
||||
if (!calls[callId].active)
|
||||
return;
|
||||
|
||||
ToxAvCSettings dest;
|
||||
if(toxav_get_peer_csettings(toxav, callId, 0, &dest) == 0)
|
||||
playAudioBuffer(callId, data, samples, dest.audio_channels, dest.audio_sample_rate);
|
||||
}
|
||||
|
||||
void Core::sendCallAudio(int callId, ToxAv* toxav)
|
||||
{
|
||||
if (!calls[callId].active)
|
||||
return;
|
||||
|
||||
if (calls[callId].muteMic)
|
||||
{
|
||||
calls[callId].sendAudioTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000;
|
||||
uint8_t buf[framesize*2], dest[framesize*2];
|
||||
|
||||
bool frame = false;
|
||||
ALint samples;
|
||||
alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
|
||||
if(samples >= framesize)
|
||||
{
|
||||
alcCaptureSamples(alInDev, buf, framesize);
|
||||
frame = 1;
|
||||
}
|
||||
|
||||
if(frame)
|
||||
{
|
||||
int r;
|
||||
if((r = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize)) < 0)
|
||||
{
|
||||
qDebug() << "Core: toxav_prepare_audio_frame error";
|
||||
calls[callId].sendAudioTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
if((r = toxav_send_audio(toxav, callId, dest, r)) < 0)
|
||||
qDebug() << "Core: toxav_send_audio error";
|
||||
}
|
||||
calls[callId].sendAudioTimer->start();
|
||||
}
|
||||
|
||||
void Core::playCallVideo(ToxAv*, int32_t callId, vpx_image_t* img, void *user_data)
|
||||
{
|
||||
Q_UNUSED(user_data);
|
||||
|
||||
if (!calls[callId].active || !calls[callId].videoEnabled)
|
||||
return;
|
||||
|
||||
if (videoBusyness >= 1)
|
||||
qWarning() << "Core: playCallVideo: Busy, dropping current frame";
|
||||
else
|
||||
emit Widget::getInstance()->getCore()->videoFrameReceived(img);
|
||||
vpx_img_free(img);
|
||||
}
|
||||
|
||||
void Core::sendCallVideo(int callId)
|
||||
{
|
||||
if (!calls[callId].active || !calls[callId].videoEnabled)
|
||||
return;
|
||||
|
||||
vpx_image frame = camera->getLastVPXImage();
|
||||
if (frame.w && frame.h)
|
||||
{
|
||||
int result;
|
||||
if((result = toxav_prepare_video_frame(toxav, callId, videobuf, videobufsize, &frame)) < 0)
|
||||
{
|
||||
qDebug() << QString("Core: toxav_prepare_video_frame: error %1").arg(result);
|
||||
vpx_img_free(&frame);
|
||||
calls[callId].sendVideoTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
if((result = toxav_send_video(toxav, callId, (uint8_t*)videobuf, result)) < 0)
|
||||
qDebug() << QString("Core: toxav_send_video error: %1").arg(result);
|
||||
|
||||
vpx_img_free(&frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("Core::sendCallVideo: Invalid frame (bad camera ?)");
|
||||
}
|
||||
|
||||
calls[callId].sendVideoTimer->start();
|
||||
}
|
||||
|
||||
|
||||
void Core::increaseVideoBusyness()
|
||||
{
|
||||
videoBusyness++;
|
||||
}
|
||||
|
||||
void Core::decreaseVideoBusyness()
|
||||
{
|
||||
videoBusyness--;
|
||||
}
|
||||
|
||||
void Core::micMuteToggle(int callId)
|
||||
{
|
||||
calls[callId].muteMic = !calls[callId].muteMic;
|
||||
}
|
||||
|
||||
void Core::onAvCancel(void* _toxav, int32_t callId, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, callId, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV cancel";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV cancel from %1").arg(friendId);
|
||||
|
||||
calls[callId].active = false;
|
||||
|
||||
emit static_cast<Core*>(core)->avCancel(friendId, callId);
|
||||
}
|
||||
|
||||
void Core::onAvReject(void*, int32_t, void*)
|
||||
{
|
||||
qDebug() << "Core: AV reject";
|
||||
}
|
||||
|
||||
void Core::onAvEnd(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV end";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV end from %1").arg(friendId);
|
||||
|
||||
cleanupCall(call_index);
|
||||
|
||||
emit static_cast<Core*>(core)->avEnd(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvRinging(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV ringing";
|
||||
return;
|
||||
}
|
||||
|
||||
if (calls[call_index].videoEnabled)
|
||||
{
|
||||
qDebug() << QString("Core: AV ringing with %1 with video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avRinging(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV ringing with %1 without video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avRinging(friendId, call_index, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::onAvStarting(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV starting";
|
||||
return;
|
||||
}
|
||||
|
||||
ToxAvCSettings* transSettings = new ToxAvCSettings;
|
||||
int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings);
|
||||
if (err != ErrorNone)
|
||||
{
|
||||
qWarning() << "Core::onAvStarting: error getting call type";
|
||||
delete transSettings;
|
||||
return;
|
||||
}
|
||||
|
||||
if (transSettings->call_type == TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: AV starting from %1 with video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, true);
|
||||
emit static_cast<Core*>(core)->avStarting(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV starting from %1 without video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, false);
|
||||
emit static_cast<Core*>(core)->avStarting(friendId, call_index, false);
|
||||
}
|
||||
|
||||
delete transSettings;
|
||||
}
|
||||
|
||||
void Core::onAvEnding(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV ending";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV ending from %1").arg(friendId);
|
||||
|
||||
cleanupCall(call_index);
|
||||
|
||||
emit static_cast<Core*>(core)->avEnding(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvRequestTimeout(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV request timeout";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV request timeout with %1").arg(friendId);
|
||||
|
||||
cleanupCall(call_index);
|
||||
|
||||
emit static_cast<Core*>(core)->avRequestTimeout(friendId, call_index);
|
||||
}
|
||||
|
||||
void Core::onAvPeerTimeout(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV peer timeout";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV peer timeout with %1").arg(friendId);
|
||||
|
||||
cleanupCall(call_index);
|
||||
|
||||
emit static_cast<Core*>(core)->avPeerTimeout(friendId, call_index);
|
||||
}
|
||||
|
||||
|
||||
void Core::onAvInvite(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV invite";
|
||||
return;
|
||||
}
|
||||
|
||||
ToxAvCSettings* transSettings = new ToxAvCSettings;
|
||||
int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings);
|
||||
if (err != ErrorNone)
|
||||
{
|
||||
qWarning() << "Core::onAvInvite: error getting call type";
|
||||
delete transSettings;
|
||||
return;
|
||||
}
|
||||
|
||||
if (transSettings->call_type == TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: AV invite from %1 with video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avInvite(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV invite from %1 without video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avInvite(friendId, call_index, false);
|
||||
}
|
||||
|
||||
delete transSettings;
|
||||
}
|
||||
|
||||
void Core::onAvStart(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV start";
|
||||
return;
|
||||
}
|
||||
|
||||
ToxAvCSettings* transSettings = new ToxAvCSettings;
|
||||
int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings);
|
||||
if (err != ErrorNone)
|
||||
{
|
||||
qWarning() << "Core::onAvStart: error getting call type";
|
||||
delete transSettings;
|
||||
return;
|
||||
}
|
||||
|
||||
if (transSettings->call_type == TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: AV start from %1 with video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, true);
|
||||
emit static_cast<Core*>(core)->avStart(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV start from %1 without video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, false);
|
||||
emit static_cast<Core*>(core)->avStart(friendId, call_index, false);
|
||||
}
|
||||
|
||||
delete transSettings;
|
||||
}
|
||||
|
||||
// This function's logic was shamelessly stolen from uTox
|
||||
void Core::playAudioBuffer(int callId, int16_t *data, int samples, unsigned channels, int sampleRate)
|
||||
{
|
||||
if(!channels || channels > 2)
|
||||
{
|
||||
qWarning() << "Core::playAudioBuffer: trying to play on "<<channels<<" channels! Giving up.";
|
||||
return;
|
||||
}
|
||||
|
||||
ALuint bufid;
|
||||
ALint processed, queued;
|
||||
alGetSourcei(calls[callId].alSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
alGetSourcei(calls[callId].alSource, AL_BUFFERS_QUEUED, &queued);
|
||||
alSourcei(calls[callId].alSource, AL_LOOPING, AL_FALSE);
|
||||
|
||||
if(processed)
|
||||
{
|
||||
ALuint bufids[processed];
|
||||
alSourceUnqueueBuffers(calls[callId].alSource, processed, bufids);
|
||||
alDeleteBuffers(processed - 1, bufids + 1);
|
||||
bufid = bufids[0];
|
||||
}
|
||||
else if(queued < 16)
|
||||
{
|
||||
alGenBuffers(1, &bufid);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Core: Dropped audio frame";
|
||||
return;
|
||||
}
|
||||
|
||||
alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data,
|
||||
samples * 2 * channels, sampleRate);
|
||||
alSourceQueueBuffers(calls[callId].alSource, 1, &bufid);
|
||||
|
||||
ALint state;
|
||||
alGetSourcei(calls[callId].alSource, AL_SOURCE_STATE, &state);
|
||||
if(state != AL_PLAYING)
|
||||
{
|
||||
alSourcePlay(calls[callId].alSource);
|
||||
qDebug() << "Core: Starting audio source of call " << callId;
|
||||
}
|
||||
}
|
5
debian/changelog
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
qtox (0.01pre-alpha-1) UNRELEASED; urgency=medium local package
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- John Smith <barrdetwix@gmail.com> Sat, 30 Aug 2014 23:27:47 +0200
|
1
debian/compat
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
9
|
14
debian/control
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
Source: qtox
|
||||
Maintainer: John Smith <barrdetwix@gmail.com>
|
||||
Section: misc
|
||||
Priority: optional
|
||||
Standards-Version: 3.9.5
|
||||
Build-Depends: debhelper (>= 9), cdbs, qt5-qmake, qt5-default, libopenal-dev, libopencv-dev, libopus-dev
|
||||
|
||||
Package: qtox
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: Tox client
|
||||
qTox is a powerful Tox client that follows the Tox design guidelines.
|
||||
Tox is a decentralized and encrypted replacement for Skype, supporting
|
||||
chats, audio, and video calls.
|
28
debian/copyright
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: qtox
|
||||
Upstream-Contact: John Smith <barrdetwix@gmail.com>
|
||||
Source: https://github.com/tux3/qTox
|
||||
|
||||
Files: *
|
||||
Copyright: 2014 John Smith <barrdetwix@gmail.com>
|
||||
License: GPL-3+
|
||||
This program is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
.
|
||||
This program is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this package; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
Boston, MA 02110-1301 USA
|
||||
.
|
||||
On Debian systems, the full text of the GNU General Public
|
||||
License version 3 can be found in the file
|
||||
`/usr/share/common-licenses/GPL-3'.
|
6
debian/rules
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
include /usr/share/cdbs/1/rules/debhelper.mk
|
||||
include /usr/share/cdbs/1/class/qmake.mk
|
||||
|
||||
QMAKE=qmake STATICPKG=YES
|
1
debian/source/format
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
3.0 (quilt)
|
|
@ -23,7 +23,7 @@ Friend::Friend(int FriendId, QString UserId)
|
|||
{
|
||||
widget = new FriendWidget(friendId, userId);
|
||||
chatForm = new ChatForm(this);
|
||||
hasNewMessages = 0;
|
||||
hasNewEvents = 0;
|
||||
friendStatus = Status::Offline;
|
||||
}
|
||||
|
||||
|
|
2
friend.h
|
@ -36,7 +36,7 @@ public:
|
|||
int friendId;
|
||||
QString userId;
|
||||
ChatForm* chatForm;
|
||||
int hasNewMessages;
|
||||
int hasNewEvents;
|
||||
Status friendStatus;
|
||||
QPixmap avatar;
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "friend.h"
|
||||
#include "friendlist.h"
|
||||
#include <QMenu>
|
||||
#include <QDebug>
|
||||
|
||||
QList<Friend*> FriendList::friendList;
|
||||
|
||||
|
|
1
main.cpp
|
@ -58,7 +58,6 @@ int main(int argc, char *argv[])
|
|||
* Most cameras use YUYV, implement YUYV -> YUV240
|
||||
* Sending large files (~380MB) "restarts" after ~10MB. Goes back to 0%, consumes twice as much ram (reloads the file?)
|
||||
* => Don't load the whole file at once, load small chunks (25MB?) when needed, then free them and load the next
|
||||
* Sort the friend list by status, online first then busy then offline
|
||||
* Don't do anything if a friend is disconnected, don't print to the chat
|
||||
* Changing online/away/busy/offline by clicking the bubble
|
||||
* /me action messages
|
||||
|
|
3262
mainwindow.ui
Normal file
65
qtox.pro
|
@ -20,12 +20,13 @@
|
|||
# See the COPYING file for more details.
|
||||
|
||||
|
||||
QT += core gui network multimedia multimediawidgets
|
||||
QT += core gui network xml
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = qtox
|
||||
TEMPLATE = app
|
||||
FORMS += widget.ui
|
||||
FORMS += \
|
||||
mainwindow.ui
|
||||
CONFIG += c++11
|
||||
|
||||
TRANSLATIONS = translations/de.ts \
|
||||
|
@ -35,16 +36,33 @@ TRANSLATIONS = translations/de.ts \
|
|||
|
||||
RESOURCES += res.qrc
|
||||
|
||||
target.path = /usr/local/bin
|
||||
INSTALLS += target
|
||||
|
||||
INCLUDEPATH += libs/include
|
||||
win32 {
|
||||
LIBS += $$PWD/libs/lib/libtoxav.a $$PWD/libs/lib/libopus.a $$PWD/libs/lib/libvpx.a $$PWD/libs/lib/libtoxcore.a -lws2_32 $$PWD/libs/lib/libsodium.a -lpthread
|
||||
contains(JENKINS,YES) {
|
||||
INCLUDEPATH += ./libs/include/
|
||||
} else {
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lsodium -lvpx
|
||||
INCLUDEPATH += libs/include
|
||||
}
|
||||
|
||||
# Rules for Windows, Mac OSX, and Linux
|
||||
win32 {
|
||||
LIBS += $$PWD/libs/lib/libtoxav.a $$PWD/libs/lib/libopus.a $$PWD/libs/lib/libvpx.a $$PWD/libs/lib/libopenal32.a $$PWD/libs/lib/libtoxcore.a -lws2_32 $$PWD/libs/lib/libsodium.a -lpthread -liphlpapi
|
||||
} macx {
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui
|
||||
} else {
|
||||
# If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
|
||||
contains(STATICPKG, YES) {
|
||||
target.path = /usr/bin
|
||||
INSTALLS += target
|
||||
LIBS += -L$$PWD/libs/lib/ -Wl,-Bstatic -ltoxcore -ltoxav -lsodium -Wl,-Bdynamic -lopus -lvpx -lopenal -lopencv_core -lopencv_highgui
|
||||
} else {
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui
|
||||
}
|
||||
|
||||
contains(JENKINS, YES) {
|
||||
LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a -lopencv_core -lopencv_highgui -lopenal
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#### Static linux build
|
||||
#LIBS += -Wl,-Bstatic -ltoxcore -ltoxav -lsodium -lvpx -lopus \
|
||||
# -lgstbase-0.10 -lgstreamer-0.10 -lgmodule-2.0 -lgstaudio-0.10 -lxml2 \
|
||||
|
@ -61,10 +79,6 @@ HEADERS += widget/form/addfriendform.h \
|
|||
widget/form/settingsform.h \
|
||||
widget/form/filesform.h \
|
||||
widget/tool/chattextedit.h \
|
||||
widget/tool/copyableelidelabel.h \
|
||||
widget/tool/editablelabelwidget.h \
|
||||
widget/tool/elidelabel.h \
|
||||
widget/tool/esclineedit.h \
|
||||
widget/tool/friendrequestdialog.h \
|
||||
widget/filetransfertwidget.h \
|
||||
widget/friendwidget.h \
|
||||
|
@ -78,12 +92,16 @@ HEADERS += widget/form/addfriendform.h \
|
|||
friendlist.h \
|
||||
cdata.h \
|
||||
cstring.h \
|
||||
audiobuffer.h \
|
||||
widget/selfcamview.h \
|
||||
widget/videosurface.h \
|
||||
widget/camera.h \
|
||||
widget/netcamview.h \
|
||||
widget/tool/clickablelabel.h
|
||||
smileypack.h \
|
||||
widget/emoticonswidget.h \
|
||||
style.h \
|
||||
widget/adjustingscrollarea.h \
|
||||
widget/croppinglabel.h \
|
||||
widget/friendlistwidget.h \
|
||||
widget/genericchatroomwidget.h
|
||||
|
||||
SOURCES += \
|
||||
widget/form/addfriendform.cpp \
|
||||
|
@ -92,10 +110,6 @@ SOURCES += \
|
|||
widget/form/settingsform.cpp \
|
||||
widget/form/filesform.cpp \
|
||||
widget/tool/chattextedit.cpp \
|
||||
widget/tool/copyableelidelabel.cpp \
|
||||
widget/tool/editablelabelwidget.cpp \
|
||||
widget/tool/elidelabel.cpp \
|
||||
widget/tool/esclineedit.cpp \
|
||||
widget/tool/friendrequestdialog.cpp \
|
||||
widget/filetransfertwidget.cpp \
|
||||
widget/friendwidget.cpp \
|
||||
|
@ -110,9 +124,14 @@ SOURCES += \
|
|||
settings.cpp \
|
||||
cdata.cpp \
|
||||
cstring.cpp \
|
||||
audiobuffer.cpp \
|
||||
widget/selfcamview.cpp \
|
||||
widget/videosurface.cpp \
|
||||
widget/camera.cpp \
|
||||
widget/netcamview.cpp \
|
||||
widget/tool/clickablelabel.cpp
|
||||
smileypack.cpp \
|
||||
widget/emoticonswidget.cpp \
|
||||
style.cpp \
|
||||
widget/adjustingscrollarea.cpp \
|
||||
widget/croppinglabel.cpp \
|
||||
widget/friendlistwidget.cpp \
|
||||
coreav.cpp \
|
||||
widget/genericchatroomwidget.cpp
|
||||
|
|
13
res.qrc
|
@ -111,5 +111,18 @@
|
|||
<file>ui/statusButton/menu_indicator.png</file>
|
||||
<file>translations/de.qm</file>
|
||||
<file>translations/it.qm</file>
|
||||
<file>ui/emoticonWidget/dot_page.png</file>
|
||||
<file>ui/emoticonWidget/dot_page_current.png</file>
|
||||
<file>ui/emoticonWidget/emoticonWidget.css</file>
|
||||
<file>ui/emoticonWidget/dot_page_hover.png</file>
|
||||
<file>ui/volButton/volButton.png</file>
|
||||
<file>ui/volButton/volButtonHover.png</file>
|
||||
<file>ui/volButton/volButtonPressed.png</file>
|
||||
<file>ui/micButton/micButton.png</file>
|
||||
<file>ui/micButton/micButtonDisabled.png</file>
|
||||
<file>ui/micButton/micButtonHover.png</file>
|
||||
<file>ui/micButton/micButtonPressed.png</file>
|
||||
<file>ui/micButton/micButton.css</file>
|
||||
<file>ui/volButton/volButton.css</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[DHT%20Server]
|
||||
dhtServerList\size=16
|
||||
dhtServerList\size=9
|
||||
dhtServerList\1\name=stqism
|
||||
dhtServerList\1\userId=951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
|
||||
dhtServerList\1\address=192.254.75.98
|
||||
|
@ -12,55 +12,27 @@ dhtServerList\3\name=stal
|
|||
dhtServerList\3\userId=A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||
dhtServerList\3\address=23.226.230.47
|
||||
dhtServerList\3\port=33445
|
||||
dhtServerList\4\name=ChauffeR
|
||||
dhtServerList\4\userId=4FD54CFD426A338399767E56FD0F44F5E35FA8C38C8E87C8DC3FEAC0160F8E1
|
||||
dhtServerList\4\address=37.187.20.216
|
||||
dhtServerList\4\name=aitjcize
|
||||
dhtServerList\4\userId=7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
||||
dhtServerList\4\address=54.199.139.199
|
||||
dhtServerList\4\port=33445
|
||||
dhtServerList\5\name=aitjcize
|
||||
dhtServerList\5\userId=7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
||||
dhtServerList\5\address=54.199.139.199
|
||||
dhtServerList\5\name=astonex
|
||||
dhtServerList\5\userId=B98A2CEAA6C6A2FADC2C3632D284318B60FE5375CCB41EFA081AB67F500C1B0B
|
||||
dhtServerList\5\address=37.59.102.176
|
||||
dhtServerList\5\port=33445
|
||||
dhtServerList\6\name=astonex
|
||||
dhtServerList\6\userId=7F31BFC93B8E4016A902144D0B110C3EA97CB7D43F1C4D21BCAE998A7C838821
|
||||
dhtServerList\6\address=109.169.46.133
|
||||
dhtServerList\6\name=nurupo
|
||||
dhtServerList\6\userId=F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
dhtServerList\6\address=192.210.149.121
|
||||
dhtServerList\6\port=33445
|
||||
dhtServerList\7\name=nurupo
|
||||
dhtServerList\7\userId=F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
dhtServerList\7\address=192.210.149.121
|
||||
dhtServerList\7\name=mousey
|
||||
dhtServerList\7\userId=5EB67C51D3FF5A9D528D242B669036ED2A30F8A60E674C45E7D43010CB2E1331
|
||||
dhtServerList\7\address=37.187.46.132
|
||||
dhtServerList\7\port=33445
|
||||
dhtServerList\8\name=mousey
|
||||
dhtServerList\8\userId=5EB67C51D3FF5A9D528D242B669036ED2A30F8A60E674C45E7D43010CB2E1331
|
||||
dhtServerList\8\address=37.187.46.132
|
||||
dhtServerList\8\name=Proplex
|
||||
dhtServerList\8\userId=7BE3951B97CA4B9ECDDA768E8C52BA19E9E2690AB584787BF4C90E04DBB75111
|
||||
dhtServerList\8\address=107.161.17.51
|
||||
dhtServerList\8\port=33445
|
||||
dhtServerList\9\name=zlacki NL
|
||||
dhtServerList\9\userId=CC2B02636A2ADBC2871D6EC57C5E9589D4FD5E6F98A14743A4B949914CF26D39
|
||||
dhtServerList\9\address=5.39.218.35
|
||||
dhtServerList\9\name=SylvieLorxu
|
||||
dhtServerList\9\userId=4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
|
||||
dhtServerList\9\address=178.21.112.187
|
||||
dhtServerList\9\port=33445
|
||||
dhtServerList\10\name=zlacki RU #2
|
||||
dhtServerList\10\userId=AE27E1E72ADA3DC423C60EEBACA241456174048BE76A283B41AD32D953182D49
|
||||
dhtServerList\10\address=193.107.16.73
|
||||
dhtServerList\10\port=33445
|
||||
dhtServerList\11\name=platos
|
||||
dhtServerList\11\userId=B24E2FB924AE66D023FE1E42A2EE3B432010206F751A2FFD3E297383ACF1572E
|
||||
dhtServerList\11\address=66.175.223.88
|
||||
dhtServerList\11\port=33445
|
||||
dhtServerList\12\name=JmanGuy
|
||||
dhtServerList\12\userId=20C797E098701A848B07D0384222416B0EFB60D08CECB925B860CAEAAB572067
|
||||
dhtServerList\12\address=66.74.15.98
|
||||
dhtServerList\12\port=33445
|
||||
dhtServerList\13\name=anonymous
|
||||
dhtServerList\13\userId=5CD7EB176C19A2FD840406CD56177BB8E75587BB366F7BB3004B19E3EDC04143
|
||||
dhtServerList\13\address=192.184.81.118
|
||||
dhtServerList\13\port=33445
|
||||
dhtServerList\14\name=benwaffle
|
||||
dhtServerList\14\userId=8E6667FF967EA30B3DC3DB57A4B533152476E7AAE090158B9C2D9DF58ECC7B78
|
||||
dhtServerList\14\address=192.3.30.132
|
||||
dhtServerList\14\port=33445
|
||||
dhtServerList\15\name=zlacki RU #1
|
||||
dhtServerList\15\userId=D59F99384592DE4C8AB9D534D5197DB90F4755CC9E975ED0C565E18468A1445B
|
||||
dhtServerList\15\address=31.192.105.19
|
||||
dhtServerList\15\port=33445
|
||||
dhtServerList\16\name=zlacki US
|
||||
dhtServerList\16\userId=9430A83211A7AD1C294711D069D587028CA0B4782FA43CB9B30008247A43C944
|
||||
dhtServerList\16\address=69.42.220.58
|
||||
dhtServerList\16\port=33445
|
||||
|
|
106
settings.cpp
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
|
||||
#include "settings.h"
|
||||
#include "smileypack.h"
|
||||
#include "widget/widget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
|
@ -22,6 +24,7 @@
|
|||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
|
||||
const QString Settings::FILENAME = "settings.ini";
|
||||
bool Settings::makeToxPortable{false};
|
||||
|
@ -53,7 +56,7 @@ void Settings::load()
|
|||
if (portableSettings.exists())
|
||||
makeToxPortable=true;
|
||||
|
||||
QString filePath = getSettingsDirPath() + '/' + FILENAME;
|
||||
QString filePath = QDir(getSettingsDirPath()).filePath(FILENAME);
|
||||
|
||||
//if no settings file exist -- use the default one
|
||||
QFile file(filePath);
|
||||
|
@ -77,6 +80,16 @@ void Settings::load()
|
|||
s.endArray();
|
||||
s.endGroup();
|
||||
|
||||
friendAddresses.clear();
|
||||
s.beginGroup("Friends");
|
||||
int size = s.beginReadArray("fullAddresses");
|
||||
for (int i = 0; i < size; i ++) {
|
||||
s.setArrayIndex(i);
|
||||
friendAddresses.append(s.value("addr").toString());
|
||||
}
|
||||
s.endArray();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("General");
|
||||
enableIPv6 = s.value("enableIPv6", true).toBool();
|
||||
useTranslations = s.value("useTranslations", true).toBool();
|
||||
|
@ -93,7 +106,7 @@ void Settings::load()
|
|||
|
||||
s.beginGroup("GUI");
|
||||
enableSmoothAnimation = s.value("smoothAnimation", true).toBool();
|
||||
smileyPack = s.value("smileyPack").toByteArray();
|
||||
smileyPack = s.value("smileyPack", QString()).toString();
|
||||
customEmojiFont = s.value("customEmojiFont", true).toBool();
|
||||
emojiFontFamily = s.value("emojiFontFamily", "DejaVu Sans").toString();
|
||||
emojiFontPointSize = s.value("emojiFontPointSize", QApplication::font().pointSize()).toInt();
|
||||
|
@ -101,18 +114,30 @@ void Settings::load()
|
|||
secondColumnHandlePosFromRight = s.value("secondColumnHandlePosFromRight", 50).toInt();
|
||||
timestampFormat = s.value("timestampFormat", "hh:mm").toString();
|
||||
minimizeOnClose = s.value("minimizeOnClose", false).toBool();
|
||||
useNativeStyle = s.value("nativeStyle", false).toBool();
|
||||
useNativeDecoration = s.value("nativeDecoration", true).toBool();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("State");
|
||||
windowGeometry = s.value("windowGeometry", QByteArray()).toByteArray();
|
||||
windowState = s.value("windowState", QByteArray()).toByteArray();
|
||||
splitterState = s.value("splitterState", QByteArray()).toByteArray();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Privacy");
|
||||
typingNotification = s.value("typingNotification", false).toBool();
|
||||
s.endGroup();
|
||||
|
||||
// try to set a smiley pack if none is selected
|
||||
if (!SmileyPack::isValid(smileyPack) && !SmileyPack::listSmileyPacks().isEmpty())
|
||||
smileyPack = SmileyPack::listSmileyPacks()[0].second;
|
||||
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
void Settings::save()
|
||||
{
|
||||
QString filePath = getSettingsDirPath() + '/' + FILENAME;
|
||||
QString filePath = QDir(getSettingsDirPath()).filePath(FILENAME);
|
||||
save(filePath);
|
||||
}
|
||||
|
||||
|
@ -134,6 +159,15 @@ void Settings::save(QString path)
|
|||
s.endArray();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Friends");
|
||||
s.beginWriteArray("fullAddresses", friendAddresses.size());
|
||||
for (int i = 0; i < friendAddresses.size(); i ++) {
|
||||
s.setArrayIndex(i);
|
||||
s.setValue("addr", friendAddresses[i]);
|
||||
}
|
||||
s.endArray();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("General");
|
||||
s.setValue("enableIPv6", enableIPv6);
|
||||
s.setValue("useTranslations",useTranslations);
|
||||
|
@ -158,6 +192,14 @@ void Settings::save(QString path)
|
|||
s.setValue("secondColumnHandlePosFromRight", secondColumnHandlePosFromRight);
|
||||
s.setValue("timestampFormat", timestampFormat);
|
||||
s.setValue("minimizeOnClose", minimizeOnClose);
|
||||
s.setValue("nativeStyle", useNativeStyle);
|
||||
s.setValue("nativeDecoration", useNativeDecoration);
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("State");
|
||||
s.setValue("windowGeometry", windowGeometry);
|
||||
s.setValue("windowState", windowState);
|
||||
s.setValue("splitterState", splitterState);
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Privacy");
|
||||
|
@ -174,7 +216,7 @@ QString Settings::getSettingsDirPath()
|
|||
#ifdef Q_OS_WIN
|
||||
return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
|
||||
#else
|
||||
return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + '/' + "tox" + '/';
|
||||
return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QDir::separator() + "tox");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -208,6 +250,8 @@ void Settings::setMakeToxPortable(bool newValue)
|
|||
{
|
||||
makeToxPortable = newValue;
|
||||
save(FILENAME); // Commit to the portable file that we don't want to use it
|
||||
if (!newValue) // Update the new file right now if not already done
|
||||
save();
|
||||
}
|
||||
|
||||
QString Settings::getCurrentProfile() const
|
||||
|
@ -270,12 +314,12 @@ void Settings::setAnimationEnabled(bool newValue)
|
|||
enableSmoothAnimation = newValue;
|
||||
}
|
||||
|
||||
QByteArray Settings::getSmileyPack() const
|
||||
QString Settings::getSmileyPack() const
|
||||
{
|
||||
return smileyPack;
|
||||
}
|
||||
|
||||
void Settings::setSmileyPack(const QByteArray &value)
|
||||
void Settings::setSmileyPack(const QString &value)
|
||||
{
|
||||
smileyPack = value;
|
||||
emit smileyPackChanged();
|
||||
|
@ -345,6 +389,56 @@ void Settings::setEmojiFontFamily(const QString &value)
|
|||
emit emojiFontChanged();
|
||||
}
|
||||
|
||||
bool Settings::getUseNativeStyle() const
|
||||
{
|
||||
return useNativeStyle;
|
||||
}
|
||||
|
||||
void Settings::setUseNativeStyle(bool value)
|
||||
{
|
||||
useNativeStyle = value;
|
||||
}
|
||||
|
||||
bool Settings::getUseNativeDecoration() const
|
||||
{
|
||||
return useNativeDecoration;
|
||||
}
|
||||
|
||||
void Settings::setUseNativeDecoration(bool value)
|
||||
{
|
||||
useNativeDecoration = value;
|
||||
}
|
||||
|
||||
QByteArray Settings::getWindowGeometry() const
|
||||
{
|
||||
return windowGeometry;
|
||||
}
|
||||
|
||||
void Settings::setWindowGeometry(const QByteArray &value)
|
||||
{
|
||||
windowGeometry = value;
|
||||
}
|
||||
|
||||
QByteArray Settings::getWindowState() const
|
||||
{
|
||||
return windowState;
|
||||
}
|
||||
|
||||
void Settings::setWindowState(const QByteArray &value)
|
||||
{
|
||||
windowState = value;
|
||||
}
|
||||
|
||||
QByteArray Settings::getSplitterState() const
|
||||
{
|
||||
return splitterState;
|
||||
}
|
||||
|
||||
void Settings::setSplitterState(const QByteArray &value)
|
||||
{
|
||||
splitterState = value;
|
||||
}
|
||||
|
||||
bool Settings::isMinimizeOnCloseEnabled() const
|
||||
{
|
||||
return minimizeOnClose;
|
||||
|
|
36
settings.h
|
@ -86,8 +86,8 @@ public:
|
|||
bool isAnimationEnabled() const;
|
||||
void setAnimationEnabled(bool newValue);
|
||||
|
||||
QByteArray getSmileyPack() const;
|
||||
void setSmileyPack(const QByteArray &value);
|
||||
QString getSmileyPack() const;
|
||||
void setSmileyPack(const QString &value);
|
||||
|
||||
bool isCurstomEmojiFont() const;
|
||||
void setCurstomEmojiFont(bool value);
|
||||
|
@ -115,16 +115,31 @@ public:
|
|||
bool isTypingNotificationEnabled() const;
|
||||
void setTypingNotification(bool enabled);
|
||||
|
||||
private:
|
||||
Settings();
|
||||
Settings(Settings &settings) = delete;
|
||||
Settings& operator=(const Settings&) = delete;
|
||||
bool getUseNativeStyle() const;
|
||||
void setUseNativeStyle(bool value);
|
||||
|
||||
bool getUseNativeDecoration() const;
|
||||
void setUseNativeDecoration(bool value);
|
||||
|
||||
QByteArray getWindowGeometry() const;
|
||||
void setWindowGeometry(const QByteArray &value);
|
||||
|
||||
QByteArray getWindowState() const;
|
||||
void setWindowState(const QByteArray &value);
|
||||
|
||||
QByteArray getSplitterState() const;
|
||||
void setSplitterState(const QByteArray &value);
|
||||
|
||||
public:
|
||||
QList<QString> friendAddresses;
|
||||
void save();
|
||||
void save(QString path);
|
||||
void load();
|
||||
|
||||
|
||||
private:
|
||||
Settings();
|
||||
Settings(Settings &settings) = delete;
|
||||
Settings& operator=(const Settings&) = delete;
|
||||
|
||||
static const QString FILENAME;
|
||||
|
||||
|
@ -146,11 +161,16 @@ private:
|
|||
|
||||
// GUI
|
||||
bool enableSmoothAnimation;
|
||||
QByteArray smileyPack;
|
||||
QString smileyPack;
|
||||
bool customEmojiFont;
|
||||
QString emojiFontFamily;
|
||||
int emojiFontPointSize;
|
||||
bool minimizeOnClose;
|
||||
bool useNativeStyle;
|
||||
bool useNativeDecoration;
|
||||
QByteArray windowGeometry;
|
||||
QByteArray windowState;
|
||||
QByteArray splitterState;
|
||||
|
||||
// ChatView
|
||||
int firstColumnHandlePos;
|
||||
|
|
199
smileypack.cpp
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "smileypack.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QFile>
|
||||
#include <QtXml>
|
||||
#include <QDebug>
|
||||
|
||||
SmileyPack::SmileyPack()
|
||||
{
|
||||
load(Settings::getInstance().getSmileyPack());
|
||||
connect(&Settings::getInstance(), &Settings::smileyPackChanged, this, &SmileyPack::onSmileyPackChanged);
|
||||
}
|
||||
|
||||
SmileyPack& SmileyPack::getInstance()
|
||||
{
|
||||
static SmileyPack smileyPack;
|
||||
return smileyPack;
|
||||
}
|
||||
|
||||
QList<QPair<QString, QString> > SmileyPack::listSmileyPacks(const QString &path)
|
||||
{
|
||||
QList<QPair<QString, QString> > smileyPacks;
|
||||
|
||||
QDir dir(path);
|
||||
foreach (const QString& subdirectory, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||
{
|
||||
dir.cd(subdirectory);
|
||||
|
||||
QFileInfoList entries = dir.entryInfoList(QStringList() << "emoticons.xml", QDir::Files);
|
||||
if (entries.size() > 0) // does it contain a file called emoticons.xml?
|
||||
{
|
||||
QString packageName = dir.dirName();
|
||||
QString relPath = QDir(QCoreApplication::applicationDirPath()).relativeFilePath(entries[0].absoluteFilePath());
|
||||
smileyPacks << QPair<QString, QString>(packageName, relPath);
|
||||
}
|
||||
|
||||
dir.cdUp();
|
||||
}
|
||||
|
||||
return smileyPacks;
|
||||
}
|
||||
|
||||
bool SmileyPack::isValid(const QString &filename)
|
||||
{
|
||||
return QFile(filename).exists();
|
||||
}
|
||||
|
||||
bool SmileyPack::load(const QString& filename)
|
||||
{
|
||||
// discard old data
|
||||
filenameTable.clear();
|
||||
imgCache.clear();
|
||||
emoticons.clear();
|
||||
path.clear();
|
||||
|
||||
// open emoticons.xml
|
||||
QFile xmlFile(filename);
|
||||
if(!xmlFile.open(QIODevice::ReadOnly))
|
||||
return false; // cannot open file
|
||||
|
||||
/* parse the cfg file
|
||||
* sample:
|
||||
* <?xml version='1.0'?>
|
||||
* <messaging-emoticon-map>
|
||||
* <emoticon file="smile.png" >
|
||||
* <string>:)</string>
|
||||
* <string>:-)</string>
|
||||
* </emoticon>
|
||||
* <emoticon file="sad.png" >
|
||||
* <string>:(</string>
|
||||
* <string>:-(</string>
|
||||
* </emoticon>
|
||||
* </messaging-emoticon-map>
|
||||
*/
|
||||
|
||||
path = QFileInfo(filename).absolutePath();
|
||||
|
||||
QDomDocument doc;
|
||||
doc.setContent(xmlFile.readAll());
|
||||
|
||||
QDomNodeList emoticonElements = doc.elementsByTagName("emoticon");
|
||||
for (int i = 0; i < emoticonElements.size(); ++i)
|
||||
{
|
||||
QString file = emoticonElements.at(i).attributes().namedItem("file").nodeValue();
|
||||
QDomElement stringElement = emoticonElements.at(i).firstChildElement("string");
|
||||
|
||||
QStringList emoticonSet; // { ":)", ":-)" } etc.
|
||||
|
||||
while (!stringElement.isNull())
|
||||
{
|
||||
QString emoticon = stringElement.text();
|
||||
filenameTable.insert(emoticon, file);
|
||||
emoticonSet.push_back(emoticon);
|
||||
cacheSmiley(file); // preload all smileys
|
||||
|
||||
stringElement = stringElement.nextSibling().toElement();
|
||||
}
|
||||
emoticons.push_back(emoticonSet);
|
||||
}
|
||||
|
||||
// success!
|
||||
return true;
|
||||
}
|
||||
|
||||
QString SmileyPack::smileyfied(QString msg)
|
||||
{
|
||||
QRegExp exp("\\S+"); // matches words
|
||||
|
||||
int index = msg.indexOf(exp);
|
||||
|
||||
// if a word is key of a smiley, replace it by its corresponding image in Rich Text
|
||||
while (index >= 0)
|
||||
{
|
||||
QString key = exp.cap();
|
||||
if (filenameTable.contains(key))
|
||||
{
|
||||
QString imgRichText = getAsRichText(key);
|
||||
|
||||
msg.replace(index, key.length(), imgRichText);
|
||||
index += imgRichText.length() - key.length();
|
||||
}
|
||||
index = msg.indexOf(exp, index + key.length());
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
QList<QStringList> SmileyPack::getEmoticons() const
|
||||
{
|
||||
return emoticons;
|
||||
}
|
||||
|
||||
QString SmileyPack::getAsRichText(const QString &key)
|
||||
{
|
||||
return "<img src=\"data:image/png;base64," % QString(getCachedSmiley(key).toBase64()) % "\">";
|
||||
}
|
||||
|
||||
QIcon SmileyPack::getAsIcon(const QString &key)
|
||||
{
|
||||
QPixmap pm;
|
||||
pm.loadFromData(getCachedSmiley(key), "PNG");
|
||||
|
||||
return QIcon(pm);
|
||||
}
|
||||
|
||||
void SmileyPack::cacheSmiley(const QString &name)
|
||||
{
|
||||
QSize size(16, 16); // TODO: adapt to text size
|
||||
QString filename = QDir(path).filePath(name);
|
||||
QImage img(filename);
|
||||
|
||||
if (!img.isNull())
|
||||
{
|
||||
QImage scaledImg = img.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
QByteArray scaledImgData;
|
||||
QBuffer buffer(&scaledImgData);
|
||||
scaledImg.save(&buffer, "PNG");
|
||||
|
||||
imgCache.insert(name, scaledImgData);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray SmileyPack::getCachedSmiley(const QString &key)
|
||||
{
|
||||
// valid key?
|
||||
if (!filenameTable.contains(key))
|
||||
return QByteArray();
|
||||
|
||||
// cache it if needed
|
||||
QString file = filenameTable.value(key);
|
||||
if (!imgCache.contains(file)) {
|
||||
cacheSmiley(file);
|
||||
}
|
||||
|
||||
return imgCache.value(file);
|
||||
}
|
||||
|
||||
void SmileyPack::onSmileyPackChanged()
|
||||
{
|
||||
load(Settings::getInstance().getSmileyPack());
|
||||
}
|
59
smileypack.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef SMILEYPACK_H
|
||||
#define SMILEYPACK_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#define SMILEYPACK_DEFAULT_PATH "./smileys"
|
||||
|
||||
//maps emoticons to smileys
|
||||
class SmileyPack : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static SmileyPack& getInstance();
|
||||
static QList<QPair<QString, QString>> listSmileyPacks(const QString& path = SMILEYPACK_DEFAULT_PATH);
|
||||
static bool isValid(const QString& filename);
|
||||
|
||||
bool load(const QString &filename);
|
||||
QString smileyfied(QString msg);
|
||||
QList<QStringList> getEmoticons() const;
|
||||
QString getAsRichText(const QString& key);
|
||||
QIcon getAsIcon(const QString& key);
|
||||
|
||||
private slots:
|
||||
void onSmileyPackChanged();
|
||||
|
||||
private:
|
||||
SmileyPack();
|
||||
SmileyPack(SmileyPack&) = delete;
|
||||
SmileyPack& operator=(const SmileyPack&) = delete;
|
||||
|
||||
void cacheSmiley(const QString& name);
|
||||
QByteArray getCachedSmiley(const QString& key);
|
||||
|
||||
QHash<QString, QString> filenameTable; // matches an emoticon to its corresponding smiley ie. ":)" -> "happy.png"
|
||||
QHash<QString, QByteArray> imgCache; // (scaled) representation of a smiley ie. "happy.png" -> data
|
||||
QList<QStringList> emoticons; // {{ ":)", ":-)" }, {":(", ...}, ... }
|
||||
QString path; // directory containing the cfg and image files
|
||||
};
|
||||
|
||||
#endif // SMILEYPACK_H
|
BIN
smileys/default/angry.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
smileys/default/cool.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
smileys/default/crying.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
85
smileys/default/emoticons.xml
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?xml version='1.0'?>
|
||||
<messaging-emoticon-map>
|
||||
<emoticon file="happy.png">
|
||||
<string>😊</string>
|
||||
<string>:)</string>
|
||||
<string>:-)</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="cool.png">
|
||||
<string>😎</string>
|
||||
<string>8-)</string>
|
||||
<string>8)</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="stunned.png">
|
||||
<string>😲</string>
|
||||
<string>:O</string>
|
||||
<string>:-O</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="tongue.png">
|
||||
<string>😋</string>
|
||||
<string>:p</string>
|
||||
<string>:P</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="uncertain.png">
|
||||
<string>😕</string>
|
||||
<string>:/</string>
|
||||
<string>:-/</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="wink.png">
|
||||
<string>😉</string>
|
||||
<string>;)</string>
|
||||
<string>;-)</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="sad.png">
|
||||
<string>😖</string>
|
||||
<string>:(</string>
|
||||
<string>:-(</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="crying.png">
|
||||
<string>😢</string>
|
||||
<string>;(</string>
|
||||
<string>;-(</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="smile.png">
|
||||
<string>😃</string>
|
||||
<string>:D</string>
|
||||
<string>:-D</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="plain.png">
|
||||
<string>😐</string>
|
||||
<string>:|</string>
|
||||
<string>:-|</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="laugh.png">
|
||||
<string>😄</string>
|
||||
<string>;D</string>
|
||||
<string>;-D</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="angry.png">
|
||||
<string>😠</string>
|
||||
<string>:@</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="scared.png">
|
||||
<string>😨</string>
|
||||
<string>D:</string>
|
||||
</emoticon>
|
||||
|
||||
<emoticon file="laugh_closed_eyes">
|
||||
<string>😆</string>
|
||||
<string>xD</string>
|
||||
<string>XD</string>
|
||||
</emoticon>
|
||||
|
||||
</messaging-emoticon-map>
|
BIN
smileys/default/happy.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
smileys/default/laugh.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
smileys/default/laugh_closed_eyes.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
smileys/default/plain.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
889
smileys/default/raw.svg
Normal file
|
@ -0,0 +1,889 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="256"
|
||||
height="256"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="raw.svg"
|
||||
inkscape:export-filename="./raw.png"
|
||||
inkscape:export-xdpi="360"
|
||||
inkscape:export-ydpi="360">
|
||||
<title
|
||||
id="title4628">qTox</title>
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient3131">
|
||||
<stop
|
||||
style="stop-color:#ffcc00;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3133" />
|
||||
<stop
|
||||
style="stop-color:#0063f6;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3135" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3181">
|
||||
<stop
|
||||
style="stop-color:#ff2000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3183" />
|
||||
<stop
|
||||
style="stop-color:#ffcc00;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3185" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3881">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3883" />
|
||||
<stop
|
||||
style="stop-color:#505050;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3885" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3777">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-8">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-3" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3881"
|
||||
id="linearGradient3887"
|
||||
x1="11.216473"
|
||||
y1="234.94836"
|
||||
x2="14.142681"
|
||||
y2="245.0323"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3881-1">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3883-2" />
|
||||
<stop
|
||||
style="stop-color:#505050;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3885-8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-5.3934044,0.00862702)"
|
||||
y2="245.0323"
|
||||
x2="14.142681"
|
||||
y1="234.94836"
|
||||
x1="11.216473"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3904"
|
||||
xlink:href="#linearGradient3881-1"
|
||||
inkscape:collect="always" />
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-5">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-0" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-5-2">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-0-5" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-5-2-6">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-0-5-3" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-56">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-5" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-4">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-4" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-4-5">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-4-7" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-42">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-6" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-42-4">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-6-6" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-42-5">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-6-4" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-42-1">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-6-0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3181"
|
||||
id="linearGradient3187"
|
||||
x1="14.965159"
|
||||
y1="231.7083"
|
||||
x2="14.965159"
|
||||
y2="244.56258"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-42-0">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-6-8" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3131"
|
||||
id="linearGradient3137"
|
||||
x1="18.417803"
|
||||
y1="245.66988"
|
||||
x2="18.417803"
|
||||
y2="219.33583"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter3777-42-51">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.20338983"
|
||||
id="feGaussianBlur3779-6-09" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8"
|
||||
inkscape:cx="126.48175"
|
||||
inkscape:cy="74.652167"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
gridtolerance="10"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
inkscape:window-x="-2"
|
||||
inkscape:window-y="-3"
|
||||
inkscape:window-maximized="1"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2985"
|
||||
empspacing="1"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
spacingx="32px"
|
||||
spacingy="32px" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>qTox</dc:title>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-nc/3.0/" />
|
||||
<dc:description>Created for the qTox project. Inspired by the "Never mind!" smiley pack.</dc:description>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-nc/3.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:prohibits
|
||||
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-796.36218)">
|
||||
<g
|
||||
id="happy"
|
||||
inkscape:label="">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,1.8194901,823.65453)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,0.34633991,917.17801)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,9.8832106,917.17801)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="czc"
|
||||
transform="translate(0,796.36218)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3809"
|
||||
d="m 6.5178572,243.05357 c 0,0 2.8196608,6.69643 9.4642858,6.69643 6.644625,0 8.571429,-6.69643 8.571429,-6.69643"
|
||||
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
id="cool"
|
||||
inkscape:label="">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,33.801736,823.74463)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-7"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-8)"
|
||||
sodipodi:type="arc" />
|
||||
<rect
|
||||
ry="0.8035714"
|
||||
y="1027.3845"
|
||||
x="37.566963"
|
||||
height="2.4541557"
|
||||
width="20.714285"
|
||||
id="rect3871"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
transform="matrix(2.2007051,0,0,1.1582659,15.153743,755.67017)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-6"
|
||||
style="fill:url(#linearGradient3887);fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="czc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3809-8"
|
||||
d="m 42.05798,1044.2158 c 0,0 1.872643,2.6558 5.991887,2.6558 4.119244,0 6.235452,-4.7392 6.235452,-4.7392"
|
||||
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
transform="matrix(2.2007051,0,0,1.1582659,27.023036,755.66017)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-6-5"
|
||||
style="fill:url(#linearGradient3904);fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
</g>
|
||||
<g
|
||||
id="stunned"
|
||||
inkscape:label="">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,65.76398,823.59407)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-3"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-5)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,64.29083,917.11755)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-0"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,73.8277,917.11755)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5-9"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(1.5951602,0,0,1.0261609,60.548479,798.24781)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5-9-5"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
</g>
|
||||
<g
|
||||
id="tongue"
|
||||
inkscape:label="">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,97.820154,823.8709)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-3-0"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-5-2)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,96.347004,917.39438)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-0-7"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,105.88387,917.39438)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5-9-3"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="czcc"
|
||||
transform="translate(0,796.36218)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4009"
|
||||
d="m 111.69643,244.57143 c 0,0 -0.45837,5.71428 3.21428,5.71428 3.67265,0 2.94643,-5.625 2.94643,-5.625 z"
|
||||
style="fill:#ff3c00;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
transform="translate(0,796.36218)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4007"
|
||||
d="m 104.19643,244.48214 15.80357,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
id="wink"
|
||||
inkscape:label="">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,161.68181,823.65707)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-1"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-56)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.1914354,160.20866,986.17167)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-2"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(1.2347542,0,0,0.64987065,165.84267,876.83486)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5-1"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="czc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3809-82-3"
|
||||
d="m 169.22444,1041.5403 c 0,0 2.64109,3.5714 7.05357,3.5714 4.41248,0 7.14286,-3.6606 7.14286,-3.6606"
|
||||
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(192.12627,0.09009866)"
|
||||
id="sad"
|
||||
inkscape:label="">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,1.8194901,823.65453)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-2"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-4)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,0.34633991,917.17801)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-9"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,9.8832106,917.17801)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5-2"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="czc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3809-82"
|
||||
d="m 9.0981694,1044.9324 c 0,0 2.6410896,-3.5714 7.0535716,-3.5714 4.412482,0 7.142857,3.6606 7.142857,3.6606"
|
||||
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
id="crying"
|
||||
inkscape:label="">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-4-5)"
|
||||
id="path2987-2-8"
|
||||
sodipodi:cx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:ry="16"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
transform="matrix(0.88628187,0,0,0.88628187,225.85699,823.74463)" />
|
||||
<path
|
||||
sodipodi:nodetypes="czaac"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4283"
|
||||
d="m 233.0569,1035.4408 c 0,0 -3.31266,1.7855 -3.58753,3.0023 -0.27489,1.2169 0.30632,2.1821 1.1672,2.5772 0.83161,0.3825 2.09139,-0.035 2.61925,-0.7703 0.93331,-1.2995 -0.19892,-4.8092 -0.19892,-4.8092 z"
|
||||
style="fill:#007eff;fill-opacity:1;stroke:#000000;stroke-width:0.24999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
id="path3781-9-1"
|
||||
sodipodi:cx="12.142858"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:ry="3.9285715"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
transform="matrix(1.2115763,0,0,0.63767169,220.9911,879.83187)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
id="path3781-5-2-2"
|
||||
sodipodi:cx="12.142858"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:ry="3.9285715"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
transform="matrix(1.2115763,0,0,0.63767169,230.2492,879.83187)" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 233.13567,1045.0225 c 0,0 2.64109,-3.5714 7.05357,-3.5714 4.41249,0 7.14286,3.6606 7.14286,3.6606"
|
||||
id="path3809-82-9"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="czc" />
|
||||
<path
|
||||
sodipodi:nodetypes="czaac"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4283-1"
|
||||
d="m 247.33865,1036.0177 c 0,0 -1.06279,3.61 -0.39172,4.6617 0.67107,1.0516 1.76583,1.318 2.65236,0.9844 0.857,-0.3216 1.4467,-1.5107 1.29603,-2.403 -0.26637,-1.5775 -3.55667,-3.2431 -3.55667,-3.2431 z"
|
||||
style="fill:#007eff;fill-opacity:1;stroke:#000000;stroke-width:0.24999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label=""
|
||||
id="smile"
|
||||
transform="translate(-0.01141139,-31.91911)">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,1.8194901,823.65453)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-19"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-42)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,0.34633991,917.17801)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-26"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,9.8832106,917.17801)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5-29"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="czcc"
|
||||
transform="translate(0,796.36218)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3809-7"
|
||||
d="m 6.5178572,243.05357 c 0,0 2.8196608,6.69643 9.4642858,6.69643 6.644625,0 8.571429,-6.69643 8.571429,-6.69643 z"
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.01859379px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 8.6777108,1042.4231 14.3101152,0"
|
||||
id="path4459"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="#"
|
||||
id="plain"
|
||||
transform="translate(-0.01141139,-31.91911)">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,33.814236,823.55768)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-19-4"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-42-4)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,32.341086,917.08116)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-26-8"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,41.877957,917.08116)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5-29-3"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 40.844992,1042.3262 13.792439,0"
|
||||
id="path4459-9"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label=""
|
||||
id="laugh"
|
||||
transform="translate(-0.01141139,-31.91911)">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,65.711639,823.5914)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-19-5"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-42-5)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
d="m 75.320085,1029.2437 c -1.4357,0 -2.580995,1.2055 -2.580995,2.7168 0,0.4945 0.144275,0.9581 0.362245,1.3584 -0.01753,-0.091 -0.04528,-0.1765 -0.04528,-0.2717 0,-1.0104 1.000947,-1.8112 2.264031,-1.8112 1.263084,0 2.309311,0.8008 2.309311,1.8112 0,0.08 -0.03293,0.1495 -0.04528,0.2264 0.203088,-0.3899 0.362245,-0.8371 0.362245,-1.3131 0,-1.5113 -1.190576,-2.7168 -2.626275,-2.7168 z"
|
||||
id="path3781-26-7" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
d="m 84.041385,1029.2437 c -1.435699,0 -2.580994,1.2055 -2.580994,2.7168 0,0.4584 0.127713,0.8883 0.316964,1.2679 0,-1.0105 1.000946,-1.8113 2.26403,-1.8113 1.263085,0 2.309312,0.8008 2.309312,1.8113 0.185411,-0.3766 0.316964,-0.8145 0.316964,-1.2679 0,-1.5113 -1.190576,-2.7168 -2.626276,-2.7168 z"
|
||||
id="path3781-5-29-1" />
|
||||
<path
|
||||
sodipodi:nodetypes="czcc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3809-7-5"
|
||||
d="m 70.410006,1039.3526 c 0,0 2.819661,6.6965 9.464286,6.6965 6.644625,0 8.571429,-6.6965 8.571429,-6.6965 z"
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#000000;stroke:none"
|
||||
d="m 71.931054,1041.3349 15.089286,0 -1.508928,2.1996 -11.568453,0 z"
|
||||
id="path4562"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="angry"
|
||||
inkscape:label="">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,97.629508,791.79739)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-19-2"
|
||||
style="fill:url(#linearGradient3187);fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-42-1)"
|
||||
sodipodi:type="arc" />
|
||||
<rect
|
||||
ry="0.8035714"
|
||||
y="1007.2888"
|
||||
x="105.08295"
|
||||
height="6.1593604"
|
||||
width="13.196339"
|
||||
id="rect3189"
|
||||
style="fill:#f9f9f9;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-opacity:1"
|
||||
rx="0.8035714" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 105.46983,1010.3042 12.71794,0"
|
||||
id="path4459-1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.03361535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 114.11573,1000.6187 6.16645,-1.17671"
|
||||
id="path4459-1-0"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.03361535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 103.85721,999.56622 6.20869,0.92818"
|
||||
id="path4459-1-0-1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="uncertain"
|
||||
inkscape:label="">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,129.83735,823.77759)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-3-0-2"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-5-2-6)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,128.3642,917.30107)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-0-7-4"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,137.90106,917.30107)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5-9-3-1"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="csc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3204"
|
||||
d="m 140.66832,1045.9796 c 0,0 2.22896,-6.1582 5.84056,-6.352 3.35156,-0.1799 5.55738,1.9643 5.55738,1.9643"
|
||||
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
id="scared"
|
||||
inkscape:label="">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,129.83735,791.59902)"
|
||||
d="m 32,240 c 0,8.83656 -7.163444,16 -16,16 -8.836556,0 -16,-7.16344 -16,-16 0,-8.83656 7.163444,-16 16,-16 8.836556,0 16,7.16344 16,16 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-19-58"
|
||||
style="fill:url(#linearGradient3137);fill-opacity:1;stroke:#000000;stroke-width:1.12830921000000006;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1.0;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-42-0)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,128.3642,885.1225)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-26-2"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.91334205,0,0,0.48070635,137.90107,885.1225)"
|
||||
d="m 14.107143,238.5 c 0,2.16969 -0.87944,3.92857 -1.964285,3.92857 -1.084846,0 -1.964286,-1.75888 -1.964286,-3.92857 0,-2.16969 0.87944,-3.92857 1.964286,-3.92857 1.084845,0 1.964285,1.75888 1.964285,3.92857 z"
|
||||
sodipodi:ry="3.9285715"
|
||||
sodipodi:rx="1.9642857"
|
||||
sodipodi:cy="238.5"
|
||||
sodipodi:cx="12.142858"
|
||||
id="path3781-5-29-6"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="szsszss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3809-7-56"
|
||||
d="m 137.06672,1013.3344 c 0.25114,-1.6566 2.15608,-5.1205 7.23695,-5.1205 5.08089,0 6.93799,3.9294 6.55424,5.1205 -0.13763,0.4272 -0.47655,0.8569 -1.31761,0.4379 -1.70599,-0.8499 -2.65516,-1.2076 -5.57799,-1.2572 -2.92284,-0.05 -4.14543,1.2631 -5.36636,1.7495 -1.22092,0.4865 -1.61867,-0.3403 -1.52923,-0.9302 z"
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.76466024;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
id="laugh_closed_eyes"
|
||||
inkscape:label="#g3182">
|
||||
<path
|
||||
transform="matrix(0.88628187,0,0,0.88628187,161.91268,791.71004)"
|
||||
d="m 32,240 a 16,16 0 1 1 -32,0 16,16 0 1 1 32,0 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="16"
|
||||
sodipodi:cy="240"
|
||||
sodipodi:cx="16"
|
||||
id="path2987-19-46"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:#000000;stroke-width:1.12830925;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3777-42-51)"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="czcc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3809-7-7"
|
||||
d="m 166.61105,1007.4713 c 0,0 2.81966,6.6964 9.46428,6.6964 6.64463,0 8.57143,-6.6964 8.57143,-6.6964 z"
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3142"
|
||||
d="m 167.85286,999.14099 5.21988,2.05271 -5.01109,1.8159"
|
||||
style="fill:none;stroke:#000000;stroke-width:1.01681638px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3142-8"
|
||||
d="m 183.57572,999.08084 -5.21988,2.05276 5.01109,1.8159"
|
||||
style="fill:none;stroke:#000000;stroke-width:1.01681638px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4562-7"
|
||||
d="m 168.16325,1009.489 15.08928,0 -1.50892,2.1996 -11.56846,0 z"
|
||||
style="fill:#000000;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 40 KiB |
BIN
smileys/default/sad.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
smileys/default/scared.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
smileys/default/smile.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
smileys/default/stunned.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
smileys/default/tongue.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
smileys/default/uncertain.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
smileys/default/wink.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
35
style.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "style.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
QString Style::get(const QString &filename)
|
||||
{
|
||||
if (!Settings::getInstance().getUseNativeStyle())
|
||||
{
|
||||
QFile file(filename);
|
||||
if (file.open(QFile::ReadOnly | QFile::Text))
|
||||
return file.readAll();
|
||||
else
|
||||
qWarning() << "Style " << filename << " not found";
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
|
@ -14,14 +14,17 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "clickablelabel.h"
|
||||
#ifndef STYLE_H
|
||||
#define STYLE_H
|
||||
|
||||
ClickableLabel::ClickableLabel(QWidget *parent) :
|
||||
QLabel(parent)
|
||||
{
|
||||
}
|
||||
#include <QString>
|
||||
|
||||
void ClickableLabel::mousePressEvent(QMouseEvent*)
|
||||
class Style
|
||||
{
|
||||
emit clicked();
|
||||
}
|
||||
public:
|
||||
static QString get(const QString& filename);
|
||||
private:
|
||||
Style();
|
||||
};
|
||||
|
||||
#endif // STYLE_H
|
98
tools/buildPackages.sh
Executable file
|
@ -0,0 +1,98 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Config (Update me if needed !)
|
||||
VERSION_UPSTREAM="0.01pre-alpha"
|
||||
VERSION_PACKAGE="1"
|
||||
PACKAGENAME="qtox"
|
||||
UPSTREAM_URL="https://github.com/tux3/qTox/archive/master.tar.gz"
|
||||
|
||||
# Make some vars for convenience
|
||||
VERNAME=$PACKAGENAME"_"$VERSION_UPSTREAM
|
||||
FULLVERNAME=$VERNAME"-"$VERSION_PACKAGE
|
||||
ARCHIVENAME=$VERNAME".orig.tar.gz"
|
||||
|
||||
# ARCHIVENAME > FULLVERNAME > VERNAME = PACKAGENAME+UPVER
|
||||
|
||||
# Get some args
|
||||
OPT_SUDO=true
|
||||
OPT_APT=true
|
||||
OPT_KEEP=false
|
||||
while [ $# -ge 1 ] ; do
|
||||
if [ ${1} = "-s" -o ${1} = "--no-sudo" ] ; then
|
||||
OPT_SUDO=false
|
||||
shift
|
||||
elif [ ${1} = "-a" -o ${1} = "--no-apt" ] ; then
|
||||
OPT_APT=false
|
||||
shift
|
||||
elif [ ${1} = "-k" -o ${1} = "--keep" ]; then
|
||||
OPT_KEEP=true
|
||||
shift
|
||||
else
|
||||
if [ ${1} != "-h" -a ${1} != "--help" ] ; then
|
||||
echo "[ERROR] Unknown parameter \"${1}\""
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# print help
|
||||
echo "Use this script to build qTox packages for Debian and Red Hat families"
|
||||
echo ""
|
||||
echo "usage:"
|
||||
echo " ${0} [-h|--help|-k|--keep|-s|--no-sudo|-a|--no-apt]"
|
||||
echo ""
|
||||
echo "parameters:"
|
||||
echo " -h|--help : displays this help"
|
||||
echo " -s|--no-sudo: disables using sudo for apt and alien"
|
||||
echo " -a|--no-apt : disables apt-get (used for build deps) entirely"
|
||||
echo " -k|--keep : does not delete the build files afterwards"
|
||||
echo ""
|
||||
echo "example usages:"
|
||||
echo " ${0} -- build packages, cleaning up trash and running sudo alien and apt-get"
|
||||
echo " ${0} -s -k -- build packages, keeping build files and non-sudo alien and apt-get"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Get the requried tools if needed
|
||||
if [[ $OPT_APT = "true" ]]; then
|
||||
echo "Installing missing tools (if any)..."
|
||||
if [[ $EUID -ne 0 && $OPT_SUDO = "true" ]]; then
|
||||
sudo apt-get install wget debhelper cdbs devscripts alien tar gzip build-essential
|
||||
else
|
||||
apt-get install wget debhelper cdbs devscripts alien tar gzip build-essential
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p .packages
|
||||
cd .packages
|
||||
|
||||
# Cleanup
|
||||
rm -r $VERNAME 2> /dev/null
|
||||
rm $ARCHIVENAME 2> /dev/null
|
||||
|
||||
# Fectch sources and layout directories
|
||||
wget -O $ARCHIVENAME $UPSTREAM_URL
|
||||
tar xvf $ARCHIVENAME 2> /dev/null # Extracts to qTox-master
|
||||
mv qTox-master $VERNAME
|
||||
#tar cz $VERNAME > $ARCHIVENAME
|
||||
|
||||
# Build packages
|
||||
cd $VERNAME
|
||||
debuild -us -uc
|
||||
cd ..
|
||||
|
||||
# alien warns that it should probably be run as root...
|
||||
if [[ $EUID -ne 0 && $OPT_SUDO = "true" ]]; then
|
||||
sudo alien ./$FULLVERNAME*.deb -r
|
||||
else
|
||||
alien ./$FULLVERNAME*.deb -r
|
||||
fi
|
||||
|
||||
mv *.deb ..
|
||||
mv -f *.rpm ..
|
||||
|
||||
if [[ $OPT_KEEP = "false" ]]; then
|
||||
rm -r *
|
||||
fi
|
||||
|
||||
cd ..
|
||||
rmdir .packages 2> /dev/null # fails if non empty
|
|
@ -1,75 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.0" language="de_DE">
|
||||
<TS version="2.1" language="de_DE">
|
||||
<context>
|
||||
<name>AddFriendForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="15"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="32"/>
|
||||
<source>Add Friends</source>
|
||||
<translation>Freunde hinzufügen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="18"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="35"/>
|
||||
<source>Tox ID</source>
|
||||
<comment>Tox ID of the person you're sending a friend request to</comment>
|
||||
<translation>Tox ID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="19"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="36"/>
|
||||
<source>Message</source>
|
||||
<comment>The message you send in friend requests</comment>
|
||||
<translation>Nachricht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="20"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="37"/>
|
||||
<source>Send friend request</source>
|
||||
<translation>Freundschaftseinladung versenden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="67"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="38"/>
|
||||
<source>Tox me maybe?</source>
|
||||
<comment>Default message in friend requests if the field is left blank. Write something appropriate!</comment>
|
||||
<translation>Lass uns Toxen!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="75"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="93"/>
|
||||
<source>Please fill in a valid Tox ID</source>
|
||||
<comment>Tox ID of the friend you're sending a friend request to</comment>
|
||||
<translation>Bitte gib eine gültige Tox ID ein</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="92"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="110"/>
|
||||
<source>This address does not exist</source>
|
||||
<comment>The DNS gives the Tox ID associated to toxme.se addresses</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="96"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="114"/>
|
||||
<source>Error while looking up DNS</source>
|
||||
<comment>The DNS gives the Tox ID associated to toxme.se addresses</comment>
|
||||
<translation>Fehler beim Auflösen des DNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="102"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="120"/>
|
||||
<source>Unexpected number of text records</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Unererwartete Anzahl von Texteinträgen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="108"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="126"/>
|
||||
<source>Unexpected number of values in text record</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Unerwartete Anzahl von Werten innerhalb des Texteintrages</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="115"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="133"/>
|
||||
<source>The DNS lookup does not contain any Tox ID</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Der DNS Eintrag enthält keine gültige TOX ID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="121"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="127"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="139"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="145"/>
|
||||
<source>The DNS lookup does not contain a valid Tox ID</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Der DNS Eintrag enthält keine gültige TOX ID</translation>
|
||||
|
@ -78,12 +78,12 @@
|
|||
<context>
|
||||
<name>Camera</name>
|
||||
<message>
|
||||
<location filename="../widget/camera.cpp" line="145"/>
|
||||
<location filename="../widget/camera.cpp" line="161"/>
|
||||
<source>Camera eror</source>
|
||||
<translation>Kamerafehler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/camera.cpp" line="146"/>
|
||||
<location filename="../widget/camera.cpp" line="162"/>
|
||||
<source>Camera format %1 not supported, can't use the camera</source>
|
||||
<translation>Kameraformat %1 wird nicht unterstützt. Die Kamera kann nicht verwendet werden</translation>
|
||||
</message>
|
||||
|
@ -91,13 +91,13 @@
|
|||
<context>
|
||||
<name>ChatForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/chatform.cpp" line="265"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="283"/>
|
||||
<source>Send a file</source>
|
||||
<translation>Datei versenden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/chatform.cpp" line="590"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="596"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="620"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="626"/>
|
||||
<source>Save chat log</source>
|
||||
<translation>Chatverlauf speichern</translation>
|
||||
</message>
|
||||
|
@ -113,11 +113,42 @@
|
|||
<context>
|
||||
<name>FileTransfertWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="249"/>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="281"/>
|
||||
<source>Save a file</source>
|
||||
<comment>Title of the file saving dialog</comment>
|
||||
<translation>Datei speichern</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
||||
<source>Location not writable</source>
|
||||
<comment>Title of permissions popup</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
||||
<source>You do not have permission to write that location. Choose another, or cancel the save dialog.</source>
|
||||
<comment>text of permissions popup</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FilesForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="25"/>
|
||||
<source>Transfered Files</source>
|
||||
<comment>"Headline" of the window</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="33"/>
|
||||
<source>Downloads</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="34"/>
|
||||
<source>Uploads</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FriendRequestDialog</name>
|
||||
|
@ -158,19 +189,19 @@
|
|||
<context>
|
||||
<name>FriendWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="71"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="86"/>
|
||||
<source>Copy friend ID</source>
|
||||
<comment>Menu to copy the Tox ID of that friend</comment>
|
||||
<translation>Tox ID kopieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="72"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="87"/>
|
||||
<source>Invite in group</source>
|
||||
<comment>Menu to invite a friend in a groupchat</comment>
|
||||
<translation>In Gruppe einladen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="82"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="97"/>
|
||||
<source>Remove friend</source>
|
||||
<comment>Menu to remove the friend from our friendlist</comment>
|
||||
<translation>Freund entfernen</translation>
|
||||
|
@ -179,23 +210,23 @@
|
|||
<context>
|
||||
<name>GroupChatForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="32"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="49"/>
|
||||
<source>%1 users in chat</source>
|
||||
<comment>Number of users in chat</comment>
|
||||
<translation>%1 Personen im Chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="155"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="146"/>
|
||||
<source><Unknown></source>
|
||||
<translation><Unbekannt></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="224"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="215"/>
|
||||
<source>%1 users in chat</source>
|
||||
<translation>%1 Personen im Chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="243"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="234"/>
|
||||
<source>Save chat log</source>
|
||||
<translation>Chatverlauf speichern</translation>
|
||||
</message>
|
||||
|
@ -203,28 +234,76 @@
|
|||
<context>
|
||||
<name>GroupWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="38"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="130"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="54"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="141"/>
|
||||
<source>%1 users in chat</source>
|
||||
<translation>%1 Personen im Chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="40"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="132"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="56"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="143"/>
|
||||
<source>0 users in chat</source>
|
||||
<translation>0 Personen im Chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="73"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="84"/>
|
||||
<source>Quit group</source>
|
||||
<comment>Menu to quit a groupchat</comment>
|
||||
<translation>Gruppe verlassen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="20"/>
|
||||
<source>qTox</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="1918"/>
|
||||
<source>Your name</source>
|
||||
<translation type="unfinished">Dein Name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2000"/>
|
||||
<source>Your status</source>
|
||||
<translation type="unfinished">Dein Status</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2566"/>
|
||||
<source>Add friends</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2592"/>
|
||||
<source>Create a group chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2624"/>
|
||||
<source>View completed file transfers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2656"/>
|
||||
<source>Change your settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="3238"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished">Schließen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="3241"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation type="unfinished">Strg+Q</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SelfCamView</name>
|
||||
<message>
|
||||
<location filename="../widget/selfcamview.cpp" line="16"/>
|
||||
<location filename="../widget/selfcamview.cpp" line="32"/>
|
||||
<source>Tox video test</source>
|
||||
<comment>Title of the window to test the video/webcam</comment>
|
||||
<translation>Tox Video testen</translation>
|
||||
|
@ -233,83 +312,105 @@
|
|||
<context>
|
||||
<name>SettingsForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="15"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="34"/>
|
||||
<source>User Settings</source>
|
||||
<comment>"Headline" of the window</comment>
|
||||
<translation>Einstellungen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="18"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="37"/>
|
||||
<source>Name</source>
|
||||
<comment>Username/nick</comment>
|
||||
<translation>Benutzername</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="19"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="38"/>
|
||||
<source>Status</source>
|
||||
<comment>Status message</comment>
|
||||
<translation>Status</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="28"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="39"/>
|
||||
<source>(click here to copy)</source>
|
||||
<comment>Click on this text to copy TID to clipboard</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="47"/>
|
||||
<source>Test video</source>
|
||||
<comment>Text on a button to test the video/webcam</comment>
|
||||
<translation>Video testen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="29"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="48"/>
|
||||
<source>Enable IPv6 (recommended)</source>
|
||||
<comment>Text on a checkbox to enable IPv6</comment>
|
||||
<translation>IPv6 aktivieren (empfohlen)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="31"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="50"/>
|
||||
<source>Use translations</source>
|
||||
<comment>Text on a checkbox to enable translations</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="52"/>
|
||||
<source>Make Tox portable</source>
|
||||
<comment>Text on a checkbox to make qTox a portable application</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="54"/>
|
||||
<source>Save settings to the working directory instead of the usual conf dir</source>
|
||||
<comment>describes makeToxPortable checkbox</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="56"/>
|
||||
<source>Smiley Pack</source>
|
||||
<comment>Text on smiley pack label</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Widget</name>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="26"/>
|
||||
<source>Tox</source>
|
||||
<translation>Tox</translation>
|
||||
<translation type="vanished">Tox</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="1935"/>
|
||||
<source>Your name</source>
|
||||
<translation>Dein Name</translation>
|
||||
<translation type="vanished">Dein Name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2017"/>
|
||||
<source>Your status</source>
|
||||
<translation>Dein Status</translation>
|
||||
<translation type="vanished">Dein Status</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="3293"/>
|
||||
<source>Close</source>
|
||||
<translation>Schließen</translation>
|
||||
<translation type="vanished">Schließen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="3296"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation>Strg+Q</translation>
|
||||
<translation type="vanished">Strg+Q</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="128"/>
|
||||
<source>Online</source>
|
||||
<comment>Button to set your status to 'Online'</comment>
|
||||
<translation type="obsolete">Online</translation>
|
||||
<translation type="unfinished">Online</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="130"/>
|
||||
<source>Away</source>
|
||||
<comment>Button to set your status to 'Away'</comment>
|
||||
<translation type="obsolete">Abwesend</translation>
|
||||
<translation type="unfinished">Abwesend</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="132"/>
|
||||
<source>Busy</source>
|
||||
<comment>Button to set your status to 'Busy'</comment>
|
||||
<translation type="obsolete">Beschäftigt</translation>
|
||||
<translation type="unfinished">Beschäftigt</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -1,75 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.0" language="fr_FR">
|
||||
<TS version="2.1" language="fr_FR">
|
||||
<context>
|
||||
<name>AddFriendForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="15"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="32"/>
|
||||
<source>Add Friends</source>
|
||||
<translation>Ajouter des amis</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="18"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="35"/>
|
||||
<source>Tox ID</source>
|
||||
<comment>Tox ID of the person you're sending a friend request to</comment>
|
||||
<translation>ID Tox</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="19"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="36"/>
|
||||
<source>Message</source>
|
||||
<comment>The message you send in friend requests</comment>
|
||||
<translation>Message</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="20"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="37"/>
|
||||
<source>Send friend request</source>
|
||||
<translation>Envoyer la demande d'ami</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="67"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="38"/>
|
||||
<source>Tox me maybe?</source>
|
||||
<comment>Default message in friend requests if the field is left blank. Write something appropriate!</comment>
|
||||
<translation>Je souhaiterais vous ajouter à mes contacts</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="75"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="93"/>
|
||||
<source>Please fill in a valid Tox ID</source>
|
||||
<comment>Tox ID of the friend you're sending a friend request to</comment>
|
||||
<translation>Merci de remplir un ID Tox valide</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="92"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="110"/>
|
||||
<source>This address does not exist</source>
|
||||
<comment>The DNS gives the Tox ID associated to toxme.se addresses</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="96"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="114"/>
|
||||
<source>Error while looking up DNS</source>
|
||||
<comment>The DNS gives the Tox ID associated to toxme.se addresses</comment>
|
||||
<translation>Erreur en consultant le serveur DNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="102"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="120"/>
|
||||
<source>Unexpected number of text records</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Nombre d'entrées texte innatendu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="108"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="126"/>
|
||||
<source>Unexpected number of values in text record</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Nombre d'entrées numériques dans l'entrée texte innatendu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="115"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="133"/>
|
||||
<source>The DNS lookup does not contain any Tox ID</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>La réponse DNS ne contient aucun ID Tox</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="121"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="127"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="139"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="145"/>
|
||||
<source>The DNS lookup does not contain a valid Tox ID</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>La réponse DNS ne contient pas d'ID Tox valide</translation>
|
||||
|
@ -78,12 +78,12 @@
|
|||
<context>
|
||||
<name>Camera</name>
|
||||
<message>
|
||||
<location filename="../widget/camera.cpp" line="145"/>
|
||||
<location filename="../widget/camera.cpp" line="161"/>
|
||||
<source>Camera eror</source>
|
||||
<translation>Erreur de caméra</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/camera.cpp" line="146"/>
|
||||
<location filename="../widget/camera.cpp" line="162"/>
|
||||
<source>Camera format %1 not supported, can't use the camera</source>
|
||||
<translation>Format %1 de la caméra non supporté, impossible de l'utiliser</translation>
|
||||
</message>
|
||||
|
@ -91,13 +91,13 @@
|
|||
<context>
|
||||
<name>ChatForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/chatform.cpp" line="265"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="283"/>
|
||||
<source>Send a file</source>
|
||||
<translation>Envoyer un fichier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/chatform.cpp" line="590"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="596"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="620"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="626"/>
|
||||
<source>Save chat log</source>
|
||||
<translation>Sauvegarder l'historique de conversation</translation>
|
||||
</message>
|
||||
|
@ -113,11 +113,42 @@
|
|||
<context>
|
||||
<name>FileTransfertWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="249"/>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="281"/>
|
||||
<source>Save a file</source>
|
||||
<comment>Title of the file saving dialog</comment>
|
||||
<translation>Sauvegarder un fichier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
||||
<source>Location not writable</source>
|
||||
<comment>Title of permissions popup</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
||||
<source>You do not have permission to write that location. Choose another, or cancel the save dialog.</source>
|
||||
<comment>text of permissions popup</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FilesForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="25"/>
|
||||
<source>Transfered Files</source>
|
||||
<comment>"Headline" of the window</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="33"/>
|
||||
<source>Downloads</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="34"/>
|
||||
<source>Uploads</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FriendRequestDialog</name>
|
||||
|
@ -158,19 +189,19 @@
|
|||
<context>
|
||||
<name>FriendWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="71"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="86"/>
|
||||
<source>Copy friend ID</source>
|
||||
<comment>Menu to copy the Tox ID of that friend</comment>
|
||||
<translation>Copier l'ID ami</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="72"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="87"/>
|
||||
<source>Invite in group</source>
|
||||
<comment>Menu to invite a friend in a groupchat</comment>
|
||||
<translation>Inviter dans un groupe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="82"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="97"/>
|
||||
<source>Remove friend</source>
|
||||
<comment>Menu to remove the friend from our friendlist</comment>
|
||||
<translation>Supprimer ami</translation>
|
||||
|
@ -179,23 +210,23 @@
|
|||
<context>
|
||||
<name>GroupChatForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="32"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="49"/>
|
||||
<source>%1 users in chat</source>
|
||||
<comment>Number of users in chat</comment>
|
||||
<translation>%1 personnes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="155"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="146"/>
|
||||
<source><Unknown></source>
|
||||
<translation><Inconnu></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="224"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="215"/>
|
||||
<source>%1 users in chat</source>
|
||||
<translation>%1 personnes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="243"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="234"/>
|
||||
<source>Save chat log</source>
|
||||
<translation>Sauvegarder l'historique de conversation</translation>
|
||||
</message>
|
||||
|
@ -203,28 +234,76 @@
|
|||
<context>
|
||||
<name>GroupWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="38"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="130"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="54"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="141"/>
|
||||
<source>%1 users in chat</source>
|
||||
<translation>%1 personnes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="40"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="132"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="56"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="143"/>
|
||||
<source>0 users in chat</source>
|
||||
<translation>0 personnes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="73"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="84"/>
|
||||
<source>Quit group</source>
|
||||
<comment>Menu to quit a groupchat</comment>
|
||||
<translation>Quitter le groupe</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="20"/>
|
||||
<source>qTox</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="1918"/>
|
||||
<source>Your name</source>
|
||||
<translation type="unfinished">Votre nom</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2000"/>
|
||||
<source>Your status</source>
|
||||
<translation type="unfinished">Votre status</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2566"/>
|
||||
<source>Add friends</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2592"/>
|
||||
<source>Create a group chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2624"/>
|
||||
<source>View completed file transfers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2656"/>
|
||||
<source>Change your settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="3238"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished">Fermer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="3241"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation type="unfinished">Ctrl+Q</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SelfCamView</name>
|
||||
<message>
|
||||
<location filename="../widget/selfcamview.cpp" line="16"/>
|
||||
<location filename="../widget/selfcamview.cpp" line="32"/>
|
||||
<source>Tox video test</source>
|
||||
<comment>Title of the window to test the video/webcam</comment>
|
||||
<translation>Test vidéo Tox</translation>
|
||||
|
@ -233,83 +312,105 @@
|
|||
<context>
|
||||
<name>SettingsForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="15"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="34"/>
|
||||
<source>User Settings</source>
|
||||
<comment>"Headline" of the window</comment>
|
||||
<translation>Configuration</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="18"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="37"/>
|
||||
<source>Name</source>
|
||||
<comment>Username/nick</comment>
|
||||
<translation>Nom</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="19"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="38"/>
|
||||
<source>Status</source>
|
||||
<comment>Status message</comment>
|
||||
<translation>Status</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="28"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="39"/>
|
||||
<source>(click here to copy)</source>
|
||||
<comment>Click on this text to copy TID to clipboard</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="47"/>
|
||||
<source>Test video</source>
|
||||
<comment>Text on a button to test the video/webcam</comment>
|
||||
<translation>Tester la vidéo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="29"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="48"/>
|
||||
<source>Enable IPv6 (recommended)</source>
|
||||
<comment>Text on a checkbox to enable IPv6</comment>
|
||||
<translation>Activer IPv6 (recommandé)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="31"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="50"/>
|
||||
<source>Use translations</source>
|
||||
<comment>Text on a checkbox to enable translations</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="52"/>
|
||||
<source>Make Tox portable</source>
|
||||
<comment>Text on a checkbox to make qTox a portable application</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="54"/>
|
||||
<source>Save settings to the working directory instead of the usual conf dir</source>
|
||||
<comment>describes makeToxPortable checkbox</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="56"/>
|
||||
<source>Smiley Pack</source>
|
||||
<comment>Text on smiley pack label</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Widget</name>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="26"/>
|
||||
<source>Tox</source>
|
||||
<translation>Tox</translation>
|
||||
<translation type="vanished">Tox</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="1935"/>
|
||||
<source>Your name</source>
|
||||
<translation>Votre nom</translation>
|
||||
<translation type="vanished">Votre nom</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2017"/>
|
||||
<source>Your status</source>
|
||||
<translation>Votre status</translation>
|
||||
<translation type="vanished">Votre status</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="3293"/>
|
||||
<source>Close</source>
|
||||
<translation>Fermer</translation>
|
||||
<translation type="vanished">Fermer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="3296"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation>Ctrl+Q</translation>
|
||||
<translation type="vanished">Ctrl+Q</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="128"/>
|
||||
<source>Online</source>
|
||||
<comment>Button to set your status to 'Online'</comment>
|
||||
<translation type="obsolete">Connecté</translation>
|
||||
<translation type="unfinished">Connecté</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="130"/>
|
||||
<source>Away</source>
|
||||
<comment>Button to set your status to 'Away'</comment>
|
||||
<translation type="obsolete">Indisponnible</translation>
|
||||
<translation type="unfinished">Indisponnible</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="132"/>
|
||||
<source>Busy</source>
|
||||
<comment>Button to set your status to 'Busy'</comment>
|
||||
<translation type="obsolete">Occupé</translation>
|
||||
<translation type="unfinished">Occupé</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.0" language="it_IT">
|
||||
<TS version="2.1" language="it_IT">
|
||||
<context>
|
||||
<name>AddFriendForm</name>
|
||||
<message>
|
||||
|
@ -26,50 +26,50 @@
|
|||
<translation>Invia richiesta d'amicizia</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="84"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="38"/>
|
||||
<source>Tox me maybe?</source>
|
||||
<comment>Default message in friend requests if the field is left blank. Write something appropriate!</comment>
|
||||
<translation>Permettimi di aggiungerti alla mia lista contatti</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="92"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="93"/>
|
||||
<source>Please fill in a valid Tox ID</source>
|
||||
<comment>Tox ID of the friend you're sending a friend request to</comment>
|
||||
<translation>Inserisci un Tox ID valido</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="109"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="110"/>
|
||||
<source>This address does not exist</source>
|
||||
<comment>The DNS gives the Tox ID associated to toxme.se addresses</comment>
|
||||
<translation>Questo indirizzo non esiste</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="113"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="114"/>
|
||||
<source>Error while looking up DNS</source>
|
||||
<comment>The DNS gives the Tox ID associated to toxme.se addresses</comment>
|
||||
<translation>Errore nel consultare il server DNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="119"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="120"/>
|
||||
<source>Unexpected number of text records</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Numero inaspettato di text-records</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="125"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="126"/>
|
||||
<source>Unexpected number of values in text record</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Numero inaspettato di valori nel text-record</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="132"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="133"/>
|
||||
<source>The DNS lookup does not contain any Tox ID</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>La risposta del server DNS non contiene nessun Tox ID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="138"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="144"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="139"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="145"/>
|
||||
<source>The DNS lookup does not contain a valid Tox ID</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>La risposta del server DNS non contiene un Tox ID valido</translation>
|
||||
|
@ -91,13 +91,13 @@
|
|||
<context>
|
||||
<name>ChatForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/chatform.cpp" line="291"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="283"/>
|
||||
<source>Send a file</source>
|
||||
<translation>Invia un file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/chatform.cpp" line="616"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="622"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="620"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="626"/>
|
||||
<source>Save chat log</source>
|
||||
<translation>Salva il log della chat</translation>
|
||||
</message>
|
||||
|
@ -113,11 +113,23 @@
|
|||
<context>
|
||||
<name>FileTransfertWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="270"/>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="281"/>
|
||||
<source>Save a file</source>
|
||||
<comment>Title of the file saving dialog</comment>
|
||||
<translation>Salva file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
||||
<source>Location not writable</source>
|
||||
<comment>Title of permissions popup</comment>
|
||||
<translation>Errore</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
||||
<source>You do not have permission to write that location. Choose another, or cancel the save dialog.</source>
|
||||
<comment>text of permissions popup</comment>
|
||||
<translation>Non hai sufficienti permessi per scrivere in questa locazione. Scegli un'altra posizione, o annulla il salvataggio.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FilesForm</name>
|
||||
|
@ -125,15 +137,15 @@
|
|||
<location filename="../widget/form/filesform.cpp" line="25"/>
|
||||
<source>Transfered Files</source>
|
||||
<comment>"Headline" of the window</comment>
|
||||
<translation>Files inviati</translation>
|
||||
<translation>Files Trasferiti</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="30"/>
|
||||
<location filename="../widget/form/filesform.cpp" line="33"/>
|
||||
<source>Downloads</source>
|
||||
<translation>Ricevuti</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="31"/>
|
||||
<location filename="../widget/form/filesform.cpp" line="34"/>
|
||||
<source>Uploads</source>
|
||||
<translation>Inviati</translation>
|
||||
</message>
|
||||
|
@ -177,19 +189,19 @@
|
|||
<context>
|
||||
<name>FriendWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="87"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="86"/>
|
||||
<source>Copy friend ID</source>
|
||||
<comment>Menu to copy the Tox ID of that friend</comment>
|
||||
<translation>Copia Tox ID del contatto</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="88"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="87"/>
|
||||
<source>Invite in group</source>
|
||||
<comment>Menu to invite a friend in a groupchat</comment>
|
||||
<translation>Invita nel gruppo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="98"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="97"/>
|
||||
<source>Remove friend</source>
|
||||
<comment>Menu to remove the friend from our friendlist</comment>
|
||||
<translation>Rimuovi contatto</translation>
|
||||
|
@ -198,23 +210,23 @@
|
|||
<context>
|
||||
<name>GroupChatForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="48"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="49"/>
|
||||
<source>%1 users in chat</source>
|
||||
<comment>Number of users in chat</comment>
|
||||
<translation>%1 utenti in chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="173"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="146"/>
|
||||
<source><Unknown></source>
|
||||
<translation><Sconosciuto></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="242"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="215"/>
|
||||
<source>%1 users in chat</source>
|
||||
<translation>%1 utenti in chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="261"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="234"/>
|
||||
<source>Save chat log</source>
|
||||
<translation>Salva il log della chat</translation>
|
||||
</message>
|
||||
|
@ -223,23 +235,71 @@
|
|||
<name>GroupWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="54"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="146"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="141"/>
|
||||
<source>%1 users in chat</source>
|
||||
<translation>%1 utenti in chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="56"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="148"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="143"/>
|
||||
<source>0 users in chat</source>
|
||||
<translation>0 utenti in chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="89"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="84"/>
|
||||
<source>Quit group</source>
|
||||
<comment>Menu to quit a groupchat</comment>
|
||||
<translation>Esci dal gruppo</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="20"/>
|
||||
<source>qTox</source>
|
||||
<translation>qTox</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="1918"/>
|
||||
<source>Your name</source>
|
||||
<translation>qTox User</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2000"/>
|
||||
<source>Your status</source>
|
||||
<translation>Toxin on qTox</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2566"/>
|
||||
<source>Add friends</source>
|
||||
<translation>Aggiungi contatto</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2592"/>
|
||||
<source>Create a group chat</source>
|
||||
<translation>Crea un gruppo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2624"/>
|
||||
<source>View completed file transfers</source>
|
||||
<translation>Visualizza i trasferimenti completati</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2656"/>
|
||||
<source>Change your settings</source>
|
||||
<translation>Cambia le impostazioni</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="3238"/>
|
||||
<source>Close</source>
|
||||
<translation>Chiudi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="3241"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation>Ctrl+Q</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SelfCamView</name>
|
||||
<message>
|
||||
|
@ -252,115 +312,129 @@
|
|||
<context>
|
||||
<name>SettingsForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="31"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="34"/>
|
||||
<source>User Settings</source>
|
||||
<comment>"Headline" of the window</comment>
|
||||
<translation>Impostazioni</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="34"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="37"/>
|
||||
<source>Name</source>
|
||||
<comment>Username/nick</comment>
|
||||
<translation>Nome</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="35"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="38"/>
|
||||
<source>Status</source>
|
||||
<comment>Status message</comment>
|
||||
<translation>Stato</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="36"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="39"/>
|
||||
<source>(click here to copy)</source>
|
||||
<comment>Click on this text to copy TID to clipboard</comment>
|
||||
<translation>(clicca qui per copiare)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="44"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="47"/>
|
||||
<source>Test video</source>
|
||||
<comment>Text on a button to test the video/webcam</comment>
|
||||
<translation>Prova la webcam</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="45"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="48"/>
|
||||
<source>Enable IPv6 (recommended)</source>
|
||||
<comment>Text on a checkbox to enable IPv6</comment>
|
||||
<translation>Abilita IPv6 (consigliato)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="47"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="50"/>
|
||||
<source>Use translations</source>
|
||||
<comment>Text on a checkbox to enable translations</comment>
|
||||
<translation>Abilita traduzioni</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="49"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="52"/>
|
||||
<source>Make Tox portable</source>
|
||||
<comment>Text on a checkbox to make qTox a portable application</comment>
|
||||
<translation>Rendi qTox portabile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="54"/>
|
||||
<source>Save settings to the working directory instead of the usual conf dir</source>
|
||||
<comment>describes makeToxPortable checkbox</comment>
|
||||
<translation>Slava le impostazioni nella directory di lavoro corrente, invece della directory di default</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="56"/>
|
||||
<source>Smiley Pack</source>
|
||||
<comment>Text on smiley pack label</comment>
|
||||
<translation>Emoticons</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select smiley pack</source>
|
||||
<translation type="obsolete">Scegli pacchetto emoticons</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Widget</name>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="42"/>
|
||||
<source>Tox</source>
|
||||
<translation>Tox</translation>
|
||||
<translation type="obsolete">Tox</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="1951"/>
|
||||
<source>Your name</source>
|
||||
<translation>Tox User</translation>
|
||||
<translation type="obsolete">Tox User</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2033"/>
|
||||
<source>Your status</source>
|
||||
<translation>Toxin on qTox</translation>
|
||||
<translation type="obsolete">Toxin on qTox</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2610"/>
|
||||
<source>Add friends</source>
|
||||
<translation>Aggiungi contatto</translation>
|
||||
<translation type="obsolete">Aggiungi contatto</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2652"/>
|
||||
<source>Create a group chat</source>
|
||||
<translation>Crea un gruppo</translation>
|
||||
<translation type="obsolete">Crea un gruppo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View completed file transfers</source>
|
||||
<translation type="obsolete">Visualizza i trasferimenti completati</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2700"/>
|
||||
<source>(button inactive currently)</source>
|
||||
<translation>(bottone attualmente inattivo)</translation>
|
||||
<translation type="obsolete">(bottone attualmente inattivo)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2748"/>
|
||||
<source>Change your settings</source>
|
||||
<translation>Cambia le impostazioni</translation>
|
||||
<translation type="obsolete">Cambia le impostazioni</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="3321"/>
|
||||
<source>Close</source>
|
||||
<translation>Chiudi</translation>
|
||||
<translation type="obsolete">Chiudi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="3324"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation>Ctrl+Q</translation>
|
||||
<translation type="obsolete">Ctrl+Q</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="128"/>
|
||||
<source>Online</source>
|
||||
<comment>Button to set your status to 'Online'</comment>
|
||||
<translation type="obsolete">Online</translation>
|
||||
<translation>Online</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="130"/>
|
||||
<source>Away</source>
|
||||
<comment>Button to set your status to 'Away'</comment>
|
||||
<translation type="obsolete">Assente</translation>
|
||||
<translation>Assente</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="132"/>
|
||||
<source>Busy</source>
|
||||
<comment>Button to set your status to 'Busy'</comment>
|
||||
<translation type="obsolete">Occupato</translation>
|
||||
<translation>Occupato</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -27,51 +27,51 @@
|
|||
<translation>Отправить запрос на добавление в друзья</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="84"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="38"/>
|
||||
<source>Tox me maybe?</source>
|
||||
<comment>Default message in friend requests if the field is left blank. Write something appropriate!</comment>
|
||||
<translatorcomment>Вот таким нехитрым и незамысловатым образом решаются сложные переводчиские проблемы</translatorcomment>
|
||||
<translation>Добавь меня, а?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="92"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="93"/>
|
||||
<source>Please fill in a valid Tox ID</source>
|
||||
<comment>Tox ID of the friend you're sending a friend request to</comment>
|
||||
<translation>Пожалуйста, введите корректный Tox ID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="109"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="110"/>
|
||||
<source>This address does not exist</source>
|
||||
<comment>The DNS gives the Tox ID associated to toxme.se addresses</comment>
|
||||
<translation>Нет такого адреса</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="113"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="114"/>
|
||||
<source>Error while looking up DNS</source>
|
||||
<comment>The DNS gives the Tox ID associated to toxme.se addresses</comment>
|
||||
<translation>Ошибка при просмотре DNS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="119"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="120"/>
|
||||
<source>Unexpected number of text records</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Непредвиденное количество текстовых записей</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="125"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="126"/>
|
||||
<source>Unexpected number of values in text record</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Непредвиденное количество значений в текстовой записи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="132"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="133"/>
|
||||
<source>The DNS lookup does not contain any Tox ID</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>В ответе DNS ни одного Tox ID</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="138"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="144"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="139"/>
|
||||
<location filename="../widget/form/addfriendform.cpp" line="145"/>
|
||||
<source>The DNS lookup does not contain a valid Tox ID</source>
|
||||
<comment>Error with the DNS</comment>
|
||||
<translation>Ответ DNS не содержит корректных Tox ID</translation>
|
||||
|
@ -93,13 +93,13 @@
|
|||
<context>
|
||||
<name>ChatForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/chatform.cpp" line="291"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="283"/>
|
||||
<source>Send a file</source>
|
||||
<translation>Отправить файл</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/chatform.cpp" line="616"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="622"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="620"/>
|
||||
<location filename="../widget/form/chatform.cpp" line="626"/>
|
||||
<source>Save chat log</source>
|
||||
<translation>Сохранить лог чата</translation>
|
||||
</message>
|
||||
|
@ -115,11 +115,23 @@
|
|||
<context>
|
||||
<name>FileTransfertWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="270"/>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="281"/>
|
||||
<source>Save a file</source>
|
||||
<comment>Title of the file saving dialog</comment>
|
||||
<translation>Сохранить файл</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
||||
<source>Location not writable</source>
|
||||
<comment>Title of permissions popup</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
||||
<source>You do not have permission to write that location. Choose another, or cancel the save dialog.</source>
|
||||
<comment>text of permissions popup</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FilesForm</name>
|
||||
|
@ -130,12 +142,12 @@
|
|||
<translation>Переданные файлы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="30"/>
|
||||
<location filename="../widget/form/filesform.cpp" line="33"/>
|
||||
<source>Downloads</source>
|
||||
<translation>Загрузки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/filesform.cpp" line="31"/>
|
||||
<location filename="../widget/form/filesform.cpp" line="34"/>
|
||||
<source>Uploads</source>
|
||||
<translation>Выгрузки</translation>
|
||||
</message>
|
||||
|
@ -181,19 +193,19 @@
|
|||
<context>
|
||||
<name>FriendWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="87"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="86"/>
|
||||
<source>Copy friend ID</source>
|
||||
<comment>Menu to copy the Tox ID of that friend</comment>
|
||||
<translation>Копировать ID друга</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="88"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="87"/>
|
||||
<source>Invite in group</source>
|
||||
<comment>Menu to invite a friend in a groupchat</comment>
|
||||
<translation>Пригласить в группу</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/friendwidget.cpp" line="98"/>
|
||||
<location filename="../widget/friendwidget.cpp" line="97"/>
|
||||
<source>Remove friend</source>
|
||||
<comment>Menu to remove the friend from our friendlist</comment>
|
||||
<translation>Удалить друга</translation>
|
||||
|
@ -202,23 +214,23 @@
|
|||
<context>
|
||||
<name>GroupChatForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="48"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="49"/>
|
||||
<source>%1 users in chat</source>
|
||||
<comment>Number of users in chat</comment>
|
||||
<translation>%1 пользователей в чате</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="173"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="146"/>
|
||||
<source><Unknown></source>
|
||||
<translation><Неизвестно></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="242"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="215"/>
|
||||
<source>%1 users in chat</source>
|
||||
<translation>%1 пользователей в чате</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="261"/>
|
||||
<location filename="../widget/form/groupchatform.cpp" line="234"/>
|
||||
<source>Save chat log</source>
|
||||
<translation>Сохранить лог чата</translation>
|
||||
</message>
|
||||
|
@ -226,24 +238,72 @@
|
|||
<context>
|
||||
<name>GroupWidget</name>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="89"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="84"/>
|
||||
<source>Quit group</source>
|
||||
<comment>Menu to quit a groupchat</comment>
|
||||
<translation>Покинуть группу</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="54"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="146"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="141"/>
|
||||
<source>%1 users in chat</source>
|
||||
<translation>%1 пользователей в чате</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/groupwidget.cpp" line="56"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="148"/>
|
||||
<location filename="../widget/groupwidget.cpp" line="143"/>
|
||||
<source>0 users in chat</source>
|
||||
<translation>Ни одного пользователя в чате</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="20"/>
|
||||
<source>qTox</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="1918"/>
|
||||
<source>Your name</source>
|
||||
<translation type="unfinished">Ваше имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2000"/>
|
||||
<source>Your status</source>
|
||||
<translation type="unfinished">Ваш статус</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2566"/>
|
||||
<source>Add friends</source>
|
||||
<translation type="unfinished">Добавить друзей</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2592"/>
|
||||
<source>Create a group chat</source>
|
||||
<translation type="unfinished">Создать групповой чат</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2624"/>
|
||||
<source>View completed file transfers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="2656"/>
|
||||
<source>Change your settings</source>
|
||||
<translation type="unfinished">Изменить ваши настройки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="3238"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished">Закрыть</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.ui" line="3241"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation type="unfinished">Ctrl+Q</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SelfCamView</name>
|
||||
<message>
|
||||
|
@ -256,117 +316,123 @@
|
|||
<context>
|
||||
<name>SettingsForm</name>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="31"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="34"/>
|
||||
<source>User Settings</source>
|
||||
<comment>"Headline" of the window</comment>
|
||||
<translation>Пользовательские настройки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="34"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="37"/>
|
||||
<source>Name</source>
|
||||
<comment>Username/nick</comment>
|
||||
<translation>Имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="35"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="38"/>
|
||||
<source>Status</source>
|
||||
<comment>Status message</comment>
|
||||
<translation>Статус</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="36"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="39"/>
|
||||
<source>(click here to copy)</source>
|
||||
<comment>Click on this text to copy TID to clipboard</comment>
|
||||
<translation>(нажмите здесь чтобы скопировать)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="44"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="47"/>
|
||||
<source>Test video</source>
|
||||
<comment>Text on a button to test the video/webcam</comment>
|
||||
<translation>Проверить видео</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="45"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="48"/>
|
||||
<source>Enable IPv6 (recommended)</source>
|
||||
<comment>Text on a checkbox to enable IPv6</comment>
|
||||
<translation>Включить IPv6 (рекомендуется)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="47"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="50"/>
|
||||
<source>Use translations</source>
|
||||
<comment>Text on a checkbox to enable translations</comment>
|
||||
<translatorcomment>Так гораздо понятнее, чем «использовать переводы»</translatorcomment>
|
||||
<translation>Русскоязычный интерфейс</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="49"/>
|
||||
<location filename="../widget/form/settingsform.cpp" line="52"/>
|
||||
<source>Make Tox portable</source>
|
||||
<comment>Text on a checkbox to make qTox a portable application</comment>
|
||||
<translation>Портативный режим</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="54"/>
|
||||
<source>Save settings to the working directory instead of the usual conf dir</source>
|
||||
<comment>describes makeToxPortable checkbox</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/form/settingsform.cpp" line="56"/>
|
||||
<source>Smiley Pack</source>
|
||||
<comment>Text on smiley pack label</comment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Widget</name>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="42"/>
|
||||
<source>Tox</source>
|
||||
<translation>Tox</translation>
|
||||
<translation type="vanished">Tox</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="1951"/>
|
||||
<source>Your name</source>
|
||||
<translation>Ваше имя</translation>
|
||||
<translation type="vanished">Ваше имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2033"/>
|
||||
<source>Your status</source>
|
||||
<translation>Ваш статус</translation>
|
||||
<translation type="vanished">Ваш статус</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2610"/>
|
||||
<source>Add friends</source>
|
||||
<translation>Добавить друзей</translation>
|
||||
<translation type="vanished">Добавить друзей</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2652"/>
|
||||
<source>Create a group chat</source>
|
||||
<translation>Создать групповой чат</translation>
|
||||
<translation type="vanished">Создать групповой чат</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2700"/>
|
||||
<source>(button inactive currently)</source>
|
||||
<translation>(кнопка на данный момент неактивна)</translation>
|
||||
<translation type="vanished">(кнопка на данный момент неактивна)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="2748"/>
|
||||
<source>Change your settings</source>
|
||||
<translation>Изменить ваши настройки</translation>
|
||||
<translation type="vanished">Изменить ваши настройки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="3321"/>
|
||||
<source>Close</source>
|
||||
<translation>Закрыть</translation>
|
||||
<translation type="vanished">Закрыть</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget.ui" line="3324"/>
|
||||
<source>Ctrl+Q</source>
|
||||
<translation>Ctrl+Q</translation>
|
||||
<translation type="vanished">Ctrl+Q</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="128"/>
|
||||
<source>Online</source>
|
||||
<comment>Button to set your status to 'Online'</comment>
|
||||
<translation type="obsolete">В сети</translation>
|
||||
<translation type="unfinished">В сети</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="130"/>
|
||||
<source>Away</source>
|
||||
<comment>Button to set your status to 'Away'</comment>
|
||||
<translatorcomment>Вероятно, это не столь долгое путешествие</translatorcomment>
|
||||
<translation type="obsolete">Отошёл</translation>
|
||||
<translation type="unfinished">Отошёл</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../widget/widget.cpp" line="132"/>
|
||||
<source>Busy</source>
|
||||
<comment>Button to set your status to 'Busy'</comment>
|
||||
<translation type="obsolete">Занят</translation>
|
||||
<translation type="unfinished">Занят</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
BIN
ui/emoticonWidget/dot_page.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
ui/emoticonWidget/dot_page_current.png
Normal file
After Width: | Height: | Size: 263 B |
BIN
ui/emoticonWidget/dot_page_hover.png
Normal file
After Width: | Height: | Size: 237 B |
40
ui/emoticonWidget/emoticonWidget.css
Normal file
|
@ -0,0 +1,40 @@
|
|||
QPushButton
|
||||
{
|
||||
background-color: transparent;
|
||||
background-repeat: none;
|
||||
border: none;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
QRadioButton::indicator
|
||||
{
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
QRadioButton::indicator::unchecked
|
||||
{
|
||||
image: url(:/ui/emoticonWidget/dot_page.png);
|
||||
}
|
||||
|
||||
QRadioButton::indicator:unchecked:hover
|
||||
{
|
||||
image: url(:/ui/emoticonWidget/dot_page_hover.png);
|
||||
}
|
||||
|
||||
QRadioButton::indicator:unchecked:pressed
|
||||
{
|
||||
image: url(:/ui/emoticonWidget/dot_page_hover.png);
|
||||
}
|
||||
|
||||
QRadioButton::indicator::checked
|
||||
{
|
||||
image: url(:/ui/emoticonWidget/dot_page_current.png);
|
||||
}
|
||||
|
||||
QMenu
|
||||
{
|
||||
background-color: rgb(240,240,240); /* sets background of the menu */
|
||||
border: 1px solid;
|
||||
}
|
|
@ -1,34 +1,34 @@
|
|||
QScrollArea {
|
||||
background: #414141!important;
|
||||
background: #414141;
|
||||
}
|
||||
|
||||
QScrollBar:vertical {
|
||||
background: #414141!important;
|
||||
width: 14px!important;
|
||||
margin-top: 2px!important;
|
||||
margin-bottom: 2px!important;
|
||||
background: transparent;
|
||||
width: 14px;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
QScrollBar:handle:vertical {
|
||||
background: #1c1c1c!important;
|
||||
min-height: 20px!important;
|
||||
border-radius: 3px!important;
|
||||
margin-left: 3px!important;
|
||||
margin-right: 1px!important;
|
||||
background: rgba(18, 18, 18, 204);
|
||||
min-height: 20px;
|
||||
border-radius: 3px;
|
||||
margin-left: 3px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
QScrollBar:handle:vertical:hover {
|
||||
background: #2d2d2d!important;
|
||||
background: rgba(35, 35, 35, 204);
|
||||
}
|
||||
|
||||
QScrollBar:handle:vertical:pressed {
|
||||
background: #171717!important;
|
||||
background: rgba(13, 13, 13, 204);
|
||||
}
|
||||
|
||||
QScrollBar:add-line:vertical {height: 0px!important;subcontrol-position: bottom!important;subcontrol-origin: margin!important;}
|
||||
QScrollBar:add-line:vertical {height: 0px;subcontrol-position: bottom;subcontrol-origin: margin;}
|
||||
|
||||
QScrollBar:sub-line:vertical {height: 0px!important;subcontrol-position: top!important;subcontrol-origin: margin!important;}
|
||||
QScrollBar:sub-line:vertical {height: 0px;subcontrol-position: top;subcontrol-origin: margin;}
|
||||
|
||||
QScrollBar:add-page:vertical, QScrollBar::sub-page:vertical {
|
||||
background: none!important;
|
||||
background: none;
|
||||
}
|
||||
|
|
38
ui/micButton/micButton.css
Normal file
|
@ -0,0 +1,38 @@
|
|||
QPushButton#green
|
||||
{
|
||||
background-color: transparent;
|
||||
background-image: url(":/ui/micButton/micButton.png");
|
||||
background-repeat: none;
|
||||
border: none;
|
||||
width: 25px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
QPushButton#green:hover
|
||||
{
|
||||
background-image:url(":/ui/micButton/micButtonHover.png");
|
||||
}
|
||||
|
||||
QPushButton#red
|
||||
{
|
||||
background-color: transparent;
|
||||
background-image: url(":/ui/micButton/micButtonPressed.png");
|
||||
background-repeat: none;
|
||||
border: none;
|
||||
width: 25px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
QPushButton#grey
|
||||
{
|
||||
background-color: transparent;
|
||||
background-image: url(":/ui/micButton/micButtonDisabled.png");
|
||||
background-repeat: none;
|
||||
border: none;
|
||||
width: 25px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
QPushButton:focus {
|
||||
outline: none;
|
||||
}
|
BIN
ui/micButton/micButton.png
Normal file
After Width: | Height: | Size: 634 B |
BIN
ui/micButton/micButtonDisabled.png
Normal file
After Width: | Height: | Size: 523 B |
BIN
ui/micButton/micButtonHover.png
Normal file
After Width: | Height: | Size: 534 B |
BIN
ui/micButton/micButtonPressed.png
Normal file
After Width: | Height: | Size: 651 B |
23
ui/volButton/volButton.css
Normal file
|
@ -0,0 +1,23 @@
|
|||
QPushButton#green
|
||||
{
|
||||
background-color: transparent;
|
||||
background-image: url(":/ui/volButton/volButton.png");
|
||||
background-repeat: none;
|
||||
border: none;
|
||||
width: 25px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
QPushButton#green:hover
|
||||
{
|
||||
background-image: url(":/ui/volButton/volButtonHover.png");
|
||||
}
|
||||
|
||||
QPushButton#green:pressed
|
||||
{
|
||||
background-image: url(":/ui/volButton/volButtonPressed.png");
|
||||
}
|
||||
|
||||
QPushButton:focus {
|
||||
outline: none;
|
||||
}
|
BIN
ui/volButton/volButton.png
Normal file
After Width: | Height: | Size: 582 B |
BIN
ui/volButton/volButtonHover.png
Normal file
After Width: | Height: | Size: 607 B |
BIN
ui/volButton/volButtonPressed.png
Normal file
After Width: | Height: | Size: 630 B |
45
widget/adjustingscrollarea.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "adjustingscrollarea.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QLayout>
|
||||
#include <QScrollBar>
|
||||
#include <QDebug>
|
||||
|
||||
AdjustingScrollArea::AdjustingScrollArea(QWidget *parent) :
|
||||
QScrollArea(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AdjustingScrollArea::resizeEvent(QResizeEvent *ev)
|
||||
{
|
||||
updateGeometry();
|
||||
QScrollArea::resizeEvent(ev);
|
||||
}
|
||||
|
||||
QSize AdjustingScrollArea::sizeHint() const
|
||||
{
|
||||
if (widget())
|
||||
{
|
||||
int scrollbarWidth = verticalScrollBar()->isVisible() ? verticalScrollBar()->width() : 0;
|
||||
return widget()->sizeHint() + QSize(scrollbarWidth, 0);
|
||||
}
|
||||
|
||||
return QScrollArea::sizeHint();
|
||||
}
|
|
@ -14,23 +14,23 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef CLICKABLELABEL_H
|
||||
#define CLICKABLELABEL_H
|
||||
#ifndef ADJUSTINGSCROLLAREA_H
|
||||
#define ADJUSTINGSCROLLAREA_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QScrollArea>
|
||||
|
||||
class ClickableLabel : public QLabel
|
||||
class AdjustingScrollArea : public QScrollArea
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ClickableLabel(QWidget *parent = 0);
|
||||
explicit AdjustingScrollArea(QWidget *parent = 0);
|
||||
|
||||
virtual void resizeEvent(QResizeEvent *ev);
|
||||
virtual QSize sizeHint() const override;
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void mousePressEvent ( QMouseEvent * event );
|
||||
public slots:
|
||||
|
||||
};
|
||||
|
||||
#endif // CLICKABLELABEL_H
|
||||
#endif // ADJUSTINGSCROLLAREA_H
|
|
@ -15,59 +15,13 @@
|
|||
*/
|
||||
|
||||
#include "camera.h"
|
||||
#include <QVideoSurfaceFormat>
|
||||
#include <QMessageBox>
|
||||
#include <QVideoEncoderSettings>
|
||||
#include <QVideoEncoderSettingsControl>
|
||||
|
||||
static inline void fromYCbCrToRGB(
|
||||
uint8_t Y, uint8_t Cb, uint8_t Cr,
|
||||
uint8_t& R, uint8_t& G, uint8_t& B)
|
||||
{
|
||||
int r = Y + ((1436 * (Cr - 128)) >> 10),
|
||||
g = Y - ((354 * (Cb - 128) + 732 * (Cr - 128)) >> 10),
|
||||
b = Y + ((1814 * (Cb - 128)) >> 10);
|
||||
|
||||
if(r < 0) {
|
||||
r = 0;
|
||||
} else if(r > 255) {
|
||||
r = 255;
|
||||
}
|
||||
|
||||
if(g < 0) {
|
||||
g = 0;
|
||||
} else if(g > 255) {
|
||||
g = 255;
|
||||
}
|
||||
|
||||
if(b < 0) {
|
||||
b = 0;
|
||||
} else if(b > 255) {
|
||||
b = 255;
|
||||
}
|
||||
|
||||
R = static_cast<uint8_t>(r);
|
||||
G = static_cast<uint8_t>(g);
|
||||
B = static_cast<uint8_t>(b);
|
||||
}
|
||||
using namespace cv;
|
||||
|
||||
Camera::Camera()
|
||||
: refcount{0}, camera{new QCamera}
|
||||
: refcount{0}
|
||||
{
|
||||
camera->setCaptureMode(QCamera::CaptureVideo);
|
||||
camera->setViewfinder(this);
|
||||
|
||||
#if 0 // Crashes on Windows
|
||||
QMediaService *m = camera->service();
|
||||
QVideoEncoderSettingsControl *enc = m->requestControl<QVideoEncoderSettingsControl*>();
|
||||
QVideoEncoderSettings sets = enc->videoSettings();
|
||||
sets.setResolution(640, 480);
|
||||
enc->setVideoSettings(sets);
|
||||
#endif
|
||||
|
||||
connect(camera, SIGNAL(error(QCamera::Error)), this, SLOT(onCameraError(QCamera::Error)));
|
||||
|
||||
supportedFormats << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12 << QVideoFrame::Format_RGB32;
|
||||
}
|
||||
|
||||
void Camera::suscribe()
|
||||
|
@ -75,7 +29,7 @@ void Camera::suscribe()
|
|||
if (refcount <= 0)
|
||||
{
|
||||
refcount = 1;
|
||||
camera->start();
|
||||
cam.open(0);
|
||||
}
|
||||
else
|
||||
refcount++;
|
||||
|
@ -87,226 +41,76 @@ void Camera::unsuscribe()
|
|||
|
||||
if (refcount <= 0)
|
||||
{
|
||||
camera->stop();
|
||||
cam.release();
|
||||
refcount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QVideoFrame Camera::getLastFrame()
|
||||
Mat Camera::getLastFrame()
|
||||
{
|
||||
return lastFrame;
|
||||
Mat frame;
|
||||
cam >> frame;
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool Camera::start(const QVideoSurfaceFormat &format)
|
||||
{
|
||||
if(supportedFormats.contains(format.pixelFormat()))
|
||||
{
|
||||
frameFormat = format.pixelFormat();
|
||||
QAbstractVideoSurface::start(format);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(0, "Camera error", "The camera only supports rare video formats, can't use it");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Camera::present(const QVideoFrame &frame)
|
||||
{
|
||||
QVideoFrame frameMap(frame); // Basically a const_cast because shallow copies
|
||||
if (!frameMap.map(QAbstractVideoBuffer::ReadOnly))
|
||||
{
|
||||
qWarning() << "Camera::present: Unable to map frame";
|
||||
return false;
|
||||
}
|
||||
int w = frameMap.width(), h = frameMap.height();
|
||||
int bpl = frameMap.bytesPerLine(), size = frameMap.mappedBytes();
|
||||
QVideoFrame frameCopy(size, QSize(w, h), bpl, frameMap.pixelFormat());
|
||||
frameCopy.map(QAbstractVideoBuffer::WriteOnly);
|
||||
memcpy(frameCopy.bits(), frameMap.bits(), size);
|
||||
frameCopy.unmap();
|
||||
lastFrame = frameCopy;
|
||||
frameMap.unmap();
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QVideoFrame::PixelFormat> Camera::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
|
||||
{
|
||||
if (handleType == QAbstractVideoBuffer::NoHandle)
|
||||
return supportedFormats;
|
||||
else
|
||||
return QList<QVideoFrame::PixelFormat>();
|
||||
}
|
||||
|
||||
void Camera::onCameraError(QCamera::Error value)
|
||||
{
|
||||
QMessageBox::warning(0,"Camera error",QString("Error %1 : %2")
|
||||
.arg(value).arg(camera->errorString()));
|
||||
}
|
||||
|
||||
bool Camera::isFormatSupported(const QVideoSurfaceFormat& format) const
|
||||
{
|
||||
if (format.pixelFormat() == 0)
|
||||
{
|
||||
//QMessageBox::warning(0, "Camera eror","The camera's video format is not supported !");
|
||||
return QAbstractVideoSurface::isFormatSupported(format);
|
||||
}
|
||||
else if(supportedFormats.contains(format.pixelFormat()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(0, tr("Camera eror"),
|
||||
tr("Camera format %1 not supported, can't use the camera")
|
||||
.arg(format.pixelFormat()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QImage Camera::getLastImage()
|
||||
{
|
||||
if (!lastFrame.map(QAbstractVideoBuffer::ReadOnly))
|
||||
Mat3b src = getLastFrame();
|
||||
QImage dest(src.cols, src.rows, QImage::Format_ARGB32);
|
||||
for (int y = 0; y < src.rows; ++y)
|
||||
{
|
||||
qWarning() << "Camera::getLastImage: Error maping last frame";
|
||||
return QImage();
|
||||
const cv::Vec3b *srcrow = src[y];
|
||||
QRgb *destrow = (QRgb*)dest.scanLine(y);
|
||||
for (int x = 0; x < src.cols; ++x)
|
||||
destrow[x] = qRgba(srcrow[x][2], srcrow[x][1], srcrow[x][0], 255);
|
||||
}
|
||||
int w = lastFrame.width(), h = lastFrame.height();
|
||||
int bpl = lastFrame.bytesPerLine(), cxbpl = bpl/2;
|
||||
QImage img(w, h, QImage::Format_RGB32);
|
||||
|
||||
if (frameFormat == QVideoFrame::Format_YUV420P)
|
||||
{
|
||||
uint8_t* yData = lastFrame.bits();
|
||||
uint8_t* uData = yData + (bpl * h);
|
||||
uint8_t* vData = uData + (bpl * h / 4);
|
||||
for (int i = 0; i< h; i++)
|
||||
{
|
||||
uint32_t* scanline = (uint32_t*)img.scanLine(i);
|
||||
for (int j=0; j < bpl; j++)
|
||||
{
|
||||
uint8_t Y = yData[i*bpl + j];
|
||||
uint8_t U = uData[i/2*cxbpl + j/2];
|
||||
uint8_t V = vData[i/2*cxbpl + j/2];
|
||||
|
||||
uint8_t R, G, B;
|
||||
fromYCbCrToRGB(Y, U, V, R, G, B);
|
||||
|
||||
scanline[j] = (0xFF<<24) + (R<<16) + (G<<8) + B;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (frameFormat == QVideoFrame::Format_YV12)
|
||||
{
|
||||
uint8_t* yData = lastFrame.bits();
|
||||
uint8_t* vData = yData + (bpl * h);
|
||||
uint8_t* uData = vData + (bpl * h / 4);
|
||||
for (int i = 0; i< h; i++)
|
||||
{
|
||||
uint32_t* scanline = (uint32_t*)img.scanLine(i);
|
||||
for (int j=0; j < bpl; j++)
|
||||
{
|
||||
uint8_t Y = yData[i*bpl + j];
|
||||
uint8_t U = uData[i/2*cxbpl + j/2];
|
||||
uint8_t V = vData[i/2*cxbpl + j/2];
|
||||
|
||||
uint8_t R, G, B;
|
||||
fromYCbCrToRGB(Y, U, V, R, G, B);
|
||||
|
||||
scanline[j] = (0xFF<<24) + (R<<16) + (G<<8) + B;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (frameFormat == QVideoFrame::Format_RGB32)
|
||||
{
|
||||
memcpy(img.bits(), lastFrame.bits(), bpl*h);
|
||||
}
|
||||
|
||||
lastFrame.unmap();
|
||||
return img;
|
||||
return dest;
|
||||
}
|
||||
|
||||
vpx_image Camera::getLastVPXImage()
|
||||
{
|
||||
Mat3b frame = getLastFrame();
|
||||
vpx_image img;
|
||||
img.w = img.h = 0;
|
||||
if (!lastFrame.isValid())
|
||||
return img;
|
||||
if (!lastFrame.map(QAbstractVideoBuffer::ReadOnly))
|
||||
{
|
||||
qWarning() << "Camera::getLastVPXImage: Error maping last frame";
|
||||
return img;
|
||||
}
|
||||
int w = lastFrame.width(), h = lastFrame.height();
|
||||
int bpl = lastFrame.bytesPerLine();
|
||||
int w = frame.size().width, h = frame.size().height;
|
||||
vpx_img_alloc(&img, VPX_IMG_FMT_I420, w, h, 1); // I420 == YUV420P, same as YV12 with U and V switched
|
||||
|
||||
if (frameFormat == QVideoFrame::Format_YUV420P)
|
||||
size_t i=0, j=0;
|
||||
for( int line = 0; line < h; ++line )
|
||||
{
|
||||
uint8_t* yData = lastFrame.bits();
|
||||
uint8_t* uData = yData + (bpl * h);
|
||||
uint8_t* vData = uData + (bpl * h / 4);
|
||||
img.planes[VPX_PLANE_Y] = yData;
|
||||
img.planes[VPX_PLANE_U] = uData;
|
||||
img.planes[VPX_PLANE_V] = vData;
|
||||
}
|
||||
else if (frameFormat == QVideoFrame::Format_YV12)
|
||||
{
|
||||
uint8_t* yData = lastFrame.bits();
|
||||
uint8_t* uData = yData + (bpl * h);
|
||||
uint8_t* vData = uData + (bpl * h / 4);
|
||||
img.planes[VPX_PLANE_Y] = yData;
|
||||
img.planes[VPX_PLANE_U] = vData;
|
||||
img.planes[VPX_PLANE_V] = uData;
|
||||
}
|
||||
else if (frameFormat == QVideoFrame::Format_RGB32 || frameFormat == QVideoFrame::Format_ARGB32)
|
||||
{
|
||||
qWarning() << "Camera::getLastVPXImage: Using experimental RGB32 conversion code";
|
||||
uint8_t* rgb = lastFrame.bits();
|
||||
size_t i=0, j=0;
|
||||
|
||||
for( size_t line = 0; line < h; ++line )
|
||||
const cv::Vec3b *srcrow = frame[line];
|
||||
if( !(line % 2) )
|
||||
{
|
||||
if( !(line % 2) )
|
||||
for( int x = 0; x < w; x += 2 )
|
||||
{
|
||||
for( size_t x = 0; x < w; x += 2 )
|
||||
{
|
||||
uint8_t r = rgb[4 * i + 1];
|
||||
uint8_t g = rgb[4 * i + 2];
|
||||
uint8_t b = rgb[4 * i + 3];
|
||||
uint8_t r = srcrow[x][2];
|
||||
uint8_t g = srcrow[x][1];
|
||||
uint8_t b = srcrow[x][0];
|
||||
|
||||
img.planes[VPX_PLANE_Y][i] = ((66*r + 129*g + 25*b) >> 8) + 16;
|
||||
img.planes[VPX_PLANE_U][j] = ((-38*r + -74*g + 112*b) >> 8) + 128;
|
||||
img.planes[VPX_PLANE_V][j] = ((112*r + -94*g + -18*b) >> 8) + 128;
|
||||
i++;
|
||||
j++;
|
||||
img.planes[VPX_PLANE_Y][i] = ((66*r + 129*g + 25*b) >> 8) + 16;
|
||||
img.planes[VPX_PLANE_V][j] = ((-38*r + -74*g + 112*b) >> 8) + 128;
|
||||
img.planes[VPX_PLANE_U][j] = ((112*r + -94*g + -18*b) >> 8) + 128;
|
||||
i++;
|
||||
j++;
|
||||
|
||||
r = rgb[4 * i + 1];
|
||||
g = rgb[4 * i + 2];
|
||||
b = rgb[4 * i + 3];
|
||||
|
||||
img.planes[VPX_PLANE_Y][i] = ((66*r + 129*g + 25*b) >> 8) + 16;
|
||||
i++;
|
||||
}
|
||||
r = srcrow[x+1][2];
|
||||
g = srcrow[x+1][1];
|
||||
b = srcrow[x+1][0];
|
||||
img.planes[VPX_PLANE_Y][i] = ((66*r + 129*g + 25*b) >> 8) + 16;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int x = 0; x < w; x += 1 )
|
||||
{
|
||||
for( size_t x = 0; x < w; x += 1 )
|
||||
{
|
||||
uint8_t r = rgb[4 * i + 1];
|
||||
uint8_t g = rgb[4 * i + 2];
|
||||
uint8_t b = rgb[4 * i + 3];
|
||||
uint8_t r = srcrow[x][2];
|
||||
uint8_t g = srcrow[x][1];
|
||||
uint8_t b = srcrow[x][0];
|
||||
|
||||
img.planes[VPX_PLANE_Y][i] = ((66*r + 129*g + 25*b) >> 8) + 16;
|
||||
i++;
|
||||
}
|
||||
img.planes[VPX_PLANE_Y][i] = ((66*r + 129*g + 25*b) >> 8) + 16;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastFrame.unmap();
|
||||
return img;
|
||||
|
||||
}
|
||||
|
|
|
@ -17,47 +17,29 @@
|
|||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
|
||||
#include <QCamera>
|
||||
#include <QVideoFrame>
|
||||
#include <QAbstractVideoSurface>
|
||||
#include <QImage>
|
||||
#include "vpx/vpx_image.h"
|
||||
#include "opencv2/opencv.hpp"
|
||||
|
||||
/**
|
||||
* This class is a wrapper to share a camera's captured video frames
|
||||
* In Qt cameras normally only send their frames to a single output at a time
|
||||
* So you can't, for example, send the frames over the network
|
||||
* and output them to a widget on the screen at the same time
|
||||
*
|
||||
* Instead this class allows objects to surscribe and unsuscribe, starting
|
||||
* the camera only when needed, and giving access to the last frame
|
||||
* It allows objects to suscribe and unsuscribe to the stream, starting
|
||||
* the camera only when needed, and giving access to the last frames
|
||||
**/
|
||||
|
||||
class Camera : private QAbstractVideoSurface
|
||||
class Camera
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Camera();
|
||||
void suscribe(); ///< Call this once before trying to get frames
|
||||
void unsuscribe(); ///< Call this once when you don't need frames anymore
|
||||
QVideoFrame getLastFrame(); ///< Get the last captured frame
|
||||
cv::Mat getLastFrame(); ///< Get the last captured frame
|
||||
QImage getLastImage(); ///< Convert the last frame to a QImage (can be expensive !)
|
||||
vpx_image getLastVPXImage(); ///< Convert the last frame to a vpx_image (can be expensive !)
|
||||
bool isFormatSupported(const QVideoSurfaceFormat & format) const;
|
||||
|
||||
private slots:
|
||||
void onCameraError(QCamera::Error value);
|
||||
|
||||
private:
|
||||
bool start(const QVideoSurfaceFormat &format);
|
||||
bool present(const QVideoFrame &frame);
|
||||
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
|
||||
|
||||
private:
|
||||
int refcount; ///< Number of users suscribed to the camera
|
||||
QCamera *camera;
|
||||
QVideoFrame lastFrame;
|
||||
int frameFormat;
|
||||
QList<QVideoFrame::PixelFormat> supportedFormats;
|
||||
cv::VideoCapture cam; ///< OpenCV camera capture opbject
|
||||
};
|
||||
|
||||
#endif // CAMERA_H
|
||||
|
|
142
widget/croppinglabel.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "croppinglabel.h"
|
||||
#include <QResizeEvent>
|
||||
|
||||
CroppingLabel::CroppingLabel(QWidget* parent)
|
||||
: QLabel(parent)
|
||||
, blockPaintEvents(false)
|
||||
, editable(false)
|
||||
, elideMode(Qt::ElideRight)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
|
||||
textEdit = new QLineEdit(this);
|
||||
textEdit->hide();
|
||||
|
||||
installEventFilter(this);
|
||||
textEdit->installEventFilter(this);
|
||||
}
|
||||
|
||||
void CroppingLabel::setEditable(bool editable)
|
||||
{
|
||||
this->editable = editable;
|
||||
|
||||
if (editable)
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
else
|
||||
unsetCursor();
|
||||
}
|
||||
|
||||
void CroppingLabel::setEdlideMode(Qt::TextElideMode elide)
|
||||
{
|
||||
elideMode = elide;
|
||||
}
|
||||
|
||||
void CroppingLabel::setText(const QString& text)
|
||||
{
|
||||
origText = text.trimmed();
|
||||
setElidedText();
|
||||
}
|
||||
|
||||
void CroppingLabel::resizeEvent(QResizeEvent* ev)
|
||||
{
|
||||
setElidedText();
|
||||
textEdit->resize(ev->size());
|
||||
|
||||
QLabel::resizeEvent(ev);
|
||||
}
|
||||
|
||||
QSize CroppingLabel::sizeHint() const
|
||||
{
|
||||
return QSize(0, QLabel::sizeHint().height());
|
||||
}
|
||||
|
||||
QSize CroppingLabel::minimumSizeHint() const
|
||||
{
|
||||
return QSize(fontMetrics().width("..."), QLabel::minimumSizeHint().height());
|
||||
}
|
||||
|
||||
void CroppingLabel::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
if (editable)
|
||||
showTextEdit();
|
||||
|
||||
emit clicked();
|
||||
|
||||
QLabel::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
bool CroppingLabel::eventFilter(QObject *obj, QEvent *e)
|
||||
{
|
||||
// catch paint events if needed
|
||||
if (obj == this)
|
||||
{
|
||||
if (e->type() == QEvent::Paint && blockPaintEvents)
|
||||
return true;
|
||||
}
|
||||
|
||||
// events fired by the QLineEdit
|
||||
if (obj == textEdit)
|
||||
{
|
||||
if (e->type() == QEvent::KeyPress)
|
||||
{
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
|
||||
if (keyEvent->key() == Qt::Key_Return)
|
||||
hideTextEdit(true);
|
||||
|
||||
if (keyEvent->key() == Qt::Key_Escape)
|
||||
hideTextEdit(false);
|
||||
}
|
||||
|
||||
if (e->type() == QEvent::FocusOut)
|
||||
hideTextEdit(true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CroppingLabel::setElidedText()
|
||||
{
|
||||
QString elidedText = fontMetrics().elidedText(origText, elideMode, width());
|
||||
if (elidedText != origText)
|
||||
setToolTip(origText);
|
||||
else
|
||||
setToolTip(QString());
|
||||
|
||||
QLabel::setText(elidedText);
|
||||
}
|
||||
|
||||
void CroppingLabel::hideTextEdit(bool acceptText)
|
||||
{
|
||||
if (acceptText)
|
||||
{
|
||||
emit textChanged(textEdit->text(), origText);
|
||||
setText(textEdit->text());
|
||||
}
|
||||
|
||||
textEdit->hide();
|
||||
blockPaintEvents = false;
|
||||
}
|
||||
|
||||
void CroppingLabel::showTextEdit()
|
||||
{
|
||||
blockPaintEvents = true;
|
||||
textEdit->show();
|
||||
textEdit->setFocus();
|
||||
textEdit->setText(origText);
|
||||
}
|
56
widget/croppinglabel.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef CROPPINGLABEL_H
|
||||
#define CROPPINGLABEL_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
|
||||
class CroppingLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CroppingLabel(QWidget *parent = 0);
|
||||
|
||||
void setEditable(bool editable);
|
||||
void setEdlideMode(Qt::TextElideMode elide);
|
||||
|
||||
virtual void setText(const QString& text);
|
||||
virtual void resizeEvent(QResizeEvent *ev);
|
||||
virtual QSize sizeHint() const;
|
||||
virtual QSize minimumSizeHint() const;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *e);
|
||||
virtual bool eventFilter(QObject *obj, QEvent *e);
|
||||
|
||||
signals:
|
||||
void textChanged(QString newText, QString oldText);
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void setElidedText();
|
||||
void hideTextEdit(bool acceptText);
|
||||
void showTextEdit();
|
||||
|
||||
private:
|
||||
QString origText;
|
||||
QLineEdit* textEdit;
|
||||
bool blockPaintEvents;
|
||||
bool editable;
|
||||
Qt::TextElideMode elideMode;
|
||||
};
|
||||
|
||||
#endif // CROPPINGLABEL_H
|
136
widget/emoticonswidget.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "emoticonswidget.h"
|
||||
#include "smileypack.h"
|
||||
#include "style.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QFile>
|
||||
#include <QLayout>
|
||||
#include <QGridLayout>
|
||||
|
||||
EmoticonsWidget::EmoticonsWidget(QWidget *parent) :
|
||||
QMenu(parent)
|
||||
{
|
||||
setStyleSheet(Style::get(":/ui/emoticonWidget/emoticonWidget.css"));
|
||||
setLayout(&layout);
|
||||
layout.addWidget(&stack);
|
||||
|
||||
QWidget* pageButtonsContainer = new QWidget;
|
||||
QHBoxLayout* buttonLayout = new QHBoxLayout;
|
||||
pageButtonsContainer->setLayout(buttonLayout);
|
||||
|
||||
layout.addWidget(pageButtonsContainer);
|
||||
|
||||
const int maxCols = 5;
|
||||
const int maxRows = 3;
|
||||
const int itemsPerPage = maxRows * maxCols;
|
||||
|
||||
const QList<QStringList>& emoticons = SmileyPack::getInstance().getEmoticons();
|
||||
int itemCount = emoticons.size();
|
||||
int pageCount = (itemCount / itemsPerPage) + 1;
|
||||
int currPage = 0;
|
||||
int currItem = 0;
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
// create pages
|
||||
buttonLayout->addStretch();
|
||||
for (int i = 0; i < pageCount; i++)
|
||||
{
|
||||
QGridLayout* pageLayout = new QGridLayout;
|
||||
pageLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), maxRows, 0);
|
||||
pageLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, maxCols);
|
||||
|
||||
QWidget* page = new QWidget;
|
||||
page->setLayout(pageLayout);
|
||||
stack.addWidget(page);
|
||||
|
||||
// page buttons are only needed if there is more than 1 page
|
||||
if (pageCount > 1)
|
||||
{
|
||||
QRadioButton* pageButton = new QRadioButton;
|
||||
pageButton->setProperty("pageIndex", i);
|
||||
pageButton->setChecked(i == 0);
|
||||
buttonLayout->addWidget(pageButton);
|
||||
|
||||
connect(pageButton, &QRadioButton::clicked, this, &EmoticonsWidget::onPageButtonClicked);
|
||||
}
|
||||
}
|
||||
buttonLayout->addStretch();
|
||||
|
||||
for (const QStringList& set : emoticons)
|
||||
{
|
||||
QPushButton* button = new QPushButton;
|
||||
button->setIcon(SmileyPack::getInstance().getAsIcon(set[0]));
|
||||
button->setToolTip(set.join(" "));
|
||||
button->setProperty("sequence", set[0]);
|
||||
button->setFlat(true);
|
||||
|
||||
connect(button, &QPushButton::clicked, this, &EmoticonsWidget::onSmileyClicked);
|
||||
|
||||
qobject_cast<QGridLayout*>(stack.widget(currPage)->layout())->addWidget(button, row, col);
|
||||
|
||||
col++;
|
||||
currItem++;
|
||||
|
||||
// next row
|
||||
if (col >= maxCols)
|
||||
{
|
||||
col = 0;
|
||||
row++;
|
||||
}
|
||||
|
||||
// next page
|
||||
if (currItem >= itemsPerPage)
|
||||
{
|
||||
row = 0;
|
||||
currItem = 0;
|
||||
currPage++;
|
||||
}
|
||||
}
|
||||
|
||||
// calculates sizeHint
|
||||
layout.activate();
|
||||
}
|
||||
|
||||
void EmoticonsWidget::onSmileyClicked()
|
||||
{
|
||||
// hide the QMenu
|
||||
QMenu::hide();
|
||||
|
||||
// emit insert emoticon
|
||||
QWidget* sender = qobject_cast<QWidget*>(QObject::sender());
|
||||
if (sender)
|
||||
emit insertEmoticon(' ' + sender->property("sequence").toString() + ' ');
|
||||
}
|
||||
|
||||
void EmoticonsWidget::onPageButtonClicked()
|
||||
{
|
||||
QWidget* sender = qobject_cast<QRadioButton*>(QObject::sender());
|
||||
if (sender)
|
||||
{
|
||||
int page = sender->property("pageIndex").toInt();
|
||||
stack.setCurrentIndex(page);
|
||||
}
|
||||
}
|
||||
|
||||
QSize EmoticonsWidget::sizeHint() const
|
||||
{
|
||||
return layout.sizeHint();
|
||||
}
|
|
@ -14,29 +14,32 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef AUDIOBUFFER_H
|
||||
#define AUDIOBUFFER_H
|
||||
#ifndef EMOTICONSWIDGET_H
|
||||
#define EMOTICONSWIDGET_H
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QByteArray>
|
||||
#include <QMutex>
|
||||
#include <QMenu>
|
||||
#include <QStackedWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class AudioBuffer : public QIODevice
|
||||
class EmoticonsWidget : public QMenu
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AudioBuffer();
|
||||
~AudioBuffer();
|
||||
explicit EmoticonsWidget(QWidget *parent = 0);
|
||||
|
||||
qint64 readData(char *data, qint64 maxlen);
|
||||
qint64 writeData(const char *data, qint64 len);
|
||||
qint64 bytesAvailable() const;
|
||||
qint64 bufferSize() const;
|
||||
void clear();
|
||||
signals:
|
||||
void insertEmoticon(QString str);
|
||||
|
||||
private slots:
|
||||
void onSmileyClicked();
|
||||
void onPageButtonClicked();
|
||||
|
||||
private:
|
||||
QByteArray buffer;
|
||||
mutable QMutex bufferMutex;
|
||||
QStackedWidget stack;
|
||||
QVBoxLayout layout;
|
||||
|
||||
public:
|
||||
virtual QSize sizeHint() const;
|
||||
};
|
||||
|
||||
#endif // AUDIOBUFFER_H
|
||||
#endif // EMOTICONSWIDGET_H
|
|
@ -18,6 +18,7 @@
|
|||
#include "widget.h"
|
||||
#include "core.h"
|
||||
#include "math.h"
|
||||
#include "style.h"
|
||||
#include <QFileDialog>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
|
@ -36,10 +37,7 @@ FileTransfertWidget::FileTransfertWidget(ToxFile File)
|
|||
QFont prettysmall;
|
||||
prettysmall.setPixelSize(10);
|
||||
this->setObjectName("default");
|
||||
QFile f0(":/ui/fileTransferWidget/fileTransferWidget.css");
|
||||
f0.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream fileTransfertWidgetStylesheet(&f0);
|
||||
this->setStyleSheet(fileTransfertWidgetStylesheet.readAll());
|
||||
this->setStyleSheet(Style::get(":/ui/fileTransferWidget/fileTransferWidget.css"));
|
||||
QPalette greybg;
|
||||
greybg.setColor(QPalette::Window, QColor(209,209,209));
|
||||
greybg.setColor(QPalette::Base, QColor(150,150,150));
|
||||
|
@ -71,20 +69,9 @@ FileTransfertWidget::FileTransfertWidget(ToxFile File)
|
|||
buttonWidget->setAutoFillBackground(true);
|
||||
buttonWidget->setLayout(buttonLayout);
|
||||
|
||||
QFile f1(":/ui/stopFileButton/style.css");
|
||||
f1.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream stopFileButtonStylesheetStream(&f1);
|
||||
stopFileButtonStylesheet = stopFileButtonStylesheetStream.readAll();
|
||||
|
||||
QFile f2(":/ui/pauseFileButton/style.css");
|
||||
f2.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream pauseFileButtonStylesheetStream(&f2);
|
||||
pauseFileButtonStylesheet = pauseFileButtonStylesheetStream.readAll();
|
||||
|
||||
QFile f3(":/ui/acceptFileButton/style.css");
|
||||
f3.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream acceptFileButtonStylesheetStream(&f3);
|
||||
acceptFileButtonStylesheet = acceptFileButtonStylesheetStream.readAll();
|
||||
stopFileButtonStylesheet = Style::get(":/ui/stopFileButton/style.css");
|
||||
pauseFileButtonStylesheet = Style::get(":/ui/pauseFileButton/style.css");
|
||||
acceptFileButtonStylesheet = Style::get(":/ui/acceptFileButton/style.css");
|
||||
|
||||
topright->setStyleSheet(stopFileButtonStylesheet);
|
||||
if (File.direction == ToxFile::SENDING)
|
||||
|
@ -151,13 +138,13 @@ FileTransfertWidget::FileTransfertWidget(ToxFile File)
|
|||
buttonLayout->setSpacing(0);
|
||||
}
|
||||
|
||||
QString FileTransfertWidget::getHumanReadableSize(int size)
|
||||
QString FileTransfertWidget::getHumanReadableSize(unsigned long long size)
|
||||
{
|
||||
static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"};
|
||||
int exp = 0;
|
||||
if (size)
|
||||
exp = std::min( (int) (log(size) / log(1024)), (int) (sizeof(suffix) / sizeof(suffix[0]) - 1));
|
||||
return QString().setNum(size / pow(1024, exp),'g',3).append(suffix[exp]);
|
||||
return QString().setNum(size / pow(1024, exp),'f',2).append(suffix[exp]);
|
||||
}
|
||||
|
||||
void FileTransfertWidget::onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction)
|
||||
|
@ -174,7 +161,7 @@ void FileTransfertWidget::onFileTransferInfo(int FriendId, int FileNum, int64_t
|
|||
qWarning() << "FileTransfertWidget::onFileTransferInfo: Negative transfer speed !";
|
||||
diff = 0;
|
||||
}
|
||||
int rawspeed = diff / timediff;
|
||||
long rawspeed = diff / timediff;
|
||||
speed->setText(getHumanReadableSize(rawspeed)+"/s");
|
||||
size->setText(getHumanReadableSize(Filesize));
|
||||
if (!rawspeed)
|
||||
|
@ -184,10 +171,15 @@ void FileTransfertWidget::onFileTransferInfo(int FriendId, int FileNum, int64_t
|
|||
etaTime = etaTime.addSecs(etaSecs);
|
||||
eta->setText(etaTime.toString("mm:ss"));
|
||||
if (!Filesize)
|
||||
{
|
||||
progress->setValue(0);
|
||||
qDebug() << QString("FT: received %1 bytes of an empty file, stop sending sequential devices, zetok!").arg(BytesSent);
|
||||
}
|
||||
else
|
||||
{
|
||||
progress->setValue(BytesSent*100/Filesize);
|
||||
qDebug() << QString("FT: received %1/%2 bytes, progress is %3%").arg(BytesSent).arg(Filesize).arg(BytesSent*100/Filesize);
|
||||
qDebug() << QString("FT: received %1/%2 bytes, progress is %3%").arg(BytesSent).arg(Filesize).arg(BytesSent*100/Filesize);
|
||||
}
|
||||
lastUpdate = newtime;
|
||||
lastBytesSent = BytesSent;
|
||||
}
|
||||
|
@ -286,7 +278,7 @@ void FileTransfertWidget::acceptRecvRequest()
|
|||
QString path;
|
||||
while (true)
|
||||
{
|
||||
path = QFileDialog::getSaveFileName(0,tr("Save a file","Title of the file saving dialog"),QDir::currentPath()+'/'+filename->text());
|
||||
path = QFileDialog::getSaveFileName(this, tr("Save a file","Title of the file saving dialog"), QDir::current().filePath(filename->text()));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
else
|
||||
|
@ -297,7 +289,7 @@ void FileTransfertWidget::acceptRecvRequest()
|
|||
if (isWritable(path))
|
||||
break;
|
||||
else
|
||||
QMessageBox::warning(0, tr("Location not writable","Title of permissions popup"), tr("You do not have permission to write that location. Choose another, or cancel the save dialog.", "text of permissions popup"));
|
||||
QMessageBox::warning(this, tr("Location not writable","Title of permissions popup"), tr("You do not have permission to write that location. Choose another, or cancel the save dialog.", "text of permissions popup"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ private slots:
|
|||
void pauseResumeSend();
|
||||
|
||||
private:
|
||||
QString getHumanReadableSize(int size);
|
||||
QString getHumanReadableSize(unsigned long long size);
|
||||
|
||||
private:
|
||||
QLabel *pic, *filename, *size, *speed, *eta;
|
||||
|
|
|
@ -35,6 +35,7 @@ AddFriendForm::AddFriendForm() : dns(this)
|
|||
toxIdLabel.setText(tr("Tox ID","Tox ID of the person you're sending a friend request to"));
|
||||
messageLabel.setText(tr("Message","The message you send in friend requests"));
|
||||
sendButton.setText(tr("Send friend request"));
|
||||
message.setPlaceholderText(tr("Tox me maybe?","Default message in friend requests if the field is left blank. Write something appropriate!"));
|
||||
|
||||
main->setLayout(&layout);
|
||||
layout.addWidget(&toxIdLabel);
|
||||
|
@ -56,7 +57,7 @@ AddFriendForm::~AddFriendForm()
|
|||
main->deleteLater();
|
||||
}
|
||||
|
||||
void AddFriendForm::show(Ui::Widget &ui)
|
||||
void AddFriendForm::show(Ui::MainWindow &ui)
|
||||
{
|
||||
ui.mainContent->layout()->addWidget(main);
|
||||
ui.mainHead->layout()->addWidget(head);
|
||||
|
@ -81,7 +82,7 @@ void AddFriendForm::showWarning(const QString &message) const
|
|||
QString AddFriendForm::getMessage() const
|
||||
{
|
||||
const QString msg = message.toPlainText();
|
||||
return !msg.isEmpty() ? msg : tr("Tox me maybe?","Default message in friend requests if the field is left blank. Write something appropriate!");
|
||||
return !msg.isEmpty() ? msg : message.placeholderText();
|
||||
}
|
||||
|
||||
void AddFriendForm::onSendTriggered()
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#ifndef ADDFRIENDFORM_H
|
||||
#define ADDFRIENDFORM_H
|
||||
|
||||
#include "ui_widget.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
|
@ -33,7 +33,7 @@ public:
|
|||
AddFriendForm();
|
||||
~AddFriendForm();
|
||||
|
||||
void show(Ui::Widget& ui);
|
||||
void show(Ui::MainWindow &ui);
|
||||
bool isToxId(const QString& value) const;
|
||||
void showWarning(const QString& message) const;
|
||||
QString getMessage() const;
|
||||
|
|
|
@ -16,14 +16,20 @@
|
|||
|
||||
#include "chatform.h"
|
||||
#include "friend.h"
|
||||
#include "smileypack.h"
|
||||
#include "widget/friendwidget.h"
|
||||
#include "widget/widget.h"
|
||||
#include "widget/filetransfertwidget.h"
|
||||
#include "widget/emoticonswidget.h"
|
||||
#include "style.h"
|
||||
#include <QFont>
|
||||
#include <QTime>
|
||||
#include <QScrollBar>
|
||||
#include <QFileDialog>
|
||||
#include <QMenu>
|
||||
#include <QWidgetAction>
|
||||
#include <QGridLayout>
|
||||
#include <QMessageBox>
|
||||
|
||||
ChatForm::ChatForm(Friend* chatFriend)
|
||||
: f(chatFriend), curRow{0}, lockSliderToBottom{true}
|
||||
|
@ -31,12 +37,16 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
main = new QWidget(), head = new QWidget(), chatAreaWidget = new QWidget();
|
||||
name = new QLabel(), avatar = new QLabel(), statusMessage = new QLabel();
|
||||
headLayout = new QHBoxLayout(), mainFootLayout = new QHBoxLayout();
|
||||
headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout(), footButtonsSmall = new QVBoxLayout();
|
||||
headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout(),
|
||||
footButtonsSmall = new QVBoxLayout(), volMicLayout = new QVBoxLayout();
|
||||
mainChatLayout = new QGridLayout();
|
||||
msgEdit = new ChatTextEdit();
|
||||
sendButton = new QPushButton(), fileButton = new QPushButton(), emoteButton = new QPushButton(), callButton = new QPushButton(), videoButton = new QPushButton();
|
||||
sendButton = new QPushButton(), fileButton = new QPushButton(), emoteButton = new QPushButton(),
|
||||
callButton = new QPushButton(), videoButton = new QPushButton(),
|
||||
volButton = new QPushButton(), micButton = new QPushButton();
|
||||
chatArea = new QScrollArea();
|
||||
netcam = new NetCamView();
|
||||
audioInputFlag = false;
|
||||
|
||||
QFont bold;
|
||||
bold.setBold(true);
|
||||
|
@ -49,16 +59,8 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
avatar->setPixmap(QPixmap(":/img/contact_dark.png"));
|
||||
|
||||
chatAreaWidget->setLayout(mainChatLayout);
|
||||
QString chatAreaStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/chatArea/chatArea.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream chatAreaStylesheetStream(&f);
|
||||
chatAreaStylesheet = chatAreaStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
chatArea->setStyleSheet(chatAreaStylesheet);
|
||||
chatAreaWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css"));
|
||||
|
||||
chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
chatArea->setWidgetResizable(true);
|
||||
chatArea->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
@ -69,76 +71,43 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
|
||||
footButtonsSmall->setSpacing(2);
|
||||
|
||||
QString msgEditStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/msgEdit/msgEdit.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream msgEditStylesheetStream(&f);
|
||||
msgEditStylesheet = msgEditStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
msgEdit->setStyleSheet(msgEditStylesheet);
|
||||
msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css"));
|
||||
msgEdit->setFixedHeight(50);
|
||||
msgEdit->setFrameStyle(QFrame::NoFrame);
|
||||
|
||||
QString sendButtonStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/sendButton/sendButton.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream sendButtonStylesheetStream(&f);
|
||||
sendButtonStylesheet = sendButtonStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
sendButton->setStyleSheet(sendButtonStylesheet);
|
||||
sendButton->setStyleSheet(Style::get(":/ui/sendButton/sendButton.css"));
|
||||
fileButton->setStyleSheet(Style::get(":/ui/fileButton/fileButton.css"));
|
||||
emoteButton->setStyleSheet(Style::get(":/ui/emoteButton/emoteButton.css"));
|
||||
|
||||
QString fileButtonStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/fileButton/fileButton.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream fileButtonStylesheetStream(&f);
|
||||
fileButtonStylesheet = fileButtonStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
fileButton->setStyleSheet(fileButtonStylesheet);
|
||||
|
||||
|
||||
QString emoteButtonStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/emoteButton/emoteButton.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream emoteButtonStylesheetStream(&f);
|
||||
emoteButtonStylesheet = emoteButtonStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
emoteButton->setStyleSheet(emoteButtonStylesheet);
|
||||
|
||||
QString callButtonStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/callButton/callButton.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream callButtonStylesheetStream(&f);
|
||||
callButtonStylesheet = callButtonStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
callButton->setObjectName("green");
|
||||
callButton->setStyleSheet(callButtonStylesheet);
|
||||
callButton->setStyleSheet(Style::get(":/ui/callButton/callButton.css"));
|
||||
|
||||
QString videoButtonStylesheet = "";
|
||||
videoButton->setObjectName("green");
|
||||
videoButton->setStyleSheet(Style::get(":/ui/videoButton/videoButton.css"));
|
||||
|
||||
QString volButtonStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/videoButton/videoButton.css");
|
||||
QFile f(":/ui/volButton/volButton.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream videoButtonStylesheetStream(&f);
|
||||
videoButtonStylesheet = videoButtonStylesheetStream.readAll();
|
||||
QTextStream volButtonStylesheetStream(&f);
|
||||
volButtonStylesheet = volButtonStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
videoButton->setObjectName("green");
|
||||
videoButton->setStyleSheet(videoButtonStylesheet);
|
||||
volButton->setObjectName("green");
|
||||
volButton->setStyleSheet(volButtonStylesheet);
|
||||
|
||||
QString micButtonStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/micButton/micButton.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream micButtonStylesheetStream(&f);
|
||||
micButtonStylesheet = micButtonStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
micButton->setObjectName("green");
|
||||
micButton->setStyleSheet(micButtonStylesheet);
|
||||
|
||||
main->setLayout(mainLayout);
|
||||
mainLayout->addWidget(chatArea);
|
||||
|
@ -158,9 +127,13 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
headLayout->addWidget(avatar);
|
||||
headLayout->addLayout(headTextLayout);
|
||||
headLayout->addStretch();
|
||||
headLayout->addLayout(volMicLayout);
|
||||
headLayout->addWidget(callButton);
|
||||
headLayout->addWidget(videoButton);
|
||||
|
||||
volMicLayout->addWidget(micButton);
|
||||
volMicLayout->addWidget(volButton);
|
||||
|
||||
headTextLayout->addStretch();
|
||||
headTextLayout->addWidget(name);
|
||||
headTextLayout->addWidget(statusMessage);
|
||||
|
@ -173,20 +146,22 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
// callButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
// videoButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
// msgEdit->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
// chatArea->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
// callButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
// videoButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
// msgEdit->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
// chatArea->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
|
||||
connect(Widget::getInstance()->getCore(), &Core::fileSendStarted, this, &ChatForm::startFileSend);
|
||||
connect(Widget::getInstance()->getCore(), &Core::videoFrameReceived, netcam, &NetCamView::updateDisplay);
|
||||
connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered()));
|
||||
connect(fileButton, SIGNAL(clicked()), this, SLOT(onAttachClicked()));
|
||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
||||
connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered()));
|
||||
connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged()));
|
||||
connect(chatArea, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
||||
connect(sendButton, &QPushButton::clicked, this, &ChatForm::onSendTriggered);
|
||||
connect(fileButton, &QPushButton::clicked, this, &ChatForm::onAttachClicked);
|
||||
connect(callButton, &QPushButton::clicked, this, &ChatForm::onCallTriggered);
|
||||
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered);
|
||||
connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered);
|
||||
connect(chatArea->verticalScrollBar(), &QScrollBar::rangeChanged, this, &ChatForm::onSliderRangeChanged);
|
||||
connect(chatArea, &QScrollArea::customContextMenuRequested, this, &ChatForm::onChatContextMenuRequested);
|
||||
connect(emoteButton, &QPushButton::clicked, this, &ChatForm::onEmoteButtonClicked);
|
||||
connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle()));
|
||||
}
|
||||
|
||||
ChatForm::~ChatForm()
|
||||
|
@ -196,7 +171,7 @@ ChatForm::~ChatForm()
|
|||
delete netcam;
|
||||
}
|
||||
|
||||
void ChatForm::show(Ui::Widget &ui)
|
||||
void ChatForm::show(Ui::MainWindow &ui)
|
||||
{
|
||||
ui.mainContent->layout()->addWidget(main);
|
||||
ui.mainHead->layout()->addWidget(head);
|
||||
|
@ -243,8 +218,6 @@ void ChatForm::addMessage(QString author, QString message, QString date)
|
|||
|
||||
void ChatForm::addMessage(QLabel* author, QLabel* message, QLabel* date)
|
||||
{
|
||||
QPalette greentext;
|
||||
greentext.setColor(QPalette::WindowText, QColor(61,204,61));
|
||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
||||
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
|
||||
author->setAlignment(Qt::AlignTop | Qt::AlignRight);
|
||||
|
@ -272,8 +245,24 @@ void ChatForm::addMessage(QLabel* author, QLabel* message, QLabel* date)
|
|||
}
|
||||
else if (curRow)// onSaveLogClicked expects 0 or 3 QLabel per line
|
||||
author->setText("");
|
||||
if (message->text()[0] == '>')
|
||||
message->setPalette(greentext);
|
||||
|
||||
QColor greentext(61,204,61);
|
||||
QString fontTemplate = "<font color='%1'>%2</font>";
|
||||
|
||||
QString finalMessage;
|
||||
QStringList messageLines = message->text().split("\n");
|
||||
for (QString& s : messageLines)
|
||||
{
|
||||
if (QRegExp("^[ ]*>.*").exactMatch(s))
|
||||
finalMessage += fontTemplate.arg(greentext.name(), s.replace(" ", " "));
|
||||
else
|
||||
finalMessage += s.replace(" ", " ");
|
||||
finalMessage += "<br>";
|
||||
}
|
||||
message->setText(finalMessage.left(finalMessage.length()-4));
|
||||
message->setText(SmileyPack::getInstance().smileyfied(message->text()));
|
||||
message->setTextFormat(Qt::RichText);
|
||||
|
||||
mainChatLayout->addWidget(author, curRow, 0);
|
||||
mainChatLayout->addWidget(message, curRow, 1);
|
||||
mainChatLayout->addWidget(date, curRow, 3);
|
||||
|
@ -297,6 +286,12 @@ void ChatForm::onAttachClicked()
|
|||
QFile file(path);
|
||||
if (!file.exists() || !file.open(QIODevice::ReadOnly))
|
||||
return;
|
||||
if (file.isSequential())
|
||||
{
|
||||
QMessageBox::critical(0, "Bad Idea", "You're trying to send a special (sequential) file, that's not going to work!");
|
||||
return;
|
||||
file.close();
|
||||
}
|
||||
long long filesize = file.size();
|
||||
file.close();
|
||||
QFileInfo fi(path);
|
||||
|
@ -308,13 +303,14 @@ void ChatForm::onSliderRangeChanged()
|
|||
{
|
||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
||||
if (lockSliderToBottom)
|
||||
scroll->setValue(scroll->maximum());
|
||||
scroll->setValue(scroll->maximum());
|
||||
}
|
||||
|
||||
void ChatForm::startFileSend(ToxFile file)
|
||||
{
|
||||
if (file.friendId != f->friendId)
|
||||
return;
|
||||
|
||||
QLabel *author = new QLabel(Widget::getInstance()->getUsername());
|
||||
QLabel *date = new QLabel(QTime::currentTime().toString("hh:mm"));
|
||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
||||
|
@ -351,6 +347,7 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
|||
{
|
||||
if (file.friendId != f->friendId)
|
||||
return;
|
||||
|
||||
QLabel *author = new QLabel(f->getName());
|
||||
QLabel *date = new QLabel(QTime::currentTime().toString("hh:mm"));
|
||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
||||
|
@ -378,12 +375,21 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
|||
connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransfertWidget::onFileTransferInfo);
|
||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransfertWidget::onFileTransferCancelled);
|
||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished);
|
||||
|
||||
Widget* w = Widget::getInstance();
|
||||
if (!w->isFriendWidgetCurActiveWidget(f)|| w->getIsWindowMinimized() || !w->isActiveWindow())
|
||||
{
|
||||
w->newMessageAlert();
|
||||
f->hasNewEvents=true;
|
||||
f->widget->updateStatusLight();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
|
||||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
callId = CallId;
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
|
@ -405,11 +411,11 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
|
|||
}
|
||||
|
||||
Widget* w = Widget::getInstance();
|
||||
if (!w->isFriendWidgetCurActiveWidget(f))
|
||||
if (!w->isFriendWidgetCurActiveWidget(f)|| w->getIsWindowMinimized() || !w->isActiveWindow())
|
||||
{
|
||||
w->newMessageAlert();
|
||||
f->hasNewMessages=true;
|
||||
w->updateFriendStatusLights(f->friendId);
|
||||
f->hasNewEvents=true;
|
||||
f->widget->updateStatusLight();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,6 +423,8 @@ void ChatForm::onAvStart(int FriendId, int CallId, bool video)
|
|||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
audioInputFlag = true;
|
||||
callId = CallId;
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
|
@ -443,6 +451,10 @@ void ChatForm::onAvCancel(int FriendId, int)
|
|||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
audioInputFlag = false;
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
|
@ -458,6 +470,10 @@ void ChatForm::onAvEnd(int FriendId, int)
|
|||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
audioInputFlag = false;
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
|
@ -473,6 +489,7 @@ void ChatForm::onAvRinging(int FriendId, int CallId, bool video)
|
|||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
callId = CallId;
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
|
@ -498,6 +515,7 @@ void ChatForm::onAvStarting(int FriendId, int, bool video)
|
|||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
if (video)
|
||||
|
@ -523,6 +541,10 @@ void ChatForm::onAvEnding(int FriendId, int)
|
|||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
audioInputFlag = false;
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
|
@ -540,6 +562,10 @@ void ChatForm::onAvRequestTimeout(int FriendId, int)
|
|||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
audioInputFlag = false;
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
|
@ -557,6 +583,10 @@ void ChatForm::onAvPeerTimeout(int FriendId, int)
|
|||
{
|
||||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
audioInputFlag = false;
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
|
@ -570,25 +600,43 @@ void ChatForm::onAvPeerTimeout(int FriendId, int)
|
|||
netcam->hide();
|
||||
}
|
||||
|
||||
void ChatForm::onAvMediaChange(int, int, bool video)
|
||||
{
|
||||
if (video)
|
||||
{
|
||||
netcam->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
netcam->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onAnswerCallTriggered()
|
||||
{
|
||||
audioInputFlag = true;
|
||||
emit answerCall(callId);
|
||||
}
|
||||
|
||||
void ChatForm::onHangupCallTriggered()
|
||||
{
|
||||
audioInputFlag = false;
|
||||
emit hangupCall(callId);
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
}
|
||||
|
||||
void ChatForm::onCallTriggered()
|
||||
{
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
emit startCall(f->friendId);
|
||||
audioInputFlag = true;
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
emit startCall(f->friendId);
|
||||
}
|
||||
|
||||
void ChatForm::onVideoCallTriggered()
|
||||
{
|
||||
audioInputFlag = true;
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
emit startVideoCall(f->friendId, true);
|
||||
|
@ -596,6 +644,9 @@ void ChatForm::onVideoCallTriggered()
|
|||
|
||||
void ChatForm::onCancelCallTriggered()
|
||||
{
|
||||
audioInputFlag = false;
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
callButton->disconnect();
|
||||
videoButton->disconnect();
|
||||
callButton->setObjectName("green");
|
||||
|
@ -650,3 +701,48 @@ void ChatForm::onSaveLogClicked()
|
|||
file.write(log.toUtf8());
|
||||
file.close();
|
||||
}
|
||||
|
||||
void ChatForm::onEmoteButtonClicked()
|
||||
{
|
||||
// don't show the smiley selection widget if there are no smileys available
|
||||
if (SmileyPack::getInstance().getEmoticons().empty())
|
||||
return;
|
||||
|
||||
EmoticonsWidget widget;
|
||||
connect(&widget, &EmoticonsWidget::insertEmoticon, this, &ChatForm::onEmoteInsertRequested);
|
||||
|
||||
QWidget* sender = qobject_cast<QWidget*>(QObject::sender());
|
||||
if (sender)
|
||||
{
|
||||
QPoint pos = -QPoint(widget.sizeHint().width() / 2, widget.sizeHint().height()) - QPoint(0, 10);
|
||||
widget.exec(sender->mapToGlobal(pos));
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onEmoteInsertRequested(QString str)
|
||||
{
|
||||
// insert the emoticon
|
||||
QWidget* sender = qobject_cast<QWidget*>(QObject::sender());
|
||||
if (sender)
|
||||
msgEdit->insertPlainText(str);
|
||||
|
||||
msgEdit->setFocus(); // refocus so that we can continue typing
|
||||
}
|
||||
|
||||
void ChatForm::onMicMuteToggle()
|
||||
{
|
||||
if (audioInputFlag == true)
|
||||
{
|
||||
emit micMuteToggle(callId);
|
||||
if (micButton->objectName() == "red")
|
||||
{
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
micButton->setObjectName("red");
|
||||
micButton->style()->polish(micButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <QPoint>
|
||||
|
||||
#include "widget/tool/chattextedit.h"
|
||||
#include "ui_widget.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "core.h"
|
||||
#include "widget/netcamview.h"
|
||||
|
||||
|
@ -43,7 +43,7 @@ class ChatForm : public QObject
|
|||
public:
|
||||
ChatForm(Friend* chatFriend);
|
||||
~ChatForm();
|
||||
void show(Ui::Widget& ui);
|
||||
void show(Ui::MainWindow &ui);
|
||||
void setName(QString newName);
|
||||
void setStatusMessage(QString newMessage);
|
||||
void addFriendMessage(QString message);
|
||||
|
@ -58,6 +58,7 @@ signals:
|
|||
void answerCall(int callId);
|
||||
void hangupCall(int callId);
|
||||
void cancelCall(int callId, int friendId);
|
||||
void micMuteToggle(int callId);
|
||||
|
||||
public slots:
|
||||
void startFileSend(ToxFile file);
|
||||
|
@ -71,6 +72,8 @@ public slots:
|
|||
void onAvEnding(int FriendId, int CallId);
|
||||
void onAvRequestTimeout(int FriendId, int CallId);
|
||||
void onAvPeerTimeout(int FriendId, int CallId);
|
||||
void onAvMediaChange(int FriendId, int CallId, bool video);
|
||||
void onMicMuteToggle();
|
||||
|
||||
private slots:
|
||||
void onSendTriggered();
|
||||
|
@ -83,21 +86,23 @@ private slots:
|
|||
void onCancelCallTriggered();
|
||||
void onChatContextMenuRequested(QPoint pos);
|
||||
void onSaveLogClicked();
|
||||
void onEmoteButtonClicked();
|
||||
void onEmoteInsertRequested(QString str);
|
||||
|
||||
private:
|
||||
Friend* f;
|
||||
QHBoxLayout *headLayout, *mainFootLayout;
|
||||
QVBoxLayout *headTextLayout, *mainLayout, *footButtonsSmall;
|
||||
QVBoxLayout *headTextLayout, *mainLayout, *footButtonsSmall, *volMicLayout;
|
||||
QGridLayout *mainChatLayout;
|
||||
QLabel *avatar, *name, *statusMessage;
|
||||
ChatTextEdit *msgEdit;
|
||||
QPushButton *sendButton, *fileButton, *emoteButton, *callButton, *videoButton;
|
||||
QPushButton *sendButton, *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton;
|
||||
QScrollArea *chatArea;
|
||||
QWidget *main, *head, *chatAreaWidget;
|
||||
QString previousName;
|
||||
NetCamView* netcam;
|
||||
int curRow;
|
||||
bool lockSliderToBottom;
|
||||
bool lockSliderToBottom, audioInputFlag;
|
||||
int callId;
|
||||
};
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ FilesForm::~FilesForm()
|
|||
// I'm not too bummed about removing it
|
||||
}
|
||||
|
||||
void FilesForm::show(Ui::Widget& ui)
|
||||
void FilesForm::show(Ui::MainWindow& ui)
|
||||
{
|
||||
ui.mainContent->layout()->addWidget(&main);
|
||||
ui.mainHead->layout()->addWidget(head);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#ifndef FILESFORM_H
|
||||
#define FILESFORM_H
|
||||
|
||||
#include "ui_widget.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QListWidget>
|
||||
#include <QTabWidget>
|
||||
|
@ -37,7 +37,7 @@ public:
|
|||
FilesForm();
|
||||
~FilesForm();
|
||||
|
||||
void show(Ui::Widget& ui);
|
||||
void show(Ui::MainWindow &ui);
|
||||
|
||||
public slots:
|
||||
void onFileDownloadComplete(const QString& path);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "widget/widget.h"
|
||||
#include "friend.h"
|
||||
#include "friendlist.h"
|
||||
#include "style.h"
|
||||
#include <QFont>
|
||||
#include <QTime>
|
||||
#include <QScrollBar>
|
||||
|
@ -55,16 +56,8 @@ GroupChatForm::GroupChatForm(Group* chatGroup)
|
|||
namesList->setFont(small);
|
||||
|
||||
chatAreaWidget->setLayout(mainChatLayout);
|
||||
QString chatAreaStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/chatArea/chatArea.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream chatAreaStylesheetStream(&f);
|
||||
chatAreaStylesheet = chatAreaStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
chatArea->setStyleSheet(chatAreaStylesheet);
|
||||
|
||||
chatArea->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css"));
|
||||
chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
chatArea->setWidgetResizable(true);
|
||||
chatArea->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
@ -73,35 +66,15 @@ GroupChatForm::GroupChatForm(Group* chatGroup)
|
|||
mainChatLayout->setColumnStretch(1,1);
|
||||
mainChatLayout->setSpacing(10);
|
||||
|
||||
|
||||
QString msgEditStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/msgEdit/msgEdit.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream msgEditStylesheetStream(&f);
|
||||
msgEditStylesheet = msgEditStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
msgEdit->setObjectName("group");
|
||||
msgEdit->setStyleSheet(msgEditStylesheet);
|
||||
msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css"));
|
||||
msgEdit->setFixedHeight(50);
|
||||
msgEdit->setFrameStyle(QFrame::NoFrame);
|
||||
|
||||
mainChatLayout->setColumnStretch(1,1);
|
||||
mainChatLayout->setHorizontalSpacing(10);
|
||||
|
||||
QString sendButtonStylesheet = "";
|
||||
try
|
||||
{
|
||||
QFile f(":/ui/sendButton/sendButton.css");
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream sendButtonStylesheetStream(&f);
|
||||
sendButtonStylesheet = sendButtonStylesheetStream.readAll();
|
||||
}
|
||||
catch (int e) {}
|
||||
sendButton->setStyleSheet(sendButtonStylesheet);
|
||||
|
||||
sendButton->setStyleSheet(Style::get(":/ui/sendButton/sendButton.css"));
|
||||
sendButton->setFixedSize(50, 50);
|
||||
|
||||
main->setLayout(mainLayout);
|
||||
|
@ -142,7 +115,7 @@ GroupChatForm::~GroupChatForm()
|
|||
delete main;
|
||||
}
|
||||
|
||||
void GroupChatForm::show(Ui::Widget &ui)
|
||||
void GroupChatForm::show(Ui::MainWindow &ui)
|
||||
{
|
||||
ui.mainContent->layout()->addWidget(main);
|
||||
ui.mainHead->layout()->addWidget(head);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include <QTime>
|
||||
|
||||
#include "widget/tool/chattextedit.h"
|
||||
#include "ui_widget.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
// Spacing in px inserted when the author of the last message changes
|
||||
#define AUTHOR_CHANGE_SPACING 5
|
||||
|
@ -40,7 +40,7 @@ class GroupChatForm : public QObject
|
|||
public:
|
||||
GroupChatForm(Group* chatGroup);
|
||||
~GroupChatForm();
|
||||
void show(Ui::Widget& ui);
|
||||
void show(Ui::MainWindow &ui);
|
||||
void setName(QString newName);
|
||||
void addGroupMessage(QString message, int peerId);
|
||||
void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm"));
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
#include "settingsform.h"
|
||||
#include "widget/widget.h"
|
||||
#include "settings.h"
|
||||
#include "smileypack.h"
|
||||
#include <QFont>
|
||||
#include <QClipboard>
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QDir>
|
||||
|
||||
SettingsForm::SettingsForm()
|
||||
: QObject()
|
||||
|
@ -55,6 +58,12 @@ SettingsForm::SettingsForm()
|
|||
useTranslations.setChecked(Settings::getInstance().getUseTranslations());
|
||||
makeToxPortable.setText(tr("Make Tox portable","Text on a checkbox to make qTox a portable application"));
|
||||
makeToxPortable.setChecked(Settings::getInstance().getMakeToxPortable());
|
||||
makeToxPortable.setToolTip(tr("Save settings to the working directory instead of the usual conf dir","describes makeToxPortable checkbox"));
|
||||
|
||||
smileyPackLabel.setText(tr("Smiley Pack", "Text on smiley pack label"));
|
||||
for (auto entry : SmileyPack::listSmileyPacks())
|
||||
smileyPackBrowser.addItem(entry.first, entry.second);
|
||||
smileyPackBrowser.setCurrentIndex(smileyPackBrowser.findData(Settings::getInstance().getSmileyPack()));
|
||||
|
||||
main->setLayout(&layout);
|
||||
layout.addWidget(&idLabel);
|
||||
|
@ -73,7 +82,9 @@ SettingsForm::SettingsForm()
|
|||
layout.addWidget(&enableIPv6);
|
||||
layout.addWidget(&useTranslations);
|
||||
layout.addWidget(&makeToxPortable);
|
||||
//layout.addStretch();
|
||||
layout.addWidget(&smileyPackLabel);
|
||||
layout.addWidget(&smileyPackBrowser);
|
||||
layout.addStretch();
|
||||
|
||||
head->setLayout(&headLayout);
|
||||
headLayout.addWidget(&headLabel);
|
||||
|
@ -87,6 +98,7 @@ SettingsForm::SettingsForm()
|
|||
connect(&useTranslations, SIGNAL(stateChanged(int)), this, SLOT(onUseTranslationUpdated()));
|
||||
connect(&makeToxPortable, SIGNAL(stateChanged(int)), this, SLOT(onMakeToxPortableUpdated()));
|
||||
connect(&idLabel, SIGNAL(clicked()), this, SLOT(copyIdClicked()));
|
||||
connect(&smileyPackBrowser, SIGNAL(currentIndexChanged(int)), this, SLOT(onSmileyBrowserIndexChanged(int)));
|
||||
}
|
||||
|
||||
SettingsForm::~SettingsForm()
|
||||
|
@ -116,7 +128,7 @@ void SettingsForm::setFriendAddress(const QString& friendAddress)
|
|||
id.setText(friendAddress);
|
||||
}
|
||||
|
||||
void SettingsForm::show(Ui::Widget &ui)
|
||||
void SettingsForm::show(Ui::MainWindow &ui)
|
||||
{
|
||||
profiles.clear();
|
||||
for (QString profile : searchProfiles())
|
||||
|
@ -198,3 +210,9 @@ void SettingsForm::onMakeToxPortableUpdated()
|
|||
{
|
||||
Settings::getInstance().setMakeToxPortable(makeToxPortable.isChecked());
|
||||
}
|
||||
|
||||
void SettingsForm::onSmileyBrowserIndexChanged(int index)
|
||||
{
|
||||
QString filename = smileyPackBrowser.itemData(index).toString();
|
||||
Settings::getInstance().setSmileyPack(filename);
|
||||
}
|
||||
|
|
|
@ -31,9 +31,9 @@
|
|||
#include <QFileInfo>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include "widget/tool/clickablelabel.h"
|
||||
#include "ui_widget.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "widget/selfcamview.h"
|
||||
#include "widget/croppinglabel.h"
|
||||
#include "core.h"
|
||||
|
||||
class SettingsForm : public QObject
|
||||
|
@ -43,7 +43,7 @@ public:
|
|||
SettingsForm();
|
||||
~SettingsForm();
|
||||
|
||||
void show(Ui::Widget& ui);
|
||||
void show(Ui::MainWindow &ui);
|
||||
static QList<QString> searchProfiles();
|
||||
|
||||
public slots:
|
||||
|
@ -58,12 +58,13 @@ private slots:
|
|||
void onEnableIPv6Updated();
|
||||
void onUseTranslationUpdated();
|
||||
void onMakeToxPortableUpdated();
|
||||
void onSmileyBrowserIndexChanged(int index);
|
||||
void copyIdClicked();
|
||||
|
||||
private:
|
||||
QLabel headLabel;/*, nameLabel, statusTextLabel;*/
|
||||
QLabel headLabel, smileyPackLabel;
|
||||
QTextEdit id;
|
||||
ClickableLabel idLabel;
|
||||
CroppingLabel idLabel;
|
||||
QLabel profilesLabel;
|
||||
QComboBox profiles;
|
||||
QPushButton loadConf, exportConf, delConf, importConf, videoTest;
|
||||
|
@ -71,10 +72,8 @@ private:
|
|||
QCheckBox enableIPv6, useTranslations, makeToxPortable;
|
||||
QVBoxLayout layout, headLayout;
|
||||
QWidget *main, *head, *hboxcont1, *hboxcont2;
|
||||
QComboBox smileyPackBrowser;
|
||||
QString getSelectedSavePath();
|
||||
|
||||
public:
|
||||
//QLineEdit name, statusText;
|
||||
};
|
||||
|
||||
#endif // SETTINGSFORM_H
|
||||
|
|
67
widget/friendlistwidget.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#include "friendlistwidget.h"
|
||||
#include <QDebug>
|
||||
|
||||
FriendListWidget::FriendListWidget(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
mainLayout = new QGridLayout();
|
||||
setLayout(mainLayout);
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
layout()->setSpacing(0);
|
||||
layout()->setMargin(0);
|
||||
|
||||
groupLayout = new QVBoxLayout();
|
||||
groupLayout->setSpacing(0);
|
||||
groupLayout->setMargin(0);
|
||||
|
||||
for (Status s : {Status::Online, Status::Away, Status::Busy, Status::Offline})
|
||||
{
|
||||
QLayout *l = new QVBoxLayout();
|
||||
l->setSpacing(0);
|
||||
l->setMargin(0);
|
||||
|
||||
layouts[static_cast<int>(s)] = l;
|
||||
}
|
||||
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Online)], 0, 0);
|
||||
mainLayout->addLayout(groupLayout, 1, 0);
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Away)], 2, 0);
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Busy)], 3, 0);
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Offline)], 4, 0);
|
||||
}
|
||||
|
||||
QLayout* FriendListWidget::getGroupLayout()
|
||||
{
|
||||
return groupLayout;
|
||||
}
|
||||
|
||||
QLayout* FriendListWidget::getFriendLayout(Status s)
|
||||
{
|
||||
auto res = layouts.find(static_cast<int>(s));
|
||||
if (res != layouts.end())
|
||||
return res.value();
|
||||
|
||||
qDebug() << "Friend Status: " << static_cast<int>(s) << " not found!";
|
||||
return layouts[static_cast<int>(Status::Online)];
|
||||
}
|
||||
|
||||
void FriendListWidget::moveWidget(QWidget *w, Status s)
|
||||
{
|
||||
mainLayout->removeWidget(w);
|
||||
getFriendLayout(s)->addWidget(w);
|
||||
}
|
|
@ -14,29 +14,31 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef VIDEOSURFACE_H
|
||||
#define VIDEOSURFACE_H
|
||||
#ifndef FRIENDLISTWIDGET_H
|
||||
#define FRIENDLISTWIDGET_H
|
||||
|
||||
#include <QAbstractVideoSurface>
|
||||
#include <QVideoSurfaceFormat>
|
||||
#include "vpx/vpx_image.h"
|
||||
#include <QWidget>
|
||||
#include <QGridLayout>
|
||||
#include "core.h"
|
||||
|
||||
class VideoSurface : public QAbstractVideoSurface
|
||||
class FriendListWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VideoSurface();
|
||||
bool start(const QVideoSurfaceFormat &format);
|
||||
bool present(const QVideoFrame &frame);
|
||||
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
|
||||
explicit FriendListWidget(QWidget *parent = 0);
|
||||
|
||||
QLayout* getGroupLayout();
|
||||
QLayout* getFriendLayout(Status s);
|
||||
void moveWidget(QWidget *w, Status s);
|
||||
|
||||
signals:
|
||||
// Slots MUST be called with a direct or blocking connection, or img may die before they return !
|
||||
void videoFrameReady(vpx_image img);
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
QVideoSurfaceFormat mVideoFormat;
|
||||
vpx_image_t input;
|
||||
QHash<int, QLayout*> layouts;
|
||||
QLayout *groupLayout;
|
||||
QGridLayout *mainLayout;
|
||||
};
|
||||
|
||||
#endif // VIDEOSURFACE_H
|
||||
#endif // FRIENDLISTWIDGET_H
|
|
@ -19,22 +19,24 @@
|
|||
#include "grouplist.h"
|
||||
#include "groupwidget.h"
|
||||
#include "widget.h"
|
||||
#include "friendlist.h"
|
||||
#include "friend.h"
|
||||
#include <QContextMenuEvent>
|
||||
#include <QMenu>
|
||||
|
||||
FriendWidget::FriendWidget(int FriendId, QString id)
|
||||
: friendId(FriendId)
|
||||
{
|
||||
this->setMouseTracking(true);
|
||||
this->setAutoFillBackground(true);
|
||||
this->setFixedWidth(225);
|
||||
this->setFixedHeight(55);
|
||||
this->setLayout(&layout);
|
||||
setMouseTracking(true);
|
||||
setAutoFillBackground(true);
|
||||
setFixedHeight(55);
|
||||
setLayout(&layout);
|
||||
layout.setSpacing(0);
|
||||
layout.setMargin(0);
|
||||
layout.setStretchFactor(this, 100);
|
||||
textLayout.setSpacing(0);
|
||||
textLayout.setMargin(0);
|
||||
setLayoutDirection(Qt::LeftToRight); // parent might have set Qt::RightToLeft
|
||||
|
||||
avatar.setPixmap(QPixmap(":img/contact.png"));
|
||||
name.setText(id);
|
||||
|
@ -62,22 +64,16 @@ FriendWidget::FriendWidget(int FriendId, QString id)
|
|||
layout.addWidget(&avatar);
|
||||
layout.addSpacing(5);
|
||||
layout.addLayout(&textLayout);
|
||||
layout.addStretch();
|
||||
layout.addSpacing(5);
|
||||
layout.addWidget(&statusPic);
|
||||
layout.addSpacing(5);
|
||||
|
||||
isActiveWidget = 0;
|
||||
}
|
||||
|
||||
void FriendWidget::setNewFixedWidth(int newWidth)
|
||||
{
|
||||
this->setFixedWidth(newWidth);
|
||||
}
|
||||
|
||||
void FriendWidget::mouseReleaseEvent (QMouseEvent*)
|
||||
{
|
||||
emit friendWidgetClicked(this);
|
||||
layout.invalidate();
|
||||
layout.update();
|
||||
layout.activate();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
|
||||
|
@ -121,47 +117,6 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
|
|||
}
|
||||
}
|
||||
|
||||
void FriendWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if ((event->buttons() & Qt::LeftButton) == Qt::LeftButton)
|
||||
{
|
||||
if (isActiveWidget)
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, QColor(250,250,250,255));
|
||||
this->setPalette(pal);
|
||||
}
|
||||
else
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, QColor(85,85,85,255));
|
||||
this->setPalette(pal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FriendWidget::enterEvent(QEvent*)
|
||||
{
|
||||
if (isActiveWidget != 1)
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, QColor(75,75,75,255));
|
||||
lastColor = this->palette().background().color();
|
||||
this->setPalette(pal);
|
||||
}
|
||||
}
|
||||
|
||||
void FriendWidget::leaveEvent(QEvent*)
|
||||
{
|
||||
if (isActiveWidget != 1)
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, lastColor);
|
||||
this->setPalette(pal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FriendWidget::setAsActiveChatroom()
|
||||
{
|
||||
isActiveWidget = 1;
|
||||
|
@ -199,3 +154,38 @@ void FriendWidget::setAsInactiveChatroom()
|
|||
this->setPalette(pal3);
|
||||
avatar.setPixmap(QPixmap(":img/contact.png"));
|
||||
}
|
||||
|
||||
void FriendWidget::updateStatusLight()
|
||||
{
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
Status status = f->friendStatus;
|
||||
|
||||
if (status == Status::Online && f->hasNewEvents == 0)
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_online.png"));
|
||||
else if (status == Status::Online && f->hasNewEvents == 1)
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png"));
|
||||
else if (status == Status::Away && f->hasNewEvents == 0)
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_idle.png"));
|
||||
else if (status == Status::Away && f->hasNewEvents == 1)
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_idle_notification.png"));
|
||||
else if (status == Status::Busy && f->hasNewEvents == 0)
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_busy.png"));
|
||||
else if (status == Status::Busy && f->hasNewEvents == 1)
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_busy_notification.png"));
|
||||
else if (status == Status::Offline && f->hasNewEvents == 0)
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_away.png"));
|
||||
else if (status == Status::Offline && f->hasNewEvents == 1)
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_away_notification.png"));
|
||||
}
|
||||
|
||||
void FriendWidget::setChatForm(Ui::MainWindow &ui)
|
||||
{
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
f->chatForm->show(ui);
|
||||
}
|
||||
|
||||
void FriendWidget::resetEventFlags()
|
||||
{
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
f->hasNewEvents = 0;
|
||||
}
|
||||
|
|
|
@ -22,19 +22,20 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
struct FriendWidget : public QWidget
|
||||
#include "genericchatroomwidget.h"
|
||||
#include "croppinglabel.h"
|
||||
|
||||
struct FriendWidget : public GenericChatroomWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FriendWidget(int FriendId, QString id);
|
||||
void mouseReleaseEvent (QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void contextMenuEvent(QContextMenuEvent * event);
|
||||
void enterEvent(QEvent* event);
|
||||
void leaveEvent(QEvent* event);
|
||||
void setAsActiveChatroom();
|
||||
void setAsInactiveChatroom();
|
||||
void setNewFixedWidth(int newWidth);
|
||||
void updateStatusLight();
|
||||
void setChatForm(Ui::MainWindow &);
|
||||
void resetEventFlags();
|
||||
|
||||
signals:
|
||||
void friendWidgetClicked(FriendWidget* widget);
|
||||
|
@ -43,13 +44,8 @@ signals:
|
|||
|
||||
public:
|
||||
int friendId;
|
||||
QLabel avatar, name, statusMessage, statusPic;
|
||||
QHBoxLayout layout;
|
||||
QVBoxLayout textLayout;
|
||||
|
||||
private:
|
||||
QColor lastColor;
|
||||
int isActiveWidget;
|
||||
QLabel avatar, statusPic;
|
||||
CroppingLabel name, statusMessage;
|
||||
};
|
||||
|
||||
#endif // FRIENDWIDGET_H
|
||||
|
|
73
widget/genericchatroomwidget.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "genericchatroomwidget.h"
|
||||
#include <QMouseEvent>
|
||||
|
||||
GenericChatroomWidget::GenericChatroomWidget(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int GenericChatroomWidget::isActive()
|
||||
{
|
||||
return isActiveWidget;
|
||||
}
|
||||
|
||||
void GenericChatroomWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if ((event->buttons() & Qt::LeftButton) == Qt::LeftButton)
|
||||
{
|
||||
if (isActive())
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, QColor(250,250,250,255));
|
||||
this->setPalette(pal);
|
||||
}
|
||||
else
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, QColor(85,85,85,255));
|
||||
this->setPalette(pal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GenericChatroomWidget::leaveEvent(QEvent *)
|
||||
{
|
||||
if (isActive() != 1)
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, lastColor);
|
||||
this->setPalette(pal);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericChatroomWidget::enterEvent(QEvent *)
|
||||
{
|
||||
if (isActive() != 1)
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, QColor(75,75,75,255));
|
||||
lastColor = this->palette().background().color();
|
||||
this->setPalette(pal);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericChatroomWidget::mouseReleaseEvent(QMouseEvent*)
|
||||
{
|
||||
emit chatroomWidgetClicked(this);
|
||||
}
|
58
widget/genericchatroomwidget.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef GENERICCHATROOMWIDGET_H
|
||||
#define GENERICCHATROOMWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class GenericChatroomWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GenericChatroomWidget(QWidget *parent = 0);
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void mouseReleaseEvent (QMouseEvent* event);
|
||||
void leaveEvent(QEvent *);
|
||||
void enterEvent(QEvent *);
|
||||
|
||||
virtual void setAsActiveChatroom(){;}
|
||||
virtual void setAsInactiveChatroom(){;}
|
||||
virtual void updateStatusLight(){;}
|
||||
virtual void setChatForm(Ui::MainWindow &){;}
|
||||
virtual void resetEventFlags(){;}
|
||||
|
||||
int isActive();
|
||||
|
||||
signals:
|
||||
void chatroomWidgetClicked(GenericChatroomWidget* widget);
|
||||
|
||||
public slots:
|
||||
|
||||
protected:
|
||||
int isActiveWidget;
|
||||
QColor lastColor;
|
||||
QHBoxLayout layout;
|
||||
QVBoxLayout textLayout;
|
||||
};
|
||||
|
||||
#endif // GENERICCHATROOMWIDGET_H
|
|
@ -17,22 +17,26 @@
|
|||
#include "groupwidget.h"
|
||||
#include "grouplist.h"
|
||||
#include "group.h"
|
||||
#include "settings.h"
|
||||
#include "widget/form/groupchatform.h"
|
||||
#include <QPalette>
|
||||
#include <QMenu>
|
||||
#include <QContextMenuEvent>
|
||||
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
GroupWidget::GroupWidget(int GroupId, QString Name)
|
||||
: groupId{GroupId}
|
||||
{
|
||||
this->setMouseTracking(true);
|
||||
this->setAutoFillBackground(true);
|
||||
this->setLayout(&layout);
|
||||
this->setFixedWidth(225);
|
||||
this->setFixedHeight(55);
|
||||
setMouseTracking(true);
|
||||
setAutoFillBackground(true);
|
||||
setLayout(&layout);
|
||||
setFixedHeight(55);
|
||||
layout.setSpacing(0);
|
||||
layout.setMargin(0);
|
||||
textLayout.setSpacing(0);
|
||||
textLayout.setMargin(0);
|
||||
setLayoutDirection(Qt::LeftToRight); // parent might have set Qt::RightToLeft
|
||||
|
||||
avatar.setPixmap(QPixmap(":img/group.png"));
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_online.png"));
|
||||
|
@ -72,16 +76,6 @@ GroupWidget::GroupWidget(int GroupId, QString Name)
|
|||
isActiveWidget = 0;
|
||||
}
|
||||
|
||||
void GroupWidget::setNewFixedWidth(int newWidth)
|
||||
{
|
||||
this->setFixedWidth(newWidth);
|
||||
}
|
||||
|
||||
void GroupWidget::mouseReleaseEvent (QMouseEvent*)
|
||||
{
|
||||
emit groupWidgetClicked(this);
|
||||
}
|
||||
|
||||
void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
|
||||
{
|
||||
QPoint pos = event->globalPos();
|
||||
|
@ -99,46 +93,6 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
|
|||
}
|
||||
}
|
||||
|
||||
void GroupWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if ((event->buttons() & Qt::LeftButton) == Qt::LeftButton)
|
||||
{
|
||||
if (isActiveWidget)
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, QColor(250,250,250,255));
|
||||
this->setPalette(pal);
|
||||
}
|
||||
else
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, QColor(85,85,85,255));
|
||||
this->setPalette(pal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroupWidget::enterEvent(QEvent*)
|
||||
{
|
||||
if (isActiveWidget != 1)
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, QColor(75,75,75,255));
|
||||
lastColor = this->palette().background().color();
|
||||
this->setPalette(pal);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupWidget::leaveEvent(QEvent*)
|
||||
{
|
||||
if (isActiveWidget != 1)
|
||||
{
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Background, lastColor);
|
||||
this->setPalette(pal);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupWidget::onUserListChanged()
|
||||
{
|
||||
Group* g = GroupList::findGroup(groupId);
|
||||
|
@ -185,3 +139,40 @@ void GroupWidget::setAsInactiveChatroom()
|
|||
this->setPalette(pal3);
|
||||
avatar.setPixmap(QPixmap(":img/group.png"));
|
||||
}
|
||||
|
||||
void GroupWidget::updateStatusLight()
|
||||
{
|
||||
Group *g = GroupList::findGroup(groupId);
|
||||
|
||||
if (Settings::getInstance().getUseNativeDecoration())
|
||||
{
|
||||
if (g->hasNewMessages == 0)
|
||||
{
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_online.png"));
|
||||
} else {
|
||||
if (g->userWasMentioned == 0) statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png"));
|
||||
else statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png"));
|
||||
}
|
||||
} else {
|
||||
if (g->hasNewMessages == 0)
|
||||
{
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_groupchat.png"));
|
||||
} else {
|
||||
if (g->userWasMentioned == 0) statusPic.setPixmap(QPixmap(":img/status/dot_groupchat_newmessages.png"));
|
||||
else statusPic.setPixmap(QPixmap(":img/status/dot_groupchat_notification.png"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroupWidget::setChatForm(Ui::MainWindow &ui)
|
||||
{
|
||||
Group* g = GroupList::findGroup(groupId);
|
||||
g->chatForm->show(ui);
|
||||
}
|
||||
|
||||
void GroupWidget::resetEventFlags()
|
||||
{
|
||||
Group* g = GroupList::findGroup(groupId);
|
||||
g->hasNewMessages = 0;
|
||||
g->userWasMentioned = 0;
|
||||
}
|
||||
|
|
|
@ -19,21 +19,20 @@
|
|||
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include "genericchatroomwidget.h"
|
||||
|
||||
class GroupWidget : public QWidget
|
||||
class GroupWidget : public GenericChatroomWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GroupWidget(int GroupId, QString Name);
|
||||
void onUserListChanged();
|
||||
void mouseReleaseEvent (QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void contextMenuEvent(QContextMenuEvent * event);
|
||||
void enterEvent(QEvent* event);
|
||||
void leaveEvent(QEvent* event);
|
||||
|
||||
void setAsInactiveChatroom();
|
||||
void setAsActiveChatroom();
|
||||
void updateStatusLight();
|
||||
void setChatForm(Ui::MainWindow &);
|
||||
void resetEventFlags();
|
||||
|
||||
signals:
|
||||
void groupWidgetClicked(GroupWidget* widget);
|
||||
|
@ -42,15 +41,6 @@ signals:
|
|||
public:
|
||||
int groupId;
|
||||
QLabel avatar, name, nusers, statusPic;
|
||||
QHBoxLayout layout;
|
||||
QVBoxLayout textLayout;
|
||||
void setAsInactiveChatroom();
|
||||
void setAsActiveChatroom();
|
||||
void setNewFixedWidth(int newWidth);
|
||||
|
||||
private:
|
||||
QColor lastColor;
|
||||
int isActiveWidget;
|
||||
};
|
||||
|
||||
#endif // GROUPWIDGET_H
|
||||
|
|
|
@ -89,8 +89,8 @@ QImage NetCamView::convert(vpx_image& frame)
|
|||
QImage img(w, h, QImage::Format_RGB32);
|
||||
|
||||
uint8_t* yData = frame.planes[VPX_PLANE_Y];
|
||||
uint8_t* uData = frame.planes[VPX_PLANE_U];
|
||||
uint8_t* vData = frame.planes[VPX_PLANE_V];
|
||||
uint8_t* uData = frame.planes[VPX_PLANE_V];
|
||||
uint8_t* vData = frame.planes[VPX_PLANE_U];
|
||||
for (int i = 0; i< h; i++)
|
||||
{
|
||||
uint32_t* scanline = (uint32_t*)img.scanLine(i);
|
||||
|
|
|
@ -15,15 +15,13 @@
|
|||
*/
|
||||
|
||||
#include "selfcamview.h"
|
||||
#include <QActionGroup>
|
||||
#include <QMessageBox>
|
||||
#include <QCloseEvent>
|
||||
#include <QShowEvent>
|
||||
#include <QVideoFrame>
|
||||
|
||||
#include "videosurface.h"
|
||||
#include "widget.h"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
SelfCamView::SelfCamView(Camera* Cam, QWidget* parent)
|
||||
: QWidget(parent), displayLabel{new QLabel},
|
||||
mainLayout{new QHBoxLayout()}, cam(Cam)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
ChatTextEdit::ChatTextEdit(QWidget *parent) :
|
||||
QTextEdit(parent)
|
||||
{
|
||||
setPlaceholderText("Type your message here...");
|
||||
}
|
||||
|
||||
void ChatTextEdit::keyPressEvent(QKeyEvent * event)
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
|
||||
|
||||
This file is part of Tox Qt GUI.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "copyableelidelabel.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMenu>
|
||||
#include <QClipboard>
|
||||
|
||||
CopyableElideLabel::CopyableElideLabel(QWidget* parent) :
|
||||
ElideLabel(parent)
|
||||
{
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &CopyableElideLabel::customContextMenuRequested, this, &CopyableElideLabel::showContextMenu);
|
||||
|
||||
actionCopy = new QAction(CopyableElideLabel::tr("Copy"), this);
|
||||
connect(actionCopy, &QAction::triggered, [this]() {
|
||||
QApplication::clipboard()->setText(text());
|
||||
});
|
||||
}
|
||||
|
||||
void CopyableElideLabel::showContextMenu(const QPoint& pos)
|
||||
{
|
||||
if (text().length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPoint globalPos = mapToGlobal(pos);
|
||||
|
||||
QMenu contextMenu;
|
||||
contextMenu.addAction(actionCopy);
|
||||
|
||||
contextMenu.exec(globalPos);
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
|
||||
|
||||
This file is part of Tox Qt GUI.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef COPYABLEELIDELABEL_HPP
|
||||
#define COPYABLEELIDELABEL_HPP
|
||||
|
||||
#include "elidelabel.h"
|
||||
|
||||
class CopyableElideLabel : public ElideLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CopyableElideLabel(QWidget* parent = 0);
|
||||
|
||||
private:
|
||||
QAction* actionCopy;
|
||||
|
||||
private slots:
|
||||
void showContextMenu(const QPoint& pos);
|
||||
|
||||
};
|
||||
|
||||
#endif // COPYABLEELIDELABEL_HPP
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
|
||||
|
||||
This file is part of Tox Qt GUI.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "editablelabelwidget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
#include <QFontMetrics>
|
||||
#include <QMouseEvent>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
ClickableCopyableElideLabel::ClickableCopyableElideLabel(QWidget* parent) :
|
||||
CopyableElideLabel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool ClickableCopyableElideLabel::event(QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
if (mouseEvent->button() == Qt::LeftButton) {
|
||||
emit clicked();
|
||||
}
|
||||
} else if (event->type() == QEvent::Enter) {
|
||||
QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor));
|
||||
} else if (event->type() == QEvent::Leave) {
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
return CopyableElideLabel::event(event);
|
||||
}
|
||||
|
||||
EditableLabelWidget::EditableLabelWidget(QWidget* parent) :
|
||||
QStackedWidget(parent), isSubmitting(false)
|
||||
{
|
||||
label = new ClickableCopyableElideLabel(this);
|
||||
|
||||
connect(label, &ClickableCopyableElideLabel::clicked, this, &EditableLabelWidget::onLabelClicked);
|
||||
|
||||
lineEdit = new EscLineEdit(this);
|
||||
lineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||
lineEdit->setMinimumHeight(label->fontMetrics().lineSpacing() + LINE_SPACING_OFFSET);
|
||||
|
||||
// Set dark background for >windows
|
||||
//QColor toxDarkAsMySoul(28,28,28);
|
||||
//QPalette darkPal;
|
||||
//darkPal.setColor(QPalette::Window, toxDarkAsMySoul);
|
||||
//darkPal.setColor(QPalette::Base, toxDarkAsMySoul);
|
||||
//lineEdit->setPalette(darkPal);
|
||||
|
||||
connect(lineEdit, &EscLineEdit::editingFinished, this, &EditableLabelWidget::onLabelChangeSubmited);
|
||||
connect(lineEdit, &EscLineEdit::escPressed, this, &EditableLabelWidget::onLabelChangeCancelled);
|
||||
|
||||
addWidget(label);
|
||||
addWidget(lineEdit);
|
||||
|
||||
setCurrentWidget(label);
|
||||
}
|
||||
|
||||
void EditableLabelWidget::setText(const QString& text)
|
||||
{
|
||||
label->setText(text);
|
||||
lineEdit->setText(text);
|
||||
}
|
||||
|
||||
QString EditableLabelWidget::text()
|
||||
{
|
||||
return label->text();
|
||||
}
|
||||
|
||||
void EditableLabelWidget::onLabelChangeSubmited()
|
||||
{
|
||||
if (isSubmitting) {
|
||||
return;
|
||||
}
|
||||
isSubmitting = true;
|
||||
|
||||
QString oldText = label->text();
|
||||
QString newText = lineEdit->text();
|
||||
// `lineEdit->clearFocus()` triggers `onLabelChangeSubmited()`, we use `isSubmitting` as a workaround
|
||||
lineEdit->clearFocus();
|
||||
setCurrentWidget(label);
|
||||
|
||||
if (oldText != newText) {
|
||||
label->setText(newText);
|
||||
emit textChanged(newText, oldText);
|
||||
}
|
||||
|
||||
isSubmitting = false;
|
||||
}
|
||||
|
||||
void EditableLabelWidget::onLabelChangeCancelled()
|
||||
{
|
||||
// order of calls matters, since clearFocus() triggers EditableLabelWidget::onLabelChangeSubmited()
|
||||
lineEdit->setText(label->text());
|
||||
lineEdit->clearFocus();
|
||||
setCurrentWidget(label);
|
||||
}
|
||||
|
||||
void EditableLabelWidget::onLabelClicked()
|
||||
{
|
||||
setCurrentWidget(lineEdit);
|
||||
lineEdit->setFocus();
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
|
||||
|
||||
This file is part of Tox Qt GUI.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef EDITABLELABELWIDGET_HPP
|
||||
#define EDITABLELABELWIDGET_HPP
|
||||
|
||||
#include "copyableelidelabel.h"
|
||||
#include "esclineedit.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QStackedWidget>
|
||||
|
||||
class ClickableCopyableElideLabel : public CopyableElideLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ClickableCopyableElideLabel(QWidget* parent = 0);
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) Q_DECL_OVERRIDE;
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
};
|
||||
|
||||
class EditableLabelWidget : public QStackedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EditableLabelWidget(QWidget* parent = 0);
|
||||
|
||||
ClickableCopyableElideLabel* label;
|
||||
EscLineEdit* lineEdit;
|
||||
|
||||
void setText(const QString& text);
|
||||
QString text();
|
||||
|
||||
private:
|
||||
static const int LINE_SPACING_OFFSET = 2;
|
||||
bool isSubmitting;
|
||||
|
||||
private slots:
|
||||
void onLabelChangeSubmited();
|
||||
void onLabelChangeCancelled();
|
||||
void onLabelClicked();
|
||||
|
||||
signals:
|
||||
void textChanged(QString newText, QString oldText);
|
||||
|
||||
};
|
||||
|
||||
#endif // EDITABLELABELWIDGET_HPP
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
|
||||
|
||||
This file is part of Tox Qt GUI.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "elidelabel.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QEvent>
|
||||
|
||||
ElideLabel::ElideLabel(QWidget *parent) :
|
||||
QLabel(parent), _textElide(false), _textElideMode(Qt::ElideNone), _showToolTipOnElide(false)
|
||||
{
|
||||
}
|
||||
|
||||
void ElideLabel::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QFrame::paintEvent(event);
|
||||
QPainter p(this);
|
||||
QFontMetrics metrics(font());
|
||||
if ((metrics.width(text()) > contentsRect().width()) && textElide()) {
|
||||
QString elidedText = fontMetrics().elidedText(text(), textElideMode(), rect().width());
|
||||
p.drawText(rect(), alignment(), elidedText);
|
||||
} else {
|
||||
QLabel::paintEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
bool ElideLabel::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::ToolTip) {
|
||||
QFontMetrics metrics(font());
|
||||
if ((metrics.width(text()) > contentsRect().width()) && textElide() && showToolTipOnElide()) {
|
||||
setToolTip(text());
|
||||
} else {
|
||||
setToolTip("");
|
||||
}
|
||||
}
|
||||
|
||||
return QLabel::event(event);
|
||||
}
|
||||
|
||||
void ElideLabel::setTextElide(bool set)
|
||||
{
|
||||
_textElide = set;
|
||||
}
|
||||
|
||||
bool ElideLabel::textElide() const
|
||||
{
|
||||
return _textElide;
|
||||
}
|
||||
|
||||
void ElideLabel::setTextElideMode(Qt::TextElideMode mode)
|
||||
{
|
||||
_textElideMode = mode;
|
||||
}
|
||||
|
||||
Qt::TextElideMode ElideLabel::textElideMode() const
|
||||
{
|
||||
return _textElideMode;
|
||||
}
|
||||
|
||||
void ElideLabel::setShowToolTipOnElide(bool show)
|
||||
{
|
||||
_showToolTipOnElide = show;
|
||||
}
|
||||
|
||||
bool ElideLabel::showToolTipOnElide()
|
||||
{
|
||||
return _showToolTipOnElide;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
|
||||
|
||||
This file is part of Tox Qt GUI.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef ELIDELABEL_HPP
|
||||
#define ELIDELABEL_HPP
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
class ElideLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ElideLabel(QWidget *parent = 0);
|
||||
|
||||
void setTextElide(bool set);
|
||||
bool textElide() const;
|
||||
|
||||
void setTextElideMode(Qt::TextElideMode mode);
|
||||
Qt::TextElideMode textElideMode() const;
|
||||
|
||||
void setShowToolTipOnElide(bool show);
|
||||
bool showToolTipOnElide();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
|
||||
bool event(QEvent *e) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
bool _textElide;
|
||||
Qt::TextElideMode _textElideMode;
|
||||
|
||||
bool _showToolTipOnElide;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // ELIDELABEL_HPP
|