Merge branch 'master' into chatlog_v3_1
Conflicts: qtox.pro res.qrc src/misc/smileypack.cpp src/widget/chatareawidget.cpp src/widget/chatareawidget.h src/widget/form/chatform.cpp src/widget/form/genericchatform.cpp src/widget/form/genericchatform.h src/widget/form/settings/generalform.cpp src/widget/tool/chatactions/messageaction.cpp src/widget/tool/chatactions/systemmessageaction.cpp src/widget/widget.cpp
367
INSTALL.md
@ -1,8 +1,8 @@
|
||||
#Install Instructions
|
||||
- [Dependencies](#dependencies)
|
||||
- [Windows](#windows)
|
||||
- [Linux](#linux)
|
||||
- [OS X](#osx)
|
||||
- [Windows](#windows)
|
||||
|
||||
<a name="dependencies" />
|
||||
##Dependencies
|
||||
@ -12,8 +12,205 @@
|
||||
| Qt | >= 5.2.0 | core, gui, network, widget, xml, sql |
|
||||
| GCC/MinGW | >= 4.8 | C++11 enabled |
|
||||
| Tox Core | most recent | core, av |
|
||||
| OpenCV | >= 2.4.9 | core, highgui |
|
||||
| OpenCV | >= 2.4.9 | core, highgui, imgproc |
|
||||
| OpenAL Soft | >= 1.16.0 | |
|
||||
| filter_audio | most recent | |
|
||||
|
||||
|
||||
<a name="linux" />
|
||||
##Linux
|
||||
###Simple install
|
||||
Easy qTox install is provided for variety of distributions:
|
||||
https://wiki.tox.im/Binaries#Apt.2FAptitude_.28Debian.2C_Ubuntu.2C_Mint.2C_etc..29
|
||||
|
||||
If your distribution is not listed, or you want/need to compile qTox, there are provided instructions.
|
||||
|
||||
**Please note that installing toxcore/qTox from AUR is not supported**, although installing other dependencies, provided that they met requirements, should be fine, unless you are installing cryptography library from AUR, which should rise red flags by itself…
|
||||
|
||||
----
|
||||
|
||||
Most of the dependencies should be available through your package manger. You may either follow the directions below, or simply run `./simple_make.sh` after cloning, which will attempt to automatically download dependencies followed by compilation.
|
||||
|
||||
|
||||
###Cloning the Repository
|
||||
In order to clone the qTox repository you need Git.
|
||||
|
||||
Arch Linux:
|
||||
```bash
|
||||
sudo pacman -S --needed git
|
||||
```
|
||||
|
||||
Debian:
|
||||
```bash
|
||||
sudo apt-get install git
|
||||
```
|
||||
|
||||
Fedora:
|
||||
```bash
|
||||
yum install git
|
||||
```
|
||||
|
||||
Ubuntu:
|
||||
```bash
|
||||
sudo apt-get 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
|
||||
|
||||
Arch Linux:
|
||||
```bash
|
||||
sudo pacman -S --needed base-devel qt5 opencv openal libxss
|
||||
```
|
||||
|
||||
Debian:
|
||||
```bash
|
||||
sudo apt-get install build-essential qt5-qmake qt5-default libopenal-dev libopencv-dev libxss-dev
|
||||
```
|
||||
|
||||
Fedora:
|
||||
```bash
|
||||
yum groupinstall "Development Tools"
|
||||
yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libXScrnSaver-devel
|
||||
```
|
||||
|
||||
Slackware:
|
||||
|
||||
You can grab slackbuilds of the needed dependencies here:
|
||||
|
||||
http://slackbuilds.org/repository/14.1/libraries/OpenAL/
|
||||
|
||||
http://slackbuilds.org/repository/14.1/libraries/qt5/
|
||||
|
||||
http://slackbuilds.org/repository/14.1/libraries/opencv/
|
||||
|
||||
Ubuntu:
|
||||
```bash
|
||||
sudo apt-get install build-essential qt5-qmake qt5-default qttools5-dev-tools libopenal-dev libopencv-dev libxss-dev
|
||||
```
|
||||
|
||||
###Tox Core
|
||||
|
||||
First of all install the dependencies of Tox Core.
|
||||
|
||||
Arch Linux:
|
||||
```bash
|
||||
sudo pacman -S --needed opus vpx
|
||||
```
|
||||
|
||||
```
|
||||
Debian:
|
||||
```bash
|
||||
sudo apt-get install libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev
|
||||
```
|
||||
|
||||
Fedora:
|
||||
```bash
|
||||
yum install libtool autoconf automake check check-devel
|
||||
```
|
||||
|
||||
Ubuntu:
|
||||
```bash
|
||||
sudo apt-get install libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
###filter_audio
|
||||
You also need to install filter_audio library separately if you did not run ``./bootstrap.sh``.
|
||||
```bash
|
||||
./install_libfilteraudio.sh
|
||||
```
|
||||
|
||||
After all the dependencies are thus reeady to go, compiling should be as simple as
|
||||
```bash
|
||||
qmake
|
||||
make
|
||||
```
|
||||
|
||||
###Building packages
|
||||
|
||||
Alternately, 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.
|
||||
|
||||
|
||||
<a name="osx" />
|
||||
##OS X
|
||||
|
||||
###OSX Easy Install
|
||||
|
||||
Since https://github.com/ReDetection/homebrew-qtox you can easily install qtox with homebrew
|
||||
```bash
|
||||
brew install --HEAD ReDetection/qtox/qtox
|
||||
```
|
||||
|
||||
|
||||
###OSX Full Install Guide
|
||||
|
||||
This guide is intended for people who wish to use an existing or new ProjectTox-Core installation separate to the bundled installation with qTox, if you do not wish to use a separate installation you can skip to the section titled 'Final Steps'.
|
||||
|
||||
Installation on OSX, isn't quite straight forward, here is a quick guide on how to install;
|
||||
|
||||
Note that qTox now requires OpenCV and OpenAL for video and audio.
|
||||
|
||||
The first thing you need to do is install ProjectTox-Core with a/v support. Refer to the INSTALL guide in the PrjectTox-Core github repo.
|
||||
|
||||
Next you need to download QtTools (http://qt-project.org/downloads), at the time of writing this is at version .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
|
||||
```
|
||||
####Final Steps
|
||||
|
||||
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
|
||||
make
|
||||
```
|
||||
Assuming all went well you should now have a qTox.app file within the directory. Double click and it should open!
|
||||
|
||||
|
||||
<a name="windows" />
|
||||
##Windows
|
||||
@ -74,168 +271,4 @@ As for OpenCV there are no prebuild packages of OpenAL Softe compiled with MinGW
|
||||
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.
|
||||
|
||||
<a name="linux" />
|
||||
##Linux
|
||||
Most of the dependencies should be available through your package manger. You may either follow the directions below, or simply run `./simple_make.sh` after cloning, which will attempt to automatically download dependencies followed by compilation.
|
||||
|
||||
###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 qttools5-dev-tools 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 libopus-dev libvpx-dev
|
||||
```
|
||||
|
||||
Ubuntu:
|
||||
```bash
|
||||
sudo apt-get install libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev
|
||||
```
|
||||
|
||||
Arch Linux: (Arch Linux provides the package "tox-git" in AUR)
|
||||
```bash
|
||||
sudo pacman -S --needed 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
|
||||
```
|
||||
|
||||
After all the dependencies are thus reeady to go, compiling should be as simple as
|
||||
```bash
|
||||
qmake
|
||||
make
|
||||
```
|
||||
|
||||
###Building packages
|
||||
|
||||
Alternately, 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.
|
||||
|
||||
<a name="osx" />
|
||||
##OS X
|
||||
|
||||
###OSX Easy Install
|
||||
|
||||
Since https://github.com/ReDetection/homebrew-qtox you can easily install qtox with homebrew
|
||||
```bash
|
||||
brew install --HEAD ReDetection/qtox/qtox
|
||||
```
|
||||
|
||||
###OSX Full Install Guide
|
||||
|
||||
This guide is intended for people who wish to use an existing or new ProjectTox-Core installation separate to the bundled installation with qTox, if you do not wish to use a separate installation you can skip to the section titled 'Final Steps'.
|
||||
|
||||
Installation on OSX, isn't quite straight forward, here is a quick guide on how to install;
|
||||
|
||||
Note that qTox now requires OpenCV and OpenAL for video and audio.
|
||||
|
||||
The first thing you need to do is install ProjectTox-Core with a/v support. Refer to the INSTALL guide in the PrjectTox-Core github repo.
|
||||
|
||||
Next you need to download QtTools (http://qt-project.org/downloads), at the time of writing this is at version .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
|
||||
```
|
||||
####Final Steps
|
||||
|
||||
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
|
||||
make
|
||||
```
|
||||
Assuming all went well you should now have a qTox.app file within the directory. Double click and it should open!
|
||||
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.
|
21
README.md
@ -1,27 +1,28 @@
|
||||
qTox
|
||||
======
|
||||
|
||||
Powerful Tox client that tries to follow the Tox UI mockup while running on all major systems. <br/>
|
||||
This GUI uses code from @nurupo'tos ProjectTox-Qt-GUI, in particular the "Core" Toxcore wrapper. <br/>
|
||||
However, it is not a fork.
|
||||
qTox is a powerful Tox client that tries to follow the Tox design guidelines while running on all major platforms. <br/>
|
||||
|
||||
<h2>Features</h2>
|
||||
|
||||
- One to one chat with friends
|
||||
- Group chats
|
||||
- File transfers, with previewing of images
|
||||
- Audio calls
|
||||
- Audio calls, including group calls
|
||||
- Video calls
|
||||
- Tox DNS
|
||||
- Tox DNS and Tox URI support
|
||||
- Translations in various languages
|
||||
- Avatars
|
||||
- Auto-updates on Windows and Mac, packages on Linux
|
||||
- And many more options!
|
||||
|
||||
<h2>Downloads</h2>
|
||||
|
||||
This client runs on Windows, Linux and Mac natively.<br/>
|
||||
|
||||
<a href="https://github.com/tux3/qTox/releases/latest">Windows download</a><br/>
|
||||
<a href="https://dist-build.tox.im/qtox.dmg">Mac download </a><br/>
|
||||
<a href="http://207.12.89.155:8080/job/qTox-win64-nsis/lastSuccessfulBuild/artifact/setup-qtox64.exe">Windows 64 bit download</a><br/>
|
||||
<a href="http://207.12.89.155:8080/job/qTox-win32-nsis/lastSuccessfulBuild/artifact/setup-qtox32.exe">Windows 32 bit download (for older hardware)</a><br/>
|
||||
<a href="https://dist-build.tox.im/qtox.dmg">Mac OS X download </a><br/>
|
||||
<a href="https://jenkins.libtoxcore.so/job/qTox-linux-amd64/lastSuccessfulBuild/artifact/qt/qtox.xz">Linux binary download</a><br/>
|
||||
<a href="https://jenkins.libtoxcore.so/user/tux3/my-views/view/qTox/job/qTox-Linux-pkg/lastSuccessfulBuild/artifact/">Linux packages</a><br/>
|
||||
|
||||
@ -33,3 +34,9 @@ This client runs on Windows, Linux and Mac natively.<br/>
|
||||
##Documentation:
|
||||
|
||||
[Compiling](/INSTALL.md)
|
||||
|
||||
##Developer overview:
|
||||
|
||||
[GitStats](http://207.12.89.155/index.html)<br/>
|
||||
[Mac & Linux jenkins](https://jenkins.libtoxcore.so/user/tux3/my-views/view/qTox/)<br/>
|
||||
[Windows jenkins](http://207.12.89.155:8080)<br/>
|
||||
|
14
bootstrap.sh
@ -15,6 +15,7 @@ SODIUM_VER=1.0.0
|
||||
# directory names of cloned repositories
|
||||
SODIUM_DIR=libsodium-$SODIUM_VER
|
||||
TOX_CORE_DIR=libtoxcore-latest
|
||||
FILTER_AUDIO_DIR=filter_audio
|
||||
|
||||
# this boolean describes whether the installation of
|
||||
# libsodium should be skipped or not
|
||||
@ -42,6 +43,11 @@ if [ -z "$TOX_CORE_DIR" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$FILTER_AUDIO_DIR" ]; then
|
||||
echo "internal error detected!"
|
||||
echo "FILTER_AUDIO_DIR should not be empty... aborting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
########## check input parameters ##########
|
||||
@ -95,7 +101,7 @@ mkdir -p ${BASE_DIR}
|
||||
# if exists, otherwise cloning them may fail
|
||||
rm -rf ${BASE_DIR}/${SODIUM_DIR}
|
||||
rm -rf ${BASE_DIR}/${TOX_CORE_DIR}
|
||||
|
||||
rm -rf ${BASE_DIR}/${FILTER_AUDIO_DIR}
|
||||
|
||||
|
||||
############### install step ###############
|
||||
@ -122,6 +128,12 @@ if [[ $TOX_ONLY = "false" ]]; then
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
if [[ $GLOBAL = "false" ]]; then
|
||||
./install_libfilteraudio.sh ${BASE_DIR}/${FILTER_AUDIO_DIR} ${BASE_DIR}
|
||||
else
|
||||
./install_libfilteraudio.sh ${BASE_DIR}/${FILTER_AUDIO_DIR}
|
||||
fi
|
||||
fi
|
||||
|
||||
# clone current master of libtoxcore
|
||||
|
BIN
img/group_button.png
Normal file
After Width: | Height: | Size: 454 B |
BIN
img/icons/qtox_profile.icns
Normal file
BIN
img/taskbar/dark/taskbar_busy.png
Normal file
After Width: | Height: | Size: 420 B |
BIN
img/taskbar/dark/taskbar_busy_2x.png
Normal file
After Width: | Height: | Size: 716 B |
BIN
img/taskbar/dark/taskbar_idle.png
Normal file
After Width: | Height: | Size: 410 B |
BIN
img/taskbar/dark/taskbar_idle_2x.png
Normal file
After Width: | Height: | Size: 719 B |
BIN
img/taskbar/dark/taskbar_invisible.png
Normal file
After Width: | Height: | Size: 478 B |
BIN
img/taskbar/dark/taskbar_invisible_2x.png
Normal file
After Width: | Height: | Size: 704 B |
BIN
img/taskbar/dark/taskbar_offline.png
Normal file
After Width: | Height: | Size: 395 B |
BIN
img/taskbar/dark/taskbar_offline_2x.png
Normal file
After Width: | Height: | Size: 711 B |
BIN
img/taskbar/dark/taskbar_online.png
Normal file
After Width: | Height: | Size: 374 B |
BIN
img/taskbar/dark/taskbar_online_2x.png
Normal file
After Width: | Height: | Size: 637 B |
BIN
img/taskbar/light/taskbar_busy.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
img/taskbar/light/taskbar_busy_2x.png
Normal file
After Width: | Height: | Size: 685 B |
BIN
img/taskbar/light/taskbar_idle.png
Normal file
After Width: | Height: | Size: 363 B |
BIN
img/taskbar/light/taskbar_idle_2x.png
Normal file
After Width: | Height: | Size: 635 B |
BIN
img/taskbar/light/taskbar_invisible.png
Normal file
After Width: | Height: | Size: 366 B |
BIN
img/taskbar/light/taskbar_invisible_2x.png
Normal file
After Width: | Height: | Size: 670 B |
BIN
img/taskbar/light/taskbar_offline.png
Normal file
After Width: | Height: | Size: 378 B |
BIN
img/taskbar/light/taskbar_offline_2x.png
Normal file
After Width: | Height: | Size: 677 B |
BIN
img/taskbar/light/taskbar_online.png
Normal file
After Width: | Height: | Size: 320 B |
BIN
img/taskbar/light/taskbar_online_2x.png
Normal file
After Width: | Height: | Size: 578 B |
40
install_libfilteraudio.sh
Executable file
@ -0,0 +1,40 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z $1 ]; then
|
||||
SOURCE_DIR="filter_audio/"
|
||||
else
|
||||
SOURCE_DIR="$1/"
|
||||
fi
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
LIB_DIR="/usr/local/lib/"
|
||||
INCLUDE_DIR="/usr/local/include/"
|
||||
else
|
||||
LIB_DIR="$2/lib/"
|
||||
INCLUDE_DIR="$2/include/"
|
||||
fi
|
||||
|
||||
echo "Cloning filter_audio from GitHub.com"
|
||||
git clone https://github.com/irungentoo/filter_audio.git $SOURCE_DIR
|
||||
|
||||
echo "Compiling filter_audio"
|
||||
cd $SOURCE_DIR
|
||||
gcc -c -fPIC filter_audio.c aec/*.c agc/*.c ns/*.c other/*.c -lm -lpthread
|
||||
|
||||
echo "Creating shared object file"
|
||||
gcc *.o -shared -o libfilteraudio.so
|
||||
|
||||
echo "Cleaning up"
|
||||
rm *.o
|
||||
|
||||
muhcmd="cp libfilteraudio.so $LIB_DIR"
|
||||
[ -z "$2" ] && muhcmd="sudo $muhcmd"
|
||||
echo "Installing libfilteraudio.so with $muhcmd"
|
||||
$muhcmd
|
||||
|
||||
muhcmd="cp *.h $INCLUDE_DIR"
|
||||
[ -z "$2" ] && muhcmd="sudo $muhcmd"
|
||||
echo "Installing include files with $muhcmd"
|
||||
$muhcmd
|
||||
|
||||
echo "Finished."
|
154
osx/gplv3.rtf
Normal file
@ -0,0 +1,154 @@
|
||||
{\rtf1\ansi\deff0\adeflang1025
|
||||
{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset0 Times New Roman;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\fnil\fprq2\fcharset0 Arial Unicode MS;}{\f4\fnil\fprq2\fcharset0 MS Mincho;}{\f5\fnil\fprq2\fcharset0 Tahoma;}{\f6\fnil\fprq0\fcharset0 Tahoma;}}
|
||||
{\colortbl;\red0\green0\blue0;\red128\green128\blue128;}
|
||||
{\stylesheet{\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035\snext1 Normal;}
|
||||
{\s2\sb240\sa120\keepn\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\afs28\lang255\ltrch\dbch\af4\langfe255\hich\f2\fs28\lang1035\loch\f2\fs28\lang1035\sbasedon1\snext3 Heading;}
|
||||
{\s3\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035\sbasedon1\snext3 Body Text;}
|
||||
{\s4\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035\sbasedon3\snext4 List;}
|
||||
{\s5\sb120\sa120\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ai\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\i\loch\f0\fs24\lang1035\i\sbasedon1\snext5 caption;}
|
||||
{\s6\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af6\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035\sbasedon1\snext6 Index;}
|
||||
}
|
||||
{\info{\author Kimmo Varis}{\creatim\yr2010\mo1\dy17\hr1\min15}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment StarWriter}{\vern3100}}\deftab709
|
||||
{\*\pgdsctbl
|
||||
{\pgdsc0\pgdscuse195\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\pgdscnxt0 Standard;}}
|
||||
\paperh15840\paperw12240\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc
|
||||
\pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1035\i0\b0 GNU GENERAL PUBLIC LICENSE\line Version 3, 29 June 2007\line \line Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\line Everyone is permitted to copy and distribute verbatim copies\line of this license document, but
|
||||
changing it is not allowed.\line \line Preamble\line \line The GNU General Public License is a free, copyleft license for\line software and other kinds of works.\line \line The licenses for most software and other practical works are designed\line to take away yo
|
||||
ur freedom to share and change the works. By contrast,\line the GNU General Public License is intended to guarantee your freedom to\line share and change all versions of a program--to make sure it remains free\line software for all its users. We, the Free Software Foun
|
||||
dation, use the\line GNU General Public License for most of our software; it applies also to\line any other work released this way by its authors. You can apply it to\line your programs, too.\line \line When we speak of free software, we are referring to freedom, not\line price. Ou
|
||||
r General Public Licenses are designed to make sure that you\line have the freedom to distribute copies of free software (and charge for\line them if you wish), that you receive source code or can get it if you\line want it, that you can change the software or use pieces
|
||||
of it in new\line free programs, and that you know you can do these things.\line \line To protect your rights, we need to prevent others from denying you\line these rights or asking you to surrender the rights. Therefore, you have\line certain responsibilities if you distribut
|
||||
e copies of the software, or if\line you modify it: responsibilities to respect the freedom of others.\line \line For example, if you distribute copies of such a program, whether\line gratis or for a fee, you must pass on to the recipients the same\line freedoms that you receive
|
||||
d. You must make sure that they, too, receive\line or can get the source code. And you must show them these terms so they\line know their rights.\line \line Developers that use the GNU GPL protect your rights with two steps:\line (1) assert copyright on the software, and (2) o
|
||||
ffer you this License\line giving you legal permission to copy, distribute and/or modify it.\line \line For the developers' and authors' protection, the GPL clearly explains\line that there is no warranty for this free software. For both users' and\line authors' sake, the GPL r
|
||||
equires that modified versions be marked as\line changed, so that their problems will not be attributed erroneously to\line authors of previous versions.\line \line Some devices are designed to deny users access to install or run\line modified versions of the software inside the
|
||||
m, although the manufacturer\line can do so. This is fundamentally incompatible with the aim of\line protecting users' freedom to change the software. The systematic\line pattern of such abuse occurs in the area of products for individuals to\line use, which is precisely wh
|
||||
ere it is most unacceptable. Therefore, we\line have designed this version of the GPL to prohibit the practice for those\line products. If such problems arise substantially in other domains, we\line stand ready to extend this provision to those domains in future versio
|
||||
ns\line of the GPL, as needed to protect the freedom of users.\line \line Finally, every program is threatened constantly by software patents.\line States should not allow patents to restrict development and use of\line software on general-purpose computers, but in those that do
|
||||
, we wish to\line avoid the special danger that patents applied to a free program could\line make it effectively proprietary. To prevent this, the GPL assures that\line patents cannot be used to render the program non-free.\line \line The precise terms and conditions for copyin
|
||||
g, distribution and\line modification follow.\line \line TERMS AND CONDITIONS\line \line 0. Definitions.\line \line "This License" refers to version 3 of the GNU General Public License.\line \line "Copyright" also means copyright-like laws that apply to other kinds of\line wor
|
||||
ks, such as semiconductor masks.\line \line "The Program" refers to any copyrightable work licensed under this\line License. Each licensee is addressed as "you". "Licensees" and\line "recipients" may be individuals or organizations.\line \line To "modify" a work means to copy fro
|
||||
m or adapt all or part of the work\line in a fashion requiring copyright permission, other than the making of an\line exact copy. The resulting work is called a "modified version" of the\line earlier work or a work "based on" the earlier work.\line \line A "covered work" means
|
||||
either the unmodified Program or a work based\line on the Program.\line \line To "propagate" a work means to do anything with it that, without\line permission, would make you directly or secondarily liable for\line infringement under applicable copyright law, except executing it
|
||||
on a\line computer or modifying a private copy. Propagation includes copying,\line distribution (with or without modification), making available to the\line public, and in some countries other activities as well.\line \line To "convey" a work means any kind of propagation that
|
||||
enables other\line parties to make or receive copies. Mere interaction with a user through\line a computer network, with no transfer of a copy, is not conveying.\line \line An interactive user interface displays "Appropriate Legal Notices"\line to the extent that it includes a
|
||||
convenient and prominently visible\line feature that (1) displays an appropriate copyright notice, and (2)\line tells the user that there is no warranty for the work (except to the\line extent that warranties are provided), that licensees may convey the\line work under this
|
||||
License, and how to view a copy of this License. If\line the interface presents a list of user commands or options, such as a\line menu, a prominent item in the list meets this criterion.\line \line 1. Source Code.\line \line The "source code" for a work means the preferred form o
|
||||
f the work\line for making modifications to it. "Object code" means any non-source\line form of a work.\line \line A "Standard Interface" means an interface that either is an official\line standard defined by a recognized standards body, or, in the case of\line interfaces specified
|
||||
for a particular programming language, one that\line is widely used among developers working in that language.\line \line The "System Libraries" of an executable work include anything, other\line than the work as a whole, that (a) is included in the normal form of\line packaging
|
||||
a Major Component, but which is not part of that Major\line Component, and (b) serves only to enable use of the work with that\line Major Component, or to implement a Standard Interface for which an\line implementation is available to the public in source code form. A\line
|
||||
"Major Component", in this context, means a major essential component\line (kernel, window system, and so on) of the specific operating system\line (if any) on which the executable work runs, or a compiler used to\line produce the work, or an object code interpreter used
|
||||
to run it.\line \line The "Corresponding Source" for a work in object code form means all\line the source code needed to generate, install, and (for an executable\line work) run the object code and to modify the work, including scripts to\line control those activities. However
|
||||
, it does not include the work's\line System Libraries, or general-purpose tools or generally available free\line programs which are used unmodified in performing those activities but\line which are not part of the work. For example, Corresponding Source\line includes interf
|
||||
ace definition files associated with source files for\line the work, and the source code for shared libraries and dynamically\line linked subprograms that the work is specifically designed to require,\line such as by intimate data communication or control flow between th
|
||||
ose\line subprograms and other parts of the work.\line \line The Corresponding Source need not include anything that users\line can regenerate automatically from other parts of the Corresponding\line Source.\line \line The Corresponding Source for a work in source code form is that\line same
|
||||
work.\line \line 2. Basic Permissions.\line \line All rights granted under this License are granted for the term of\line copyright on the Program, and are irrevocable provided the stated\line conditions are met. This License explicitly affirms your unlimited\line permission to run the
|
||||
unmodified Program. The output from running a\line covered work is covered by this License only if the output, given its\line content, constitutes a covered work. This License acknowledges your\line rights of fair use or other equivalent, as provided by copyright law.
|
||||
\line \line You may make, run and propagate covered works that you do not\line convey, without conditions so long as your license otherwise remains\line in force. You may convey covered works to others for the sole purpose\line of having them make modifications exclusively for
|
||||
you, or provide you\line with facilities for running those works, provided that you comply with\line the terms of this License in conveying all material for which you do\line not control copyright. Those thus making or running the covered works\line for you must do so exclus
|
||||
ively on your behalf, under your direction\line and control, on terms that prohibit them from making any copies of\line your copyrighted material outside their relationship with you.\line \line Conveying under any other circumstances is permitted solely under\line the conditions
|
||||
stated below. Sublicensing is not allowed; section 10\line makes it unnecessary.\line \line 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\line \line No covered work shall be deemed part of an effective technological\line measure under any applicable law fulfillin
|
||||
g obligations under article\line 11 of the WIPO copyright treaty adopted on 20 December 1996, or\line similar laws prohibiting or restricting circumvention of such\line measures.\line \line When you convey a covered work, you waive any legal power to forbid\line circumvention of tech
|
||||
nological measures to the extent such circumvention\line is effected by exercising rights under this License with respect to\line the covered work, and you disclaim any intention to limit operation or\line modification of the work as a means of enforcing, against the wor
|
||||
k's\line users, your or third parties' legal rights to forbid circumvention of\line technological measures.\line \line 4. Conveying Verbatim Copies.\line \line You may convey verbatim copies of the Program's}
|
||||
\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1035\i0\b0 source code as you\line receive it, in any medium, provided that you conspicuously and\line appropriately publish on each copy an appropriate copyright notice;\line keep intact all notices stating that this License and any\line non-permissive terms added in accord with secti
|
||||
on 7 apply to the code;\line keep intact all notices of the absence of any warranty; and give all\line recipients a copy of this License along with the Program.\line \line You may charge any price or no price for each copy that you convey,\line and you may offer support or warra
|
||||
nty protection for a fee.\line \line 5. Conveying Modified Source Versions.\line \line You may convey a work based on the Program, or the modifications to\line produce it from the Program, in the form of source code under the\line terms of section 4, provided that you also meet all
|
||||
of these conditions:\line \line a) The work must carry prominent notices stating that you modified\line it, and giving a relevant date.\line \line b) The work must carry prominent notices stating that it is\line released under this License and any conditions added unde
|
||||
r section\line 7. This requirement modifies the requirement in section 4 to\line "keep intact all notices".\line \line c) You must license the entire work, as a whole, under this\line License to anyone who comes into possession of a copy. This\line License will th
|
||||
erefore apply, along with any applicable section 7\line additional terms, to the whole of the work, and all its parts,\line regardless of how they are packaged. This License gives no\line permission to license the work in any other way, but it does not\line i
|
||||
nvalidate such permission if you have separately received it.\line \line d) If the work has interactive user interfaces, each must display\line Appropriate Legal Notices; however, if the Program has interactive\line interfaces that do not display Appropriate Legal
|
||||
Notices, your\line work need not make them do so.\line \line A compilation of a covered work with other separate and independent\line works, which are not by their nature extensions of the covered work,\line and which are not combined with it such as to form a larger progra
|
||||
m,\line in or on a volume of a storage or distribution medium, is called an\line "aggregate" if the compilation and its resulting copyright are not\line used to limit the access or legal rights of the compilation's users\line beyond what the individual works permit. Inclusio
|
||||
n of a covered work\line in an aggregate does not cause this License to apply to the other\line parts of the aggregate.\line \line 6. Conveying Non-Source Forms.\line \line You may convey a covered work in object code form under the terms\line of sections 4 and 5, provided that you also
|
||||
convey the\line machine-readable Corresponding Source under the terms of this License,\line in one of these ways:\line \line a) Convey the object code in, or embodied in, a physical product\line (including a physical distribution medium), accompanied by the\line Correspond
|
||||
ing Source fixed on a durable physical medium\line customarily used for software interchange.\line \line b) Convey the object code in, or embodied in, a physical product\line (including a physical distribution medium), accompanied by a\line written offer, valid for
|
||||
at least three years and valid for as\line long as you offer spare parts or customer support for that product\line model, to give anyone who possesses the object code either (1) a\line copy of the Corresponding Source for all the software in the\line product
|
||||
that is covered by this License, on a durable physical\line medium customarily used for software interchange, for a price no\line more than your reasonable cost of physically performing this\line conveying of source, or (2) access to copy the\line Correspondin
|
||||
g Source from a network server at no charge.\line \line c) Convey individual copies of the object code with a copy of the\line written offer to provide the Corresponding Source. This\line alternative is allowed only occasionally and noncommercially, and\line only
|
||||
if you received the object code with such an offer, in accord\line with subsection 6b.\line \line d) Convey the object code by offering access from a designated\line place (gratis or for a charge), and offer equivalent access to the\line Corresponding Source in the
|
||||
same way through the same place at no\line further charge. You need not require recipients to copy the\line Corresponding Source along with the object code. If the place to\line copy the object code is a network server, the Corresponding Source\line may be
|
||||
on a different server (operated by you or a third party)\line that supports equivalent copying facilities, provided you maintain\line clear directions next to the object code saying where to find the\line Corresponding Source. Regardless of what server hosts
|
||||
the\line Corresponding Source, you remain obligated to ensure that it is\line available for as long as needed to satisfy these requirements.\line \line e) Convey the object code using peer-to-peer transmission, provided\line you inform other peers where the object
|
||||
code and Corresponding\line Source of the work are being offered to the general public at no\line charge under subsection 6d.\line \line A separable portion of the object code, whose source code is excluded\line from the Corresponding Source as a System Library, need no
|
||||
t be\line included in conveying the object code work.\line \line A "User Product" is either (1) a "consumer product", which means any\line tangible personal property which is normally used for personal, family,\line or household purposes, or (2) anything designed or sold for inc
|
||||
orporation\line into a dwelling. In determining whether a product is a consumer product,\line doubtful cases shall be resolved in favor of coverage. For a particular\line product received by a particular user, "normally used" refers to a\line typical or common use of that c
|
||||
lass of product, regardless of the status\line of the particular user or of the way in which the particular user\line actually uses, or expects or is expected to use, the product. A product\line is a consumer product regardless of whether the product has substantial\line com
|
||||
mercial, industrial or non-consumer uses, unless such uses represent\line the only significant mode of use of the product.\line \line "Installation Information" for a User Product means any methods,\line procedures, authorization keys, or other information required to insta
|
||||
ll\line and execute modified versions of a covered work in that User Product from\line a modified version of its Corresponding Source. The information must\line suffice to ensure that the continued functioning of the modified object\line code is in no case prevented or inter
|
||||
fered with solely because\line modification has been made.\line \line If you convey an object code work under this section in, or with, or\line specifically for use in, a User Product, and the conveying occurs as\line part of a transaction in which the right of possession and us
|
||||
e of the\line User Product is transferred to the recipient in perpetuity or for a\line fixed term (regardless of how the transaction is characterized), the\line Corresponding Source conveyed under this section must be accompanied\line by the Installation Information. But thi
|
||||
s requirement does not apply\line if neither you nor any third party retains the ability to install\line modified object code on the User Product (for example, the work has\line been installed in ROM).\line \line The requirement to provide Installation Information does not inclu
|
||||
de a\line requirement to continue to provide support service, warranty, or updates\line for a work that has been modified or installed by the recipient, or for\line the User Product in which it has been modified or installed. Access to a\line network may be denied when the m
|
||||
odification itself materially and\line adversely affects the operation of the network or violates the rules and\line protocols for communication across the network.\line \line Corresponding Source conveyed, and Installation Information provided,\line in accord with this section
|
||||
must be in a format that is publicly\line documented (and with an implementation available to the public in\line source code form), and must require no special password or key for\line unpacking, reading or copying.\line \line 7. Additional Terms.\line \line "Additional permissions" are
|
||||
terms that supplement the terms of this\line License by making exceptions from one or more of its conditions.\line Additional permissions that are applicable to the entire Program shall\line be treated as though they were included in this License, to the extent\line that the
|
||||
y are valid under applicable law. If additional permissions\line apply only to part of the Program, that part may be used separately\line under those permissions, but the entire Program remains governed by\line this License without regard to the additional permissions.\line
|
||||
\line When you convey a copy of a covered work, you may at your option\line remove any additional permissions from that copy, or from any part of\line it. (Additional permissions may be written to require their own\line removal in certain cases when you modify the work.)
|
||||
You may place\line additional permissions on material, added by you to a covered work,\line for which you have or can give appropriate copyright permission.\line \line Notwithstanding any other provision of this License, for material you\line add to a covered work, you may (if a
|
||||
uthorized by the copyright holders of\line that material) supplement the terms of this License with terms:\line \line a) Disclaiming warranty or limiting liability differently from the\line terms of sections 15 and 16 of this License; or\line \line b) Requiring preservation
|
||||
of specified reasonable legal notices or\line author attributions in that material or in the Appropriate Legal\line Notices displayed by works containing it; or\line \line c) Prohibiting misrepresentation of the origin of that material, or\line requiring that modi
|
||||
fied versions of such material be marked in\line reasonable ways as different from the original version; or\line \line d) Limiting the use for publicity purposes of names of licensors or\line authors of the material; or\line \line e) Declining to grant rights under trad
|
||||
emark law for use of some\line trade names, trademarks, or service marks; or\line \line f) Requiring indemnification of licensors and authors of that\line material by anyone who conveys}
|
||||
\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1035\i0\b0 the material (or modified versions of\line it) with contractual assumptions of liability to the recipient, for\line any liability that these contractual assumptions directly impose on\line those licensors and authors.\line \line All other non-permissive additional t
|
||||
erms are considered "further\line restrictions" within the meaning of section 10. If the Program as you\line received it, or any part of it, contains a notice stating that it is\line governed by this License along with a term that is a further\line restriction, you may remov
|
||||
e that term. If a license document contains\line a further restriction but permits relicensing or conveying under this\line License, you may add to a covered work material governed by the terms\line of that license document, provided that the further restriction does\line no
|
||||
t survive such relicensing or conveying.\line \line If you add terms to a covered work in accord with this section, you\line must place, in the relevant source files, a statement of the\line additional terms that apply to those files, or a notice indicating\line where to find th
|
||||
e applicable terms.\line \line Additional terms, permissive or non-permissive, may be stated in the\line form of a separately written license, or stated as exceptions;\line the above requirements apply either way.\line \line 8. Termination.\line \line You may not propagate or modify a cove
|
||||
red work except as expressly\line provided under this License. Any attempt otherwise to propagate or\line modify it is void, and will automatically terminate your rights under\line this License (including any patent licenses granted under the third\line paragraph of section
|
||||
11).\line \line However, if you cease all violation of this License, then your\line license from a particular copyright holder is reinstated (a)\line provisionally, unless and until the copyright holder explicitly and\line finally terminates your license, and (b) permanently, if
|
||||
the copyright\line holder fails to notify you of the violation by some reasonable means\line prior to 60 days after the cessation.\line \line Moreover, your license from a particular copyright holder is\line reinstated permanently if the copyright holder notifies you of the\line vio
|
||||
lation by some reasonable means, this is the first time you have\line received notice of violation of this License (for any work) from that\line copyright holder, and you cure the violation prior to 30 days after\line your receipt of the notice.\line \line Termination of your ri
|
||||
ghts under this section does not terminate the\line licenses of parties who have received copies or rights from you under\line this License. If your rights have been terminated and not permanently\line reinstated, you do not qualify to receive new licenses for the same\line
|
||||
material under section 10.\line \line 9. Acceptance Not Required for Having Copies.\line \line You are not required to accept this License in order to receive or\line run a copy of the Program. Ancillary propagation of a covered work\line occurring solely as a consequence of using
|
||||
peer-to-peer transmission\line to receive a copy likewise does not require acceptance. However,\line nothing other than this License grants you permission to propagate or\line modify any covered work. These actions infringe copyright if you do\line not accept this License.
|
||||
Therefore, by modifying or propagating a\line covered work, you indicate your acceptance of this License to do so.\line \line 10. Automatic Licensing of Downstream Recipients.\line \line Each time you convey a covered work, the recipient automatically\line receives a license from
|
||||
the original licensors, to run, modify and\line propagate that work, subject to this License. You are not responsible\line for enforcing compliance by third parties with this License.\line \line An "entity transaction" is a transaction transferring control of an\line organizat
|
||||
ion, or substantially all assets of one, or subdividing an\line organization, or merging organizations. If propagation of a covered\line work results from an entity transaction, each party to that\line transaction who receives a copy of the work also receives whatever\line l
|
||||
icenses to the work the party's predecessor in interest had or could\line give under the previous paragraph, plus a right to possession of the\line Corresponding Source of the work from the predecessor in interest, if\line the predecessor has it or can get it with reason
|
||||
able efforts.\line \line You may not impose any further restrictions on the exercise of the\line rights granted or affirmed under this License. For example, you may\line not impose a license fee, royalty, or other charge for exercise of\line rights granted under this License, a
|
||||
nd you may not initiate litigation\line (including a cross-claim or counterclaim in a lawsuit) alleging that\line any patent claim is infringed by making, using, selling, offering for\line sale, or importing the Program or any portion of it.\line \line 11. Patents.\line \line A "contrib
|
||||
utor" is a copyright holder who authorizes use under this\line License of the Program or a work on which the Program is based. The\line work thus licensed is called the contributor's "contributor version".\line \line A contributor's "essential patent claims" are all patent
|
||||
claims\line owned or controlled by the contributor, whether already acquired or\line hereafter acquired, that would be infringed by some manner, permitted\line by this License, of making, using, or selling its contributor version,\line but do not include claims that would be
|
||||
infringed only as a\line consequence of further modification of the contributor version. For\line purposes of this definition, "control" includes the right to grant\line patent sublicenses in a manner consistent with the requirements of\line this License.\line \line Each contributo
|
||||
r grants you a non-exclusive, worldwide, royalty-free\line patent license under the contributor's essential patent claims, to\line make, use, sell, offer for sale, import and otherwise run, modify and\line propagate the contents of its contributor version.\line \line In the foll
|
||||
owing three paragraphs, a "patent license" is any express\line agreement or commitment, however denominated, not to enforce a patent\line (such as an express permission to practice a patent or covenant not to\line sue for patent infringement). To "grant" such a patent l
|
||||
icense to a\line party means to make such an agreement or commitment not to enforce a\line patent against the party.\line \line If you convey a covered work, knowingly relying on a patent license,\line and the Corresponding Source of the work is not available for anyone\line to copy,
|
||||
free of charge and under the terms of this License, through a\line publicly available network server or other readily accessible means,\line then you must either (1) cause the Corresponding Source to be so\line available, or (2) arrange to deprive yourself of the benefi
|
||||
t of the\line patent license for this particular work, or (3) arrange, in a manner\line consistent with the requirements of this License, to extend the patent\line license to downstream recipients. "Knowingly relying" means you have\line actual knowledge that, but for the pa
|
||||
tent license, your conveying the\line covered work in a country, or your recipient's use of the covered work\line in a country, would infringe one or more identifiable patents in that\line country that you have reason to believe are valid.\line \line If, pursuant to or in connec
|
||||
tion with a single transaction or\line arrangement, you convey, or propagate by procuring conveyance of, a\line covered work, and grant a patent license to some of the parties\line receiving the covered work authorizing them to use, propagate, modify\line or convey a specific
|
||||
copy of the covered work, then the patent license\line you grant is automatically extended to all recipients of the covered\line work and works based on it.\line \line A patent license is "discriminatory" if it does not include within\line the scope of its coverage, prohibits t
|
||||
he exercise of, or is\line conditioned on the non-exercise of one or more of the rights that are\line specifically granted under this License. You may not convey a covered\line work if you are a party to an arrangement with a third party that is\line in the business of distr
|
||||
ibuting software, under which you make payment\line to the third party based on the extent of your activity of conveying\line the work, and under which the third party grants, to any of the\line parties who would receive the covered work from you, a discriminatory\line patent
|
||||
license (a) in connection with copies of the covered work\line conveyed by you (or copies made from those copies), or (b) primarily\line for and in connection with specific products or compilations that\line contain the covered work, unless you entered into that arrange
|
||||
ment,\line or that patent license was granted, prior to 28 March 2007.\line \line Nothing in this License shall be construed as excluding or limiting\line any implied license or other defenses to infringement that may\line otherwise be available to you under applicable patent la
|
||||
w.\line \line 12. No Surrender of Others' Freedom.\line \line If conditions are imposed on you (whether by court order, agreement or\line otherwise) that contradict the conditions of this License, they do not\line excuse you from the conditions of this License. If you cannot conve
|
||||
y a\line covered work so as to satisfy simultaneously your obligations under this\line License and any other pertinent obligations, then as a consequence you may\line not convey it at all. For example, if you agree to terms that obligate you\line to collect a royalty for fur
|
||||
ther conveying from those to whom you convey\line the Program, the only way you could satisfy both those terms and this\line License would be to refrain entirely from conveying the Program.\line \line 13. Use with the GNU Affero General Public License.\line \line Notwithstanding an
|
||||
y other provision of this License, you have\line permission to link or combine any covered work with a work licensed\line under version 3 of the GNU Affero General Public License into a single\line combined work, and to convey the resulting work. The terms of this\line Licen
|
||||
se will continue to apply to the part which is the covered work,\line but the special requirements of the GNU Affero General Public License,\line section 13, concerning interaction through a network will apply to the\line combination as such.\line \line 14. Revised Versions of t
|
||||
his License.\line \line The Free Software Foundation may publish revised and/or new versions of\line the GNU General Public License from time to time. Such new versions will\line be similar}
|
||||
\par \pard\plain \ltrpar\s1\cf0{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\rtlch\af5\afs24\lang255\ltrch\dbch\af3\langfe255\hich\f0\fs24\lang1035\loch\f0\fs24\lang1035 {\rtlch \ltrch\loch }{\rtlch \ltrch\loch\f0\fs24\lang1035\i0\b0 in spirit to the present version, but may differ in detail to\line address new problems or concerns.\line \line Each version is given a distinguishing version number. If the\line Program specifies that a certain numbered version of the GNU General\line Public License "or any l
|
||||
ater version" applies to it, you have the\line option of following the terms and conditions either of that numbered\line version or of any later version published by the Free Software\line Foundation. If the Program does not specify a version number of the\line GNU General P
|
||||
ublic License, you may choose any version ever published\line by the Free Software Foundation.\line \line If the Program specifies that a proxy can decide which future\line versions of the GNU General Public License can be used, that proxy's\line public statement of acceptance o
|
||||
f a version permanently authorizes you\line to choose that version for the Program.\line \line Later license versions may give you additional or different\line permissions. However, no additional obligations are imposed on any\line author or copyright holder as a result of your
|
||||
choosing to follow a\line later version.\line \line 15. Disclaimer of Warranty.\line \line THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\line APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\line HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
|
||||
"AS IS" WITHOUT WARRANTY\line OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\line THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\line PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\line IS WITH YOU.
|
||||
SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\line ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\line \line 16. Limitation of Liability.\line \line IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\line WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PAR
|
||||
TY WHO MODIFIES AND/OR CONVEYS\line THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\line GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\line USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\line DA
|
||||
TA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\line PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\line EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\line SUCH DAMAGES.\line \line 17. Interpretation
|
||||
of Sections 15 and 16.\line \line If the disclaimer of warranty and limitation of liability provided\line above cannot be given local legal effect according to their terms,\line reviewing courts shall apply local law that most closely approximates\line an absolute waiver of all
|
||||
civil liability in connection with the\line Program, unless a warranty or assumption of liability accompanies a\line copy of the Program in return for a fee.\line \line END OF TERMS AND CONDITIONS\line \line How to Apply These Terms to Your New Programs
|
||||
\line \line If you develop a new program, and you want it to be of the greatest\line possible use to the public, the best way to achieve this is to make it\line free software which everyone can redistribute and change under these terms.\line \line To do so, attach the following not
|
||||
ices to the program. It is safest\line to attach them to the start of each source file to most effectively\line state the exclusion of warranty; and each file should have at least\line the "copyright" line and a pointer to where the full notice is found.\line \line <one line
|
||||
to give the program's name and a brief idea of what it does.>\line Copyright (C) <year> <name of author>\line \line This program is free software: you can redistribute it and/or modify\line it under the terms of the GNU General Public License as published by\line
|
||||
the Free Software Foundation, either version 3 of the License, or\line (at your option) any later version.\line \line This program is distributed in the hope that it will be useful,\line but WITHOUT ANY WARRANTY; without even the implied warranty of\line MERCHANTAB
|
||||
ILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\line GNU General Public License for more details.\line \line You should have received a copy of the GNU General Public License\line along with this program. If not, see <http://www.gnu.org/licenses/>.\line \line Also add
|
||||
information on how to contact you by electronic and paper mail.\line \line If the program does terminal interaction, make it output a short\line notice like this when it starts in an interactive mode:\line \line <program> Copyright (C) <year> <name of author>\line This prog
|
||||
ram comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\line This is free software, and you are welcome to redistribute it\line under certain conditions; type `show c' for details.\line \line The hypothetical commands `show w' and `show c' should show the ap
|
||||
propriate\line parts of the General Public License. Of course, your program's commands\line might be different; for a GUI interface, you would use an "about box".\line \line You should also get your employer (if you work as a programmer) or school,\line if any, to sign a "copyr
|
||||
ight disclaimer" for the program, if necessary.\line For more information on this, and how to apply and follow the GNU GPL, see\line <http://www.gnu.org/licenses/>.\line \line The GNU General Public License does not permit incorporating your program\line into proprietary program
|
||||
s. If your program is a subroutine library, you\line may consider it more useful to permit linking proprietary applications with\line the library. If this is what you want to do, use the GNU Lesser General\line Public License instead of this License. But first, please
|
||||
read\line <http://www.gnu.org/philosophy/why-not-lgpl.html>.\line }
|
||||
\par }
|
102
osx/info.plist
Normal file
@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>qtox.icns</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>qtox</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>qTox</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>qTox</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0-EXPERIMENTIAL</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>im.tox.qtox</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Tox URL</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>tox</string>
|
||||
</array>
|
||||
<key>CFBundleURLIconFile</key>
|
||||
<string>qtox_profile.icns</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>tox</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Tox profile</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>qtox_profile.icns</string>
|
||||
<key>CFBundleTypeMIMETypes</key>
|
||||
<array>
|
||||
<string>application/x-tox.profile</string>
|
||||
</array>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.tox</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>public.tox</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>com.apple.ostype</key>
|
||||
<string>TOX</string>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>tox</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<string>tox/x-profile</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en_US</string>
|
||||
<string>en</string>
|
||||
<string>bg_BG</string>
|
||||
<string>de_DE</string>
|
||||
<string>fi_FI</string>
|
||||
<string>fr_FR</string>
|
||||
<string>it_IT</string>
|
||||
<string>pl_PL</string>
|
||||
<string>ru_RU</string>
|
||||
<string>uk_UA</string>
|
||||
<string>sv</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
27
osx/makedist.sh
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PWD=`pwd`
|
||||
|
||||
echo "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>
|
||||
<installer-gui-script minSpecVersion=\"1\">
|
||||
<pkg-ref id=\"im.tox.qtox\"/>
|
||||
<options hostArchitectures=\"x86_64\"/>
|
||||
<title>qTox</title>
|
||||
<license file=\"$PWD/gplv3.rtf\"/>
|
||||
<welcome file=\"$PWD/welcome.txt\"/>
|
||||
<domains enable_currentUserHome=\"true\" enable_localSystem=\"false\" enable_anywhere=\"false\"/>
|
||||
<options customize=\"never\" require-scripts=\"true\"/>
|
||||
<choices-outline>
|
||||
<line choice=\"default\">
|
||||
<line choice=\"im.tox.qtox\"/>
|
||||
</line>
|
||||
</choices-outline>
|
||||
<allowed-os-versions>
|
||||
<os-version min=\"10.7\"/>
|
||||
</allowed-os-versions>
|
||||
<choice id=\"default\"/>
|
||||
<choice id=\"im.tox.qtox\" visible=\"false\">
|
||||
<pkg-ref id=\"im.tox.qtox\"/>
|
||||
</choice>
|
||||
<pkg-ref id=\"im.tox.qtox\" version=\"1\" onConclusion=\"none\">qtox.pkg</pkg-ref>
|
||||
</installer-gui-script>" > distribution.xml
|
23
osx/updater/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
13
osx/updater/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
The qTox OS X updater is a mix of objective C and Go compiled as static binaries used do effortless updates in the background.
|
||||
|
||||
It uses Objective C to access Apples own security framework and call some long dead APIs in order to give the statically linked go updater permissions to install the latest build without prompting the user for every file.
|
||||
|
||||
* Release commits: ``https://github.com/Tox/qTox_updater``
|
||||
* Development commits: ``https://github.mit.edu/sean-2/updater``
|
||||
|
||||
Compiling:
|
||||
|
||||
* ```clang qtox_sudo.m -framework corefoundation -framework security -framework cocoa -Os -o qtox_sudo```
|
||||
* ```go build updater.go```
|
||||
|
||||
(Starting with this commit all commits will be signed with [this key](http://pgp.mit.edu/pks/lookup?op=get&search=0x13D2043169D25DF4).)
|
273
osx/updater/qtox_sudo.m
Normal file
@ -0,0 +1,273 @@
|
||||
// Modifications listed here GPLv3: https://gist.githubusercontent.com/stqism/2e82352026915f8f6ab3/raw/fca6f6f16fb8d61a64b6053dacf50c3433c2e0af/gistfile1.txt
|
||||
//
|
||||
// Copyright 2009 Performant Design, LLC. All rights reserved.
|
||||
// Copyright (C) 2014 Tox Foundation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by the Free
|
||||
// Software Foundation, either version 3 of the License, or (at your option)
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
// more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <Security/Authorization.h>
|
||||
#include <Security/AuthorizationTags.h>
|
||||
|
||||
char *addFileToPath(const char *path, const char *filename) {
|
||||
char *outbuf;
|
||||
char *lc;
|
||||
|
||||
lc = (char *)path + strlen(path) - 1;
|
||||
|
||||
if (lc < path || *lc != '/') {
|
||||
lc = NULL;
|
||||
}
|
||||
|
||||
while (*filename == '/') {
|
||||
filename++;
|
||||
}
|
||||
|
||||
outbuf = malloc(strlen(path) + strlen(filename) + 1 + (lc == NULL ? 1 : 0));
|
||||
|
||||
sprintf(outbuf, "%s%s%s", path, (lc == NULL) ? "/" : "", filename);
|
||||
|
||||
return outbuf;
|
||||
}
|
||||
|
||||
int isExecFile(const char *name) {
|
||||
struct stat s;
|
||||
|
||||
return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode));
|
||||
}
|
||||
|
||||
char *which(const char *filename)
|
||||
{
|
||||
char *path, *p, *n;
|
||||
|
||||
path = getenv("PATH");
|
||||
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = path = strdup(path);
|
||||
|
||||
while (p) {
|
||||
n = strchr(p, ':');
|
||||
|
||||
if (n) {
|
||||
*n++ = '\0';
|
||||
}
|
||||
|
||||
if (*p != '\0') {
|
||||
p = addFileToPath(p, filename);
|
||||
|
||||
if (isExecFile(p)) {
|
||||
free(path);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
p = n;
|
||||
}
|
||||
|
||||
free(path);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cocoaSudo(char *executable, char *commandArgs[], char *icon, char *prompt) {
|
||||
int retVal = 1;
|
||||
OSStatus status;
|
||||
AuthorizationRef authRef;
|
||||
|
||||
AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0};
|
||||
AuthorizationRights rightSet = {1, &right};
|
||||
|
||||
AuthorizationEnvironment myAuthorizationEnvironment;
|
||||
AuthorizationItem kAuthEnv[2];
|
||||
myAuthorizationEnvironment.items = kAuthEnv;
|
||||
|
||||
AuthorizationFlags flags = kAuthorizationFlagDefaults;
|
||||
|
||||
|
||||
if (prompt && icon) {
|
||||
kAuthEnv[0].name = kAuthorizationEnvironmentPrompt;
|
||||
kAuthEnv[0].valueLength = strlen(prompt);
|
||||
kAuthEnv[0].value = prompt;
|
||||
kAuthEnv[0].flags = 0;
|
||||
|
||||
kAuthEnv[1].name = kAuthorizationEnvironmentIcon;
|
||||
kAuthEnv[1].valueLength = strlen(icon);
|
||||
kAuthEnv[1].value = icon;
|
||||
kAuthEnv[1].flags = 0;
|
||||
|
||||
myAuthorizationEnvironment.count = 2;
|
||||
}
|
||||
else if (prompt) {
|
||||
kAuthEnv[0].name = kAuthorizationEnvironmentPrompt;
|
||||
kAuthEnv[0].valueLength = strlen(prompt);
|
||||
kAuthEnv[0].value = prompt;
|
||||
kAuthEnv[0].flags = 0;
|
||||
|
||||
myAuthorizationEnvironment.count = 1;
|
||||
}
|
||||
else if (icon) {
|
||||
kAuthEnv[0].name = kAuthorizationEnvironmentIcon;
|
||||
kAuthEnv[0].valueLength = strlen(icon);
|
||||
kAuthEnv[0].value = icon;
|
||||
kAuthEnv[0].flags = 0;
|
||||
|
||||
myAuthorizationEnvironment.count = 1;
|
||||
}
|
||||
else {
|
||||
myAuthorizationEnvironment.count = 0;
|
||||
}
|
||||
|
||||
status = AuthorizationCreate(NULL, &myAuthorizationEnvironment, flags, &authRef);
|
||||
|
||||
if (status != errAuthorizationSuccess) {
|
||||
NSLog(@"Could not create authorization reference object.");
|
||||
status = errAuthorizationBadAddress;
|
||||
}
|
||||
else {
|
||||
flags = kAuthorizationFlagDefaults |
|
||||
kAuthorizationFlagInteractionAllowed |
|
||||
kAuthorizationFlagPreAuthorize |
|
||||
kAuthorizationFlagExtendRights;
|
||||
|
||||
status = AuthorizationCopyRights(authRef, &rightSet, &myAuthorizationEnvironment, flags, NULL);
|
||||
}
|
||||
|
||||
if (status == errAuthorizationSuccess) {
|
||||
FILE *ioPipe;
|
||||
char buffer[1024];
|
||||
int bytesRead;
|
||||
|
||||
flags = kAuthorizationFlagDefaults;
|
||||
status = AuthorizationExecuteWithPrivileges(authRef, executable, flags, commandArgs, &ioPipe);
|
||||
|
||||
/* Just pipe processes' stdout to our stdout for now; hopefully can add stdin pipe later as well */
|
||||
|
||||
for (;;) {
|
||||
bytesRead = fread(buffer, sizeof(char), 1024, ioPipe);
|
||||
|
||||
if (bytesRead < 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
write(STDOUT_FILENO, buffer, bytesRead * sizeof(char));
|
||||
}
|
||||
|
||||
pid_t pid;
|
||||
int pidStatus;
|
||||
|
||||
do {
|
||||
pid = wait(&pidStatus);
|
||||
}
|
||||
while (pid != -1);
|
||||
|
||||
if (status == errAuthorizationSuccess) {
|
||||
retVal = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
|
||||
authRef = NULL;
|
||||
|
||||
if (status != errAuthorizationCanceled) {
|
||||
// pre-auth failed
|
||||
NSLog(@"Pre-auth failed");
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void usage(char *appNameFull) {
|
||||
char *appName = strrchr(appNameFull, '/');
|
||||
|
||||
if (appName == NULL) {
|
||||
appName = appNameFull;
|
||||
}
|
||||
else {
|
||||
appName++;
|
||||
}
|
||||
|
||||
fprintf(stderr, "usage: %s command\n", appName);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
int retVal = 1;
|
||||
int programArgsStartAt = 1;
|
||||
char *icon = NULL;
|
||||
char *prompt = NULL;
|
||||
|
||||
if (programArgsStartAt >= argc) {
|
||||
usage(argv[0]);
|
||||
}
|
||||
else {
|
||||
char *executable;
|
||||
|
||||
if (strchr(argv[programArgsStartAt], '/')) {
|
||||
executable = isExecFile(argv[programArgsStartAt]) ? strdup(argv[programArgsStartAt]) : NULL;
|
||||
}
|
||||
else {
|
||||
executable = which(argv[programArgsStartAt]);
|
||||
}
|
||||
|
||||
if (executable) {
|
||||
char **commandArgs = malloc((argc - programArgsStartAt) * sizeof(char**));
|
||||
|
||||
memcpy(commandArgs, argv + programArgsStartAt + 1, (argc - programArgsStartAt - 1) * sizeof(char**));
|
||||
|
||||
commandArgs[argc - programArgsStartAt - 1] = NULL;
|
||||
|
||||
retVal = cocoaSudo(executable, commandArgs, icon, prompt);
|
||||
|
||||
free(commandArgs);
|
||||
free(executable);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Unable to find %s\n", argv[programArgsStartAt]);
|
||||
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (prompt) {
|
||||
free(prompt);
|
||||
}
|
||||
|
||||
[pool release];
|
||||
|
||||
return retVal;
|
||||
}
|
133
osx/updater/updater.go
Normal file
@ -0,0 +1,133 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"syscall"
|
||||
|
||||
"bitbucket.org/kardianos/osext"
|
||||
)
|
||||
|
||||
var custom_user string
|
||||
|
||||
func fs_type(path string) int {
|
||||
//name := "FileOrDir"
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return -1
|
||||
}
|
||||
defer f.Close()
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return -1
|
||||
}
|
||||
switch mode := fi.Mode(); {
|
||||
case mode.IsDir():
|
||||
return 0
|
||||
case mode.IsRegular():
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
func install(path string, pathlen int) int {
|
||||
files, _ := ioutil.ReadDir(path)
|
||||
|
||||
for _, file := range files {
|
||||
if fs_type(path+file.Name()) == 1 {
|
||||
|
||||
addpath := ""
|
||||
if len(path) != pathlen {
|
||||
addpath = path[pathlen:len(path)]
|
||||
}
|
||||
|
||||
fmt.Print("Installing: ")
|
||||
fmt.Println("/Applications/qtox.app/Contents/" + addpath + file.Name())
|
||||
if _, err := os.Stat("/Applications/qtox.app/Contents/" + file.Name()); os.IsNotExist(err) {
|
||||
newfile := exec.Command("/usr/libexec/authopen", "-c", "-x", "-m", "drwxrwxr-x+", "/Applications/qtox.app/Contents/"+addpath+file.Name())
|
||||
newfile.Run()
|
||||
}
|
||||
|
||||
cat := exec.Command("/bin/cat", path+file.Name())
|
||||
|
||||
auth := exec.Command("/usr/libexec/authopen", "-w", "/Applications/qtox.app/Contents/"+addpath+file.Name())
|
||||
auth.Stdin, _ = cat.StdoutPipe()
|
||||
auth.Stdout = os.Stdout
|
||||
auth.Stderr = os.Stderr
|
||||
_ = auth.Start()
|
||||
_ = cat.Run()
|
||||
_ = auth.Wait()
|
||||
|
||||
} else {
|
||||
install(path+file.Name()+"/", pathlen)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
syscall.Setuid(0)
|
||||
usr, e := user.Current()
|
||||
if e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
|
||||
CHECK:
|
||||
if usr.Name != "System Administrator" {
|
||||
fmt.Println("Not running as root, relaunching")
|
||||
|
||||
appdir, _ := osext.Executable()
|
||||
appdir_len := len(appdir)
|
||||
sudo_path := appdir[0:(appdir_len-7)] + "qtox_sudo" //qtox_sudo is a fork of cocoasudo with all of its flags and other features stripped out
|
||||
|
||||
if _, err := os.Stat(sudo_path); os.IsNotExist(err) {
|
||||
fmt.Println("Error: No qtox_sudo binary installed, falling back")
|
||||
custom_user = usr.Name
|
||||
usr.Name = "System Administrator"
|
||||
goto CHECK
|
||||
}
|
||||
|
||||
relaunch := exec.Command(sudo_path, appdir, usr.Name)
|
||||
relaunch.Stdout = os.Stdout
|
||||
relaunch.Stderr = os.Stderr
|
||||
relaunch.Run()
|
||||
return
|
||||
|
||||
} else {
|
||||
|
||||
if len(os.Args) > 1 || custom_user != "" {
|
||||
|
||||
if custom_user == "" {
|
||||
custom_user = os.Args[1]
|
||||
}
|
||||
|
||||
update_dir := "/Users/" + custom_user + "/Library/Preferences/tox/update/"
|
||||
if _, err := os.Stat(update_dir); os.IsNotExist(err) {
|
||||
fmt.Println("Error: No update folder, is check for updates enabled?")
|
||||
return
|
||||
}
|
||||
fmt.Println("qTox Updater")
|
||||
|
||||
killqtox := exec.Command("/usr/bin/killall", "qtox")
|
||||
_ = killqtox.Run()
|
||||
|
||||
install(update_dir, len(update_dir))
|
||||
|
||||
os.RemoveAll(update_dir)
|
||||
fmt.Println("Update metadata wiped, launching qTox")
|
||||
launchqtox := exec.Command("/usr/bin/open", "-b", "im.tox.qtox")
|
||||
launchqtox.Run()
|
||||
|
||||
} else {
|
||||
fmt.Println("Error: no user passed")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
12
osx/welcome.txt
Normal file
@ -0,0 +1,12 @@
|
||||
Welcome to the qTox for OS X internal nightly installer!
|
||||
|
||||
Please report all bugs to https://support.libtoxcore.so
|
||||
|
||||
########################################################
|
||||
## WARNING: Install me to your user ONLY
|
||||
##
|
||||
## Failure to do so WILL break automatic updating
|
||||
## and you WILL be left with and old and
|
||||
## potentially broken qTox install!
|
||||
##
|
||||
########################################################
|
63
qtox.pro
@ -34,7 +34,8 @@ FORMS += \
|
||||
src/widget/form/loadhistorydialog.ui \
|
||||
src/widget/form/inputpassworddialog.ui \
|
||||
src/widget/form/setpassworddialog.ui \
|
||||
src/chatlog/content/filetransferwidget.ui
|
||||
src/chatlog/content/filetransferwidget.ui \
|
||||
src/widget/form/settings/advancedsettings.ui
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
@ -47,8 +48,24 @@ RESOURCES += res.qrc
|
||||
|
||||
GIT_VERSION = $$system(git rev-parse HEAD 2> /dev/null || echo "built without git")
|
||||
DEFINES += GIT_VERSION=\"\\\"$$quote($$GIT_VERSION)\\\"\"
|
||||
# date works on linux/mac, but it would hangs qmake on windows
|
||||
# This hack returns 0 on batch (windows), but executes "date +%s" or return 0 if it fails on bash (linux/mac)
|
||||
TIMESTAMP = $$system($1 2>null||echo 0||a;rm null;date +%s||echo 0) # I'm so sorry
|
||||
DEFINES += TIMESTAMP=$$TIMESTAMP
|
||||
DEFINES += LOG_TO_FILE
|
||||
|
||||
contains(DISABLE_PLATFORM_EXT, YES) {
|
||||
|
||||
} else {
|
||||
DEFINES += QTOX_PLATFORM_EXT
|
||||
}
|
||||
|
||||
contains(DISABLE_FILTER_AUDIO, YES) {
|
||||
|
||||
} else {
|
||||
DEFINES += QTOX_FILTER_AUDIO
|
||||
}
|
||||
|
||||
contains(JENKINS,YES) {
|
||||
INCLUDEPATH += ./libs/include/
|
||||
} else {
|
||||
@ -60,13 +77,15 @@ win32 {
|
||||
RC_FILE = windows/qtox.rc
|
||||
LIBS += -liphlpapi -L$$PWD/libs/lib -lsodium -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lvpx -lpthread
|
||||
LIBS += -L$$PWD/libs/lib -lopencv_core248 -lopencv_highgui248 -lopencv_imgproc248 -lOpenAL32 -lopus
|
||||
LIBS += -lz -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -ljpeg -ltiff -lpng -ljasper -lIlmImf -lHalf -lws2_32
|
||||
LIBS += -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -ljpeg -ltiff -lpng -ljasper -lIlmImf -lHalf -lws2_32 -lz
|
||||
} else {
|
||||
macx {
|
||||
BUNDLEID = im.tox.qtox
|
||||
ICON = img/icons/qtox.icns
|
||||
QMAKE_INFO_PLIST = res/info.plist
|
||||
QMAKE_INFO_PLIST = osx/info.plist
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui
|
||||
contains(DEFINES, QTOX_PLATFORM_EXT) { LIBS += -framework IOKit -framework CoreFoundation }
|
||||
contains(DEFINES, QTOX_FILTER_AUDIO) { LIBS += -lfilteraudio }
|
||||
} else {
|
||||
# If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
|
||||
contains(STATICPKG, YES) {
|
||||
@ -74,14 +93,25 @@ win32 {
|
||||
INSTALLS += target
|
||||
LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic
|
||||
LIBS += -Wl,-Bstatic -ljpeg -ltiff -lpng -ljasper -lIlmImf -lIlmThread -lIex -ldc1394 -lraw1394 -lHalf -lz -llzma -ljbig
|
||||
LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0
|
||||
|
||||
LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0
|
||||
} else {
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lvpx -lsodium -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
|
||||
}
|
||||
|
||||
contains(DEFINES, QTOX_PLATFORM_EXT) {
|
||||
LIBS += -lX11 -lXss
|
||||
}
|
||||
|
||||
contains(DEFINES, QTOX_FILTER_AUDIO) {
|
||||
contains(STATICPKG, YES) {
|
||||
LIBS += -Wl,-Bstatic -lfilteraudio
|
||||
} else {
|
||||
LIBS += -lfilteraudio
|
||||
}
|
||||
}
|
||||
|
||||
contains(JENKINS, YES) {
|
||||
LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxdns.a ./libs/lib/libtoxencryptsave.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a /usr/lib/libopencv_core.so /usr/lib/libopencv_highgui.so /usr/lib/libopencv_imgproc.so -lopenal -s
|
||||
LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxdns.a ./libs/lib/libtoxencryptsave.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a ./libs/lib/libfilteraudio.a /usr/lib/libopencv_core.so /usr/lib/libopencv_highgui.so /usr/lib/libopencv_imgproc.so -lopenal -lX11 -lXss -s
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,7 +182,9 @@ HEADERS += src/widget/form/addfriendform.h \
|
||||
src/chatlog/content/filetransferwidget.h \
|
||||
src/chatlog/chatmessage.h \
|
||||
src/chatlog/content/image.h \
|
||||
src/chatlog/customtextdocument.h
|
||||
src/chatlog/customtextdocument.h \
|
||||
src/widget/form/settings/advancedform.h \
|
||||
src/audio.h
|
||||
|
||||
SOURCES += \
|
||||
src/widget/form/addfriendform.cpp \
|
||||
@ -220,4 +252,19 @@ SOURCES += \
|
||||
src/chatlog/content/filetransferwidget.cpp \
|
||||
src/chatlog/chatmessage.cpp \
|
||||
src/chatlog/content/image.cpp \
|
||||
src/chatlog/customtextdocument.cpp
|
||||
src/chatlog/customtextdocument.cpp\
|
||||
src/widget/form/settings/advancedform.cpp \
|
||||
src/audio.cpp
|
||||
|
||||
contains(DEFINES, QTOX_FILTER_AUDIO) {
|
||||
HEADERS += src/audiofilterer.h
|
||||
SOURCES += src/audiofilterer.cpp
|
||||
}
|
||||
|
||||
contains(DEFINES, QTOX_PLATFORM_EXT) {
|
||||
HEADERS += src/platform/timer.h
|
||||
SOURCES += src/platform/timer_osx.cpp \
|
||||
src/platform/timer_win.cpp \
|
||||
src/platform/timer_x11.cpp
|
||||
}
|
||||
|
||||
|
312
res.qrc
@ -6,13 +6,28 @@
|
||||
<file alias="DejaVuSans.ttf">res/DejaVuSans.ttf</file>
|
||||
</qresource>
|
||||
<qresource prefix="/">
|
||||
<file>img/icon.png</file>
|
||||
<file>audio/notification.pcm</file>
|
||||
<file>audio/ToxicIncomingCall.pcm</file>
|
||||
<file>img/add.png</file>
|
||||
<file>img/avatar_mask.png</file>
|
||||
<file>img/contact.png</file>
|
||||
<file>img/contact_dark.png</file>
|
||||
<file>img/group.png</file>
|
||||
<file>img/group_2x.png</file>
|
||||
<file>img/group_button.png</file>
|
||||
<file>img/group_dark.png</file>
|
||||
<file>img/icon.png</file>
|
||||
<file>img/settings.png</file>
|
||||
<file>img/settings/av.png</file>
|
||||
<file>img/settings/general.png</file>
|
||||
<file>img/settings/identity.png</file>
|
||||
<file>img/settings/privacy.png</file>
|
||||
<file>img/status/dot_away.png</file>
|
||||
<file>img/status/dot_away_2x.png</file>
|
||||
<file>img/status/dot_away_notification.png</file>
|
||||
<file>img/status/dot_busy.png</file>
|
||||
<file>img/status/dot_busy_2x.png</file>
|
||||
<file>img/status/dot_busy_notification.png</file>
|
||||
<file>img/status/dot_groupchat.png</file>
|
||||
<file>img/status/dot_groupchat_newmessages.png</file>
|
||||
<file>img/status/dot_groupchat_notification.png</file>
|
||||
@ -22,156 +37,15 @@
|
||||
<file>img/status/dot_online.png</file>
|
||||
<file>img/status/dot_online_2x.png</file>
|
||||
<file>img/status/dot_online_notification.png</file>
|
||||
<file>img/add.png</file>
|
||||
<file>img/settings.png</file>
|
||||
<file>img/taskbar/dark/taskbar_online_2x.png</file>
|
||||
<file>img/taskbar/dark/taskbar_idle_2x.png</file>
|
||||
<file>img/taskbar/dark/taskbar_busy_2x.png</file>
|
||||
<file>img/taskbar/dark/taskbar_offline_2x.png</file>
|
||||
<file>img/taskbar/light/taskbar_online_2x.png</file>
|
||||
<file>img/taskbar/light/taskbar_idle_2x.png</file>
|
||||
<file>img/taskbar/light/taskbar_busy_2x.png</file>
|
||||
<file>img/taskbar/light/taskbar_offline_2x.png</file>
|
||||
<file>img/transfer.png</file>
|
||||
<file>ui/acceptFileButton/default.png</file>
|
||||
<file>ui/acceptFileButton/hover.png</file>
|
||||
<file>ui/acceptFileButton/pressed.png</file>
|
||||
<file>ui/acceptFileButton/style.css</file>
|
||||
<file>ui/callButton/callButton.css</file>
|
||||
<file>ui/callButton/callButton.png</file>
|
||||
<file>ui/callButton/callButtonDisabled.png</file>
|
||||
<file>ui/callButton/callButtonHover.png</file>
|
||||
<file>ui/callButton/callButtonPressed.png</file>
|
||||
<file>ui/callButton/callButtonRed.png</file>
|
||||
<file>ui/callButton/callButtonRedHover.png</file>
|
||||
<file>ui/callButton/callButtonRedPressed.png</file>
|
||||
<file>ui/callButton/callButtonYellow.png</file>
|
||||
<file>ui/callButton/callButtonYellowHover.png</file>
|
||||
<file>ui/callButton/callButtonYellowPressed.png</file>
|
||||
<file>ui/chatArea/chatArea.css</file>
|
||||
<file>ui/chatArea/innerStyle.css</file>
|
||||
<file>ui/chatArea/spinner.png</file>
|
||||
<file>ui/chatArea/info.png</file>
|
||||
<file>ui/chatArea/scrollBarDownArrow.png</file>
|
||||
<file>ui/chatArea/scrollBarDownArrowHover.png</file>
|
||||
<file>ui/chatArea/scrollBarDownArrowPressed.png</file>
|
||||
<file>ui/chatArea/scrollBarHandle.png</file>
|
||||
<file>ui/chatArea/scrollBarUpArrow.png</file>
|
||||
<file>ui/chatArea/scrollBarUpArrowHover.png</file>
|
||||
<file>ui/chatArea/scrollBarUpArrowPressed.png</file>
|
||||
<file>ui/emoteButton/emoteButton.css</file>
|
||||
<file>ui/emoteButton/emoteButton.png</file>
|
||||
<file>ui/emoteButton/emoteButtonHover.png</file>
|
||||
<file>ui/emoteButton/emoteButtonPressed.png</file>
|
||||
<file>ui/fileButton/fileButton.css</file>
|
||||
<file>ui/fileButton/fileButton.png</file>
|
||||
<file>ui/fileButton/fileButtonHover.png</file>
|
||||
<file>ui/fileButton/fileButtonPressed.png</file>
|
||||
<file>ui/fileButton/fileButtonDisabled.png</file>
|
||||
<file>ui/msgEdit/msgEdit.css</file>
|
||||
<file>ui/pauseFileButton/default.png</file>
|
||||
<file>ui/pauseFileButton/hover.png</file>
|
||||
<file>ui/pauseFileButton/pressed.png</file>
|
||||
<file>ui/pauseFileButton/style.css</file>
|
||||
<file>ui/sendButton/sendButton.css</file>
|
||||
<file>ui/sendButton/sendButton.png</file>
|
||||
<file>ui/sendButton/sendButtonHover.png</file>
|
||||
<file>ui/sendButton/sendButtonPressed.png</file>
|
||||
<file>ui/stopFileButton/default.png</file>
|
||||
<file>ui/stopFileButton/hover.png</file>
|
||||
<file>ui/stopFileButton/pressed.png</file>
|
||||
<file>ui/stopFileButton/style.css</file>
|
||||
<file>ui/videoButton/videoButton.css</file>
|
||||
<file>ui/videoButton/videoButton.png</file>
|
||||
<file>ui/videoButton/videoButtonDisabled.png</file>
|
||||
<file>ui/videoButton/videoButtonHover.png</file>
|
||||
<file>ui/videoButton/videoButtonPressed.png</file>
|
||||
<file>ui/videoButton/videoButtonRed.png</file>
|
||||
<file>ui/videoButton/videoButtonRedHover.png</file>
|
||||
<file>ui/videoButton/videoButtonRedPressed.png</file>
|
||||
<file>ui/videoButton/videoButtonYellow.png</file>
|
||||
<file>ui/videoButton/videoButtonYellowHover.png</file>
|
||||
<file>ui/videoButton/videoButtonYellowPressed.png</file>
|
||||
<file>img/group_dark.png</file>
|
||||
<file>ui/window/applicationIcon.png</file>
|
||||
<file>ui/friendList/friendList.css</file>
|
||||
<file>ui/window/window.css</file>
|
||||
<file>img/status/dot_busy.png</file>
|
||||
<file>img/status/dot_busy_2x.png</file>
|
||||
<file>img/status/dot_busy_notification.png</file>
|
||||
<file>translations/fr.qm</file>
|
||||
<file>translations/ru.qm</file>
|
||||
<file>ui/fileTransferWidget/fileTransferWidget.css</file>
|
||||
<file>ui/fileTransferInstance/red.css</file>
|
||||
<file>ui/fileTransferInstance/green.css</file>
|
||||
<file>ui/fileTransferInstance/grey.css</file>
|
||||
<file>ui/fileTransferInstance/yellow.css</file>
|
||||
<file>ui/fileTransferInstance/background_red.png</file>
|
||||
<file>ui/fileTransferInstance/background_yellow.png</file>
|
||||
<file>ui/fileTransferInstance/background_green.png</file>
|
||||
<file>ui/fileTransferInstance/background_grey.png</file>
|
||||
<file>ui/fileTransferInstance/pause_2x.png</file>
|
||||
<file>ui/fileTransferInstance/no_2x.png</file>
|
||||
<file>ui/fileTransferInstance/yes_2x.png</file>
|
||||
<file>ui/fileTransferInstance/arrow_white_2x.png</file>
|
||||
<file>ui/statusButton/dot_away.png</file>
|
||||
<file>ui/statusButton/dot_busy.png</file>
|
||||
<file>ui/statusButton/dot_idle.png</file>
|
||||
<file>ui/statusButton/dot_online.png</file>
|
||||
<file>ui/statusButton/statusButton.css</file>
|
||||
<file>ui/statusButton/menu_indicator.png</file>
|
||||
<file>translations/de.qm</file>
|
||||
<file>translations/it.qm</file>
|
||||
<file>ui/emoticonWidget/dot_page.png</file>
|
||||
<file>ui/emoticonWidget/dot_page_current.png</file>
|
||||
<file>ui/emoticonWidget/emoticonWidget.css</file>
|
||||
<file>ui/emoticonWidget/dot_page_hover.png</file>
|
||||
<file>ui/volButton/volButton.png</file>
|
||||
<file>ui/volButton/volButtonHover.png</file>
|
||||
<file>ui/volButton/volButtonPressed.png</file>
|
||||
<file>ui/micButton/micButton.png</file>
|
||||
<file>ui/micButton/micButtonDisabled.png</file>
|
||||
<file>ui/micButton/micButtonHover.png</file>
|
||||
<file>ui/micButton/micButtonPressed.png</file>
|
||||
<file>ui/micButton/micButton.css</file>
|
||||
<file>ui/volButton/volButton.css</file>
|
||||
<file>ui/fileTransferInstance/acceptFileButton.png</file>
|
||||
<file>ui/fileTransferInstance/pauseFileButton.png</file>
|
||||
<file>ui/fileTransferInstance/pauseGreyFileButton.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>
|
||||
<file>audio/notification.pcm</file>
|
||||
<file>audio/ToxicIncomingCall.pcm</file>
|
||||
<file>img/settings/general.png</file>
|
||||
<file>img/settings/identity.png</file>
|
||||
<file>img/settings/privacy.png</file>
|
||||
<file>img/settings/av.png</file>
|
||||
<file>translations/pl.qm</file>
|
||||
<file>translations/fi.qm</file>
|
||||
<file>translations/mannol.qm</file>
|
||||
<file>translations/uk.qm</file>
|
||||
<file>img/avatar_mask.png</file>
|
||||
<file>img/group_2x.png</file>
|
||||
<file>ui/chatroomWidgets/genericChatroomWidget.css</file>
|
||||
<file>ui/fileTransferInstance/sliverRTEdge.png</file>
|
||||
<file>ui/window/statusPanel.css</file>
|
||||
<file>ui/settings/mainContent.css</file>
|
||||
<file>ui/settings/mainHead.css</file>
|
||||
<file>translations/pirate.qm</file>
|
||||
<file>ui/chatArea/chatHead.css</file>
|
||||
<file>smileys/krepa098/angry.png</file>
|
||||
<file>smileys/krepa098/cool.png</file>
|
||||
<file>smileys/krepa098/crying.png</file>
|
||||
<file>smileys/krepa098/emoticons.xml</file>
|
||||
<file>smileys/krepa098/happy.png</file>
|
||||
<file>smileys/krepa098/Kappa.png</file>
|
||||
<file>smileys/krepa098/laugh_closed_eyes.png</file>
|
||||
<file>smileys/krepa098/laugh.png</file>
|
||||
<file>smileys/krepa098/plain.png</file>
|
||||
<file>smileys/krepa098/raw.svg</file>
|
||||
<file>smileys/krepa098/sad.png</file>
|
||||
<file>smileys/krepa098/scared.png</file>
|
||||
<file>smileys/krepa098/smile.png</file>
|
||||
<file>smileys/krepa098/stunned.png</file>
|
||||
<file>smileys/krepa098/tongue.png</file>
|
||||
<file>smileys/krepa098/uncertain.png</file>
|
||||
<file>smileys/krepa098/wink.png</file>
|
||||
<file>smileys/cylgom/angel.png</file>
|
||||
<file>smileys/cylgom/angry.png</file>
|
||||
<file>smileys/cylgom/beer.png</file>
|
||||
@ -227,8 +101,144 @@
|
||||
<file>smileys/cylgom/XD.png</file>
|
||||
<file>smileys/cylgom/XP.png</file>
|
||||
<file>smileys/cylgom/yawn.png</file>
|
||||
<file>smileys/krepa098/angry.png</file>
|
||||
<file>smileys/krepa098/cool.png</file>
|
||||
<file>smileys/krepa098/crying.png</file>
|
||||
<file>smileys/krepa098/emoticons.xml</file>
|
||||
<file>smileys/krepa098/happy.png</file>
|
||||
<file>smileys/krepa098/Kappa.png</file>
|
||||
<file>smileys/krepa098/laugh_closed_eyes.png</file>
|
||||
<file>smileys/krepa098/laugh.png</file>
|
||||
<file>smileys/krepa098/plain.png</file>
|
||||
<file>smileys/krepa098/raw.svg</file>
|
||||
<file>smileys/krepa098/sad.png</file>
|
||||
<file>smileys/krepa098/scared.png</file>
|
||||
<file>smileys/krepa098/smile.png</file>
|
||||
<file>smileys/krepa098/stunned.png</file>
|
||||
<file>smileys/krepa098/tongue.png</file>
|
||||
<file>smileys/krepa098/uncertain.png</file>
|
||||
<file>smileys/krepa098/wink.png</file>
|
||||
<file>translations/bg.qm</file>
|
||||
<file>translations/de.qm</file>
|
||||
<file>translations/es.qm</file>
|
||||
<file>translations/fi.qm</file>
|
||||
<file>translations/fr.qm</file>
|
||||
<file>translations/it.qm</file>
|
||||
<file>translations/mannol.qm</file>
|
||||
<file>translations/pirate.qm</file>
|
||||
<file>translations/pl.qm</file>
|
||||
<file>translations/ru.qm</file>
|
||||
<file>translations/sv.qm</file>
|
||||
<file>ui/fileTransferInstance/browse_path.png</file>
|
||||
<file>translations/uk.qm</file>
|
||||
<file>ui/acceptFileButton/default.png</file>
|
||||
<file>ui/acceptFileButton/hover.png</file>
|
||||
<file>ui/acceptFileButton/pressed.png</file>
|
||||
<file>ui/acceptFileButton/style.css</file>
|
||||
<file>ui/callButton/callButton.css</file>
|
||||
<file>ui/callButton/callButton.png</file>
|
||||
<file>ui/callButton/callButtonDisabled.png</file>
|
||||
<file>ui/callButton/callButtonHover.png</file>
|
||||
<file>ui/callButton/callButtonPressed.png</file>
|
||||
<file>ui/callButton/callButtonRed.png</file>
|
||||
<file>ui/callButton/callButtonRedHover.png</file>
|
||||
<file>ui/callButton/callButtonRedPressed.png</file>
|
||||
<file>ui/callButton/callButtonYellow.png</file>
|
||||
<file>ui/callButton/callButtonYellowHover.png</file>
|
||||
<file>ui/callButton/callButtonYellowPressed.png</file>
|
||||
<file>ui/chatArea/chatArea.css</file>
|
||||
<file>ui/chatArea/chatHead.css</file>
|
||||
<file>ui/chatArea/innerStyle.css</file>
|
||||
<file>ui/chatArea/spinner.png</file>
|
||||
<file>ui/chatArea/info.png</file>
|
||||
<file>ui/chatArea/scrollBarDownArrow.png</file>
|
||||
<file>ui/chatArea/scrollBarDownArrowHover.png</file>
|
||||
<file>ui/chatArea/scrollBarDownArrowPressed.png</file>
|
||||
<file>ui/chatArea/scrollBarHandle.png</file>
|
||||
<file>ui/chatArea/scrollBarUpArrow.png</file>
|
||||
<file>ui/chatArea/scrollBarUpArrowHover.png</file>
|
||||
<file>ui/chatArea/scrollBarUpArrowPressed.png</file>
|
||||
<file>ui/chatroomWidgets/genericChatroomWidget.css</file>
|
||||
<file>ui/emoteButton/emoteButton.css</file>
|
||||
<file>ui/emoteButton/emoteButton.png</file>
|
||||
<file>ui/emoteButton/emoteButtonHover.png</file>
|
||||
<file>ui/emoteButton/emoteButtonPressed.png</file>
|
||||
<file>ui/emoticonWidget/dot_page.png</file>
|
||||
<file>ui/emoticonWidget/dot_page_current.png</file>
|
||||
<file>ui/emoticonWidget/dot_page_hover.png</file>
|
||||
<file>ui/emoticonWidget/emoticonWidget.css</file>
|
||||
<file>ui/fileButton/fileButton.css</file>
|
||||
<file>ui/fileButton/fileButton.png</file>
|
||||
<file>ui/fileButton/fileButtonHover.png</file>
|
||||
<file>ui/fileButton/fileButtonPressed.png</file>
|
||||
<file>ui/fileButton/fileButtonDisabled.png</file>
|
||||
<file>ui/fileTransferInstance/acceptFileButton.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>
|
||||
<file>ui/fileTransferInstance/pauseFileButton.png</file>
|
||||
<file>ui/fileTransferInstance/pauseGreyFileButton.png</file>
|
||||
<file>ui/fileTransferInstance/resumeFileButton.png</file>
|
||||
<file>ui/fileTransferInstance/stopFileButton.png</file>
|
||||
<file>ui/fileTransferInstance/sliverRTEdge.png</file>
|
||||
<file>ui/fileTransferWidget/fileTransferWidget.css</file>
|
||||
<file>ui/friendList/friendList.css</file>
|
||||
<file>ui/micButton/micButton.css</file>
|
||||
<file>ui/micButton/micButton.png</file>
|
||||
<file>ui/micButton/micButtonDisabled.png</file>
|
||||
<file>ui/micButton/micButtonHover.png</file>
|
||||
<file>ui/micButton/micButtonPressed.png</file>
|
||||
<file>ui/msgEdit/msgEdit.css</file>
|
||||
<file>ui/pauseFileButton/default.png</file>
|
||||
<file>ui/pauseFileButton/hover.png</file>
|
||||
<file>ui/pauseFileButton/pressed.png</file>
|
||||
<file>ui/pauseFileButton/style.css</file>
|
||||
<file>ui/sendButton/sendButton.css</file>
|
||||
<file>ui/sendButton/sendButton.png</file>
|
||||
<file>ui/sendButton/sendButtonHover.png</file>
|
||||
<file>ui/sendButton/sendButtonPressed.png</file>
|
||||
<file>ui/settings/mainContent.css</file>
|
||||
<file>ui/settings/mainHead.css</file>
|
||||
<file>ui/statusButton/dot_away.png</file>
|
||||
<file>ui/statusButton/dot_busy.png</file>
|
||||
<file>ui/statusButton/dot_idle.png</file>
|
||||
<file>ui/statusButton/dot_online.png</file>
|
||||
<file>ui/statusButton/menu_indicator.png</file>
|
||||
<file>ui/statusButton/statusButton.css</file>
|
||||
<file>ui/stopFileButton/default.png</file>
|
||||
<file>ui/stopFileButton/hover.png</file>
|
||||
<file>ui/stopFileButton/pressed.png</file>
|
||||
<file>ui/stopFileButton/style.css</file>
|
||||
<file>ui/videoButton/videoButton.css</file>
|
||||
<file>ui/videoButton/videoButton.png</file>
|
||||
<file>ui/videoButton/videoButtonDisabled.png</file>
|
||||
<file>ui/videoButton/videoButtonHover.png</file>
|
||||
<file>ui/videoButton/videoButtonPressed.png</file>
|
||||
<file>ui/videoButton/videoButtonRed.png</file>
|
||||
<file>ui/videoButton/videoButtonRedHover.png</file>
|
||||
<file>ui/videoButton/videoButtonRedPressed.png</file>
|
||||
<file>ui/videoButton/videoButtonYellow.png</file>
|
||||
<file>ui/videoButton/videoButtonYellowHover.png</file>
|
||||
<file>ui/videoButton/videoButtonYellowPressed.png</file>
|
||||
<file>ui/fileTransferWidget/fileTransferWidget.css</file>
|
||||
<file>ui/fileTransferInstance/red.css</file>
|
||||
<file>ui/fileTransferInstance/green.css</file>
|
||||
<file>ui/fileTransferInstance/grey.css</file>
|
||||
<file>ui/fileTransferInstance/yellow.css</file>
|
||||
<file>ui/fileTransferInstance/background_red.png</file>
|
||||
<file>ui/fileTransferInstance/background_yellow.png</file>
|
||||
<file>ui/fileTransferInstance/background_green.png</file>
|
||||
<file>ui/fileTransferInstance/background_grey.png</file>
|
||||
<file>ui/fileTransferInstance/pause_2x.png</file>
|
||||
<file>ui/fileTransferInstance/no_2x.png</file>
|
||||
<file>ui/fileTransferInstance/yes_2x.png</file>
|
||||
<file>ui/fileTransferInstance/arrow_white_2x.png</file>
|
||||
<file>ui/volButton/volButton.png</file>
|
||||
<file>ui/volButton/volButtonHover.png</file>
|
||||
<file>ui/volButton/volButtonPressed.png</file>
|
||||
<file>ui/volButton/volButton.css</file>
|
||||
<file>ui/window/applicationIcon.png</file>
|
||||
<file>ui/window/statusPanel.css</file>
|
||||
<file>ui/window/window.css</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>qtox.icns</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>qtox</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>im.tox.qtox</string>
|
||||
</dict>
|
||||
</plist>
|
100
res/settings.ini
@ -1,42 +1,86 @@
|
||||
[DHT%20Server]
|
||||
dhtServerList\size=9
|
||||
dhtServerList\size=21
|
||||
dhtServerList\1\name=Nikolai Toryzin
|
||||
dhtServerList\1\userId=951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
|
||||
dhtServerList\1\address=192.254.75.98
|
||||
dhtServerList\1\port=33445
|
||||
dhtServerList\2\name=sonOfRa
|
||||
dhtServerList\2\userId=04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
||||
dhtServerList\2\address=144.76.60.215
|
||||
dhtServerList\2\port=33445
|
||||
dhtServerList\3\name=stal
|
||||
dhtServerList\3\userId=A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||
dhtServerList\3\address=23.226.230.47
|
||||
dhtServerList\2\name=Nikolai Toryzin
|
||||
dhtServerList\2\userId=2A4B50D1D525DA2E669592A20C327B5FAD6C7E5962DC69296F9FEC77C4436E4E
|
||||
dhtServerList\2\address=31.7.57.236
|
||||
dhtServerList\2\port=443
|
||||
dhtServerList\3\name=sonOfRa
|
||||
dhtServerList\3\userId=04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
||||
dhtServerList\3\address=144.76.60.215
|
||||
dhtServerList\3\port=33445
|
||||
dhtServerList\4\name=aitjcize
|
||||
dhtServerList\4\userId=7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
||||
dhtServerList\4\address=54.199.139.199
|
||||
dhtServerList\4\name=stal
|
||||
dhtServerList\4\userId=A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||
dhtServerList\4\address=23.226.230.47
|
||||
dhtServerList\4\port=33445
|
||||
dhtServerList\5\name=astonex
|
||||
dhtServerList\5\userId=B98A2CEAA6C6A2FADC2C3632D284318B60FE5375CCB41EFA081AB67F500C1B0B
|
||||
dhtServerList\5\address=37.59.102.176
|
||||
dhtServerList\5\userId=10B20C49ACBD968D7C80F2E8438F92EA51F189F4E70CFBBB2C2C8C799E97F03E
|
||||
dhtServerList\5\address=178.62.125.224
|
||||
dhtServerList\5\port=33445
|
||||
dhtServerList\6\name=nurupo
|
||||
dhtServerList\6\userId=F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
dhtServerList\6\address=192.210.149.121
|
||||
dhtServerList\6\name=mousey
|
||||
dhtServerList\6\userId=5EB67C51D3FF5A9D528D242B669036ED2A30F8A60E674C45E7D43010CB2E1331
|
||||
dhtServerList\6\address=37.187.46.132
|
||||
dhtServerList\6\port=33445
|
||||
dhtServerList\7\name=mousey
|
||||
dhtServerList\7\userId=5EB67C51D3FF5A9D528D242B669036ED2A30F8A60E674C45E7D43010CB2E1331
|
||||
dhtServerList\7\address=37.187.46.132
|
||||
dhtServerList\7\name=SylvieLorxu
|
||||
dhtServerList\7\userId=4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
|
||||
dhtServerList\7\address=178.21.112.187
|
||||
dhtServerList\7\port=33445
|
||||
dhtServerList\8\name=Proplex
|
||||
dhtServerList\8\userId=7BE3951B97CA4B9ECDDA768E8C52BA19E9E2690AB584787BF4C90E04DBB75111
|
||||
dhtServerList\8\address=107.161.17.51
|
||||
dhtServerList\8\name=Munrek
|
||||
dhtServerList\8\userId=E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354
|
||||
dhtServerList\8\address=195.154.119.113
|
||||
dhtServerList\8\port=33445
|
||||
dhtServerList\9\name=SylvieLorxu
|
||||
dhtServerList\9\userId=4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
|
||||
dhtServerList\9\address=178.21.112.187
|
||||
dhtServerList\9\name=nurupo
|
||||
dhtServerList\9\userId=F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
dhtServerList\9\address=192.210.149.121
|
||||
dhtServerList\9\port=33445
|
||||
dhtServerList\10\name=Unknown (uTox)
|
||||
dhtServerList\10\userId=7187969BB10B54C98538BAE94C069CE5C84E650D54F7E596543D8FB1ECF4CF23
|
||||
dhtServerList\10\address=95.85.13.245
|
||||
dhtServerList\10\name=aitjcize
|
||||
dhtServerList\10\userId=7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
||||
dhtServerList\10\address=54.199.139.199
|
||||
dhtServerList\10\port=33445
|
||||
dhtServerList\11\name=Jfreegman
|
||||
dhtServerList\11\userId=8CD087E31C67568103E8C2A28653337E90E6B8EDA0D765D57C6B5172B4F1F04C
|
||||
dhtServerList\11\address=104.219.184.206
|
||||
dhtServerList\11\port=443
|
||||
dhtServerList\12\name=bunslow
|
||||
dhtServerList\12\userId=93574A3FAB7D612FEA29FD8D67D3DD10DFD07A075A5D62E8AF3DD9F5D0932E11
|
||||
dhtServerList\12\address=76.191.23.96
|
||||
dhtServerList\12\port=33445
|
||||
dhtServerList\13\name=Martin Schröder
|
||||
dhtServerList\13\userId=F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A
|
||||
dhtServerList\13\address=46.38.239.179
|
||||
dhtServerList\13\port=33445
|
||||
dhtServerList\14\name=lkwg82
|
||||
dhtServerList\14\userId=2C308B4518862740AD9A121598BCA7713AFB25858B747313A4D073E2F6AC506C
|
||||
dhtServerList\14\address=144.76.93.230
|
||||
dhtServerList\14\port=33445
|
||||
dhtServerList\15\name=Impyy
|
||||
dhtServerList\15\userId=788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B
|
||||
dhtServerList\15\address=178.62.250.138
|
||||
dhtServerList\15\port=33445
|
||||
dhtServerList\16\name=Thierry Thomas
|
||||
dhtServerList\16\userId=7A2306BFBA665E5480AE59B31E116BE9C04DCEFE04D9FE25082316FA34B4DA0C
|
||||
dhtServerList\16\address=78.225.128.39
|
||||
dhtServerList\16\port=33445
|
||||
dhtServerList\17\name=Manolis
|
||||
dhtServerList\17\userId=461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F
|
||||
dhtServerList\17\address=130.133.110.14
|
||||
dhtServerList\17\port=33445
|
||||
dhtServerList\18\name=lawk1
|
||||
dhtServerList\18\userId=58D2DE4B169502669941E50780C1630FAA48A0B7026D6F4066C320D47AC6401E
|
||||
dhtServerList\18\address=178.62.150.106
|
||||
dhtServerList\18\port=33445
|
||||
dhtServerList\19\name=noisykeyboard
|
||||
dhtServerList\19\userId=5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57
|
||||
dhtServerList\19\address=104.167.101.29
|
||||
dhtServerList\19\port=33445
|
||||
dhtServerList\20\name=aceawan
|
||||
dhtServerList\20\userId=391C96CB67AE893D4782B8E4495EB9D89CF1031F48460C06075AA8CE76D50A21
|
||||
dhtServerList\20\address=195.154.109.148
|
||||
dhtServerList\20\port=33445
|
||||
dhtServerList\21\name=pastly
|
||||
dhtServerList\21\userId=3E1FFDEB667BFF549F619EC6737834762124F50A89C8D0DBF1DDF64A2DD6CD1B
|
||||
dhtServerList\21\address=192.3.173.88
|
||||
dhtServerList\21\port=33445
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
if which apt-get; then
|
||||
sudo apt-get install build-essential qt5-qmake qt5-default libopenal-dev libopencv-dev \
|
||||
libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev
|
||||
libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev qttools5-dev-tools qtchooser libxss-dev
|
||||
elif which pacman; then
|
||||
sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx
|
||||
sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss
|
||||
elif which yum; then
|
||||
yum groupinstall "Development Tools"
|
||||
yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel
|
||||
yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel libXScrnSaver-devel
|
||||
else
|
||||
echo "Unknown package manager, attempting to compile anyways"
|
||||
fi
|
||||
|
212
src/audio.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
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 "audio.h"
|
||||
#include "src/core.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
std::atomic<int> Audio::userCount{0};
|
||||
Audio* Audio::instance{nullptr};
|
||||
QThread* Audio::audioThread{nullptr};
|
||||
ALCdevice* Audio::alInDev{nullptr};
|
||||
ALCdevice* Audio::alOutDev{nullptr};
|
||||
ALCcontext* Audio::alContext{nullptr};
|
||||
ALuint Audio::alMainSource{0};
|
||||
|
||||
Audio& Audio::getInstance()
|
||||
{
|
||||
if (!instance)
|
||||
{
|
||||
instance = new Audio();
|
||||
audioThread = new QThread(instance);
|
||||
audioThread->setObjectName("qTox Audio");
|
||||
audioThread->start();
|
||||
instance->moveToThread(audioThread);
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
void Audio::suscribeInput()
|
||||
{
|
||||
if (!userCount++ && alInDev)
|
||||
alcCaptureStart(alInDev);
|
||||
}
|
||||
|
||||
void Audio::unsuscribeInput()
|
||||
{
|
||||
if (!--userCount && alInDev)
|
||||
alcCaptureStop(alInDev);
|
||||
}
|
||||
|
||||
void Audio::openInput(const QString& inDevDescr)
|
||||
{
|
||||
auto* tmp = alInDev;
|
||||
alInDev = nullptr;
|
||||
if (tmp)
|
||||
alcCaptureCloseDevice(tmp);
|
||||
int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
||||
if (inDevDescr.isEmpty())
|
||||
alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, stereoFlag,
|
||||
(av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4)
|
||||
/ 1000 * av_DefaultSettings.audio_channels);
|
||||
else
|
||||
alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),av_DefaultSettings.audio_sample_rate, stereoFlag,
|
||||
(av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4)
|
||||
/ 1000 * av_DefaultSettings.audio_channels);
|
||||
if (!alInDev)
|
||||
qWarning() << "Audio: Cannot open input audio device";
|
||||
else
|
||||
qDebug() << "Audio: Opening audio input "<<inDevDescr;
|
||||
|
||||
Core::getInstance()->resetCallSources(); // Force to regen each group call's sources
|
||||
|
||||
// Restart the capture if necessary
|
||||
if (userCount.load() != 0 && alInDev)
|
||||
alcCaptureStart(alInDev);
|
||||
}
|
||||
|
||||
void Audio::openOutput(const QString& outDevDescr)
|
||||
{
|
||||
auto* tmp = alOutDev;
|
||||
alOutDev = nullptr;
|
||||
if (outDevDescr.isEmpty())
|
||||
alOutDev = alcOpenDevice(nullptr);
|
||||
else
|
||||
alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str());
|
||||
if (!alOutDev)
|
||||
{
|
||||
qWarning() << "Audio: Cannot open output audio device";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alContext)
|
||||
{
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(alContext);
|
||||
}
|
||||
if (tmp)
|
||||
alcCloseDevice(tmp);
|
||||
alContext=alcCreateContext(alOutDev,nullptr);
|
||||
if (!alcMakeContextCurrent(alContext))
|
||||
{
|
||||
qWarning() << "Audio: Cannot create output audio context";
|
||||
alcCloseDevice(alOutDev);
|
||||
}
|
||||
else
|
||||
alGenSources(1, &alMainSource);
|
||||
|
||||
|
||||
qDebug() << "Audio: Opening audio output "<<outDevDescr;
|
||||
}
|
||||
|
||||
Core::getInstance()->resetCallSources(); // Force to regen each group call's sources
|
||||
}
|
||||
|
||||
void Audio::closeInput()
|
||||
{
|
||||
if (alInDev)
|
||||
alcCaptureCloseDevice(alInDev);
|
||||
|
||||
userCount = 0;
|
||||
}
|
||||
|
||||
void Audio::closeOutput()
|
||||
{
|
||||
if (alContext)
|
||||
{
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(alContext);
|
||||
}
|
||||
|
||||
if (alOutDev)
|
||||
alcCloseDevice(alOutDev);
|
||||
}
|
||||
|
||||
void Audio::playMono16Sound(const QByteArray& data)
|
||||
{
|
||||
ALuint buffer;
|
||||
alGenBuffers(1, &buffer);
|
||||
alBufferData(buffer, AL_FORMAT_MONO16, data.data(), data.size(), 44100);
|
||||
alSourcei(alMainSource, AL_BUFFER, buffer);
|
||||
alSourcePlay(alMainSource);
|
||||
alDeleteBuffers(1, &buffer);
|
||||
}
|
||||
|
||||
void Audio::playGroupAudioQueued(Tox*,int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate,void*)
|
||||
{
|
||||
QMetaObject::invokeMethod(instance, "playGroupAudio", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(int,group), Q_ARG(int,peer), Q_ARG(const int16_t*,data),
|
||||
Q_ARG(unsigned,samples), Q_ARG(uint8_t,channels), Q_ARG(unsigned,sample_rate));
|
||||
}
|
||||
|
||||
void Audio::playGroupAudio(int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate)
|
||||
{
|
||||
assert(QThread::currentThread() == audioThread);
|
||||
|
||||
ToxGroupCall& call = Core::groupCalls[group];
|
||||
|
||||
if (!call.active || call.muteVol)
|
||||
return;
|
||||
|
||||
if (!call.alSources.contains(peer))
|
||||
alGenSources(1, &call.alSources[peer]);
|
||||
|
||||
playAudioBuffer(call.alSources[peer], data, samples, channels, sample_rate);
|
||||
}
|
||||
|
||||
void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
|
||||
{
|
||||
assert(channels == 1 || channels == 2);
|
||||
|
||||
ALuint bufid;
|
||||
ALint processed = 0, queued = 16;
|
||||
alGetSourcei(alSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued);
|
||||
alSourcei(alSource, AL_LOOPING, AL_FALSE);
|
||||
|
||||
if (processed)
|
||||
{
|
||||
ALuint bufids[processed];
|
||||
alSourceUnqueueBuffers(alSource, processed, bufids);
|
||||
alDeleteBuffers(processed - 1, bufids + 1);
|
||||
bufid = bufids[0];
|
||||
}
|
||||
else if (queued < 16)
|
||||
{
|
||||
alGenBuffers(1, &bufid);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Audio: Dropped frame";
|
||||
return;
|
||||
}
|
||||
|
||||
alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data,
|
||||
samples * 2 * channels, sampleRate);
|
||||
alSourceQueueBuffers(alSource, 1, &bufid);
|
||||
|
||||
ALint state;
|
||||
alGetSourcei(alSource, AL_SOURCE_STATE, &state);
|
||||
if (state != AL_PLAYING)
|
||||
alSourcePlay(alSource);
|
||||
}
|
82
src/audio.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
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 AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <atomic>
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
class QString;
|
||||
class QByteArray;
|
||||
class QTimer;
|
||||
class QThread;
|
||||
struct Tox;
|
||||
|
||||
class Audio : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static Audio& getInstance(); ///< Returns the singleton's instance. Will construct on first call.
|
||||
|
||||
static void suscribeInput(); ///< Call when you need to capture sound from the open input device.
|
||||
static void unsuscribeInput(); ///< Call once you don't need to capture on the open input device anymore.
|
||||
|
||||
static void openInput(const QString& inDevDescr); ///< Open an input device, use before suscribing
|
||||
static void openOutput(const QString& outDevDescr); ///< Open an output device
|
||||
|
||||
static void closeInput(); ///< Close an input device, please don't use unless everyone's unsuscribed
|
||||
static void closeOutput(); ///< Close an output device
|
||||
|
||||
static void playMono16Sound(const QByteArray& data); ///< Play a 44100Hz mono 16bit PCM sound
|
||||
|
||||
/// May be called from any thread, will always queue a call to playGroupAudio
|
||||
/// The first and last argument are ignored, but allow direct compatibility with toxcore
|
||||
static void playGroupAudioQueued(Tox*, int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate, void*);
|
||||
|
||||
public slots:
|
||||
/// Must be called from the audio thread, plays a group call's received audio
|
||||
void playGroupAudio(int group, int peer, const int16_t* data,
|
||||
unsigned samples, uint8_t channels, unsigned sample_rate);
|
||||
|
||||
public:
|
||||
static QThread* audioThread;
|
||||
static ALCdevice* alOutDev, *alInDev;
|
||||
static ALCcontext* alContext;
|
||||
static ALuint alMainSource;
|
||||
|
||||
private:
|
||||
explicit Audio()=default;
|
||||
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate);
|
||||
|
||||
private:
|
||||
static Audio* instance;
|
||||
static std::atomic<int> userCount;
|
||||
};
|
||||
|
||||
#endif // AUDIO_H
|
53
src/audiofilterer.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
|
||||
#include "audiofilterer.h"
|
||||
extern "C"{
|
||||
#include <filter_audio.h>
|
||||
}
|
||||
|
||||
void AudioFilterer::startFilter(unsigned int fs)
|
||||
{
|
||||
closeFilter();
|
||||
filter = new_filter_audio(fs);
|
||||
}
|
||||
|
||||
void AudioFilterer::closeFilter()
|
||||
{
|
||||
if (filter)
|
||||
kill_filter_audio(filter);
|
||||
filter = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void AudioFilterer::filterAudio(int16_t* data, int framesize)
|
||||
{
|
||||
if (!filter)
|
||||
return;
|
||||
|
||||
filter_audio(filter, (int16_t*) data, framesize);
|
||||
}
|
||||
|
||||
|
||||
AudioFilterer::~AudioFilterer()
|
||||
{
|
||||
closeFilter();
|
||||
}
|
||||
|
||||
#endif // QTOX_FILTER_AUDIO
|
41
src/audiofilterer.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
#ifndef AUDIOFILTERER_H
|
||||
#define AUDIOFILTERER_H
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef _FILTER_AUDIO
|
||||
typedef struct Filter_Audio Filter_Audio;
|
||||
#endif
|
||||
|
||||
class AudioFilterer
|
||||
{
|
||||
public:
|
||||
explicit AudioFilterer() = default;
|
||||
~AudioFilterer();
|
||||
|
||||
void startFilter(unsigned int fs);
|
||||
void filterAudio(int16_t* data, int framesize);
|
||||
void closeFilter();
|
||||
|
||||
private:
|
||||
struct Filter_Audio* filter{nullptr};
|
||||
};
|
||||
|
||||
#endif // AUDIOFILTERER_H
|
||||
#endif // QTOX_FILTER_AUDIO
|
@ -46,14 +46,13 @@ unsigned char AutoUpdater::key[crypto_sign_PUBLICKEYBYTES] =
|
||||
|
||||
#elif defined(Q_OS_OSX)
|
||||
const QString AutoUpdater::platform = "osx";
|
||||
const QString AutoUpdater::updaterBin = "installer -store -pkg "+Settings::getInstance().getSettingsDirPath()
|
||||
+"/update/qtox.pkg -target /";
|
||||
const QString AutoUpdater::updaterBin = "/Applications/qtox.app/Contents/MacOS/updater";
|
||||
const QString AutoUpdater::updateServer = "https://dist-build.tox.im";
|
||||
|
||||
unsigned char AutoUpdater::key[crypto_sign_PUBLICKEYBYTES] =
|
||||
{
|
||||
0xa5, 0x80, 0xf3, 0xb7, 0xd0, 0x10, 0xc0, 0xf9, 0xd6, 0xcf, 0x48, 0x15, 0x99, 0x70, 0x92, 0x49,
|
||||
0xf6, 0xe8, 0xe5, 0xe2, 0x6c, 0x73, 0x8c, 0x48, 0x25, 0xed, 0x01, 0x72, 0xf7, 0x6c, 0x17, 0x28
|
||||
0x12, 0x86, 0x25, 0x05, 0xb8, 0x9b, 0x39, 0x6f, 0xf1, 0xb1, 0xc4, 0x4d, 0x6f, 0x39, 0x35, 0x4d,
|
||||
0xea, 0xdf, 0x6c, 0x97, 0x98, 0x7d, 0x6f, 0x1c, 0x29, 0xf5, 0xb2, 0x3a, 0x5b, 0x78, 0xc1, 0x34
|
||||
};
|
||||
|
||||
#else
|
||||
@ -68,20 +67,22 @@ const QString AutoUpdater::filesURI = AutoUpdater::updateServer+"/qtox/"+AutoUpd
|
||||
|
||||
bool AutoUpdater::isUpdateAvailable()
|
||||
{
|
||||
QString newVersion = getUpdateVersion();
|
||||
if (newVersion.isEmpty() || newVersion == GIT_VERSION)
|
||||
VersionInfo newVersion = getUpdateVersion();
|
||||
if (newVersion.timestamp <= TIMESTAMP
|
||||
|| newVersion.versionString.isEmpty() || newVersion.versionString == GIT_VERSION)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
QString AutoUpdater::getUpdateVersion()
|
||||
AutoUpdater::VersionInfo AutoUpdater::getUpdateVersion()
|
||||
{
|
||||
QString version;
|
||||
VersionInfo versionInfo;
|
||||
versionInfo.timestamp = 0;
|
||||
|
||||
// Updates only for supported platforms
|
||||
if (platform.isEmpty())
|
||||
return version;
|
||||
return versionInfo;
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager;
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(checkURI)));
|
||||
@ -93,20 +94,20 @@ QString AutoUpdater::getUpdateVersion()
|
||||
qWarning() << "AutoUpdater: getUpdateVersion: network error: "<<reply->errorString();
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
return version;
|
||||
return versionInfo;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
if (data.size() < (int)(1+crypto_sign_BYTES))
|
||||
return version;
|
||||
return versionInfo;
|
||||
|
||||
// Check updater protocol version
|
||||
if ((int)data[0] != '1')
|
||||
if ((int)data[0] != '2')
|
||||
{
|
||||
qWarning() << "AutoUpdater: getUpdateVersion: Bad version "<<(uint8_t)data[0];
|
||||
return version;
|
||||
return versionInfo;
|
||||
}
|
||||
|
||||
// Check the signature
|
||||
@ -118,12 +119,16 @@ QString AutoUpdater::getUpdateVersion()
|
||||
if (crypto_sign_verify_detached(sig, msg, msgData.size(), key) != 0)
|
||||
{
|
||||
qCritical() << "AutoUpdater: getUpdateVersion: RECEIVED FORGED VERSION FILE FROM "<<updateServer;
|
||||
return version;
|
||||
return versionInfo;
|
||||
}
|
||||
|
||||
version = msgData;
|
||||
int sepPos = msgData.indexOf('!');
|
||||
versionInfo.timestamp = QString(msgData.left(sepPos)).toInt();
|
||||
versionInfo.versionString = msgData.mid(sepPos+1);
|
||||
|
||||
return version;
|
||||
qDebug() << "timestamp:"<<versionInfo.timestamp << ", str:"<<versionInfo.versionString;
|
||||
|
||||
return versionInfo;
|
||||
}
|
||||
|
||||
QList<AutoUpdater::UpdateFileMeta> AutoUpdater::parseFlist(QByteArray flistData)
|
||||
@ -387,13 +392,13 @@ void AutoUpdater::installLocalUpdate()
|
||||
// Workaround QTBUG-7645
|
||||
// QProcess fails silently when elevation is required instead of showing a UAC prompt on Win7/Vista
|
||||
#ifdef Q_OS_WIN
|
||||
int result = (int)::ShellExecuteA(0, "open", updaterBin.toUtf8().constData(), 0, 0, SW_SHOWNORMAL);
|
||||
if (SE_ERR_ACCESSDENIED == result)
|
||||
HINSTANCE result = ::ShellExecuteA(0, "open", updaterBin.toUtf8().constData(), 0, 0, SW_SHOWNORMAL);
|
||||
if (result == (HINSTANCE)SE_ERR_ACCESSDENIED)
|
||||
{
|
||||
// Requesting elevation
|
||||
result = (int)::ShellExecuteA(0, "runas", updaterBin.toUtf8().constData(), 0, 0, SW_SHOWNORMAL);
|
||||
result = ::ShellExecuteA(0, "runas", updaterBin.toUtf8().constData(), 0, 0, SW_SHOWNORMAL);
|
||||
}
|
||||
if (result <= 32)
|
||||
if (result <= (HINSTANCE)32)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
@ -423,7 +428,7 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker()
|
||||
return;
|
||||
|
||||
if (Widget::getInstance()->askMsgboxQuestion(QObject::tr("Update", "The title of a message box"),
|
||||
QObject::tr("An update is available, do you want to download it now ?\nIt will be installed when qTox restarts.")))
|
||||
QObject::tr("An update is available, do you want to download it now?\nIt will be installed when qTox restarts.")))
|
||||
{
|
||||
downloadUpdate();
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
#ifdef Q_OS_WIN
|
||||
#define AUTOUPDATE_ENABLED 1
|
||||
#elif defined(Q_OS_OSX)
|
||||
#define AUTOUPDATE_ENABLED 0
|
||||
#define AUTOUPDATE_ENABLED 1
|
||||
#else
|
||||
#define AUTOUPDATE_ENABLED 0
|
||||
#endif
|
||||
@ -58,6 +58,12 @@ public:
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
struct VersionInfo
|
||||
{
|
||||
uint64_t timestamp;
|
||||
QString versionString;
|
||||
};
|
||||
|
||||
public:
|
||||
/// Connects to the qTox update server, if an updat is found shows a dialog to the user asking to download it
|
||||
/// Runs asynchronously in its own thread, and will return immediatly
|
||||
@ -66,9 +72,9 @@ public:
|
||||
/// Connects to the qTox update server, returns true if an update is available for download
|
||||
/// Will call getUpdateVersion, and as such may block and processEvents
|
||||
static bool isUpdateAvailable();
|
||||
/// Fetch the version string of the last update available from the qTox update server
|
||||
/// Fetch the version info of the last update available from the qTox update server
|
||||
/// Will try to follow qTox's proxy settings, may block and processEvents
|
||||
static QString getUpdateVersion();
|
||||
static VersionInfo getUpdateVersion();
|
||||
/// Will try to download an update, if successful returns true and qTox will apply it after a restart
|
||||
/// Will try to follow qTox's proxy settings, may block and processEvents
|
||||
static bool downloadUpdate();
|
||||
|
221
src/core.cpp
@ -20,6 +20,7 @@
|
||||
#include "misc/settings.h"
|
||||
#include "widget/widget.h"
|
||||
#include "historykeeper.h"
|
||||
#include "src/audio.h"
|
||||
|
||||
#include <tox/tox.h>
|
||||
#include <tox/toxencryptsave.h>
|
||||
@ -45,12 +46,20 @@ const QString Core::CONFIG_FILE_NAME = "data";
|
||||
const QString Core::TOX_EXT = ".tox";
|
||||
QList<ToxFile> Core::fileSendQueue;
|
||||
QList<ToxFile> Core::fileRecvQueue;
|
||||
QHash<int, ToxGroupCall> Core::groupCalls;
|
||||
QThread* Core::coreThread{nullptr};
|
||||
|
||||
Core::Core(Camera* cam, QThread *coreThread, QString loadPath) :
|
||||
#define MAX_GROUP_MESSAGE_LEN 1024
|
||||
|
||||
Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
|
||||
tox(nullptr), camera(cam), loadPath(loadPath), ready{false}
|
||||
{
|
||||
qDebug() << "Core: loading Tox from" << loadPath;
|
||||
|
||||
coreThread = CoreThread;
|
||||
|
||||
Audio::getInstance();
|
||||
|
||||
videobuf = new uint8_t[videobufsize];
|
||||
|
||||
for (int i = 0; i < ptCounter; i++)
|
||||
@ -65,6 +74,8 @@ Core::Core(Camera* cam, QThread *coreThread, QString loadPath) :
|
||||
|
||||
for (int i=0; i<TOXAV_MAX_CALLS;i++)
|
||||
{
|
||||
calls[i].active = false;
|
||||
calls[i].alSource = 0;
|
||||
calls[i].sendAudioTimer = new QTimer();
|
||||
calls[i].sendVideoTimer = new QTimer();
|
||||
calls[i].sendAudioTimer->moveToThread(coreThread);
|
||||
@ -74,42 +85,22 @@ Core::Core(Camera* cam, QThread *coreThread, QString loadPath) :
|
||||
|
||||
// OpenAL init
|
||||
QString outDevDescr = Settings::getInstance().getOutDev(); ;
|
||||
if (outDevDescr.isEmpty())
|
||||
alOutDev = alcOpenDevice(nullptr);
|
||||
else
|
||||
alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str());
|
||||
if (!alOutDev)
|
||||
{
|
||||
qWarning() << "Core: Cannot open output audio device";
|
||||
}
|
||||
else
|
||||
{
|
||||
alContext=alcCreateContext(alOutDev,nullptr);
|
||||
if (!alcMakeContextCurrent(alContext))
|
||||
{
|
||||
qWarning() << "Core: Cannot create output audio context";
|
||||
alcCloseDevice(alOutDev);
|
||||
}
|
||||
else
|
||||
alGenSources(1, &alMainSource);
|
||||
}
|
||||
|
||||
Audio::openOutput(outDevDescr);
|
||||
QString inDevDescr = Settings::getInstance().getInDev();
|
||||
if (inDevDescr.isEmpty())
|
||||
alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16,
|
||||
(av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) / 1000);
|
||||
else
|
||||
alInDev = alcCaptureOpenDevice(inDevDescr.toStdString().c_str(),av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16,
|
||||
(av_DefaultSettings.audio_frame_duration * av_DefaultSettings.audio_sample_rate * 4) / 1000);
|
||||
if (!alInDev)
|
||||
qWarning() << "Core: Cannot open input audio device";
|
||||
Audio::openInput(inDevDescr);
|
||||
}
|
||||
|
||||
Core::~Core()
|
||||
{
|
||||
if (tox) {
|
||||
clearPassword(Core::ptMain);
|
||||
clearPassword(Core::ptHistory);
|
||||
|
||||
if (tox)
|
||||
{
|
||||
toxav_kill(toxav);
|
||||
toxav = nullptr;
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
}
|
||||
|
||||
if (videobuf)
|
||||
@ -118,18 +109,8 @@ Core::~Core()
|
||||
videobuf=nullptr;
|
||||
}
|
||||
|
||||
if (alContext)
|
||||
{
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(alContext);
|
||||
}
|
||||
if (alOutDev)
|
||||
alcCloseDevice(alOutDev);
|
||||
if (alInDev)
|
||||
alcCaptureCloseDevice(alInDev);
|
||||
|
||||
clearPassword(Core::ptMain);
|
||||
clearPassword(Core::ptHistory);
|
||||
Audio::closeInput();
|
||||
Audio::closeOutput();
|
||||
}
|
||||
|
||||
Core* Core::getInstance()
|
||||
@ -142,7 +123,7 @@ void Core::make_tox()
|
||||
// IPv6 needed for LAN discovery, but can crash some weird routers. On by default, can be disabled in options.
|
||||
bool enableIPv6 = Settings::getInstance().getEnableIPv6();
|
||||
bool forceTCP = Settings::getInstance().getForceTCP();
|
||||
bool useProxy = Settings::getInstance().getUseProxy();
|
||||
ProxyType proxyType = Settings::getInstance().getProxyType();
|
||||
|
||||
if (enableIPv6)
|
||||
qDebug() << "Core starting with IPv6 enabled";
|
||||
@ -154,11 +135,11 @@ void Core::make_tox()
|
||||
toxOptions.udp_disabled = forceTCP;
|
||||
|
||||
// No proxy by default
|
||||
toxOptions.proxy_enabled = false;
|
||||
toxOptions.proxy_type = TOX_PROXY_NONE;
|
||||
toxOptions.proxy_address[0] = 0;
|
||||
toxOptions.proxy_port = 0;
|
||||
|
||||
if (useProxy)
|
||||
if (proxyType != ProxyType::ptNone)
|
||||
{
|
||||
QString proxyAddr = Settings::getInstance().getProxyAddr();
|
||||
int proxyPort = Settings::getInstance().getProxyPort();
|
||||
@ -170,7 +151,11 @@ void Core::make_tox()
|
||||
else if (proxyAddr != "" && proxyPort > 0)
|
||||
{
|
||||
qDebug() << "Core: using proxy" << proxyAddr << ":" << proxyPort;
|
||||
toxOptions.proxy_enabled = true;
|
||||
// protection against changings in TOX_PROXY_TYPE enum
|
||||
if (proxyType == ProxyType::ptSOCKS5)
|
||||
toxOptions.proxy_type = TOX_PROXY_SOCKS5;
|
||||
else if (proxyType == ProxyType::ptHTTP)
|
||||
toxOptions.proxy_type = TOX_PROXY_HTTP;
|
||||
uint16_t sz = CString::fromString(proxyAddr, (unsigned char*)toxOptions.proxy_address);
|
||||
toxOptions.proxy_address[sz] = 0;
|
||||
toxOptions.proxy_port = proxyPort;
|
||||
@ -186,7 +171,7 @@ void Core::make_tox()
|
||||
tox = tox_new(&toxOptions);
|
||||
if (tox == nullptr)
|
||||
{
|
||||
if (toxOptions.proxy_enabled)
|
||||
if (toxOptions.proxy_type != TOX_PROXY_NONE)
|
||||
{
|
||||
//QMessageBox::critical(Widget::getInstance(), tr("Proxy failure", "popup title"),
|
||||
//tr("toxcore failed to start with your proxy settings. qTox cannot run; please modify your "
|
||||
@ -204,7 +189,7 @@ void Core::make_tox()
|
||||
else
|
||||
qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery may not work properly.";
|
||||
}
|
||||
else if (toxOptions.proxy_enabled)
|
||||
else if (toxOptions.proxy_type != TOX_PROXY_NONE)
|
||||
{
|
||||
emit badProxy();
|
||||
return;
|
||||
@ -262,6 +247,7 @@ void Core::start()
|
||||
tox_callback_group_invite(tox, onGroupInvite, this);
|
||||
tox_callback_group_message(tox, onGroupMessage, this);
|
||||
tox_callback_group_namelist_change(tox, onGroupNamelistChange, this);
|
||||
tox_callback_group_title(tox, onGroupTitleChange, this);
|
||||
tox_callback_group_action(tox, onGroupAction, this);
|
||||
tox_callback_file_send_request(tox, onFileSendRequestCallback, this);
|
||||
tox_callback_file_control(tox, onFileControlCallback, this);
|
||||
@ -276,14 +262,13 @@ void Core::start()
|
||||
toxav_register_callstate_callback(toxav, onAvReject, av_OnReject, this);
|
||||
toxav_register_callstate_callback(toxav, onAvEnd, av_OnEnd, this);
|
||||
toxav_register_callstate_callback(toxav, onAvRinging, av_OnRinging, this);
|
||||
toxav_register_callstate_callback(toxav, onAvStarting, av_OnStarting, this);
|
||||
toxav_register_callstate_callback(toxav, onAvEnding, av_OnEnding, this);
|
||||
toxav_register_callstate_callback(toxav, onAvMediaChange, av_OnMediaChange, this);
|
||||
toxav_register_callstate_callback(toxav, onAvMediaChange, av_OnPeerCSChange, this);
|
||||
toxav_register_callstate_callback(toxav, onAvMediaChange, av_OnSelfCSChange, this);
|
||||
toxav_register_callstate_callback(toxav, onAvRequestTimeout, av_OnRequestTimeout, this);
|
||||
toxav_register_callstate_callback(toxav, onAvPeerTimeout, av_OnPeerTimeout, this);
|
||||
|
||||
toxav_register_audio_recv_callback(toxav, playCallAudio, this);
|
||||
toxav_register_video_recv_callback(toxav, playCallVideo, this);
|
||||
toxav_register_audio_callback(toxav, playCallAudio, this);
|
||||
toxav_register_video_callback(toxav, playCallVideo, this);
|
||||
|
||||
QPixmap pic = Settings::getInstance().getSavedAvatar(getSelfId().toString());
|
||||
if (!pic.isNull() && !pic.size().isEmpty())
|
||||
@ -319,6 +304,7 @@ void Core::process()
|
||||
|
||||
static int tolerance = CORE_DISCONNECT_TOLERANCE;
|
||||
tox_do(tox);
|
||||
toxav_do(toxav);
|
||||
|
||||
#ifdef DEBUG
|
||||
//we want to see the debug messages immediately
|
||||
@ -332,7 +318,7 @@ void Core::process()
|
||||
bootstrapDht();
|
||||
}
|
||||
|
||||
toxTimer->start(tox_do_interval(tox));
|
||||
toxTimer->start(qMin(tox_do_interval(tox), toxav_do_interval(toxav)));
|
||||
}
|
||||
|
||||
bool Core::checkConnection()
|
||||
@ -362,6 +348,11 @@ void Core::bootstrapDht()
|
||||
QList<Settings::DhtServer> dhtServerList = s.getDhtServerList();
|
||||
|
||||
int listSize = dhtServerList.size();
|
||||
if (listSize == 0)
|
||||
{
|
||||
qDebug() << "Settings: no bootstrap list?!?";
|
||||
return;
|
||||
}
|
||||
static int j = qrand() % listSize;
|
||||
|
||||
qDebug() << "Core: Bootstraping to the DHT ...";
|
||||
@ -475,21 +466,21 @@ void Core::onAction(Tox*/* tox*/, int friendId, const uint8_t *cMessage, uint16_
|
||||
void Core::onGroupAction(Tox*, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void* _core)
|
||||
{
|
||||
Core* core = static_cast<Core*>(_core);
|
||||
emit core->groupMessageReceived(groupnumber, CString::toString(action, length),
|
||||
core->getGroupPeerName(groupnumber, peernumber), true);
|
||||
emit core->groupMessageReceived(groupnumber, peernumber, CString::toString(action, length), true);
|
||||
}
|
||||
|
||||
void Core::onGroupInvite(Tox*, int friendnumber, uint8_t type, const uint8_t *data, uint16_t length,void *core)
|
||||
{
|
||||
QByteArray pk((char*)data, length);
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||
{
|
||||
qDebug() << QString("Core: Text group invite by %1").arg(friendnumber);
|
||||
emit static_cast<Core*>(core)->groupInviteReceived(friendnumber,type,data,length);
|
||||
emit static_cast<Core*>(core)->groupInviteReceived(friendnumber,type,pk);
|
||||
}
|
||||
else if (type == TOX_GROUPCHAT_TYPE_AV)
|
||||
{
|
||||
qDebug() << QString("Core: AV group invite by %1").arg(friendnumber);
|
||||
emit static_cast<Core*>(core)->groupInviteReceived(friendnumber,type,data,length);
|
||||
emit static_cast<Core*>(core)->groupInviteReceived(friendnumber,type,pk);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -500,8 +491,8 @@ void Core::onGroupInvite(Tox*, int friendnumber, uint8_t type, const uint8_t *da
|
||||
void Core::onGroupMessage(Tox*, int groupnumber, int peernumber, const uint8_t * message, uint16_t length, void *_core)
|
||||
{
|
||||
Core* core = static_cast<Core*>(_core);
|
||||
emit core->groupMessageReceived(groupnumber, CString::toString(message, length),
|
||||
core->getGroupPeerName(groupnumber, peernumber), false);
|
||||
|
||||
emit core->groupMessageReceived(groupnumber, peernumber, CString::toString(message, length), false);
|
||||
}
|
||||
|
||||
void Core::onGroupNamelistChange(Tox*, int groupnumber, int peernumber, uint8_t change, void *core)
|
||||
@ -510,6 +501,16 @@ void Core::onGroupNamelistChange(Tox*, int groupnumber, int peernumber, uint8_t
|
||||
emit static_cast<Core*>(core)->groupNamelistChanged(groupnumber, peernumber, change);
|
||||
}
|
||||
|
||||
void Core::onGroupTitleChange(Tox*, int groupnumber, int peernumber, const uint8_t* title, uint8_t len, void* _core)
|
||||
{
|
||||
qDebug() << "Core: group" << groupnumber << "title changed by" << peernumber;
|
||||
Core* core = static_cast<Core*>(_core);
|
||||
QString author;
|
||||
if (peernumber >= 0)
|
||||
author = core->getGroupPeerName(groupnumber, peernumber);
|
||||
emit core->groupTitleChanged(groupnumber, author, CString::toString(title, len));
|
||||
}
|
||||
|
||||
void Core::onFileSendRequestCallback(Tox*, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
|
||||
const uint8_t *filename, uint16_t filename_length, void *core)
|
||||
{
|
||||
@ -732,7 +733,7 @@ void Core::requestFriendship(const QString& friendAddress, const QString& messag
|
||||
{
|
||||
const QString userId = friendAddress.mid(0, TOX_CLIENT_ID_SIZE * 2);
|
||||
|
||||
if(hasFriendWithAddress(friendAddress))
|
||||
if (hasFriendWithAddress(friendAddress))
|
||||
{
|
||||
emit failedToAddFriend(userId, QString(tr("Friend is already added")));
|
||||
}
|
||||
@ -783,7 +784,7 @@ void Core::sendTyping(int friendId, bool typing)
|
||||
|
||||
void Core::sendGroupMessage(int groupId, const QString& message)
|
||||
{
|
||||
QList<CString> cMessages = splitMessage(message);
|
||||
QList<CString> cMessages = splitMessage(message, MAX_GROUP_MESSAGE_LEN);
|
||||
|
||||
for (auto &cMsg :cMessages)
|
||||
{
|
||||
@ -796,7 +797,7 @@ void Core::sendGroupMessage(int groupId, const QString& message)
|
||||
|
||||
void Core::sendGroupAction(int groupId, const QString& message)
|
||||
{
|
||||
QList<CString> cMessages = splitMessage(message);
|
||||
QList<CString> cMessages = splitMessage(message, MAX_GROUP_MESSAGE_LEN);
|
||||
|
||||
for (auto &cMsg :cMessages)
|
||||
{
|
||||
@ -807,6 +808,14 @@ void Core::sendGroupAction(int groupId, const QString& message)
|
||||
}
|
||||
}
|
||||
|
||||
void Core::changeGroupTitle(int groupId, const QString& title)
|
||||
{
|
||||
CString cTitle(title);
|
||||
int err = tox_group_set_title(tox, groupId, cTitle.data(), cTitle.size());
|
||||
if (!err)
|
||||
emit groupTitleChanged(groupId, getUsername(), title);
|
||||
}
|
||||
|
||||
void Core::sendFile(int32_t friendId, QString Filename, QString FilePath, long long filesize)
|
||||
{
|
||||
QMutexLocker mlocker(&fileSendMutex);
|
||||
@ -1007,6 +1016,9 @@ void Core::removeGroup(int groupId, bool fake)
|
||||
if (!tox || fake)
|
||||
return;
|
||||
tox_del_groupchat(tox, groupId);
|
||||
|
||||
if (groupCalls[groupId].active)
|
||||
leaveGroupCall(groupId);
|
||||
}
|
||||
|
||||
QString Core::getUsername() const
|
||||
@ -1255,7 +1267,7 @@ bool Core::loadConfiguration(QString path)
|
||||
if (!HistoryKeeper::checkPassword())
|
||||
{
|
||||
if (QMessageBox::Ok == Widget::getInstance()->showWarningMsgBox(tr("Encrypted log"),
|
||||
tr("Your history is encrypted with different password\nDo you want to try another password?"),
|
||||
tr("Your history is encrypted with different password.\nDo you want to try another password?"),
|
||||
QMessageBox::Ok | QMessageBox::Cancel))
|
||||
{
|
||||
error = true;
|
||||
@ -1265,7 +1277,7 @@ bool Core::loadConfiguration(QString path)
|
||||
{
|
||||
error = false;
|
||||
clearPassword(ptHistory);
|
||||
Widget::getInstance()->showWarningMsgBox(tr("Loggin"), tr("Due to incorret password logging will be disabled"));
|
||||
Widget::getInstance()->showWarningMsgBox(tr("History"), tr("Due to incorret password history will be disabled."));
|
||||
Settings::getInstance().setEncryptLogs(false);
|
||||
Settings::getInstance().setEnableLogging(false);
|
||||
}
|
||||
@ -1366,12 +1378,13 @@ void Core::switchConfiguration(const QString& profile)
|
||||
qDebug() << "Core: creating new Id";
|
||||
else
|
||||
qDebug() << "Core: switching from" << Settings::getInstance().getCurrentProfile() << "to" << profile;
|
||||
|
||||
|
||||
saveConfiguration();
|
||||
|
||||
ready = false;
|
||||
clearPassword(ptMain);
|
||||
clearPassword(ptHistory);
|
||||
|
||||
ready = false;
|
||||
toxTimer->stop();
|
||||
Widget::getInstance()->setEnabledThreadsafe(false);
|
||||
if (tox) {
|
||||
@ -1380,6 +1393,7 @@ void Core::switchConfiguration(const QString& profile)
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
}
|
||||
|
||||
emit selfAvatarChanged(QPixmap(":/img/contact_dark.png"));
|
||||
emit blockingClearContacts(); // we need this to block, but signals are required for thread safety
|
||||
|
||||
@ -1387,7 +1401,12 @@ void Core::switchConfiguration(const QString& profile)
|
||||
loadPath = "";
|
||||
else
|
||||
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||
Settings::getInstance().setCurrentProfile(profile);
|
||||
|
||||
// the new profile needs to be set before resetting the settings, so that
|
||||
// we don't load the old profile's profile.ini
|
||||
Settings::getInstance().setCurrentProfile(profile);
|
||||
Settings::getInstance().save(false); // save new profile, but don't write old profile info to newprofile.ini
|
||||
Settings::resetInstance();
|
||||
HistoryKeeper::getInstance()->resetInstance();
|
||||
|
||||
start();
|
||||
@ -1458,6 +1477,22 @@ QString Core::getGroupPeerName(int groupId, int peerId) const
|
||||
return name;
|
||||
}
|
||||
|
||||
ToxID Core::getGroupPeerToxID(int groupId, int peerId) const
|
||||
{
|
||||
ToxID peerToxID;
|
||||
|
||||
uint8_t rawID[TOX_CLIENT_ID_SIZE];
|
||||
int res = tox_group_peer_pubkey(tox, groupId, peerId, rawID);
|
||||
if (res == -1)
|
||||
{
|
||||
qWarning() << "Core::getGroupPeerToxID: Unknown error";
|
||||
return peerToxID;
|
||||
}
|
||||
|
||||
peerToxID = ToxID::fromString(CUserId::toString(rawID));
|
||||
return peerToxID;
|
||||
}
|
||||
|
||||
QList<QString> Core::getGroupPeerNames(int groupId) const
|
||||
{
|
||||
QList<QString> names;
|
||||
@ -1490,7 +1525,8 @@ int Core::joinGroupchat(int32_t friendnumber, uint8_t type, const uint8_t* frien
|
||||
else if (type == TOX_GROUPCHAT_TYPE_AV)
|
||||
{
|
||||
qDebug() << QString("Trying to join AV groupchat invite sent by friend %1").arg(friendnumber);
|
||||
return toxav_join_av_groupchat(tox, friendnumber, friend_group_public_key, length, playGroupAudio, const_cast<Core*>(this));
|
||||
return toxav_join_av_groupchat(tox, friendnumber, friend_group_public_key, length,
|
||||
&Audio::playGroupAudioQueued, const_cast<Core*>(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1630,17 +1666,23 @@ void Core::groupInviteFriend(int friendId, int groupId)
|
||||
void Core::createGroup(uint8_t type)
|
||||
{
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||
{
|
||||
emit emptyGroupCreated(tox_add_groupchat(tox));
|
||||
}
|
||||
else if (type == TOX_GROUPCHAT_TYPE_AV)
|
||||
emit emptyGroupCreated(toxav_add_av_groupchat(tox, playGroupAudio, this));
|
||||
{
|
||||
emit emptyGroupCreated(toxav_add_av_groupchat(tox, &Audio::playGroupAudioQueued, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Core::createGroup: Unknown type "<<type;
|
||||
}
|
||||
}
|
||||
|
||||
bool Core::hasFriendWithAddress(const QString &addr) const
|
||||
{
|
||||
// Valid length check
|
||||
if(addr.length() != (TOX_FRIEND_ADDRESS_SIZE * 2))
|
||||
if (addr.length() != (TOX_FRIEND_ADDRESS_SIZE * 2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -1652,7 +1694,7 @@ bool Core::hasFriendWithAddress(const QString &addr) const
|
||||
bool Core::hasFriendWithPublicKey(const QString &pubkey) const
|
||||
{
|
||||
// Valid length check
|
||||
if(pubkey.length() != (TOX_CLIENT_ID_SIZE * 2))
|
||||
if (pubkey.length() != (TOX_CLIENT_ID_SIZE * 2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -1669,7 +1711,7 @@ bool Core::hasFriendWithPublicKey(const QString &pubkey) const
|
||||
QString addrOrId = getFriendAddress(ids[i]);
|
||||
|
||||
// Set true if found
|
||||
if(addrOrId.toUpper().startsWith(pubkey.toUpper()))
|
||||
if (addrOrId.toUpper().startsWith(pubkey.toUpper()))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
@ -1704,17 +1746,17 @@ QString Core::getFriendUsername(int friendnumber) const
|
||||
return CString::toString(name, tox_get_name_size(tox, friendnumber));
|
||||
}
|
||||
|
||||
QList<CString> Core::splitMessage(const QString &message)
|
||||
QList<CString> Core::splitMessage(const QString &message, int maxLen)
|
||||
{
|
||||
QList<CString> splittedMsgs;
|
||||
QByteArray ba_message(message.toUtf8());
|
||||
|
||||
while (ba_message.size() > TOX_MAX_MESSAGE_LENGTH)
|
||||
while (ba_message.size() > maxLen)
|
||||
{
|
||||
int splitPos = ba_message.lastIndexOf(' ', TOX_MAX_MESSAGE_LENGTH - 1);
|
||||
int splitPos = ba_message.lastIndexOf(' ', maxLen - 1);
|
||||
if (splitPos <= 0)
|
||||
{
|
||||
splitPos = TOX_MAX_MESSAGE_LENGTH;
|
||||
splitPos = maxLen;
|
||||
if (ba_message[splitPos] & 0x80)
|
||||
{
|
||||
do {
|
||||
@ -1832,3 +1874,28 @@ bool Core::isReady()
|
||||
{
|
||||
return ready;
|
||||
}
|
||||
|
||||
void Core::setNospam(uint32_t nospam)
|
||||
{
|
||||
uint8_t *nspm = reinterpret_cast<uint8_t*>(&nospam);
|
||||
std::reverse(nspm, nspm + 4);
|
||||
tox_set_nospam(tox, nospam);
|
||||
}
|
||||
|
||||
void Core::resetCallSources()
|
||||
{
|
||||
for (ToxGroupCall& call : groupCalls)
|
||||
call.alSources.clear();
|
||||
|
||||
for (ToxCall& call : calls)
|
||||
{
|
||||
if (call.active && call.alSource)
|
||||
{
|
||||
ALuint tmp = call.alSource;
|
||||
call.alSource = 0;
|
||||
alDeleteSources(1, &tmp);
|
||||
|
||||
alGenSources(1, &call.alSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
53
src/core.h
@ -33,6 +33,9 @@ class QTimer;
|
||||
class QString;
|
||||
class CString;
|
||||
class VideoSource;
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
class AudioFilterer;
|
||||
#endif
|
||||
|
||||
class Core : public QObject
|
||||
{
|
||||
@ -47,12 +50,13 @@ public:
|
||||
static const QString TOX_EXT;
|
||||
static const QString CONFIG_FILE_NAME;
|
||||
static QString sanitize(QString name);
|
||||
static QList<CString> splitMessage(const QString &message);
|
||||
static QList<CString> splitMessage(const QString &message, int maxLen);
|
||||
|
||||
QString getPeerName(const ToxID& id) const;
|
||||
|
||||
int getGroupNumberPeers(int groupId) const; ///< Return the number of peers in the group chat on success, or -1 on failure
|
||||
QString getGroupPeerName(int groupId, int peerId) const; ///< Get the name of a peer of a group
|
||||
ToxID getGroupPeerToxID(int groupId, int peerId) const; ///< Get the ToxID of a peer of a group
|
||||
QList<QString> getGroupPeerNames(int groupId) const; ///< Get the names of the peers of a group
|
||||
QString getFriendAddress(int friendNumber) const; ///< Get the full address if known, or Tox ID of a friend
|
||||
QString getFriendUsername(int friendNumber) const; ///< Get the username of a friend
|
||||
@ -76,6 +80,8 @@ public:
|
||||
bool isPasswordSet(PasswordType passtype);
|
||||
bool isReady(); ///< Most of the API shouldn't be used until Core is ready, call start() first
|
||||
|
||||
void resetCallSources(); ///< Forces to regenerate each call's audio sources
|
||||
|
||||
public slots:
|
||||
void start(); ///< Initializes the core, must be called before anything else
|
||||
void process(); ///< Processes toxcore events and ensure we stay connected, called by its own timer
|
||||
@ -95,10 +101,11 @@ public slots:
|
||||
void setStatusMessage(const QString& message);
|
||||
void setAvatar(uint8_t format, const QByteArray& data);
|
||||
|
||||
int sendMessage(int friendId, const QString& message);
|
||||
int sendMessage(int friendId, const QString& message);
|
||||
void sendGroupMessage(int groupId, const QString& message);
|
||||
void sendGroupAction(int groupId, const QString& message);
|
||||
int sendAction(int friendId, const QString& action);
|
||||
void changeGroupTitle(int groupId, const QString& title);
|
||||
int sendAction(int friendId, const QString& action);
|
||||
void sendTyping(int friendId, bool typing);
|
||||
|
||||
void sendFile(int32_t friendId, QString Filename, QString FilePath, long long filesize);
|
||||
@ -117,6 +124,17 @@ public slots:
|
||||
void micMuteToggle(int callId);
|
||||
void volMuteToggle(int callId);
|
||||
|
||||
void setNospam(uint32_t nospam);
|
||||
|
||||
static void joinGroupCall(int groupId); ///< Starts a call in an existing AV groupchat. Call from the GUI thread.
|
||||
static void leaveGroupCall(int groupId); ///< Will not leave the group, just stop the call. Call from the GUI thread.
|
||||
static void disableGroupCallMic(int groupId);
|
||||
static void disableGroupCallVol(int groupId);
|
||||
static void enableGroupCallMic(int groupId);
|
||||
static void enableGroupCallVol(int groupId);
|
||||
static bool isGroupCallMicEnabled(int groupId);
|
||||
static bool isGroupCallVolEnabled(int groupId);
|
||||
|
||||
void setPassword(QString& password, PasswordType passtype, uint8_t* salt = nullptr);
|
||||
void clearPassword(PasswordType passtype);
|
||||
QByteArray encryptData(const QByteArray& data, PasswordType passtype);
|
||||
@ -145,9 +163,10 @@ signals:
|
||||
void friendLastSeenChanged(int friendId, const QDateTime& dateTime);
|
||||
|
||||
void emptyGroupCreated(int groupnumber);
|
||||
void groupInviteReceived(int friendnumber, uint8_t type, const uint8_t *group_public_key,uint16_t length);
|
||||
void groupMessageReceived(int groupnumber, const QString& message, const QString& author, bool isAction);
|
||||
void groupInviteReceived(int friendnumber, uint8_t type, QByteArray publicKey);
|
||||
void groupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
|
||||
void groupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
|
||||
void groupTitleChanged(int groupnumber, const QString& author, const QString& title);
|
||||
|
||||
void usernameSet(const QString& username);
|
||||
void statusMessageSet(const QString& message);
|
||||
@ -157,6 +176,7 @@ signals:
|
||||
|
||||
void messageSentResult(int friendId, const QString& message, int messageId);
|
||||
void groupSentResult(int groupId, const QString& message, int result);
|
||||
void actionSentResult(int friendId, const QString& action, int success);
|
||||
|
||||
void receiptRecieved(int friedId, int receipt);
|
||||
|
||||
@ -212,6 +232,7 @@ private:
|
||||
static void onGroupInvite(Tox *tox, int friendnumber, uint8_t type, const uint8_t *data, uint16_t length,void *userdata);
|
||||
static void onGroupMessage(Tox *tox, int groupnumber, int friendgroupnumber, const uint8_t * message, uint16_t length, void *userdata);
|
||||
static void onGroupNamelistChange(Tox *tox, int groupnumber, int peernumber, uint8_t change, void *userdata);
|
||||
static void onGroupTitleChange(Tox*, int groupnumber, int peernumber, const uint8_t* title, uint8_t len, void* _core);
|
||||
static void onFileSendRequestCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
|
||||
const uint8_t *filename, uint16_t filename_length, void *userdata);
|
||||
static void onFileControlCallback(Tox *tox, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
|
||||
@ -227,21 +248,18 @@ private:
|
||||
static void onAvReject(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvEnd(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvRinging(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvStarting(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvEnding(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvRequestTimeout(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvPeerTimeout(void* toxav, int32_t call_index, void* core);
|
||||
static void onAvMediaChange(void *toxav, int32_t call_index, void* core);
|
||||
|
||||
static void playGroupAudio(Tox* tox, int groupnumber, int friendgroupnumber, const int16_t* out_audio,
|
||||
unsigned out_audio_samples, uint8_t decoder_channels, unsigned audio_sample_rate, void* userdata);
|
||||
static void sendGroupCallAudio(int groupId, ToxAv* toxav);
|
||||
|
||||
static void prepareCall(int friendId, int callId, ToxAv *toxav, bool videoEnabled);
|
||||
static void cleanupCall(int callId);
|
||||
static void playCallAudio(ToxAv *toxav, int32_t callId, int16_t *data, int samples, void *user_data); // Callback
|
||||
static void playCallAudio(void *toxav, int32_t callId, const int16_t *data, uint16_t samples, void *user_data); // Callback
|
||||
static void sendCallAudio(int callId, ToxAv* toxav);
|
||||
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate);
|
||||
static void playCallVideo(ToxAv* toxav, int32_t callId, vpx_image_t* img, void *user_data);
|
||||
static void playCallVideo(void *toxav, int32_t callId, const vpx_image_t* img, void *user_data);
|
||||
void sendCallVideo(int callId);
|
||||
|
||||
bool checkConnection();
|
||||
@ -267,7 +285,11 @@ private:
|
||||
QList<DhtServer> dhtServerList;
|
||||
int dhtServerId;
|
||||
static QList<ToxFile> fileSendQueue, fileRecvQueue;
|
||||
static ToxCall calls[];
|
||||
static ToxCall calls[TOXAV_MAX_CALLS];
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
static AudioFilterer * filterer[TOXAV_MAX_CALLS];
|
||||
#endif
|
||||
static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
|
||||
QMutex fileSendMutex, messageSendMutex;
|
||||
bool ready;
|
||||
|
||||
@ -276,10 +298,9 @@ private:
|
||||
static const int videobufsize;
|
||||
static uint8_t* videobuf;
|
||||
|
||||
static ALCdevice* alOutDev, *alInDev;
|
||||
static ALCcontext* alContext;
|
||||
public:
|
||||
static ALuint alMainSource;
|
||||
static QThread *coreThread;
|
||||
|
||||
friend class Audio; ///< Audio can access our calls directly to reduce latency
|
||||
};
|
||||
|
||||
#endif // CORE_HPP
|
||||
|
320
src/coreav.cpp
@ -16,17 +16,21 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "video/camera.h"
|
||||
#include "audio.h"
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
#include "audiofilterer.h"
|
||||
#endif
|
||||
#include "misc/settings.h"
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
|
||||
ToxCall Core::calls[TOXAV_MAX_CALLS];
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
AudioFilterer * Core::filterer[TOXAV_MAX_CALLS] { nullptr};
|
||||
#endif
|
||||
const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4};
|
||||
uint8_t* Core::videobuf;
|
||||
|
||||
ALCdevice* Core::alOutDev, *Core::alInDev;
|
||||
ALCcontext* Core::alContext;
|
||||
ALuint Core::alMainSource;
|
||||
|
||||
bool Core::anyActiveCalls()
|
||||
{
|
||||
for (auto& call : calls)
|
||||
@ -48,11 +52,12 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled
|
||||
calls[callId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
|
||||
calls[callId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
|
||||
calls[callId].videoEnabled = videoEnabled;
|
||||
toxav_prepare_transmission(toxav, callId, av_jbufdc, av_VADd, videoEnabled);
|
||||
int r = toxav_prepare_transmission(toxav, callId, videoEnabled);
|
||||
if (r < 0)
|
||||
qWarning() << QString("Error starting call %1: toxav_prepare_transmission failed with %2").arg(callId).arg(r);
|
||||
|
||||
// Audio
|
||||
alGenSources(1, &calls[callId].alSource);
|
||||
alcCaptureStart(alInDev);
|
||||
Audio::suscribeInput();
|
||||
|
||||
// Go
|
||||
calls[callId].active = true;
|
||||
@ -67,6 +72,19 @@ void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled
|
||||
calls[callId].sendVideoTimer->start();
|
||||
Camera::getInstance()->subscribe();
|
||||
}
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
if (Settings::getInstance().getFilterAudio())
|
||||
{
|
||||
Core::filterer[callId] = new AudioFilterer();
|
||||
filterer[callId]->startFilter(48000);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete filterer[callId];
|
||||
filterer[callId] = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Core::onAvMediaChange(void* toxav, int32_t callId, void* core)
|
||||
@ -81,7 +99,7 @@ void Core::onAvMediaChange(void* toxav, int32_t callId, void* core)
|
||||
|
||||
qDebug() << "Core: Received media change from friend "<<friendId;
|
||||
|
||||
if (settings.call_type == TypeAudio)
|
||||
if (settings.call_type == av_TypeAudio)
|
||||
{
|
||||
calls[callId].videoEnabled = false;
|
||||
calls[callId].sendVideoTimer->stop();
|
||||
@ -113,14 +131,14 @@ void Core::answerCall(int callId)
|
||||
|
||||
ToxAvCSettings* transSettings = new ToxAvCSettings;
|
||||
int err = toxav_get_peer_csettings(toxav, callId, 0, transSettings);
|
||||
if (err != ErrorNone)
|
||||
if (err != av_ErrorNone)
|
||||
{
|
||||
qWarning() << "Core::answerCall: error getting call settings";
|
||||
delete transSettings;
|
||||
return;
|
||||
}
|
||||
|
||||
if (transSettings->call_type == TypeVideo)
|
||||
if (transSettings->call_type == av_TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: answering call %1 with video").arg(callId);
|
||||
toxav_answer(toxav, callId, transSettings);
|
||||
@ -150,7 +168,7 @@ void Core::startCall(int friendId, bool video)
|
||||
if (video)
|
||||
{
|
||||
qDebug() << QString("Core: Starting new call with %1 with video").arg(friendId);
|
||||
cSettings.call_type = TypeVideo;
|
||||
cSettings.call_type = av_TypeVideo;
|
||||
if (toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME) == 0)
|
||||
{
|
||||
calls[callId].videoEnabled=true;
|
||||
@ -165,7 +183,7 @@ void Core::startCall(int friendId, bool video)
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: Starting new call with %1 without video").arg(friendId);
|
||||
cSettings.call_type = TypeAudio;
|
||||
cSettings.call_type = av_TypeAudio;
|
||||
if (toxav_call(toxav, &callId, friendId, &cSettings, TOXAV_RINGING_TIME) == 0)
|
||||
{
|
||||
calls[callId].videoEnabled=false;
|
||||
@ -176,7 +194,6 @@ void Core::startCall(int friendId, bool video)
|
||||
emit avCallFailed(friendId);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,18 +213,21 @@ void Core::cleanupCall(int callId)
|
||||
calls[callId].sendVideoTimer->stop();
|
||||
if (calls[callId].videoEnabled)
|
||||
Camera::getInstance()->unsubscribe();
|
||||
alcCaptureStop(alInDev);
|
||||
Audio::unsuscribeInput();
|
||||
}
|
||||
|
||||
void Core::playCallAudio(ToxAv* toxav, int32_t callId, int16_t *data, int samples, void *user_data)
|
||||
void Core::playCallAudio(void* toxav, int32_t callId, const int16_t *data, uint16_t samples, void *user_data)
|
||||
{
|
||||
Q_UNUSED(user_data);
|
||||
|
||||
if (!calls[callId].active)
|
||||
return;
|
||||
|
||||
if (!calls[callId].alSource)
|
||||
alGenSources(1, &calls[callId].alSource);
|
||||
|
||||
ToxAvCSettings dest;
|
||||
if(toxav_get_peer_csettings(toxav, callId, 0, &dest) == 0)
|
||||
if (toxav_get_peer_csettings((ToxAv*)toxav, callId, 0, &dest) == 0)
|
||||
playAudioBuffer(calls[callId].alSource, data, samples, dest.audio_channels, dest.audio_sample_rate);
|
||||
}
|
||||
|
||||
@ -216,41 +236,51 @@ void Core::sendCallAudio(int callId, ToxAv* toxav)
|
||||
if (!calls[callId].active)
|
||||
return;
|
||||
|
||||
if (calls[callId].muteMic)
|
||||
if (calls[callId].muteMic || !Audio::alInDev)
|
||||
{
|
||||
calls[callId].sendAudioTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000;
|
||||
uint8_t buf[framesize*2], dest[framesize*2];
|
||||
const int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000 * av_DefaultSettings.audio_channels;
|
||||
const int bufsize = framesize * 2 * av_DefaultSettings.audio_channels;
|
||||
uint8_t buf[bufsize], dest[bufsize];
|
||||
|
||||
bool frame = false;
|
||||
ALint samples;
|
||||
alcGetIntegerv(alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
|
||||
if(samples >= framesize)
|
||||
alcGetIntegerv(Audio::alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
|
||||
if (samples >= framesize)
|
||||
{
|
||||
alcCaptureSamples(alInDev, buf, framesize);
|
||||
memset(buf, 0, bufsize); // Avoid uninitialized values (Valgrind)
|
||||
alcCaptureSamples(Audio::alInDev, buf, framesize);
|
||||
frame = 1;
|
||||
}
|
||||
|
||||
if(frame)
|
||||
if (frame)
|
||||
{
|
||||
int r;
|
||||
if((r = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize)) < 0)
|
||||
if ((r = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize)) < 0)
|
||||
{
|
||||
qDebug() << "Core: toxav_prepare_audio_frame error";
|
||||
calls[callId].sendAudioTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
if((r = toxav_send_audio(toxav, callId, dest, r)) < 0)
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
if (filterer[callId])
|
||||
{
|
||||
filterer[callId]->filterAudio((int16_t*) buf, framesize);
|
||||
}
|
||||
#endif
|
||||
if ((r = toxav_send_audio(toxav, callId, dest, r)) < 0)
|
||||
{
|
||||
qDebug() << "Core: toxav_send_audio error";
|
||||
}
|
||||
}
|
||||
calls[callId].sendAudioTimer->start();
|
||||
}
|
||||
|
||||
void Core::playCallVideo(ToxAv*, int32_t callId, vpx_image_t* img, void *user_data)
|
||||
void Core::playCallVideo(void*, int32_t callId, const vpx_image_t* img, void *user_data)
|
||||
{
|
||||
Q_UNUSED(user_data);
|
||||
|
||||
@ -258,8 +288,6 @@ void Core::playCallVideo(ToxAv*, int32_t callId, vpx_image_t* img, void *user_da
|
||||
return;
|
||||
|
||||
calls[callId].videoSource.pushVPXFrame(img);
|
||||
|
||||
vpx_img_free(img);
|
||||
}
|
||||
|
||||
void Core::sendCallVideo(int callId)
|
||||
@ -271,7 +299,7 @@ void Core::sendCallVideo(int callId)
|
||||
if (frame.w && frame.h)
|
||||
{
|
||||
int result;
|
||||
if((result = toxav_prepare_video_frame(toxav, callId, videobuf, videobufsize, &frame)) < 0)
|
||||
if ((result = toxav_prepare_video_frame(toxav, callId, videobuf, videobufsize, &frame)) < 0)
|
||||
{
|
||||
qDebug() << QString("Core: toxav_prepare_video_frame: error %1").arg(result);
|
||||
vpx_img_free(&frame);
|
||||
@ -279,7 +307,7 @@ void Core::sendCallVideo(int callId)
|
||||
return;
|
||||
}
|
||||
|
||||
if((result = toxav_send_video(toxav, callId, (uint8_t*)videobuf, result)) < 0)
|
||||
if ((result = toxav_send_video(toxav, callId, (uint8_t*)videobuf, result)) < 0)
|
||||
qDebug() << QString("Core: toxav_send_video error: %1").arg(result);
|
||||
|
||||
vpx_img_free(&frame);
|
||||
@ -294,14 +322,16 @@ void Core::sendCallVideo(int callId)
|
||||
|
||||
void Core::micMuteToggle(int callId)
|
||||
{
|
||||
if (calls[callId].active) {
|
||||
if (calls[callId].active)
|
||||
{
|
||||
calls[callId].muteMic = !calls[callId].muteMic;
|
||||
}
|
||||
}
|
||||
|
||||
void Core::volMuteToggle(int callId)
|
||||
{
|
||||
if (calls[callId].active) {
|
||||
if (calls[callId].active)
|
||||
{
|
||||
calls[callId].muteVol = !calls[callId].muteVol;
|
||||
alSourcef(calls[callId].alSource, AL_GAIN, calls[callId].muteVol ? 0.f : 1.f);
|
||||
}
|
||||
@ -321,6 +351,15 @@ void Core::onAvCancel(void* _toxav, int32_t callId, void* core)
|
||||
|
||||
calls[callId].active = false;
|
||||
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
if (filterer[callId])
|
||||
{
|
||||
filterer[callId]->closeFilter();
|
||||
delete filterer[callId];
|
||||
filterer[callId] = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
emit static_cast<Core*>(core)->avCancel(friendId, callId);
|
||||
}
|
||||
|
||||
@ -379,58 +418,58 @@ void Core::onAvRinging(void* _toxav, int32_t call_index, void* core)
|
||||
}
|
||||
}
|
||||
|
||||
void Core::onAvStarting(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
//void Core::onAvStarting(void* _toxav, int32_t call_index, void* core)
|
||||
//{
|
||||
// ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV starting";
|
||||
return;
|
||||
}
|
||||
// int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
// if (friendId < 0)
|
||||
// {
|
||||
// qWarning() << "Core: Received invalid AV starting";
|
||||
// return;
|
||||
// }
|
||||
|
||||
ToxAvCSettings* transSettings = new ToxAvCSettings;
|
||||
int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings);
|
||||
if (err != ErrorNone)
|
||||
{
|
||||
qWarning() << "Core::onAvStarting: error getting call type";
|
||||
delete transSettings;
|
||||
return;
|
||||
}
|
||||
// ToxAvCSettings* transSettings = new ToxAvCSettings;
|
||||
// int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings);
|
||||
// if (err != ErrorNone)
|
||||
// {
|
||||
// qWarning() << "Core::onAvStarting: error getting call type";
|
||||
// delete transSettings;
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (transSettings->call_type == TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: AV starting from %1 with video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, true);
|
||||
emit static_cast<Core*>(core)->avStarting(friendId, call_index, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << QString("Core: AV starting from %1 without video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, false);
|
||||
emit static_cast<Core*>(core)->avStarting(friendId, call_index, false);
|
||||
}
|
||||
// if (transSettings->call_type == TypeVideo)
|
||||
// {
|
||||
// qDebug() << QString("Core: AV starting from %1 with video").arg(friendId);
|
||||
// prepareCall(friendId, call_index, toxav, true);
|
||||
// emit static_cast<Core*>(core)->avStarting(friendId, call_index, true);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// qDebug() << QString("Core: AV starting from %1 without video").arg(friendId);
|
||||
// prepareCall(friendId, call_index, toxav, false);
|
||||
// emit static_cast<Core*>(core)->avStarting(friendId, call_index, false);
|
||||
// }
|
||||
|
||||
delete transSettings;
|
||||
}
|
||||
// delete transSettings;
|
||||
//}
|
||||
|
||||
void Core::onAvEnding(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
//void Core::onAvEnding(void* _toxav, int32_t call_index, void* core)
|
||||
//{
|
||||
// ToxAv* toxav = static_cast<ToxAv*>(_toxav);
|
||||
|
||||
int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
if (friendId < 0)
|
||||
{
|
||||
qWarning() << "Core: Received invalid AV ending";
|
||||
return;
|
||||
}
|
||||
qDebug() << QString("Core: AV ending from %1").arg(friendId);
|
||||
// int friendId = toxav_get_peer_id(toxav, call_index, 0);
|
||||
// if (friendId < 0)
|
||||
// {
|
||||
// qWarning() << "Core: Received invalid AV ending";
|
||||
// return;
|
||||
// }
|
||||
// qDebug() << QString("Core: AV ending from %1").arg(friendId);
|
||||
|
||||
cleanupCall(call_index);
|
||||
// cleanupCall(call_index);
|
||||
|
||||
emit static_cast<Core*>(core)->avEnding(friendId, call_index);
|
||||
}
|
||||
// emit static_cast<Core*>(core)->avEnding(friendId, call_index);
|
||||
//}
|
||||
|
||||
void Core::onAvRequestTimeout(void* _toxav, int32_t call_index, void* core)
|
||||
{
|
||||
@ -480,14 +519,14 @@ void Core::onAvInvite(void* _toxav, int32_t call_index, void* core)
|
||||
|
||||
ToxAvCSettings* transSettings = new ToxAvCSettings;
|
||||
int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings);
|
||||
if (err != ErrorNone)
|
||||
if (err != av_ErrorNone)
|
||||
{
|
||||
qWarning() << "Core::onAvInvite: error getting call type";
|
||||
delete transSettings;
|
||||
return;
|
||||
}
|
||||
|
||||
if (transSettings->call_type == TypeVideo)
|
||||
if (transSettings->call_type == av_TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: AV invite from %1 with video").arg(friendId);
|
||||
emit static_cast<Core*>(core)->avInvite(friendId, call_index, true);
|
||||
@ -514,14 +553,14 @@ void Core::onAvStart(void* _toxav, int32_t call_index, void* core)
|
||||
|
||||
ToxAvCSettings* transSettings = new ToxAvCSettings;
|
||||
int err = toxav_get_peer_csettings(toxav, call_index, 0, transSettings);
|
||||
if (err != ErrorNone)
|
||||
if (err != av_ErrorNone)
|
||||
{
|
||||
qWarning() << "Core::onAvStart: error getting call type";
|
||||
delete transSettings;
|
||||
return;
|
||||
}
|
||||
|
||||
if (transSettings->call_type == TypeVideo)
|
||||
if (transSettings->call_type == av_TypeVideo)
|
||||
{
|
||||
qDebug() << QString("Core: AV start from %1 with video").arg(friendId);
|
||||
prepareCall(friendId, call_index, toxav, true);
|
||||
@ -540,26 +579,26 @@ void Core::onAvStart(void* _toxav, int32_t call_index, void* core)
|
||||
// This function's logic was shamelessly stolen from uTox
|
||||
void Core::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate)
|
||||
{
|
||||
if(!channels || channels > 2)
|
||||
if (!channels || channels > 2)
|
||||
{
|
||||
qWarning() << "Core::playAudioBuffer: trying to play on "<<channels<<" channels! Giving up.";
|
||||
return;
|
||||
}
|
||||
|
||||
ALuint bufid;
|
||||
ALint processed, queued;
|
||||
ALint processed = 0, queued = 16;
|
||||
alGetSourcei(alSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
alGetSourcei(alSource, AL_BUFFERS_QUEUED, &queued);
|
||||
alSourcei(alSource, AL_LOOPING, AL_FALSE);
|
||||
|
||||
if(processed)
|
||||
if (processed)
|
||||
{
|
||||
ALuint bufids[processed];
|
||||
alSourceUnqueueBuffers(alSource, processed, bufids);
|
||||
alDeleteBuffers(processed - 1, bufids + 1);
|
||||
bufid = bufids[0];
|
||||
}
|
||||
else if(queued < 16)
|
||||
else if (queued < 32)
|
||||
{
|
||||
alGenBuffers(1, &bufid);
|
||||
}
|
||||
@ -575,10 +614,10 @@ void Core::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, un
|
||||
|
||||
ALint state;
|
||||
alGetSourcei(alSource, AL_SOURCE_STATE, &state);
|
||||
if(state != AL_PLAYING)
|
||||
if (state != AL_PLAYING)
|
||||
{
|
||||
alSourcePlay(alSource);
|
||||
qDebug() << "Core: Starting audio source " << (int)alSource;
|
||||
//qDebug() << "Core: Starting audio source " << (int)alSource;
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,10 +626,109 @@ VideoSource *Core::getVideoSourceFromCall(int callNumber)
|
||||
return &calls[callNumber].videoSource;
|
||||
}
|
||||
|
||||
void Core::playGroupAudio(Tox* /*tox*/, int /*groupnumber*/, int /*friendgroupnumber*/, const int16_t* out_audio,
|
||||
unsigned out_audio_samples, uint8_t decoder_channels, unsigned audio_sample_rate, void* /*userdata*/)
|
||||
void Core::joinGroupCall(int groupId)
|
||||
{
|
||||
/// TODO: FIXME: Don't play groupchat audio on the main source!
|
||||
/// We'll need some sort of call[] array but for groupchats, when that's done use this source
|
||||
playAudioBuffer(alMainSource, out_audio, out_audio_samples, decoder_channels, audio_sample_rate);
|
||||
qDebug() << QString("Core: Joining group call %1").arg(groupId);
|
||||
groupCalls[groupId].groupId = groupId;
|
||||
groupCalls[groupId].muteMic = false;
|
||||
groupCalls[groupId].muteVol = false;
|
||||
// the following three lines are also now redundant from startCall, but are
|
||||
// necessary there for outbound and here for inbound
|
||||
groupCalls[groupId].codecSettings = av_DefaultSettings;
|
||||
groupCalls[groupId].codecSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
|
||||
groupCalls[groupId].codecSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
|
||||
|
||||
// Audio
|
||||
Audio::suscribeInput();
|
||||
|
||||
// Go
|
||||
Core* core = Core::getInstance();
|
||||
ToxAv* toxav = core->toxav;
|
||||
|
||||
groupCalls[groupId].sendAudioTimer = new QTimer();
|
||||
groupCalls[groupId].active = true;
|
||||
groupCalls[groupId].sendAudioTimer->setInterval(5);
|
||||
groupCalls[groupId].sendAudioTimer->setSingleShot(true);
|
||||
connect(groupCalls[groupId].sendAudioTimer, &QTimer::timeout, [=](){sendGroupCallAudio(groupId,toxav);});
|
||||
groupCalls[groupId].sendAudioTimer->start();
|
||||
}
|
||||
|
||||
void Core::leaveGroupCall(int groupId)
|
||||
{
|
||||
qDebug() << QString("Core: Leaving group call %1").arg(groupId);
|
||||
groupCalls[groupId].active = false;
|
||||
disconnect(groupCalls[groupId].sendAudioTimer,0,0,0);
|
||||
groupCalls[groupId].sendAudioTimer->stop();
|
||||
groupCalls[groupId].alSources.clear();
|
||||
Audio::unsuscribeInput();
|
||||
delete groupCalls[groupId].sendAudioTimer;
|
||||
}
|
||||
|
||||
void Core::sendGroupCallAudio(int groupId, ToxAv* toxav)
|
||||
{
|
||||
if (!groupCalls[groupId].active)
|
||||
return;
|
||||
|
||||
if (groupCalls[groupId].muteMic || !Audio::alInDev)
|
||||
{
|
||||
groupCalls[groupId].sendAudioTimer->start();
|
||||
return;
|
||||
}
|
||||
|
||||
const int framesize = (groupCalls[groupId].codecSettings.audio_frame_duration * groupCalls[groupId].codecSettings.audio_sample_rate) / 1000 * av_DefaultSettings.audio_channels;
|
||||
const int bufsize = framesize * 2 * av_DefaultSettings.audio_channels;
|
||||
uint8_t buf[bufsize];
|
||||
|
||||
bool frame = false;
|
||||
ALint samples;
|
||||
alcGetIntegerv(Audio::alInDev, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
|
||||
if (samples >= framesize)
|
||||
{
|
||||
memset(buf, 0, bufsize); // Avoid uninitialized values (Valgrind)
|
||||
alcCaptureSamples(Audio::alInDev, buf, framesize);
|
||||
frame = 1;
|
||||
}
|
||||
|
||||
if (frame)
|
||||
{
|
||||
int r;
|
||||
if ((r = toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf,
|
||||
framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate)) < 0)
|
||||
{
|
||||
qDebug() << "Core: toxav_group_send_audio error";
|
||||
groupCalls[groupId].sendAudioTimer->start();
|
||||
return;
|
||||
}
|
||||
}
|
||||
groupCalls[groupId].sendAudioTimer->start();
|
||||
}
|
||||
|
||||
void Core::disableGroupCallMic(int groupId)
|
||||
{
|
||||
groupCalls[groupId].muteMic = true;
|
||||
}
|
||||
|
||||
void Core::disableGroupCallVol(int groupId)
|
||||
{
|
||||
groupCalls[groupId].muteVol = true;
|
||||
}
|
||||
|
||||
void Core::enableGroupCallMic(int groupId)
|
||||
{
|
||||
groupCalls[groupId].muteMic = false;
|
||||
}
|
||||
|
||||
void Core::enableGroupCallVol(int groupId)
|
||||
{
|
||||
groupCalls[groupId].muteVol = false;
|
||||
}
|
||||
|
||||
bool Core::isGroupCallMicEnabled(int groupId)
|
||||
{
|
||||
return !groupCalls[groupId].muteMic;
|
||||
}
|
||||
|
||||
bool Core::isGroupCallVolEnabled(int groupId)
|
||||
{
|
||||
return !groupCalls[groupId].muteVol;
|
||||
}
|
||||
|
13
src/coreav.h
@ -1,6 +1,7 @@
|
||||
#ifndef COREAV_H
|
||||
#define COREAV_H
|
||||
|
||||
#include <QHash>
|
||||
#include <tox/toxav.h>
|
||||
#include "video/netvideosource.h"
|
||||
|
||||
@ -16,7 +17,6 @@ class QTimer;
|
||||
|
||||
struct ToxCall
|
||||
{
|
||||
public:
|
||||
ToxAvCSettings codecSettings;
|
||||
QTimer *sendAudioTimer, *sendVideoTimer;
|
||||
int callId;
|
||||
@ -29,4 +29,15 @@ public:
|
||||
NetVideoSource videoSource;
|
||||
};
|
||||
|
||||
struct ToxGroupCall
|
||||
{
|
||||
ToxAvCSettings codecSettings;
|
||||
QTimer *sendAudioTimer;
|
||||
int groupId;
|
||||
bool active = false;
|
||||
bool muteMic;
|
||||
bool muteVol;
|
||||
QHash<int, ALuint> alSources;
|
||||
};
|
||||
|
||||
#endif // COREAV_H
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "core.h"
|
||||
#include "misc/settings.h"
|
||||
#include "misc/style.h"
|
||||
#include "src/friendlist.h"
|
||||
#include "src/friend.h"
|
||||
#include <math.h>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
@ -208,9 +210,9 @@ bool isFileWritable(QString& path)
|
||||
|
||||
void FileTransferInstance::acceptRecvRequest()
|
||||
{
|
||||
QString path = Settings::getInstance().getAutoAcceptDir(Core::getInstance()->getFriendAddress(friendId));
|
||||
QString path = Settings::getInstance().getAutoAcceptDir(FriendList::findFriend(friendId)->getToxID());
|
||||
|
||||
if (path.isEmpty())
|
||||
if (path.isEmpty() && Settings::getInstance().getAutoSaveEnabled())
|
||||
path = Settings::getInstance().getGlobalAutoAcceptDir();
|
||||
|
||||
if (!path.isEmpty())
|
||||
|
@ -19,15 +19,22 @@
|
||||
#include "widget/friendwidget.h"
|
||||
#include "widget/form/chatform.h"
|
||||
#include "widget/widget.h"
|
||||
#include "src/core.h"
|
||||
#include "src/misc/settings.h"
|
||||
|
||||
Friend::Friend(int FriendId, QString UserId)
|
||||
Friend::Friend(int FriendId, const ToxID &UserId)
|
||||
: friendId(FriendId)
|
||||
{
|
||||
hasNewEvents = 0;
|
||||
friendStatus = Status::Offline;
|
||||
userID = ToxID::fromString(UserId);
|
||||
userName = UserId;
|
||||
widget = new FriendWidget(friendId, UserId);
|
||||
userID = UserId;
|
||||
userName = Core::getInstance()->getPeerName(UserId);
|
||||
if (userName.size() == 0)
|
||||
userName = UserId.publicKey;
|
||||
|
||||
userAlias = Settings::getInstance().getFriendAlias(UserId);
|
||||
|
||||
widget = new FriendWidget(friendId, getDisplayedName());
|
||||
chatForm = new ChatForm(this);
|
||||
}
|
||||
|
||||
@ -46,7 +53,7 @@ void Friend::setName(QString name)
|
||||
chatForm->setName(name);
|
||||
|
||||
if (widget->isActive())
|
||||
Widget::getInstance()->setWindowTitle(name + " - qTox");
|
||||
Widget::getInstance()->setWindowTitle(name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,6 +64,9 @@ void Friend::setAlias(QString name)
|
||||
|
||||
widget->setName(dispName);
|
||||
chatForm->setName(dispName);
|
||||
|
||||
if (widget->isActive())
|
||||
Widget::getInstance()->setWindowTitle(dispName);
|
||||
}
|
||||
|
||||
void Friend::setStatusMessage(QString message)
|
||||
|
@ -26,7 +26,7 @@ class ChatForm;
|
||||
struct Friend
|
||||
{
|
||||
public:
|
||||
Friend(int FriendId, QString UserId);
|
||||
Friend(int FriendId, const ToxID &UserId);
|
||||
~Friend();
|
||||
|
||||
void setName(QString name);
|
||||
|
@ -24,7 +24,7 @@
|
||||
QHash<int, Friend*> FriendList::friendList;
|
||||
QHash<QString, int> FriendList::tox2id;
|
||||
|
||||
Friend* FriendList::addFriend(int friendId, const QString& userId)
|
||||
Friend* FriendList::addFriend(int friendId, const ToxID& userId)
|
||||
{
|
||||
auto friendChecker = friendList.find(friendId);
|
||||
if (friendChecker != friendList.end())
|
||||
@ -32,7 +32,7 @@ Friend* FriendList::addFriend(int friendId, const QString& userId)
|
||||
|
||||
Friend* newfriend = new Friend(friendId, userId);
|
||||
friendList[friendId] = newfriend;
|
||||
tox2id[userId] = friendId;
|
||||
tox2id[userId.publicKey] = friendId;
|
||||
|
||||
return newfriend;
|
||||
}
|
||||
@ -61,17 +61,18 @@ void FriendList::clear()
|
||||
{
|
||||
for (auto friendptr : friendList)
|
||||
delete friendptr;
|
||||
friendList.clear();
|
||||
}
|
||||
|
||||
Friend* FriendList::findFriend(QString userId)
|
||||
Friend* FriendList::findFriend(const ToxID& userId)
|
||||
{
|
||||
auto id = tox2id.find(userId);
|
||||
auto id = tox2id.find(userId.publicKey);
|
||||
if (id != tox2id.end())
|
||||
{
|
||||
Friend *f = findFriend(*id);
|
||||
if (!f)
|
||||
return nullptr;
|
||||
if (f->getToxID() == ToxID::fromString(userId))
|
||||
if (f->getToxID() == userId)
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -21,14 +21,14 @@ template <class T> class QList;
|
||||
template <class A, class B> class QHash;
|
||||
struct Friend;
|
||||
class QString;
|
||||
struct ToxID;
|
||||
|
||||
class FriendList
|
||||
{
|
||||
public:
|
||||
FriendList();
|
||||
static Friend* addFriend(int friendId, const QString& userId);
|
||||
static Friend* addFriend(int friendId, const ToxID &userId);
|
||||
static Friend* findFriend(int friendId);
|
||||
static Friend* findFriend(QString userId);
|
||||
static Friend* findFriend(const ToxID &userId);
|
||||
static QList<Friend*> getAllFriends();
|
||||
static void removeFriend(int friendId, bool fake = false);
|
||||
static void clear();
|
||||
|
115
src/group.cpp
@ -20,11 +20,12 @@
|
||||
#include "friendlist.h"
|
||||
#include "friend.h"
|
||||
#include "core.h"
|
||||
#include "widget/widget.h"
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
|
||||
Group::Group(int GroupId, QString Name)
|
||||
: groupId(GroupId), nPeers{0}
|
||||
Group::Group(int GroupId, QString Name, bool IsAvGroupchat)
|
||||
: groupId(GroupId), nPeers{0}, avGroupchat{IsAvGroupchat}
|
||||
{
|
||||
widget = new GroupWidget(groupId, Name);
|
||||
chatForm = new GroupChatForm(this);
|
||||
@ -42,6 +43,7 @@ Group::~Group()
|
||||
delete widget;
|
||||
}
|
||||
|
||||
/*
|
||||
void Group::addPeer(int peerId, QString name)
|
||||
{
|
||||
if (peers.contains(peerId))
|
||||
@ -62,10 +64,119 @@ void Group::removePeer(int peerId)
|
||||
widget->onUserListChanged();
|
||||
chatForm->onUserListChanged();
|
||||
}
|
||||
*/
|
||||
|
||||
void Group::updatePeer(int peerId, QString name)
|
||||
{
|
||||
ToxID id = Core::getInstance()->getGroupPeerToxID(groupId, peerId);
|
||||
QString toxid = id.publicKey;
|
||||
peers[peerId] = name;
|
||||
toxids[toxid] = name;
|
||||
|
||||
Friend *f = FriendList::findFriend(id);
|
||||
if (f)
|
||||
{
|
||||
peers[peerId] = f->getDisplayedName();
|
||||
toxids[toxid] = f->getDisplayedName();
|
||||
}
|
||||
|
||||
widget->onUserListChanged();
|
||||
chatForm->onUserListChanged();
|
||||
}
|
||||
|
||||
void Group::setName(const QString& name)
|
||||
{
|
||||
widget->setName(name);
|
||||
chatForm->setName(name);
|
||||
|
||||
if (widget->isActive())
|
||||
Widget::getInstance()->setWindowTitle(name);
|
||||
}
|
||||
|
||||
void Group::regeneratePeerList()
|
||||
{
|
||||
QList<QString> peerLst = Core::getInstance()->getGroupPeerNames(groupId);
|
||||
peers.clear();
|
||||
toxids.clear();
|
||||
nPeers = peerLst.size();
|
||||
for (int i = 0; i < peerLst.size(); i++)
|
||||
{
|
||||
ToxID id = Core::getInstance()->getGroupPeerToxID(groupId, i);
|
||||
QString toxid = id.publicKey;
|
||||
peers[i] = peerLst.at(i);
|
||||
toxids[toxid] = peerLst.at(i);
|
||||
|
||||
Friend *f = FriendList::findFriend(id);
|
||||
if (f)
|
||||
{
|
||||
peers[i] = f->getDisplayedName();
|
||||
toxids[toxid] = f->getDisplayedName();
|
||||
}
|
||||
}
|
||||
|
||||
widget->onUserListChanged();
|
||||
chatForm->onUserListChanged();
|
||||
}
|
||||
|
||||
bool Group::isAvGroupchat() const
|
||||
{
|
||||
return avGroupchat;
|
||||
}
|
||||
|
||||
int Group::getGroupId() const
|
||||
{
|
||||
return groupId;
|
||||
}
|
||||
|
||||
int Group::getPeersCount() const
|
||||
{
|
||||
return nPeers;
|
||||
}
|
||||
|
||||
GroupChatForm *Group::getChatForm()
|
||||
{
|
||||
return chatForm;
|
||||
}
|
||||
|
||||
GroupWidget *Group::getGroupWidget()
|
||||
{
|
||||
return widget;
|
||||
}
|
||||
|
||||
QStringList Group::getPeerList() const
|
||||
{
|
||||
return peers.values();
|
||||
}
|
||||
|
||||
void Group::setEventFlag(int f)
|
||||
{
|
||||
hasNewMessages = f;
|
||||
}
|
||||
|
||||
int Group::getEventFlag() const
|
||||
{
|
||||
return hasNewMessages;
|
||||
}
|
||||
|
||||
void Group::setMentionedFlag(int f)
|
||||
{
|
||||
userWasMentioned = f;
|
||||
}
|
||||
|
||||
int Group::getMentionedFlag() const
|
||||
{
|
||||
return userWasMentioned;
|
||||
}
|
||||
|
||||
QString Group::resolveToxID(const ToxID &id) const
|
||||
{
|
||||
QString key = id.publicKey;
|
||||
auto it = toxids.find(key);
|
||||
|
||||
if (it != toxids.end())
|
||||
{
|
||||
return *it;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
40
src/group.h
@ -25,24 +25,50 @@
|
||||
struct Friend;
|
||||
class GroupWidget;
|
||||
class GroupChatForm;
|
||||
struct ToxID;
|
||||
|
||||
class Group : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Group(int GroupId, QString Name);
|
||||
~Group();
|
||||
Group(int GroupId, QString Name, bool IsAvGroupchat);
|
||||
virtual ~Group();
|
||||
|
||||
bool isAvGroupchat() const;
|
||||
int getGroupId() const;
|
||||
int getPeersCount() const;
|
||||
void regeneratePeerList();
|
||||
QStringList getPeerList() const;
|
||||
|
||||
GroupChatForm *getChatForm();
|
||||
GroupWidget *getGroupWidget();
|
||||
|
||||
void setEventFlag(int f);
|
||||
int getEventFlag() const;
|
||||
|
||||
void setMentionedFlag(int f);
|
||||
int getMentionedFlag() const;
|
||||
|
||||
/*
|
||||
void addPeer(int peerId, QString name);
|
||||
void removePeer(int peerId);
|
||||
void updatePeer(int peerId, QString newName);
|
||||
*/
|
||||
|
||||
public:
|
||||
int groupId;
|
||||
QMap<int,QString> peers;
|
||||
int nPeers;
|
||||
void updatePeer(int peerId, QString newName);
|
||||
void setName(const QString& name);
|
||||
|
||||
QString resolveToxID(const ToxID &id) const;
|
||||
|
||||
private:
|
||||
GroupWidget* widget;
|
||||
GroupChatForm* chatForm;
|
||||
QMap<int, QString> peers;
|
||||
QMap<QString, QString> toxids;
|
||||
int hasNewMessages, userWasMentioned;
|
||||
int groupId;
|
||||
int nPeers;
|
||||
bool avGroupchat;
|
||||
|
||||
};
|
||||
|
||||
#endif // GROUP_H
|
||||
|
@ -16,32 +16,54 @@
|
||||
|
||||
#include "grouplist.h"
|
||||
#include "group.h"
|
||||
#include <QHash>
|
||||
#include <QDebug>
|
||||
|
||||
QList<Group*> GroupList::groupList;
|
||||
QHash<int, Group*> GroupList::groupList;
|
||||
|
||||
Group* GroupList::addGroup(int groupId, const QString& name)
|
||||
Group* GroupList::addGroup(int groupId, const QString& name, bool isAvGroupchat)
|
||||
{
|
||||
Group* newGroup = new Group(groupId, name);
|
||||
groupList.append(newGroup);
|
||||
auto checker = groupList.find(groupId);
|
||||
if (checker != groupList.end())
|
||||
qWarning() << "GroupList::addGroup: groupId already taken";
|
||||
|
||||
Group* newGroup = new Group(groupId, name, isAvGroupchat);
|
||||
groupList[groupId] = newGroup;
|
||||
|
||||
return newGroup;
|
||||
}
|
||||
|
||||
Group* GroupList::findGroup(int groupId)
|
||||
{
|
||||
for (Group* g : groupList)
|
||||
if (g->groupId == groupId)
|
||||
return g;
|
||||
auto g_it = groupList.find(groupId);
|
||||
if (g_it != groupList.end())
|
||||
return *g_it;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GroupList::removeGroup(int groupId, bool /*fake*/)
|
||||
{
|
||||
for (int i=0; i<groupList.size(); i++)
|
||||
auto g_it = groupList.find(groupId);
|
||||
if (g_it != groupList.end())
|
||||
{
|
||||
if (groupList[i]->groupId == groupId)
|
||||
{
|
||||
groupList.removeAt(i);
|
||||
return;
|
||||
}
|
||||
groupList.erase(g_it);
|
||||
}
|
||||
}
|
||||
|
||||
QList<Group *> GroupList::getAllGroups()
|
||||
{
|
||||
QList<Group*> res;
|
||||
|
||||
for (auto it : groupList)
|
||||
res.append(it);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void GroupList::clear()
|
||||
{
|
||||
for (auto groupptr : groupList)
|
||||
delete groupptr;
|
||||
groupList.clear();
|
||||
}
|
||||
|
@ -17,21 +17,22 @@
|
||||
#ifndef GROUPLIST_H
|
||||
#define GROUPLIST_H
|
||||
|
||||
template <typename T>
|
||||
class QList;
|
||||
template <class A, class B> class QHash;
|
||||
template <class T> class QList;
|
||||
class Group;
|
||||
class QString;
|
||||
|
||||
class GroupList
|
||||
{
|
||||
public:
|
||||
GroupList();
|
||||
static Group* addGroup(int groupId, const QString& name);
|
||||
static Group* addGroup(int groupId, const QString& name, bool isAvGroupchat);
|
||||
static Group* findGroup(int groupId);
|
||||
static void removeGroup(int groupId, bool fake = false);
|
||||
static QList<Group*> getAllGroups();
|
||||
static void clear();
|
||||
|
||||
public:
|
||||
static QList<Group*> groupList;
|
||||
private:
|
||||
static QHash<int, Group*> groupList;
|
||||
};
|
||||
|
||||
#endif // GROUPLIST_H
|
||||
|
@ -122,7 +122,7 @@ HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
||||
|
||||
if (idCur != idMax)
|
||||
{
|
||||
QString cmd = QString("INSERT INTO sent_status (id, status) VALUES (%1, 1)").arg(idMax);
|
||||
QString cmd = QString("INSERT INTO sent_status (id, status) VALUES (%1, 1);").arg(idMax);
|
||||
db->exec(cmd);
|
||||
}
|
||||
}
|
||||
@ -130,6 +130,8 @@ HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
||||
updateChatsID();
|
||||
updateAliases();
|
||||
|
||||
setSyncType(Settings::getInstance().getDbSyncType());
|
||||
|
||||
QSqlQuery sqlAnswer = db->exec("select seq from sqlite_sequence where name=\"history\";");
|
||||
sqlAnswer.first();
|
||||
messageID = sqlAnswer.value(0).toInt();
|
||||
@ -145,10 +147,12 @@ int HistoryKeeper::addChatEntry(const QString& chat, const QString& message, con
|
||||
int chat_id = getChatID(chat, ctSingle).first;
|
||||
int sender_id = getAliasID(sender);
|
||||
|
||||
db->exec(QString("INSERT INTO history (timestamp, chat_id, sender, message)") +
|
||||
db->exec("BEGIN TRANSACTION;");
|
||||
db->exec(QString("INSERT INTO history (timestamp, chat_id, sender, message) ") +
|
||||
QString("VALUES (%1, %2, %3, '%4');")
|
||||
.arg(dt.toMSecsSinceEpoch()).arg(chat_id).arg(sender_id).arg(wrapMessage(message)));
|
||||
db->exec(QString("INSERT INTO sent_status (status) VALUES (%1)").arg(isSent));
|
||||
db->exec(QString("INSERT INTO sent_status (status) VALUES (%1);").arg(isSent));
|
||||
db->exec("COMMIT TRANSACTION;");
|
||||
|
||||
messageID++;
|
||||
return messageID;
|
||||
@ -287,12 +291,13 @@ HistoryKeeper::ChatType HistoryKeeper::convertToChatType(int ct)
|
||||
return static_cast<ChatType>(ct);
|
||||
}
|
||||
|
||||
QString HistoryKeeper::getHistoryPath()
|
||||
QString HistoryKeeper::getHistoryPath(QString currentProfile, int encrypted)
|
||||
{
|
||||
QDir baseDir(Settings::getInstance().getSettingsDirPath());
|
||||
QString currentProfile = Settings::getInstance().getCurrentProfile();
|
||||
QDir baseDir(Settings::getSettingsDirPath());
|
||||
if (currentProfile.isEmpty())
|
||||
currentProfile = Settings::getInstance().getCurrentProfile();
|
||||
|
||||
if (Settings::getInstance().getEncryptLogs())
|
||||
if (encrypted == 1 || (encrypted == -1 && Settings::getInstance().getEncryptLogs()))
|
||||
return baseDir.filePath(currentProfile + ".qtox_history.encrypted");
|
||||
else
|
||||
return baseDir.filePath(currentProfile + ".qtox_history");
|
||||
@ -315,3 +320,25 @@ void HistoryKeeper::markAsSent(int m_id)
|
||||
{
|
||||
db->exec(QString("UPDATE sent_status SET status = 1 WHERE id = %1;").arg(m_id));
|
||||
}
|
||||
|
||||
void HistoryKeeper::setSyncType(Db::syncType sType)
|
||||
{
|
||||
QString syncCmd;
|
||||
|
||||
switch (sType) {
|
||||
case Db::syncType::stFull:
|
||||
syncCmd = "FULL";
|
||||
break;
|
||||
case Db::syncType::stNormal:
|
||||
syncCmd = "NORMAL";
|
||||
break;
|
||||
case Db::syncType::stOff:
|
||||
syncCmd = "OFF";
|
||||
break;
|
||||
default:
|
||||
syncCmd = "FULL";
|
||||
break;
|
||||
}
|
||||
|
||||
db->exec(QString("PRAGMA synchronous=%1;").arg(syncCmd));
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <QDateTime>
|
||||
|
||||
class GenericDdInterface;
|
||||
namespace Db { enum class syncType; }
|
||||
|
||||
class HistoryKeeper
|
||||
{
|
||||
@ -42,7 +43,7 @@ public:
|
||||
static HistoryKeeper* getInstance();
|
||||
static void resetInstance();
|
||||
|
||||
static QString getHistoryPath();
|
||||
static QString getHistoryPath(QString currentProfile = QString(), int encrypted = -1); // -1 defaults to checking settings, 0 or 1 to specify
|
||||
static bool checkPassword();
|
||||
static void renameHistory(QString from, QString to);
|
||||
|
||||
@ -51,6 +52,8 @@ public:
|
||||
QList<HistMessage> getChatHistory(ChatType ct, const QString &chat, const QDateTime &time_from, const QDateTime &time_to);
|
||||
void markAsSent(int m_id);
|
||||
|
||||
void setSyncType(Db::syncType sType);
|
||||
|
||||
private:
|
||||
HistoryKeeper(GenericDdInterface *db_);
|
||||
HistoryKeeper(HistoryKeeper &hk) = delete;
|
||||
@ -68,7 +71,6 @@ private:
|
||||
GenericDdInterface *db;
|
||||
QMap<QString, int> aliases;
|
||||
QMap<QString, QPair<int, ChatType>> chats;
|
||||
bool isEncrypted;
|
||||
int messageID;
|
||||
};
|
||||
|
||||
|
@ -71,7 +71,9 @@ IPC::~IPC()
|
||||
{
|
||||
if (globalMemory.lock())
|
||||
{
|
||||
*(time_t*)((char*)globalMemory.data()+sizeof(globalId)) = 0;
|
||||
char* data = (char*)globalMemory.data();
|
||||
if (data)
|
||||
*(time_t*)(data+sizeof(globalId)) = 0;
|
||||
globalMemory.unlock();
|
||||
}
|
||||
}
|
||||
@ -132,7 +134,7 @@ void IPC::processEvents()
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "IPC:processEvents failed to lock";
|
||||
//qWarning() << "IPC:processEvents failed to lock";
|
||||
goto restartTimer;
|
||||
}
|
||||
|
||||
|
45
src/main.cpp
@ -21,11 +21,12 @@
|
||||
#include "src/widget/toxsave.h"
|
||||
#include "src/autoupdate.h"
|
||||
#include <QApplication>
|
||||
#include <QFontDatabase>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QCommandLineParser>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFontDatabase>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include <sodium.h>
|
||||
@ -57,8 +58,20 @@ int main(int argc, char *argv[])
|
||||
QApplication a(argc, argv);
|
||||
a.setApplicationName("qTox");
|
||||
a.setOrganizationName("Tox");
|
||||
a.setApplicationVersion("\nGit commit: " + QString(GIT_VERSION));
|
||||
|
||||
// Process arguments
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("qTox, version: " + QString(GIT_VERSION) + "\nBuilt: " + __TIME__ + " " + __DATE__);
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
parser.addPositionalArgument("uri", QObject::tr("Tox URI to parse"));
|
||||
parser.addOption(QCommandLineOption("P", QObject::tr("Starts new instance and loads specified profile."), QObject::tr("profile")));
|
||||
parser.process(a);
|
||||
|
||||
Settings::getInstance(); // Build our Settings singleton as soon as QApplication is ready, not before
|
||||
if (parser.isSet("P"))
|
||||
Settings::getInstance().setCurrentProfile(parser.value("P"));
|
||||
|
||||
sodium_init(); // For the auto-updater
|
||||
|
||||
@ -83,8 +96,8 @@ int main(int argc, char *argv[])
|
||||
// Windows platform plugins DLL hell fix
|
||||
QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
|
||||
a.addLibraryPath("platforms");
|
||||
|
||||
qDebug() << "built on: " << __TIME__ << __DATE__;
|
||||
|
||||
qDebug() << "built on: " << __TIME__ << __DATE__ << "(" << TIMESTAMP << ")";
|
||||
qDebug() << "commit: " << GIT_VERSION << "\n";
|
||||
|
||||
// Install Unicode 6.1 supporting font
|
||||
@ -100,11 +113,11 @@ int main(int argc, char *argv[])
|
||||
IPC ipc;
|
||||
ipc.registerEventHandler(&toxURIEventHandler);
|
||||
ipc.registerEventHandler(&toxSaveEventHandler);
|
||||
ipc.registerEventHandler(&toxActivateEventHandler);
|
||||
|
||||
// Process arguments
|
||||
if (argc >= 2)
|
||||
if (parser.positionalArguments().size() > 0)
|
||||
{
|
||||
QString firstParam(argv[1]);
|
||||
QString firstParam(parser.positionalArguments()[0]);
|
||||
// Tox URIs. If there's already another qTox instance running, we ask it to handle the URI and we exit
|
||||
// Otherwise we start a new qTox instance and process it ourselves
|
||||
if (firstParam.startsWith("tox:"))
|
||||
@ -137,10 +150,22 @@ int main(int argc, char *argv[])
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Invalid argument\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if (!ipc.isCurrentOwner() && !parser.isSet("P"))
|
||||
{
|
||||
time_t event = ipc.postEvent("$activate");
|
||||
ipc.waitUntilProcessed(event);
|
||||
if (!ipc.isCurrentOwner())
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// Run
|
||||
Widget* w = Widget::getInstance();
|
||||
Widget* w = Widget::getInstance();
|
||||
int errorcode = a.exec();
|
||||
|
||||
delete w;
|
||||
|
@ -1034,7 +1034,7 @@ QSplitter:handle{
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>284</width>
|
||||
<height>401</height>
|
||||
<height>399</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5"/>
|
||||
@ -1053,16 +1053,7 @@ QSplitter:handle{
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton{
|
||||
background-color:#1c1c1c;
|
||||
border:none;
|
||||
}
|
||||
|
||||
QPushButton:hover{
|
||||
background-color:#292929;
|
||||
border:none;
|
||||
}
|
||||
</string>
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
@ -1146,7 +1137,7 @@ QPushButton:hover{
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../res.qrc">
|
||||
<normaloff>:/img/group_2x.png</normaloff>:/img/group_2x.png</iconset>
|
||||
<normaloff>:/img/group_button.png</normaloff>:/img/group_button.png</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
@ -1674,7 +1665,7 @@ QPushButton:hover{
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>57</height>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
@ -1784,7 +1775,7 @@ QPushButton:hover{
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>775</width>
|
||||
<height>19</height>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
|
@ -21,6 +21,10 @@
|
||||
|
||||
#include <QSqlDatabase>
|
||||
|
||||
namespace Db {
|
||||
enum class syncType : int {stOff = 0, stNormal = 1, stFull = 2};
|
||||
}
|
||||
|
||||
class PlainDb : public GenericDdInterface
|
||||
{
|
||||
public:
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "settings.h"
|
||||
#include "smileypack.h"
|
||||
#include "src/corestructs.h"
|
||||
#include "src/misc/db/plaindb.h"
|
||||
|
||||
#include <QFont>
|
||||
#include <QApplication>
|
||||
@ -28,7 +29,16 @@
|
||||
#include <QList>
|
||||
#include <QStyleFactory>
|
||||
|
||||
const QString Settings::FILENAME = "settings.ini";
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#define SHOW_SYSTEM_TRAY_DEFAULT (bool) false
|
||||
#else // OS is not linux
|
||||
#define SHOW_SYSTEM_TRAY_DEFAULT (bool) true
|
||||
#endif
|
||||
|
||||
const QString Settings::OLDFILENAME = "settings.ini";
|
||||
const QString Settings::FILENAME = "qtox.ini";
|
||||
Settings* Settings::settings{nullptr};
|
||||
bool Settings::makeToxPortable{false};
|
||||
|
||||
Settings::Settings() :
|
||||
@ -39,36 +49,53 @@ Settings::Settings() :
|
||||
|
||||
Settings& Settings::getInstance()
|
||||
{
|
||||
static Settings* settings{nullptr};
|
||||
if (!settings)
|
||||
settings = new Settings();
|
||||
return *settings;
|
||||
}
|
||||
|
||||
void Settings::resetInstance()
|
||||
{
|
||||
if (settings)
|
||||
{
|
||||
delete settings;
|
||||
settings = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::load()
|
||||
{
|
||||
if (loaded) {
|
||||
if (loaded)
|
||||
return;
|
||||
}
|
||||
|
||||
QFile portableSettings(FILENAME);
|
||||
if (portableSettings.exists())
|
||||
if (QFile(FILENAME).exists())
|
||||
{
|
||||
QSettings ps(FILENAME, QSettings::IniFormat);
|
||||
ps.beginGroup("General");
|
||||
makeToxPortable = ps.value("makeToxPortable", false).toBool();
|
||||
ps.endGroup();
|
||||
}
|
||||
else if (QFile(OLDFILENAME).exists())
|
||||
{
|
||||
QSettings ps(OLDFILENAME, QSettings::IniFormat);
|
||||
ps.beginGroup("General");
|
||||
makeToxPortable = ps.value("makeToxPortable", false).toBool();
|
||||
ps.endGroup();
|
||||
}
|
||||
else
|
||||
makeToxPortable = false;
|
||||
|
||||
QString filePath = QDir(getSettingsDirPath()).filePath(FILENAME);
|
||||
QDir dir(getSettingsDirPath());
|
||||
QString filePath = dir.filePath(FILENAME);
|
||||
|
||||
//if no settings file exist -- use the default one
|
||||
QFile file(filePath);
|
||||
if (!file.exists()) {
|
||||
qDebug() << "No settings file found, using defaults";
|
||||
filePath = ":/conf/" + FILENAME;
|
||||
if (!QFile(filePath).exists())
|
||||
{
|
||||
if (!QFile(filePath = dir.filePath(OLDFILENAME)).exists())
|
||||
{
|
||||
qDebug() << "No settings file found, using defaults";
|
||||
filePath = ":/conf/" + FILENAME;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Settings: Loading from "<<filePath;
|
||||
@ -95,35 +122,31 @@ void Settings::load()
|
||||
useCustomDhtList=false;
|
||||
s.endGroup();
|
||||
|
||||
friendLst.clear();
|
||||
s.beginGroup("Friends");
|
||||
int size = s.beginReadArray("fullAddresses");
|
||||
for (int i = 0; i < size; i ++)
|
||||
{
|
||||
s.setArrayIndex(i);
|
||||
friendProp fp;
|
||||
fp.addr = s.value("addr").toString();
|
||||
fp.alias = s.value("alias").toString();
|
||||
friendLst[ToxID::fromString(fp.addr).publicKey] = fp;
|
||||
}
|
||||
s.endArray();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("General");
|
||||
enableIPv6 = s.value("enableIPv6", true).toBool();
|
||||
translation = s.value("translation", "en").toString();
|
||||
showSystemTray = s.value("showSystemTray", SHOW_SYSTEM_TRAY_DEFAULT).toBool();
|
||||
makeToxPortable = s.value("makeToxPortable", false).toBool();
|
||||
autostartInTray = s.value("autostartInTray", false).toBool();
|
||||
closeToTray = s.value("closeToTray", false).toBool();
|
||||
forceTCP = s.value("forceTCP", false).toBool();
|
||||
useProxy = s.value("useProxy", false).toBool();
|
||||
setProxyType(s.value("proxyType", static_cast<int>(ProxyType::ptNone)).toInt());
|
||||
proxyAddr = s.value("proxyAddr", "").toString();
|
||||
proxyPort = s.value("proxyPort", 0).toInt();
|
||||
currentProfile = s.value("currentProfile", "").toString();
|
||||
autoAwayTime = s.value("autoAwayTime", 10).toInt();
|
||||
autoAwayTime = s.value("autoAwayTime", 10).toInt();
|
||||
checkUpdates = s.value("checkUpdates", false).toBool();
|
||||
showInFront = s.value("showInFront", false).toBool();
|
||||
fauxOfflineMessaging = s.value("fauxOfflineMessaging", true).toBool();
|
||||
autoSaveEnabled = s.value("autoSaveEnabled", false).toBool();
|
||||
globalAutoAcceptDir = s.value("globalAutoAcceptDir",
|
||||
QStandardPaths::locate(QStandardPaths::HomeLocation, QString(), QStandardPaths::LocateDirectory)
|
||||
).toString();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Advanced");
|
||||
int sType = s.value("dbSyncType", static_cast<int>(Db::syncType::stFull)).toInt();
|
||||
setDbSyncType(sType);
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Widgets");
|
||||
@ -138,15 +161,17 @@ void Settings::load()
|
||||
smileyPack = s.value("smileyPack", ":/smileys/cylgom/emoticons.xml").toString();
|
||||
customEmojiFont = s.value("customEmojiFont", true).toBool();
|
||||
emojiFontFamily = s.value("emojiFontFamily", "DejaVu Sans").toString();
|
||||
emojiFontPointSize = s.value("emojiFontPointSize", QApplication::font().pointSize()).toInt();
|
||||
emojiFontPointSize = s.value("emojiFontPointSize", 12).toInt();
|
||||
firstColumnHandlePos = s.value("firstColumnHandlePos", 50).toInt();
|
||||
secondColumnHandlePosFromRight = s.value("secondColumnHandlePosFromRight", 50).toInt();
|
||||
timestampFormat = s.value("timestampFormat", "hh:mm").toString();
|
||||
minimizeOnClose = s.value("minimizeOnClose", false).toBool();
|
||||
minimizeToTray = s.value("minimizeToTray", false).toBool();
|
||||
lightTrayIcon = s.value("lightTrayIcon", false).toBool();
|
||||
useNativeStyle = s.value("nativeStyle", false).toBool();
|
||||
useEmoticons = s.value("useEmoticons", true).toBool();
|
||||
statusChangeNotificationEnabled = s.value("statusChangeNotificationEnabled", false).toBool();
|
||||
themeColor = s.value("themeColor", 0).toInt();
|
||||
style = s.value("style", "").toString();
|
||||
if (style == "") // Default to Fusion if available, otherwise no style
|
||||
{
|
||||
@ -170,19 +195,10 @@ void Settings::load()
|
||||
encryptTox = s.value("encryptTox", false).toBool();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("AutoAccept");
|
||||
autoSaveEnabled = s.value("autoSaveEnabled", false).toBool();
|
||||
globalAutoAcceptDir = s.value("globalAutoAcceptDir",
|
||||
QStandardPaths::locate(QStandardPaths::HomeLocation, QString(), QStandardPaths::LocateDirectory)
|
||||
).toString();
|
||||
|
||||
for (auto& key : s.childKeys())
|
||||
autoAccept[key] = s.value(key).toString();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Audio");
|
||||
inDev = s.value("inDev", "").toString();
|
||||
outDev = s.value("outDev", "").toString();
|
||||
filterAudio = s.value("filterAudio", false).toBool();
|
||||
s.endGroup();
|
||||
|
||||
// Read the embedded DHT bootsrap nodes list if needed
|
||||
@ -206,15 +222,39 @@ void Settings::load()
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
|
||||
if (currentProfile.isEmpty()) // new profile in Core::switchConfiguration
|
||||
return;
|
||||
|
||||
// load from a profile specific friend data list if possible
|
||||
QString tmp = dir.filePath(currentProfile + ".ini");
|
||||
if (QFile(tmp).exists())
|
||||
filePath = tmp;
|
||||
|
||||
QSettings fs(filePath, QSettings::IniFormat);
|
||||
friendLst.clear();
|
||||
fs.beginGroup("Friends");
|
||||
int size = fs.beginReadArray("Friend");
|
||||
for (int i = 0; i < size; i ++)
|
||||
{
|
||||
fs.setArrayIndex(i);
|
||||
friendProp fp;
|
||||
fp.addr = fs.value("addr").toString();
|
||||
fp.alias = fs.value("alias").toString();
|
||||
fp.autoAcceptDir = fs.value("autoAcceptDir").toString();
|
||||
friendLst[ToxID::fromString(fp.addr).publicKey] = fp;
|
||||
}
|
||||
fs.endArray();
|
||||
fs.endGroup();
|
||||
}
|
||||
|
||||
void Settings::save()
|
||||
void Settings::save(bool writeFriends)
|
||||
{
|
||||
QString filePath = QDir(getSettingsDirPath()).filePath(FILENAME);
|
||||
save(filePath);
|
||||
save(filePath, writeFriends);
|
||||
}
|
||||
|
||||
void Settings::save(QString path)
|
||||
void Settings::save(QString path, bool writeFriends)
|
||||
{
|
||||
qDebug() << "Settings: Saving in "<<path;
|
||||
|
||||
@ -235,26 +275,14 @@ void Settings::save(QString path)
|
||||
s.endArray();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Friends");
|
||||
s.beginWriteArray("fullAddresses", friendLst.size());
|
||||
int index = 0;
|
||||
for (auto &frnd : friendLst)
|
||||
{
|
||||
s.setArrayIndex(index);
|
||||
s.setValue("addr", frnd.addr);
|
||||
s.setValue("alias", frnd.alias);
|
||||
index++;
|
||||
}
|
||||
s.endArray();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("General");
|
||||
s.setValue("enableIPv6", enableIPv6);
|
||||
s.setValue("translation",translation);
|
||||
s.setValue("makeToxPortable",makeToxPortable);
|
||||
s.setValue("showSystemTray", showSystemTray);
|
||||
s.setValue("autostartInTray",autostartInTray);
|
||||
s.setValue("closeToTray", closeToTray);
|
||||
s.setValue("useProxy", useProxy);
|
||||
s.setValue("proxyType", static_cast<int>(proxyType));
|
||||
s.setValue("forceTCP", forceTCP);
|
||||
s.setValue("proxyAddr", proxyAddr);
|
||||
s.setValue("proxyPort", proxyPort);
|
||||
@ -263,6 +291,12 @@ void Settings::save(QString path)
|
||||
s.setValue("checkUpdates", checkUpdates);
|
||||
s.setValue("showInFront", showInFront);
|
||||
s.setValue("fauxOfflineMessaging", fauxOfflineMessaging);
|
||||
s.setValue("autoSaveEnabled", autoSaveEnabled);
|
||||
s.setValue("globalAutoAcceptDir", globalAutoAcceptDir);
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Advanced");
|
||||
s.setValue("dbSyncType", static_cast<int>(dbSyncType));
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Widgets");
|
||||
@ -283,8 +317,10 @@ void Settings::save(QString path)
|
||||
s.setValue("timestampFormat", timestampFormat);
|
||||
s.setValue("minimizeOnClose", minimizeOnClose);
|
||||
s.setValue("minimizeToTray", minimizeToTray);
|
||||
s.setValue("lightTrayIcon", lightTrayIcon);
|
||||
s.setValue("nativeStyle", useNativeStyle);
|
||||
s.setValue("useEmoticons", useEmoticons);
|
||||
s.setValue("themeColor", themeColor);
|
||||
s.setValue("style", style);
|
||||
s.setValue("statusChangeNotificationEnabled", statusChangeNotificationEnabled);
|
||||
s.endGroup();
|
||||
@ -302,17 +338,29 @@ void Settings::save(QString path)
|
||||
s.setValue("encryptTox", encryptTox);
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("AutoAccept");
|
||||
s.setValue("autoSaveEnabled", autoSaveEnabled);
|
||||
s.setValue("globalAutoAcceptDir", globalAutoAcceptDir);
|
||||
for (auto& id : autoAccept.keys())
|
||||
s.setValue(id, autoAccept.value(id));
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Audio");
|
||||
s.setValue("inDev", inDev);
|
||||
s.setValue("outDev", outDev);
|
||||
s.setValue("filterAudio", filterAudio);
|
||||
s.endGroup();
|
||||
|
||||
if (!writeFriends || currentProfile.isEmpty()) // Core::switchConfiguration
|
||||
return;
|
||||
|
||||
QSettings fs(QFileInfo(path).dir().filePath(currentProfile + ".ini"), QSettings::IniFormat);
|
||||
fs.beginGroup("Friends");
|
||||
fs.beginWriteArray("Friend", friendLst.size());
|
||||
int index = 0;
|
||||
for (auto& frnd : friendLst)
|
||||
{
|
||||
fs.setArrayIndex(index);
|
||||
fs.setValue("addr", frnd.addr);
|
||||
fs.setValue("alias", frnd.alias);
|
||||
fs.setValue("autoAcceptDir", frnd.autoAcceptDir);
|
||||
index++;
|
||||
}
|
||||
fs.endArray();
|
||||
fs.endGroup();
|
||||
}
|
||||
|
||||
QString Settings::getSettingsDirPath()
|
||||
@ -430,6 +478,16 @@ void Settings::setStyle(const QString& newStyle)
|
||||
style = newStyle;
|
||||
}
|
||||
|
||||
bool Settings::getShowSystemTray() const
|
||||
{
|
||||
return showSystemTray;
|
||||
}
|
||||
|
||||
void Settings::setShowSystemTray(const bool& newValue)
|
||||
{
|
||||
showSystemTray = newValue;
|
||||
}
|
||||
|
||||
void Settings::setUseEmoticons(bool newValue)
|
||||
{
|
||||
useEmoticons = newValue;
|
||||
@ -476,6 +534,16 @@ void Settings::setMinimizeToTray(bool newValue)
|
||||
minimizeToTray = newValue;
|
||||
}
|
||||
|
||||
bool Settings::getLightTrayIcon() const
|
||||
{
|
||||
return lightTrayIcon;
|
||||
}
|
||||
|
||||
void Settings::setLightTrayIcon(bool newValue)
|
||||
{
|
||||
lightTrayIcon = newValue;
|
||||
}
|
||||
|
||||
bool Settings::getStatusChangeNotificationEnabled() const
|
||||
{
|
||||
return statusChangeNotificationEnabled;
|
||||
@ -516,13 +584,17 @@ void Settings::setForceTCP(bool newValue)
|
||||
forceTCP = newValue;
|
||||
}
|
||||
|
||||
bool Settings::getUseProxy() const
|
||||
ProxyType Settings::getProxyType() const
|
||||
{
|
||||
return useProxy;
|
||||
return proxyType;
|
||||
}
|
||||
void Settings::setUseProxy(bool newValue)
|
||||
|
||||
void Settings::setProxyType(int newValue)
|
||||
{
|
||||
useProxy = newValue;
|
||||
if (newValue >= 0 && newValue <= 2)
|
||||
proxyType = static_cast<ProxyType>(newValue);
|
||||
else
|
||||
proxyType = ProxyType::ptNone;
|
||||
}
|
||||
|
||||
QString Settings::getProxyAddr() const
|
||||
@ -585,6 +657,19 @@ void Settings::setEncryptTox(bool newValue)
|
||||
encryptTox = newValue;
|
||||
}
|
||||
|
||||
Db::syncType Settings::getDbSyncType() const
|
||||
{
|
||||
return dbSyncType;
|
||||
}
|
||||
|
||||
void Settings::setDbSyncType(int newValue)
|
||||
{
|
||||
if (newValue >= 0 && newValue <= 2)
|
||||
dbSyncType = static_cast<Db::syncType>(newValue);
|
||||
else
|
||||
dbSyncType = Db::syncType::stFull;
|
||||
}
|
||||
|
||||
int Settings::getAutoAwayTime() const
|
||||
{
|
||||
return autoAwayTime;
|
||||
@ -597,17 +682,31 @@ void Settings::setAutoAwayTime(int newValue)
|
||||
autoAwayTime = newValue;
|
||||
}
|
||||
|
||||
QString Settings::getAutoAcceptDir(const QString& id) const
|
||||
QString Settings::getAutoAcceptDir(const ToxID& id) const
|
||||
{
|
||||
return autoAccept.value(id.left(TOX_ID_PUBLIC_KEY_LENGTH));
|
||||
QString key = id.publicKey;
|
||||
|
||||
auto it = friendLst.find(key);
|
||||
if (it != friendLst.end())
|
||||
{
|
||||
return it->autoAcceptDir;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void Settings::setAutoAcceptDir(const QString& id, const QString& dir)
|
||||
void Settings::setAutoAcceptDir(const ToxID &id, const QString& dir)
|
||||
{
|
||||
if (dir.isEmpty())
|
||||
autoAccept.remove(id.left(TOX_ID_PUBLIC_KEY_LENGTH));
|
||||
else
|
||||
autoAccept[id.left(TOX_ID_PUBLIC_KEY_LENGTH)] = dir;
|
||||
QString key = id.publicKey;
|
||||
|
||||
auto it = friendLst.find(key);
|
||||
if (it != friendLst.end())
|
||||
{
|
||||
it->autoAcceptDir = dir;
|
||||
} else {
|
||||
updateFriendAdress(id.toString());
|
||||
setAutoAcceptDir(id, dir);
|
||||
}
|
||||
}
|
||||
|
||||
QString Settings::getGlobalAutoAcceptDir() const
|
||||
@ -805,6 +904,16 @@ void Settings::setOutDev(const QString& deviceSpecifier)
|
||||
outDev = deviceSpecifier;
|
||||
}
|
||||
|
||||
bool Settings::getFilterAudio() const
|
||||
{
|
||||
return filterAudio;
|
||||
}
|
||||
|
||||
void Settings::setFilterAudio(bool newValue)
|
||||
{
|
||||
filterAudio = newValue;
|
||||
}
|
||||
|
||||
QString Settings::getFriendAdress(const QString &publicKey) const
|
||||
{
|
||||
QString key = ToxID::fromString(publicKey).publicKey;
|
||||
@ -828,6 +937,7 @@ void Settings::updateFriendAdress(const QString &newAddr)
|
||||
friendProp fp;
|
||||
fp.addr = newAddr;
|
||||
fp.alias = "";
|
||||
fp.autoAcceptDir = "";
|
||||
friendLst[newAddr] = fp;
|
||||
}
|
||||
}
|
||||
@ -855,6 +965,7 @@ void Settings::setFriendAlias(const ToxID &id, const QString &alias)
|
||||
friendProp fp;
|
||||
fp.addr = key;
|
||||
fp.alias = alias;
|
||||
fp.autoAcceptDir = "";
|
||||
friendLst[key] = fp;
|
||||
}
|
||||
}
|
||||
@ -874,3 +985,13 @@ void Settings::setFauxOfflineMessaging(bool value)
|
||||
{
|
||||
fauxOfflineMessaging = value;
|
||||
}
|
||||
|
||||
int Settings::getThemeColor() const
|
||||
{
|
||||
return themeColor;
|
||||
}
|
||||
|
||||
void Settings::setThemeColor(const int &value)
|
||||
{
|
||||
themeColor = value;
|
||||
}
|
||||
|
@ -21,13 +21,17 @@
|
||||
#include <QObject>
|
||||
#include <QPixmap>
|
||||
|
||||
class ToxID;
|
||||
struct ToxID;
|
||||
namespace Db { enum class syncType; }
|
||||
|
||||
enum ProxyType {ptNone, ptSOCKS5, ptHTTP};
|
||||
|
||||
class Settings : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static Settings& getInstance();
|
||||
static void resetInstance();
|
||||
~Settings() = default;
|
||||
|
||||
void executeSettingsDialog(QWidget* parent);
|
||||
@ -59,9 +63,15 @@ public:
|
||||
|
||||
bool getMinimizeToTray() const;
|
||||
void setMinimizeToTray(bool newValue);
|
||||
|
||||
bool getLightTrayIcon() const;
|
||||
void setLightTrayIcon(bool newValue);
|
||||
|
||||
QString getStyle() const;
|
||||
void setStyle(const QString& newValue);
|
||||
|
||||
bool getShowSystemTray() const;
|
||||
void setShowSystemTray(const bool& newValue);
|
||||
|
||||
bool getUseEmoticons() const;
|
||||
void setUseEmoticons(bool newValue);
|
||||
@ -81,8 +91,8 @@ public:
|
||||
QString getProxyAddr() const;
|
||||
void setProxyAddr(const QString& newValue);
|
||||
|
||||
bool getUseProxy() const;
|
||||
void setUseProxy(bool newValue);
|
||||
ProxyType getProxyType() const;
|
||||
void setProxyType(int newValue);
|
||||
|
||||
int getProxyPort() const;
|
||||
void setProxyPort(int newValue);
|
||||
@ -96,6 +106,9 @@ public:
|
||||
bool getEncryptTox() const;
|
||||
void setEncryptTox(bool newValue);
|
||||
|
||||
Db::syncType getDbSyncType() const;
|
||||
void setDbSyncType(int newValue);
|
||||
|
||||
int getAutoAwayTime() const;
|
||||
void setAutoAwayTime(int newValue);
|
||||
|
||||
@ -117,6 +130,9 @@ public:
|
||||
QString getOutDev() const;
|
||||
void setOutDev(const QString& deviceSpecifier);
|
||||
|
||||
bool getFilterAudio() const;
|
||||
void setFilterAudio(bool newValue);
|
||||
|
||||
// Assume all widgets have unique names
|
||||
// Don't use it to save every single thing you want to save, use it
|
||||
// for some general purpose widgets, such as MainWindows or Splitters,
|
||||
@ -145,6 +161,9 @@ public:
|
||||
QString getSmileyPack() const;
|
||||
void setSmileyPack(const QString &value);
|
||||
|
||||
int getThemeColor() const;
|
||||
void setThemeColor(const int& value);
|
||||
|
||||
bool isCurstomEmojiFont() const;
|
||||
void setCurstomEmojiFont(bool value);
|
||||
|
||||
@ -154,8 +173,8 @@ public:
|
||||
int getEmojiFontPointSize() const;
|
||||
void setEmojiFontPointSize(int value);
|
||||
|
||||
QString getAutoAcceptDir(const QString& id) const;
|
||||
void setAutoAcceptDir(const QString&id, const QString& dir);
|
||||
QString getAutoAcceptDir(const ToxID& id) const;
|
||||
void setAutoAcceptDir(const ToxID&id, const QString& dir);
|
||||
|
||||
QString getGlobalAutoAcceptDir() const;
|
||||
void setGlobalAutoAcceptDir(const QString& dir);
|
||||
@ -205,16 +224,19 @@ public:
|
||||
void setFauxOfflineMessaging(bool value);
|
||||
|
||||
public:
|
||||
void save();
|
||||
void save(QString path);
|
||||
void save(bool writeFriends = true);
|
||||
void save(QString path, bool writeFriends = true);
|
||||
void load();
|
||||
|
||||
private:
|
||||
static Settings* settings;
|
||||
|
||||
Settings();
|
||||
Settings(Settings &settings) = delete;
|
||||
Settings& operator=(const Settings&) = delete;
|
||||
|
||||
static const QString FILENAME;
|
||||
static const QString OLDFILENAME;
|
||||
|
||||
bool loaded;
|
||||
|
||||
@ -230,13 +252,14 @@ private:
|
||||
bool autostartInTray;
|
||||
bool closeToTray;
|
||||
bool minimizeToTray;
|
||||
bool lightTrayIcon;
|
||||
bool useEmoticons;
|
||||
bool checkUpdates;
|
||||
bool showInFront;
|
||||
|
||||
bool forceTCP;
|
||||
|
||||
bool useProxy;
|
||||
ProxyType proxyType;
|
||||
QString proxyAddr;
|
||||
int proxyPort;
|
||||
|
||||
@ -265,7 +288,8 @@ private:
|
||||
QByteArray windowState;
|
||||
QByteArray splitterState;
|
||||
QString style;
|
||||
|
||||
bool showSystemTray;
|
||||
|
||||
// ChatView
|
||||
int firstColumnHandlePos;
|
||||
int secondColumnHandlePosFromRight;
|
||||
@ -274,19 +298,23 @@ private:
|
||||
|
||||
// Privacy
|
||||
bool typingNotification;
|
||||
Db::syncType dbSyncType;
|
||||
|
||||
// Audio
|
||||
QString inDev;
|
||||
QString outDev;
|
||||
bool filterAudio;
|
||||
|
||||
struct friendProp
|
||||
{
|
||||
QString alias;
|
||||
QString addr;
|
||||
QString autoAcceptDir;
|
||||
};
|
||||
|
||||
QHash<QString, friendProp> friendLst;
|
||||
|
||||
int themeColor;
|
||||
|
||||
signals:
|
||||
//void dataChanged();
|
||||
|
@ -69,9 +69,9 @@ QList<QPair<QString, QString> > SmileyPack::listSmileyPacks(const QStringList &p
|
||||
|
||||
if (relPath.leftRef(2) == "..")
|
||||
{
|
||||
if(!smileyPacks.contains(QPair<QString, QString>(packageName, absPath)))
|
||||
if (!smileyPacks.contains(QPair<QString, QString>(packageName, absPath)))
|
||||
smileyPacks << QPair<QString, QString>(packageName, absPath);
|
||||
else if(!smileyPacks.contains(QPair<QString, QString>(packageName, relPath)))
|
||||
else if (!smileyPacks.contains(QPair<QString, QString>(packageName, relPath)))
|
||||
smileyPacks << QPair<QString, QString>(packageName, relPath); // use relative path for subdirectories
|
||||
}
|
||||
}
|
||||
@ -97,7 +97,7 @@ bool SmileyPack::load(const QString& filename)
|
||||
|
||||
// open emoticons.xml
|
||||
QFile xmlFile(filename);
|
||||
if(!xmlFile.open(QIODevice::ReadOnly))
|
||||
if (!xmlFile.open(QIODevice::ReadOnly))
|
||||
return false; // cannot open file
|
||||
|
||||
/* parse the cfg file
|
||||
@ -134,7 +134,7 @@ bool SmileyPack::load(const QString& filename)
|
||||
filenameTable.insert(emoticon, file);
|
||||
|
||||
cacheSmiley(file); // preload all smileys
|
||||
|
||||
|
||||
if(!getCachedSmiley(emoticon).size().isEmpty())
|
||||
emoticonSet.push_back(emoticon);
|
||||
|
||||
@ -142,7 +142,7 @@ bool SmileyPack::load(const QString& filename)
|
||||
|
||||
}
|
||||
|
||||
if(emoticonSet.size() > 0)
|
||||
if (emoticonSet.size() > 0)
|
||||
emoticons.push_back(emoticonSet);
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,10 @@
|
||||
#include "style.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "src/widget/widget.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "src/widget/genericchatroomwidget.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QMap>
|
||||
@ -42,6 +46,33 @@ QString qssifyFont(QFont font)
|
||||
.arg(font.family());
|
||||
}
|
||||
|
||||
// colors as defined in
|
||||
// https://github.com/ItsDuke/Tox-UI/blob/master/UI%20GUIDELINES.md
|
||||
static QColor palette[] = {
|
||||
QColor("#6bc260"),
|
||||
QColor("#cebf44"),
|
||||
QColor("#c84e4e"),
|
||||
QColor("#000000"),
|
||||
QColor("#1c1c1c"),
|
||||
QColor("#414141"),
|
||||
QColor("#414141").lighter(120),
|
||||
QColor("#d1d1d1"),
|
||||
QColor("#ffffff"),
|
||||
QColor("#ff7700"),
|
||||
|
||||
// Theme colors
|
||||
QColor("#1c1c1c"),
|
||||
QColor("#2a2a2a"),
|
||||
QColor("#414141"),
|
||||
QColor("#4e4e4e"),
|
||||
};
|
||||
|
||||
static QMap<QString, QString> dict;
|
||||
|
||||
QStringList Style::themeColorNames = {QObject::tr("Default"), QObject::tr("Blue"), QObject::tr("Olive"), QObject::tr("Red"), QObject::tr("Violet")};
|
||||
QList<QColor> Style::themeColorColors = {QColor(), QColor("#004aa4"), QColor("#97ba00"), QColor("#c23716"), QColor("#4617b5")};
|
||||
|
||||
|
||||
QString Style::getStylesheet(const QString &filename)
|
||||
{
|
||||
if (!Settings::getInstance().getUseNativeStyle())
|
||||
@ -58,21 +89,6 @@ QString Style::getStylesheet(const QString &filename)
|
||||
|
||||
QColor Style::getColor(Style::ColorPalette entry)
|
||||
{
|
||||
// colors as defined in
|
||||
// https://github.com/ItsDuke/Tox-UI/blob/master/UI%20GUIDELINES.md
|
||||
static QColor palette[] = {
|
||||
QColor("#6bc260"),
|
||||
QColor("#cebf44"),
|
||||
QColor("#c84e4e"),
|
||||
QColor("#000000"),
|
||||
QColor("#1c1c1c"),
|
||||
QColor("#414141"),
|
||||
QColor("#414141").lighter(120),
|
||||
QColor("#d1d1d1"),
|
||||
QColor("#ffffff"),
|
||||
QColor("#ff7700"),
|
||||
};
|
||||
|
||||
return palette[entry];
|
||||
}
|
||||
|
||||
@ -98,28 +114,35 @@ QFont Style::getFont(Style::Font font)
|
||||
|
||||
QString Style::resolve(QString qss)
|
||||
{
|
||||
static QMap<QString, QString> dict = {
|
||||
// colors
|
||||
{"@green", getColor(Green).name()},
|
||||
{"@yellow", getColor(Yellow).name()},
|
||||
{"@red", getColor(Red).name()},
|
||||
{"@black", getColor(Black).name()},
|
||||
{"@darkGrey", getColor(DarkGrey).name()},
|
||||
{"@mediumGrey", getColor(MediumGrey).name()},
|
||||
{"@mediumGreyLight", getColor(MediumGreyLight).name()},
|
||||
{"@lightGrey", getColor(LightGrey).name()},
|
||||
{"@white", getColor(White).name()},
|
||||
{"@orange", getColor(Orange).name()},
|
||||
if (dict.isEmpty())
|
||||
{
|
||||
dict = {
|
||||
// colors
|
||||
{"@green", Style::getColor(Style::Green).name()},
|
||||
{"@yellow", Style::getColor(Style::Yellow).name()},
|
||||
{"@red", Style::getColor(Style::Red).name()},
|
||||
{"@black", Style::getColor(Style::Black).name()},
|
||||
{"@darkGrey", Style::getColor(Style::DarkGrey).name()},
|
||||
{"@mediumGrey", Style::getColor(Style::MediumGrey).name()},
|
||||
{"@mediumGreyLight", Style::getColor(Style::MediumGreyLight).name()},
|
||||
{"@lightGrey", Style::getColor(Style::LightGrey).name()},
|
||||
{"@white", Style::getColor(Style::White).name()},
|
||||
{"@orange", Style::getColor(Style::Orange).name()},
|
||||
{"@themeDark", Style::getColor(Style::ThemeDark).name()},
|
||||
{"@themeMediumDark", Style::getColor(Style::ThemeMediumDark).name()},
|
||||
{"@themeMedium", Style::getColor(Style::ThemeMedium).name()},
|
||||
{"@themeLight", Style::getColor(Style::ThemeLight).name()},
|
||||
|
||||
// fonts
|
||||
{"@extraBig", qssifyFont(getFont(ExtraBig))},
|
||||
{"@big", qssifyFont(getFont(Big))},
|
||||
{"@bigBold", qssifyFont(getFont(BigBold))},
|
||||
{"@medium", qssifyFont(getFont(Medium))},
|
||||
{"@mediumBold", qssifyFont(getFont(MediumBold))},
|
||||
{"@small", qssifyFont(getFont(Small))},
|
||||
{"@smallLight", qssifyFont(getFont(SmallLight))},
|
||||
};
|
||||
// fonts
|
||||
{"@extraBig", qssifyFont(Style::getFont(Style::ExtraBig))},
|
||||
{"@big", qssifyFont(Style::getFont(Style::Big))},
|
||||
{"@bigBold", qssifyFont(Style::getFont(Style::BigBold))},
|
||||
{"@medium", qssifyFont(Style::getFont(Style::Medium))},
|
||||
{"@mediumBold", qssifyFont(Style::getFont(Style::MediumBold))},
|
||||
{"@small", qssifyFont(Style::getFont(Style::Small))},
|
||||
{"@smallLight", qssifyFont(Style::getFont(Style::SmallLight))},
|
||||
};
|
||||
}
|
||||
|
||||
for (const QString& key : dict.keys())
|
||||
{
|
||||
@ -144,3 +167,42 @@ void Style::repolish(QWidget *w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Style::setThemeColor(int color)
|
||||
{
|
||||
if (color < 0 || color >= themeColorColors.size())
|
||||
setThemeColor(QColor());
|
||||
else
|
||||
setThemeColor(themeColorColors[color]);
|
||||
}
|
||||
|
||||
void Style::setThemeColor(QColor color)
|
||||
{
|
||||
if (!color.isValid())
|
||||
{
|
||||
// Reset to default
|
||||
palette[ThemeDark] = QColor("#1c1c1c");
|
||||
palette[ThemeMediumDark] = QColor("#2a2a2a");
|
||||
palette[ThemeMedium] = QColor("#414141");
|
||||
palette[ThemeLight] = QColor("#4e4e4e");
|
||||
}
|
||||
else
|
||||
{
|
||||
palette[ThemeDark] = color.darker(155);
|
||||
palette[ThemeMediumDark] = color.darker(135);
|
||||
palette[ThemeMedium] = color.darker(120);
|
||||
palette[ThemeLight] = color.lighter(110);
|
||||
}
|
||||
|
||||
dict["@themeDark"] = getColor(ThemeDark).name();
|
||||
dict["@themeMediumDark"] = getColor(ThemeMediumDark).name();
|
||||
dict["@themeMedium"] = getColor(ThemeMedium).name();
|
||||
dict["@themeLight"] = getColor(ThemeLight).name();
|
||||
|
||||
applyTheme();
|
||||
}
|
||||
|
||||
void Style::applyTheme()
|
||||
{
|
||||
Widget::getInstance()->reloadTheme();
|
||||
}
|
||||
|
@ -38,6 +38,10 @@ public:
|
||||
LightGrey,
|
||||
White,
|
||||
Orange,
|
||||
ThemeDark,
|
||||
ThemeMediumDark,
|
||||
ThemeMedium,
|
||||
ThemeLight,
|
||||
};
|
||||
|
||||
enum Font
|
||||
@ -56,6 +60,15 @@ public:
|
||||
static QFont getFont(Font font);
|
||||
static QString resolve(QString qss);
|
||||
static void repolish(QWidget* w);
|
||||
static void setThemeColor(int color);
|
||||
static void setThemeColor(QColor color); ///< Pass an invalid QColor to reset to defaults
|
||||
static void applyTheme(); ///< Reloads some CCS
|
||||
|
||||
static QStringList themeColorNames;
|
||||
static QList<QColor> themeColorColors;
|
||||
|
||||
signals:
|
||||
void themeChanged();
|
||||
|
||||
private:
|
||||
Style();
|
||||
|
32
src/platform/timer.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifdef QTOX_PLATFORM_EXT
|
||||
|
||||
#ifndef PLATFORM_TIMER_H
|
||||
#define PLATFORM_TIMER_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
uint32_t getIdleTime();
|
||||
}
|
||||
|
||||
#endif // PLATFORM_TIMER_H
|
||||
|
||||
#endif // QTOX_PLATFORM_EXT
|
50
src/platform/timer_osx.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Pidgin is the legal property of its developers, whose names are too numerous
|
||||
to list here. Please refer to the COPYRIGHT file distributed with this
|
||||
source distribution (which can be found at
|
||||
<https://hg.pidgin.im/pidgin/main/file/13e4ae613a6a/COPYRIGHT> ).
|
||||
|
||||
Additional 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.
|
||||
*/
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include "src/platform/timer.h"
|
||||
#include <QDebug>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
|
||||
uint32_t Platform::getIdleTime()
|
||||
{
|
||||
// https://hg.pidgin.im/pidgin/main/file/13e4ae613a6a/pidgin/gtkidle.c
|
||||
static io_service_t service = NULL;
|
||||
CFTypeRef property;
|
||||
uint64_t idleTime_ns = 0;
|
||||
|
||||
if (!service)
|
||||
{
|
||||
mach_port_t master;
|
||||
IOMasterPort(MACH_PORT_NULL, &master);
|
||||
service = IOServiceGetMatchingService(master, IOServiceMatching("IOHIDSystem"));
|
||||
}
|
||||
|
||||
property = IORegistryEntryCreateCFProperty(service, CFSTR("HIDIdleTime"), kCFAllocatorDefault, 0);
|
||||
CFNumberGetValue((CFNumberRef)property, kCFNumberSInt64Type, &idleTime_ns);
|
||||
CFRelease(property);
|
||||
|
||||
return idleTime_ns / 1000000;
|
||||
}
|
||||
|
||||
#endif // defined(__APPLE__) && defined(__MACH__)
|
32
src/platform/timer_win.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
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 <QDebug>
|
||||
#ifdef Q_OS_WIN32
|
||||
#include "src/platform/timer.h"
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
uint32_t Platform::getIdleTime()
|
||||
{
|
||||
LASTINPUTINFO info = { 0 };
|
||||
info.cbSize = sizeof(info);
|
||||
if (GetLastInputInfo(&info))
|
||||
return GetTickCount() - info.dwTime;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // Q_OS_WIN32
|
52
src/platform/timer_x11.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
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 <QDebug>
|
||||
#if defined(Q_OS_UNIX) && !defined(__APPLE__) && !defined(__MACH__)
|
||||
#include "src/platform/timer.h"
|
||||
#include <X11/extensions/scrnsaver.h>
|
||||
|
||||
|
||||
uint32_t Platform::getIdleTime()
|
||||
{
|
||||
uint32_t idleTime = 0;
|
||||
|
||||
Display *display = XOpenDisplay(NULL);
|
||||
if (!display)
|
||||
{
|
||||
qDebug() << "XOpenDisplay(NULL) failed";
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t x11event = 0, x11error = 0;
|
||||
static int32_t hasExtension = XScreenSaverQueryExtension(display, &x11event, &x11error);
|
||||
if (hasExtension)
|
||||
{
|
||||
XScreenSaverInfo *info = XScreenSaverAllocInfo();
|
||||
if (info)
|
||||
{
|
||||
XScreenSaverQueryInfo(display, DefaultRootWindow(display), info);
|
||||
idleTime = info->idle;
|
||||
XFree(info);
|
||||
}
|
||||
else
|
||||
qDebug() << "XScreenSaverAllocInfo() failed";
|
||||
}
|
||||
XCloseDisplay(display);
|
||||
return idleTime;
|
||||
}
|
||||
|
||||
#endif // Q_OS_UNIX
|
@ -129,7 +129,7 @@ void CameraWorker::applyProps()
|
||||
if (!cam.isOpened())
|
||||
return;
|
||||
|
||||
for(int prop : props.keys())
|
||||
for (int prop : props.keys())
|
||||
cam.set(prop, props.value(prop));
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ void CameraWorker::subscribe()
|
||||
|
||||
void CameraWorker::unsubscribe()
|
||||
{
|
||||
if(--refCount <= 0)
|
||||
if (--refCount <= 0)
|
||||
{
|
||||
cam.release();
|
||||
refCount = 0;
|
||||
|
@ -28,7 +28,7 @@ void NetVideoSource::pushFrame(VideoFrame frame)
|
||||
emit frameAvailable(frame);
|
||||
}
|
||||
|
||||
void NetVideoSource::pushVPXFrame(vpx_image *image)
|
||||
void NetVideoSource::pushVPXFrame(const vpx_image *image)
|
||||
{
|
||||
const int dw = image->d_w;
|
||||
const int dh = image->d_h;
|
||||
|
@ -27,7 +27,7 @@ public:
|
||||
NetVideoSource();
|
||||
|
||||
void pushFrame(VideoFrame frame);
|
||||
void pushVPXFrame(vpx_image* image);
|
||||
void pushVPXFrame(const vpx_image *image);
|
||||
|
||||
virtual void subscribe() {}
|
||||
virtual void unsubscribe() {}
|
||||
|
@ -94,6 +94,9 @@ bool CroppingLabel::eventFilter(QObject *obj, QEvent *e)
|
||||
// events fired by the QLineEdit
|
||||
if (obj == textEdit)
|
||||
{
|
||||
if (!textEdit->isVisible())
|
||||
return false;
|
||||
|
||||
if (e->type() == QEvent::KeyPress)
|
||||
{
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
|
||||
@ -126,8 +129,9 @@ void CroppingLabel::hideTextEdit(bool acceptText)
|
||||
{
|
||||
if (acceptText)
|
||||
{
|
||||
emit textChanged(textEdit->text(), origText);
|
||||
setText(textEdit->text());
|
||||
QString oldOrigText = origText;
|
||||
setText(textEdit->text()); // set before emitting so we don't override external reactions to signal
|
||||
emit textChanged(textEdit->text(), oldOrigText);
|
||||
}
|
||||
|
||||
textEdit->hide();
|
||||
@ -141,3 +145,8 @@ void CroppingLabel::showTextEdit()
|
||||
textEdit->setFocus();
|
||||
textEdit->setText(origText);
|
||||
}
|
||||
|
||||
QString CroppingLabel::fullText()
|
||||
{
|
||||
return origText;
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ public:
|
||||
virtual void mouseReleaseEvent(QMouseEvent *e);
|
||||
virtual bool eventFilter(QObject *obj, QEvent *e);
|
||||
|
||||
QString fullText(); ///< Returns the un-cropped text
|
||||
|
||||
signals:
|
||||
void textChanged(QString newText, QString oldText);
|
||||
void clicked();
|
||||
|
@ -95,10 +95,10 @@ void AddFriendForm::onSendTriggered()
|
||||
this->toxId.clear();
|
||||
this->message.clear();
|
||||
} else {
|
||||
if (Settings::getInstance().getUseProxy())
|
||||
if (Settings::getInstance().getProxyType() != ProxyType::ptNone)
|
||||
{
|
||||
QMessageBox::StandardButton btn = QMessageBox::warning(main, "qTox", tr("qTox needs to use the Tox DNS, but can't do it through a proxy.\n\
|
||||
Ignore the proxy and connect to the Internet directly ?"), QMessageBox::Ok|QMessageBox::No, QMessageBox::No);
|
||||
Ignore the proxy and connect to the Internet directly?"), QMessageBox::Ok|QMessageBox::No, QMessageBox::No);
|
||||
if (btn != QMessageBox::Ok)
|
||||
return;
|
||||
}
|
||||
|
@ -43,8 +43,6 @@
|
||||
|
||||
ChatForm::ChatForm(Friend* chatFriend)
|
||||
: f(chatFriend)
|
||||
, audioInputFlag(false)
|
||||
, audioOutputFlag(false)
|
||||
, callId(0)
|
||||
{
|
||||
nameLabel->setText(f->getDisplayedName());
|
||||
@ -77,11 +75,10 @@ ChatForm::ChatForm(Friend* chatFriend)
|
||||
connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle()));
|
||||
connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
|
||||
connect(this, SIGNAL(chatAreaCleared()), this, SLOT(clearReciepts()));
|
||||
connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig)
|
||||
{if (text != orig) emit aliasChanged(text);} );
|
||||
|
||||
setAcceptDrops(true);
|
||||
|
||||
if (Settings::getInstance().getEnableLogging())
|
||||
loadHistory(QDateTime::currentDateTime().addDays(-7), true);
|
||||
}
|
||||
|
||||
ChatForm::~ChatForm()
|
||||
@ -105,7 +102,7 @@ void ChatForm::onSendTriggered()
|
||||
if (isAction)
|
||||
msg = msg = msg.right(msg.length() - 4);
|
||||
|
||||
QList<CString> splittedMsg = Core::splitMessage(msg);
|
||||
QList<CString> splittedMsg = Core::splitMessage(msg, TOX_MAX_MESSAGE_LENGTH);
|
||||
QDateTime timestamp = QDateTime::currentDateTime();
|
||||
|
||||
for (CString& c_msg : splittedMsg)
|
||||
@ -120,14 +117,13 @@ void ChatForm::onSendTriggered()
|
||||
int id = HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, qt_msg_hist,
|
||||
Core::getInstance()->getSelfId().publicKey, timestamp, status);
|
||||
|
||||
|
||||
ChatMessage* ma = addSelfMessage(msg, isAction, timestamp, false);
|
||||
|
||||
int rec;
|
||||
if (isAction)
|
||||
rec = Core::getInstance()->sendAction(f->getFriendID(), msg);
|
||||
rec = Core::getInstance()->sendAction(f->getFriendID(), qt_msg);
|
||||
else
|
||||
rec = Core::getInstance()->sendMessage(f->getFriendID(), msg);
|
||||
rec = Core::getInstance()->sendMessage(f->getFriendID(), qt_msg);
|
||||
|
||||
registerReceipt(rec, id, ma);
|
||||
}
|
||||
@ -144,7 +140,10 @@ void ChatForm::onAttachClicked()
|
||||
{
|
||||
QFile file(path);
|
||||
if (!file.exists() || !file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QMessageBox::warning(this, tr("File not read"), tr("qTox wasn't able to open %1").arg(QFileInfo(path).fileName()));
|
||||
continue;
|
||||
}
|
||||
if (file.isSequential())
|
||||
{
|
||||
QMessageBox::critical(0, tr("Bad Idea"), tr("You're trying to send a special (sequential) file, that's not going to work!"));
|
||||
@ -198,12 +197,12 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
||||
}
|
||||
|
||||
ChatMessage* msg = chatWidget->addFileTransferMessage(name, file, QDateTime::currentDateTime(), false);
|
||||
if (!Settings::getInstance().getAutoAcceptDir(Core::getInstance()->getFriendAddress(f->getFriendID())).isEmpty()
|
||||
if (!Settings::getInstance().getAutoAcceptDir(f->getToxID()).isEmpty()
|
||||
|| Settings::getInstance().getAutoSaveEnabled())
|
||||
{
|
||||
FileTransferWidget* tfWidget = dynamic_cast<FileTransferWidget*>(msg->getContent(1));
|
||||
if(tfWidget)
|
||||
tfWidget->acceptTransfer(Settings::getInstance().getAutoAcceptDir(Core::getInstance()->getFriendAddress(f->getFriendID())));
|
||||
tfWidget->acceptTransfer(Settings::getInstance().getAutoAcceptDir(f->getToxID()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -284,6 +283,8 @@ void ChatForm::onAvCancel(int FriendId, int)
|
||||
|
||||
if (FriendId != f->getFriendID())
|
||||
return;
|
||||
|
||||
stopCounter();
|
||||
|
||||
audioInputFlag = false;
|
||||
audioOutputFlag = false;
|
||||
@ -638,7 +639,7 @@ void ChatForm::onFileSendFailed(int FriendId, const QString &fname)
|
||||
if (FriendId != f->getFriendID())
|
||||
return;
|
||||
|
||||
addSystemInfoMessage("File: \"" + fname + "\" failed to send.", "red", QDateTime::currentDateTime());
|
||||
addSystemInfoMessage(tr("Failed to send file \"%1\"").arg(fname), "red", QDateTime::currentDateTime());
|
||||
}
|
||||
|
||||
void ChatForm::onAvatarChange(int FriendId, const QPixmap &pic)
|
||||
@ -717,7 +718,7 @@ void ChatForm::loadHistory(QDateTime since, bool processUndelivered)
|
||||
// Show each messages
|
||||
ToxID id = ToxID::fromString(it.sender);
|
||||
ChatMessage* msg = chatWidget->addChatMessage(Core::getInstance()->getPeerName(id), it.message, id.isMine(), false);
|
||||
if (it.isSent)
|
||||
if (it.isSent || !id.isMine())
|
||||
{
|
||||
msg->markAsSent(msgDateTime);
|
||||
}
|
||||
@ -757,7 +758,7 @@ void ChatForm::onLoadHistory()
|
||||
|
||||
void ChatForm::startCounter()
|
||||
{
|
||||
if(!timer)
|
||||
if (!timer)
|
||||
{
|
||||
timer = new QTimer();
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(updateTime()));
|
||||
@ -769,7 +770,7 @@ void ChatForm::startCounter()
|
||||
|
||||
void ChatForm::stopCounter()
|
||||
{
|
||||
if(timer)
|
||||
if (timer)
|
||||
{
|
||||
addSystemInfoMessage(tr("Call with %1 ended. %2").arg(f->getDisplayedName(),secondsToDHMS(timeElapsed.elapsed()/1000)),
|
||||
"white", QDateTime::currentDateTime());
|
||||
@ -797,10 +798,10 @@ QString ChatForm::secondsToDHMS(quint32 duration)
|
||||
int hours = (int) (duration % 24);
|
||||
int days = (int) (duration / 24);
|
||||
|
||||
if(minutes == 0)
|
||||
if (minutes == 0)
|
||||
return cD + res.sprintf("%02ds", seconds);
|
||||
|
||||
if(hours == 0 && days == 0)
|
||||
if (hours == 0 && days == 0)
|
||||
return cD + res.sprintf("%02dm %02ds", minutes, seconds);
|
||||
|
||||
if (days == 0)
|
||||
|
@ -36,6 +36,7 @@ public:
|
||||
ChatForm(Friend* chatFriend);
|
||||
~ChatForm();
|
||||
void setStatusMessage(QString newMessage);
|
||||
void loadHistory(QDateTime since, bool processUndelivered = false);
|
||||
|
||||
void dischargeReceipt(int receipt);
|
||||
|
||||
@ -48,6 +49,7 @@ signals:
|
||||
void cancelCall(int callId, int friendId);
|
||||
void micMuteToggle(int callId);
|
||||
void volMuteToggle(int callId);
|
||||
void aliasChanged(const QString& alias);
|
||||
|
||||
public slots:
|
||||
void deliverOfflineMsgs();
|
||||
@ -84,7 +86,6 @@ private slots:
|
||||
void updateTime();
|
||||
|
||||
protected:
|
||||
void loadHistory(QDateTime since, bool processUndelivered = false);
|
||||
// drag & drop
|
||||
void dragEnterEvent(QDragEnterEvent* ev);
|
||||
void dropEvent(QDropEvent* ev);
|
||||
@ -94,8 +95,6 @@ private:
|
||||
Friend* f;
|
||||
CroppingLabel *statusMessageLabel;
|
||||
NetCamView* netcam;
|
||||
bool audioInputFlag;
|
||||
bool audioOutputFlag;
|
||||
int callId;
|
||||
QLabel *callDuration;
|
||||
QTimer *timer;
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "src/widget/tool/chattextedit.h"
|
||||
#include "src/widget/maskablepixmapwidget.h"
|
||||
#include "src/core.h"
|
||||
#include "src/grouplist.h"
|
||||
#include "src/group.h"
|
||||
#include "src/friendlist.h"
|
||||
#include "src/friend.h"
|
||||
#include "src/chatlog/content/text.h"
|
||||
@ -34,6 +36,8 @@
|
||||
GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
earliestMessage(nullptr)
|
||||
, audioInputFlag(false)
|
||||
, audioOutputFlag(false)
|
||||
{
|
||||
curRow = 0;
|
||||
|
||||
@ -42,12 +46,16 @@ GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||
nameLabel = new CroppingLabel();
|
||||
nameLabel->setObjectName("nameLabel");
|
||||
nameLabel->setMinimumHeight(Style::getFont(Style::Medium).pixelSize());
|
||||
nameLabel->setEditable(true);
|
||||
|
||||
avatar = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.png");
|
||||
QHBoxLayout *headLayout = new QHBoxLayout(), *mainFootLayout = new QHBoxLayout();
|
||||
headTextLayout = new QVBoxLayout();
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
QVBoxLayout *footButtonsSmall = new QVBoxLayout(), *volMicLayout = new QVBoxLayout();
|
||||
QHBoxLayout *headLayout = new QHBoxLayout(),
|
||||
*mainFootLayout = new QHBoxLayout();
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(),
|
||||
*footButtonsSmall = new QVBoxLayout(),
|
||||
*volMicLayout = new QVBoxLayout();
|
||||
headTextLayout = new QVBoxLayout();
|
||||
|
||||
chatWidget = new ChatLog(this);
|
||||
|
||||
@ -63,16 +71,16 @@ GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||
fileButton->setToolTip(tr("Send file(s)"));
|
||||
callButton = new QPushButton();
|
||||
callButton->setFixedSize(50,40);
|
||||
callButton->setToolTip(tr("Audio call"));
|
||||
callButton->setToolTip(tr("Audio call: RED means you're on a call"));
|
||||
videoButton = new QPushButton();
|
||||
videoButton->setFixedSize(50,40);
|
||||
videoButton->setToolTip(tr("Video call"));
|
||||
videoButton->setToolTip(tr("Video call: RED means you're on a call"));
|
||||
volButton = new QPushButton();
|
||||
volButton->setFixedSize(25,20);
|
||||
volButton->setToolTip(tr("Toggle speakers volume"));
|
||||
//volButton->setFixedSize(25,20);
|
||||
volButton->setToolTip(tr("Toggle speakers volume: RED is OFF"));
|
||||
micButton = new QPushButton();
|
||||
micButton->setFixedSize(25,20);
|
||||
micButton->setToolTip(tr("Toggle microphone"));
|
||||
// micButton->setFixedSize(25,20);
|
||||
micButton->setToolTip(tr("Toggle microphone: RED is OFF"));
|
||||
|
||||
footButtonsSmall->setSpacing(2);
|
||||
|
||||
@ -111,6 +119,13 @@ GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||
mainFootLayout->addSpacing(5);
|
||||
mainFootLayout->addWidget(sendButton);
|
||||
mainFootLayout->setSpacing(0);
|
||||
|
||||
headTextLayout->addStretch();
|
||||
headTextLayout->addWidget(nameLabel);
|
||||
|
||||
volMicLayout->addWidget(micButton, Qt::AlignTop);
|
||||
volMicLayout->addSpacing(2);
|
||||
volMicLayout->addWidget(volButton, Qt::AlignBottom);
|
||||
|
||||
headWidget->setLayout(headLayout);
|
||||
headLayout->addWidget(avatar);
|
||||
@ -122,14 +137,6 @@ GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||
headLayout->addWidget(videoButton);
|
||||
headLayout->setSpacing(0);
|
||||
|
||||
volMicLayout->addSpacing(1);
|
||||
volMicLayout->addWidget(micButton);
|
||||
volMicLayout->addSpacing(2);
|
||||
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);
|
||||
@ -142,6 +149,7 @@ GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||
|
||||
connect(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked()));
|
||||
connect(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
||||
connect(chatWidget, SIGNAL(onClick()), this, SLOT(onChatWidgetClicked()));
|
||||
|
||||
//chatWidget->document()->setDefaultStyleSheet(Style::getStylesheet(":ui/chatArea/innerStyle.css"));
|
||||
//chatWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatArea.css"));
|
||||
@ -174,18 +182,6 @@ void GenericChatForm::show(Ui::MainWindow &ui)
|
||||
QWidget::show();
|
||||
}
|
||||
|
||||
void GenericChatForm::addMessage(const QString &author, const QString &message, bool isAction, const QDateTime &datetime)
|
||||
{
|
||||
if(!isAction)
|
||||
{
|
||||
chatWidget->addChatMessage(author, message, datetime, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
chatWidget->addChatAction(author, message, datetime);
|
||||
}
|
||||
}
|
||||
|
||||
void GenericChatForm::onChatContextMenuRequested(QPoint pos)
|
||||
{
|
||||
QWidget* sender = (QWidget*)QObject::sender();
|
||||
@ -239,6 +235,7 @@ void GenericChatForm::addAlertMessage(const ToxID &author, QString message, QDat
|
||||
{
|
||||
QString authorStr = Core::getInstance()->getPeerName(author);
|
||||
chatWidget->addChatMessage(author != previousId ? authorStr : QString(), message, author.isMine(), true);
|
||||
|
||||
previousId = author;
|
||||
}
|
||||
|
||||
@ -259,6 +256,11 @@ void GenericChatForm::onEmoteButtonClicked()
|
||||
}
|
||||
}
|
||||
|
||||
void GenericChatForm::onChatWidgetClicked()
|
||||
{
|
||||
msgEdit->setFocus();
|
||||
}
|
||||
|
||||
void GenericChatForm::onEmoteInsertRequested(QString str)
|
||||
{
|
||||
// insert the emoticon
|
||||
@ -280,20 +282,6 @@ void GenericChatForm::addSystemInfoMessage(const QString &message, const QString
|
||||
chatWidget->addSystemMessage(message, datetime);
|
||||
}
|
||||
|
||||
void GenericChatForm::addAlertMessage(const QString &author, QString message, QDateTime datetime)
|
||||
{
|
||||
ChatMessage* msg = chatWidget->addChatMessage(author, message, false, true);
|
||||
msg->markAsSent(datetime);
|
||||
}
|
||||
|
||||
//QString GenericChatForm::getElidedName(const QString& name)
|
||||
//{
|
||||
// // update this whenever you change the font in innerStyle.css
|
||||
// QFontMetrics fm(Style::getFont(Style::BigBold));
|
||||
|
||||
// return fm.elidedText(name, Qt::ElideRight, chatWidget->nameColWidth());
|
||||
//}
|
||||
|
||||
void GenericChatForm::clearChatArea(bool notinform)
|
||||
{
|
||||
chatWidget->clear();
|
||||
@ -311,127 +299,20 @@ void GenericChatForm::clearChatArea(bool notinform)
|
||||
emit chatAreaCleared();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated The only reason it's still alive is because the groupchat API is a bit limited
|
||||
*/
|
||||
//MessageActionPtr GenericChatForm::genMessageActionAction(const QString &author, QString message, bool isAction,
|
||||
// const QDateTime &datetime)
|
||||
//{
|
||||
// if (earliestMessage == nullptr)
|
||||
// {
|
||||
// earliestMessage = new QDateTime(datetime);
|
||||
// }
|
||||
QString GenericChatForm::resolveToxID(const ToxID &id)
|
||||
{
|
||||
Friend *f = FriendList::findFriend(id);
|
||||
if (f)
|
||||
{
|
||||
return f->getDisplayedName();
|
||||
} else {
|
||||
for (auto it : GroupList::getAllGroups())
|
||||
{
|
||||
QString res = it->resolveToxID(id);
|
||||
if (res.size())
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||
// bool isMe = (author == Widget::getInstance()->getUsername());
|
||||
|
||||
// if (!isAction && message.startsWith("/me "))
|
||||
// { // always render actions regardless of what core thinks
|
||||
// isAction = true;
|
||||
// message = message.right(message.length()-4);
|
||||
// }
|
||||
|
||||
// if (isAction)
|
||||
// {
|
||||
// previousId = ToxID(); // next msg has a name regardless
|
||||
// return MessageActionPtr(new ActionAction (getElidedName(author), message, date, isMe));
|
||||
// }
|
||||
|
||||
// MessageActionPtr res;
|
||||
// if (previousId.publicKey == author)
|
||||
// res = MessageActionPtr(new MessageAction(QString(), message, date, isMe));
|
||||
// else
|
||||
// res = MessageActionPtr(new MessageAction(getElidedName(author), message, date, isMe));
|
||||
|
||||
// previousId.publicKey = author;
|
||||
// return res;
|
||||
//}
|
||||
|
||||
//MessageActionPtr GenericChatForm::genMessageActionAction(const ToxID& author, QString message, bool isAction, const QDateTime &datetime)
|
||||
//{
|
||||
// if (earliestMessage == nullptr)
|
||||
// {
|
||||
// earliestMessage = new QDateTime(datetime);
|
||||
// }
|
||||
|
||||
// const Core* core = Core::getInstance();
|
||||
|
||||
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||
// bool isMe = (author == core->getSelfId());
|
||||
// QString authorStr;
|
||||
// if (isMe)
|
||||
// authorStr = core->getUsername();
|
||||
// else {
|
||||
// Friend *f = FriendList::findFriend(author.publicKey);
|
||||
// if (f)
|
||||
// authorStr = f->getDisplayedName();
|
||||
// else
|
||||
// authorStr = core->getPeerName(author);
|
||||
// }
|
||||
|
||||
// if (authorStr.isEmpty()) // Fallback if we can't find a username
|
||||
// authorStr = author.toString();
|
||||
|
||||
// if (!isAction && message.startsWith("/me "))
|
||||
// { // always render actions regardless of what core thinks
|
||||
// isAction = true;
|
||||
// message = message.right(message.length()-4);
|
||||
// }
|
||||
|
||||
// if (isAction)
|
||||
// {
|
||||
// previousId = ToxID(); // next msg has a name regardless
|
||||
// return MessageActionPtr(new ActionAction (getElidedName(authorStr), message, date, isMe));
|
||||
// }
|
||||
|
||||
// MessageActionPtr res;
|
||||
// if (previousId == author)
|
||||
// res = MessageActionPtr(new MessageAction(QString(), message, date, isMe));
|
||||
// else
|
||||
// res = MessageActionPtr(new MessageAction(getElidedName(authorStr), message, date, isMe));
|
||||
|
||||
// previousId = author;
|
||||
// return res;
|
||||
//}
|
||||
|
||||
//MessageActionPtr GenericChatForm::genSelfActionAction(QString message, bool isAction, const QDateTime &datetime)
|
||||
//{
|
||||
// if (earliestMessage == nullptr)
|
||||
// {
|
||||
// earliestMessage = new QDateTime(datetime);
|
||||
// }
|
||||
|
||||
// const Core* core = Core::getInstance();
|
||||
|
||||
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||
// QString author = core->getUsername();;
|
||||
|
||||
// if (!isAction && message.startsWith("/me "))
|
||||
// { // always render actions regardless of what core thinks
|
||||
// isAction = true;
|
||||
// message = message.right(message.length()-4);
|
||||
// }
|
||||
|
||||
// if (isAction)
|
||||
// {
|
||||
// previousId = ToxID(); // next msg has a name regardless
|
||||
// return MessageActionPtr(new ActionAction (getElidedName(author), message, date, true));
|
||||
// }
|
||||
|
||||
// MessageActionPtr res;
|
||||
// if (previousId.isMine())
|
||||
// res = MessageActionPtr(new MessageAction(QString(), message, date, true));
|
||||
// else
|
||||
// res = MessageActionPtr(new MessageAction(getElidedName(author), message, date, true));
|
||||
|
||||
// previousId = Core::getInstance()->getSelfId();
|
||||
// return res;
|
||||
//}
|
||||
|
||||
//ChatMessage* GenericChatForm::genSystemInfoAction(const QString &message, const QString &type, const QDateTime &datetime)
|
||||
//{
|
||||
// previousId = ToxID();
|
||||
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||
|
||||
// return ChatActionPtr(new SystemMessageAction(message, type, date));
|
||||
//}
|
||||
return QString();
|
||||
}
|
||||
|
@ -50,11 +50,10 @@ public:
|
||||
virtual void setName(const QString &newName);
|
||||
virtual void show(Ui::MainWindow &ui);
|
||||
|
||||
void addMessage(const QString& author, const QString &message, bool isAction, const QDateTime &datetime); ///< Deprecated
|
||||
ChatMessage* addMessage(const ToxID& author, const QString &message, bool isAction, const QDateTime &datetime, bool isSent);
|
||||
ChatMessage* addSelfMessage(const QString &message, bool isAction, const QDateTime &datetime, bool isSent);
|
||||
|
||||
void addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime);
|
||||
void addAlertMessage(const QString& author, QString message, QDateTime datetime); ///< Deprecated
|
||||
void addAlertMessage(const ToxID& author, QString message, QDateTime datetime);
|
||||
bool isEmpty();
|
||||
|
||||
@ -74,9 +73,10 @@ protected slots:
|
||||
void onEmoteButtonClicked();
|
||||
void onEmoteInsertRequested(QString str);
|
||||
void clearChatArea(bool);
|
||||
void onChatWidgetClicked();
|
||||
|
||||
protected:
|
||||
QString getElidedName(const QString& name);
|
||||
QString resolveToxID(const ToxID &id);
|
||||
|
||||
ToxID previousId;
|
||||
QMenu menu;
|
||||
@ -90,6 +90,8 @@ protected:
|
||||
QPushButton *sendButton;
|
||||
ChatLog *chatWidget;
|
||||
QDateTime *earliestMessage;
|
||||
bool audioInputFlag;
|
||||
bool audioOutputFlag;
|
||||
};
|
||||
|
||||
#endif // GENERICCHATFORM_H
|
||||
|
@ -28,24 +28,33 @@
|
||||
#include <QDragEnterEvent>
|
||||
#include "src/historykeeper.h"
|
||||
#include "src/misc/flowlayout.h"
|
||||
#include <QDebug>
|
||||
|
||||
GroupChatForm::GroupChatForm(Group* chatGroup)
|
||||
: group(chatGroup)
|
||||
: group(chatGroup), inCall{false}
|
||||
{
|
||||
nusersLabel = new QLabel();
|
||||
|
||||
tabber = new TabCompleter(msgEdit, group);
|
||||
|
||||
fileButton->setEnabled(false);
|
||||
callButton->setVisible(false);
|
||||
videoButton->setVisible(false);
|
||||
volButton->setVisible(false);
|
||||
micButton->setVisible(false);
|
||||
if (group->isAvGroupchat())
|
||||
{
|
||||
videoButton->setEnabled(false);
|
||||
videoButton->setObjectName("grey");
|
||||
}
|
||||
else
|
||||
{
|
||||
videoButton->setVisible(false);
|
||||
callButton->setVisible(false);
|
||||
volButton->setVisible(false);
|
||||
micButton->setVisible(false);
|
||||
}
|
||||
|
||||
nameLabel->setText(group->widget->getName());
|
||||
nameLabel->setText(group->getGroupWidget()->getName());
|
||||
|
||||
nusersLabel->setFont(Style::getFont(Style::Medium));
|
||||
nusersLabel->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->getPeersCount()));
|
||||
nusersLabel->setObjectName("statusLabel");
|
||||
|
||||
avatar->setPixmap(QPixmap(":/img/group_dark.png"), Qt::transparent);
|
||||
@ -53,7 +62,7 @@ GroupChatForm::GroupChatForm(Group* chatGroup)
|
||||
msgEdit->setObjectName("group");
|
||||
|
||||
namesListLayout = new FlowLayout(0,5,0);
|
||||
QStringList names(group->peers.values());
|
||||
QStringList names(group->getPeerList());
|
||||
for (const QString& name : names)
|
||||
namesListLayout->addWidget(new QLabel(name));
|
||||
|
||||
@ -68,6 +77,11 @@ GroupChatForm::GroupChatForm(Group* chatGroup)
|
||||
connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered()));
|
||||
connect(msgEdit, &ChatTextEdit::tabPressed, tabber, &TabCompleter::complete);
|
||||
connect(msgEdit, &ChatTextEdit::keyPressed, tabber, &TabCompleter::reset);
|
||||
connect(callButton, &QPushButton::clicked, this, &GroupChatForm::onCallClicked);
|
||||
connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle()));
|
||||
connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle()));
|
||||
connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig)
|
||||
{if (text != orig) emit groupTitleChanged(group->getGroupId(), text.left(128));} );
|
||||
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
@ -83,15 +97,15 @@ void GroupChatForm::onSendTriggered()
|
||||
if (msg.startsWith("/me "))
|
||||
{
|
||||
msg = msg.right(msg.length() - 4);
|
||||
emit sendAction(group->groupId, msg);
|
||||
emit sendAction(group->getGroupId(), msg);
|
||||
} else {
|
||||
emit sendMessage(group->groupId, msg);
|
||||
emit sendMessage(group->getGroupId(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupChatForm::onUserListChanged()
|
||||
{
|
||||
nusersLabel->setText(tr("%1 users in chat").arg(group->nPeers));
|
||||
nusersLabel->setText(tr("%1 users in chat").arg(group->getPeersCount()));
|
||||
|
||||
QLayoutItem *child;
|
||||
while ((child = namesListLayout->takeAt(0)))
|
||||
@ -101,7 +115,7 @@ void GroupChatForm::onUserListChanged()
|
||||
delete child;
|
||||
}
|
||||
|
||||
QStringList names(group->peers.values());
|
||||
QStringList names(group->getPeerList());
|
||||
unsigned nNames = names.size();
|
||||
for (unsigned i=0; i<nNames; ++i)
|
||||
{
|
||||
@ -125,7 +139,108 @@ void GroupChatForm::dropEvent(QDropEvent *ev)
|
||||
if (ev->mimeData()->hasFormat("friend"))
|
||||
{
|
||||
int friendId = ev->mimeData()->data("friend").toInt();
|
||||
Core::getInstance()->groupInviteFriend(friendId, group->groupId);
|
||||
Core::getInstance()->groupInviteFriend(friendId, group->getGroupId());
|
||||
}
|
||||
}
|
||||
|
||||
void GroupChatForm::onMicMuteToggle()
|
||||
{
|
||||
if (audioInputFlag == true)
|
||||
{
|
||||
if (micButton->objectName() == "red")
|
||||
{
|
||||
Core::getInstance()->enableGroupCallMic(group->getGroupId());
|
||||
micButton->setObjectName("green");
|
||||
}
|
||||
else
|
||||
{
|
||||
Core::getInstance()->disableGroupCallMic(group->getGroupId());
|
||||
micButton->setObjectName("red");
|
||||
}
|
||||
|
||||
Style::repolish(micButton);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupChatForm::onVolMuteToggle()
|
||||
{
|
||||
if (audioOutputFlag == true)
|
||||
{
|
||||
if (volButton->objectName() == "red")
|
||||
{
|
||||
Core::getInstance()->enableGroupCallVol(group->getGroupId());
|
||||
volButton->setObjectName("green");
|
||||
}
|
||||
else
|
||||
{
|
||||
Core::getInstance()->disableGroupCallVol(group->getGroupId());
|
||||
volButton->setObjectName("red");
|
||||
}
|
||||
|
||||
Style::repolish(volButton);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupChatForm::onCallClicked()
|
||||
{
|
||||
if (!inCall)
|
||||
{
|
||||
Core::getInstance()->joinGroupCall(group->getGroupId());
|
||||
audioInputFlag = true;
|
||||
audioOutputFlag = true;
|
||||
callButton->setObjectName("red");
|
||||
callButton->style()->polish(callButton);
|
||||
inCall = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Core::getInstance()->leaveGroupCall(group->getGroupId());
|
||||
audioInputFlag = false;
|
||||
audioOutputFlag = false;
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
volButton->setObjectName("green");
|
||||
volButton->style()->polish(volButton);
|
||||
callButton->setObjectName("green");
|
||||
callButton->style()->polish(callButton);
|
||||
inCall = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GroupChatForm::keyPressEvent(QKeyEvent* ev)
|
||||
{
|
||||
// Push to talk (CTRL+P)
|
||||
if (ev->key() == Qt::Key_P && (ev->modifiers() & Qt::ControlModifier) && inCall)
|
||||
{
|
||||
Core* core = Core::getInstance();
|
||||
if (!core->isGroupCallMicEnabled(group->getGroupId()))
|
||||
{
|
||||
core->enableGroupCallMic(group->getGroupId());
|
||||
micButton->setObjectName("green");
|
||||
micButton->style()->polish(micButton);
|
||||
Style::repolish(micButton);
|
||||
}
|
||||
}
|
||||
|
||||
if (msgEdit->hasFocus())
|
||||
return;
|
||||
}
|
||||
|
||||
void GroupChatForm::keyReleaseEvent(QKeyEvent* ev)
|
||||
{
|
||||
// Push to talk (CTRL+P)
|
||||
if (ev->key() == Qt::Key_P && (ev->modifiers() & Qt::ControlModifier) && inCall)
|
||||
{
|
||||
Core* core = Core::getInstance();
|
||||
if (core->isGroupCallMicEnabled(group->getGroupId()))
|
||||
{
|
||||
core->disableGroupCallMic(group->getGroupId());
|
||||
micButton->setObjectName("red");
|
||||
micButton->style()->polish(micButton);
|
||||
Style::repolish(micButton);
|
||||
}
|
||||
}
|
||||
|
||||
if (msgEdit->hasFocus())
|
||||
return;
|
||||
}
|
||||
|
@ -32,8 +32,17 @@ public:
|
||||
|
||||
void onUserListChanged();
|
||||
|
||||
void keyPressEvent(QKeyEvent* ev);
|
||||
void keyReleaseEvent(QKeyEvent* ev);
|
||||
|
||||
signals:
|
||||
void groupTitleChanged(int groupnum, const QString& name);
|
||||
|
||||
private slots:
|
||||
void onSendTriggered();
|
||||
void onMicMuteToggle();
|
||||
void onVolMuteToggle();
|
||||
void onCallClicked();
|
||||
|
||||
protected:
|
||||
// drag & drop
|
||||
@ -45,6 +54,7 @@ private:
|
||||
FlowLayout* namesListLayout;
|
||||
QLabel *nusersLabel;
|
||||
TabCompleter* tabber;
|
||||
bool inCall;
|
||||
};
|
||||
|
||||
#endif // GROUPCHATFORM_H
|
||||
|
62
src/widget/form/settings/advancedform.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 "ui_advancedsettings.h"
|
||||
|
||||
#include "advancedform.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include "src/misc/db/plaindb.h"
|
||||
|
||||
AdvancedForm::AdvancedForm() :
|
||||
GenericForm(tr("Advanced"), QPixmap(":/img/settings/general.png"))
|
||||
{
|
||||
bodyUI = new Ui::AdvancedSettings;
|
||||
bodyUI->setupUi(this);
|
||||
|
||||
bodyUI->dbLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
bodyUI->dbLabel->setOpenExternalLinks(true);
|
||||
|
||||
bodyUI->syncTypeComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
|
||||
bodyUI->syncTypeComboBox->addItems({tr("FULL - very safe, slowest (recommended)"),
|
||||
tr("NORMAL - almost as safe as FULL, about 20% faster than FULL"),
|
||||
tr("OFF - disables all safety, when something goes wrong your history may be lost, fastest (not recommended)")
|
||||
});
|
||||
int index = 2 - static_cast<int>(Settings::getInstance().getDbSyncType());
|
||||
bodyUI->syncTypeComboBox->setCurrentIndex(index);
|
||||
|
||||
connect(bodyUI->syncTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onDbSyncTypeUpdated()));
|
||||
connect(bodyUI->resetButton, SIGNAL(clicked()), this, SLOT(resetToDefault()));
|
||||
}
|
||||
|
||||
AdvancedForm::~AdvancedForm()
|
||||
{
|
||||
delete bodyUI;
|
||||
}
|
||||
|
||||
void AdvancedForm::onDbSyncTypeUpdated()
|
||||
{
|
||||
int index = 2 - bodyUI->syncTypeComboBox->currentIndex();
|
||||
Settings::getInstance().setDbSyncType(index);
|
||||
HistoryKeeper::getInstance()->setSyncType(Settings::getInstance().getDbSyncType());
|
||||
}
|
||||
|
||||
void AdvancedForm::resetToDefault()
|
||||
{
|
||||
int index = 2 - static_cast<int>(Db::syncType::stFull);
|
||||
bodyUI->syncTypeComboBox->setCurrentIndex(index);
|
||||
onDbSyncTypeUpdated();
|
||||
}
|
43
src/widget/form/settings/advancedform.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
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 ADVANCEDFORM_H
|
||||
#define ADVANCEDFORM_H
|
||||
|
||||
#include "genericsettings.h"
|
||||
|
||||
class Core;
|
||||
|
||||
namespace Ui {
|
||||
class AdvancedSettings;
|
||||
}
|
||||
|
||||
class AdvancedForm : public GenericForm
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AdvancedForm();
|
||||
virtual ~AdvancedForm();
|
||||
|
||||
private slots:
|
||||
void onDbSyncTypeUpdated();
|
||||
void resetToDefault();
|
||||
|
||||
private:
|
||||
Ui::AdvancedSettings* bodyUI;
|
||||
};
|
||||
|
||||
#endif // ADVANCEDFORM_H
|
109
src/widget/form/settings/advancedsettings.ui
Normal file
@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AdvancedSettings</class>
|
||||
<widget class="QWidget" name="AdvancedSettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>380</width>
|
||||
<height>280</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="warningLabel">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; color:#ff0000;">IMPORTANT NOTE</span></p><p><span style=" color:#ff0000;">Unless you </span><span style=" font-weight:600; color:#ff0000;">really</span><span style=" color:#ff0000;"> know what you are doing, please do </span><span style=" font-weight:600; color:#ff0000;">not</span><span style=" color:#ff0000;"> change anything here. Changes made here may lead to problems with qTox, and even to loss of your data, e.g. history.</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QPushButton" name="resetButton">
|
||||
<property name="text">
|
||||
<string>Reset to default settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QGroupBox" name="historyGroup">
|
||||
<property name="title">
|
||||
<string>History</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="dbLabel">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Synchronous writing to DB</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="syncTypeComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -17,6 +17,7 @@
|
||||
#include "avform.h"
|
||||
#include "ui_avsettings.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include "src/audio.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <OpenAL/al.h>
|
||||
@ -32,8 +33,11 @@ AVForm::AVForm() :
|
||||
bodyUI = new Ui::AVSettings;
|
||||
bodyUI->setupUi(this);
|
||||
|
||||
getAudioOutDevices();
|
||||
getAudioInDevices();
|
||||
#ifdef QTOX_FILTER_AUDIO
|
||||
bodyUI->filterAudio->setChecked(Settings::getInstance().getFilterAudio());
|
||||
#else
|
||||
bodyUI->filterAudio->setDisabled(true);
|
||||
#endif
|
||||
|
||||
connect(Camera::getInstance(), &Camera::propProbingFinished, this, &AVForm::onPropProbingFinished);
|
||||
connect(Camera::getInstance(), &Camera::resolutionProbingFinished, this, &AVForm::onResProbingFinished);
|
||||
@ -41,6 +45,8 @@ AVForm::AVForm() :
|
||||
auto qcomboboxIndexChanged = (void(QComboBox::*)(const QString&)) &QComboBox::currentIndexChanged;
|
||||
connect(bodyUI->inDevCombobox, qcomboboxIndexChanged, this, &AVForm::onInDevChanged);
|
||||
connect(bodyUI->outDevCombobox, qcomboboxIndexChanged, this, &AVForm::onOutDevChanged);
|
||||
connect(bodyUI->filterAudio, SIGNAL(toggled(bool)), this, SLOT(onFilterAudioToggled(bool)));
|
||||
connect(bodyUI->rescanButton, &QPushButton::clicked, this, [=](){getAudioInDevices(); getAudioOutDevices();});
|
||||
}
|
||||
|
||||
AVForm::~AVForm()
|
||||
@ -50,6 +56,9 @@ AVForm::~AVForm()
|
||||
|
||||
void AVForm::present()
|
||||
{
|
||||
getAudioOutDevices();
|
||||
getAudioInDevices();
|
||||
|
||||
bodyUI->CamVideoSurface->setSource(Camera::getInstance());
|
||||
|
||||
Camera::getInstance()->probeProp(Camera::SATURATION);
|
||||
@ -179,9 +188,16 @@ void AVForm::getAudioOutDevices()
|
||||
void AVForm::onInDevChanged(const QString &deviceDescriptor)
|
||||
{
|
||||
Settings::getInstance().setInDev(deviceDescriptor);
|
||||
Audio::openInput(deviceDescriptor);
|
||||
}
|
||||
|
||||
void AVForm::onOutDevChanged(const QString& deviceDescriptor)
|
||||
{
|
||||
Settings::getInstance().setOutDev(deviceDescriptor);
|
||||
Audio::openOutput(deviceDescriptor);
|
||||
}
|
||||
|
||||
void AVForm::onFilterAudioToggled(bool filterAudio)
|
||||
{
|
||||
Settings::getInstance().setFilterAudio(filterAudio);
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ private slots:
|
||||
// audio
|
||||
void onInDevChanged(const QString& deviceDescriptor);
|
||||
void onOutDevChanged(const QString& deviceDescriptor);
|
||||
void onFilterAudioToggled(bool filterAudio);
|
||||
|
||||
// camera
|
||||
void onPropProbingFinished(Camera::Prop prop, double val);
|
||||
|
@ -30,8 +30,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>824</width>
|
||||
<height>489</height>
|
||||
<width>808</width>
|
||||
<height>618</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
@ -41,54 +41,68 @@
|
||||
<string>Audio Settings</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="6" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="microphoneLabel">
|
||||
<property name="text">
|
||||
<string>Microphone</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="playbackLabel">
|
||||
<property name="text">
|
||||
<string>Playback</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QSlider" name="playbackSlider">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="7" column="1">
|
||||
<widget class="QSlider" name="microphoneSlider">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="outDevLabel">
|
||||
<property name="text">
|
||||
<string>Playback device</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="inDevLabel">
|
||||
<property name="text">
|
||||
<string>Capture device</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QComboBox" name="inDevCombobox"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="outDevCombobox"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="rescanButton">
|
||||
<property name="text">
|
||||
<string>Rescan audio devices</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QCheckBox" name="filterAudio">
|
||||
<property name="text">
|
||||
<string>Filter audio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "src/misc/settings.h"
|
||||
#include "src/misc/smileypack.h"
|
||||
#include "src/core.h"
|
||||
#include "src/misc/style.h"
|
||||
#include <QMessageBox>
|
||||
#include <QStyleFactory>
|
||||
#include <QTime>
|
||||
@ -29,38 +30,47 @@
|
||||
|
||||
#include "src/autoupdate.h"
|
||||
|
||||
static QStringList locales = {"bg", "de", "en", "fr", "it", "mannol", "pirate", "pl", "ru", "fi", "sv", "uk"};
|
||||
static QStringList langs = {"Български", "Deustch", "English", "Français", "Italiano", "mannol", "Pirate", "Polski", "Русский", "Suomi", "Svenska", "Українська"};
|
||||
static QStringList locales = {"bg", "de", "en", "es", "fr", "it", "mannol", "pirate", "pl", "ru", "fi", "sv", "uk"};
|
||||
static QStringList langs = {"Български", "Deutsch", "English", "Español", "Français", "Italiano", "mannol", "Pirate", "Polski", "Русский", "Suomi", "Svenska", "Українська"};
|
||||
|
||||
static QStringList timeFormats = {"hh:mm AP", "hh:mm", "hh:mm:ss AP", "hh:mm:ss"};
|
||||
|
||||
GeneralForm::GeneralForm(SettingsWidget *myParent) :
|
||||
GenericForm(tr("General"), QPixmap(":/img/settings/general.png"))
|
||||
{
|
||||
parent = myParent;
|
||||
|
||||
parent = myParent;
|
||||
|
||||
bodyUI = new Ui::GeneralSettings;
|
||||
bodyUI->setupUi(this);
|
||||
|
||||
bodyUI->checkUpdates->setVisible(AUTOUPDATE_ENABLED);
|
||||
bodyUI->checkUpdates->setChecked(Settings::getInstance().getCheckUpdates());
|
||||
bodyUI->trayLayout->addStretch();
|
||||
|
||||
bodyUI->trayBehavior->addStretch();
|
||||
|
||||
bodyUI->cbEnableIPv6->setChecked(Settings::getInstance().getEnableIPv6());
|
||||
for (int i = 0; i < langs.size(); i++)
|
||||
bodyUI->transComboBox->insertItem(i, langs[i]);
|
||||
bodyUI->transComboBox->setCurrentIndex(locales.indexOf(Settings::getInstance().getTranslation()));
|
||||
bodyUI->cbMakeToxPortable->setChecked(Settings::getInstance().getMakeToxPortable());
|
||||
|
||||
bool showSystemTray = Settings::getInstance().getShowSystemTray();
|
||||
|
||||
bodyUI->showSystemTray->setChecked(showSystemTray);
|
||||
bodyUI->startInTray->setChecked(Settings::getInstance().getAutostartInTray());
|
||||
bodyUI->startInTray->setEnabled(showSystemTray);
|
||||
bodyUI->closeToTray->setChecked(Settings::getInstance().getCloseToTray());
|
||||
bodyUI->closeToTray->setEnabled(showSystemTray);
|
||||
bodyUI->minimizeToTray->setChecked(Settings::getInstance().getMinimizeToTray());
|
||||
bodyUI->minimizeToTray->setEnabled(showSystemTray);
|
||||
bodyUI->lightTrayIcon->setChecked(Settings::getInstance().getLightTrayIcon());
|
||||
bodyUI->lightTrayIcon->setEnabled(showSystemTray);
|
||||
bodyUI->statusChanges->setChecked(Settings::getInstance().getStatusChangeNotificationEnabled());
|
||||
bodyUI->useEmoticons->setChecked(Settings::getInstance().getUseEmoticons());
|
||||
bodyUI->autoacceptFiles->setChecked(Settings::getInstance().getAutoSaveEnabled());
|
||||
bodyUI->autoSaveFilesDir->setText(Settings::getInstance().getGlobalAutoAcceptDir());
|
||||
bodyUI->showInFront->setChecked(Settings::getInstance().getShowInFront());
|
||||
bodyUI->cbFauxOfflineMessaging->setChecked(Settings::getInstance().getFauxOfflineMessaging());
|
||||
|
||||
|
||||
for (auto entry : SmileyPack::listSmileyPacks())
|
||||
{
|
||||
bodyUI->smileyPackBrowser->addItem(entry.first, entry.second);
|
||||
@ -70,63 +80,75 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
|
||||
|
||||
bodyUI->styleBrowser->addItem(tr("None"));
|
||||
bodyUI->styleBrowser->addItems(QStyleFactory::keys());
|
||||
|
||||
if(QStyleFactory::keys().contains(Settings::getInstance().getStyle()))
|
||||
|
||||
if (QStyleFactory::keys().contains(Settings::getInstance().getStyle()))
|
||||
bodyUI->styleBrowser->setCurrentText(Settings::getInstance().getStyle());
|
||||
else
|
||||
bodyUI->styleBrowser->setCurrentText(tr("None"));
|
||||
|
||||
|
||||
for (QString color : Style::themeColorNames)
|
||||
bodyUI->themeColorCBox->addItem(color);
|
||||
bodyUI->themeColorCBox->setCurrentIndex(Settings::getInstance().getThemeColor());
|
||||
|
||||
bodyUI->emoticonSize->setValue(Settings::getInstance().getEmojiFontPointSize());
|
||||
|
||||
|
||||
QStringList timestamps;
|
||||
timestamps << QString("%1 - %2").arg(timeFormats[0],QTime::currentTime().toString(timeFormats[0]))
|
||||
<< QString("%1 - %2").arg(timeFormats[1],QTime::currentTime().toString(timeFormats[1]))
|
||||
<< QString("%1 - %2").arg(timeFormats[2],QTime::currentTime().toString(timeFormats[2]))
|
||||
<< QString("%1 - %2").arg(timeFormats[3],QTime::currentTime().toString(timeFormats[3]));
|
||||
bodyUI->timestamp->addItems(timestamps);
|
||||
|
||||
|
||||
bodyUI->timestamp->setCurrentText(QString("%1 - %2").arg(Settings::getInstance().getTimestampFormat(),
|
||||
QTime::currentTime().toString(Settings::getInstance().getTimestampFormat()))
|
||||
); //idiot proof enough?
|
||||
|
||||
|
||||
bodyUI->autoAwaySpinBox->setValue(Settings::getInstance().getAutoAwayTime());
|
||||
|
||||
|
||||
bodyUI->cbEnableUDP->setChecked(!Settings::getInstance().getForceTCP());
|
||||
bodyUI->proxyAddr->setText(Settings::getInstance().getProxyAddr());
|
||||
int port = Settings::getInstance().getProxyPort();
|
||||
if (port != -1)
|
||||
bodyUI->proxyPort->setValue(port);
|
||||
|
||||
bodyUI->cbUseProxy->setChecked(Settings::getInstance().getUseProxy());
|
||||
bodyUI->proxyType->setCurrentIndex(static_cast<int>(Settings::getInstance().getProxyType()));
|
||||
onUseProxyUpdated();
|
||||
|
||||
//general
|
||||
connect(bodyUI->checkUpdates, &QCheckBox::stateChanged, this, &GeneralForm::onCheckUpdateChanged);
|
||||
connect(bodyUI->transComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onTranslationUpdated()));
|
||||
connect(bodyUI->cbMakeToxPortable, &QCheckBox::stateChanged, this, &GeneralForm::onMakeToxPortableUpdated);
|
||||
connect(bodyUI->showSystemTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetShowSystemTray);
|
||||
connect(bodyUI->startInTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetAutostartInTray);
|
||||
connect(bodyUI->closeToTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetCloseToTray);
|
||||
connect(bodyUI->minimizeToTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetMinimizeToTray);
|
||||
connect(bodyUI->lightTrayIcon, &QCheckBox::stateChanged, this, &GeneralForm::onSetLightTrayIcon);
|
||||
connect(bodyUI->statusChanges, &QCheckBox::stateChanged, this, &GeneralForm::onSetStatusChange);
|
||||
connect(bodyUI->autoAwaySpinBox, SIGNAL(editingFinished()), this, SLOT(onAutoAwayChanged()));
|
||||
connect(bodyUI->showInFront, &QCheckBox::stateChanged, this, &GeneralForm::onSetShowInFront);
|
||||
connect(bodyUI->autoacceptFiles, &QCheckBox::stateChanged, this, &GeneralForm::onAutoAcceptFileChange);
|
||||
if(bodyUI->autoacceptFiles->isChecked())
|
||||
if (bodyUI->autoacceptFiles->isChecked())
|
||||
connect(bodyUI->autoSaveFilesDir, SIGNAL(clicked()), this, SLOT(onAutoSaveDirChange()));
|
||||
//theme
|
||||
connect(bodyUI->useEmoticons, &QCheckBox::stateChanged, this, &GeneralForm::onUseEmoticonsChange);
|
||||
connect(bodyUI->smileyPackBrowser, SIGNAL(currentIndexChanged(int)), this, SLOT(onSmileyBrowserIndexChanged(int)));
|
||||
connect(bodyUI->styleBrowser, SIGNAL(currentTextChanged(QString)), this, SLOT(onStyleSelected(QString)));
|
||||
connect(bodyUI->emoticonSize, SIGNAL(editingFinished()), this, SLOT(onEmoticonSizeChanged()));
|
||||
connect(bodyUI->themeColorCBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeColorChanged(int)));
|
||||
connect(bodyUI->emoticonSize, SIGNAL(editingFinished()), this, SLOT(onEmoticonSizeChanged()));
|
||||
connect(bodyUI->timestamp, SIGNAL(currentIndexChanged(int)), this, SLOT(onTimestampSelected(int)));
|
||||
//connection
|
||||
connect(bodyUI->cbEnableIPv6, &QCheckBox::stateChanged, this, &GeneralForm::onEnableIPv6Updated);
|
||||
connect(bodyUI->cbEnableUDP, &QCheckBox::stateChanged, this, &GeneralForm::onUDPUpdated);
|
||||
connect(bodyUI->cbUseProxy, &QCheckBox::stateChanged, this, &GeneralForm::onUseProxyUpdated);
|
||||
connect(bodyUI->proxyType, SIGNAL(currentIndexChanged(int)), this, SLOT(onUseProxyUpdated()));
|
||||
connect(bodyUI->proxyAddr, &QLineEdit::editingFinished, this, &GeneralForm::onProxyAddrEdited);
|
||||
connect(bodyUI->proxyPort, SIGNAL(valueChanged(int)), this, SLOT(onProxyPortEdited(int)));
|
||||
connect(bodyUI->reconnectButton, &QPushButton::clicked, this, &GeneralForm::onReconnectClicked);
|
||||
connect(bodyUI->cbFauxOfflineMessaging, &QCheckBox::stateChanged, this, &GeneralForm::onFauxOfflineMessaging);
|
||||
|
||||
#ifndef QTOX_PLATFORM_EXT
|
||||
bodyUI->autoAwayLabel->setEnabled(false); // these don't seem to change the appearance of the widgets,
|
||||
bodyUI->autoAwaySpinBox->setEnabled(false); // though they are unusable
|
||||
#endif
|
||||
}
|
||||
|
||||
GeneralForm::~GeneralForm()
|
||||
@ -150,6 +172,12 @@ void GeneralForm::onMakeToxPortableUpdated()
|
||||
Settings::getInstance().setMakeToxPortable(bodyUI->cbMakeToxPortable->isChecked());
|
||||
}
|
||||
|
||||
void GeneralForm::onSetShowSystemTray()
|
||||
{
|
||||
Settings::getInstance().setShowSystemTray(bodyUI->showSystemTray->isChecked());
|
||||
emit parent->setShowSystemTray(bodyUI->showSystemTray->isChecked());
|
||||
}
|
||||
|
||||
void GeneralForm::onSetAutostartInTray()
|
||||
{
|
||||
Settings::getInstance().setAutostartInTray(bodyUI->startInTray->isChecked());
|
||||
@ -160,6 +188,12 @@ void GeneralForm::onSetCloseToTray()
|
||||
Settings::getInstance().setCloseToTray(bodyUI->closeToTray->isChecked());
|
||||
}
|
||||
|
||||
void GeneralForm::onSetLightTrayIcon()
|
||||
{
|
||||
Settings::getInstance().setLightTrayIcon(bodyUI->lightTrayIcon->isChecked());
|
||||
Widget::getInstance()->updateTrayIcon();
|
||||
}
|
||||
|
||||
void GeneralForm::onSetMinimizeToTray()
|
||||
{
|
||||
Settings::getInstance().setMinimizeToTray(bodyUI->minimizeToTray->isChecked());
|
||||
@ -167,11 +201,11 @@ void GeneralForm::onSetMinimizeToTray()
|
||||
|
||||
void GeneralForm::onStyleSelected(QString style)
|
||||
{
|
||||
if(bodyUI->styleBrowser->currentIndex() == 0)
|
||||
if (bodyUI->styleBrowser->currentIndex() == 0)
|
||||
Settings::getInstance().setStyle("None");
|
||||
else
|
||||
Settings::getInstance().setStyle(style);
|
||||
|
||||
|
||||
this->setStyle(QStyleFactory::create(style));
|
||||
parent->setBodyHeadStyle(style);
|
||||
}
|
||||
@ -190,14 +224,13 @@ void GeneralForm::onAutoAwayChanged()
|
||||
{
|
||||
int minutes = bodyUI->autoAwaySpinBox->value();
|
||||
Settings::getInstance().setAutoAwayTime(minutes);
|
||||
Widget::getInstance()->setIdleTimer(minutes);
|
||||
}
|
||||
|
||||
void GeneralForm::onAutoAcceptFileChange()
|
||||
{
|
||||
Settings::getInstance().setAutoSaveEnabled(bodyUI->autoacceptFiles->isChecked());
|
||||
|
||||
if(bodyUI->autoacceptFiles->isChecked() == true)
|
||||
|
||||
if (bodyUI->autoacceptFiles->isChecked() == true)
|
||||
connect(bodyUI->autoSaveFilesDir, SIGNAL(clicked()), this, SLOT(onAutoSaveDirChange()));
|
||||
else
|
||||
disconnect(bodyUI->autoSaveFilesDir, SIGNAL(clicked()),this, SLOT(onAutoSaveDirChange()));
|
||||
@ -207,9 +240,9 @@ void GeneralForm::onAutoSaveDirChange()
|
||||
{
|
||||
QString previousDir = Settings::getInstance().getGlobalAutoAcceptDir();
|
||||
QString directory = QFileDialog::getExistingDirectory(0, tr("Choose an auto accept directory","popup title"));
|
||||
if(directory.isEmpty())
|
||||
if (directory.isEmpty())
|
||||
directory = previousDir;
|
||||
|
||||
|
||||
Settings::getInstance().setGlobalAutoAcceptDir(directory);
|
||||
bodyUI->autoSaveFilesDir->setText(directory);
|
||||
}
|
||||
@ -253,11 +286,11 @@ void GeneralForm::onProxyPortEdited(int port)
|
||||
|
||||
void GeneralForm::onUseProxyUpdated()
|
||||
{
|
||||
bool state = bodyUI->cbUseProxy->isChecked();
|
||||
int proxytype = bodyUI->proxyType->currentIndex();
|
||||
|
||||
bodyUI->proxyAddr->setEnabled(state);
|
||||
bodyUI->proxyPort->setEnabled(state);
|
||||
Settings::getInstance().setUseProxy(state);
|
||||
bodyUI->proxyAddr->setEnabled(proxytype);
|
||||
bodyUI->proxyPort->setEnabled(proxytype);
|
||||
Settings::getInstance().setProxyType(proxytype);
|
||||
}
|
||||
|
||||
void GeneralForm::onReconnectClicked()
|
||||
@ -275,10 +308,11 @@ void GeneralForm::reloadSmiles()
|
||||
QStringList smiles;
|
||||
smiles << ":)" << ";)" << ":p" << ":O" << ":["; //just in case...
|
||||
|
||||
for(int i = 0; i < emoticons.size(); i++)
|
||||
for (int i = 0; i < emoticons.size(); i++)
|
||||
smiles.push_front(emoticons.at(i).first());
|
||||
|
||||
|
||||
int pixSize = 30;
|
||||
|
||||
bodyUI->smile1->setPixmap(SmileyPack::getInstance().getAsPixmap(smiles[0]));
|
||||
bodyUI->smile2->setPixmap(SmileyPack::getInstance().getAsPixmap(smiles[1]));
|
||||
bodyUI->smile3->setPixmap(SmileyPack::getInstance().getAsPixmap(smiles[2]));
|
||||
@ -306,3 +340,10 @@ void GeneralForm::onFauxOfflineMessaging()
|
||||
{
|
||||
Settings::getInstance().setFauxOfflineMessaging(bodyUI->cbFauxOfflineMessaging->isChecked());
|
||||
}
|
||||
|
||||
void GeneralForm::onThemeColorChanged(int)
|
||||
{
|
||||
int index = bodyUI->themeColorCBox->currentIndex();
|
||||
Settings::getInstance().setThemeColor(index);
|
||||
Style::setThemeColor(index);
|
||||
}
|
||||
|
@ -34,8 +34,10 @@ private slots:
|
||||
void onEnableIPv6Updated();
|
||||
void onTranslationUpdated();
|
||||
void onMakeToxPortableUpdated();
|
||||
void onSetShowSystemTray();
|
||||
void onSetAutostartInTray();
|
||||
void onSetCloseToTray();
|
||||
void onSetLightTrayIcon();
|
||||
void onSmileyBrowserIndexChanged(int index);
|
||||
void onUDPUpdated();
|
||||
void onProxyAddrEdited();
|
||||
@ -54,6 +56,7 @@ private slots:
|
||||
void onCheckUpdateChanged();
|
||||
void onSetShowInFront();
|
||||
void onFauxOfflineMessaging();
|
||||
void onThemeColorChanged(int);
|
||||
|
||||
private:
|
||||
Ui::GeneralSettings *bodyUI;
|
||||
|
@ -38,12 +38,12 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<y>-173</y>
|
||||
<width>511</width>
|
||||
<height>698</height>
|
||||
<height>797</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,0,1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,0,0">
|
||||
<property name="spacing">
|
||||
<number>9</number>
|
||||
</property>
|
||||
@ -94,47 +94,73 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="trayLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="startInTray">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start in tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="closeToTray">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close to tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="minimizeToTray">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Minimize to tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QGroupBox" name="trayGroup">
|
||||
<property name="title">
|
||||
<string>System tray integration</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="showSystemTray">
|
||||
<property name="text">
|
||||
<string>Show system tray icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="trayBehavior">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="startInTray">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start in tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="closeToTray">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close to tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="minimizeToTray">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Minimize to tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="lightTrayIcon">
|
||||
<property name="text">
|
||||
<string>Light icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="statusChanges">
|
||||
@ -210,6 +236,9 @@
|
||||
<layout class="QHBoxLayout" name="fileLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="autoacceptFiles">
|
||||
<property name="toolTip">
|
||||
<string comment="autoaccept cb tooltip">You can set this on a per-friend basis by right clicking them.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Autoaccept files</string>
|
||||
</property>
|
||||
@ -244,8 +273,14 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="themeGroup">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>232</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Theme</string>
|
||||
</property>
|
||||
@ -258,15 +293,24 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="smileyHLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="smileyPackLabel">
|
||||
<property name="text">
|
||||
<string extracomment="Text on smiley pack label">Smiley Pack</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="smileyPackBrowser">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@ -276,75 +320,71 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile1">
|
||||
<property name="toolTip">
|
||||
<string notr="true">:)</string>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile1">
|
||||
<property name="toolTip">
|
||||
<string notr="true">:)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile2">
|
||||
<property name="toolTip">
|
||||
<string notr="true">;)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile3">
|
||||
<property name="toolTip">
|
||||
<string notr="true">:p</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile4">
|
||||
<property name="toolTip">
|
||||
<string notr="true">:O</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile5">
|
||||
<property name="toolTip">
|
||||
<string notr="true">:'(</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile2">
|
||||
<property name="toolTip">
|
||||
<string notr="true">;)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile3">
|
||||
<property name="toolTip">
|
||||
<string notr="true">:p</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile4">
|
||||
<property name="toolTip">
|
||||
<string notr="true">:O</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="smile5">
|
||||
<property name="toolTip">
|
||||
<string notr="true">:'(</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="styleHLayout">
|
||||
<item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="styleLabel">
|
||||
<property name="text">
|
||||
<string>Style</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="styleBrowser">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@ -354,18 +394,31 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="emoiticonHLayout">
|
||||
<item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="themeColorLabel">
|
||||
<property name="text">
|
||||
<string>Theme color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="themeColorCBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="emoticonSizeLabel">
|
||||
<property name="text">
|
||||
<string>Emoticon size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QSpinBox" name="emoticonSize">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@ -390,18 +443,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="timestampHLayout">
|
||||
<item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="timestampLabel">
|
||||
<property name="text">
|
||||
<string>Timestamp format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="timestamp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@ -416,7 +465,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="connectionGroup">
|
||||
<property name="title">
|
||||
<string>Connection Settings</string>
|
||||
@ -446,11 +495,40 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbUseProxy">
|
||||
<property name="text">
|
||||
<string>Use proxy (SOCKS5)</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="proxyLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Proxy type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="proxyType">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>SOCKS5</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>HTTP</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="proxyLayout">
|
||||
@ -503,5 +581,54 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>showSystemTray</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>startInTray</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>105</x>
|
||||
<y>144</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>119</x>
|
||||
<y>177</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>showSystemTray</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>closeToTray</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>205</x>
|
||||
<y>146</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>224</x>
|
||||
<y>178</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>showSystemTray</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>minimizeToTray</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>180</x>
|
||||
<y>144</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>359</x>
|
||||
<y>178</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
@ -196,9 +196,16 @@ void IdentityForm::onDeleteClicked()
|
||||
else
|
||||
{
|
||||
if (checkContinue(tr("Deletion imminent!","deletion confirmation title"),
|
||||
tr("Are you sure you want to delete this profile?","deletion confirmation text")))
|
||||
tr("Are you sure you want to delete this profile?\nAssociated friend information and chat logs will be deleted as well.","deletion confirmation text")))
|
||||
{
|
||||
QFile::remove(QDir(Settings::getSettingsDirPath()).filePath(bodyUI->profiles->currentText()+Core::TOX_EXT));
|
||||
QString profile = bodyUI->profiles->currentText();
|
||||
QDir dir(Settings::getSettingsDirPath());
|
||||
|
||||
QFile::remove(dir.filePath(profile + Core::TOX_EXT));
|
||||
QFile::remove(dir.filePath(profile + ".ini"));
|
||||
QFile::remove(HistoryKeeper::getHistoryPath(profile, 0));
|
||||
QFile::remove(HistoryKeeper::getHistoryPath(profile, 1));
|
||||
|
||||
bodyUI->profiles->removeItem(bodyUI->profiles->currentIndex());
|
||||
bodyUI->profiles->setCurrentText(Settings::getInstance().getCurrentProfile());
|
||||
}
|
||||
|
@ -30,16 +30,13 @@ PrivacyForm::PrivacyForm() :
|
||||
bodyUI = new Ui::PrivacySettings;
|
||||
bodyUI->setupUi(this);
|
||||
|
||||
bodyUI->cbTypingNotification->setChecked(Settings::getInstance().isTypingNotificationEnabled());
|
||||
bodyUI->cbKeepHistory->setChecked(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptHistory->setChecked(Settings::getInstance().getEncryptLogs());
|
||||
bodyUI->cbEncryptHistory->setEnabled(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptTox->setChecked(Settings::getInstance().getEncryptTox());
|
||||
|
||||
connect(bodyUI->cbTypingNotification, SIGNAL(stateChanged(int)), this, SLOT(onTypingNotificationEnabledUpdated()));
|
||||
connect(bodyUI->cbKeepHistory, SIGNAL(stateChanged(int)), this, SLOT(onEnableLoggingUpdated()));
|
||||
connect(bodyUI->cbEncryptHistory, SIGNAL(clicked()), this, SLOT(onEncryptLogsUpdated()));
|
||||
connect(bodyUI->cbEncryptTox, SIGNAL(clicked()), this, SLOT(onEncryptToxUpdated()));
|
||||
connect(bodyUI->nospamLineEdit, SIGNAL(editingFinished()), this, SLOT(setNospam()));
|
||||
connect(bodyUI->randomNosapamButton, SIGNAL(clicked()), this, SLOT(generateRandomNospam()));
|
||||
connect(bodyUI->nospamLineEdit, SIGNAL(textChanged(QString)), this, SLOT(onNospamEdit()));
|
||||
}
|
||||
|
||||
PrivacyForm::~PrivacyForm()
|
||||
@ -134,3 +131,48 @@ void PrivacyForm::onEncryptToxUpdated()
|
||||
if (!Settings::getInstance().getEncryptTox())
|
||||
Core::getInstance()->clearPassword(Core::ptMain);
|
||||
}
|
||||
|
||||
void PrivacyForm::setNospam()
|
||||
{
|
||||
QString newNospam = bodyUI->nospamLineEdit->text();
|
||||
|
||||
bool ok;
|
||||
uint32_t nospam = newNospam.toLongLong(&ok, 16);
|
||||
if (ok)
|
||||
Core::getInstance()->setNospam(nospam);
|
||||
}
|
||||
|
||||
void PrivacyForm::present()
|
||||
{
|
||||
bodyUI->nospamLineEdit->setText(Core::getInstance()->getSelfId().noSpam);
|
||||
bodyUI->cbTypingNotification->setChecked(Settings::getInstance().isTypingNotificationEnabled());
|
||||
bodyUI->cbKeepHistory->setChecked(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptHistory->setChecked(Settings::getInstance().getEncryptLogs());
|
||||
bodyUI->cbEncryptHistory->setEnabled(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptTox->setChecked(Settings::getInstance().getEncryptTox());
|
||||
}
|
||||
|
||||
void PrivacyForm::generateRandomNospam()
|
||||
{
|
||||
QTime time = QTime::currentTime();
|
||||
qsrand((uint)time.msec());
|
||||
|
||||
uint32_t newNospam{0};
|
||||
for (int i = 0; i < 4; i++)
|
||||
newNospam = (newNospam<<8) + (qrand() % 256); // Generate byte by byte. For some reason.
|
||||
|
||||
Core::getInstance()->setNospam(newNospam);
|
||||
bodyUI->nospamLineEdit->setText(Core::getInstance()->getSelfId().noSpam);
|
||||
}
|
||||
|
||||
void PrivacyForm::onNospamEdit()
|
||||
{
|
||||
QString str = bodyUI->nospamLineEdit->text();
|
||||
int curs = bodyUI->nospamLineEdit->cursorPosition();
|
||||
if (str.length() != 8)
|
||||
{
|
||||
str = QString("00000000").replace(0, str.length(), str);
|
||||
bodyUI->nospamLineEdit->setText(str);
|
||||
bodyUI->nospamLineEdit->setCursorPosition(curs);
|
||||
};
|
||||
}
|
||||
|
@ -30,10 +30,14 @@ public:
|
||||
PrivacyForm();
|
||||
~PrivacyForm();
|
||||
|
||||
virtual void present();
|
||||
|
||||
private slots:
|
||||
void onEnableLoggingUpdated();
|
||||
void onTypingNotificationEnabledUpdated();
|
||||
|
||||
void setNospam();
|
||||
void generateRandomNospam();
|
||||
void onNospamEdit();
|
||||
void onEncryptLogsUpdated();
|
||||
void onEncryptToxUpdated();
|
||||
|
||||
|
@ -96,6 +96,46 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QGroupBox" name="nospamGroup">
|
||||
<property name="title">
|
||||
<string>Nospam</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="nospamLineEdit">
|
||||
<property name="inputMask">
|
||||
<string>HHHHHHHH</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="randomNosapamButton">
|
||||
<property name="text">
|
||||
<string>Generate random nospam</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "src/widget/form/settings/identityform.h"
|
||||
#include "src/widget/form/settings/privacyform.h"
|
||||
#include "src/widget/form/settings/avform.h"
|
||||
#include "src/widget/form/settings/advancedform.h"
|
||||
#include <QTabWidget>
|
||||
|
||||
SettingsWidget::SettingsWidget(QWidget* parent)
|
||||
@ -54,8 +55,9 @@ SettingsWidget::SettingsWidget(QWidget* parent)
|
||||
IdentityForm* ifrm = new IdentityForm;
|
||||
PrivacyForm* pfrm = new PrivacyForm;
|
||||
AVForm* avfrm = new AVForm;
|
||||
AdvancedForm *expfrm = new AdvancedForm;
|
||||
|
||||
GenericForm* cfgForms[] = { gfrm, ifrm, pfrm, avfrm };
|
||||
GenericForm* cfgForms[] = { gfrm, ifrm, pfrm, avfrm, expfrm };
|
||||
for (GenericForm* cfgForm : cfgForms)
|
||||
settingsWidgets->addTab(cfgForm, cfgForm->getFormIcon(), cfgForm->getFormName());
|
||||
|
||||
|