diff --git a/README.md b/README.md index 9609fee0c..6264fbf25 100644 --- a/README.md +++ b/README.md @@ -15,31 +15,73 @@ However, it is not a fork. - Tox DNS - Translations in various languages -

Requirements

+

Downloads

-This client runs on Windows, Linux and Mac natively, but is not build regularly for Linux
-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.
Windows download
-Mac download
-Linux download (12st July 2014 20:30 GMT)
-Note that the Linux download has not been tested and may not be up to date.
+Mac download
+Linux download (click "Last successful artifacts")

Screenshots

Note: The screenshots may not always be up to date, but they should give a good idea of the general look and features
-

Compiling

-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. +

Compiling on GNU-Linux

+

Acquiring dependencies

+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 its installation guide for more details): +```bash +sudo apt-get install libtool autotools-dev automake checkinstall check git yasm libopus-dev libvpx-dev +``` -

OSX Install Guide

+Finally, qTox itself requires OpenAL and OpenCV: +```bash +sudo apt-get install libopenal-dev libopencv-dev +``` + +

Compilation

+ +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! + +

Building packages

+ +qTox now has the experimental and probably-dodgy ability to package itself (in .deb +form natively, and .rpm form with alien). + +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. + +

OSX Easy Install

+ +Since https://github.com/ReDetection/homebrew-qtox you can easily install qtox with homebrew +```bash +brew install --HEAD ReDetection/qtox/qtox +``` + +

