Merge branch 'master' into profiles
166
INSTALL.md
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
##Dependencies
|
||||||
|
|
||||||
|
| Name | Version | Modules |
|
||||||
|
|--------------|-------------|-------------------------------- |
|
||||||
|
| Qt | >= 5.2.0 | core, gui, network, widget, xml |
|
||||||
|
| GCC/MinGW | >= 4.6 | C++11 enabled |
|
||||||
|
| Tox Core | most recent | core, av |
|
||||||
|
| OpenCV | >= 2.4.9 | core, highgui |
|
||||||
|
| OpenAL Soft | >= 1.16.0 | |
|
||||||
|
|
||||||
|
##Windows
|
||||||
|
|
||||||
|
###Qt
|
||||||
|
|
||||||
|
Download the Qt online installer for Windows from [qt-project.org](http://qt-project.org/downloads).
|
||||||
|
While installation you have to assemble your Qt toolchain. Take the most recent version of Qt compiled with MinGW.
|
||||||
|
Although the installer provides its own bundled MinGW compiler toolchain its recommend installing it separately because Qt is missing MSYS which is needed to compile and install OpenCV and OpenAL. Thus you can - if needed - deselect the tab "Tools".
|
||||||
|
The following steps assume that Qt is installed at "C:\Qt". If you decided to choose another location, replace corresponding parts.
|
||||||
|
|
||||||
|
###MinGW
|
||||||
|
|
||||||
|
Download the MinGW installer for Windows from [sourceforge.net](http://sourceforge.net/projects/mingw/files/Installer/).
|
||||||
|
Make sure to install MSYS (a set of Unix tools for Windows).
|
||||||
|
The following steps assume that MinGW is installed at "C:\MinGW". If you decided to choose another location, replace corresponding parts.
|
||||||
|
|
||||||
|
###Setting up Path
|
||||||
|
|
||||||
|
Add MinGW/MSYS binaries to the system path to make them globally accessible.
|
||||||
|
Open Control Panel -> System and Security -> System -> Advanced system settings -> Environment Variables...
|
||||||
|
In the second box search for the PATH variable and press Edit...
|
||||||
|
The input box "Variable value:" should already contain some directories. Each directory is separated with a semicolon.
|
||||||
|
Extend the input box by adding ";C:\MinGW\bin;C:\MinGW\msys\1.0\bin". The very first semicolon must only be added if it is missing.
|
||||||
|
|
||||||
|
###Cloning the Repository
|
||||||
|
|
||||||
|
Clone the repository (https://github.com/tux3/qTox.git) with your preferred Git client. [SmartGit](http://www.syntevo.com/smartgit/) is very nice for this task.
|
||||||
|
The following steps assume that you cloned the repository at "C:\qTox". If you decided to choose another location, replace corresponding parts.
|
||||||
|
|
||||||
|
###Tox Core
|
||||||
|
|
||||||
|
[jenkins.libtoxcore.so](http://jenkins.libtoxcore.so/job/libtoxcore-win32-i686/lastSuccessfulBuild/artifact/libtoxcore-win32-i686.zip)
|
||||||
|
provides a prebuild package of Tox Core. Download this package and extract its content to "C:\qTox\libs". You may have to create the directory "libs".
|
||||||
|
If you prefer to compile Tox Core on your own follow the instructions at https://github.com/irungentoo/toxcore/blob/master/INSTALL.md#windows
|
||||||
|
|
||||||
|
###OpenCV
|
||||||
|
|
||||||
|
Unfortunately there are no prebuild packages for OpenCV compiled with MinGW. Thus, you have to create your own.
|
||||||
|
First of all download and install the most recent version of CMake from
|
||||||
|
[cmake.org](http://www.cmake.org/cmake/resources/software.html).
|
||||||
|
Afterwards download OpenCV in version 2.4.9 from [sourceforge.net](http://sourceforge.net/projects/opencvlibrary/files/opencv-unix/2.4.9/opencv-2.4.9.zip/download) and extract the content of the source archive to "C:\qTox\libs". Furthermore, create a new directory named "opencv-build" in "C:\qTox\libs".
|
||||||
|
Now you should have the two directories "opencv-2.4.9" and "opencv-build" inside your "C:\qTox\libs" directory.
|
||||||
|
|
||||||
|
Run CMake (cmake-gui) and set up the input boxes "Where is the source code:" and "Where to build the binaries" with "C:\qTox\libs\opencv-2.4.9" and "C:\qTox\libs\opencv-build". Press configure and choose "MSYS Makefiles" in the drop down menu with "Use default native compilers". To start initial configuration press Finish. Given that qTox only needs some components of OpenCV it's recommended to disable not required modules. Furthermore, this will decrease compilation time of OpenCV dramatically. Each module begins with "BUILD_opencv_" and can be disabled by deselecting its entry. Use the "Search" input box for convenience. Disable all modules except of "core", "highgui" and "imgproc" (highgui depends on imgproc and will automatically be disabled if imgproc is disabled). For maximum performance search for "CMAKE_BUILD_TYPE" and set this value to "Release". Finally, make sure "CMAKE_INSTALL_PREFIX" points to "C:\qTox\libs\opencv-build\install" (should be by default). To update the configuration press Configure again. To generate the Makefiles press Generate.
|
||||||
|
|
||||||
|
Open a new command prompt within "C:\qTox\libs\opencv-build" (HINT: Use shift + right click -> "Open command window here" on the directory within Windows Explorer). Compile and install OpenCV with the following command. It's not recommended to use -j for multicore compilation, because it freezes the terminal from time to time.
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
After OpenCV was successfully installed to "C:\qTox\libs\opencv-build\install" copy the dlls "libopencv_core249.dll", "libopencv_highgui249.dll" and "libopencv_imgproc249.dll" located at "C:\qTox\libs\opencv-build\install\x86\mingw\bin" to "C:\qTox\libs\lib". Afterwards copy the content of the directory "C:\qTox\libs\opencv-build\install\include" to "C:\qTox\libs\include". Finally, you have to patch the file "C:\qTox\libs\include\opencv2\opencv.hpp" because it includes all modules of OpenCV regardless of your configuration. Open this file with your preferred text editor and remove all includes except of "opencv2/core/core_c.h", "opencv2/core/core.hpp", "opencv2/imgproc/imgproc_c.h", "opencv2/imgproc/imgproc.hpp", "opencv2/highgui/highgui_c.h" and "opencv2/highgui/highgui.hpp". OpenCV is now ready to use. Feel free to delete the directories "opencv-2.4.9" and "opencv-build", but you don't need to.
|
||||||
|
|
||||||
|
###OpenAL Soft
|
||||||
|
As for OpenCV there are no prebuild packages of OpenAL Softe compiled with MinGW, but the installation process is very similar to OpenCV. Download the most recent source archive of OpenAL Soft from [http://kcat.strangesoft.net](http://kcat.strangesoft.net/openal.html#download). Extract its content to "C:\qTox\libs". Besides the source folder itself you'll find the file "pax_global_header". It is not required and can be deleted. Create the directory "openal-build" next to source folder. Now you should have the two directories "openal-soft-x.y.z" where x.y.z is the version of OpenAL and "openal-build" inside your "C:\qTox\libs" directory. Run CMake (cmake-gui) and setup the source and build location. Run the initial configuration and use "MSYS Makefiles" with "Use default native compilers". The only thing you need to configure is "CMAKE_INSTALL_PREFIX" which does not point to "C:\qTox\libs\openal-build\install" by default. Configure the project and generate the Makefiles. Compile and install OpenAL Soft with:
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
Copy the dll "OpenAL32.dll" located at "C:\qTox\libs\openal-build\install\bin" to "C:\qTox\libs\lib". Finally, copy the directory "AL" located at "C:\qTox\libs\openal-build\install\include" to "C:\qTox\libs\include". Unlike OpenCV you don't need to patch any files. Feel free to delete the directories "openal-soft-x.y.z" and "openal-build", but you don't need to.
|
||||||
|
|
||||||
|
##Linux
|
||||||
|
Most of the dependencies should be available through your package manger.
|
||||||
|
|
||||||
|
###Cloning the Repository
|
||||||
|
In order to clone the qTox repository you need Git.
|
||||||
|
|
||||||
|
Debian:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install git
|
||||||
|
```
|
||||||
|
|
||||||
|
Ubuntu:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install git
|
||||||
|
```
|
||||||
|
|
||||||
|
Arch Linux:
|
||||||
|
```bash
|
||||||
|
sudo pacman -S --needed git
|
||||||
|
```
|
||||||
|
|
||||||
|
Fedora:
|
||||||
|
```bash
|
||||||
|
yum install git
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterwards open a new Terminal, change to a directory of your choice and clone the repository:
|
||||||
|
```bash
|
||||||
|
cd /home/user/qTox
|
||||||
|
git clone https://github.com/tux3/qTox.git qTox
|
||||||
|
```
|
||||||
|
|
||||||
|
The following steps assumes that you cloned the repository at "/home/user/qTox". If you decided to choose another location, replace corresponding parts.
|
||||||
|
|
||||||
|
###GCC, Qt, OpenCV and OpanAL Soft
|
||||||
|
|
||||||
|
Debian:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install build-essential qt5-qmake qt5-default libopenal-dev libopencv-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Ubuntu:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install build-essential qt5-qmake qt5-default libopenal-dev libopencv-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Arch Linux:
|
||||||
|
```bash
|
||||||
|
sudo pacman -S --needed base-devel qt5 opencv openal
|
||||||
|
```
|
||||||
|
|
||||||
|
Fedora:
|
||||||
|
```bash
|
||||||
|
yum groupinstall "Development Tools"
|
||||||
|
yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel
|
||||||
|
```
|
||||||
|
|
||||||
|
###Tox Core
|
||||||
|
|
||||||
|
First of all install the dependencies of Tox Core.
|
||||||
|
|
||||||
|
Debian:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install libtool autotools-dev automake checkinstall check yasm libopus-dev libvpx-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Ubuntu:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install libtool autotools-dev automake checkinstall check yasm libopus-dev libvpx-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Arch Linux: (Arch Linux provides the package "tox-git" in AUR)
|
||||||
|
```bash
|
||||||
|
sudo pacman -S --needed yasm opus vpx
|
||||||
|
```
|
||||||
|
|
||||||
|
Fedora:
|
||||||
|
```bash
|
||||||
|
yum install libtool autoconf automake check check-devel
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can either follow the instructions at https://github.com/irungentoo/toxcore/blob/master/INSTALL.md#unix or use the "bootstrap.sh" script located at "/home/user/qTox".
|
||||||
|
The script will automatically download and install Tox Core and libsodium to "/home/user/qTox/libs":
|
||||||
|
```bash
|
||||||
|
cd /home/user/qTox
|
||||||
|
./bootstrap.sh # use -h or --help for more information
|
||||||
|
```
|
||||||
|
|
||||||
|
##Building packages
|
||||||
|
|
||||||
|
qTox now has the experimental and probably-dodgy ability to package itself (in .deb
|
||||||
|
form natively, and .rpm form with <a href="http://joeyh.name/code/alien/">alien</a>).
|
||||||
|
|
||||||
|
After installing the required dependencies, run `bootstrap.sh` and then run the
|
||||||
|
`buildPackages.sh` script, found in the tools folder. It will automatically get the
|
||||||
|
packages necessary for building .debs, so be prepared to type your password for sudo.
|
100
README.md
|
@ -28,102 +28,6 @@ This client runs on Windows, Linux and Mac natively.<br/>
|
||||||
<img src="http://i.imgur.com/mMUdr6u.png"/>
|
<img src="http://i.imgur.com/mMUdr6u.png"/>
|
||||||
<img src="http://i.imgur.com/66ARBGC.png"/>
|
<img src="http://i.imgur.com/66ARBGC.png"/>
|
||||||
|
|
||||||
<h3>Compiling on GNU-Linux</h3>
|
##Documentation:
|
||||||
<h4>Acquiring dependencies</h4>
|
|
||||||
Compiling qTox requires several dependencies, however these are easily installable
|
|
||||||
with your system's package manager. The step-by-step instructions assume Debian-style apt, but
|
|
||||||
it should be easy enough to get equivalent packages with yum or pacman.
|
|
||||||
|
|
||||||
First, we need Qt 5.2 with a C++11 compatible compiler:
|
[Compiling](/INSTALL.md)
|
||||||
```bash
|
|
||||||
sudo apt-get install build-essential qt5-qmake qt5-default
|
|
||||||
```
|
|
||||||
|
|
||||||
toxcore and toxav, the client-agnostic network code for Tox, has several dependencies
|
|
||||||
of its own (see <a href="https://github.com/irungentoo/toxcore/blob/master/INSTALL.md#unix">its installation guide</a> for more details):
|
|
||||||
```bash
|
|
||||||
sudo apt-get install libtool autotools-dev automake checkinstall check git yasm libopus-dev libvpx-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, qTox itself requires OpenAL and OpenCV:
|
|
||||||
```bash
|
|
||||||
sudo apt-get install libopenal-dev libopencv-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
<h4>Compilation</h4>
|
|
||||||
|
|
||||||
Having acquired all the dependencies, the following commands should get and compile qTox:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wget -O qtox.tgz https://github.com/tux3/qTox/archive/master.tar.gz
|
|
||||||
tar xvf qtox.tgz
|
|
||||||
cd qTox-master
|
|
||||||
./bootstrap.sh # This will automagically download and compile libsodium, toxcore, and toxav
|
|
||||||
qmake
|
|
||||||
make # Should compile to "qtox"
|
|
||||||
```
|
|
||||||
|
|
||||||
And that's it!
|
|
||||||
|
|
||||||
<h4>Building packages</h4>
|
|
||||||
|
|
||||||
qTox now has the experimental and probably-dodgy ability to package itself (in .deb
|
|
||||||
form natively, and .rpm form with <a href="http://joeyh.name/code/alien/">alien</a>).
|
|
||||||
|
|
||||||
After installing the required dependencies, run `bootstrap.sh` and then run the
|
|
||||||
`buildPackages.sh` script, found in the tools folder. It will automatically get the
|
|
||||||
packages necessary for building .debs, so be prepared to type your password for sudo.
|
|
||||||
|
|
||||||
<h3>OSX Easy Install</h3>
|
|
||||||
|
|
||||||
Since https://github.com/ReDetection/homebrew-qtox you can easily install qtox with homebrew
|
|
||||||
```bash
|
|
||||||
brew install --HEAD ReDetection/qtox/qtox
|
|
||||||
```
|
|
||||||
|
|
||||||
<h3>OSX Full Install Guide</h3>
|
|
||||||
|
|
||||||
<strong>This guide is intended for people who wish to use an existing or new ProjectTox-Core installation separate to the bundled installation with qTox, if you do not wish to use a separate installation you can skip to the section titled 'Final Steps'.</strong>
|
|
||||||
|
|
||||||
Installation on OSX, isn't quite straight forward, here is a quick guide on how to install;
|
|
||||||
|
|
||||||
The first thing you need to do is install ProjectTox-Core with a/v support. Refer to the INSTALL guide in the ProjectTox-Core github repo.
|
|
||||||
|
|
||||||
Next you need to download QtTools (http://qt-project.org/downloads), at the time of writing this is at version 5.3.0.
|
|
||||||
Make sure you deselect all the unnecessary components from the 5.3 checkbox (iOS/Android libs) otherwise you will end up with a very large download.
|
|
||||||
|
|
||||||
Once that is installed you will most likely need to set the path for qmake. To do this, open up terminal and paste in the following;
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export PATH=/location/to/qmake/binary:$PATH
|
|
||||||
```
|
|
||||||
|
|
||||||
For myself, the qmake binary was located in /Users/mouseym/Qt/5.3/clang_64/bin/.
|
|
||||||
|
|
||||||
This is not a permanent change, it will revert when you close the terminal window, to add it permanently you will need to add echo the above line to your .profile/.bash_profile.
|
|
||||||
|
|
||||||
Once this is installed, do the following;
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/tux3/qTox
|
|
||||||
cd toxgui
|
|
||||||
qmake
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, we need to create a symlink to /usr/local/lib/ and /usr/local/include/
|
|
||||||
```
|
|
||||||
mkdir -p $HOME/qTox/libs
|
|
||||||
sudo ln -s /usr/local/lib $HOME/qTox/libs/lib
|
|
||||||
sudo ln -s /usr/local/include $HOME/qTox/libs/include
|
|
||||||
```
|
|
||||||
<h5>Final Steps</h5>
|
|
||||||
|
|
||||||
The final step is to run
|
|
||||||
```bash
|
|
||||||
make
|
|
||||||
```
|
|
||||||
in the qTox directory, or if you are using the bundled tox core installation, you can use
|
|
||||||
```bash
|
|
||||||
./bootstrap.sh
|
|
||||||
```
|
|
||||||
Assuming all went well you should now have a qTox.app file within the directory. Double click and it should open!
|
|
||||||
|
|
329
filetransferinstance.cpp
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "filetransferinstance.h"
|
||||||
|
#include "widget/widget.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include "math.h"
|
||||||
|
#include "style.h"
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
uint FileTransferInstance::Idconter = 0;
|
||||||
|
|
||||||
|
FileTransferInstance::FileTransferInstance(ToxFile File)
|
||||||
|
: lastUpdate{QDateTime::currentDateTime()}, lastBytesSent{0},
|
||||||
|
fileNum{File.fileNum}, friendId{File.friendId}, direction{File.direction}
|
||||||
|
{
|
||||||
|
id = Idconter++;
|
||||||
|
state = tsPending;
|
||||||
|
|
||||||
|
filename = File.fileName;
|
||||||
|
size = getHumanReadableSize(File.filesize);
|
||||||
|
speed = "0B/s";
|
||||||
|
eta = "00:00";
|
||||||
|
if (File.direction == ToxFile::SENDING)
|
||||||
|
{
|
||||||
|
QImage preview;
|
||||||
|
File.file->seek(0);
|
||||||
|
if (preview.loadFromData(File.file->readAll()))
|
||||||
|
{
|
||||||
|
pic = preview.scaledToHeight(50);
|
||||||
|
}
|
||||||
|
File.file->seek(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::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),'f',2).append(suffix[exp]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction)
|
||||||
|
{
|
||||||
|
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = tsProcessing;
|
||||||
|
QDateTime newtime = QDateTime::currentDateTime();
|
||||||
|
int timediff = lastUpdate.secsTo(newtime);
|
||||||
|
if (timediff <= 0)
|
||||||
|
return;
|
||||||
|
qint64 diff = BytesSent - lastBytesSent;
|
||||||
|
if (diff < 0)
|
||||||
|
{
|
||||||
|
qWarning() << "FileTransferInstance::onFileTransferInfo: Negative transfer speed !";
|
||||||
|
diff = 0;
|
||||||
|
}
|
||||||
|
long rawspeed = diff / timediff;
|
||||||
|
speed = getHumanReadableSize(rawspeed)+"/s";
|
||||||
|
size = getHumanReadableSize(Filesize);
|
||||||
|
if (!rawspeed)
|
||||||
|
return;
|
||||||
|
int etaSecs = (Filesize - BytesSent) / rawspeed;
|
||||||
|
QTime etaTime(0,0);
|
||||||
|
etaTime = etaTime.addSecs(etaSecs);
|
||||||
|
eta = etaTime.toString("mm:ss");
|
||||||
|
lastUpdate = newtime;
|
||||||
|
lastBytesSent = BytesSent;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction)
|
||||||
|
{
|
||||||
|
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
|
||||||
|
return;
|
||||||
|
disconnect(Widget::getInstance()->getCore(),0,this,0);
|
||||||
|
state = tsCanceled;
|
||||||
|
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::onFileTransferFinished(ToxFile File)
|
||||||
|
{
|
||||||
|
if (File.fileNum != fileNum || File.friendId != friendId || File.direction != direction)
|
||||||
|
return;
|
||||||
|
disconnect(Widget::getInstance()->getCore(),0,this,0);
|
||||||
|
|
||||||
|
if (File.direction == ToxFile::RECEIVING)
|
||||||
|
{
|
||||||
|
QImage preview;
|
||||||
|
QFile previewFile(File.filePath);
|
||||||
|
if (previewFile.open(QIODevice::ReadOnly) && previewFile.size() <= 1024*1024*25) // Don't preview big (>25MiB) images
|
||||||
|
{
|
||||||
|
if (preview.loadFromData(previewFile.readAll()))
|
||||||
|
{
|
||||||
|
pic = preview.scaledToHeight(50);
|
||||||
|
}
|
||||||
|
previewFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = tsFinished;
|
||||||
|
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::cancelTransfer()
|
||||||
|
{
|
||||||
|
Widget::getInstance()->getCore()->cancelFileSend(friendId, fileNum);
|
||||||
|
state = tsCanceled;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::rejectRecvRequest()
|
||||||
|
{
|
||||||
|
Widget::getInstance()->getCore()->rejectFileRecvRequest(friendId, fileNum);
|
||||||
|
onFileTransferCancelled(friendId, fileNum, direction);
|
||||||
|
state = tsCanceled;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
// for whatever the fuck reason, QFileInfo::isWritable() always fails for files that don't exist
|
||||||
|
// which makes it useless for our case
|
||||||
|
// since QDir doesn't have an isWritable(), the only option I can think of is to make/delete the file
|
||||||
|
// surely this is a common problem that has a qt-implemented solution?
|
||||||
|
bool isFileWritable(QString& path)
|
||||||
|
{
|
||||||
|
QFile file(path);
|
||||||
|
bool exists = file.exists();
|
||||||
|
bool out = file.open(QIODevice::WriteOnly);
|
||||||
|
file.close();
|
||||||
|
if (!exists)
|
||||||
|
file.remove();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::acceptRecvRequest()
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
path = QFileDialog::getSaveFileName(Widget::getInstance(), tr("Save a file","Title of the file saving dialog"), QDir::current().filePath(filename));
|
||||||
|
if (path.isEmpty())
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//bool savable = QFileInfo(path).isWritable();
|
||||||
|
//qDebug() << path << " is writable: " << savable;
|
||||||
|
//qDebug() << "/home/bill/bliss.pdf writable: " << QFileInfo("/home/bill/bliss.pdf").isWritable();
|
||||||
|
if (isFileWritable(path))
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
QMessageBox::warning(Widget::getInstance(), 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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
savePath = path;
|
||||||
|
|
||||||
|
Widget::getInstance()->getCore()->acceptFileRecvRequest(friendId, fileNum, path);
|
||||||
|
state = tsProcessing;
|
||||||
|
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::pauseResumeRecv()
|
||||||
|
{
|
||||||
|
Widget::getInstance()->getCore()->pauseResumeFileRecv(friendId, fileNum);
|
||||||
|
if (state == tsProcessing)
|
||||||
|
state = tsPaused;
|
||||||
|
else state = tsProcessing;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::pauseResumeSend()
|
||||||
|
{
|
||||||
|
Widget::getInstance()->getCore()->pauseResumeFileSend(friendId, fileNum);
|
||||||
|
if (state == tsProcessing)
|
||||||
|
state = tsPaused;
|
||||||
|
else state = tsProcessing;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::QImage2base64(const QImage &img)
|
||||||
|
{
|
||||||
|
QByteArray ba;
|
||||||
|
QBuffer buffer(&ba);
|
||||||
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
img.save(&buffer, "PNG"); // writes image into ba in PNG format
|
||||||
|
return ba.toBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::getHtmlImage()
|
||||||
|
{
|
||||||
|
qDebug() << "QString FileTransferInstance::getHtmlImage() " << state;
|
||||||
|
|
||||||
|
QString res;
|
||||||
|
if (state == tsPending || state == tsProcessing || state == tsPaused)
|
||||||
|
{
|
||||||
|
QImage leftBtnImg(":/ui/fileTransferInstance/stopFileButton.png");
|
||||||
|
QImage rightBtnImg;
|
||||||
|
if (state == tsProcessing)
|
||||||
|
rightBtnImg = QImage(":/ui/fileTransferInstance/pauseFileButton.png");
|
||||||
|
else if (state == tsPaused)
|
||||||
|
rightBtnImg = QImage(":/ui/fileTransferInstance/resumeFileButton.png");
|
||||||
|
else
|
||||||
|
rightBtnImg = QImage(":/ui/fileTransferInstance/acceptFileButton.png");
|
||||||
|
|
||||||
|
res = draw2ButtonsForm("silver", leftBtnImg, rightBtnImg);
|
||||||
|
} else if (state == tsCanceled)
|
||||||
|
{
|
||||||
|
res = drawButtonlessForm("red");
|
||||||
|
} else if (state == tsFinished)
|
||||||
|
{
|
||||||
|
res = drawButtonlessForm("green");
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::pressFromHtml(QString code)
|
||||||
|
{
|
||||||
|
if (state == tsFinished || state == tsCanceled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (direction == ToxFile::SENDING)
|
||||||
|
{
|
||||||
|
if (code == "btnA")
|
||||||
|
cancelTransfer();
|
||||||
|
else if (code == "btnB")
|
||||||
|
pauseResumeSend();
|
||||||
|
} else {
|
||||||
|
if (code == "btnA")
|
||||||
|
rejectRecvRequest();
|
||||||
|
else if (code == "btnB")
|
||||||
|
{
|
||||||
|
if (state == tsPending)
|
||||||
|
acceptRecvRequest();
|
||||||
|
else
|
||||||
|
pauseResumeRecv();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::drawButtonlessForm(const QString &type)
|
||||||
|
{
|
||||||
|
QString imgAStr;
|
||||||
|
QString imgBStr;
|
||||||
|
|
||||||
|
if (type == "red")
|
||||||
|
{
|
||||||
|
imgAStr = "<img src=\"data:placeholder/png;base64," + QImage2base64(QImage(":/ui/fileTransferInstance/emptyLRedFileButton.png")) + "\">";
|
||||||
|
imgBStr = "<img src=\"data:placeholder/png;base64," + QImage2base64(QImage(":/ui/fileTransferInstance/emptyRRedFileButton.png")) + "\">";
|
||||||
|
} else {
|
||||||
|
imgAStr = "<img src=\"data:placeholder/png;base64," + QImage2base64(QImage(":/ui/fileTransferInstance/emptyLGreenFileButton.png")) + "\">";
|
||||||
|
imgBStr = "<img src=\"data:placeholder/png;base64," + QImage2base64(QImage(":/ui/fileTransferInstance/emptyRGreenFileButton.png")) + "\">";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString content = "<p>" + filename + "</p><p>" + size + "</p>";
|
||||||
|
|
||||||
|
return wrapIntoForm(content, type, imgAStr, imgBStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::insertMiniature(const QString &type)
|
||||||
|
{
|
||||||
|
if (pic == QImage())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
QString widgetId = QString::number(getId());
|
||||||
|
|
||||||
|
QString res;
|
||||||
|
res = "<td><div class=" + type + ">\n";
|
||||||
|
res += "<img src=\"data:mini." + widgetId + "/png;base64," + QImage2base64(pic) + "\">";
|
||||||
|
res += "</div></td>\n";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::draw2ButtonsForm(const QString &type, const QImage &imgA, const QImage &imgB)
|
||||||
|
{
|
||||||
|
QString widgetId = QString::number(getId());
|
||||||
|
QString imgAstr = "<img src=\"data:ftrans." + widgetId + ".btnA/png;base64," + QImage2base64(imgA) + "\">";
|
||||||
|
QString imgBstr = "<img src=\"data:ftrans." + widgetId + ".btnB/png;base64," + QImage2base64(imgB) + "\">";
|
||||||
|
|
||||||
|
QString content;
|
||||||
|
content += "<p>" + filename + "</p>";
|
||||||
|
content += "<p>" + getHumanReadableSize(lastBytesSent) + " / " + size + " (" + speed + " ETA: " + eta + ")</p>\n";
|
||||||
|
|
||||||
|
return wrapIntoForm(content, type, imgAstr, imgBstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::wrapIntoForm(const QString& content, const QString &type, const QString &imgAstr, const QString &imgBstr)
|
||||||
|
{
|
||||||
|
QString res;
|
||||||
|
|
||||||
|
res = "<table widht=100% cellspacing=\"0\">\n";
|
||||||
|
res += "<tr valign=middle>\n";
|
||||||
|
res += insertMiniature(type);
|
||||||
|
res += "<td width=100%>\n";
|
||||||
|
res += "<div class=" + type + ">";
|
||||||
|
res += content;
|
||||||
|
res += "</div>\n";
|
||||||
|
res += "</td>\n";
|
||||||
|
res += "<td>\n";
|
||||||
|
res += "<div class=button>" + imgAstr + "<br>" + imgBstr+ "</div>\n";
|
||||||
|
res += "</td>\n";
|
||||||
|
res += "</tr>\n";
|
||||||
|
res += "</table>\n";
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -13,11 +13,10 @@
|
||||||
|
|
||||||
See the COPYING file for more details.
|
See the COPYING file for more details.
|
||||||
*/
|
*/
|
||||||
|
#ifndef FILETRANSFERINSTANCE_H
|
||||||
|
#define FILETRANSFERINSTANCE_H
|
||||||
|
|
||||||
#ifndef FILETRANSFERTWIDGET_H
|
#include <QObject>
|
||||||
#define FILETRANSFERTWIDGET_H
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
|
@ -29,17 +28,22 @@
|
||||||
|
|
||||||
struct ToxFile;
|
struct ToxFile;
|
||||||
|
|
||||||
class FileTransfertWidget : public QWidget
|
class FileTransferInstance : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileTransfertWidget(ToxFile File);
|
explicit FileTransferInstance(ToxFile File);
|
||||||
|
QString getHtmlImage();
|
||||||
|
uint getId(){return id;}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction);
|
void onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction);
|
||||||
void onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction);
|
void onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction);
|
||||||
void onFileTransferFinished(ToxFile File);
|
void onFileTransferFinished(ToxFile File);
|
||||||
|
void pressFromHtml(QString);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void stateUpdated();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void cancelTransfer();
|
void cancelTransfer();
|
||||||
|
@ -50,14 +54,21 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString getHumanReadableSize(unsigned long long size);
|
QString getHumanReadableSize(unsigned long long size);
|
||||||
|
QString QImage2base64(const QImage &img);
|
||||||
|
QString drawButtonlessForm(const QString &type);
|
||||||
|
QString draw2ButtonsForm(const QString &type, const QImage &imgA, const QImage &imgB);
|
||||||
|
QString insertMiniature(const QString &type);
|
||||||
|
QString wrapIntoForm(const QString &content, const QString &type, const QString &imgAstr, const QString &imgBstr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLabel *pic, *filename, *size, *speed, *eta;
|
enum TransfState {tsPending, tsProcessing, tsPaused, tsFinished, tsCanceled};
|
||||||
QPushButton *topright, *bottomright;
|
|
||||||
QProgressBar *progress;
|
static uint Idconter;
|
||||||
QHBoxLayout *mainLayout, *textLayout;
|
uint id;
|
||||||
QVBoxLayout *infoLayout, *buttonLayout;
|
|
||||||
QWidget* buttonWidget;
|
TransfState state;
|
||||||
|
QImage pic;
|
||||||
|
QString filename, size, speed, eta;
|
||||||
QDateTime lastUpdate;
|
QDateTime lastUpdate;
|
||||||
long long lastBytesSent;
|
long long lastBytesSent;
|
||||||
int fileNum;
|
int fileNum;
|
||||||
|
@ -65,7 +76,6 @@ private:
|
||||||
QString savePath;
|
QString savePath;
|
||||||
ToxFile::FileDirection direction;
|
ToxFile::FileDirection direction;
|
||||||
QString stopFileButtonStylesheet, pauseFileButtonStylesheet, acceptFileButtonStylesheet;
|
QString stopFileButtonStylesheet, pauseFileButtonStylesheet, acceptFileButtonStylesheet;
|
||||||
void paintEvent(QPaintEvent *);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FILETRANSFERTWIDGET_H
|
#endif // FILETRANSFERINSTANCE_H
|
23
qtox.pro
|
@ -44,8 +44,10 @@ contains(JENKINS,YES) {
|
||||||
|
|
||||||
# Rules for Windows, Mac OSX, and Linux
|
# Rules for Windows, Mac OSX, and Linux
|
||||||
win32 {
|
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
|
LIBS += -L$$PWD/libs/lib -llibopencv_core249 -llibopencv_highgui249 -llibopencv_imgproc249 -lOpenAL32
|
||||||
} macx {
|
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 -liphlpapi
|
||||||
|
} else {
|
||||||
|
macx {
|
||||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui
|
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui
|
||||||
} else {
|
} else {
|
||||||
# If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
|
# If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
|
||||||
|
@ -57,10 +59,11 @@ win32 {
|
||||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui
|
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui
|
||||||
}
|
}
|
||||||
|
|
||||||
contains(JENKINS, YES) {
|
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
|
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
|
#### Static linux build
|
||||||
|
@ -80,7 +83,6 @@ HEADERS += widget/form/addfriendform.h \
|
||||||
widget/form/filesform.h \
|
widget/form/filesform.h \
|
||||||
widget/tool/chattextedit.h \
|
widget/tool/chattextedit.h \
|
||||||
widget/tool/friendrequestdialog.h \
|
widget/tool/friendrequestdialog.h \
|
||||||
widget/filetransfertwidget.h \
|
|
||||||
widget/friendwidget.h \
|
widget/friendwidget.h \
|
||||||
widget/groupwidget.h \
|
widget/groupwidget.h \
|
||||||
widget/widget.h \
|
widget/widget.h \
|
||||||
|
@ -101,7 +103,11 @@ HEADERS += widget/form/addfriendform.h \
|
||||||
widget/adjustingscrollarea.h \
|
widget/adjustingscrollarea.h \
|
||||||
widget/croppinglabel.h \
|
widget/croppinglabel.h \
|
||||||
widget/friendlistwidget.h \
|
widget/friendlistwidget.h \
|
||||||
widget/genericchatroomwidget.h
|
widget/genericchatroomwidget.h \
|
||||||
|
widget/form/genericchatform.h \
|
||||||
|
widget/tool/chataction.h \
|
||||||
|
widget/chatareawidget.h \
|
||||||
|
filetransferinstance.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
widget/form/addfriendform.cpp \
|
widget/form/addfriendform.cpp \
|
||||||
|
@ -111,7 +117,6 @@ SOURCES += \
|
||||||
widget/form/filesform.cpp \
|
widget/form/filesform.cpp \
|
||||||
widget/tool/chattextedit.cpp \
|
widget/tool/chattextedit.cpp \
|
||||||
widget/tool/friendrequestdialog.cpp \
|
widget/tool/friendrequestdialog.cpp \
|
||||||
widget/filetransfertwidget.cpp \
|
|
||||||
widget/friendwidget.cpp \
|
widget/friendwidget.cpp \
|
||||||
widget/groupwidget.cpp \
|
widget/groupwidget.cpp \
|
||||||
widget/widget.cpp \
|
widget/widget.cpp \
|
||||||
|
@ -134,4 +139,8 @@ SOURCES += \
|
||||||
widget/croppinglabel.cpp \
|
widget/croppinglabel.cpp \
|
||||||
widget/friendlistwidget.cpp \
|
widget/friendlistwidget.cpp \
|
||||||
coreav.cpp \
|
coreav.cpp \
|
||||||
widget/genericchatroomwidget.cpp
|
widget/genericchatroomwidget.cpp \
|
||||||
|
widget/form/genericchatform.cpp \
|
||||||
|
widget/tool/chataction.cpp \
|
||||||
|
widget/chatareawidget.cpp \
|
||||||
|
filetransferinstance.cpp
|
||||||
|
|
10
res.qrc
|
@ -42,6 +42,7 @@
|
||||||
<file>ui/callButton/callButtonYellowHover.png</file>
|
<file>ui/callButton/callButtonYellowHover.png</file>
|
||||||
<file>ui/callButton/callButtonYellowPressed.png</file>
|
<file>ui/callButton/callButtonYellowPressed.png</file>
|
||||||
<file>ui/chatArea/chatArea.css</file>
|
<file>ui/chatArea/chatArea.css</file>
|
||||||
|
<file>ui/chatArea/innerStyle.css</file>
|
||||||
<file>ui/chatArea/scrollBarDownArrow.png</file>
|
<file>ui/chatArea/scrollBarDownArrow.png</file>
|
||||||
<file>ui/chatArea/scrollBarDownArrowHover.png</file>
|
<file>ui/chatArea/scrollBarDownArrowHover.png</file>
|
||||||
<file>ui/chatArea/scrollBarDownArrowPressed.png</file>
|
<file>ui/chatArea/scrollBarDownArrowPressed.png</file>
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
<file>ui/fileButton/fileButton.png</file>
|
<file>ui/fileButton/fileButton.png</file>
|
||||||
<file>ui/fileButton/fileButtonHover.png</file>
|
<file>ui/fileButton/fileButtonHover.png</file>
|
||||||
<file>ui/fileButton/fileButtonPressed.png</file>
|
<file>ui/fileButton/fileButtonPressed.png</file>
|
||||||
|
<file>ui/fileButton/fileButtonDisabled.png</file>
|
||||||
<file>ui/msgEdit/msgEdit.css</file>
|
<file>ui/msgEdit/msgEdit.css</file>
|
||||||
<file>ui/pauseFileButton/default.png</file>
|
<file>ui/pauseFileButton/default.png</file>
|
||||||
<file>ui/pauseFileButton/hover.png</file>
|
<file>ui/pauseFileButton/hover.png</file>
|
||||||
|
@ -124,5 +126,13 @@
|
||||||
<file>ui/micButton/micButtonPressed.png</file>
|
<file>ui/micButton/micButtonPressed.png</file>
|
||||||
<file>ui/micButton/micButton.css</file>
|
<file>ui/micButton/micButton.css</file>
|
||||||
<file>ui/volButton/volButton.css</file>
|
<file>ui/volButton/volButton.css</file>
|
||||||
|
<file>ui/fileTransferInstance/acceptFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/pauseFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/resumeFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/stopFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/emptyLGreenFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/emptyLRedFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/emptyRGreenFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/emptyRRedFileButton.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -78,33 +78,27 @@
|
||||||
<context>
|
<context>
|
||||||
<name>ChatForm</name>
|
<name>ChatForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../widget/form/chatform.cpp" line="282"/>
|
<location filename="../widget/form/chatform.cpp" line="73"/>
|
||||||
<source>Send a file</source>
|
<source>Send a file</source>
|
||||||
<translation>Invia un file</translation>
|
<translation>Invia un file</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../widget/form/chatform.cpp" line="669"/>
|
|
||||||
<location filename="../widget/form/chatform.cpp" line="675"/>
|
|
||||||
<source>Save chat log</source>
|
|
||||||
<translation>Salva il log della chat</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>FileTransfertWidget</name>
|
<name>FileTransferInstance</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../widget/filetransfertwidget.cpp" line="281"/>
|
<location filename="../filetransferinstance.cpp" line="162"/>
|
||||||
<source>Save a file</source>
|
<source>Save a file</source>
|
||||||
<comment>Title of the file saving dialog</comment>
|
<comment>Title of the file saving dialog</comment>
|
||||||
<translation>Salva file</translation>
|
<translation>Salva file</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
<location filename="../filetransferinstance.cpp" line="173"/>
|
||||||
<source>Location not writable</source>
|
<source>Location not writable</source>
|
||||||
<comment>Title of permissions popup</comment>
|
<comment>Title of permissions popup</comment>
|
||||||
<translation>Errore</translation>
|
<translation>Errore</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../widget/filetransfertwidget.cpp" line="292"/>
|
<location filename="../filetransferinstance.cpp" line="173"/>
|
||||||
<source>You do not have permission to write that location. Choose another, or cancel the save dialog.</source>
|
<source>You do not have permission to write that location. Choose another, or cancel the save dialog.</source>
|
||||||
<comment>text of permissions popup</comment>
|
<comment>text of permissions popup</comment>
|
||||||
<translation>Non hai sufficienti permessi per scrivere in questa locazione. Scegli un'altra posizione, o annulla il salvataggio.</translation>
|
<translation>Non hai sufficienti permessi per scrivere in questa locazione. Scegli un'altra posizione, o annulla il salvataggio.</translation>
|
||||||
|
@ -186,29 +180,33 @@
|
||||||
<translation>Rimuovi contatto</translation>
|
<translation>Rimuovi contatto</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>GenericChatForm</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../widget/form/genericchatform.cpp" line="158"/>
|
||||||
|
<location filename="../widget/form/genericchatform.cpp" line="164"/>
|
||||||
|
<source>Save chat log</source>
|
||||||
|
<translation>Salva il log della chat</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>GroupChatForm</name>
|
<name>GroupChatForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../widget/form/groupchatform.cpp" line="49"/>
|
<location filename="../widget/form/groupchatform.cpp" line="39"/>
|
||||||
<source>%1 users in chat</source>
|
<source>%1 users in chat</source>
|
||||||
<comment>Number of users in chat</comment>
|
<comment>Number of users in chat</comment>
|
||||||
<translation>%1 utenti in chat</translation>
|
<translation>%1 utenti in chat</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../widget/form/groupchatform.cpp" line="146"/>
|
<location filename="../widget/form/groupchatform.cpp" line="81"/>
|
||||||
<source><Unknown></source>
|
<source><Unknown></source>
|
||||||
<translation><Sconosciuto></translation>
|
<translation><Sconosciuto></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../widget/form/groupchatform.cpp" line="215"/>
|
<location filename="../widget/form/groupchatform.cpp" line="88"/>
|
||||||
<source>%1 users in chat</source>
|
<source>%1 users in chat</source>
|
||||||
<translation>%1 utenti in chat</translation>
|
<translation>%1 utenti in chat</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<location filename="../widget/form/groupchatform.cpp" line="234"/>
|
|
||||||
<source>Save chat log</source>
|
|
||||||
<translation>Salva il log della chat</translation>
|
|
||||||
</message>
|
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>GroupWidget</name>
|
<name>GroupWidget</name>
|
||||||
|
@ -246,7 +244,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.ui" line="2000"/>
|
<location filename="../mainwindow.ui" line="2000"/>
|
||||||
<source>Your status</source>
|
<source>Your status</source>
|
||||||
<translation>Toxin on qTox</translation>
|
<translation>Toxing on qTox</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.ui" line="2566"/>
|
<location filename="../mainwindow.ui" line="2566"/>
|
||||||
|
|
64
ui/chatArea/innerStyle.css
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
div.name_me {
|
||||||
|
color: #646464;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.name {
|
||||||
|
color: #000000;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.message {
|
||||||
|
color: #000000;
|
||||||
|
padding-right: 3px;
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.date {
|
||||||
|
color: #000000;
|
||||||
|
padding-left: 3px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.quote {
|
||||||
|
background-color: #6bc260;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.green {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #6bc260;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.silver {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #000000;
|
||||||
|
background-color: #d1d1d1;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.red {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: rgb(200,78,78);
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.button {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-left: 0px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
|
@ -17,6 +17,11 @@ QPushButton:pressed
|
||||||
background-image: url(":/ui/fileButton/fileButtonPressed.png");
|
background-image: url(":/ui/fileButton/fileButtonPressed.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPushButton[enabled="false"]
|
||||||
|
{
|
||||||
|
background-image: url(":/ui/fileButton/fileButtonDisabled.png");
|
||||||
|
}
|
||||||
|
|
||||||
QPushButton:focus {
|
QPushButton:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
BIN
ui/fileButton/fileButtonDisabled.png
Normal file
After Width: | Height: | Size: 457 B |
BIN
ui/fileTransferInstance/acceptFileButton.png
Normal file
After Width: | Height: | Size: 449 B |
BIN
ui/fileTransferInstance/emptyLGreenFileButton.png
Normal file
After Width: | Height: | Size: 236 B |
BIN
ui/fileTransferInstance/emptyLRedFileButton.png
Normal file
After Width: | Height: | Size: 229 B |
BIN
ui/fileTransferInstance/emptyRGreenFileButton.png
Normal file
After Width: | Height: | Size: 216 B |
BIN
ui/fileTransferInstance/emptyRRedFileButton.png
Normal file
After Width: | Height: | Size: 211 B |
BIN
ui/fileTransferInstance/pauseFileButton.png
Normal file
After Width: | Height: | Size: 308 B |
BIN
ui/fileTransferInstance/resumeFileButton.png
Normal file
After Width: | Height: | Size: 244 B |
BIN
ui/fileTransferInstance/stopFileButton.png
Normal file
After Width: | Height: | Size: 516 B |
112
widget/chatareawidget.cpp
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "chatareawidget.h"
|
||||||
|
#include <QAbstractTextDocumentLayout>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
ChatAreaWidget::ChatAreaWidget(QWidget *parent) :
|
||||||
|
QTextEdit(parent)
|
||||||
|
{
|
||||||
|
setReadOnly(true);
|
||||||
|
viewport()->setCursor(Qt::ArrowCursor);
|
||||||
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
setUndoRedoEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatAreaWidget::~ChatAreaWidget()
|
||||||
|
{
|
||||||
|
for (ChatAction *it : messages)
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAreaWidget::mouseReleaseEvent(QMouseEvent * event)
|
||||||
|
{
|
||||||
|
QTextEdit::mouseReleaseEvent(event);
|
||||||
|
int pos = this->document()->documentLayout()->hitTest(event->pos(), Qt::ExactHit);
|
||||||
|
if (pos > 0)
|
||||||
|
{
|
||||||
|
QTextCursor cursor(document());
|
||||||
|
cursor.setPosition(pos);
|
||||||
|
if( ! cursor.atEnd() )
|
||||||
|
{
|
||||||
|
cursor.setPosition(pos+1);
|
||||||
|
|
||||||
|
QTextFormat format = cursor.charFormat();
|
||||||
|
if (format.isImageFormat())
|
||||||
|
{
|
||||||
|
QString imageName = format.toImageFormat().name();
|
||||||
|
if (QRegExp("^data:ftrans.*").exactMatch(imageName))
|
||||||
|
{
|
||||||
|
QString data = imageName.right(imageName.length() - 12);
|
||||||
|
int endpos = data.indexOf("/png;base64");
|
||||||
|
data = data.left(endpos);
|
||||||
|
int middlepos = data.indexOf(".");
|
||||||
|
QString widgetID = data.left(middlepos);
|
||||||
|
QString widgetBtn = data.right(data.length() - middlepos - 1);
|
||||||
|
emit onFileTranfertInterract(widgetID, widgetBtn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAreaWidget::getHtmledMessages()
|
||||||
|
{
|
||||||
|
QString res("<table width=100%>\n");
|
||||||
|
|
||||||
|
for (ChatAction *it : messages)
|
||||||
|
{
|
||||||
|
res += it->getHtml();
|
||||||
|
}
|
||||||
|
res += "</table>";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAreaWidget::insertMessage(ChatAction *msgAction)
|
||||||
|
{
|
||||||
|
if (msgAction == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
messages.append(msgAction);
|
||||||
|
//updateChatContent();
|
||||||
|
|
||||||
|
moveCursor(QTextCursor::End);
|
||||||
|
moveCursor(QTextCursor::PreviousCell);
|
||||||
|
insertHtml(msgAction->getHtml());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAreaWidget::updateChatContent()
|
||||||
|
{
|
||||||
|
QScrollBar* scroll = verticalScrollBar();
|
||||||
|
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
|
||||||
|
|
||||||
|
setUpdatesEnabled(false);
|
||||||
|
setHtml(getHtmledMessages());
|
||||||
|
setUpdatesEnabled(true);
|
||||||
|
if (lockSliderToBottom)
|
||||||
|
sliderPosition = scroll->maximum();
|
||||||
|
|
||||||
|
scroll->setValue(sliderPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAreaWidget::clearMessages()
|
||||||
|
{
|
||||||
|
for (ChatAction *it : messages)
|
||||||
|
delete it;
|
||||||
|
updateChatContent();
|
||||||
|
}
|
49
widget/chatareawidget.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CHATAREAWIDGET_H
|
||||||
|
#define CHATAREAWIDGET_H
|
||||||
|
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QList>
|
||||||
|
#include "widget/tool/chataction.h"
|
||||||
|
|
||||||
|
class ChatAreaWidget : public QTextEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ChatAreaWidget(QWidget *parent = 0);
|
||||||
|
virtual ~ChatAreaWidget();
|
||||||
|
void insertMessage(ChatAction *msgAction);
|
||||||
|
void clearMessages();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void onFileTranfertInterract(QString widgetName, QString buttonName);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mouseReleaseEvent(QMouseEvent * event);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateChatContent();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString getHtmledMessages();
|
||||||
|
QList<ChatAction*> messages;
|
||||||
|
bool lockSliderToBottom;
|
||||||
|
int sliderPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATAREAWIDGET_H
|
|
@ -1,320 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "filetransfertwidget.h"
|
|
||||||
#include "widget.h"
|
|
||||||
#include "core.h"
|
|
||||||
#include "math.h"
|
|
||||||
#include "style.h"
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QPixmap>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QMessageBox>
|
|
||||||
|
|
||||||
FileTransfertWidget::FileTransfertWidget(ToxFile File)
|
|
||||||
: lastUpdate{QDateTime::currentDateTime()}, lastBytesSent{0},
|
|
||||||
fileNum{File.fileNum}, friendId{File.friendId}, direction{File.direction}
|
|
||||||
{
|
|
||||||
pic=new QLabel(), filename=new QLabel(), size=new QLabel(), speed=new QLabel(), eta=new QLabel();
|
|
||||||
topright = new QPushButton(), bottomright = new QPushButton();
|
|
||||||
progress = new QProgressBar();
|
|
||||||
mainLayout = new QHBoxLayout(), textLayout = new QHBoxLayout();
|
|
||||||
infoLayout = new QVBoxLayout(), buttonLayout = new QVBoxLayout();
|
|
||||||
buttonWidget = new QWidget();
|
|
||||||
QFont prettysmall;
|
|
||||||
prettysmall.setPixelSize(10);
|
|
||||||
this->setObjectName("default");
|
|
||||||
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));
|
|
||||||
setPalette(greybg);
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
|
|
||||||
setMinimumSize(250,58);
|
|
||||||
setMaximumHeight(58);
|
|
||||||
setLayout(mainLayout);
|
|
||||||
mainLayout->setMargin(0);
|
|
||||||
|
|
||||||
pic->setMaximumHeight(40);
|
|
||||||
pic->setContentsMargins(5,0,0,0);
|
|
||||||
filename->setText(File.fileName);
|
|
||||||
filename->setFont(prettysmall);
|
|
||||||
size->setText(getHumanReadableSize(File.filesize));
|
|
||||||
size->setFont(prettysmall);
|
|
||||||
speed->setText("0B/s");
|
|
||||||
speed->setFont(prettysmall);
|
|
||||||
eta->setText("00:00");
|
|
||||||
eta->setFont(prettysmall);
|
|
||||||
progress->setValue(0);
|
|
||||||
progress->setMinimumHeight(11);
|
|
||||||
progress->setFont(prettysmall);
|
|
||||||
progress->setTextVisible(false);
|
|
||||||
QPalette whitebg;
|
|
||||||
whitebg.setColor(QPalette::Window, QColor(255,255,255));
|
|
||||||
buttonWidget->setPalette(whitebg);
|
|
||||||
buttonWidget->setAutoFillBackground(true);
|
|
||||||
buttonWidget->setLayout(buttonLayout);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
bottomright->setStyleSheet(pauseFileButtonStylesheet);
|
|
||||||
connect(topright, SIGNAL(clicked()), this, SLOT(cancelTransfer()));
|
|
||||||
connect(bottomright, SIGNAL(clicked()), this, SLOT(pauseResumeSend()));
|
|
||||||
|
|
||||||
QPixmap preview;
|
|
||||||
File.file->seek(0);
|
|
||||||
if (preview.loadFromData(File.file->readAll()))
|
|
||||||
{
|
|
||||||
preview = preview.scaledToHeight(40);
|
|
||||||
pic->setPixmap(preview);
|
|
||||||
}
|
|
||||||
File.file->seek(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bottomright->setStyleSheet(acceptFileButtonStylesheet);
|
|
||||||
connect(topright, SIGNAL(clicked()), this, SLOT(rejectRecvRequest()));
|
|
||||||
connect(bottomright, SIGNAL(clicked()), this, SLOT(acceptRecvRequest()));
|
|
||||||
}
|
|
||||||
|
|
||||||
QPalette toxgreen;
|
|
||||||
toxgreen.setColor(QPalette::Button, QColor(107,194,96)); // Tox Green
|
|
||||||
topright->setIconSize(QSize(10,10));
|
|
||||||
topright->setMinimumSize(25,28);
|
|
||||||
topright->setFlat(true);
|
|
||||||
topright->setAutoFillBackground(true);
|
|
||||||
topright->setPalette(toxgreen);
|
|
||||||
bottomright->setIconSize(QSize(10,10));
|
|
||||||
bottomright->setMinimumSize(25,28);
|
|
||||||
bottomright->setFlat(true);
|
|
||||||
bottomright->setAutoFillBackground(true);
|
|
||||||
bottomright->setPalette(toxgreen);
|
|
||||||
|
|
||||||
mainLayout->addStretch();
|
|
||||||
mainLayout->addWidget(pic);
|
|
||||||
mainLayout->addLayout(infoLayout,3);
|
|
||||||
mainLayout->addStretch();
|
|
||||||
mainLayout->addWidget(buttonWidget);
|
|
||||||
mainLayout->setMargin(0);
|
|
||||||
mainLayout->setSpacing(0);
|
|
||||||
|
|
||||||
infoLayout->addWidget(filename);
|
|
||||||
infoLayout->addLayout(textLayout);
|
|
||||||
infoLayout->addWidget(progress);
|
|
||||||
infoLayout->setMargin(4);
|
|
||||||
infoLayout->setSpacing(4);
|
|
||||||
|
|
||||||
textLayout->addWidget(size);
|
|
||||||
textLayout->addStretch(0);
|
|
||||||
textLayout->addWidget(speed);
|
|
||||||
textLayout->addStretch(0);
|
|
||||||
textLayout->addWidget(eta);
|
|
||||||
textLayout->setMargin(2);
|
|
||||||
textLayout->setSpacing(5);
|
|
||||||
|
|
||||||
buttonLayout->addWidget(topright);
|
|
||||||
buttonLayout->addSpacing(2);
|
|
||||||
buttonLayout->addWidget(bottomright);
|
|
||||||
buttonLayout->setContentsMargins(2,0,0,0);
|
|
||||||
buttonLayout->setSpacing(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
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),'f',2).append(suffix[exp]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction)
|
|
||||||
{
|
|
||||||
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
|
|
||||||
return;
|
|
||||||
QDateTime newtime = QDateTime::currentDateTime();
|
|
||||||
int timediff = lastUpdate.secsTo(newtime);
|
|
||||||
if (timediff <= 0)
|
|
||||||
return;
|
|
||||||
qint64 diff = BytesSent - lastBytesSent;
|
|
||||||
if (diff < 0)
|
|
||||||
{
|
|
||||||
qWarning() << "FileTransfertWidget::onFileTransferInfo: Negative transfer speed !";
|
|
||||||
diff = 0;
|
|
||||||
}
|
|
||||||
long rawspeed = diff / timediff;
|
|
||||||
speed->setText(getHumanReadableSize(rawspeed)+"/s");
|
|
||||||
size->setText(getHumanReadableSize(Filesize));
|
|
||||||
if (!rawspeed)
|
|
||||||
return;
|
|
||||||
int etaSecs = (Filesize - BytesSent) / rawspeed;
|
|
||||||
QTime etaTime(0,0);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
lastUpdate = newtime;
|
|
||||||
lastBytesSent = BytesSent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction)
|
|
||||||
{
|
|
||||||
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
|
|
||||||
return;
|
|
||||||
buttonLayout->setContentsMargins(0,0,0,0);
|
|
||||||
disconnect(topright);
|
|
||||||
disconnect(Widget::getInstance()->getCore(),0,this,0);
|
|
||||||
progress->hide();
|
|
||||||
speed->hide();
|
|
||||||
eta->hide();
|
|
||||||
topright->hide();
|
|
||||||
bottomright->hide();
|
|
||||||
QPalette whiteText;
|
|
||||||
whiteText.setColor(QPalette::WindowText, Qt::white);
|
|
||||||
filename->setPalette(whiteText);
|
|
||||||
size->setPalette(whiteText);
|
|
||||||
this->setObjectName("error");
|
|
||||||
this->style()->polish(this);
|
|
||||||
|
|
||||||
//Toggle window visibility to fix draw order bug
|
|
||||||
this->hide();
|
|
||||||
this->show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::onFileTransferFinished(ToxFile File)
|
|
||||||
{
|
|
||||||
if (File.fileNum != fileNum || File.friendId != friendId || File.direction != direction)
|
|
||||||
return;
|
|
||||||
topright->disconnect();
|
|
||||||
disconnect(Widget::getInstance()->getCore(),0,this,0);
|
|
||||||
progress->hide();
|
|
||||||
speed->hide();
|
|
||||||
eta->hide();
|
|
||||||
topright->hide();
|
|
||||||
bottomright->hide();
|
|
||||||
buttonLayout->setContentsMargins(0,0,0,0);
|
|
||||||
QPalette whiteText;
|
|
||||||
whiteText.setColor(QPalette::WindowText, Qt::white);
|
|
||||||
filename->setPalette(whiteText);
|
|
||||||
size->setPalette(whiteText);
|
|
||||||
this->setObjectName("success");
|
|
||||||
this->style()->polish(this);
|
|
||||||
|
|
||||||
//Toggle window visibility to fix draw order bug
|
|
||||||
this->hide();
|
|
||||||
this->show();
|
|
||||||
|
|
||||||
if (File.direction == ToxFile::RECEIVING)
|
|
||||||
{
|
|
||||||
QPixmap preview;
|
|
||||||
QFile previewFile(File.filePath);
|
|
||||||
if (previewFile.open(QIODevice::ReadOnly) && previewFile.size() <= 1024*1024*25) // Don't preview big (>25MiB) images
|
|
||||||
{
|
|
||||||
if (preview.loadFromData(previewFile.readAll()))
|
|
||||||
{
|
|
||||||
preview = preview.scaledToHeight(40);
|
|
||||||
pic->setPixmap(preview);
|
|
||||||
}
|
|
||||||
previewFile.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::cancelTransfer()
|
|
||||||
{
|
|
||||||
Widget::getInstance()->getCore()->cancelFileSend(friendId, fileNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::rejectRecvRequest()
|
|
||||||
{
|
|
||||||
Widget::getInstance()->getCore()->rejectFileRecvRequest(friendId, fileNum);
|
|
||||||
onFileTransferCancelled(friendId, fileNum, direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// for whatever the fuck reason, QFileInfo::isWritable() always fails for files that don't exist
|
|
||||||
// which makes it useless for our case
|
|
||||||
// since QDir doesn't have an isWritable(), the only option I can think of is to make/delete the file
|
|
||||||
// surely this is a common problem that has a qt-implemented solution?
|
|
||||||
bool isWritable(QString& path)
|
|
||||||
{
|
|
||||||
QFile file(path);
|
|
||||||
bool exists = file.exists();
|
|
||||||
bool out = file.open(QIODevice::WriteOnly);
|
|
||||||
file.close();
|
|
||||||
if (!exists)
|
|
||||||
file.remove();
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::acceptRecvRequest()
|
|
||||||
{
|
|
||||||
QString path;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
path = QFileDialog::getSaveFileName(this, tr("Save a file","Title of the file saving dialog"), QDir::current().filePath(filename->text()));
|
|
||||||
if (path.isEmpty())
|
|
||||||
return;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//bool savable = QFileInfo(path).isWritable();
|
|
||||||
//qDebug() << path << " is writable: " << savable;
|
|
||||||
//qDebug() << "/home/bill/bliss.pdf writable: " << QFileInfo("/home/bill/bliss.pdf").isWritable();
|
|
||||||
if (isWritable(path))
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
savePath = path;
|
|
||||||
|
|
||||||
bottomright->setStyleSheet(pauseFileButtonStylesheet);
|
|
||||||
bottomright->disconnect();
|
|
||||||
connect(bottomright, SIGNAL(clicked()), this, SLOT(pauseResumeRecv()));
|
|
||||||
Widget::getInstance()->getCore()->acceptFileRecvRequest(friendId, fileNum, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::pauseResumeRecv()
|
|
||||||
{
|
|
||||||
Widget::getInstance()->getCore()->pauseResumeFileRecv(friendId, fileNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::pauseResumeSend()
|
|
||||||
{
|
|
||||||
Widget::getInstance()->getCore()->pauseResumeFileSend(friendId, fileNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
QStyleOption opt;
|
|
||||||
opt.init(this);
|
|
||||||
QPainter p(this);
|
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
||||||
}
|
|
|
@ -16,140 +16,24 @@
|
||||||
|
|
||||||
#include "chatform.h"
|
#include "chatform.h"
|
||||||
#include "friend.h"
|
#include "friend.h"
|
||||||
#include "smileypack.h"
|
|
||||||
#include "widget/friendwidget.h"
|
#include "widget/friendwidget.h"
|
||||||
|
#include "filetransferinstance.h"
|
||||||
#include "widget/widget.h"
|
#include "widget/widget.h"
|
||||||
#include "widget/filetransfertwidget.h"
|
|
||||||
#include "widget/emoticonswidget.h"
|
|
||||||
#include "style.h"
|
|
||||||
#include <QFont>
|
|
||||||
#include <QTime>
|
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMenu>
|
|
||||||
#include <QWidgetAction>
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
ChatForm::ChatForm(Friend* chatFriend)
|
ChatForm::ChatForm(Friend* chatFriend)
|
||||||
: f(chatFriend), curRow{0}, lockSliderToBottom{true}
|
: f(chatFriend)
|
||||||
{
|
{
|
||||||
main = new QWidget(), head = new QWidget(), chatAreaWidget = new QWidget();
|
nameLabel->setText(f->getName());
|
||||||
name = new QLabel(), avatar = new QLabel(), statusMessage = new QLabel();
|
avatarLabel->setPixmap(QPixmap(":/img/contact_dark.png"));
|
||||||
headLayout = new QHBoxLayout(), mainFootLayout = new QHBoxLayout();
|
|
||||||
headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout(),
|
statusMessageLabel = new CroppingLabel();
|
||||||
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(),
|
|
||||||
volButton = new QPushButton(), micButton = new QPushButton();
|
|
||||||
chatArea = new QScrollArea();
|
|
||||||
netcam = new NetCamView();
|
netcam = new NetCamView();
|
||||||
audioInputFlag = false;
|
|
||||||
|
|
||||||
QFont bold;
|
|
||||||
bold.setBold(true);
|
|
||||||
name->setText(chatFriend->widget->name.text());
|
|
||||||
name->setFont(bold);
|
|
||||||
statusMessage->setText(chatFriend->widget->statusMessage.text());
|
|
||||||
|
|
||||||
// No real avatar support in toxcore, better draw a pretty picture
|
|
||||||
//avatar->setPixmap(*chatFriend->widget->avatar.pixmap());
|
|
||||||
avatar->setPixmap(QPixmap(":/img/contact_dark.png"));
|
|
||||||
|
|
||||||
chatAreaWidget->setLayout(mainChatLayout);
|
|
||||||
chatAreaWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css"));
|
|
||||||
|
|
||||||
chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
||||||
chatArea->setWidgetResizable(true);
|
|
||||||
chatArea->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
chatArea->setFrameStyle(QFrame::NoFrame);
|
|
||||||
|
|
||||||
mainChatLayout->setColumnStretch(1,1);
|
|
||||||
mainChatLayout->setSpacing(5);
|
|
||||||
|
|
||||||
footButtonsSmall->setSpacing(2);
|
|
||||||
|
|
||||||
msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css"));
|
|
||||||
msgEdit->setFixedHeight(50);
|
|
||||||
msgEdit->setFrameStyle(QFrame::NoFrame);
|
|
||||||
|
|
||||||
sendButton->setStyleSheet(Style::get(":/ui/sendButton/sendButton.css"));
|
|
||||||
fileButton->setStyleSheet(Style::get(":/ui/fileButton/fileButton.css"));
|
|
||||||
emoteButton->setStyleSheet(Style::get(":/ui/emoteButton/emoteButton.css"));
|
|
||||||
|
|
||||||
callButton->setObjectName("green");
|
|
||||||
callButton->setStyleSheet(Style::get(":/ui/callButton/callButton.css"));
|
|
||||||
|
|
||||||
videoButton->setObjectName("green");
|
|
||||||
videoButton->setStyleSheet(Style::get(":/ui/videoButton/videoButton.css"));
|
|
||||||
|
|
||||||
QString volButtonStylesheet = "";
|
|
||||||
try
|
|
||||||
{
|
|
||||||
QFile f(":/ui/volButton/volButton.css");
|
|
||||||
f.open(QFile::ReadOnly | QFile::Text);
|
|
||||||
QTextStream volButtonStylesheetStream(&f);
|
|
||||||
volButtonStylesheet = volButtonStylesheetStream.readAll();
|
|
||||||
}
|
|
||||||
catch (int e) {}
|
|
||||||
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);
|
|
||||||
mainLayout->addLayout(mainFootLayout);
|
|
||||||
mainLayout->setMargin(0);
|
|
||||||
|
|
||||||
footButtonsSmall->addWidget(emoteButton);
|
|
||||||
footButtonsSmall->addWidget(fileButton);
|
|
||||||
|
|
||||||
mainFootLayout->addWidget(msgEdit);
|
|
||||||
mainFootLayout->addLayout(footButtonsSmall);
|
|
||||||
mainFootLayout->addSpacing(5);
|
|
||||||
mainFootLayout->addWidget(sendButton);
|
|
||||||
mainFootLayout->setSpacing(0);
|
|
||||||
|
|
||||||
head->setLayout(headLayout);
|
|
||||||
headLayout->addWidget(avatar);
|
|
||||||
headLayout->addLayout(headTextLayout);
|
|
||||||
headLayout->addStretch();
|
|
||||||
headLayout->addLayout(volMicLayout);
|
|
||||||
headLayout->addWidget(callButton);
|
|
||||||
headLayout->addWidget(videoButton);
|
|
||||||
|
|
||||||
volMicLayout->addWidget(micButton);
|
|
||||||
volMicLayout->addWidget(volButton);
|
|
||||||
|
|
||||||
|
headTextLayout->addWidget(statusMessageLabel);
|
||||||
headTextLayout->addStretch();
|
headTextLayout->addStretch();
|
||||||
headTextLayout->addWidget(name);
|
|
||||||
headTextLayout->addWidget(statusMessage);
|
|
||||||
headTextLayout->addStretch();
|
|
||||||
|
|
||||||
chatArea->setWidget(chatAreaWidget);
|
|
||||||
|
|
||||||
//Fix for incorrect layouts on OS X as per
|
|
||||||
//https://bugreports.qt-project.org/browse/QTBUG-14591
|
|
||||||
sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
|
||||||
fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
|
||||||
emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
|
||||||
// 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::fileSendStarted, this, &ChatForm::startFileSend);
|
||||||
connect(Widget::getInstance()->getCore(), &Core::videoFrameReceived, netcam, &NetCamView::updateDisplay);
|
connect(Widget::getInstance()->getCore(), &Core::videoFrameReceived, netcam, &NetCamView::updateDisplay);
|
||||||
|
@ -158,37 +42,19 @@ ChatForm::ChatForm(Friend* chatFriend)
|
||||||
connect(callButton, &QPushButton::clicked, this, &ChatForm::onCallTriggered);
|
connect(callButton, &QPushButton::clicked, this, &ChatForm::onCallTriggered);
|
||||||
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered);
|
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered);
|
||||||
connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered);
|
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()));
|
connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle()));
|
||||||
|
connect(chatWidget, SIGNAL(onFileTranfertInterract(QString,QString)), this, SLOT(onFileTansBtnClicked(QString,QString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatForm::~ChatForm()
|
ChatForm::~ChatForm()
|
||||||
{
|
{
|
||||||
delete main;
|
|
||||||
delete head;
|
|
||||||
delete netcam;
|
delete netcam;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::show(Ui::MainWindow &ui)
|
|
||||||
{
|
|
||||||
ui.mainContent->layout()->addWidget(main);
|
|
||||||
ui.mainHead->layout()->addWidget(head);
|
|
||||||
main->show();
|
|
||||||
head->show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::setName(QString newName)
|
|
||||||
{
|
|
||||||
name->setText(newName);
|
|
||||||
name->setToolTip(newName); // for overlength names
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::setStatusMessage(QString newMessage)
|
void ChatForm::setStatusMessage(QString newMessage)
|
||||||
{
|
{
|
||||||
statusMessage->setText(newMessage);
|
statusMessageLabel->setText(newMessage);
|
||||||
statusMessage->setToolTip(newMessage); // for overlength messsages
|
statusMessageLabel->setToolTip(newMessage); // for overlength messsages
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onSendTriggered()
|
void ChatForm::onSendTriggered()
|
||||||
|
@ -202,81 +68,6 @@ void ChatForm::onSendTriggered()
|
||||||
emit sendMessage(f->friendId, msg);
|
emit sendMessage(f->friendId, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::addFriendMessage(QString message)
|
|
||||||
{
|
|
||||||
QLabel *msgAuthor = new QLabel(name->text());
|
|
||||||
QLabel *msgText = new QLabel(message);
|
|
||||||
QLabel *msgDate = new QLabel(QTime::currentTime().toString("hh:mm"));
|
|
||||||
|
|
||||||
addMessage(msgAuthor, msgText, msgDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::addMessage(QString author, QString message, QString date)
|
|
||||||
{
|
|
||||||
addMessage(new QLabel(author), new QLabel(message), new QLabel(date));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::addMessage(QLabel* author, QLabel* message, QLabel* date)
|
|
||||||
{
|
|
||||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
|
||||||
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
|
|
||||||
author->setAlignment(Qt::AlignTop | Qt::AlignRight);
|
|
||||||
date->setAlignment(Qt::AlignTop);
|
|
||||||
message->setWordWrap(true);
|
|
||||||
message->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
author->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
date->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
if (author->text() == Widget::getInstance()->getUsername())
|
|
||||||
{
|
|
||||||
QPalette pal;
|
|
||||||
pal.setColor(QPalette::WindowText, QColor(100,100,100));
|
|
||||||
author->setPalette(pal);
|
|
||||||
message->setPalette(pal);
|
|
||||||
}
|
|
||||||
if (previousName.isEmpty() || previousName != author->text())
|
|
||||||
{
|
|
||||||
if (curRow)
|
|
||||||
{
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3);
|
|
||||||
}
|
|
||||||
previousName = author->text();
|
|
||||||
curRow++;
|
|
||||||
}
|
|
||||||
else if (curRow)// onSaveLogClicked expects 0 or 3 QLabel per line
|
|
||||||
author->setText("");
|
|
||||||
|
|
||||||
QColor greentext(61,204,61);
|
|
||||||
QString fontTemplate = "<font color='%1'>%2</font>";
|
|
||||||
|
|
||||||
QString finalMessage;
|
|
||||||
QStringList messageLines = message->text().split("\n");
|
|
||||||
for (QString& s : messageLines)
|
|
||||||
{
|
|
||||||
if (QRegExp("^[ ]*>.*").exactMatch(s))
|
|
||||||
finalMessage += fontTemplate.arg(greentext.name(), s.replace(" ", " "));
|
|
||||||
else
|
|
||||||
finalMessage += s.replace(" ", " ");
|
|
||||||
finalMessage += "<br>";
|
|
||||||
}
|
|
||||||
message->setText(finalMessage.left(finalMessage.length()-4));
|
|
||||||
message->setText(SmileyPack::getInstance().smileyfied(message->text()));
|
|
||||||
message->setTextFormat(Qt::RichText);
|
|
||||||
|
|
||||||
mainChatLayout->addWidget(author, curRow, 0);
|
|
||||||
mainChatLayout->addWidget(message, curRow, 1);
|
|
||||||
mainChatLayout->addWidget(date, curRow, 3);
|
|
||||||
mainChatLayout->setRowStretch(curRow+1, 1);
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
curRow++;
|
|
||||||
author->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
message->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
date->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
connect(author, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
|
||||||
connect(message, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
|
||||||
connect(date, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::onAttachClicked()
|
void ChatForm::onAttachClicked()
|
||||||
{
|
{
|
||||||
QString path = QFileDialog::getOpenFileName(0,tr("Send a file"));
|
QString path = QFileDialog::getOpenFileName(0,tr("Send a file"));
|
||||||
|
@ -299,48 +90,25 @@ void ChatForm::onAttachClicked()
|
||||||
emit sendFile(f->friendId, fi.fileName(), path, filesize);
|
emit sendFile(f->friendId, fi.fileName(), path, filesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onSliderRangeChanged()
|
|
||||||
{
|
|
||||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
|
||||||
if (lockSliderToBottom)
|
|
||||||
scroll->setValue(scroll->maximum());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::startFileSend(ToxFile file)
|
void ChatForm::startFileSend(ToxFile file)
|
||||||
{
|
{
|
||||||
if (file.friendId != f->friendId)
|
if (file.friendId != f->friendId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QLabel *author = new QLabel(Widget::getInstance()->getUsername());
|
FileTransferInstance* fileTrans = new FileTransferInstance(file);
|
||||||
QLabel *date = new QLabel(QTime::currentTime().toString("hh:mm"));
|
ftransWidgets.insert(fileTrans->getId(), fileTrans);
|
||||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
|
||||||
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
|
|
||||||
author->setAlignment(Qt::AlignTop | Qt::AlignRight);
|
|
||||||
date->setAlignment(Qt::AlignTop);
|
|
||||||
QPalette pal;
|
|
||||||
pal.setColor(QPalette::WindowText, Qt::gray);
|
|
||||||
author->setPalette(pal);
|
|
||||||
if (previousName.isEmpty() || previousName != author->text())
|
|
||||||
{
|
|
||||||
if (curRow)
|
|
||||||
{
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3);
|
|
||||||
curRow++;
|
|
||||||
}
|
|
||||||
mainChatLayout->addWidget(author, curRow, 0);
|
|
||||||
}
|
|
||||||
FileTransfertWidget* fileTrans = new FileTransfertWidget(file);
|
|
||||||
previousName = author->text();
|
|
||||||
mainChatLayout->addWidget(fileTrans, curRow, 1);
|
|
||||||
mainChatLayout->addWidget(date, curRow, 3);
|
|
||||||
mainChatLayout->setRowStretch(curRow+1, 1);
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
curRow++;
|
|
||||||
|
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransfertWidget::onFileTransferInfo);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransferInstance::onFileTransferInfo);
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransfertWidget::onFileTransferCancelled);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransferInstance::onFileTransferCancelled);
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransferInstance::onFileTransferFinished);
|
||||||
|
connect(fileTrans, SIGNAL(stateUpdated()), chatWidget, SLOT(updateChatContent()));
|
||||||
|
|
||||||
|
QString name = Widget::getInstance()->getUsername();
|
||||||
|
if (name == previousName)
|
||||||
|
name = "";
|
||||||
|
previousName = Widget::getInstance()->getUsername();
|
||||||
|
|
||||||
|
chatWidget->insertMessage(new FileTransferAction(fileTrans, name, QTime::currentTime().toString("hh:mm"), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onFileRecvRequest(ToxFile file)
|
void ChatForm::onFileRecvRequest(ToxFile file)
|
||||||
|
@ -348,33 +116,13 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
||||||
if (file.friendId != f->friendId)
|
if (file.friendId != f->friendId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QLabel *author = new QLabel(f->getName());
|
FileTransferInstance* fileTrans = new FileTransferInstance(file);
|
||||||
QLabel *date = new QLabel(QTime::currentTime().toString("hh:mm"));
|
ftransWidgets.insert(fileTrans->getId(), fileTrans);
|
||||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
|
||||||
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
|
|
||||||
author->setAlignment(Qt::AlignTop | Qt::AlignRight);
|
|
||||||
date->setAlignment(Qt::AlignTop);
|
|
||||||
if (previousName.isEmpty() || previousName != author->text())
|
|
||||||
{
|
|
||||||
if (curRow)
|
|
||||||
{
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3);
|
|
||||||
curRow++;
|
|
||||||
}
|
|
||||||
mainChatLayout->addWidget(author, curRow, 0);
|
|
||||||
}
|
|
||||||
FileTransfertWidget* fileTrans = new FileTransfertWidget(file);
|
|
||||||
previousName = author->text();
|
|
||||||
mainChatLayout->addWidget(fileTrans, curRow, 1);
|
|
||||||
mainChatLayout->addWidget(date, curRow, 3);
|
|
||||||
mainChatLayout->setRowStretch(curRow+1, 1);
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
curRow++;
|
|
||||||
|
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransfertWidget::onFileTransferInfo);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransferInstance::onFileTransferInfo);
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransfertWidget::onFileTransferCancelled);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransferInstance::onFileTransferCancelled);
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransferInstance::onFileTransferFinished);
|
||||||
|
connect(fileTrans, SIGNAL(stateUpdated()), chatWidget, SLOT(updateChatContent()));
|
||||||
|
|
||||||
Widget* w = Widget::getInstance();
|
Widget* w = Widget::getInstance();
|
||||||
if (!w->isFriendWidgetCurActiveWidget(f)|| w->getIsWindowMinimized() || !w->isActiveWindow())
|
if (!w->isFriendWidgetCurActiveWidget(f)|| w->getIsWindowMinimized() || !w->isActiveWindow())
|
||||||
|
@ -383,6 +131,13 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
||||||
f->hasNewEvents=true;
|
f->hasNewEvents=true;
|
||||||
f->widget->updateStatusLight();
|
f->widget->updateStatusLight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString name = f->getName();
|
||||||
|
if (name == previousName)
|
||||||
|
name = "";
|
||||||
|
previousName = f->getName();
|
||||||
|
|
||||||
|
chatWidget->insertMessage(new FileTransferAction(fileTrans, name, QTime::currentTime().toString("hh:mm"), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
|
void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
|
||||||
|
@ -661,74 +416,6 @@ void ChatForm::onCancelCallTriggered()
|
||||||
emit cancelCall(callId, f->friendId);
|
emit cancelCall(callId, f->friendId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onChatContextMenuRequested(QPoint pos)
|
|
||||||
{
|
|
||||||
QWidget* sender = (QWidget*)QObject::sender();
|
|
||||||
pos = sender->mapToGlobal(pos);
|
|
||||||
QMenu menu;
|
|
||||||
menu.addAction(tr("Save chat log"), this, SLOT(onSaveLogClicked()));
|
|
||||||
menu.exec(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::onSaveLogClicked()
|
|
||||||
{
|
|
||||||
QString path = QFileDialog::getSaveFileName(0,tr("Save chat log"));
|
|
||||||
if (path.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QFile file(path);
|
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString log;
|
|
||||||
QList<QLabel*> labels = chatAreaWidget->findChildren<QLabel*>();
|
|
||||||
int i=0;
|
|
||||||
for (QLabel* label : labels)
|
|
||||||
{
|
|
||||||
log += label->text();
|
|
||||||
if (i==2)
|
|
||||||
{
|
|
||||||
i=0;
|
|
||||||
log += '\n';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log += '\t';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file.write(log.toUtf8());
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::onEmoteButtonClicked()
|
|
||||||
{
|
|
||||||
// don't show the smiley selection widget if there are no smileys available
|
|
||||||
if (SmileyPack::getInstance().getEmoticons().empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
EmoticonsWidget widget;
|
|
||||||
connect(&widget, &EmoticonsWidget::insertEmoticon, this, &ChatForm::onEmoteInsertRequested);
|
|
||||||
|
|
||||||
QWidget* sender = qobject_cast<QWidget*>(QObject::sender());
|
|
||||||
if (sender)
|
|
||||||
{
|
|
||||||
QPoint pos = -QPoint(widget.sizeHint().width() / 2, widget.sizeHint().height()) - QPoint(0, 10);
|
|
||||||
widget.exec(sender->mapToGlobal(pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::onEmoteInsertRequested(QString str)
|
|
||||||
{
|
|
||||||
// insert the emoticon
|
|
||||||
QWidget* sender = qobject_cast<QWidget*>(QObject::sender());
|
|
||||||
if (sender)
|
|
||||||
msgEdit->insertPlainText(str);
|
|
||||||
|
|
||||||
msgEdit->setFocus(); // refocus so that we can continue typing
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatForm::onMicMuteToggle()
|
void ChatForm::onMicMuteToggle()
|
||||||
{
|
{
|
||||||
if (audioInputFlag == true)
|
if (audioInputFlag == true)
|
||||||
|
@ -746,3 +433,14 @@ void ChatForm::onMicMuteToggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatForm::onFileTansBtnClicked(QString widgetName, QString buttonName)
|
||||||
|
{
|
||||||
|
uint id = widgetName.toUInt();
|
||||||
|
|
||||||
|
auto it = ftransWidgets.find(id);
|
||||||
|
if (it != ftransWidgets.end())
|
||||||
|
it.value()->pressFromHtml(buttonName);
|
||||||
|
else
|
||||||
|
qDebug() << "no filetransferwidget: " << id;
|
||||||
|
}
|
||||||
|
|
|
@ -17,41 +17,22 @@
|
||||||
#ifndef CHATFORM_H
|
#ifndef CHATFORM_H
|
||||||
#define CHATFORM_H
|
#define CHATFORM_H
|
||||||
|
|
||||||
#include <QLabel>
|
#include "genericchatform.h"
|
||||||
#include <QWidget>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QTextEdit>
|
|
||||||
#include <QScrollArea>
|
|
||||||
#include <QTime>
|
|
||||||
#include <QPoint>
|
|
||||||
|
|
||||||
#include "widget/tool/chattextedit.h"
|
|
||||||
#include "ui_mainwindow.h"
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "widget/netcamview.h"
|
#include "widget/netcamview.h"
|
||||||
|
|
||||||
// Spacing in px inserted when the author of the last message changes
|
|
||||||
#define AUTHOR_CHANGE_SPACING 5
|
|
||||||
|
|
||||||
struct Friend;
|
struct Friend;
|
||||||
|
class FileTransferInstance;
|
||||||
|
|
||||||
class ChatForm : public QObject
|
class ChatForm : public GenericChatForm
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ChatForm(Friend* chatFriend);
|
ChatForm(Friend* chatFriend);
|
||||||
~ChatForm();
|
~ChatForm();
|
||||||
void show(Ui::MainWindow &ui);
|
|
||||||
void setName(QString newName);
|
|
||||||
void setStatusMessage(QString newMessage);
|
void setStatusMessage(QString newMessage);
|
||||||
void addFriendMessage(QString message);
|
|
||||||
void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm"));
|
|
||||||
void addMessage(QLabel* author, QLabel* message, QLabel* date);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sendMessage(int, QString);
|
|
||||||
void sendFile(int32_t friendId, QString, QString, long long);
|
void sendFile(int32_t friendId, QString, QString, long long);
|
||||||
void startCall(int friendId);
|
void startCall(int friendId);
|
||||||
void startVideoCall(int friendId, bool video);
|
void startVideoCall(int friendId, bool video);
|
||||||
|
@ -78,32 +59,21 @@ public slots:
|
||||||
private slots:
|
private slots:
|
||||||
void onSendTriggered();
|
void onSendTriggered();
|
||||||
void onAttachClicked();
|
void onAttachClicked();
|
||||||
void onSliderRangeChanged();
|
|
||||||
void onCallTriggered();
|
void onCallTriggered();
|
||||||
void onVideoCallTriggered();
|
void onVideoCallTriggered();
|
||||||
void onAnswerCallTriggered();
|
void onAnswerCallTriggered();
|
||||||
void onHangupCallTriggered();
|
void onHangupCallTriggered();
|
||||||
void onCancelCallTriggered();
|
void onCancelCallTriggered();
|
||||||
void onChatContextMenuRequested(QPoint pos);
|
void onFileTansBtnClicked(QString widgetName, QString buttonName);
|
||||||
void onSaveLogClicked();
|
|
||||||
void onEmoteButtonClicked();
|
|
||||||
void onEmoteInsertRequested(QString str);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Friend* f;
|
Friend* f;
|
||||||
QHBoxLayout *headLayout, *mainFootLayout;
|
CroppingLabel *statusMessageLabel;
|
||||||
QVBoxLayout *headTextLayout, *mainLayout, *footButtonsSmall, *volMicLayout;
|
|
||||||
QGridLayout *mainChatLayout;
|
|
||||||
QLabel *avatar, *name, *statusMessage;
|
|
||||||
ChatTextEdit *msgEdit;
|
|
||||||
QPushButton *sendButton, *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton;
|
|
||||||
QScrollArea *chatArea;
|
|
||||||
QWidget *main, *head, *chatAreaWidget;
|
|
||||||
QString previousName;
|
|
||||||
NetCamView* netcam;
|
NetCamView* netcam;
|
||||||
int curRow;
|
bool audioInputFlag;
|
||||||
bool lockSliderToBottom, audioInputFlag;
|
|
||||||
int callId;
|
int callId;
|
||||||
|
|
||||||
|
QHash<uint, FileTransferInstance*> ftransWidgets;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHATFORM_H
|
#endif // CHATFORM_H
|
||||||
|
|
202
widget/form/genericchatform.cpp
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "genericchatform.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include "smileypack.h"
|
||||||
|
#include "widget/emoticonswidget.h"
|
||||||
|
#include "style.h"
|
||||||
|
#include "widget/widget.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
GenericChatForm::GenericChatForm(QObject *parent) :
|
||||||
|
QObject(parent)
|
||||||
|
{
|
||||||
|
curRow = 0;
|
||||||
|
|
||||||
|
mainWidget = new QWidget(); headWidget = new QWidget();
|
||||||
|
|
||||||
|
nameLabel = new CroppingLabel();
|
||||||
|
avatarLabel = new QLabel();
|
||||||
|
QHBoxLayout *headLayout = new QHBoxLayout(), *mainFootLayout = new QHBoxLayout();
|
||||||
|
headTextLayout = new QVBoxLayout();
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||||
|
QVBoxLayout *footButtonsSmall = new QVBoxLayout(), *volMicLayout = new QVBoxLayout();
|
||||||
|
|
||||||
|
chatWidget = new ChatAreaWidget();
|
||||||
|
chatWidget->document()->setDefaultStyleSheet(Style::get(":ui/chatArea/innerStyle.css"));
|
||||||
|
chatWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css"));
|
||||||
|
|
||||||
|
msgEdit = new ChatTextEdit();
|
||||||
|
|
||||||
|
sendButton = new QPushButton();
|
||||||
|
emoteButton = new QPushButton();
|
||||||
|
|
||||||
|
fileButton = new QPushButton();
|
||||||
|
callButton = new QPushButton();
|
||||||
|
videoButton = new QPushButton();
|
||||||
|
volButton = new QPushButton();
|
||||||
|
micButton = new QPushButton();
|
||||||
|
|
||||||
|
QFont bold;
|
||||||
|
bold.setBold(true);
|
||||||
|
nameLabel->setFont(bold);
|
||||||
|
|
||||||
|
footButtonsSmall->setSpacing(2);
|
||||||
|
|
||||||
|
msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css"));
|
||||||
|
msgEdit->setFixedHeight(50);
|
||||||
|
msgEdit->setFrameStyle(QFrame::NoFrame);
|
||||||
|
|
||||||
|
sendButton->setStyleSheet(Style::get(":/ui/sendButton/sendButton.css"));
|
||||||
|
fileButton->setStyleSheet(Style::get(":/ui/fileButton/fileButton.css"));
|
||||||
|
emoteButton->setStyleSheet(Style::get(":/ui/emoteButton/emoteButton.css"));
|
||||||
|
|
||||||
|
callButton->setObjectName("green");
|
||||||
|
callButton->setStyleSheet(Style::get(":/ui/callButton/callButton.css"));
|
||||||
|
|
||||||
|
videoButton->setObjectName("green");
|
||||||
|
videoButton->setStyleSheet(Style::get(":/ui/videoButton/videoButton.css"));
|
||||||
|
|
||||||
|
QString volButtonStylesheet = Style::get(":/ui/volButton/volButton.css");
|
||||||
|
volButton->setObjectName("green");
|
||||||
|
volButton->setStyleSheet(volButtonStylesheet);
|
||||||
|
|
||||||
|
QString micButtonStylesheet = Style::get(":/ui/micButton/micButton.css");
|
||||||
|
micButton->setObjectName("green");
|
||||||
|
micButton->setStyleSheet(micButtonStylesheet);
|
||||||
|
|
||||||
|
mainWidget->setLayout(mainLayout);
|
||||||
|
mainLayout->addWidget(chatWidget);
|
||||||
|
mainLayout->addLayout(mainFootLayout);
|
||||||
|
mainLayout->setMargin(0);
|
||||||
|
|
||||||
|
footButtonsSmall->addWidget(emoteButton);
|
||||||
|
footButtonsSmall->addWidget(fileButton);
|
||||||
|
|
||||||
|
mainFootLayout->addWidget(msgEdit);
|
||||||
|
mainFootLayout->addLayout(footButtonsSmall);
|
||||||
|
mainFootLayout->addSpacing(5);
|
||||||
|
mainFootLayout->addWidget(sendButton);
|
||||||
|
mainFootLayout->setSpacing(0);
|
||||||
|
|
||||||
|
headWidget->setLayout(headLayout);
|
||||||
|
headLayout->addWidget(avatarLabel);
|
||||||
|
headLayout->addLayout(headTextLayout);
|
||||||
|
headLayout->addLayout(volMicLayout);
|
||||||
|
headLayout->addWidget(callButton);
|
||||||
|
headLayout->addWidget(videoButton);
|
||||||
|
|
||||||
|
volMicLayout->addWidget(micButton);
|
||||||
|
volMicLayout->addWidget(volButton);
|
||||||
|
|
||||||
|
headTextLayout->addStretch();
|
||||||
|
headTextLayout->addWidget(nameLabel);
|
||||||
|
|
||||||
|
//Fix for incorrect layouts on OS X as per
|
||||||
|
//https://bugreports.qt-project.org/browse/QTBUG-14591
|
||||||
|
sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||||
|
fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||||
|
emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||||
|
|
||||||
|
connect(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked()));
|
||||||
|
connect(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericChatForm::setName(const QString &newName)
|
||||||
|
{
|
||||||
|
nameLabel->setText(newName);
|
||||||
|
nameLabel->setToolTip(newName); // for overlength names
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericChatForm::show(Ui::MainWindow &ui)
|
||||||
|
{
|
||||||
|
ui.mainContent->layout()->addWidget(mainWidget);
|
||||||
|
ui.mainHead->layout()->addWidget(headWidget);
|
||||||
|
mainWidget->show();
|
||||||
|
headWidget->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericChatForm::onChatContextMenuRequested(QPoint pos)
|
||||||
|
{
|
||||||
|
QWidget* sender = (QWidget*)QObject::sender();
|
||||||
|
pos = sender->mapToGlobal(pos);
|
||||||
|
QMenu menu;
|
||||||
|
menu.addAction(tr("Save chat log"), this, SLOT(onSaveLogClicked()));
|
||||||
|
menu.exec(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericChatForm::onSaveLogClicked()
|
||||||
|
{
|
||||||
|
QString path = QFileDialog::getSaveFileName(0, tr("Save chat log"));
|
||||||
|
if (path.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(path);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString log;
|
||||||
|
log = chatWidget->toPlainText();
|
||||||
|
|
||||||
|
file.write(log.toUtf8());
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericChatForm::addMessage(QString author, QString message, QDateTime datetime)
|
||||||
|
{
|
||||||
|
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||||
|
bool isMe = (author == Widget::getInstance()->getUsername());
|
||||||
|
|
||||||
|
if (previousName == author)
|
||||||
|
chatWidget->insertMessage(new MessageAction("", message, date, isMe));
|
||||||
|
else chatWidget->insertMessage(new MessageAction(author , message, date, isMe));
|
||||||
|
previousName = author;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericChatForm::~GenericChatForm()
|
||||||
|
{
|
||||||
|
delete mainWidget;
|
||||||
|
delete headWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericChatForm::onEmoteButtonClicked()
|
||||||
|
{
|
||||||
|
// don't show the smiley selection widget if there are no smileys available
|
||||||
|
if (SmileyPack::getInstance().getEmoticons().empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
EmoticonsWidget widget;
|
||||||
|
connect(&widget, SIGNAL(insertEmoticon(QString)), this, SLOT(onEmoteInsertRequested(QString)));
|
||||||
|
|
||||||
|
QWidget* sender = qobject_cast<QWidget*>(QObject::sender());
|
||||||
|
if (sender)
|
||||||
|
{
|
||||||
|
QPoint pos = -QPoint(widget.sizeHint().width() / 2, widget.sizeHint().height()) - QPoint(0, 10);
|
||||||
|
widget.exec(sender->mapToGlobal(pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericChatForm::onEmoteInsertRequested(QString str)
|
||||||
|
{
|
||||||
|
// insert the emoticon
|
||||||
|
QWidget* sender = qobject_cast<QWidget*>(QObject::sender());
|
||||||
|
if (sender)
|
||||||
|
msgEdit->insertPlainText(str);
|
||||||
|
|
||||||
|
msgEdit->setFocus(); // refocus so that we can continue typing
|
||||||
|
}
|
74
widget/form/genericchatform.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GENERICCHATFORM_H
|
||||||
|
#define GENERICCHATFORM_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QTime>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
#include "widget/croppinglabel.h"
|
||||||
|
#include "widget/chatareawidget.h"
|
||||||
|
#include "widget/tool/chattextedit.h"
|
||||||
|
|
||||||
|
// Spacing in px inserted when the author of the last message changes
|
||||||
|
#define AUTHOR_CHANGE_SPACING 5
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GenericChatForm : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
GenericChatForm(QObject *parent = 0);
|
||||||
|
virtual ~GenericChatForm();
|
||||||
|
|
||||||
|
virtual void setName(const QString &newName);
|
||||||
|
virtual void show(Ui::MainWindow &ui);
|
||||||
|
void addMessage(QString author, QString message, QDateTime datetime=QDateTime::currentDateTime());
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sendMessage(int, QString);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void onChatContextMenuRequested(QPoint pos);
|
||||||
|
void onSaveLogClicked();
|
||||||
|
void onEmoteButtonClicked();
|
||||||
|
void onEmoteInsertRequested(QString str);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CroppingLabel *nameLabel;
|
||||||
|
QLabel *avatarLabel;
|
||||||
|
QWidget *mainWidget, *headWidget;
|
||||||
|
QPushButton *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton;
|
||||||
|
QVBoxLayout *headTextLayout;
|
||||||
|
ChatTextEdit *msgEdit;
|
||||||
|
QPushButton *sendButton;
|
||||||
|
QString previousName;
|
||||||
|
ChatAreaWidget *chatWidget;
|
||||||
|
int curRow;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GENERICCHATFORM_H
|
|
@ -18,36 +18,27 @@
|
||||||
#include "group.h"
|
#include "group.h"
|
||||||
#include "widget/groupwidget.h"
|
#include "widget/groupwidget.h"
|
||||||
#include "widget/widget.h"
|
#include "widget/widget.h"
|
||||||
#include "friend.h"
|
|
||||||
#include "friendlist.h"
|
|
||||||
#include "style.h"
|
|
||||||
#include <QFont>
|
|
||||||
#include <QTime>
|
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QFileDialog>
|
|
||||||
|
|
||||||
GroupChatForm::GroupChatForm(Group* chatGroup)
|
GroupChatForm::GroupChatForm(Group* chatGroup)
|
||||||
: group(chatGroup), curRow{0}, lockSliderToBottom{true}
|
: group(chatGroup)
|
||||||
{
|
{
|
||||||
main = new QWidget(), head = new QWidget(), chatAreaWidget = new QWidget();
|
nusersLabel = new QLabel();
|
||||||
headLayout = new QHBoxLayout(), mainFootLayout = new QHBoxLayout();
|
namesList = new QLabel();
|
||||||
headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout();
|
|
||||||
mainChatLayout = new QGridLayout();
|
fileButton->setEnabled(false);
|
||||||
avatar = new QLabel(), name = new QLabel(), nusers = new QLabel(), namesList = new QLabel();
|
callButton->setVisible(false);
|
||||||
msgEdit = new ChatTextEdit();
|
videoButton->setVisible(false);
|
||||||
sendButton = new QPushButton();
|
volButton->setVisible(false);
|
||||||
chatArea = new QScrollArea();
|
micButton->setVisible(false);
|
||||||
QFont bold;
|
|
||||||
bold.setBold(true);
|
|
||||||
QFont small;
|
QFont small;
|
||||||
small.setPixelSize(10);
|
small.setPixelSize(10);
|
||||||
name->setText(group->widget->name.text());
|
|
||||||
name->setFont(bold);
|
nameLabel->setText(group->widget->name.text());
|
||||||
nusers->setFont(small);
|
nusersLabel->setFont(small);
|
||||||
nusers->setText(GroupChatForm::tr("%1 users in chat","Number of users in chat").arg(group->peers.size()));
|
nusersLabel->setText(GroupChatForm::tr("%1 users in chat","Number of users in chat").arg(group->peers.size()));
|
||||||
avatar->setPixmap(QPixmap(":/img/group.png"));
|
avatarLabel->setPixmap(QPixmap(":/img/group_dark.png"));
|
||||||
|
|
||||||
QString names;
|
QString names;
|
||||||
for (QString& s : group->peers)
|
for (QString& s : group->peers)
|
||||||
names.append(s+", ");
|
names.append(s+", ");
|
||||||
|
@ -55,77 +46,21 @@ GroupChatForm::GroupChatForm(Group* chatGroup)
|
||||||
namesList->setText(names);
|
namesList->setText(names);
|
||||||
namesList->setFont(small);
|
namesList->setFont(small);
|
||||||
|
|
||||||
chatAreaWidget->setLayout(mainChatLayout);
|
|
||||||
|
|
||||||
chatArea->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css"));
|
|
||||||
chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
||||||
chatArea->setWidgetResizable(true);
|
|
||||||
chatArea->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
chatArea->setFrameStyle(QFrame::NoFrame);
|
|
||||||
|
|
||||||
mainChatLayout->setColumnStretch(1,1);
|
|
||||||
mainChatLayout->setSpacing(10);
|
|
||||||
|
|
||||||
msgEdit->setObjectName("group");
|
msgEdit->setObjectName("group");
|
||||||
msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css"));
|
|
||||||
msgEdit->setFixedHeight(50);
|
|
||||||
msgEdit->setFrameStyle(QFrame::NoFrame);
|
|
||||||
|
|
||||||
mainChatLayout->setColumnStretch(1,1);
|
headTextLayout->addWidget(nusersLabel);
|
||||||
mainChatLayout->setHorizontalSpacing(10);
|
|
||||||
|
|
||||||
sendButton->setStyleSheet(Style::get(":/ui/sendButton/sendButton.css"));
|
|
||||||
sendButton->setFixedSize(50, 50);
|
|
||||||
|
|
||||||
main->setLayout(mainLayout);
|
|
||||||
mainLayout->addWidget(chatArea);
|
|
||||||
mainLayout->addLayout(mainFootLayout);
|
|
||||||
mainLayout->setMargin(0);
|
|
||||||
|
|
||||||
mainFootLayout->addWidget(msgEdit);
|
|
||||||
mainFootLayout->addWidget(sendButton);
|
|
||||||
|
|
||||||
head->setLayout(headLayout);
|
|
||||||
headLayout->addWidget(avatar);
|
|
||||||
headLayout->addLayout(headTextLayout);
|
|
||||||
headLayout->addStretch();
|
|
||||||
headLayout->setMargin(0);
|
|
||||||
|
|
||||||
headTextLayout->addStretch();
|
|
||||||
headTextLayout->addWidget(name);
|
|
||||||
headTextLayout->addWidget(nusers);
|
|
||||||
headTextLayout->addWidget(namesList);
|
headTextLayout->addWidget(namesList);
|
||||||
headTextLayout->setMargin(0);
|
headTextLayout->setMargin(0);
|
||||||
headTextLayout->setSpacing(0);
|
headTextLayout->setSpacing(0);
|
||||||
headTextLayout->addStretch();
|
headTextLayout->addStretch();
|
||||||
|
|
||||||
chatArea->setWidget(chatAreaWidget);
|
|
||||||
|
|
||||||
sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
|
||||||
|
|
||||||
connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered()));
|
connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered()));
|
||||||
connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered()));
|
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)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupChatForm::~GroupChatForm()
|
GroupChatForm::~GroupChatForm()
|
||||||
{
|
{
|
||||||
delete head;
|
|
||||||
delete main;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupChatForm::show(Ui::MainWindow &ui)
|
|
||||||
{
|
|
||||||
ui.mainContent->layout()->addWidget(main);
|
|
||||||
ui.mainHead->layout()->addWidget(head);
|
|
||||||
main->show();
|
|
||||||
head->show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupChatForm::setName(QString newName)
|
|
||||||
{
|
|
||||||
name->setText(newName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupChatForm::onSendTriggered()
|
void GroupChatForm::onSendTriggered()
|
||||||
|
@ -139,124 +74,21 @@ void GroupChatForm::onSendTriggered()
|
||||||
|
|
||||||
void GroupChatForm::addGroupMessage(QString message, int peerId)
|
void GroupChatForm::addGroupMessage(QString message, int peerId)
|
||||||
{
|
{
|
||||||
QLabel *msgAuthor;
|
QString msgAuthor;
|
||||||
if (group->peers.contains(peerId))
|
if (group->peers.contains(peerId))
|
||||||
msgAuthor = new QLabel(group->peers[peerId]);
|
msgAuthor = group->peers[peerId];
|
||||||
else
|
else
|
||||||
msgAuthor = new QLabel(tr("<Unknown>"));
|
msgAuthor = tr("<Unknown>");
|
||||||
|
|
||||||
QLabel *msgText = new QLabel(message);
|
addMessage(msgAuthor, message);
|
||||||
QLabel *msgDate = new QLabel(QTime::currentTime().toString("hh:mm"));
|
|
||||||
|
|
||||||
addMessage(msgAuthor, msgText, msgDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupChatForm::addMessage(QString author, QString message, QString date)
|
|
||||||
{
|
|
||||||
addMessage(new QLabel(author), new QLabel(message), new QLabel(date));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupChatForm::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::AlignLeft);
|
|
||||||
date->setAlignment(Qt::AlignTop);
|
|
||||||
message->setWordWrap(true);
|
|
||||||
message->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
author->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
date->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
if (author->text() == Widget::getInstance()->getUsername())
|
|
||||||
{
|
|
||||||
QPalette pal;
|
|
||||||
pal.setColor(QPalette::WindowText, Qt::gray);
|
|
||||||
author->setPalette(pal);
|
|
||||||
message->setPalette(pal);
|
|
||||||
}
|
|
||||||
if (previousName.isEmpty() || previousName != author->text())
|
|
||||||
{
|
|
||||||
if (curRow)
|
|
||||||
{
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3);
|
|
||||||
}
|
|
||||||
previousName = author->text();
|
|
||||||
curRow++;
|
|
||||||
}
|
|
||||||
else if (curRow)// onSaveLogClicked expects 0 or 3 QLabel per line
|
|
||||||
author->setText("");
|
|
||||||
if (message->text()[0] == '>')
|
|
||||||
message->setPalette(greentext);
|
|
||||||
mainChatLayout->addWidget(author, curRow, 0);
|
|
||||||
mainChatLayout->addWidget(message, curRow, 1);
|
|
||||||
mainChatLayout->addWidget(date, curRow, 3);
|
|
||||||
mainChatLayout->setRowStretch(curRow+1, 1);
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
curRow++;
|
|
||||||
author->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
message->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
date->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
connect(author, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
|
||||||
connect(message, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
|
||||||
connect(date, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupChatForm::onSliderRangeChanged()
|
|
||||||
{
|
|
||||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
|
||||||
if (lockSliderToBottom)
|
|
||||||
scroll->setValue(scroll->maximum());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupChatForm::onUserListChanged()
|
void GroupChatForm::onUserListChanged()
|
||||||
{
|
{
|
||||||
nusers->setText(tr("%1 users in chat").arg(group->nPeers));
|
nusersLabel->setText(tr("%1 users in chat").arg(group->nPeers));
|
||||||
QString names;
|
QString names;
|
||||||
for (QString& s : group->peers)
|
for (QString& s : group->peers)
|
||||||
names.append(s+", ");
|
names.append(s+", ");
|
||||||
names.chop(2);
|
names.chop(2);
|
||||||
namesList->setText(names);
|
namesList->setText(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupChatForm::onChatContextMenuRequested(QPoint pos)
|
|
||||||
{
|
|
||||||
QWidget* sender = (QWidget*)QObject::sender();
|
|
||||||
pos = sender->mapToGlobal(pos);
|
|
||||||
QMenu menu;
|
|
||||||
menu.addAction("Save chat log", this, SLOT(onSaveLogClicked()));
|
|
||||||
menu.exec(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupChatForm::onSaveLogClicked()
|
|
||||||
{
|
|
||||||
QString path = QFileDialog::getSaveFileName(0,tr("Save chat log"));
|
|
||||||
if (path.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QFile file(path);
|
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString log;
|
|
||||||
QList<QLabel*> labels = chatAreaWidget->findChildren<QLabel*>();
|
|
||||||
int i=0;
|
|
||||||
for (QLabel* label : labels)
|
|
||||||
{
|
|
||||||
log += label->text();
|
|
||||||
if (i==2)
|
|
||||||
{
|
|
||||||
i=0;
|
|
||||||
log += '\n';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log += '\t';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file.write(log.toUtf8());
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,58 +17,27 @@
|
||||||
#ifndef GROUPCHATFORM_H
|
#ifndef GROUPCHATFORM_H
|
||||||
#define GROUPCHATFORM_H
|
#define GROUPCHATFORM_H
|
||||||
|
|
||||||
#include <QLabel>
|
#include "genericchatform.h"
|
||||||
#include <QWidget>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QTextEdit>
|
|
||||||
#include <QScrollArea>
|
|
||||||
#include <QTime>
|
|
||||||
|
|
||||||
#include "widget/tool/chattextedit.h"
|
#include "widget/tool/chattextedit.h"
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
// Spacing in px inserted when the author of the last message changes
|
|
||||||
#define AUTHOR_CHANGE_SPACING 5
|
|
||||||
|
|
||||||
class Group;
|
class Group;
|
||||||
|
|
||||||
class GroupChatForm : public QObject
|
class GroupChatForm : public GenericChatForm
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
GroupChatForm(Group* chatGroup);
|
GroupChatForm(Group* chatGroup);
|
||||||
~GroupChatForm();
|
~GroupChatForm();
|
||||||
void show(Ui::MainWindow &ui);
|
|
||||||
void setName(QString newName);
|
|
||||||
void addGroupMessage(QString message, int peerId);
|
void addGroupMessage(QString message, int peerId);
|
||||||
void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm"));
|
|
||||||
void addMessage(QLabel* author, QLabel* message, QLabel* date);
|
|
||||||
void onUserListChanged();
|
void onUserListChanged();
|
||||||
|
|
||||||
signals:
|
|
||||||
void sendMessage(int, QString);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onSendTriggered();
|
void onSendTriggered();
|
||||||
void onSliderRangeChanged();
|
|
||||||
void onChatContextMenuRequested(QPoint pos);
|
|
||||||
void onSaveLogClicked();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Group* group;
|
Group* group;
|
||||||
QHBoxLayout *headLayout, *mainFootLayout;
|
QLabel *nusersLabel, *namesList;
|
||||||
QVBoxLayout *headTextLayout, *mainLayout;
|
|
||||||
QGridLayout *mainChatLayout;
|
|
||||||
QLabel *avatar, *name, *nusers, *namesList;
|
|
||||||
ChatTextEdit *msgEdit;
|
|
||||||
QPushButton *sendButton;
|
|
||||||
QScrollArea *chatArea;
|
|
||||||
QWidget *main, *head, *chatAreaWidget;
|
|
||||||
QString previousName;
|
|
||||||
int curRow;
|
|
||||||
bool lockSliderToBottom;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GROUPCHATFORM_H
|
#endif // GROUPCHATFORM_H
|
||||||
|
|
120
widget/tool/chataction.cpp
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "chataction.h"
|
||||||
|
#include "smileypack.h"
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
QString ChatAction::toHtmlChars(const QString &str)
|
||||||
|
{
|
||||||
|
static QList<QPair<QString, QString>> replaceList = {{"&","&"}, {" "," "}, {">",">"}, {"<","<"}};
|
||||||
|
QString res = str;
|
||||||
|
|
||||||
|
for (auto &it : replaceList)
|
||||||
|
res = res.replace(it.first,it.second);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::QImage2base64(const QImage &img)
|
||||||
|
{
|
||||||
|
QByteArray ba;
|
||||||
|
QBuffer buffer(&ba);
|
||||||
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
img.save(&buffer, "PNG"); // writes image into ba in PNG format
|
||||||
|
return ba.toBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::wrapName(const QString &name)
|
||||||
|
{
|
||||||
|
if (isMe)
|
||||||
|
return QString("<td><div class=name_me>" + name + "</div></td>\n");
|
||||||
|
else
|
||||||
|
return QString("<td><div class=name>" + name + "</div></td>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::wrapDate(const QString &date)
|
||||||
|
{
|
||||||
|
QString res = "<td align=right><div class=date>" + date + "</div></td>\n";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::wrapMessage(const QString &message)
|
||||||
|
{
|
||||||
|
QString res = "<td width=100%><div class=message>" + message + "</div></td>\n";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::wrapWholeLine(const QString &line)
|
||||||
|
{
|
||||||
|
QString res = "<tr>\n" + line + "</tr>\n";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageAction::MessageAction(const QString &author, const QString &message, const QString &date, const bool &me) :
|
||||||
|
ChatAction(me)
|
||||||
|
{
|
||||||
|
QString message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message));
|
||||||
|
|
||||||
|
QStringList messageLines = message_.split("\n");
|
||||||
|
message_ = "";
|
||||||
|
for (QString& s : messageLines)
|
||||||
|
{
|
||||||
|
if (QRegExp("^[ ]*>.*").exactMatch(s))
|
||||||
|
message_ += "<div class=quote>" + s.right(s.length()-4) + "</div><br>";
|
||||||
|
else
|
||||||
|
message_ += s + "<br>";
|
||||||
|
}
|
||||||
|
message_ = message_.left(message_.length()-4);
|
||||||
|
|
||||||
|
content = wrapWholeLine(wrapName(author) + wrapMessage(message_) + wrapDate(date));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageAction::getHtml()
|
||||||
|
{
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTransferAction::FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me) :
|
||||||
|
ChatAction(me),
|
||||||
|
sender(author),
|
||||||
|
timestamp(date)
|
||||||
|
{
|
||||||
|
w = widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTransferAction::~FileTransferAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferAction::getHtml()
|
||||||
|
{
|
||||||
|
QString widgetHtml;
|
||||||
|
if (w != nullptr)
|
||||||
|
widgetHtml = w->getHtmlImage();
|
||||||
|
else
|
||||||
|
widgetHtml = "<div class=quote>EMPTY CONTENT</div>";
|
||||||
|
QString res = wrapWholeLine(wrapName(sender) + wrapMessage(widgetHtml) + wrapDate(timestamp));;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferAction::wrapMessage(const QString &message)
|
||||||
|
{
|
||||||
|
QString res = "<td width=100%>" + message + "</td>\n";
|
||||||
|
return res;
|
||||||
|
}
|
67
widget/tool/chataction.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
See the COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CHATACTION_H
|
||||||
|
#define CHATACTION_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include "filetransferinstance.h"
|
||||||
|
|
||||||
|
class ChatAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChatAction(const bool &me) : isMe(me) {;}
|
||||||
|
virtual ~ChatAction(){;}
|
||||||
|
virtual QString getHtml() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString toHtmlChars(const QString &str);
|
||||||
|
QString QImage2base64(const QImage &img);
|
||||||
|
|
||||||
|
virtual QString wrapName(const QString &name);
|
||||||
|
virtual QString wrapDate(const QString &date);
|
||||||
|
virtual QString wrapMessage(const QString &message);
|
||||||
|
virtual QString wrapWholeLine(const QString &line);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isMe;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MessageAction : public ChatAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MessageAction(const QString &author, const QString &message, const QString &date, const bool &me);
|
||||||
|
virtual ~MessageAction(){;}
|
||||||
|
virtual QString getHtml();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString content;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileTransferAction : public ChatAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me);
|
||||||
|
virtual ~FileTransferAction();
|
||||||
|
virtual QString getHtml();
|
||||||
|
virtual QString wrapMessage(const QString &message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileTransferInstance *w;
|
||||||
|
QString sender, timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATACTION_H
|
|
@ -487,7 +487,7 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message)
|
||||||
if (!f)
|
if (!f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
f->chatForm->addFriendMessage(message);
|
f->chatForm->addMessage(f->getName(), message);
|
||||||
|
|
||||||
if (activeChatroomWidget != nullptr)
|
if (activeChatroomWidget != nullptr)
|
||||||
{
|
{
|
||||||
|
|