OSX Full Install Guide

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'. diff --git a/audiobuffer.cpp b/audiobuffer.cpp deleted file mode 100644 index 9ca0960bb..000000000 --- a/audiobuffer.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - 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(); -} diff --git a/bootstrap.sh b/bootstrap.sh index cc25129f8..2f7067fb6 100755 --- a/bootstrap.sh +++ b/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 diff --git a/core.cpp b/core.cpp index 2403dcc2b..fbba751c0 100644 --- a/core.cpp +++ b/core.cpp @@ -20,6 +20,9 @@ #include "settings.h" #include "widget/widget.h" +#include +#include + #include #include #include @@ -33,10 +36,6 @@ const QString Core::CONFIG_FILE_NAME = "data"; const QString Core::TOX_EXT = ".tox"; QList Core::fileSendQueue; QList 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)->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)->fileTransferAccepted(*file); qDebug() << "Core: File control callback, file accepted"; - file->sendFuture = QtConcurrent::run(sendAllFileData, static_cast(core), file); + file->sendTimer = new QTimer(static_cast(core)); + connect(file->sendTimer, &QTimer::timeout, std::bind(sendAllFileData,static_cast(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)->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)->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)->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)->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& 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 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(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); - - 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)->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)->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); - - 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)->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)->avStart(friendId, call_index, false); - } -} - -void Core::onAvCancel(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_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)->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); - - 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)->avEnd(friendId, call_index); -} - -void Core::onAvRinging(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_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)->avRinging(friendId, call_index, true); - } - else - { - qDebug() << QString("Core: AV ringing with %1 without video").arg(friendId); - emit static_cast(core)->avRinging(friendId, call_index, false); - } -} - -void Core::onAvStarting(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_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)->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)->avStarting(friendId, call_index, false); - } -} - -void Core::onAvEnding(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_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)->avEnding(friendId, call_index); -} - -void Core::onAvRequestTimeout(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_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)->avRequestTimeout(friendId, call_index); -} - -void Core::onAvPeerTimeout(void* _toxav, int32_t call_index, void* core) -{ - ToxAv* toxav = static_cast(_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)->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& friendAddresses = Settings::getInstance().friendAddresses; + for (QString addr : friendAddresses) + if (addr.toUpper().contains(id)) + return addr; + + return id; } diff --git a/core.h b/core.h index 9ba3e2778..5de02e7f7 100644 --- a/core.h +++ b/core.h @@ -17,11 +17,17 @@ #ifndef CORE_HPP #define CORE_HPP -#include "audiobuffer.h" - #include #include +#if defined(__APPLE__) && defined(__MACH__) + #include + #include +#else + #include + #include +#endif + #include #include #include @@ -30,16 +36,12 @@ #include #include #include -#include -#include -#include -#include #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 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 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 diff --git a/coreav.cpp b/coreav.cpp new file mode 100644 index 000000000..2a3ce7a67 --- /dev/null +++ b/coreav.cpp @@ -0,0 +1,540 @@ +/* + Copyright (C) 2013 by Maxim Biro + + 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 "<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); + + 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)->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); + + 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)->avEnd(friendId, call_index); +} + +void Core::onAvRinging(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_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)->avRinging(friendId, call_index, true); + } + else + { + qDebug() << QString("Core: AV ringing with %1 without video").arg(friendId); + emit static_cast(core)->avRinging(friendId, call_index, false); + } +} + +void Core::onAvStarting(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_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)->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)->avStarting(friendId, call_index, false); + } + + delete transSettings; +} + +void Core::onAvEnding(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_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)->avEnding(friendId, call_index); +} + +void Core::onAvRequestTimeout(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_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)->avRequestTimeout(friendId, call_index); +} + +void Core::onAvPeerTimeout(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_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)->avPeerTimeout(friendId, call_index); +} + + +void Core::onAvInvite(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_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)->avInvite(friendId, call_index, true); + } + else + { + qDebug() << QString("Core: AV invite from %1 without video").arg(friendId); + emit static_cast(core)->avInvite(friendId, call_index, false); + } + + delete transSettings; +} + +void Core::onAvStart(void* _toxav, int32_t call_index, void* core) +{ + ToxAv* toxav = static_cast(_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)->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)->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 "< Sat, 30 Aug 2014 23:27:47 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 000000000..ec635144f --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 000000000..ef73726b7 --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: qtox +Maintainer: John Smith +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. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 000000000..6770976a6 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,28 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: qtox +Upstream-Contact: John Smith +Source: https://github.com/tux3/qTox + +Files: * +Copyright: 2014 John Smith +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'. diff --git a/debian/rules b/debian/rules new file mode 100644 index 000000000..6815966a4 --- /dev/null +++ b/debian/rules @@ -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 diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 000000000..163aaf8d8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/friend.cpp b/friend.cpp index b024d2dda..ec8dd3860 100644 --- a/friend.cpp +++ b/friend.cpp @@ -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; } diff --git a/friend.h b/friend.h index 8730ae435..8a3a2427a 100644 --- a/friend.h +++ b/friend.h @@ -36,7 +36,7 @@ public: int friendId; QString userId; ChatForm* chatForm; - int hasNewMessages; + int hasNewEvents; Status friendStatus; QPixmap avatar; }; diff --git a/friendlist.cpp b/friendlist.cpp index 08ecb903d..6547c408c 100644 --- a/friendlist.cpp +++ b/friendlist.cpp @@ -17,6 +17,7 @@ #include "friend.h" #include "friendlist.h" #include +#include QList FriendList::friendList; diff --git a/main.cpp b/main.cpp index f61cbadf4..d2d34b138 100644 --- a/main.cpp +++ b/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 diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 000000000..7f9cd12c5 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,3262 @@ + + + MainWindow + + + + 0 + 0 + 716 + 543 + + + + + 640 + 420 + + + + qTox + + + + :/img/icon.png:/img/icon.png + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 23 + + + + + 16777215 + 23 + + + + + 4 + + + 5 + + + 0 + + + 1 + + + 0 + + + + + + 16 + 16 + + + + + 16 + 16 + + + + + 16 + 16 + + + + QToolButton::InstantPopup + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 15 + 1 + + + + + + + + + 100 + 22 + + + + + 16777215 + 22 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 134 + 20 + + + + + + + + + 22 + 22 + + + + + 22 + 22 + + + + + 22 + 22 + + + + true + + + + + + + + 22 + 22 + + + + + 22 + 22 + + + + + 22 + 22 + + + + true + + + + + + + + 22 + 22 + + + + + 22 + 22 + + + + + 22 + 22 + + + + true + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + true + + + false + + + QSplitter{ + color: rgb(255, 255, 255); + background-color: rgb(255, 255, 255); + alternate-background-color: rgb(255, 255, 255); + border-color: rgb(255, 255, 255); + gridline-color: rgb(255, 255, 255); + selection-color: rgb(255, 255, 255); + selection-background-color: rgb(255, 255, 255); +} +QSplitter:handle{ + color: rgb(255, 255, 255); + background-color: rgb(255, 255, 255); +} + + + Qt::Horizontal + + + 6 + + + false + + + + + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 28 + 28 + 28 + + + + + + + 42 + 42 + 42 + + + + + + + 35 + 35 + 35 + + + + + + + 14 + 14 + 14 + + + + + + + 18 + 18 + 18 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 28 + 28 + 28 + + + + + + + 0 + 0 + 0 + + + + + + + 14 + 14 + 14 + + + + + + + 28 + 28 + 28 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 28 + 28 + 28 + + + + + + + 42 + 42 + 42 + + + + + + + 35 + 35 + 35 + + + + + + + 14 + 14 + 14 + + + + + + + 18 + 18 + 18 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 28 + 28 + 28 + + + + + + + 0 + 0 + 0 + + + + + + + 14 + 14 + 14 + + + + + + + 28 + 28 + 28 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 14 + 14 + 14 + + + + + + + 28 + 28 + 28 + + + + + + + 42 + 42 + 42 + + + + + + + 35 + 35 + 35 + + + + + + + 14 + 14 + 14 + + + + + + + 18 + 18 + 18 + + + + + + + 14 + 14 + 14 + + + + + + + 255 + 255 + 255 + + + + + + + 14 + 14 + 14 + + + + + + + 28 + 28 + 28 + + + + + + + 28 + 28 + 28 + + + + + + + 0 + 0 + 0 + + + + + + + 28 + 28 + 28 + + + + + + + 28 + 28 + 28 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 28 + 28 + 28 + + + + + + + 42 + 42 + 42 + + + + + + + 35 + 35 + 35 + + + + + + + 14 + 14 + 14 + + + + + + + 18 + 18 + 18 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 28 + 28 + 28 + + + + + + + 0 + 0 + 0 + + + + + + + 14 + 14 + 14 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 28 + 28 + 28 + + + + + + + 42 + 42 + 42 + + + + + + + 35 + 35 + 35 + + + + + + + 14 + 14 + 14 + + + + + + + 18 + 18 + 18 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 28 + 28 + 28 + + + + + + + 0 + 0 + 0 + + + + + + + 14 + 14 + 14 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 14 + 14 + 14 + + + + + + + 28 + 28 + 28 + + + + + + + 42 + 42 + 42 + + + + + + + 35 + 35 + 35 + + + + + + + 14 + 14 + 14 + + + + + + + 18 + 18 + 18 + + + + + + + 14 + 14 + 14 + + + + + + + 255 + 255 + 255 + + + + + + + 14 + 14 + 14 + + + + + + + 28 + 28 + 28 + + + + + + + 28 + 28 + 28 + + + + + + + 0 + 0 + 0 + + + + + + + 28 + 28 + 28 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + + + 0 + + + + + + 40 + 40 + + + + + 40 + 40 + + + + + + + :/img/contact.png + + + true + + + + + + + 0 + + + + + true + + + + 1 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 240 + 240 + 240 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + 11 + 75 + true + + + + Your name + + + + + + + + 1 + 0 + + + + + + + + + 193 + 193 + 193 + + + + + + + 193 + 193 + 193 + + + + + + + + + 193 + 193 + 193 + + + + + + + 193 + 193 + 193 + + + + + + + + + 14 + 14 + 14 + + + + + + + 14 + 14 + 14 + + + + + + + + + 8 + + + + Your status + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + + 0 + 0 + + + + + 20 + 40 + + + + Qt::NoFocus + + + + + + + 10 + 10 + + + + false + + + false + + + false + + + true + + + + + + + + + + + + + 0 + 0 + + + + Qt::LeftToRight + + + true + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 263 + 373 + + + + + + + + + + true + + + + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 28 + 28 + 28 + + + + + + + 42 + 42 + 42 + + + + + + + 35 + 35 + 35 + + + + + + + 14 + 14 + 14 + + + + + + + 18 + 18 + 18 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 28 + 28 + 28 + + + + + + + 0 + 0 + 0 + + + + + + + 14 + 14 + 14 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 28 + 28 + 28 + + + + + + + 42 + 42 + 42 + + + + + + + 35 + 35 + 35 + + + + + + + 14 + 14 + 14 + + + + + + + 18 + 18 + 18 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 28 + 28 + 28 + + + + + + + 0 + 0 + 0 + + + + + + + 14 + 14 + 14 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 14 + 14 + 14 + + + + + + + 28 + 28 + 28 + + + + + + + 42 + 42 + 42 + + + + + + + 35 + 35 + 35 + + + + + + + 14 + 14 + 14 + + + + + + + 18 + 18 + 18 + + + + + + + 14 + 14 + 14 + + + + + + + 255 + 255 + 255 + + + + + + + 14 + 14 + 14 + + + + + + + 28 + 28 + 28 + + + + + + + 28 + 28 + 28 + + + + + + + 0 + 0 + 0 + + + + + + + 28 + 28 + 28 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 55 + 35 + + + + + 55 + 35 + + + + Qt::NoFocus + + + Add friends + + + + + + + :/img/add.png:/img/add.png + + + true + + + + + + + + 55 + 35 + + + + Qt::NoFocus + + + Create a group chat + + + + + + + :/img/group.png:/img/group.png + + + true + + + + + + + + 55 + 35 + + + + + 55 + 35 + + + + Qt::NoFocus + + + View completed file transfers + + + + + + + :/img/transfer.png:/img/transfer.png + + + true + + + + + + + + 55 + 35 + + + + + 55 + 35 + + + + Qt::NoFocus + + + Change your settings + + + + + + + :/img/settings.png:/img/settings.png + + + true + + + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 170 + 170 + 170 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 127 + 127 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 60 + + + + + 16777215 + 60 + + + + true + + + + + + + + 0 + 1 + + + + + 16777215 + 1 + + + + + + + + + 193 + 193 + 193 + + + + + + + + + 193 + 193 + 193 + + + + + + + + + 127 + 127 + 127 + + + + + + + + QFrame::HLine + + + QFrame::Plain + + + + + + + + 0 + 0 + + + + + 375 + 0 + + + + + + + + + + + + + true + + + + 0 + 0 + 716 + 23 + + + + + + + Close + + + Ctrl+Q + + + + + + AdjustingScrollArea + QScrollArea +
widget/adjustingscrollarea.h
+ 1 +
+ + CroppingLabel + QLabel +
widget/croppinglabel.h
+
+
+ + + + +
diff --git a/qtox.pro b/qtox.pro index e12cc4cd1..679e6d17e 100644 --- a/qtox.pro +++ b/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 diff --git a/res.qrc b/res.qrc index cb2fa192b..f413e0209 100644 --- a/res.qrc +++ b/res.qrc @@ -111,5 +111,18 @@ ui/statusButton/menu_indicator.png translations/de.qm translations/it.qm + ui/emoticonWidget/dot_page.png + ui/emoticonWidget/dot_page_current.png + ui/emoticonWidget/emoticonWidget.css + ui/emoticonWidget/dot_page_hover.png + ui/volButton/volButton.png + ui/volButton/volButtonHover.png + ui/volButton/volButtonPressed.png + ui/micButton/micButton.png + ui/micButton/micButtonDisabled.png + ui/micButton/micButtonHover.png + ui/micButton/micButtonPressed.png + ui/micButton/micButton.css + ui/volButton/volButton.css diff --git a/res/settings.ini b/res/settings.ini index e1420de5b..25d126190 100644 --- a/res/settings.ini +++ b/res/settings.ini @@ -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 diff --git a/settings.cpp b/settings.cpp index 96eb9ae15..434236d4e 100644 --- a/settings.cpp +++ b/settings.cpp @@ -15,6 +15,8 @@ */ #include "settings.h" +#include "smileypack.h" +#include "widget/widget.h" #include #include @@ -22,6 +24,7 @@ #include #include #include +#include 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; diff --git a/settings.h b/settings.h index 3ad240bc7..322ccea94 100644 --- a/settings.h +++ b/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 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; diff --git a/smileypack.cpp b/smileypack.cpp new file mode 100644 index 000000000..cca56583c --- /dev/null +++ b/smileypack.cpp @@ -0,0 +1,199 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include +#include +#include + +SmileyPack::SmileyPack() +{ + load(Settings::getInstance().getSmileyPack()); + connect(&Settings::getInstance(), &Settings::smileyPackChanged, this, &SmileyPack::onSmileyPackChanged); +} + +SmileyPack& SmileyPack::getInstance() +{ + static SmileyPack smileyPack; + return smileyPack; +} + +QList > SmileyPack::listSmileyPacks(const QString &path) +{ + QList > 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(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: + * + * + * + * :) + * :-) + * + * + * :( + * :-( + * + * + */ + + 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 SmileyPack::getEmoticons() const +{ + return emoticons; +} + +QString SmileyPack::getAsRichText(const QString &key) +{ + return ""; +} + +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()); +} diff --git a/smileypack.h b/smileypack.h new file mode 100644 index 000000000..9f4ea31ba --- /dev/null +++ b/smileypack.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include +#include +#include + +#define SMILEYPACK_DEFAULT_PATH "./smileys" + +//maps emoticons to smileys +class SmileyPack : public QObject +{ + Q_OBJECT +public: + static SmileyPack& getInstance(); + static QList> listSmileyPacks(const QString& path = SMILEYPACK_DEFAULT_PATH); + static bool isValid(const QString& filename); + + bool load(const QString &filename); + QString smileyfied(QString msg); + QList 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 filenameTable; // matches an emoticon to its corresponding smiley ie. ":)" -> "happy.png" + QHash imgCache; // (scaled) representation of a smiley ie. "happy.png" -> data + QList emoticons; // {{ ":)", ":-)" }, {":(", ...}, ... } + QString path; // directory containing the cfg and image files +}; + +#endif // SMILEYPACK_H diff --git a/smileys/default/angry.png b/smileys/default/angry.png new file mode 100644 index 000000000..e096ce5ff Binary files /dev/null and b/smileys/default/angry.png differ diff --git a/smileys/default/cool.png b/smileys/default/cool.png new file mode 100644 index 000000000..9c5f1b13a Binary files /dev/null and b/smileys/default/cool.png differ diff --git a/smileys/default/crying.png b/smileys/default/crying.png new file mode 100644 index 000000000..b03be57a4 Binary files /dev/null and b/smileys/default/crying.png differ diff --git a/smileys/default/emoticons.xml b/smileys/default/emoticons.xml new file mode 100644 index 000000000..d1fe8c715 --- /dev/null +++ b/smileys/default/emoticons.xml @@ -0,0 +1,85 @@ + + + + 😊 + :) + :-) + + + + 😎 + 8-) + 8) + + + + 😲 + :O + :-O + + + + 😋 + :p + :P + + + + 😕 + :/ + :-/ + + + + 😉 + ;) + ;-) + + + + 😖 + :( + :-( + + + + 😢 + ;( + ;-( + + + + 😃 + :D + :-D + + + + 😐 + :| + :-| + + + + 😄 + ;D + ;-D + + + + 😠 + :@ + + + + 😨 + D: + + + + 😆 + xD + XD + + + diff --git a/smileys/default/happy.png b/smileys/default/happy.png new file mode 100644 index 000000000..bddcc567b Binary files /dev/null and b/smileys/default/happy.png differ diff --git a/smileys/default/laugh.png b/smileys/default/laugh.png new file mode 100644 index 000000000..163b0c4c7 Binary files /dev/null and b/smileys/default/laugh.png differ diff --git a/smileys/default/laugh_closed_eyes.png b/smileys/default/laugh_closed_eyes.png new file mode 100644 index 000000000..fc294a510 Binary files /dev/null and b/smileys/default/laugh_closed_eyes.png differ diff --git a/smileys/default/plain.png b/smileys/default/plain.png new file mode 100644 index 000000000..3916b06c6 Binary files /dev/null and b/smileys/default/plain.png differ diff --git a/smileys/default/raw.svg b/smileys/default/raw.svg new file mode 100644 index 000000000..8ca952f95 --- /dev/null +++ b/smileys/default/raw.svg @@ -0,0 +1,889 @@ + + + + + qTox + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + qTox + + Created for the qTox project. Inspired by the "Never mind!" smiley pack. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/smileys/default/sad.png b/smileys/default/sad.png new file mode 100644 index 000000000..1fdd9909d Binary files /dev/null and b/smileys/default/sad.png differ diff --git a/smileys/default/scared.png b/smileys/default/scared.png new file mode 100644 index 000000000..c8a904977 Binary files /dev/null and b/smileys/default/scared.png differ diff --git a/smileys/default/smile.png b/smileys/default/smile.png new file mode 100644 index 000000000..b13fa01d2 Binary files /dev/null and b/smileys/default/smile.png differ diff --git a/smileys/default/stunned.png b/smileys/default/stunned.png new file mode 100644 index 000000000..cbdbaa6c4 Binary files /dev/null and b/smileys/default/stunned.png differ diff --git a/smileys/default/tongue.png b/smileys/default/tongue.png new file mode 100644 index 000000000..946419fc1 Binary files /dev/null and b/smileys/default/tongue.png differ diff --git a/smileys/default/uncertain.png b/smileys/default/uncertain.png new file mode 100644 index 000000000..d6cdddbed Binary files /dev/null and b/smileys/default/uncertain.png differ diff --git a/smileys/default/wink.png b/smileys/default/wink.png new file mode 100644 index 000000000..ab5858c2d Binary files /dev/null and b/smileys/default/wink.png differ diff --git a/style.cpp b/style.cpp new file mode 100644 index 000000000..609dcc277 --- /dev/null +++ b/style.cpp @@ -0,0 +1,35 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include + +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(); +} diff --git a/widget/tool/clickablelabel.cpp b/style.h similarity index 78% rename from widget/tool/clickablelabel.cpp rename to style.h index 9e61becff..77a09ce25 100644 --- a/widget/tool/clickablelabel.cpp +++ b/style.h @@ -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 -void ClickableLabel::mousePressEvent(QMouseEvent*) +class Style { - emit clicked(); -} +public: + static QString get(const QString& filename); +private: + Style(); +}; + +#endif // STYLE_H diff --git a/tools/buildPackages.sh b/tools/buildPackages.sh new file mode 100755 index 000000000..d0ec345c0 --- /dev/null +++ b/tools/buildPackages.sh @@ -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 diff --git a/translations/de.ts b/translations/de.ts index 63b4dada9..ab61ba9ad 100644 --- a/translations/de.ts +++ b/translations/de.ts @@ -1,75 +1,75 @@ - + AddFriendForm - + Add Friends Freunde hinzufügen - + Tox ID Tox ID of the person you're sending a friend request to Tox ID - + Message The message you send in friend requests Nachricht - + Send friend request Freundschaftseinladung versenden - + Tox me maybe? Default message in friend requests if the field is left blank. Write something appropriate! Lass uns Toxen! - + Please fill in a valid Tox ID Tox ID of the friend you're sending a friend request to Bitte gib eine gültige Tox ID ein - + This address does not exist The DNS gives the Tox ID associated to toxme.se addresses - + Error while looking up DNS The DNS gives the Tox ID associated to toxme.se addresses Fehler beim Auflösen des DNS - + Unexpected number of text records Error with the DNS Unererwartete Anzahl von Texteinträgen - + Unexpected number of values in text record Error with the DNS Unerwartete Anzahl von Werten innerhalb des Texteintrages - + The DNS lookup does not contain any Tox ID Error with the DNS Der DNS Eintrag enthält keine gültige TOX ID - - + + The DNS lookup does not contain a valid Tox ID Error with the DNS Der DNS Eintrag enthält keine gültige TOX ID @@ -78,12 +78,12 @@ Camera - + Camera eror Kamerafehler - + Camera format %1 not supported, can't use the camera Kameraformat %1 wird nicht unterstützt. Die Kamera kann nicht verwendet werden @@ -91,13 +91,13 @@ ChatForm - + Send a file Datei versenden - - + + Save chat log Chatverlauf speichern @@ -113,11 +113,42 @@ FileTransfertWidget - + Save a file Title of the file saving dialog Datei speichern + + + Location not writable + Title of permissions popup + + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + + + + + FilesForm + + + Transfered Files + "Headline" of the window + + + + + Downloads + + + + + Uploads + + FriendRequestDialog @@ -158,19 +189,19 @@ FriendWidget - + Copy friend ID Menu to copy the Tox ID of that friend Tox ID kopieren - + Invite in group Menu to invite a friend in a groupchat In Gruppe einladen - + Remove friend Menu to remove the friend from our friendlist Freund entfernen @@ -179,23 +210,23 @@ GroupChatForm - + %1 users in chat Number of users in chat %1 Personen im Chat - + <Unknown> <Unbekannt> - + %1 users in chat %1 Personen im Chat - + Save chat log Chatverlauf speichern @@ -203,28 +234,76 @@ GroupWidget - - + + %1 users in chat %1 Personen im Chat - - + + 0 users in chat 0 Personen im Chat - + Quit group Menu to quit a groupchat Gruppe verlassen + + MainWindow + + + qTox + + + + + Your name + Dein Name + + + + Your status + Dein Status + + + + Add friends + + + + + Create a group chat + + + + + View completed file transfers + + + + + Change your settings + + + + + Close + Schließen + + + + Ctrl+Q + Strg+Q + + SelfCamView - + Tox video test Title of the window to test the video/webcam Tox Video testen @@ -233,83 +312,105 @@ SettingsForm - + User Settings "Headline" of the window Einstellungen - + Name Username/nick Benutzername - + Status Status message Status - + + (click here to copy) + Click on this text to copy TID to clipboard + + + + Test video Text on a button to test the video/webcam Video testen - + Enable IPv6 (recommended) Text on a checkbox to enable IPv6 IPv6 aktivieren (empfohlen) - + Use translations Text on a checkbox to enable translations + + + Make Tox portable + Text on a checkbox to make qTox a portable application + + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + + + + + Smiley Pack + Text on smiley pack label + + Widget - Tox - Tox + Tox - Your name - Dein Name + Dein Name - Your status - Dein Status + Dein Status - Close - Schließen + Schließen - Ctrl+Q - Strg+Q + Strg+Q + Online Button to set your status to 'Online' - Online + Online + Away Button to set your status to 'Away' - Abwesend + Abwesend + Busy Button to set your status to 'Busy' - Beschäftigt + Beschäftigt diff --git a/translations/fr.ts b/translations/fr.ts index 3851b7684..435d022c7 100644 --- a/translations/fr.ts +++ b/translations/fr.ts @@ -1,75 +1,75 @@ - + AddFriendForm - + Add Friends Ajouter des amis - + Tox ID Tox ID of the person you're sending a friend request to ID Tox - + Message The message you send in friend requests Message - + Send friend request Envoyer la demande d'ami - + Tox me maybe? Default message in friend requests if the field is left blank. Write something appropriate! Je souhaiterais vous ajouter à mes contacts - + Please fill in a valid Tox ID Tox ID of the friend you're sending a friend request to Merci de remplir un ID Tox valide - + This address does not exist The DNS gives the Tox ID associated to toxme.se addresses - + Error while looking up DNS The DNS gives the Tox ID associated to toxme.se addresses Erreur en consultant le serveur DNS - + Unexpected number of text records Error with the DNS Nombre d'entrées texte innatendu - + Unexpected number of values in text record Error with the DNS Nombre d'entrées numériques dans l'entrée texte innatendu - + The DNS lookup does not contain any Tox ID Error with the DNS La réponse DNS ne contient aucun ID Tox - - + + The DNS lookup does not contain a valid Tox ID Error with the DNS La réponse DNS ne contient pas d'ID Tox valide @@ -78,12 +78,12 @@ Camera - + Camera eror Erreur de caméra - + Camera format %1 not supported, can't use the camera Format %1 de la caméra non supporté, impossible de l'utiliser @@ -91,13 +91,13 @@ ChatForm - + Send a file Envoyer un fichier - - + + Save chat log Sauvegarder l'historique de conversation @@ -113,11 +113,42 @@ FileTransfertWidget - + Save a file Title of the file saving dialog Sauvegarder un fichier + + + Location not writable + Title of permissions popup + + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + + + + + FilesForm + + + Transfered Files + "Headline" of the window + + + + + Downloads + + + + + Uploads + + FriendRequestDialog @@ -158,19 +189,19 @@ FriendWidget - + Copy friend ID Menu to copy the Tox ID of that friend Copier l'ID ami - + Invite in group Menu to invite a friend in a groupchat Inviter dans un groupe - + Remove friend Menu to remove the friend from our friendlist Supprimer ami @@ -179,23 +210,23 @@ GroupChatForm - + %1 users in chat Number of users in chat %1 personnes - + <Unknown> <Inconnu> - + %1 users in chat %1 personnes - + Save chat log Sauvegarder l'historique de conversation @@ -203,28 +234,76 @@ GroupWidget - - + + %1 users in chat %1 personnes - - + + 0 users in chat 0 personnes - + Quit group Menu to quit a groupchat Quitter le groupe + + MainWindow + + + qTox + + + + + Your name + Votre nom + + + + Your status + Votre status + + + + Add friends + + + + + Create a group chat + + + + + View completed file transfers + + + + + Change your settings + + + + + Close + Fermer + + + + Ctrl+Q + Ctrl+Q + + SelfCamView - + Tox video test Title of the window to test the video/webcam Test vidéo Tox @@ -233,83 +312,105 @@ SettingsForm - + User Settings "Headline" of the window Configuration - + Name Username/nick Nom - + Status Status message Status - + + (click here to copy) + Click on this text to copy TID to clipboard + + + + Test video Text on a button to test the video/webcam Tester la vidéo - + Enable IPv6 (recommended) Text on a checkbox to enable IPv6 Activer IPv6 (recommandé) - + Use translations Text on a checkbox to enable translations + + + Make Tox portable + Text on a checkbox to make qTox a portable application + + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + + + + + Smiley Pack + Text on smiley pack label + + Widget - Tox - Tox + Tox - Your name - Votre nom + Votre nom - Your status - Votre status + Votre status - Close - Fermer + Fermer - Ctrl+Q - Ctrl+Q + Ctrl+Q + Online Button to set your status to 'Online' - Connecté + Connecté + Away Button to set your status to 'Away' - Indisponnible + Indisponnible + Busy Button to set your status to 'Busy' - Occupé + Occupé diff --git a/translations/it.qm b/translations/it.qm index d8c42654a..ea977d5d6 100644 Binary files a/translations/it.qm and b/translations/it.qm differ diff --git a/translations/it.ts b/translations/it.ts index cde73cd0f..7ada13790 100644 --- a/translations/it.ts +++ b/translations/it.ts @@ -1,6 +1,6 @@ - + AddFriendForm @@ -26,50 +26,50 @@ Invia richiesta d'amicizia - + Tox me maybe? Default message in friend requests if the field is left blank. Write something appropriate! Permettimi di aggiungerti alla mia lista contatti - + Please fill in a valid Tox ID Tox ID of the friend you're sending a friend request to Inserisci un Tox ID valido - + This address does not exist The DNS gives the Tox ID associated to toxme.se addresses Questo indirizzo non esiste - + Error while looking up DNS The DNS gives the Tox ID associated to toxme.se addresses Errore nel consultare il server DNS - + Unexpected number of text records Error with the DNS Numero inaspettato di text-records - + Unexpected number of values in text record Error with the DNS Numero inaspettato di valori nel text-record - + The DNS lookup does not contain any Tox ID Error with the DNS La risposta del server DNS non contiene nessun Tox ID - - + + The DNS lookup does not contain a valid Tox ID Error with the DNS La risposta del server DNS non contiene un Tox ID valido @@ -91,13 +91,13 @@ ChatForm - + Send a file Invia un file - - + + Save chat log Salva il log della chat @@ -113,11 +113,23 @@ FileTransfertWidget - + Save a file Title of the file saving dialog Salva file + + + Location not writable + Title of permissions popup + Errore + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + Non hai sufficienti permessi per scrivere in questa locazione. Scegli un'altra posizione, o annulla il salvataggio. + FilesForm @@ -125,15 +137,15 @@ Transfered Files "Headline" of the window - Files inviati + Files Trasferiti - + Downloads Ricevuti - + Uploads Inviati @@ -177,19 +189,19 @@ FriendWidget - + Copy friend ID Menu to copy the Tox ID of that friend Copia Tox ID del contatto - + Invite in group Menu to invite a friend in a groupchat Invita nel gruppo - + Remove friend Menu to remove the friend from our friendlist Rimuovi contatto @@ -198,23 +210,23 @@ GroupChatForm - + %1 users in chat Number of users in chat %1 utenti in chat - + <Unknown> <Sconosciuto> - + %1 users in chat %1 utenti in chat - + Save chat log Salva il log della chat @@ -223,23 +235,71 @@ GroupWidget - + %1 users in chat %1 utenti in chat - + 0 users in chat 0 utenti in chat - + Quit group Menu to quit a groupchat Esci dal gruppo + + MainWindow + + + qTox + qTox + + + + Your name + qTox User + + + + Your status + Toxin on qTox + + + + Add friends + Aggiungi contatto + + + + Create a group chat + Crea un gruppo + + + + View completed file transfers + Visualizza i trasferimenti completati + + + + Change your settings + Cambia le impostazioni + + + + Close + Chiudi + + + + Ctrl+Q + Ctrl+Q + + SelfCamView @@ -252,115 +312,129 @@ SettingsForm - + User Settings "Headline" of the window Impostazioni - + Name Username/nick Nome - + Status Status message Stato - + (click here to copy) Click on this text to copy TID to clipboard (clicca qui per copiare) - + Test video Text on a button to test the video/webcam Prova la webcam - + Enable IPv6 (recommended) Text on a checkbox to enable IPv6 Abilita IPv6 (consigliato) - + Use translations Text on a checkbox to enable translations Abilita traduzioni - + Make Tox portable Text on a checkbox to make qTox a portable application Rendi qTox portabile + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Slava le impostazioni nella directory di lavoro corrente, invece della directory di default + + + + Smiley Pack + Text on smiley pack label + Emoticons + + + Select smiley pack + Scegli pacchetto emoticons + Widget - Tox - Tox + Tox - Your name - Tox User + Tox User - Your status - Toxin on qTox + Toxin on qTox - Add friends - Aggiungi contatto + Aggiungi contatto - Create a group chat - Crea un gruppo + Crea un gruppo + + + View completed file transfers + Visualizza i trasferimenti completati - (button inactive currently) - (bottone attualmente inattivo) + (bottone attualmente inattivo) - Change your settings - Cambia le impostazioni + Cambia le impostazioni - Close - Chiudi + Chiudi - Ctrl+Q - Ctrl+Q + Ctrl+Q + Online Button to set your status to 'Online' - Online + Online + Away Button to set your status to 'Away' - Assente + Assente + Busy Button to set your status to 'Busy' - Occupato + Occupato diff --git a/translations/ru.ts b/translations/ru.ts index 2ce21d900..1e2118770 100644 --- a/translations/ru.ts +++ b/translations/ru.ts @@ -27,51 +27,51 @@ Отправить запрос на добавление в друзья - + Tox me maybe? Default message in friend requests if the field is left blank. Write something appropriate! Вот таким нехитрым и незамысловатым образом решаются сложные переводчиские проблемы Добавь меня, а? - + Please fill in a valid Tox ID Tox ID of the friend you're sending a friend request to Пожалуйста, введите корректный Tox ID - + This address does not exist The DNS gives the Tox ID associated to toxme.se addresses Нет такого адреса - + Error while looking up DNS The DNS gives the Tox ID associated to toxme.se addresses Ошибка при просмотре DNS - + Unexpected number of text records Error with the DNS Непредвиденное количество текстовых записей - + Unexpected number of values in text record Error with the DNS Непредвиденное количество значений в текстовой записи - + The DNS lookup does not contain any Tox ID Error with the DNS В ответе DNS ни одного Tox ID - - + + The DNS lookup does not contain a valid Tox ID Error with the DNS Ответ DNS не содержит корректных Tox ID @@ -93,13 +93,13 @@ ChatForm - + Send a file Отправить файл - - + + Save chat log Сохранить лог чата @@ -115,11 +115,23 @@ FileTransfertWidget - + Save a file Title of the file saving dialog Сохранить файл + + + Location not writable + Title of permissions popup + + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + + FilesForm @@ -130,12 +142,12 @@ Переданные файлы - + Downloads Загрузки - + Uploads Выгрузки @@ -181,19 +193,19 @@ FriendWidget - + Copy friend ID Menu to copy the Tox ID of that friend Копировать ID друга - + Invite in group Menu to invite a friend in a groupchat Пригласить в группу - + Remove friend Menu to remove the friend from our friendlist Удалить друга @@ -202,23 +214,23 @@ GroupChatForm - + %1 users in chat Number of users in chat %1 пользователей в чате - + <Unknown> <Неизвестно> - + %1 users in chat %1 пользователей в чате - + Save chat log Сохранить лог чата @@ -226,24 +238,72 @@ GroupWidget - + Quit group Menu to quit a groupchat Покинуть группу - + %1 users in chat %1 пользователей в чате - + 0 users in chat Ни одного пользователя в чате + + MainWindow + + + qTox + + + + + Your name + Ваше имя + + + + Your status + Ваш статус + + + + Add friends + Добавить друзей + + + + Create a group chat + Создать групповой чат + + + + View completed file transfers + + + + + Change your settings + Изменить ваши настройки + + + + Close + Закрыть + + + + Ctrl+Q + Ctrl+Q + + SelfCamView @@ -256,117 +316,123 @@ SettingsForm - + User Settings "Headline" of the window Пользовательские настройки - + Name Username/nick Имя - + Status Status message Статус - + (click here to copy) Click on this text to copy TID to clipboard (нажмите здесь чтобы скопировать) - + Test video Text on a button to test the video/webcam Проверить видео - + Enable IPv6 (recommended) Text on a checkbox to enable IPv6 Включить IPv6 (рекомендуется) - + Use translations Text on a checkbox to enable translations Так гораздо понятнее, чем «использовать переводы» Русскоязычный интерфейс - + Make Tox portable Text on a checkbox to make qTox a portable application Портативный режим + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + + + + + Smiley Pack + Text on smiley pack label + + Widget - Tox - Tox + Tox - Your name - Ваше имя + Ваше имя - Your status - Ваш статус + Ваш статус - Add friends - Добавить друзей + Добавить друзей - Create a group chat - Создать групповой чат + Создать групповой чат - (button inactive currently) - (кнопка на данный момент неактивна) + (кнопка на данный момент неактивна) - Change your settings - Изменить ваши настройки + Изменить ваши настройки - Close - Закрыть + Закрыть - Ctrl+Q - Ctrl+Q + Ctrl+Q + Online Button to set your status to 'Online' - В сети + В сети + Away Button to set your status to 'Away' Вероятно, это не столь долгое путешествие - Отошёл + Отошёл + Busy Button to set your status to 'Busy' - Занят + Занят diff --git a/ui/emoticonWidget/dot_page.png b/ui/emoticonWidget/dot_page.png new file mode 100644 index 000000000..c7c2aa172 Binary files /dev/null and b/ui/emoticonWidget/dot_page.png differ diff --git a/ui/emoticonWidget/dot_page_current.png b/ui/emoticonWidget/dot_page_current.png new file mode 100644 index 000000000..aa101903a Binary files /dev/null and b/ui/emoticonWidget/dot_page_current.png differ diff --git a/ui/emoticonWidget/dot_page_hover.png b/ui/emoticonWidget/dot_page_hover.png new file mode 100644 index 000000000..1c23931b9 Binary files /dev/null and b/ui/emoticonWidget/dot_page_hover.png differ diff --git a/ui/emoticonWidget/emoticonWidget.css b/ui/emoticonWidget/emoticonWidget.css new file mode 100644 index 000000000..a1aab7de3 --- /dev/null +++ b/ui/emoticonWidget/emoticonWidget.css @@ -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; +} diff --git a/ui/friendList/friendList.css b/ui/friendList/friendList.css index 42bf69cc1..55b39e1e6 100644 --- a/ui/friendList/friendList.css +++ b/ui/friendList/friendList.css @@ -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; } diff --git a/ui/micButton/micButton.css b/ui/micButton/micButton.css new file mode 100644 index 000000000..ec784ae7a --- /dev/null +++ b/ui/micButton/micButton.css @@ -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; +} diff --git a/ui/micButton/micButton.png b/ui/micButton/micButton.png new file mode 100644 index 000000000..7f0cf0c4d Binary files /dev/null and b/ui/micButton/micButton.png differ diff --git a/ui/micButton/micButtonDisabled.png b/ui/micButton/micButtonDisabled.png new file mode 100644 index 000000000..9cb38c9be Binary files /dev/null and b/ui/micButton/micButtonDisabled.png differ diff --git a/ui/micButton/micButtonHover.png b/ui/micButton/micButtonHover.png new file mode 100644 index 000000000..462337253 Binary files /dev/null and b/ui/micButton/micButtonHover.png differ diff --git a/ui/micButton/micButtonPressed.png b/ui/micButton/micButtonPressed.png new file mode 100644 index 000000000..cf46b7cf6 Binary files /dev/null and b/ui/micButton/micButtonPressed.png differ diff --git a/ui/volButton/volButton.css b/ui/volButton/volButton.css new file mode 100644 index 000000000..066d9ae66 --- /dev/null +++ b/ui/volButton/volButton.css @@ -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; +} diff --git a/ui/volButton/volButton.png b/ui/volButton/volButton.png new file mode 100644 index 000000000..994fcce4f Binary files /dev/null and b/ui/volButton/volButton.png differ diff --git a/ui/volButton/volButtonHover.png b/ui/volButton/volButtonHover.png new file mode 100644 index 000000000..e6422b7f8 Binary files /dev/null and b/ui/volButton/volButtonHover.png differ diff --git a/ui/volButton/volButtonPressed.png b/ui/volButton/volButtonPressed.png new file mode 100644 index 000000000..fc9b67a27 Binary files /dev/null and b/ui/volButton/volButtonPressed.png differ diff --git a/widget.ui b/widget.ui deleted file mode 100644 index 0364da3d8..000000000 --- a/widget.ui +++ /dev/null @@ -1,3357 +0,0 @@ - - - - - Widget - - - - 0 - 0 - 640 - 320 - - - - - 640 - 320 - - - - - 16777215 - 16777215 - - - - Tox - - - - :/img/icon.png:/img/icon.png - - - - 2 - - - 1 - - - 2 - - - 2 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 23 - - - - - 16777215 - 23 - - - - - 4 - - - 5 - - - 0 - - - 1 - - - 0 - - - - - - 16 - 16 - - - - - 16 - 16 - - - - - 16 - 16 - - - - QToolButton::InstantPopup - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 15 - 1 - - - - - - - - - 100 - 22 - - - - - 16777215 - 22 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 134 - 20 - - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - 22 - 22 - - - - true - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - 22 - 22 - - - - true - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - 22 - 22 - - - - true - - - - - - - - - - - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - true - - - false - - - QSplitter{ - color: rgb(255, 255, 255); - background-color: rgb(255, 255, 255); - alternate-background-color: rgb(255, 255, 255); - border-color: rgb(255, 255, 255); - gridline-color: rgb(255, 255, 255); - selection-color: rgb(255, 255, 255); - selection-background-color: rgb(255, 255, 255); -} -QSplitter:handle{ - color: rgb(255, 255, 255); - background-color: rgb(255, 255, 255); -} - - - Qt::Horizontal - - - false - - - - - 0 - 0 - - - - - 225 - 0 - - - - - 1920 - 16777215 - - - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 255 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - 0 - 0 - - - - - 225 - 60 - - - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 255 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - - - - - - 0 - - - - - - 40 - 40 - - - - - 40 - 40 - - - - - - - :/img/contact.png - - - true - - - - - - - 0 - - - - - true - - - - 1 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 240 - 240 - 240 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - - - 11 - 75 - true - - - - Your name - - - - - - - - 1 - 0 - - - - - - - - - 193 - 193 - 193 - - - - - - - 193 - 193 - 193 - - - - - - - - - 193 - 193 - 193 - - - - - - - 193 - 193 - 193 - - - - - - - - - 14 - 14 - 14 - - - - - - - 14 - 14 - 14 - - - - - - - - - 8 - - - - Your status - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - - - - - - 0 - 0 - - - - - 20 - 40 - - - - Qt::NoFocus - - - - - - - 10 - 10 - - - - false - - - false - - - false - - - true - - - - - - - - - - - - - 0 - 0 - - - - - 225 - 0 - - - - Qt::RightToLeft - - - true - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 256 - 199 - - - - - - - - - true - - - - 0 - 0 - - - - - 225 - 35 - - - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 255 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 55 - 35 - - - - - 55 - 35 - - - - Qt::NoFocus - - - Add friends - - - - - - - :/img/add.png:/img/add.png - - - true - - - - - - - Qt::Horizontal - - - QSizePolicy::Maximum - - - - 500 - 20 - - - - - - - - - 55 - 35 - - - - Qt::NoFocus - - - Create a group chat - - - - - - - :/img/group.png:/img/group.png - - - true - - - - - - - Qt::Horizontal - - - QSizePolicy::Maximum - - - - 500 - 20 - - - - - - - - - 55 - 35 - - - - - 55 - 35 - - - - Qt::NoFocus - - - View completed file transfers - - - - - - - :/img/transfer.png:/img/transfer.png - - - true - - - - - - - Qt::Horizontal - - - QSizePolicy::Maximum - - - - 500 - 20 - - - - - - - - - 55 - 35 - - - - - 55 - 35 - - - - Qt::NoFocus - - - Change your settings - - - - - - - :/img/settings.png:/img/settings.png - - - true - - - - - - - - - - - - 1 - 0 - - - - - 375 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 127 - 127 - 127 - - - - - - - 170 - 170 - 170 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 127 - 127 - 127 - - - - - - - 170 - 170 - 170 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 127 - 127 - 127 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 127 - 127 - 127 - - - - - - - 170 - 170 - 170 - - - - - - - 127 - 127 - 127 - - - - - - - 255 - 255 - 255 - - - - - - - 127 - 127 - 127 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 60 - - - - - 16777215 - 60 - - - - true - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - - - - - - 193 - 193 - 193 - - - - - - - - - 193 - 193 - 193 - - - - - - - - - 127 - 127 - 127 - - - - - - - - QFrame::HLine - - - QFrame::Plain - - - - - - - - 0 - 0 - - - - - 375 - 0 - - - - - - - - - - - - Close - - - Ctrl+Q - - - - - - - EditableLabelWidget - QLabel -
widget/tool/editablelabelwidget.h
-
-
- - - - - - actionClose - triggered() - Widget - close() - - - -1 - -1 - - - 69 - 61 - - - - -
diff --git a/widget/adjustingscrollarea.cpp b/widget/adjustingscrollarea.cpp new file mode 100644 index 000000000..bb273fa90 --- /dev/null +++ b/widget/adjustingscrollarea.cpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include +#include +#include + +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(); +} diff --git a/widget/tool/clickablelabel.h b/widget/adjustingscrollarea.h similarity index 67% rename from widget/tool/clickablelabel.h rename to widget/adjustingscrollarea.h index db475ee1c..a1645cbba 100644 --- a/widget/tool/clickablelabel.h +++ b/widget/adjustingscrollarea.h @@ -14,23 +14,23 @@ See the COPYING file for more details. */ -#ifndef CLICKABLELABEL_H -#define CLICKABLELABEL_H +#ifndef ADJUSTINGSCROLLAREA_H +#define ADJUSTINGSCROLLAREA_H -#include +#include -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 diff --git a/widget/camera.cpp b/widget/camera.cpp index 7c8c5582c..e4a6010c7 100644 --- a/widget/camera.cpp +++ b/widget/camera.cpp @@ -15,59 +15,13 @@ */ #include "camera.h" -#include #include -#include -#include -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(r); - G = static_cast(g); - B = static_cast(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(); - 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 Camera::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const -{ - if (handleType == QAbstractVideoBuffer::NoHandle) - return supportedFormats; - else - return QList(); -} - -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; - } diff --git a/widget/camera.h b/widget/camera.h index dd085b61b..e717c5c1a 100644 --- a/widget/camera.h +++ b/widget/camera.h @@ -17,47 +17,29 @@ #ifndef CAMERA_H #define CAMERA_H -#include -#include -#include +#include #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 supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; private: int refcount; ///< Number of users suscribed to the camera - QCamera *camera; - QVideoFrame lastFrame; - int frameFormat; - QList supportedFormats; + cv::VideoCapture cam; ///< OpenCV camera capture opbject }; #endif // CAMERA_H diff --git a/widget/croppinglabel.cpp b/widget/croppinglabel.cpp new file mode 100644 index 000000000..47b324d1e --- /dev/null +++ b/widget/croppinglabel.cpp @@ -0,0 +1,142 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 + +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(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); +} diff --git a/widget/croppinglabel.h b/widget/croppinglabel.h new file mode 100644 index 000000000..aeca082b1 --- /dev/null +++ b/widget/croppinglabel.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include + +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 diff --git a/widget/emoticonswidget.cpp b/widget/emoticonswidget.cpp new file mode 100644 index 000000000..bab7abe8d --- /dev/null +++ b/widget/emoticonswidget.cpp @@ -0,0 +1,136 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include +#include +#include +#include + +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& 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(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(QObject::sender()); + if (sender) + emit insertEmoticon(' ' + sender->property("sequence").toString() + ' '); +} + +void EmoticonsWidget::onPageButtonClicked() +{ + QWidget* sender = qobject_cast(QObject::sender()); + if (sender) + { + int page = sender->property("pageIndex").toInt(); + stack.setCurrentIndex(page); + } +} + +QSize EmoticonsWidget::sizeHint() const +{ + return layout.sizeHint(); +} diff --git a/audiobuffer.h b/widget/emoticonswidget.h similarity index 59% rename from audiobuffer.h rename to widget/emoticonswidget.h index e94b70447..559fb7443 100644 --- a/audiobuffer.h +++ b/widget/emoticonswidget.h @@ -14,29 +14,32 @@ See the COPYING file for more details. */ -#ifndef AUDIOBUFFER_H -#define AUDIOBUFFER_H +#ifndef EMOTICONSWIDGET_H +#define EMOTICONSWIDGET_H -#include -#include -#include +#include +#include +#include -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 diff --git a/widget/filetransfertwidget.cpp b/widget/filetransfertwidget.cpp index 568ce93a2..41fb4ed56 100644 --- a/widget/filetransfertwidget.cpp +++ b/widget/filetransfertwidget.cpp @@ -18,6 +18,7 @@ #include "widget.h" #include "core.h" #include "math.h" +#include "style.h" #include #include #include @@ -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")); } } diff --git a/widget/filetransfertwidget.h b/widget/filetransfertwidget.h index c5933eeb5..699c9d548 100644 --- a/widget/filetransfertwidget.h +++ b/widget/filetransfertwidget.h @@ -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; diff --git a/widget/form/addfriendform.cpp b/widget/form/addfriendform.cpp index 76072b230..9d09e0682 100644 --- a/widget/form/addfriendform.cpp +++ b/widget/form/addfriendform.cpp @@ -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() diff --git a/widget/form/addfriendform.h b/widget/form/addfriendform.h index 7f2263827..0edcf2214 100644 --- a/widget/form/addfriendform.h +++ b/widget/form/addfriendform.h @@ -17,7 +17,7 @@ #ifndef ADDFRIENDFORM_H #define ADDFRIENDFORM_H -#include "ui_widget.h" +#include "ui_mainwindow.h" #include #include @@ -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; diff --git a/widget/form/chatform.cpp b/widget/form/chatform.cpp index 13d4b5271..50f09f5a2 100644 --- a/widget/form/chatform.cpp +++ b/widget/form/chatform.cpp @@ -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 #include #include #include #include +#include +#include +#include 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 = "%2"; + + 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 += "
"; + } + 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(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(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); + } + } +} diff --git a/widget/form/chatform.h b/widget/form/chatform.h index d6d44143b..dd746d6c1 100644 --- a/widget/form/chatform.h +++ b/widget/form/chatform.h @@ -28,7 +28,7 @@ #include #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; }; diff --git a/widget/form/filesform.cpp b/widget/form/filesform.cpp index 14ffe5fe0..6d69c1f5a 100644 --- a/widget/form/filesform.cpp +++ b/widget/form/filesform.cpp @@ -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); diff --git a/widget/form/filesform.h b/widget/form/filesform.h index f8fb51105..0f1a2b1d8 100644 --- a/widget/form/filesform.h +++ b/widget/form/filesform.h @@ -17,7 +17,7 @@ #ifndef FILESFORM_H #define FILESFORM_H -#include "ui_widget.h" +#include "ui_mainwindow.h" #include #include @@ -37,7 +37,7 @@ public: FilesForm(); ~FilesForm(); - void show(Ui::Widget& ui); + void show(Ui::MainWindow &ui); public slots: void onFileDownloadComplete(const QString& path); diff --git a/widget/form/groupchatform.cpp b/widget/form/groupchatform.cpp index f3f12afcf..e5e630ae1 100644 --- a/widget/form/groupchatform.cpp +++ b/widget/form/groupchatform.cpp @@ -20,6 +20,7 @@ #include "widget/widget.h" #include "friend.h" #include "friendlist.h" +#include "style.h" #include #include #include @@ -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); diff --git a/widget/form/groupchatform.h b/widget/form/groupchatform.h index b4ac9a5ce..af008476a 100644 --- a/widget/form/groupchatform.h +++ b/widget/form/groupchatform.h @@ -27,7 +27,7 @@ #include #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")); diff --git a/widget/form/settingsform.cpp b/widget/form/settingsform.cpp index 573d80101..bb8c12093 100644 --- a/widget/form/settingsform.cpp +++ b/widget/form/settingsform.cpp @@ -17,9 +17,12 @@ #include "settingsform.h" #include "widget/widget.h" #include "settings.h" +#include "smileypack.h" #include #include #include +#include +#include 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); +} diff --git a/widget/form/settingsform.h b/widget/form/settingsform.h index a14ed9142..8e012c5ee 100644 --- a/widget/form/settingsform.h +++ b/widget/form/settingsform.h @@ -31,9 +31,9 @@ #include #include #include -#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 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 diff --git a/widget/friendlistwidget.cpp b/widget/friendlistwidget.cpp new file mode 100644 index 000000000..aced0f297 --- /dev/null +++ b/widget/friendlistwidget.cpp @@ -0,0 +1,67 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 + +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(s)] = l; + } + + mainLayout->addLayout(layouts[static_cast(Status::Online)], 0, 0); + mainLayout->addLayout(groupLayout, 1, 0); + mainLayout->addLayout(layouts[static_cast(Status::Away)], 2, 0); + mainLayout->addLayout(layouts[static_cast(Status::Busy)], 3, 0); + mainLayout->addLayout(layouts[static_cast(Status::Offline)], 4, 0); +} + +QLayout* FriendListWidget::getGroupLayout() +{ + return groupLayout; +} + +QLayout* FriendListWidget::getFriendLayout(Status s) +{ + auto res = layouts.find(static_cast(s)); + if (res != layouts.end()) + return res.value(); + + qDebug() << "Friend Status: " << static_cast(s) << " not found!"; + return layouts[static_cast(Status::Online)]; +} + +void FriendListWidget::moveWidget(QWidget *w, Status s) +{ + mainLayout->removeWidget(w); + getFriendLayout(s)->addWidget(w); +} diff --git a/widget/videosurface.h b/widget/friendlistwidget.h similarity index 51% rename from widget/videosurface.h rename to widget/friendlistwidget.h index c93ed2c8f..02baa3f94 100644 --- a/widget/videosurface.h +++ b/widget/friendlistwidget.h @@ -14,29 +14,31 @@ See the COPYING file for more details. */ -#ifndef VIDEOSURFACE_H -#define VIDEOSURFACE_H +#ifndef FRIENDLISTWIDGET_H +#define FRIENDLISTWIDGET_H -#include -#include -#include "vpx/vpx_image.h" +#include +#include +#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 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 layouts; + QLayout *groupLayout; + QGridLayout *mainLayout; }; -#endif // VIDEOSURFACE_H +#endif // FRIENDLISTWIDGET_H diff --git a/widget/friendwidget.cpp b/widget/friendwidget.cpp index 845bf816c..97c7b9c77 100644 --- a/widget/friendwidget.cpp +++ b/widget/friendwidget.cpp @@ -19,22 +19,24 @@ #include "grouplist.h" #include "groupwidget.h" #include "widget.h" +#include "friendlist.h" +#include "friend.h" #include #include 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; +} diff --git a/widget/friendwidget.h b/widget/friendwidget.h index 091f78c69..257d48a24 100644 --- a/widget/friendwidget.h +++ b/widget/friendwidget.h @@ -22,19 +22,20 @@ #include #include -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 diff --git a/widget/genericchatroomwidget.cpp b/widget/genericchatroomwidget.cpp new file mode 100644 index 000000000..234da1222 --- /dev/null +++ b/widget/genericchatroomwidget.cpp @@ -0,0 +1,73 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 + +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); +} diff --git a/widget/genericchatroomwidget.h b/widget/genericchatroomwidget.h new file mode 100644 index 000000000..14e46c16e --- /dev/null +++ b/widget/genericchatroomwidget.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 +#include +#include + +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 diff --git a/widget/groupwidget.cpp b/widget/groupwidget.cpp index 6eb995031..b8c1cbcc0 100644 --- a/widget/groupwidget.cpp +++ b/widget/groupwidget.cpp @@ -17,22 +17,26 @@ #include "groupwidget.h" #include "grouplist.h" #include "group.h" +#include "settings.h" +#include "widget/form/groupchatform.h" #include #include #include +#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; +} diff --git a/widget/groupwidget.h b/widget/groupwidget.h index fe66a2371..c32db80a6 100644 --- a/widget/groupwidget.h +++ b/widget/groupwidget.h @@ -19,21 +19,20 @@ #include #include -#include -#include +#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 diff --git a/widget/netcamview.cpp b/widget/netcamview.cpp index dd7454972..cd9e78fda 100644 --- a/widget/netcamview.cpp +++ b/widget/netcamview.cpp @@ -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); diff --git a/widget/selfcamview.cpp b/widget/selfcamview.cpp index d38b773dd..2d46e99bb 100644 --- a/widget/selfcamview.cpp +++ b/widget/selfcamview.cpp @@ -15,15 +15,13 @@ */ #include "selfcamview.h" -#include -#include #include #include -#include -#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) diff --git a/widget/tool/chattextedit.cpp b/widget/tool/chattextedit.cpp index face13191..8f727cfbb 100644 --- a/widget/tool/chattextedit.cpp +++ b/widget/tool/chattextedit.cpp @@ -20,6 +20,7 @@ ChatTextEdit::ChatTextEdit(QWidget *parent) : QTextEdit(parent) { + setPlaceholderText("Type your message here..."); } void ChatTextEdit::keyPressEvent(QKeyEvent * event) diff --git a/widget/tool/copyableelidelabel.cpp b/widget/tool/copyableelidelabel.cpp deleted file mode 100644 index ae05717ca..000000000 --- a/widget/tool/copyableelidelabel.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - Copyright (C) 2013 by Maxim Biro - - 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 -#include -#include - -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); -} diff --git a/widget/tool/copyableelidelabel.h b/widget/tool/copyableelidelabel.h deleted file mode 100644 index 69bc09382..000000000 --- a/widget/tool/copyableelidelabel.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - Copyright (C) 2013 by Maxim Biro - - 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 diff --git a/widget/tool/editablelabelwidget.cpp b/widget/tool/editablelabelwidget.cpp deleted file mode 100644 index a9c87f27a..000000000 --- a/widget/tool/editablelabelwidget.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - Copyright (C) 2013 by Maxim Biro - - 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 -#include -#include -#include -#include - -ClickableCopyableElideLabel::ClickableCopyableElideLabel(QWidget* parent) : - CopyableElideLabel(parent) -{ -} - -bool ClickableCopyableElideLabel::event(QEvent* event) -{ - if (event->type() == QEvent::MouseButtonRelease) { - QMouseEvent* mouseEvent = static_cast(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(); -} diff --git a/widget/tool/editablelabelwidget.h b/widget/tool/editablelabelwidget.h deleted file mode 100644 index 17772f8c4..000000000 --- a/widget/tool/editablelabelwidget.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright (C) 2013 by Maxim Biro - - 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 -#include - -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 diff --git a/widget/tool/elidelabel.cpp b/widget/tool/elidelabel.cpp deleted file mode 100644 index 239400f49..000000000 --- a/widget/tool/elidelabel.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - Copyright (C) 2013 by Maxim Biro - - 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 -#include - -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; -} diff --git a/widget/tool/elidelabel.h b/widget/tool/elidelabel.h deleted file mode 100644 index 5dd309001..000000000 --- a/widget/tool/elidelabel.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright (C) 2013 by Maxim Biro - - 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 - -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 diff --git a/widget/tool/esclineedit.cpp b/widget/tool/esclineedit.cpp deleted file mode 100644 index 1a6adc373..000000000 --- a/widget/tool/esclineedit.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright (C) 2013 by Maxim Biro - - 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 "esclineedit.h" - -#include -#include - -EscLineEdit::EscLineEdit(QWidget* parent) : - QLineEdit(parent) -{ -} - -void EscLineEdit::keyPressEvent(QKeyEvent* event) -{ - if (event->key() == Qt::Key_Escape && event->modifiers() == Qt::NoModifier) { - emit escPressed(); - } else { - QLineEdit::keyPressEvent(event); - } -} diff --git a/widget/tool/esclineedit.h b/widget/tool/esclineedit.h deleted file mode 100644 index 75eac7315..000000000 --- a/widget/tool/esclineedit.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - Copyright (C) 2013 by Maxim Biro - - 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 ESCLINEEDIT_HPP -#define ESCLINEEDIT_HPP - -#include - -class EscLineEdit : public QLineEdit -{ - Q_OBJECT -public: - explicit EscLineEdit(QWidget* parent); - -protected: - void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE; - -signals: - void escPressed(); - -}; - -#endif // ESCLINEEDIT_HPP diff --git a/widget/videosurface.cpp b/widget/videosurface.cpp deleted file mode 100644 index 68ec9ff49..000000000 --- a/widget/videosurface.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - 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 "videosurface.h" -#include "core.h" -#include -#include - -VideoSurface::VideoSurface() - : QAbstractVideoSurface() -{ - vpx_img_alloc(&input, VPX_IMG_FMT_YV12, TOXAV_MAX_VIDEO_WIDTH, TOXAV_MAX_VIDEO_HEIGHT, 1); -} - -bool VideoSurface::start(const QVideoSurfaceFormat &format) -{ - mVideoFormat = format; - //start only if format is UYVY, dont handle other format now - if( format.pixelFormat() == QVideoFrame::Format_YV12 ){ - QAbstractVideoSurface::start(format); - return true; - } else { - return false; - } -} - -bool VideoSurface::present(const QVideoFrame&) -{ - /* - mFrame = frame; - - qDebug() << QString("Video: Frame format is %1").arg(mFrame.pixelFormat()); - - stop(); - - //this is necessary to get valid data from frame - mFrame.map(QAbstractVideoBuffer::ReadOnly); - - uchar* data = new uchar[frame.mappedBytes()]; - memcpy(data, frame.bits(), frame.mappedBytes()); - - input.planes[VPX_PLANE_Y] = data; - input.planes[VPX_PLANE_U] = data + (frame.bytesPerLine() * frame.height()); - input.planes[VPX_PLANE_V] = input.planes[VPX_PLANE_U] + (frame.bytesPerLine()/2 * frame.height()/2); - input.planes[VPX_PLANE_ALPHA] = nullptr; - - //qDebug() << QString("Got %1 bytes, first plane is %2 bytes long") - // .arg(frame.mappedBytes()).arg(frame.bytesPerLine() * frame.height()); - - // Slots MUST be called with a direct or blocking connection, or input may die before they return ! - emit videoFrameReady(input); - - - QImage lastImage( mFrame.size(), QImage::Format_RGB16); - const uchar *src = mFrame.bits(); - uchar *dst = lastImage.bits(); - const int srcLineStep = mFrame.bytesPerLine(); - const int dstLineStep = lastImage.bytesPerLine(); - const int h = mFrame.height(); - const int w = mFrame.width(); - - for (int y=0; y < h; y++) { - //this function you can find in qgraphicsvideoitem_maemo5.cpp, - //link is mentioned above - uyvy422_to_rgb16_line_neon(dst, src, w); - src += srcLineStep; - dst += dstLineStep; - } - - mLastFrame = QPixmap::fromImage(lastImage); - //emit signal, other can handle it and do necessary processing - emit frameUpdated(mLastFrame); - - delete[] data; - mFrame.unmap(); - -*/ - return true; -} - -QList VideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const -{ - if (handleType == QAbstractVideoBuffer::NoHandle) { - qDebug() << "Video: No handle"; - return QList() << QVideoFrame::Format_YV12; - } else { - qDebug() << "Video: Handle type is not NoHandle"; - return QList(); - } -} diff --git a/widget/widget.cpp b/widget/widget.cpp index 9fb07d766..361b698da 100644 --- a/widget/widget.cpp +++ b/widget/widget.cpp @@ -15,7 +15,7 @@ */ #include "widget.h" -#include "ui_widget.h" +#include "ui_mainwindow.h" #include "settings.h" #include "friend.h" #include "friendlist.h" @@ -25,9 +25,9 @@ #include "group.h" #include "widget/groupwidget.h" #include "widget/form/groupchatform.h" +#include "style.h" #include #include -#include #include #include #include @@ -40,61 +40,38 @@ Widget *Widget::instance{nullptr}; -Widget::Widget(QWidget *parent) : - QWidget(parent), ui(new Ui::Widget), activeFriendWidget{nullptr}, activeGroupWidget{nullptr} +Widget::Widget(QWidget *parent) + : QMainWindow(parent), + ui(new Ui::MainWindow), + activeChatroomWidget{nullptr} { ui->setupUi(this); - QSettings settings(Settings::getInstance().getSettingsDirPath() + '/' + "windowSettings.ini", QSettings::IniFormat); - if (!settings.contains("useNativeTheme")) - useNativeTheme = 1; - else - useNativeTheme = settings.value("useNativeTheme").toInt(); + ui->statusbar->hide(); + ui->menubar->hide(); - if (useNativeTheme) + //restore window state + restoreGeometry(Settings::getInstance().getWindowGeometry()); + restoreState(Settings::getInstance().getWindowState()); + ui->mainSplitter->restoreState(Settings::getInstance().getSplitterState()); + + if (Settings::getInstance().getUseNativeDecoration()) { ui->titleBar->hide(); this->layout()->setContentsMargins(0, 0, 0, 0); - QString friendListStylesheet = ""; - try - { - QFile f(":ui/friendList/friendList.css"); - f.open(QFile::ReadOnly | QFile::Text); - QTextStream friendListStylesheetStream(&f); - friendListStylesheet = friendListStylesheetStream.readAll(); - } - catch (int e) {} ui->friendList->setObjectName("friendList"); - ui->friendList->setStyleSheet(friendListStylesheet); + ui->friendList->setStyleSheet(Style::get(":ui/friendList/friendList.css")); } else { - QString windowStylesheet = ""; - try - { - QFile f(":ui/window/window.css"); - f.open(QFile::ReadOnly | QFile::Text); - QTextStream windowStylesheetStream(&f); - windowStylesheet = windowStylesheetStream.readAll(); - } - catch (int e) {} this->setObjectName("activeWindow"); - this->setStyleSheet(windowStylesheet); + this->setStyleSheet(Style::get(":ui/window/window.css")); ui->statusPanel->setStyleSheet(QString("")); ui->friendList->setStyleSheet(QString("")); - QString friendListStylesheet = ""; - try - { - QFile f(":ui/friendList/friendList.css"); - f.open(QFile::ReadOnly | QFile::Text); - QTextStream friendListStylesheetStream(&f); - friendListStylesheet = friendListStylesheetStream.readAll(); - } - catch (int e) {} ui->friendList->setObjectName("friendList"); - ui->friendList->setStyleSheet(friendListStylesheet); + ui->friendList->setStyleSheet(Style::get(":ui/friendList/friendList.css")); ui->tbMenu->setIcon(QIcon(":ui/window/applicationIcon.png")); ui->pbMin->setObjectName("minimizeButton"); @@ -119,24 +96,11 @@ Widget::Widget(QWidget *parent) : resizeDiagSupEsq = false; resizeDiagSupDer = false; - QSettings settings(Settings::getInstance().getSettingsDirPath() + '/' + "windowSettings.ini", QSettings::IniFormat); - QRect geo = settings.value("geometry").toRect(); - - if (geo.height() > 0 and geo.x() < QApplication::desktop()->width() and geo.width() > 0 and geo.y() < QApplication::desktop()->height()) - this->setGeometry(geo); - - if (settings.value("maximized").toBool()) + if (isMaximized()) { showMaximized(); ui->pbMax->setObjectName("restoreButton"); } - - QList widgets = this->findChildren(); - - foreach (QWidget *widget, widgets) - { - widget->setMouseTracking(true); - } } isWindowMinimized = 0; @@ -145,41 +109,31 @@ Widget::Widget(QWidget *parent) : ui->mainHead->setLayout(new QVBoxLayout()); ui->mainHead->layout()->setMargin(0); ui->mainHead->layout()->setSpacing(0); - QWidget* friendListWidget = new QWidget(); - friendListWidget->setLayout(new QVBoxLayout()); - friendListWidget->layout()->setSpacing(0); - friendListWidget->layout()->setMargin(0); - friendListWidget->setLayoutDirection(Qt::LeftToRight); - ui->friendList->setWidget(friendListWidget); + + contactListWidget = new FriendListWidget(); + ui->friendList->setWidget(contactListWidget); + ui->friendList->setLayoutDirection(Qt::RightToLeft); + + ui->nameLabel->setEditable(true); + ui->statusLabel->setEditable(true); // delay setting username and message until Core inits //ui->nameLabel->setText(core->getUsername()); - ui->nameLabel->label->setStyleSheet("QLabel { color : white; font-size: 11pt; font-weight:bold;}"); + ui->nameLabel->setStyleSheet("QLabel { color : white; font-size: 11pt; font-weight:bold;}"); //ui->statusLabel->setText(core->getStatusMessage()); - ui->statusLabel->label->setStyleSheet("QLabel { color : white; font-size: 8pt;}"); - ui->friendList->widget()->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + ui->statusLabel->setStyleSheet("QLabel { color : white; font-size: 8pt;}"); - QFile f1(":/ui/statusButton/statusButton.css"); - f1.open(QFile::ReadOnly | QFile::Text); - QTextStream statusButtonStylesheetStream(&f1); - ui->statusButton->setStyleSheet(statusButtonStylesheetStream.readAll()); + ui->statusButton->setStyleSheet(Style::get(":/ui/statusButton/statusButton.css")); QMenu *statusButtonMenu = new QMenu(ui->statusButton); - QAction* setStatusOnline = statusButtonMenu->addAction(tr("Online","Button to set your status to 'Online'")); + QAction* setStatusOnline = statusButtonMenu->addAction(Widget::tr("Online","Button to set your status to 'Online'")); setStatusOnline->setIcon(QIcon(":ui/statusButton/dot_online.png")); - QAction* setStatusAway = statusButtonMenu->addAction(tr("Away","Button to set your status to 'Away'")); + QAction* setStatusAway = statusButtonMenu->addAction(Widget::tr("Away","Button to set your status to 'Away'")); setStatusAway->setIcon(QIcon(":ui/statusButton/dot_idle.png")); - QAction* setStatusBusy = statusButtonMenu->addAction(tr("Busy","Button to set your status to 'Busy'")); + QAction* setStatusBusy = statusButtonMenu->addAction(Widget::tr("Busy","Button to set your status to 'Busy'")); setStatusBusy->setIcon(QIcon(":ui/statusButton/dot_busy.png")); ui->statusButton->setMenu(statusButtonMenu); - - this->setMouseTracking(true); - - QList widgets = this->findChildren(); - foreach (QWidget *widget, widgets) - widget->setMouseTracking(true); - ui->titleBar->setMouseTracking(true); ui->LTitle->setMouseTracking(true); ui->tbMenu->setMouseTracking(true); @@ -188,11 +142,11 @@ Widget::Widget(QWidget *parent) : ui->pbClose->setMouseTracking(true); ui->statusHead->setMouseTracking(true); - ui->friendList->viewport()->installEventFilter(this); + //ui->friendList->viewport()->installEventFilter(this); - QList currentSizes = ui->centralWidget->sizes(); - currentSizes[0] = 225; - ui->centralWidget->setSizes(currentSizes); + // disable proportional scaling + ui->mainSplitter->setStretchFactor(0,0); + ui->mainSplitter->setStretchFactor(1,1); ui->statusButton->setObjectName("offline"); ui->statusButton->style()->polish(ui->statusButton); @@ -200,6 +154,9 @@ Widget::Widget(QWidget *parent) : camera = new Camera; camview = new SelfCamView(camera); + // Disable some widgets until we're connected to the DHT + ui->statusButton->setEnabled(false); + qRegisterMetaType("Status"); qRegisterMetaType("vpx_image"); qRegisterMetaType("uint8_t"); @@ -243,7 +200,6 @@ Widget::Widget(QWidget *parent) : connect(this, &Widget::friendRequested, core, &Core::requestFriendship); connect(this, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest); - connect(ui->centralWidget, SIGNAL(splitterMoved(int,int)),this, SLOT(splitterMoved(int,int))); connect(ui->addButton, SIGNAL(clicked()), this, SLOT(onAddClicked())); connect(ui->groupButton, SIGNAL(clicked()), this, SLOT(onGroupClicked())); connect(ui->transferButton, SIGNAL(clicked()), this, SLOT(onTransferClicked())); @@ -253,15 +209,11 @@ Widget::Widget(QWidget *parent) : connect(setStatusOnline, SIGNAL(triggered()), this, SLOT(setStatusOnline())); connect(setStatusAway, SIGNAL(triggered()), this, SLOT(setStatusAway())); connect(setStatusBusy, SIGNAL(triggered()), this, SLOT(setStatusBusy())); - //connect(&settingsForm.name, SIGNAL(editingFinished()), this, SLOT(onUsernameChanged())); - //connect(&settingsForm.statusText, SIGNAL(editingFinished()), this, SLOT(onStatusMessageChanged())); connect(&friendForm, SIGNAL(friendRequested(QString,QString)), this, SIGNAL(friendRequested(QString,QString))); coreThread->start(); friendForm.show(*ui); - isFriendWidgetActive = 0; - isGroupWidgetActive = 0; } Widget::~Widget() @@ -269,7 +221,9 @@ Widget::~Widget() core->saveConfiguration(); instance = nullptr; coreThread->exit(); - coreThread->wait(); + coreThread->wait(500); // In case of deadlock (can happen with QtAudio/PA bugs) + if (!coreThread->isFinished()) + coreThread->terminate(); delete core; delete camview; @@ -281,10 +235,6 @@ Widget::~Widget() for (Group* g : GroupList::groupList) delete g; GroupList::groupList.clear(); - QSettings settings(Settings::getInstance().getSettingsDirPath() + '/' + "windowSettings.ini", QSettings::IniFormat); - settings.setValue("geometry", geometry()); - settings.setValue("maximized", isMaximized()); - settings.setValue("useNativeTheme", useNativeTheme); delete ui; } @@ -295,27 +245,17 @@ Widget* Widget::getInstance() return instance; } -//Super ugly hack to enable resizable friend widgets -//There should be a way to set them to resize automagicly, but I can't seem to find it. -void Widget::splitterMoved(int, int) -{ - updateFriendListWidth(); -} - QThread* Widget::getCoreThread() { return coreThread; } -void Widget::updateFriendListWidth() +void Widget::closeEvent(QCloseEvent *event) { - int newWidth = ui->friendList->width(); - for (Friend* f : FriendList::friendList) - if (f->widget != nullptr) - f->widget->setNewFixedWidth(newWidth); - for (Group* g : GroupList::groupList) - if (g->widget != nullptr) - g->widget->setNewFixedWidth(newWidth); + Settings::getInstance().setWindowGeometry(saveGeometry()); + Settings::getInstance().setWindowState(saveState()); + Settings::getInstance().setSplitterState(ui->mainSplitter->saveState()); + QWidget::closeEvent(event); } QString Widget::getUsername() @@ -330,11 +270,13 @@ Camera* Widget::getCamera() void Widget::onConnected() { + ui->statusButton->setEnabled(true); emit statusSet(Status::Online); } void Widget::onDisconnected() { + ui->statusButton->setEnabled(false); emit statusSet(Status::Offline); } @@ -388,16 +330,14 @@ void Widget::onTransferClicked() { hideMainForms(); filesForm.show(*ui); - isFriendWidgetActive = 0; - isGroupWidgetActive = 0; + activeChatroomWidget = nullptr; } void Widget::onSettingsClicked() { hideMainForms(); settingsForm.show(*ui); - isFriendWidgetActive = 0; - isGroupWidgetActive = 0; + activeChatroomWidget = nullptr; } void Widget::hideMainForms() @@ -408,71 +348,29 @@ void Widget::hideMainForms() while ((item = ui->mainContent->layout()->takeAt(0)) != 0) item->widget()->hide(); - if (activeFriendWidget != nullptr) + if (activeChatroomWidget != nullptr) { - Friend* f = FriendList::findFriend(activeFriendWidget->friendId); - if (f != nullptr) - activeFriendWidget->setAsInactiveChatroom(); - } - if (activeGroupWidget != nullptr) - { - Group* g = GroupList::findGroup(activeGroupWidget->groupId); - if (g != nullptr) - activeGroupWidget->setAsInactiveChatroom(); + activeChatroomWidget->setAsInactiveChatroom(); } } -/*void Widget::onUsernameChanged() -{ - //const QString newUsername = settingsForm.name.text(); - ui->nameLabel->setText(newUsername); - ui->nameLabel->setToolTip(newUsername); // for overlength names - settingsForm.name.setText(newUsername); - core->setUsername(newUsername); -}*/ - void Widget::onUsernameChanged(const QString& newUsername, const QString& oldUsername) { ui->nameLabel->setText(oldUsername); // restore old username until Core tells us to set it ui->nameLabel->setToolTip(oldUsername); // for overlength names - //settingsForm.name.setText(oldUsername); core->setUsername(newUsername); - - /*// move the data file with it - QString dir = Settings::getSettingsDirPath(); - QFile::rename(dir + '/' + core->sanitize(oldUsername) + core->TOX_EXT, dir + '/' + core->sanitize(newUsername) + core->TOX_EXT); - // and update current profile - Settings::getInstance().setCurrentProfile(newUsername); - */ } -// ugh... Widget::onUsernameChanged() calls Core::setUsername, -// which emits Core::usernameSet, which is connect to this function: + void Widget::setUsername(const QString& username) { ui->nameLabel->setText(username); ui->nameLabel->setToolTip(username); // for overlength names - //settingsForm.name.setText(username); } -// the end result is that the ui gets updated twice -- and actually, -// I believe this function is dead code, since the only way to change -// username now is via the ui (since I killed the settings version) -// the chain of calls before was surely much messier with two ways -// to modify the username... - -/*void Widget::onStatusMessageChanged() -{ - const QString newStatusMessage = settingsForm.statusText.text(); - ui->statusLabel->setText(newStatusMessage); - ui->statusLabel->setToolTip(newStatusMessage); // for overlength messsages - settingsForm.statusText.setText(newStatusMessage); - core->setStatusMessage(newStatusMessage); -}*/ void Widget::onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage) { ui->statusLabel->setText(oldStatusMessage); // restore old status message until Core tells us to set it ui->statusLabel->setToolTip(oldStatusMessage); // for overlength messsages - //settingsForm.statusText.setText(oldStatusMessage); core->setStatusMessage(newStatusMessage); } @@ -480,18 +378,15 @@ void Widget::setStatusMessage(const QString &statusMessage) { ui->statusLabel->setText(statusMessage); ui->statusLabel->setToolTip(statusMessage); // for overlength messsages - //settingsForm.statusText.setText(statusMessage); } void Widget::addFriend(int friendId, const QString &userId) { qDebug() << "Adding friend with id "+userId; Friend* newfriend = FriendList::addFriend(friendId, userId); - QWidget* widget = ui->friendList->widget(); - QLayout* layout = widget->layout(); + QLayout* layout = contactListWidget->getFriendLayout(Status::Offline); layout->addWidget(newfriend->widget); - updateFriendListWidth(); - connect(newfriend->widget, SIGNAL(friendWidgetClicked(FriendWidget*)), this, SLOT(onFriendWidgetClicked(FriendWidget*))); + connect(newfriend->widget, SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*))); connect(newfriend->widget, SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int))); connect(newfriend->widget, SIGNAL(copyFriendIdToClipboard(int)), this, SLOT(copyFriendIdToClipboard(int))); connect(newfriend->chatForm, SIGNAL(sendMessage(int,QString)), core, SLOT(sendMessage(int,QString))); @@ -501,6 +396,7 @@ void Widget::addFriend(int friendId, const QString &userId) connect(newfriend->chatForm, SIGNAL(startCall(int)), core, SLOT(startCall(int))); connect(newfriend->chatForm, SIGNAL(startVideoCall(int,bool)), core, SLOT(startCall(int,bool))); connect(newfriend->chatForm, SIGNAL(cancelCall(int,int)), core, SLOT(cancelCall(int,int))); + connect(newfriend->chatForm, SIGNAL(micMuteToggle(int)), core, SLOT(micMuteToggle(int))); connect(core, &Core::fileReceiveRequested, newfriend->chatForm, &ChatForm::onFileRecvRequest); connect(core, &Core::avInvite, newfriend->chatForm, &ChatForm::onAvInvite); connect(core, &Core::avStart, newfriend->chatForm, &ChatForm::onAvStart); @@ -511,6 +407,7 @@ void Widget::addFriend(int friendId, const QString &userId) connect(core, &Core::avEnding, newfriend->chatForm, &ChatForm::onAvEnding); connect(core, &Core::avRequestTimeout, newfriend->chatForm, &ChatForm::onAvRequestTimeout); connect(core, &Core::avPeerTimeout, newfriend->chatForm, &ChatForm::onAvPeerTimeout); + connect(core, &Core::avMediaChange, newfriend->chatForm, &ChatForm::onAvMediaChange); } void Widget::addFriendFailed(const QString&) @@ -524,8 +421,14 @@ void Widget::onFriendStatusChanged(int friendId, Status status) if (!f) return; + contactListWidget->moveWidget(f->widget, status); + f->friendStatus = status; - updateFriendStatusLights(friendId); + f->widget->updateStatusLight(); + + // Workaround widget style after returning to list + if (f->widget->isActive()) + f->widget->setAsActiveChatroom(); } void Widget::onFriendStatusMessageChanged(int friendId, const QString& message) @@ -564,27 +467,18 @@ void Widget::onFriendUsernameLoaded(int friendId, const QString& username) f->setName(username); } -void Widget::onFriendWidgetClicked(FriendWidget *widget) +void Widget::onChatroomWidgetClicked(GenericChatroomWidget *widget) { - Friend* f = FriendList::findFriend(widget->friendId); - if (!f) - return; - hideMainForms(); - f->chatForm->show(*ui); - if (activeFriendWidget != nullptr) + widget->setChatForm(*ui); + if (activeChatroomWidget != nullptr) { - activeFriendWidget->setAsInactiveChatroom(); + activeChatroomWidget->setAsInactiveChatroom(); } - activeFriendWidget = widget; + activeChatroomWidget = widget; widget->setAsActiveChatroom(); - isFriendWidgetActive = 1; - isGroupWidgetActive = 0; - - if (f->hasNewMessages != 0) - f->hasNewMessages = 0; - - updateFriendStatusLights(f->friendId); + widget->resetEventFlags(); + widget->updateStatusLight(); } void Widget::onFriendMessageReceived(int friendId, const QString& message) @@ -595,50 +489,41 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message) f->chatForm->addFriendMessage(message); - if (activeFriendWidget != nullptr) + if (activeChatroomWidget != nullptr) { - Friend* f2 = FriendList::findFriend(activeFriendWidget->friendId); - if (((f->friendId != f2->friendId) || isFriendWidgetActive == 0) || isWindowMinimized || !isActiveWindow()) + if ((static_cast(f->widget) != activeChatroomWidget) || isWindowMinimized || !isActiveWindow()) { - f->hasNewMessages = 1; + f->hasNewEvents = 1; newMessageAlert(); } } else { - f->hasNewMessages = 1; + f->hasNewEvents = 1; newMessageAlert(); } - updateFriendStatusLights(friendId); -} - -void Widget::updateFriendStatusLights(int friendId) -{ - Friend* f = FriendList::findFriend(friendId); - Status status = f->friendStatus; - if (status == Status::Online && f->hasNewMessages == 0) - f->widget->statusPic.setPixmap(QPixmap(":img/status/dot_online.png")); - else if (status == Status::Online && f->hasNewMessages == 1) - f->widget->statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); - else if (status == Status::Away && f->hasNewMessages == 0) - f->widget->statusPic.setPixmap(QPixmap(":img/status/dot_idle.png")); - else if (status == Status::Away && f->hasNewMessages == 1) - f->widget->statusPic.setPixmap(QPixmap(":img/status/dot_idle_notification.png")); - else if (status == Status::Busy && f->hasNewMessages == 0) - f->widget->statusPic.setPixmap(QPixmap(":img/status/dot_busy.png")); - else if (status == Status::Busy && f->hasNewMessages == 1) - f->widget->statusPic.setPixmap(QPixmap(":img/status/dot_busy_notification.png")); - else if (status == Status::Offline && f->hasNewMessages == 0) - f->widget->statusPic.setPixmap(QPixmap(":img/status/dot_away.png")); - else if (status == Status::Offline && f->hasNewMessages == 1) - f->widget->statusPic.setPixmap(QPixmap(":img/status/dot_away_notification.png")); + f->widget->updateStatusLight(); } void Widget::newMessageAlert() { QApplication::alert(this); - QSound::play(":audio/notification.wav"); + + static QFile sndFile(":audio/notification.wav"); + static QByteArray sndData; + if (sndData.isEmpty()) + { + sndFile.open(QIODevice::ReadOnly); + sndData = sndFile.readAll(); + sndFile.close(); + } + + ALuint buffer; + alGenBuffers(1, &buffer); + alBufferData(buffer, AL_FORMAT_STEREO16, sndData.data(), sndData.size(), 44100); + alSourcei(core->alMainSource, AL_BUFFER, buffer); + alSourcePlay(core->alMainSource); } void Widget::onFriendRequestReceived(const QString& userId, const QString& message) @@ -652,8 +537,8 @@ void Widget::onFriendRequestReceived(const QString& userId, const QString& messa void Widget::removeFriend(Friend* f) { f->widget->setAsInactiveChatroom(); - if (f->widget == activeFriendWidget) - activeFriendWidget = nullptr; + if (static_cast(f->widget) == activeChatroomWidget) + activeChatroomWidget = nullptr; FriendList::removeFriend(f->friendId); core->removeFriend(f->friendId); delete f; @@ -678,7 +563,7 @@ void Widget::copyFriendIdToClipboard(int friendId) if (f != nullptr) { QClipboard *clipboard = QApplication::clipboard(); - clipboard->setText(f->userId, QClipboard::Clipboard); + clipboard->setText(core->getFriendAddress(f->friendId), QClipboard::Clipboard); } } @@ -700,27 +585,15 @@ void Widget::onGroupMessageReceived(int groupnumber, int friendgroupnumber, cons g->chatForm->addGroupMessage(message, friendgroupnumber); - if ((isGroupWidgetActive != 1 || (g->groupId != activeGroupWidget->groupId)) || isWindowMinimized || !isActiveWindow()) + if ((static_cast(g->widget) != activeChatroomWidget) || isWindowMinimized || !isActiveWindow()) { + g->hasNewMessages = 1; if (message.contains(core->getUsername(), Qt::CaseInsensitive)) { newMessageAlert(); - g->hasNewMessages = 1; g->userWasMentioned = 1; - if (useNativeTheme) - g->widget->statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); - else - g->widget->statusPic.setPixmap(QPixmap(":img/status/dot_groupchat_notification.png")); } - else - if (g->hasNewMessages == 0) - { - g->hasNewMessages = 1; - if (useNativeTheme) - g->widget->statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); - else - g->widget->statusPic.setPixmap(QPixmap(":img/status/dot_groupchat_newmessages.png")); - } + g->widget->updateStatusLight(); } } @@ -742,40 +615,12 @@ void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Cha g->updatePeer(peernumber,core->getGroupPeerName(groupnumber, peernumber)); } -void Widget::onGroupWidgetClicked(GroupWidget* widget) -{ - Group* g = GroupList::findGroup(widget->groupId); - if (!g) - return; - - hideMainForms(); - g->chatForm->show(*ui); - if (activeGroupWidget != nullptr) - { - activeGroupWidget->setAsInactiveChatroom(); - } - activeGroupWidget = widget; - widget->setAsActiveChatroom(); - isFriendWidgetActive = 0; - isGroupWidgetActive = 1; - - if (g->hasNewMessages != 0) - { - g->hasNewMessages = 0; - g->userWasMentioned = 0; - if (useNativeTheme) - g->widget->statusPic.setPixmap(QPixmap(":img/status/dot_online.png")); - else - g->widget->statusPic.setPixmap(QPixmap(":img/status/dot_groupchat.png")); - } -} - void Widget::removeGroup(int groupId) { Group* g = GroupList::findGroup(groupId); g->widget->setAsInactiveChatroom(); - if (g->widget == activeGroupWidget) - activeGroupWidget = nullptr; + if (static_cast(g->widget) == activeChatroomWidget) + activeChatroomWidget = nullptr; GroupList::removeGroup(groupId); core->removeGroup(groupId); delete g; @@ -799,13 +644,11 @@ Group *Widget::createGroup(int groupId) QString groupName = QString("Groupchat #%1").arg(groupId); Group* newgroup = GroupList::addGroup(groupId, groupName); - QWidget* widget = ui->friendList->widget(); - QLayout* layout = widget->layout(); + QLayout* layout = contactListWidget->getGroupLayout(); layout->addWidget(newgroup->widget); - if (!useNativeTheme) - newgroup->widget->statusPic.setPixmap(QPixmap(":img/status/dot_groupchat.png")); - updateFriendListWidth(); - connect(newgroup->widget, SIGNAL(groupWidgetClicked(GroupWidget*)), this, SLOT(onGroupWidgetClicked(GroupWidget*))); + newgroup->widget->updateStatusLight(); + + connect(newgroup->widget, SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*))); connect(newgroup->widget, SIGNAL(removeGroup(int)), this, SLOT(removeGroup(int))); connect(newgroup->chatForm, SIGNAL(sendMessage(int,QString)), core, SLOT(sendGroupMessage(int,QString))); return newgroup; @@ -825,23 +668,13 @@ bool Widget::isFriendWidgetCurActiveWidget(Friend* f) { if (!f) return false; - if (activeFriendWidget != nullptr) - { - Friend* f2 = FriendList::findFriend(activeFriendWidget->friendId); - if ((f->friendId != f2->friendId) || isFriendWidgetActive == 0) - return false; - } + + if (activeChatroomWidget == static_cast(f->widget)) + return true; else return false; - return true; } -void Widget::resizeEvent(QResizeEvent *) -{ - updateFriendListWidth(); -} - - bool Widget::event(QEvent * e) { @@ -854,35 +687,24 @@ bool Widget::event(QEvent * e) } else if (e->type() == QEvent::WindowActivate) { - if (!useNativeTheme) + if (!Settings::getInstance().getUseNativeDecoration()) { this->setObjectName("activeWindow"); this->style()->polish(this); } isWindowMinimized = 0; - if (isFriendWidgetActive && activeFriendWidget != nullptr) + if (activeChatroomWidget != nullptr) { - Friend* f = FriendList::findFriend(activeFriendWidget->friendId); - f->hasNewMessages = 0; - updateFriendStatusLights(f->friendId); - } - else if (isGroupWidgetActive && activeGroupWidget != nullptr) - { - Group* g = GroupList::findGroup(activeGroupWidget->groupId); - g->hasNewMessages = 0; - g->userWasMentioned = 0; - if (useNativeTheme) - g->widget->statusPic.setPixmap(QPixmap(":img/status/dot_online.png")); - else - g->widget->statusPic.setPixmap(QPixmap(":img/status/dot_groupchat.png")); + activeChatroomWidget->resetEventFlags(); + activeChatroomWidget->updateStatusLight(); } } - else if (e->type() == QEvent::WindowDeactivate && !useNativeTheme) + else if (e->type() == QEvent::WindowDeactivate && !Settings::getInstance().getUseNativeDecoration()) { this->setObjectName("inactiveWindow"); this->style()->polish(this); } - else if (e->type() == QEvent::MouseMove && !useNativeTheme) + else if (e->type() == QEvent::MouseMove && !Settings::getInstance().getUseNativeDecoration()) { QMouseEvent *k = (QMouseEvent *)e; int xMouse = k->pos().x(); @@ -927,7 +749,7 @@ bool Widget::event(QEvent * e) void Widget::mousePressEvent(QMouseEvent *e) { - if (!useNativeTheme) + if (!Settings::getInstance().getUseNativeDecoration()) { if (e->button() == Qt::LeftButton) { @@ -961,7 +783,7 @@ void Widget::mousePressEvent(QMouseEvent *e) void Widget::mouseReleaseEvent(QMouseEvent *e) { - if (!useNativeTheme) + if (!Settings::getInstance().getUseNativeDecoration()) { moveWidget = false; allowToResize = false; @@ -976,7 +798,7 @@ void Widget::mouseReleaseEvent(QMouseEvent *e) void Widget::mouseDoubleClickEvent(QMouseEvent *e) { - if (!useNativeTheme) + if (!Settings::getInstance().getUseNativeDecoration()) { if (e->pos().x() < ui->tbMenu->geometry().right() and e->pos().y() < ui->tbMenu->geometry().bottom() and e->pos().x() >= ui->tbMenu->geometry().x() and e->pos().y() >= ui->tbMenu->geometry().y() @@ -1000,7 +822,7 @@ void Widget::paintEvent (QPaintEvent *) void Widget::moveWindow(QMouseEvent *e) { - if (!useNativeTheme) + if (!Settings::getInstance().getUseNativeDecoration()) { if (e->buttons() & Qt::LeftButton) { @@ -1012,8 +834,7 @@ void Widget::moveWindow(QMouseEvent *e) void Widget::resizeWindow(QMouseEvent *e) { - updateFriendListWidth(); - if (!useNativeTheme) + if (!Settings::getInstance().getUseNativeDecoration()) { if (allowToResize) { @@ -1141,47 +962,47 @@ void Widget::setTitlebarMode(const TitleMode &flag) switch (m_titleMode) { - case CleanTitle: - ui->tbMenu->setHidden(true); - ui->pbMin->setHidden(true); - ui->pbMax->setHidden(true); - ui->pbClose->setHidden(true); - break; - case OnlyCloseButton: - ui->tbMenu->setHidden(true); - ui->pbMin->setHidden(true); - ui->pbMax->setHidden(true); - break; - case MenuOff: - ui->tbMenu->setHidden(true); - break; - case MaxMinOff: - ui->pbMin->setHidden(true); - ui->pbMax->setHidden(true); - break; - case FullScreenMode: - ui->pbMax->setHidden(true); - showMaximized(); - break; - case MaximizeModeOff: - ui->pbMax->setHidden(true); - break; - case MinimizeModeOff: - ui->pbMin->setHidden(true); - break; - case FullTitle: - ui->tbMenu->setVisible(true); - ui->pbMin->setVisible(true); - ui->pbMax->setVisible(true); - ui->pbClose->setVisible(true); - break; - break; - default: - ui->tbMenu->setVisible(true); - ui->pbMin->setVisible(true); - ui->pbMax->setVisible(true); - ui->pbClose->setVisible(true); - break; + case CleanTitle: + ui->tbMenu->setHidden(true); + ui->pbMin->setHidden(true); + ui->pbMax->setHidden(true); + ui->pbClose->setHidden(true); + break; + case OnlyCloseButton: + ui->tbMenu->setHidden(true); + ui->pbMin->setHidden(true); + ui->pbMax->setHidden(true); + break; + case MenuOff: + ui->tbMenu->setHidden(true); + break; + case MaxMinOff: + ui->pbMin->setHidden(true); + ui->pbMax->setHidden(true); + break; + case FullScreenMode: + ui->pbMax->setHidden(true); + showMaximized(); + break; + case MaximizeModeOff: + ui->pbMax->setHidden(true); + break; + case MinimizeModeOff: + ui->pbMin->setHidden(true); + break; + case FullTitle: + ui->tbMenu->setVisible(true); + ui->pbMin->setVisible(true); + ui->pbMax->setVisible(true); + ui->pbClose->setVisible(true); + break; + break; + default: + ui->tbMenu->setVisible(true); + ui->pbMin->setVisible(true); + ui->pbMax->setVisible(true); + ui->pbClose->setVisible(true); + break; } ui->LTitle->setVisible(true); } @@ -1238,11 +1059,12 @@ bool Widget::eventFilter(QObject *, QEvent *event) if (event->type() == QEvent::Wheel) { QWheelEvent * whlEvnt = static_cast< QWheelEvent * >( event ); - if (whlEvnt->angleDelta().x() != 0) - { - event->accept(); - return true; - } + whlEvnt->angleDelta().setX(0); } return false; } + +bool Widget::getIsWindowMinimized() +{ + return static_cast(isWindowMinimized); +} diff --git a/widget/widget.h b/widget/widget.h index 7bcc643a8..54f2e9676 100644 --- a/widget/widget.h +++ b/widget/widget.h @@ -18,7 +18,7 @@ #define WIDGET_H #include -#include +#include #include #include #include @@ -27,19 +27,19 @@ #include "widget/form/settingsform.h" #include "widget/form/filesform.h" #include "camera.h" +#include "friendlistwidget.h" #define PIXELS_TO_ACT 7 namespace Ui { -class Widget; +class MainWindow; } -class GroupWidget; -struct FriendWidget; +class GenericChatroomWidget; class Group; struct Friend; -class Widget : public QWidget +class Widget : public QMainWindow { Q_OBJECT @@ -57,10 +57,10 @@ public: void showTestCamview(); void newMessageAlert(); bool isFriendWidgetCurActiveWidget(Friend* f); - void updateFriendStatusLights(int friendId); - int useNativeTheme; + bool getIsWindowMinimized(); ~Widget(); - void updateFriendListWidth(); + + virtual void closeEvent(QCloseEvent *event); signals: void friendRequestAccepted(const QString& userId); @@ -70,9 +70,6 @@ signals: void usernameChanged(const QString& username); void statusMessageChanged(const QString& statusMessage); -protected: - void resizeEvent(QResizeEvent *); - private slots: void maximizeBtnClicked(); void minimizeBtnClicked(); @@ -97,19 +94,17 @@ private slots: void onFriendUsernameChanged(int friendId, const QString& username); void onFriendStatusMessageLoaded(int friendId, const QString& message); void onFriendUsernameLoaded(int friendId, const QString& username); - void onFriendWidgetClicked(FriendWidget* widget); + void onChatroomWidgetClicked(GenericChatroomWidget *); void onFriendMessageReceived(int friendId, const QString& message); void onFriendRequestReceived(const QString& userId, const QString& message); void onEmptyGroupCreated(int groupId); void onGroupInviteReceived(int32_t friendId, const uint8_t *publicKey); void onGroupMessageReceived(int groupnumber, int friendgroupnumber, const QString& message); void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change); - void onGroupWidgetClicked(GroupWidget* widget); void removeFriend(int friendId); void clearFriends(); void copyFriendIdToClipboard(int friendId); void removeGroup(int groupId); - void splitterMoved(int pos, int index); void setStatusOnline(); void setStatusAway(); void setStatusBusy(); @@ -123,7 +118,7 @@ private: void removeFriend(Friend* f); private: - Ui::Widget *ui; + Ui::MainWindow *ui; QSplitter *centralLayout; QPoint dragPosition; TitleMode m_titleMode; @@ -147,9 +142,8 @@ private: SettingsForm settingsForm; FilesForm filesForm; static Widget* instance; - FriendWidget* activeFriendWidget; - GroupWidget* activeGroupWidget; - int isFriendWidgetActive, isGroupWidgetActive; + GenericChatroomWidget* activeChatroomWidget; + FriendListWidget* contactListWidget; SelfCamView* camview; Camera* camera; bool notify(QObject *receiver, QEvent *event);