diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 65f74dc..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: CI
-
-on:
-  - push
-  - pull_request
-
-jobs:
-
-  build:
-
-    strategy:
-      matrix:
-        python-version:
-          - "3.7"
-          - "3.8"
-          - "3.9"
-          - "3.10"
-
-    name: Python ${{ matrix.python-version }}
-    runs-on: ubuntu-20.04
-
-    steps:
-
-      - uses: actions/checkout@v2
-
-      - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v2
-        with:
-          python-version: ${{ matrix.python-version }}
-
-      - name: Install dependencies
-        run: |
-          pip install -r requirements.txt
-          pip install bandit flake8 pylint
-
-      - name: Lint with flake8
-        run: make flake8
-
-      # - name: Lint with pylint
-      #   run: make pylint
-
-      - name: Lint with bandit
-        run: make bandit
diff --git a/.gitignore b/.gitignore
index 5e4d717..47d34c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,41 +1,23 @@
-.pylint.err
-.pylint.out
 *.pyc
 *.pyo
-
-*.zip
-*.bak
-*.lis
-*.dst
-*.so
-
+*.ui
 toxygen/toxcore
 tests/tests
-toxygen/libs
+tests/libs
 tests/.cache
 tests/__pycache__
-tests/avatars
 toxygen/libs
 .idea
 *~
-#*
 *.iml
-*.junk
-
 *.so
 *.log
 toxygen/build
 toxygen/dist
 *.spec
-dist
+dist/
 toxygen/avatars
 toxygen/__pycache__
 /*.egg-info
 /*.egg
-html
-Toxygen.egg-info
-*.tox
-.cache
-*.db
-*~
-Makefile
+
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
deleted file mode 100644
index 524302e..0000000
--- a/.pre-commit-config.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- mode: yaml; indent-tabs-mode: nil; tab-width: 2; coding: utf-8-unix -*-
----
-
-default_language_version:
-    python: python3.11
-default_stages: [pre-commit]
-fail_fast: true
-repos:
-- repo: local
-  hooks:
-    - id: pylint
-      name: pylint
-      entry: env PYTHONPATH=/mnt/o/var/local/src/toxygen.git/toxygen toxcore_pylint.bash
-      language: system
-      types: [python]
-      args:
-        [
-          "--source-roots=/mnt/o/var/local/src/toxygen.git/toxygen",
-          "-rn", # Only display messages
-          "-sn", # Don't display the score
-          "--rcfile=/usr/local/etc/testforge/pylint.rc", # Link to your config file
-          "-E"
-        ]
diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index e79a8ae..0000000
--- a/.pylintrc
+++ /dev/null
@@ -1,4 +0,0 @@
-[pre-commit-hook]
-command=env PYTHONPATH=/mnt/o/var/local/src/toxygen.git/toxygen /usr/local/bin/toxcore_pylint.bash
-params= -E --exit-zero
-limit=8
diff --git a/.rsync b/.rsync
deleted file mode 100644
index e69de29..0000000
diff --git a/.rsync.sh b/.rsync.sh
deleted file mode 100644
index 06ad4db..0000000
--- a/.rsync.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-
-#find * -name \*.py | xargs grep -l '[ 	]*$' | xargs sed -i -e 's/[	 ]*$//'
-rsync "$@" -vaxL  --include \*.py \
-	--exclude Toxygen.egg-info --exclude build \
-	--exclude \*.pyc --exclude .pyl\* --exclude \*.so  --exclude \*~ \
-	--exclude __pycache__ --exclude \*.egg-info --exclude \*.new \
-	./ ../toxygen.git/|grep -v /$
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index a4011e1..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-language: python
-python:
-  - "3.5"
-  - "3.6"
-os:
-  - linux
-dist: trusty
-notifications:
-  email: false
-before_install:
-  - sudo apt-get update
-  - sudo apt-get install -y checkinstall build-essential
-  - sudo apt-get install portaudio19-dev
-  - sudo apt-get install libsecret-1-dev
-  - sudo apt-get install libconfig-dev libvpx-dev check -qq
-install:
-  - pip install sip
-  - pip install pyqt5
-  - pip install pyaudio
-  - pip install opencv-python
-  - pip install pydenticon
-before_script:
-# Opus
-  - wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz
-  - tar xzf opus-1.0.3.tar.gz
-  - cd opus-1.0.3
-  - ./configure
-  - make -j3
-  - sudo make install
-  - cd ..
-# Libsodium
-  - git clone git://github.com/jedisct1/libsodium.git
-  - cd libsodium
-  - git checkout tags/1.0.3
-  - ./autogen.sh
-  - ./configure && make -j$(nproc)
-  - sudo checkinstall --install --pkgname libsodium --pkgversion 1.0.0 --nodoc -y
-  - sudo ldconfig
-  - cd ..
-# Toxcore
-  - git clone https://github.com/ingvar1995/toxcore.git --branch=ngc_rebase
-  - cd toxcore
-  - mkdir _build && cd _build
-  - cmake ..
-  - make -j$(nproc)
-  - sudo make install
-  - echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
-  - sudo ldconfig
-  - cd ..
-  - cd ..
-script:
-  - py.test tests/travis.py
-  - py.test tests/tests.py
diff --git a/MANIFEST.in b/MANIFEST.in
index 89e57c6..851e0b6 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -10,10 +10,9 @@ include toxygen/smileys/animated/config.json
 include toxygen/smileys/starwars/*.gif
 include toxygen/smileys/starwars/*.png
 include toxygen/smileys/starwars/config.json
-include toxygen/smileys/ksk/*.png
-include toxygen/smileys/ksk/config.json
-include toxygen/styles/*.qss
+include toxygen/styles/style.qss
 include toxygen/translations/*.qm
 include toxygen/libs/libtox.dll
 include toxygen/libs/libsodium.a
-include toxygen/bootstrap/nodes.json
+include toxygen/libs/libtox64.dll
+include toxygen/libs/libsodium64.a
\ No newline at end of file
diff --git a/README.md b/README.md
index 8a15a09..dd86dc7 100644
--- a/README.md
+++ b/README.md
@@ -1,147 +1,60 @@
-# Toxygen
+# Toxygen 
+Toxygen is cross-platform [Tox](https://tox.chat/) client written in Python3
 
-Toxygen is powerful cross-platform [Tox](https://tox.chat/) client
-for Tox and IRC/weechat written in pure Python3.
+[![Release](https://img.shields.io/github/release/xveduk/toxygen.svg?style=flat)](https://github.com/xveduk/toxygen/releases/latest)
+[![Open issues](https://img.shields.io/github/issues/xveduk/toxygen.svg?style=flat)](https://github.com/xveduk/toxygen/issues)
+[![License](https://img.shields.io/badge/license-GPLv3-blue.svg?style=flat)](https://raw.githubusercontent.com/xveduk/toxygen/master/LICENSE.md)
 
-### [Install](/docs/install.md) - [Contribute](/docs/contributing.md) - [Plugins](/docs/plugins.md) - [Compile](/docs/compile.md) - [Contact](/docs/contact.md)
+### [Install](/docs/install.md) - [Contribute](/docs/contributing.md) - [Plugins](/docs/plugins.md)
 
-### Supported OS: Linux and Windows (only Linux is tested at the moment)
+### Supported OS:
+- Windows
+- Linux
 
-### Features:
+###Features
+- [x] 1v1 messages
+- [x] File transfers
+- [x] Audio
+- [x] Plugins support
+- [x] Chat history
+- [x] Emoticons
+- [x] Stickers
+- [x] Screenshots
+- [x] Name lookups (toxme.io support)
+- [x] Save file encryption
+- [x] Profile import and export
+- [x] Faux offline messaging
+- [x] Faux offline file transfers
+- [x] Inline images
+- [x] Message splitting
+- [x] Proxy support
+- [x] Avatars
+- [x] Multiprofile
+- [x] Multilingual
+- [x] Sound notifications
+- [x] Contact aliases
+- [x] Contact blocking
+- [x] Typing notifications
+- [x] Changing nospam
+- [x] File resuming
+- [x] Read receipts
+- [ ] Video
+- [ ] Desktop sharing
+- [ ] Group chats
 
-- PyQt5, PyQt6, and maybe PySide2, PySide6 via qtpy
-- IRC via weechat /relay
-- NGC groups
-- 1v1 messages
-- File transfers
-- Audio calls
-- Video calls
-- Group chats
-- Plugins support
-- Desktop sharing
-- Chat history
-- Emoticons
-- Stickers
-- Screenshots
-- Save file encryption
-- Profile import and export
-- Faux offline messaging
-- Faux offline file transfers
-- Inline images
-- Message splitting
-- Proxy support - runs over tor, without DNS leaks
-- Avatars
-- Multiprofile
-- Multilingual
-- Sound notifications
-- Contact aliases
-- Contact blocking
-- Typing notifications
-- Changing nospam
-- File resuming
-- Read receipts
-- uses gevent
+###Downloads
+[Releases](https://github.com/xveduk/toxygen/releases)
 
-### Screenshots
+[Download last stable version](https://github.com/xveduk/toxygen/archive/master.zip)
+
+[Download develop version](https://github.com/xveduk/toxygen/archive/develop.zip)
+
+###Screenshots
 *Toxygen on Ubuntu and Windows*
 ![Ubuntu](/docs/ubuntu.png)
 ![Windows](/docs/windows.png)
 
-Windows was working but is not currently being tested. AV is working
-but the video is garbled: we're unsure of naming the AV devices
-from the commandline. We need to get a working echobot that supports SOCKS5;
-we were working on one in https://git.plastiras.org/emdee/toxygen_wrapper
 
-## Forked
+###Docs
+[Check /docs/ for more info](/docs/)
 
-This hard-forked from the dead https://github.com/toxygen-project/toxygen
-```next_gen``` branch.
- 
-See ToDo.md to the current ToDo list.
-
-## IRC Weechat
-
-You can have a [weechat](https://github.com/weechat/qweechat)
-console so that you can have IRC and jabber in a window as well as Tox.
-There's a copy of qweechat in https://git.plastiras.org/emdee/qweechat
-that you must install first, which was backported to PyQt5 now to qtpy
-(PyQt5 PyQt6 and PySide2 and PySide6) and integrated into toxygen.
-Follow the normal instructions for adding a ```relay``` to
-[weechat](https://github.com/weechat/weechat)
-```
-/relay add weechat 9000
-/relay start weechat
-```
-or
-```
-weechat -r '/relay add weechat 9000;/relay start weechat'
-```
-and use the Plugins -> Weechat Console to start weechat under Toxygen.
-Then use the File/Connect menu item of the Console to connect to weechat.
-
-Weechat has a Jabber plugin to enable XMPP:
-```
-/python load jabber.el
-/help jabber
-```
-so you can have Tox, IRC and XMPP in the same application! See docs/ToxygenWeechat.md
-
-## Install
-
-To install read the requirements.txt and look at the comments; there
-are things that need installing by hand or decisions to be made
-on supported alternatives.
-
-https://git.plastiras.org/emdee/toxygen_wrapper needs installing as it is a
-dependency. Just download and install it from
-https://git.plastiras.org/emdee/toxygen_wrapper The same with
-https://git.plastiras.org/emdee/qweechat
-
-This is being ported to Qt6 using qtpy https://github.com/spyder-ide/qtpy
-It now runs on PyQt5 and PyQt6, and may run on PySide2 and PySide6 - YMMV.
-You will be able to choose between them by setting the environment variable
-```QT_API``` to one of: ```pyqt5 pyqt6 pyside2 pyside6```.
-It's currently tested mainly on PyQt5.
-
-To install it,  look in the Makefile for the install target and type
-```
-make install
-```
-You should set the PIP_EXE_MSYS and PYTHON_EXE_MSYS variables and it does
-```
-	${PIP_EXE_MSYS} --python ${PYTHON_EXE_MSYS} install \
-		--no-deps \
-		--target ${PREFIX}/lib/python${PYTHON_MINOR}/site-packages/ \
-		--upgrade .
-```
-and installs into PREFIX which is usually /usr/local
-
-## Updates
-
-Up-to-date code is on https://git.plastiras.org/emdee/toxygen
-
-Tox works over Tor, and the c-toxcore library can leak DNS requests
-due to a 6-year old known security issue:
-https://github.com/TokTok/c-toxcore/issues/469 but toxygen looksup
-addresses before calling c-toxcore. This also allows us to use onion
-addresses in the DHTnodes.json file. Still for anonymous communication
-we recommend having a TCP and UDP firewall in place.
-
-Although Tox works with multi-user group chat, there are no checks
-against impersonation of a screen nickname, so you may not be chatting
-with the person you think. For the Toxic client, the (closed) issue is:
-https://github.com/JFreegman/toxic/issues/622#issuecomment-1922116065
-Solving this might best be done with a solution to MultiDevice q.v.
-
-The Tox project does not follow semantic versioning of its main structures
-in C so the project may break the underlying ctypes wrapper at any time;
-it's not possible to use Tox version numbers to tell what the API will be.
-The last git version this code was tested with is
-``1623e3ee5c3a5837a92f959f289fcef18bfa9c959``` of Feb 12 10:06:37 2024.
-In which case you may need to go into the tox.py file in
-https://git.plastiras.org/emdee/toxygen_wrapper to fix it yourself.
-
-## MultiDevice
-
-Work on this project is suspended until the
-[MultiDevice](https://git.plastiras.org/emdee/tox_profile/wiki/MultiDevice-Announcements-POC) problem is solved. Fork me!
diff --git a/ToDo.md b/ToDo.md
deleted file mode 100644
index 9b4266f..0000000
--- a/ToDo.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Toxygen ToDo List
-
-## Bugs
-
-1. There is an agravating bug  where new messages are not put in the
-   current window, and a messages waiting indicator appears. You have
-   to focus out of the window and then back in the window. this may be
-   fixed already
-
-2. The tray icon is flaky and has been disabled - look in app.py
-   for bSHOW_TRAY
-
-## Fix history
-
-## Fix Audio
-
-The code is in there but it's not working. It looks like audio input
-is working but not output. The code is all in there; I may have broken
-it trying to wire up the ability to set the audio device from the
-command line.
-
-## Fix Video
-
-The code is in there but it's not working.  I may have broken it
-trying to wire up the ability to set the video device from the command
-line.
-
-## NGC Groups
-
-1. peer_id There has been a change of API on a field named
-   ```group.peer_id``` The code is broken in places because I have not
-   seen the path to change from the old API ro the new one.
-
-
-## Plugin system
-
-1. Needs better documentation and checking.
-
-2. There's something broken in the way some of them plug into Qt menus.
-
-3. Should the plugins be in toxygen or a separate repo?
-
-4. There needs to be a uniform way for plugins to wire into callbacks.
-
-## check toxygen_wrapper
-
-1. I've broken out toxygen_wrapper to be standalone,
-   https://git.plastiras.org/emdee/toxygen_wrapper but the tox.py
-   needs each call double checking.
-
-2. https://git.plastiras.org/emdee/toxygen_wrapper needs packaging
-   and making a dependency.
-
-## Migration
-
-Migrate PyQt5 to qtpy - done, but I'm not sure qtpy supports PyQt6.
-https://github.com/spyder-ide/qtpy/
-
-Maybe migrate gevent to asyncio, and migrate to
-[qasync](https://github.com/CabbageDevelopment/qasync)
-(see https://git.plastiras.org/emdee/phantompy ).
-
-(Also look at https://pypi.org/project/asyncio-gevent/ but it's dead).
-
-## Standards
-
-There's a standard for Tox clients that this has not been tested against:
-https://tox.gitbooks.io/tox-client-standard/content/general_requirements/general_requirements.html
-https://github.com/Tox/Tox-Client-Standard
-
diff --git a/_Bugs/segv.err b/_Bugs/segv.err
deleted file mode 100644
index 6f1294e..0000000
--- a/_Bugs/segv.err
+++ /dev/null
@@ -1,130 +0,0 @@
-0
-TRAC> network.c#1748:net_connect connecting socket 58 to 127.0.0.1:9050
-TRAC> Messenger.c#2709:do_messenger Friend num in DHT 2 != friend num in msger 14
-TRAC> Messenger.c#2723:do_messenger F[--: 0] D3385007C28852C5398393E3338E6AABE5F86EF249BF724E7404233207D4D927
-TRAC> Messenger.c#2723:do_messenger F[--: 1] 98984E104B8A97CC43AF03A27BE159AC1F4CF35FADCC03D6CD5F8D67B5942A56
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 185.87.49.189:3389 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 185.87.49.189:3389 (0: OK) | 010001b95731bd0d...3d
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 37.221.66.161:443 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 37.221.66.161:443 (0: OK) | 01000125dd42a101...bb
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 172.93.52.70:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 139.162.110.188:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 37.59.63.150:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 130.133.110.14:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 37.97.185.116:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 85.143.221.42:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 104.244.74.69:38445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 49.12.229.145:3389 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 168.119.209.10:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 81.169.136.229:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 91.219.59.156:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 46.101.197.175:3389 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 198.199.98.108:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 130.133.110.14:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 49.12.229.145:3389 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 188.225.9.167:33445 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 5.19.249.240:38296 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>   3= 94.156.35.247:3389 (0: OK) | 0000000000000000...00
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 2 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 172.93.52.70:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 172.93.52.70:33445 (0: OK) | 010001ac5d344682...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 139.162.110.188:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 139.162.110.188:33445 (0: OK) | 0100018ba26ebc82...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 37.59.63.150:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 37.59.63.150:33445 (0: OK) | 010001253b3f9682...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 130.133.110.14:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 130.133.110.14:33445 (0: OK) | 01000182856e0e82...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 37.97.185.116:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 37.97.185.116:33445 (0: OK) | 0100012561b97482...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 85.143.221.42:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 85.143.221.42:33445 (0: OK) | 010001558fdd2a82...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 104.244.74.69:38445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 104.244.74.69:38445 (0: OK) | 01000168f44a4596...2d
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 49.12.229.145:3389 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 49.12.229.145:3389 (0: OK) | 010001310ce5910d...3d
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 168.119.209.10:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 168.119.209.10:33445 (0: OK) | 010001a877d10a82...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 81.169.136.229:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 81.169.136.229:33445 (0: OK) | 01000151a988e582...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 91.219.59.156:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 91.219.59.156:33445 (0: OK) | 0100015bdb3b9c82...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 46.101.197.175:3389 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 46.101.197.175:3389 (0: OK) | 0100012e65c5af0d...3d
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 198.199.98.108:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 198.199.98.108:33445 (0: OK) | 010001c6c7626c82...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 130.133.110.14:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 130.133.110.14:33445 (0: OK) | 01000182856e0e82...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 49.12.229.145:3389 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 49.12.229.145:3389 (0: OK) | 010001310ce5910d...3d
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 188.225.9.167:33445 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 188.225.9.167:33445 (0: OK) | 010001bce109a782...a5
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 5.19.249.240:38296 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 5.19.249.240:38296 (0: OK) | 0100010513f9f095...98
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] =>T   2= 94.156.35.247:3389 (0: OK) | 0000000000000000...00
-TRAC> network.c#789:loglogdata [05 = <unknown>           ] T=>  10= 94.156.35.247:3389 (0: OK) | 0100015e9c23f70d...3d
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-app.contacts.contacts_manager INFO update_groups_numbers len(groups)={len(groups)}
-
-Thread 76 "ToxIterateThrea" received signal SIGSEGV, Segmentation fault.
-[Switching to Thread 0x7ffedcb6b640 (LWP 2950427)]
diff --git a/_Bugs/tox.abilinski.com.ping b/_Bugs/tox.abilinski.com.ping
deleted file mode 100644
index 920f606..0000000
--- a/_Bugs/tox.abilinski.com.ping
+++ /dev/null
@@ -1,11 +0,0 @@
- ping tox.abilinski.com
-ping: socket: Address family not supported by protocol
-PING tox.abilinski.com (172.103.226.229) 56(84) bytes of data.
-64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=1 ttl=48 time=86.6 ms
-64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=2 ttl=48 time=83.1 ms
-64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=3 ttl=48 time=82.9 ms
-64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=4 ttl=48 time=83.4 ms
-64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=5 ttl=48 time=102 ms
-64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=6 ttl=48 time=87.4 ms
-64 bytes from 172.103.226.229.cable.tpia.cipherkey.com (172.103.226.229): icmp_seq=7 ttl=48 time=84.9 ms
-^C
diff --git a/docs/ToxygenWeechat.md b/docs/ToxygenWeechat.md
deleted file mode 100644
index 1694669..0000000
--- a/docs/ToxygenWeechat.md
+++ /dev/null
@@ -1,171 +0,0 @@
-## Toxygen Weechat
-
-You can have a [weechat](https://github.com/weechat/qweechat)
-console so that you can have IRC and jabber in a window as well as Tox.
-There's a copy of qweechat in ```thirdparty/qweechat``` backported to
-PyQt5 and integrated into toxygen. Follow the normal instructions for
-adding a ```relay``` to [weechat](https://github.com/weechat/weechat)
-```
-/relay add ipv4.ssl.weechat 9000
-/relay start ipv4.ssl.weechat
-```
-or
-```
-/set relay.network.ipv6 off
-/set relay.network.password password
-/relay add weechat 9000
-/relay start weechat
-```
-and use the Plugins/Weechat Console to start weechat under Toxygen.
-Then use the File/Connect menu item of the Console to connect to weechat.
-
-Weechat has a Jabber plugin to enable XMPP:
-```
-/python load jabber.el
-/help jabber
-```
-so you can have Tox, IRC and XMPP in the same application!
-
-### Creating servers for IRC over Tor
-
-Create a proxy called tor
-```
-/proxy add tor socks5 127.0.0.1 9050
-```
-
-It should now show up in the list of proxies.
-```
-/proxy list
-```
-
-```
-/nick NickName
-```
-
-## TLS certificates
-
-[Create a Self-signed Certificate](https://www.oftc.net/NickServ/CertFP/)
-
-Choose a NickName you will identify as.
-
-Create a directory for your certificates ~/.config/weechat/ssl/ 
-and make a subdirectory for each server ~/.config/weechat/ssl/irc.oftc.net/ 
-
-Change to the server directory and use openssl to make a keypair and answer the questions:
-```
-openssl req -nodes -newkey rsa:2048 -keyout NickName.key -x509 -days 3650 -out NickName.cer
-chmod 400 NickName.key
-```
-We now combine certificate and key to a single file NickName.pem 
-```
-cat NickName.cer NickName.key > NickName.pem
-chmod 400 NickName.pem
-```
-
-Do this for each server you want to connect to, or just use one for all of them.
-
-### Libera TokTok channel
-
-The main discussion forum for Tox is the #TokTok channel on libera.
-
-https://mox.sh/sysadmin/secure-irc-connection-to-freenode-with-tor-and-weechat/
-We have to create an account without Tor, this is a requirement to use TOR:
-Connect to irc.libera.chat without Tor and register 
-```
-/msg NickServ identify NickName password
-/msg NickServ REGISTER mypassword mycoolemail@example.com
-/msg NickServ SET PRIVATE ON
-```
-You'll get an email with a registration code.
-Confirm registration after getting the mail with the code:
-```
-/msg NickServ VERIFY REGISTER NickName code1235678
-```
-
-Libera has an onion server so we can map an address in tor. Add this
-to your /etc/tor/torrc
-```
-MapAddress palladium.libera.chat libera75jm6of4wxpxt4aynol3xjmbtxgfyjpu34ss4d7r7q2v5zrpyd.onion
-```
-Or without the MapAddress just use
-libera75jm6of4wxpxt4aynol3xjmbtxgfyjpu34ss4d7r7q2v5zrpyd.onion
-as the server address below, but set tls_verify to off.
-
-Define the server in weechat
-https://www.weechat.org/files/doc/stable/weechat_user.en.html#irc_sasl_authentication
-```
-/server remove libera
-/server add libera palladium.libera.chat/6697 -tls -tls_verify
-/set irc.server.libera.ipv6 off
-/set irc.server.libera.proxy tor
-/set irc.server.libera.username NickName
-/set irc.server.libera.password password
-/set irc.server.libera.nicks NickName
-/set irc.server.libera.tls on
-/set irc.server.libera.tls_cert "${weechat_config_dir}/ssl/libera.chat/NickName.pem"
-```
-
-```
-/set irc.server.libera.sasl_mechanism ecdsa-nist256p-challenge
-/set irc.server.libera.sasl_username "NickName"
-/set irc.server.libera.sasl_key "${weechat_config_dir}/ssl/libera.chat/NickName.pem"
-```
-
-Disconnect and connect back to the server. 
-```
-/disconnect libera
-/connect libera
-```
-
-/msg nickserv identify password NickName
-
-
-### oftc.net
-
-To use oftc.net over tor, you need to authenticate by SSL certificates.
-
-
-Define the server in weechat
-```
-/server remove irc.oftc.net
-/server add OFTC irc.oftc.net/6697  -tls -tls_verify
-/set irc.server.OFTC.ipv6 off
-/set irc.server.OFTC.proxy tor
-/set irc.server.OFTC.username NickName
-/set irc.server.OFTC.nicks NickName
-/set irc.server.OFTC.tls on
-/set irc.server.OFTC.tls_cert "${weechat_config_dir}/ssl/irc.oftc.chat/NickName.pem"
-
-# Disconnect and connect back to the server. 
-/disconnect OFTC
-/connect OFTC
-``` 
-You must be identified in order to validate using certs
-```
-/msg nickserv identify password NickName
-```
-To allow NickServ to identify you based on this certificate you need
-to associate the certificate fingerprint with your nick. To do this
-issue the command cert add to Nickserv (try /msg nickserv helpcert).
-```
-/msg nickserv cert add
-```
-
-### Privacy
-
-[Add somes settings bellow to weechat](https://szorfein.github.io/weechat/tor/configure-weechat/).
-Detail from [faq](https://weechat.org/files/doc/weechat_faq.en.html#security).
-
-```
-/set irc.server_default.msg_part ""
-/set irc.server_default.msg_quit ""
-/set irc.ctcp.clientinfo ""
-/set irc.ctcp.finger ""
-/set irc.ctcp.source ""
-/set irc.ctcp.time ""
-/set irc.ctcp.userinfo ""
-/set irc.ctcp.version ""
-/set irc.ctcp.ping ""
-/plugin unload xfer
-/set weechat.plugin.autoload "*,!xfer"
-```
diff --git a/docs/compile.md b/docs/compile.md
index b4f6810..6b6ab73 100644
--- a/docs/compile.md
+++ b/docs/compile.md
@@ -1,19 +1,10 @@
-# Compile Toxygen
+#Compile Toxygen
 
 You can compile Toxygen using [PyInstaller](http://www.pyinstaller.org/)
 
-Use Dockerfile and build script from `build` directory:
+Install PyInstaller: 
+``pip3 install pyinstaller``
 
-1. Build image:
-```
-docker build -t toxygen .
-```
+``pyinstaller --windowed --icon images/icon.ico main.py``
 
-2. Run container:
-```
-docker run -it toxygen bash
-```
-
-3. Execute `build.sh` script:
-
-```./build.sh```
+Don't forget to copy /images/, /sounds/, /translations/, /styles/, /smileys/, /stickers/ (and /libs/libtox.dll on Windows) to /dist/main/
diff --git a/docs/contact.md b/docs/contact.md
deleted file mode 100644
index 5eb2fa6..0000000
--- a/docs/contact.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Contact us:
-
-1) https://git.plastiras.org/emdee/toxygen/issues
-
-2) Use Toxygen Tox Group (NGC) - 
-ID: 59D68B2709E81A679CF91416CB0E3692851C6CFCABEFF98B7131E3805A6D75FA
diff --git a/docs/contributing.md b/docs/contributing.md
index b2cebf4..8b1e7fa 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -1,25 +1,20 @@
-# Issues
+#Issues
 
 Help us find all bugs in Toxygen! Please provide following info:
 
 - OS
 - Toxygen version
-- Toxygen executable info - python executable (.py), precompiled binary, from package etc.
+- Toxygen executable info - .py or precompiled binary
 - Steps to reproduce the bug
 
-Want to see new feature in Toxygen? 
-[Ask for it!](https://git.plastiras.org/emdee/toxygen/issues)
+Want to see new feature in Toxygen? [Ask for it!](https://github.com/xveduk/toxygen/issues)
 
-# Pull requests
+#Pull requests
 
-Developer? Feel free to open pull request. Our dev team is small so we glad to get help.
-Don't know what to do? Improve UI, fix 
-[issues](https://git.plastiras.org/emdee/toxygen/issues) 
-or implement features from our TODO list.
+Developer? Feel free to open pull request. Our dev team is small so we glad to get help. 
+Don't know what to do? Improve UI, fix [issues](https://github.com/xveduk/toxygen/issues) or implement features from our TODO list.
 You can find our TODO's in code, issues list and [here](/README.md). Also you can implement [plugins](/docs/plugins.md) for Toxygen.
 
-Note that we have a lot of branches for different purposes. Master branch is for stable versions (releases) only, so I recommend to open PR's to develop branch. Development of next Toxygen version usually goes there. Other branches used for implementing different tasks such as file transfers improvements or audio calls implementation etc.
+#Translations
 
-# Translations
-
-Help us translate Toxygen! Translation can be created using pylupdate (``pylupdate5 toxygen.pro``) and QT Linguist.
+Help us translate Toxygen! Translation can be created using pyside-lupdate (``pyside-lupdate toxygen.pro``) and QT Linguist.
\ No newline at end of file
diff --git a/docs/install.md b/docs/install.md
index 7d2b773..6142c9c 100644
--- a/docs/install.md
+++ b/docs/install.md
@@ -1,51 +1,59 @@
 # How to install Toxygen
 
-### Linux
+## Use precompiled binary:
+[Check our releases page](https://github.com/xveduk/toxygen/releases)
 
-1. Install [c-toxcore](https://github.com/TokTok/c-toxcore/)
+##Using pip3
+
+### Windows  (32-bit interpreter)
+
+``pip3.4 install toxygen``
+Run app using ``toxygen`` command.
+
+##Linux
+
+1. Install [toxcore](https://github.com/irungentoo/toxcore/blob/master/INSTALL.md) with toxav support in your system (install in /usr/lib/)
 2. Install PortAudio: 
 ``sudo apt-get install portaudio19-dev``
-3. For 32-bit Linux install PyQt5: ``sudo apt-get install python3-pyqt5``
-4. Install [OpenCV](http://docs.opencv.org/trunk/d7/d9f/tutorial_linux_install.html) or via ``sudo pip3 install opencv-python``
-5. Install [toxygen](https://git.plastiras.org/emdee/toxygen/)
-6. Run toxygen using ``toxygen`` command.
+3. Install toxygen: 
+``sudo pip3.4 install toxygen``
+4 Run toxygen using ``toxygen`` command.
 
 ## From source code (recommended for developers)
 
 ### Windows
 
-Note: 32-bit Python isn't supported due to bug with videocalls. It is strictly recommended to use 64-bit Python.
+1. [Download and install latest Python 3.4](https://www.python.org/downloads/windows/)
+2. [Install PySide](https://pypi.python.org/pypi/PySide/1.2.4) (recommended) or [PyQt4](https://riverbankcomputing.com/software/pyqt/download)
+3. Install PyAudio: ``pip3.4 install pyaudio``
+4. [Download toxygen](https://github.com/xveduk/toxygen/archive/master.zip)
+5. Unpack archive  
+6. Download latest libtox.dll build, download latest libsodium.a build, put it into \src\libs\
+7. Run \src\main.py.
+
+[libtox.dll for 32-bit Python](https://build.tox.chat/view/libtoxcore/job/libtoxcore_build_windows_x86_shared_release/lastSuccessfulBuild/artifact/libtoxcore_build_windows_x86_shared_release.zip)
+
+[libtox.dll for 64-bit Python](https://build.tox.chat/view/libtoxcore/job/libtoxcore_build_windows_x86-64_shared_release/lastSuccessfulBuild/artifact/libtoxcore_build_windows_x86-64_shared_release.zip)
+
+[libsodium.a for 32-bit Python](https://build.tox.chat/view/libsodium/job/libsodium_build_windows_x86_static_release/lastSuccessfulBuild/artifact/libsodium_build_windows_x86_static_release.zip)
+
+[libsodium.a for 64-bit Python](https://build.tox.chat/view/libsodium/job/libsodium_build_windows_x86-64_static_release/lastSuccessfulBuild/artifact/libsodium_build_windows_x86-64_static_release.zip)
 
-1. [Download and install latest Python 3 64-bit](https://www.python.org/downloads/windows/)
-2. Install PyQt5: ``pip install pyqt5``
-3. Install PyAudio: ``pip install pyaudio``
-4. Install numpy: ``pip install numpy``
-5. Install OpenCV: ``pip install opencv-python``
-6. git clone --depth=1 https://git.plastiras.org/emdee/toxygen/
-7. I don't know
-8. Download latest libtox.dll build, download latest libsodium.a build, put it into \toxygen\libs\
-9. Run \toxygen\main.py.
 
 ### Linux
 
-1. Install latest Python3: 
-``sudo apt-get install python3``
-2. Install PyQt5: ``sudo apt-get install python3-pyqt5``
-3. Install [toxcore](https://github.com/TokTok/c-toxcore) with toxav support)
-4. Install PyAudio: ``sudo apt-get install portaudio19-dev python3-pyaudio`` (or ``sudo pip3 install pyaudio``)
-5. Install toxygen_wrapper https://git.plastiras.org/emdee/toxygen_wrapper
-6. Install the rest of the requirements: ``sudo pip3 install -m requirements.txt``
-7. git clone --depth=1 [toxygen](https://git.plastiras.org/emdee/toxygen/)
-8. Look in the Makefile for the install target and type
-``
-make install
-``
-You should set the PIP_EXE_MSYS and PYTHON_EXE_MSYS variables and it does
-``
-	${PIP_EXE_MSYS} --python ${PYTHON_EXE_MSYS} install \
-		--target ${PREFIX}/lib/python${PYTHON_MINOR}/site-packages/ \
-		--upgrade .
-``
-9. Run app:
-``python3 ${PREFIX}/lib/python${PYTHON_MINOR}/site-packages/bin/toxygen``
+Dependencies:
 
+1. Install latest Python3.4: 
+``sudo apt-get install python3``
+2. [Install PySide](https://wiki.qt.io/PySide_Binaries_Linux) (recommended), using terminal - ``sudo apt-get install python3-pyside``, or install [PyQt4](https://riverbankcomputing.com/software/pyqt/download).
+3. Install [toxcore](https://github.com/irungentoo/toxcore/blob/master/INSTALL.md) with toxav support in your system (install in /usr/lib/)
+4. Install PyAudio: 
+``sudo apt-get install portaudio19-dev`` and ``sudo apt-get install python3-pyaudio``
+5. [Download toxygen](https://github.com/xveduk/toxygen/archive/master.zip)
+6. Unpack archive 
+7. Run app:
+``python3.4 main.py``
+
+## Compile Toxygen
+Check [compile.md](/docs/compile.md) for more info
diff --git a/docs/plugin_api.md b/docs/plugin_api.md
index 32a27f8..1c7971c 100644
--- a/docs/plugin_api.md
+++ b/docs/plugin_api.md
@@ -1,6 +1,6 @@
-# Plugins API
+#Plugins API
 
-In Toxygen plugin is single python module (.py file) and directory with data associated with it. 
+In Toxygen plugin is single python (supported Python 3.0 - 3.4) module (.py file) and directory with data associated with it. 
 Every module must contain one class derived from PluginSuperClass defined in [plugin_super_class.py](/src/plugins/plugin_super_class.py). Instance of this class will be created by PluginLoader class (defined in [plugin_support.py](/src/plugin_support.py) ). This class can enable/disable plugins and send data to it. 
 
 Every plugin has its own full name and unique short name (1-5 symbols). Main app can get it using special methods. 
@@ -12,13 +12,12 @@ All plugin's data should be stored in following structure:
 |---plugin_short_name.py
 |---/plugin_short_name/
 	|---settings.json
-	|---readme.txt
 	|---logs.txt
 	|---other_files
 ```
 
 Plugin MUST override:
--  __init__ with params: tox (Tox instance), profile (Profile instance), settings (Settings instance), encrypt_save (ToxES instance). Call super().__init__ with params plugin_full_name, plugin_short_name, tox, profile, settings, encrypt_save.
+-  __init__ with params: tox (Tox instance), profile (Profile instance), settings (Settings instance), encrypt_save (ToxEncryptSave instance). Call super().__init__ with params plugin_full_name, plugin_short_name, tox, profile, settings, encrypt_save.
 
 Plugin can override following methods:
 - get_description - this method should return plugin description. 
@@ -45,13 +44,13 @@ Import statement will not work in case you import module that wasn't previously
 
 About GUI:
 
-GUI is available via PyQt5. Plugin can have no GUI at all.
+It's strictly recommended to support both PySide and PyQt4 in GUI. Plugin can have no GUI at all.
 
 Exceptions:
 
 Plugin's methods MUST NOT raise exceptions.
 
-# Examples
+#Examples
 
-You can find examples in [official repo](https://git.plastiras.org/emdee/toxygen_plugins)
+You can find examples in [official repo](https://github.com/ingvar1995/toxygen_plugins)
 
diff --git a/docs/plugins.md b/docs/plugins.md
index 98fbac8..ee73415 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -1,22 +1,22 @@
-# Plugins
+#Plugins
 
-Toxygen is the first [Tox](https://tox.chat/) client with plugins support. Plugin is Python 3.5 - 3.6 module (.py file) and directory with plugin's data which provide some additional functionality.
+Toxygen is the first [Tox](https://tox.chat/) client with plugins support. Plugin is Python 3.4 module (.py file) and directory with plugin's data which provide some additional functionality. 
 
-# How to write plugin
+#How to write plugin
 
 Check [Plugin API](/docs/plugin_api.md) for more info
 
-# How to install plugin
+#How to install plugin
 
 Toxygen comes without preinstalled plugins.
 
-1. Put plugin and directory with its data into /src/plugins/ or import it via GUI (In menu: Plugins => Import plugin)
-2. Restart Toxygen or choose Plugins => Reload plugins in menu.
+1. Put plugin and directory with its data into /src/plugins/ or import it via GUI (In menu: Plugins -> Import plugin)
+2. Restart Toxygen
 
-## Note: /src/plugins/ should contain plugin_super_class.py and __init__.py
+##Note: /src/plugins/ should contain plugin_super_class.py and __init__.py
 
-# Plugins list
+#Plugins list
 
 WARNING: It is unsecure to install plugin not from this list!
 
-[Main repo](https://github.com/toxygen-project/toxygen_plugins)
\ No newline at end of file
+[Main repo](https://github.com/ingvar1995/toxygen_plugins)
\ No newline at end of file
diff --git a/docs/smileys_and_stickers.md b/docs/smileys_and_stickers.md
index 8705ba8..53a360b 100644
--- a/docs/smileys_and_stickers.md
+++ b/docs/smileys_and_stickers.md
@@ -1,4 +1,4 @@
-# Smileys
+#Smileys
 
 Toxygen support smileys. Smiley is small picture which replaces some symbol or combination of symbols. If you want to create your own smiley pack, create directory in src/smileys/. This directory must contain images with smileys and config.json. Example of config.json:
 
@@ -6,8 +6,8 @@ Toxygen support smileys. Smiley is small picture which replaces some symbol or c
 
 Animated smileys (.gif) are supported too.
 
-# Stickers
+#Stickers
 
-Sticker is inline image. If you want to create your own sticker pack, create directory in src/stickers/ and place your stickers there.
+Sticker is inline image. If you want to create your own smiley pack, create directory in src/stickers/ and place your stickers there.
 
-Users can import smileys and stickers using menu: Settings -> Interface
+Users can import plugins and stickers packs using menu: Settings -> Interface
\ No newline at end of file
diff --git a/docs/todo.md b/docs/todo.md
deleted file mode 100644
index 9b4266f..0000000
--- a/docs/todo.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Toxygen ToDo List
-
-## Bugs
-
-1. There is an agravating bug  where new messages are not put in the
-   current window, and a messages waiting indicator appears. You have
-   to focus out of the window and then back in the window. this may be
-   fixed already
-
-2. The tray icon is flaky and has been disabled - look in app.py
-   for bSHOW_TRAY
-
-## Fix history
-
-## Fix Audio
-
-The code is in there but it's not working. It looks like audio input
-is working but not output. The code is all in there; I may have broken
-it trying to wire up the ability to set the audio device from the
-command line.
-
-## Fix Video
-
-The code is in there but it's not working.  I may have broken it
-trying to wire up the ability to set the video device from the command
-line.
-
-## NGC Groups
-
-1. peer_id There has been a change of API on a field named
-   ```group.peer_id``` The code is broken in places because I have not
-   seen the path to change from the old API ro the new one.
-
-
-## Plugin system
-
-1. Needs better documentation and checking.
-
-2. There's something broken in the way some of them plug into Qt menus.
-
-3. Should the plugins be in toxygen or a separate repo?
-
-4. There needs to be a uniform way for plugins to wire into callbacks.
-
-## check toxygen_wrapper
-
-1. I've broken out toxygen_wrapper to be standalone,
-   https://git.plastiras.org/emdee/toxygen_wrapper but the tox.py
-   needs each call double checking.
-
-2. https://git.plastiras.org/emdee/toxygen_wrapper needs packaging
-   and making a dependency.
-
-## Migration
-
-Migrate PyQt5 to qtpy - done, but I'm not sure qtpy supports PyQt6.
-https://github.com/spyder-ide/qtpy/
-
-Maybe migrate gevent to asyncio, and migrate to
-[qasync](https://github.com/CabbageDevelopment/qasync)
-(see https://git.plastiras.org/emdee/phantompy ).
-
-(Also look at https://pypi.org/project/asyncio-gevent/ but it's dead).
-
-## Standards
-
-There's a standard for Tox clients that this has not been tested against:
-https://tox.gitbooks.io/tox-client-standard/content/general_requirements/general_requirements.html
-https://github.com/Tox/Tox-Client-Standard
-
diff --git a/docs/ubuntu.png b/docs/ubuntu.png
old mode 100644
new mode 100755
index 67951a5..cd80444
Binary files a/docs/ubuntu.png and b/docs/ubuntu.png differ
diff --git a/docs/windows.png b/docs/windows.png
old mode 100644
new mode 100755
index f13f4c0..d4ed323
Binary files a/docs/windows.png and b/docs/windows.png differ
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 37d424a..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,56 +0,0 @@
-[project]
-name = "toxygen"
-description = "examples of using stem"
-authors = [{ name = "emdee", email = "emdee@spm.plastiras.org" } ]
-requires-python = ">=3.7"
-keywords = ["stem", "python3", "tox"]
-classifiers = [
-  # How mature is this project? Common values are
-  #   3 - Alpha
-  #   4 - Beta
-  #   5 - Production/Stable
-  "Development Status :: 4 - Beta",
-
-  # Indicate who your project is intended for
-  "Intended Audience :: Developers",
-
-  # Specify the Python versions you support here.
-  "Programming Language :: Python :: 3",
-  "License :: OSI Approved",
-  "Operating System :: POSIX :: BSD :: FreeBSD",
-  "Operating System :: POSIX :: Linux",
-  "Programming Language :: Python :: 3 :: Only",
-  "Programming Language :: Python :: 3.6",
-  "Programming Language :: Python :: 3.7",
-  "Programming Language :: Python :: 3.8",
-  "Programming Language :: Python :: 3.9",
-  "Programming Language :: Python :: 3.10",
-  "Programming Language :: Python :: 3.11",
-  "Programming Language :: Python :: Implementation :: CPython",
-]
-#
-dynamic = ["version", "readme", "dependencies"] # cannot be dynamic ['license']
-
-[project.gui-scripts]
-toxygen = "toxygen.__main__:main"
-
-[project.optional-dependencies]
-weechat = ["weechat"]
-
-#[project.license]
-#file = "LICENSE.md"
-
-[project.urls]
-repository = "https://git.plastiras.org/emdee/toxygen"
-
-[build-system]
-requires = ["setuptools >= 61.0"]
-build-backend = "setuptools.build_meta"
-
-[tool.setuptools.dynamic]
-version = {attr = "toxygen.app.__version__"} 
-readme = {file = ["README.md", "ToDo.txt"]}
-dependencies = {file = ["requirements.txt"]}
-
-[tool.setuptools]
-packages = ["toxygen"]
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 216e1a4..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-# the versions are the current ones tested - may work with earlier versions
-# choose one of PyQt5 PyQt6 PySide2 PySide6
-# for now PyQt5 and PyQt6 is working, and most of the testing is PyQt5
-# usually this is installed by your OS package manager and pip may not
-# detect the right version, so we leave these commented
-# PyQt5 >= 5.15.10
-# this is not on pypi yet but is required - get it from
-# https://git.plastiras.org/emdee/toxygen_wrapper
-# toxygen_wrapper == 1.0.0
-QtPy >= 2.4.1
-PyAudio >= 0.2.13
-numpy >= 1.26.1
-opencv_python >= 4.8.0
-pillow >= 10.2.0
-gevent >= 23.9.1
-pydenticon >= 0.3.1
-greenlet >= 2.0.2
-sounddevice >= 0.3.15
-# this is optional
-coloredlogs >= 15.0.1
-# this is optional
-# qtconsole >= 5.4.3
-# this is not on pypi yet but is optional for qweechat - get it from
-# https://git.plastiras.org/emdee/qweechat
-# qweechat_wrapper == 0.0.1
-
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index d7ffc22..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,54 +0,0 @@
-[metadata]
-classifiers =
-    License :: OSI Approved
-    License :: OSI Approved :: BSD 1-clause
-    Intended Audience :: Web Developers
-    Operating System :: Microsoft :: Windows
-    Operating System :: POSIX :: BSD :: FreeBSD
-    Operating System :: POSIX :: Linux
-    Programming Language :: Python :: 3 :: Only
-    Programming Language :: Python :: 3.6
-    Programming Language :: Python :: 3.7
-    Programming Language :: Python :: 3.8
-    Programming Language :: Python :: 3.9
-    Programming Language :: Python :: 3.10
-    Programming Language :: Python :: 3.11
-    Programming Language :: Python :: Implementation :: CPython
-
-[options]
-zip_safe = false
-python_requires = ~=3.7
-include_package_data =
-    "*" = ["*.ui", "*.txt", "*.png", "*.ico", "*.gif", "*.wav"]
-
-[options.entry_points]
-console_scripts =
-    toxygen = toxygen.__main__:iMain
-
-[easy_install]
-zip_ok = false
-
-[flake8]
-jobs = 1
-max-line-length = 88
-ignore =
-    E111
-    E114
-    E128
-    E225
-    E261
-    E302
-    E305
-    E402
-    E501
-    E502
-    E541
-    E701
-    E702
-    E704
-    E722
-    E741
-    F508
-    F541
-    W503
-    W601
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..670538b
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,51 @@
+from setuptools import setup
+from setuptools.command.install import install
+from platform import system
+from subprocess import call
+from toxygen.util import program_version
+
+
+version = program_version + '.0'
+
+MODULES = ['PyAudio']
+
+if system() == 'Windows':
+    MODULES.append('PySide')
+
+
+class InstallScript(install):
+    """This class configures Toxygen after installation"""
+
+    def run(self):
+        install.run(self)
+        OS = system()
+        if OS == 'Windows':
+            call(["toxygen", "--configure"])
+        elif OS == 'Linux':
+            call(["toxygen", "--clean"])
+
+setup(name='Toxygen',
+      version=version,
+      description='Toxygen - Tox client',
+      long_description='Toxygen is powerful Tox client written in Python3',
+      url='https://github.com/xveduk/toxygen/',
+      keywords='toxygen tox messenger',
+      author='Ingvar',
+      maintainer='Ingvar',
+      license='GPL3',
+      packages=['toxygen', 'toxygen.plugins', 'toxygen.styles'],
+      install_requires=MODULES,
+      include_package_data=True,
+      classifiers=[
+          'Programming Language :: Python :: 3 :: Only',
+          'Programming Language :: Python :: 3.2',
+          'Programming Language :: Python :: 3.3',
+          'Programming Language :: Python :: 3.4',
+      ],
+      entry_points={
+          'console_scripts': ['toxygen=toxygen.main:main'],
+      },
+      cmdclass={
+          'install': InstallScript,
+      },
+      )
diff --git a/setup.py.dst b/setup.py.dst
deleted file mode 100644
index a3f543d..0000000
--- a/setup.py.dst
+++ /dev/null
@@ -1,53 +0,0 @@
-import sys
-import os
-from setuptools import setup
-from setuptools.command.install import install
-
-version = '1.0.0'
-
-MODULES = open('requirements.txt', 'rt').readlines()
-
-def get_packages():
-    directory = os.path.join(os.path.dirname(__file__), 'tox_wrapper')
-    for root, dirs, files in os.walk(directory):
-        packages = map(lambda d: 'toxygen.' + d, dirs)
-        packages = ['toxygen'] + list(packages)
-        return packages
-
-class InstallScript(install):
-    """This class configures Toxygen after installation"""
-
-    def run(self):
-        install.run(self)
-
-setup(name='Toxygen',
-      version=version,
-      description='Toxygen - Tox client',
-      long_description='Toxygen is powerful Tox client written in Python3',
-      url='https://git.plastiras.org/emdee/toxygen/',
-      keywords='toxygen Tox messenger',
-      author='Ingvar',
-      maintainer='',
-      license='GPL3',
-      packages=get_packages(),
-      install_requires=MODULES,
-      include_package_data=True,
-      classifiers=[
-          'Programming Language :: Python :: 3 :: Only',
-    "Programming Language :: Python :: 3.6",
-    "Programming Language :: Python :: 3.7",
-    "Programming Language :: Python :: 3.8",
-    "Programming Language :: Python :: 3.9",
-    "Programming Language :: Python :: 3.10",
-    "Programming Language :: Python :: 3.11",
-          'Programming Language :: Python :: 3.11',
-      ],
-      entry_points={
-          'console_scripts': ['toxygen=toxygen.main:main']
-      },
-      package_data={"": ["*.ui"],},
-      cmdclass={
-          'install': InstallScript,
-      },
-      zip_safe=False
-      )
diff --git a/tests/tests.py b/tests/tests.py
index e3c9b6b..c9f5ca6 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -1,18 +1,70 @@
-from toxygen.middleware.tox_factory import *
+from toxygen.bootstrap import node_generator
+from toxygen.profile import *
+from toxygen.settings import ProfileHelper
+from toxygen.tox_dns import tox_dns
+import toxygen.toxencryptsave as encr
 
 
-# TODO: add new tests
+class TestProfile:
+
+    def test_search(self):
+        arr = ProfileHelper.find_profiles()
+        assert len(arr) >= 2
+
+    def test_open(self):
+        data = ProfileHelper(Settings.get_default_path(), 'alice').open_profile()
+        assert data
+
 
 class TestTox:
 
+    def test_loading(self):
+        data = ProfileHelper(Settings.get_default_path(), 'alice').open_profile()
+        settings = Settings.get_default_settings()
+        tox = tox_factory(data, settings)
+        for data in node_generator():
+            tox.bootstrap(*data)
+        del tox
+
     def test_creation(self):
-        name = 'Toxygen User'
-        status_message = 'Toxing on Toxygen'
+        name = b'Toxygen User'
+        status_message = b'Toxing on Toxygen'
         tox = tox_factory()
         tox.self_set_name(name)
         tox.self_set_status_message(status_message)
         data = tox.get_savedata()
         del tox
         tox = tox_factory(data)
-        assert tox.self_get_name() == name
-        assert tox.self_get_status_message() == status_message
+        assert tox.self_get_name() == str(name, 'utf-8')
+        assert tox.self_get_status_message() == str(status_message, 'utf-8')
+
+    def test_friend_list(self):
+        data = ProfileHelper(Settings.get_default_path(), 'bob').open_profile()
+        settings = Settings.get_default_settings()
+        tox = tox_factory(data, settings)
+        s = tox.self_get_friend_list()
+        size = tox.self_get_friend_list_size()
+        assert size <= 2
+        assert len(s) <= 2
+        del tox
+
+
+class TestDNS:
+
+    def test_dns(self):
+        bot_id = '56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE51855D34D34D37CB5'
+        tox_id = tox_dns('groupbot@toxme.io')
+        assert tox_id == bot_id
+
+
+class TestEncryption:
+
+    def test_encr_decr(self):
+        with open(settings.Settings.get_default_path() + '/alice.tox', 'rb') as fl:
+            data = fl.read()
+        lib = encr.ToxEncryptSave()
+        lib.set_password('easypassword')
+        copy_data = data[:]
+        data = lib.pass_encrypt(data)
+        data = lib.pass_decrypt(data)
+        assert copy_data == data
diff --git a/tests/travis.py b/tests/travis.py
deleted file mode 100644
index af8f83f..0000000
--- a/tests/travis.py
+++ /dev/null
@@ -1,4 +0,0 @@
-class TestToxygen:
-
-    def test_main(self):
-        import toxygen.__main__  # check for syntax errors
diff --git a/toxygen/.pylint.sh b/toxygen/.pylint.sh
deleted file mode 100755
index c2e645c..0000000
--- a/toxygen/.pylint.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-ROLE=logging
-/var/local/bin/pydev_pylint.bash -E -f text *py [a-nr-z]*/*py  >.pylint.err
-/var/local/bin/pydev_pylint.bash *py [a-nr-z]*/*py  >.pylint.out
-
-sed -e "/Module 'os' has no/d" \
-    -e "/Undefined variable 'app'/d" \
-    -e '/tests\//d' \
-    -e "/Instance of 'Curl' has no /d" \
-    -e "/No name 'path' in module 'os' /d" \
-    -e "/ in module 'os'/d" \
-    -e "/.bak\//d" \
-	-i .pylint.err .pylint.out
diff --git a/toxygen/__init__.py b/toxygen/__init__.py
index 4671c45..70180be 100644
--- a/toxygen/__init__.py
+++ b/toxygen/__init__.py
@@ -1,3 +1,8 @@
 import os
 import sys
 
+path = os.path.dirname(os.path.realpath(__file__))  # curr dir
+
+sys.path.insert(0, os.path.join(path, 'styles'))
+sys.path.insert(0, os.path.join(path, 'plugins'))
+sys.path.insert(0, path)
diff --git a/toxygen/__main__.py b/toxygen/__main__.py
deleted file mode 100644
index 406726f..0000000
--- a/toxygen/__main__.py
+++ /dev/null
@@ -1,378 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import sys
-import os
-import logging
-import signal
-import time
-import warnings
-import faulthandler
-
-from gevent import monkey; monkey.patch_all(); del monkey   # noqa
-
-faulthandler.enable()
-warnings.filterwarnings('ignore')
-
-import toxygen_wrapper.tests.support_testing as ts
-try:
-    from trepan.interfaces import server as Mserver
-    from trepan.api import debug
-except Exception as e:
-    print('trepan3 TCP server NOT enabled.')
-else:
-    import signal
-    try:
-        signal.signal(signal.SIGUSR1, ts.trepan_handler)
-        print('trepan3 TCP server enabled on port 6666.')
-    except: pass
-
-import app
-from user_data.settings import *
-from user_data.settings import Settings
-from user_data import settings
-import utils.util as util
-with ts.ignoreStderr():
-    import pyaudio
-
-__maintainer__ = 'Ingvar'
-__version__ = '1.0.0' # was 0.5.0+
-
-path = os.path.dirname(os.path.realpath(__file__))  # curr dir
-sys.path.insert(0, os.path.join(path, 'styles'))
-sys.path.insert(0, os.path.join(path, 'plugins'))
-# sys.path.insert(0, os.path.join(path, 'third_party'))
-sys.path.insert(0, path)
-
-sleep = time.sleep
-
-os.environ['QT_API'] = os.environ.get('QT_API', 'pyqt5')
-
-def reset() -> None:
-    Settings.reset_auto_profile()
-
-def clean() -> None:
-    """Removes libs folder"""
-    directory = util.get_libs_directory()
-    util.remove(directory)
-
-def print_toxygen_version() -> None:
-    print('toxygen ' + __version__)
-
-def setup_default_audio():
-    # need:
-    audio = ts.get_audio()
-    # unfinished
-    global oPYA
-    oPYA = pyaudio.PyAudio()
-    audio['output_devices'] = dict()
-    i = oPYA.get_device_count()
-    while i > 0:
-        i -= 1
-        if oPYA.get_device_info_by_index(i)['maxOutputChannels'] == 0:
-            continue
-        audio['output_devices'][i] = oPYA.get_device_info_by_index(i)['name']
-    i = oPYA.get_device_count()
-    audio['input_devices'] = dict()
-    while i > 0:
-        i -= 1
-        if oPYA.get_device_info_by_index(i)['maxInputChannels'] == 0:
-            continue
-        audio['input_devices'][i] = oPYA.get_device_info_by_index(i)['name']
-    return audio
-
-def setup_video(oArgs):
-    video = setup_default_video()
-    # this is messed up - no video_input in oArgs
-    #     parser.add_argument('--video_input', type=str,)
-    print(video)
-    if not video or not video['output_devices']:
-        video['device'] = -1
-    if not hasattr(oArgs, 'video_input'):
-        video['device'] = video['output_devices'][0]
-    elif oArgs.video_input == '-1':
-        video['device'] = video['output_devices'][-1]
-    else:
-        video['device'] = oArgs.video_input
-    return video
-
-def setup_audio(oArgs):
-    global oPYA
-    audio = setup_default_audio()
-    for k,v in audio['input_devices'].items():
-        if v == 'default' and 'input' not in audio:
-            audio['input'] = k
-        if v == getattr(oArgs, 'audio_input'):
-            audio['input'] = k
-            LOG.debug(f"Setting audio['input'] {k} = {v} {k}")
-            break
-    for k,v in audio['output_devices'].items():
-        if v == 'default' and 'output' not in audio:
-            audio['output'] = k
-        if v == getattr(oArgs, 'audio_output'):
-            audio['output'] = k
-            LOG.debug(f"Setting audio['output'] {k} = {v} " +str(k))
-            break
-
-    if hasattr(oArgs, 'mode') and getattr(oArgs, 'mode') > 1:
-        audio['enabled'] = True
-        audio['audio_enabled'] = True
-        audio['video_enabled'] = True
-    elif hasattr(oArgs, 'mode') and getattr(oArgs, 'mode') > 0:
-        audio['enabled'] = True
-        audio['audio_enabled'] = False
-        audio['video_enabled'] = True
-    else:
-        audio['enabled'] = False
-        audio['audio_enabled'] = False
-        audio['video_enabled'] = False
-
-    return audio
-
-    i = getattr(oArgs, 'audio_output')
-    if i >= 0:
-        try:
-            elt = oPYA.get_device_info_by_index(i)
-            if i >= 0 and ( 'maxOutputChannels' not in elt or \
-                            elt['maxOutputChannels'] == 0):
-                LOG.warn(f"Audio output device has no output channels:  {i}")
-                oArgs.audio_output = -1
-        except OSError as e:
-            LOG.warn("Audio output device error looking for maxOutputChannels: " \
-                      +str(i) +' ' +str(e))
-            oArgs.audio_output = -1
-
-    if getattr(oArgs, 'audio_output') < 0:
-        LOG.info("Choose an output device:")
-        i = oPYA.get_device_count()
-        while i > 0:
-            i -= 1
-            if oPYA.get_device_info_by_index(i)['maxOutputChannels'] == 0:
-                continue
-            LOG.info(str(i) \
-                  +' ' +oPYA.get_device_info_by_index(i)['name'] \
-                  +' ' +str(oPYA.get_device_info_by_index(i)['defaultSampleRate'])
-                  )
-        return 0
-
-    i = getattr(oArgs, 'audio_input')
-    if i >= 0:
-        try:
-            elt = oPYA.get_device_info_by_index(i)
-            if i >= 0 and ( 'maxInputChannels' not in elt or \
-                            elt['maxInputChannels'] == 0):
-                LOG.warn(f"Audio input device has no input channels:  {i}")
-                setattr(oArgs, 'audio_input', -1)
-        except OSError as e:
-            LOG.warn("Audio input device error looking for maxInputChannels: " \
-                      +str(i) +' ' +str(e))
-            setattr(oArgs, 'audio_input', -1)
-    if getattr(oArgs, 'audio_input') < 0:
-        LOG.info("Choose an input device:")
-        i = oPYA.get_device_count()
-        while i > 0:
-            i -= 1
-            if oPYA.get_device_info_by_index(i)['maxInputChannels'] == 0:
-                continue
-            LOG.info(str(i) \
-                     +' ' +oPYA.get_device_info_by_index(i)['name']
-                     +' ' +str(oPYA.get_device_info_by_index(i)['defaultSampleRate'])
-                  )
-        return 0
-
-def setup_default_video():
-    default_video = ["-1"]
-    default_video.extend(ts.get_video_indexes())
-    LOG.info(f"Video input choices: {default_video}")
-    video = {'device': -1, 'width': 320, 'height': 240, 'x': 0, 'y': 0}
-    video['output_devices'] = default_video
-    return video
-
-def main_parser(_=None, iMode=2):
-    if not os.path.exists('/proc/sys/net/ipv6'):
-        bIpV6 = 'False'
-    else:
-        bIpV6 = 'True'
-    lIpV6Choices=[bIpV6, 'False']
-
-    audio = setup_default_audio()
-    default_video = setup_default_video()['output_devices']
-
-    parser = ts.oMainArgparser()
-    parser.add_argument('--version', action='store_true', help='Prints Toxygen version')
-    parser.add_argument('--clean', action='store_true', help='Delete toxcore libs from libs folder')
-    parser.add_argument('--reset', action='store_true', help='Reset default profile')
-    parser.add_argument('--uri', type=str, default='',
-                        help='Add specified Tox ID to friends')
-    parser.add_argument('--auto_accept_path', '--auto-accept-path', type=str,
-                        default=os.path.join(os.environ['HOME'], 'Downloads'),
-                        help="auto_accept_path")
-#    parser.add_argument('--mode', type=int, default=iMode,
-#                        help='Mode: 0=chat 1=chat+audio 2=chat+audio+video default: 0')
-    parser.add_argument('--font', type=str, default="Courier",
-                        help='Message font')
-    parser.add_argument('--message_font_size', type=int, default=15,
-                        help='Font size in pixels')
-    parser.add_argument('--local_discovery_enabled',type=str,
-                        default='False', choices=['True','False'],
-                        help='Look on the local lan')
-    parser.add_argument('--compact_mode',type=str,
-                        default='True', choices=['True','False'],
-                        help='Compact mode')
-    parser.add_argument('--allow_inline',type=str,
-                        default='False', choices=['True','False'],
-                        help='Dis/Enable allow_inline')
-    parser.add_argument('--notifications',type=str,
-                        default='True', choices=['True','False'],
-                        help='Dis/Enable notifications')
-    parser.add_argument('--sound_notifications',type=str,
-                        default='True', choices=['True','False'],
-                        help='Enable sound notifications')
-    parser.add_argument('--calls_sound',type=str,
-                        default='True', choices=['True','False'],
-                        help='Enable calls_sound')
-    parser.add_argument('--core_logging',type=str,
-                        default='False', choices=['True','False'],
-                        help='Dis/Enable Toxcore notifications')
-    parser.add_argument('--save_history',type=str,
-                        default='True', choices=['True','False'],
-                        help='En/Disable save history')
-    parser.add_argument('--update', type=int, default=0,
-                        choices=[0,0],
-                        help='Update program (broken)')
-    parser.add_argument('--video_input', type=str,
-                        default=-1,
-                        choices=default_video,
-                        help="Video input device number - /dev/video?")
-    parser.add_argument('--audio_input', type=str,
-                        default=oPYA.get_default_input_device_info()['name'],
-                        choices=audio['input_devices'].values(),
-                        help="Audio input device name - aplay -L for help")
-    parser.add_argument('--audio_output', type=str,
-                        default=oPYA.get_default_output_device_info()['index'],
-                        choices=audio['output_devices'].values(),
-                        help="Audio output device number - -1 for help")
-    parser.add_argument('--theme', type=str, default='default',
-                        choices=['dark', 'default'],
-                        help='Theme - style of UI')
-#    parser.add_argument('--sleep', type=str, default='time',
-#                        # could expand this to tk, gtk, gevent...
-#                        choices=['qt','gevent','time'],
-#                        help='Sleep method - one of qt, gevent , time')
-    supported_languages = settings.supported_languages()
-    parser.add_argument('--language', type=str, default='English',
-                        choices=supported_languages,
-                        help='Languages')
-    parser.add_argument('profile', type=str, nargs='?', default=None,
-                        help='Path to Tox profile')
-    return parser
-
-# clean out the unchanged settings so these can override the profile
-lKEEP_SETTINGS = ['uri',
-                  'profile',
-                  'loglevel',
-                  'logfile',
-                  'mode',
-
-                  # dunno
-                  'audio_input',
-                  'audio_output',
-                  'audio',
-                  'video',
-
-                  'ipv6_enabled',
-                  'udp_enabled',
-                  'local_discovery_enabled',
-                  'trace_enabled',
-
-                  'theme',
-                  'network',
-                  'message_font_size',
-                  'font',
-                  'save_history',
-                  'language',
-                  'update',
-                  'proxy_host',
-                  'proxy_type',
-                  'proxy_port',
-                  'core_logging',
-                  'audio',
-                  'video'
-                  ] # , 'nodes_json'
-
-class A(): pass
-
-def main(lArgs=None) -> int:
-    global oPYA
-    from argparse import Namespace
-    if lArgs is None:
-        lArgs = sys.argv[1:]
-    parser = main_parser()
-    default_ns = parser.parse_args([])
-    oArgs = parser.parse_args(lArgs)
-
-    if oArgs.version:
-        print_toxygen_version()
-        return 0
-
-    if oArgs.clean:
-        clean()
-        return 0
-
-    if oArgs.reset:
-        reset()
-        return 0
-
-    # if getattr(oArgs, 'network') in ['newlocal', 'localnew']: oArgs.network = 'new'
-
-    # clean out the unchanged settings so these can override the profile
-    for key in default_ns.__dict__.keys():
-        if key in lKEEP_SETTINGS: continue
-        if not hasattr(oArgs, key): continue
-        if getattr(default_ns, key) == getattr(oArgs, key):
-            delattr(oArgs, key)
-
-    ts.clean_booleans(oArgs)
-
-    aArgs = A()
-    for key in oArgs.__dict__.keys():
-        setattr(aArgs, key, getattr(oArgs, key))
-
-    #setattr(aArgs, 'video', setup_video(oArgs))
-    aArgs.video = setup_video(oArgs)
-    assert 'video' in aArgs.__dict__
-
-    #setattr(aArgs, 'audio', setup_audio(oArgs))
-    aArgs.audio = setup_audio(oArgs)
-    assert 'audio' in aArgs.__dict__
-    oArgs = aArgs
-
-    oApp = app.App(__version__, oArgs)
-    # for pyqtconsole
-    try:
-        setattr(__builtins__, 'app', oApp)
-    except Exception as e:
-        pass
-    i = oApp.iMain()
-    return i
-
-if __name__ == '__main__':
-    iRet = 0
-    try:
-        iRet = main(sys.argv[1:])
-    except KeyboardInterrupt:
-        iRet = 0
-    except SystemExit as e:
-        iRet = e
-    except Exception as e:
-        import traceback
-        sys.stderr.write(f"Exception from main  {e}" \
-                         +'\n' + traceback.format_exc() +'\n' )
-        iRet = 1
-
-    # Exception ignored in: <module 'threading' from '/usr/lib/python3.9/threading.py'>
-    # File "/usr/lib/python3.9/threading.py", line 1428, in _shutdown
-    # lock.acquire()
-    # gevent.exceptions.LoopExit as e:
-    # This operation would block forever
-    sys.stderr.write('Calling sys.exit' +'\n')
-    with ts.ignoreStdout():
-        sys.exit(iRet)
diff --git a/toxygen/app.py b/toxygen/app.py
deleted file mode 100644
index 4326849..0000000
--- a/toxygen/app.py
+++ /dev/null
@@ -1,1050 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import os
-import sys
-import traceback
-import logging
-from random import shuffle
-import threading
-from time import sleep, time
-from copy import deepcopy
-
-# used only in loop
-import gevent
-
-from qtpy import QtWidgets, QtGui, QtCore
-from qtpy.QtCore import QTimer
-from qtpy.QtWidgets import QApplication
-
-__version__ = "1.0.0"
-
-try:
-    import coloredlogs
-    if 'COLOREDLOGS_LEVEL_STYLES' not in os.environ:
-        os.environ['COLOREDLOGS_LEVEL_STYLES'] = 'spam=22;debug=28;verbose=34;notice=220;warning=202;success=118,bold;error=124;critical=background=red'
-    # https://pypi.org/project/coloredlogs/
-except ImportError as e:
-    coloredlogs = False
-
-try:
-    # https://github.com/pyqtconsole/pyqtconsole
-    from pyqtconsole.console import PythonConsole
-except Exception as e:
-    PythonConsole = None
-
-try:
-    import qdarkstylexxx
-except ImportError:
-    qdarkstyle = None
-
-from middleware import threads
-import middleware.callbacks as callbacks
-import updater.updater as updater
-from middleware.tox_factory import tox_factory
-import toxygen_wrapper.toxencryptsave as tox_encrypt_save
-import user_data.toxes
-from user_data import settings
-from user_data.settings import get_user_config_path, merge_args_into_settings
-from user_data.settings import Settings
-from user_data.profile_manager import ProfileManager
-
-from plugin_support.plugin_support import PluginLoader
-
-import ui.password_screen as password_screen
-from ui.login_screen import LoginScreen
-from ui.main_screen import MainWindow
-from ui import tray
-
-import utils.ui as util_ui
-import utils.util as util
-from av.calls_manager import CallsManager
-from common.provider import Provider
-from contacts.contact_provider import ContactProvider
-from contacts.contacts_manager import ContactsManager
-from contacts.friend_factory import FriendFactory
-from contacts.group_factory import GroupFactory
-from contacts.group_peer_factory import GroupPeerFactory
-from contacts.profile import Profile
-from file_transfers.file_transfers_handler import FileTransfersHandler
-from file_transfers.file_transfers_messages_service import FileTransfersMessagesService
-from groups.groups_service import GroupsService
-from history.database import Database
-from history.history import History
-from messenger.messenger import Messenger
-from network.tox_dns import ToxDns
-from smileys.smileys import SmileyLoader
-from ui.create_profile_screen import CreateProfileScreen
-from ui.items_factories import MessagesItemsFactory, ContactItemsFactory
-from ui.widgets_factory import WidgetsFactory
-from user_data.backup_service import BackupService
-import styles.style  # TODO: dynamic loading
-
-import toxygen_wrapper.tests.support_testing as ts
-
-global LOG
-LOG = logging.getLogger('app')
-
-IDLE_PERIOD = 0.10
-iNODES=8
-bSHOW_TRAY=False
-
-def setup_logging(oArgs) -> None:
-    global LOG
-    logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S',
-                                                  fmt='%(levelname)s:%(name)s %(message)s')
-    logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S'
-    logging._defaultFormatter.default_msec_format = ''
-
-    if coloredlogs:
-        aKw = dict(level=oArgs.loglevel,
-                   logger=LOG,
-                   fmt='%(name)s %(levelname)s %(message)s')
-        aKw['stream'] = sys.stdout
-        coloredlogs.install(**aKw)
-
-    else:
-        aKw = dict(level=oArgs.loglevel,
-                   format='%(name)s %(levelname)-4s %(message)s')
-        aKw['stream'] = sys.stdout
-        logging.basicConfig(**aKw)
-
-    if oArgs.logfile:
-        oFd = open(oArgs.logfile, 'wt')
-        setattr(oArgs, 'log_oFd', oFd)
-        oHandler = logging.StreamHandler(stream=oFd)
-        LOG.addHandler(oHandler)
-
-    LOG.setLevel(oArgs.loglevel)
-    LOG.trace = lambda l: LOG.log(0, repr(l))
-    LOG.info(f"Setting loglevel to {oArgs.loglevel}")
-
-    if oArgs.loglevel < 20:
-        # opencv debug
-        sys.OpenCV_LOADER_DEBUG = True
-
-#? with ignoreStderr(): for png
-# silence logging PyQt5.uic.uiparser
-logging.getLogger('PyQt5.uic').setLevel(logging.ERROR)
-logging.getLogger('PyQt5.uic.uiparser').setLevel(logging.ERROR)
-logging.getLogger('PyQt5.uic.properties').setLevel(logging.ERROR)
-
-global iI
-iI = 0
-
-sSTYLE = """
-.QWidget {font-family Helvetica;}
-.QCheckBox { font-family Helvetica;}
-.QComboBox { font-family Helvetica;}
-.QGroupBox { font-family Helvetica;}
-.QLabel {font-family Helvetica;}
-.QLineEdit { font-family Helvetica;}
-.QListWidget { font-family Helvetica;}
-.QListWidgetItem { font-family Helvetica;}
-.QMainWindow {font-family Helvetica;}
-.QMenu {font-family Helvetica;}
-.QMenuBar {font-family Helvetica;}
-.QPlainText {font-family Courier; weight: 75;}
-.QPlainTextEdit {font-family Courier;}
-.QPushButton {font-family Helvetica;}
-.QRadioButton { font-family Helvetica; }
-.QText {font-family Courier; weight: 75; }
-.QTextBrowser {font-family Courier; weight: 75; }
-.QTextSingleLine {font-family Courier; weight: 75; }
-.QToolBar { font-weight: bold; }
-"""
-class App:
-
-    def __init__(self, version, oArgs):
-        global LOG
-        self._args = oArgs
-        self.oArgs = oArgs
-        self._path = path_to_profile = oArgs.profile
-        uri = oArgs.uri
-        logfile = oArgs.logfile
-        loglevel = oArgs.loglevel
-
-        setup_logging(oArgs)
-        # sys.stderr.write( 'Command line args: ' +repr(oArgs) +'\n')
-        LOG.info("Command line: " +' '.join(sys.argv[1:]))
-        LOG.debug(f'oArgs = {oArgs}')
-        LOG.info("Starting toxygen version " +version)
-
-        self._version = version
-        self._tox = None
-        self._app = self._settings = self._profile_manager = None
-        self._plugin_loader = self._messenger = None
-        self._tox = self._ms = self._init = self._main_loop = self._av_loop = None
-        self._uri = self._toxes = self._tray = None
-        self._file_transfer_handler = self._contacts_provider = None
-        self._friend_factory = self._calls_manager = None
-        self._contacts_manager = self._smiley_loader = None
-        self._group_peer_factory = self._tox_dns = self._backup_service = None
-        self._group_factory = self._groups_service = self._profile = None
-        if uri is not None and uri.startswith('tox:'):
-            self._uri = uri[4:]
-        self._history = None
-        self.bAppExiting = False
-
-    # Public methods
-
-    def set_trace(self) -> None:
-        """unused"""
-        LOG.debug('pdb.set_trace ')
-        sys.stdin = sys.__stdin__
-        sys.stdout = sys.__stdout__
-        import pdb; pdb.set_trace()
-
-    def ten(self, i=0) -> None:
-        """unused"""
-        global iI
-        iI += 1
-        if logging.getLogger('app').getEffectiveLevel() != 10:
-            sys.stderr.write('CHANGED '+str(logging.getLogger().level+'\n'))
-            LOG.setLevel(10)
-            LOG.root.setLevel(10)
-            logging.getLogger('app').setLevel(10)
-        #sys.stderr.write(f"ten '+str(iI)+'  {i}"+' '+repr(LOG) +'\n')
-        #LOG.debug('ten '+str(iI))
-
-    def iMain(self) -> int:
-        """
-        Main function of app. loads login screen if needed and starts main screen
-        """
-        self._app = QApplication([])
-        self._load_icon()
-
-        # is this still needed?
-        if util.get_platform() == 'Linux' and \
-          hasattr(QtCore.Qt, 'AA_X11InitThreads'):
-            QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
-
-        self._load_base_style()
-
-        encrypt_save = tox_encrypt_save.ToxEncryptSave()
-        self._toxes = user_data.toxes.ToxES(encrypt_save)
-        try:
-            # this throws everything as errors
-            if not self._select_and_load_profile():
-                return 2
-            if hasattr(self._args, 'update') and self._args.update:
-                if self._try_to_update(): return 3
-
-            self._load_app_styles()
-            if self._args.language != 'English':
-                # (Pdb) Fatal Python error: Segmentation fault
-                self._load_app_translations()
-            self._create_dependencies()
-
-            self._start_threads(True)
-
-            if self._uri is not None:
-                self._ms.add_contact(self._uri)
-        except Exception as e:
-            LOG.error(f"Error loading profile: {e}")
-            sys.stderr.write(' iMain(): ' +f"Error loading profile: {e}" \
-                             +'\n' + traceback.format_exc()+'\n')
-            util_ui.message_box(str(e),
-                                util_ui.tr('Error loading profile'))
-            return 4
-
-        self._app.lastWindowClosed.connect(self._app.quit)
-        try:
-            self._execute_app()
-            self.quit()
-            retval = 0
-        except KeyboardInterrupt:
-            retval = 0
-        except Exception:
-            retval = 1
-
-        return retval
-
-    # App executing
-
-    def _execute_app(self) -> None:
-        LOG.debug("_execute_app")
-
-        while True:
-            try:
-                self._app.exec_()
-            except Exception as ex:
-                LOG.error('Unhandled exception: ' + str(ex))
-            else:
-                break
-
-    def quit(self, retval=0) -> None:
-        LOG.debug("quit")
-        self._stop_app()
-
-        # failsafe: segfaults on exit - maybe it's Qt
-        if hasattr(self, '_tox'):
-            if self._tox and hasattr(self._tox, 'kill'):
-                LOG.debug(f"quit: Killing {self._tox}")
-                self._tox.kill()
-            del self._tox
-
-        if hasattr(self, '_app'):
-            self._app.quit()
-            del self._app.quit
-            del self._app
-
-        sys.stderr.write('quit raising SystemExit' +'\n')
-        # hanging on gevents
-        # Thread 1 "python3.9" received signal SIGSEGV, Segmentation fault.
-        #44 0x00007ffff7fb2f93 in  () at /usr/lib/python3.9/site-packages/greenlet/_greenlet.cpython-39-x86_64-linux-gnu.so
-        #45 0x00007ffff7fb31ef in  () at /usr/lib/python3.9/site-packages/greenlet/_greenlet.cpython-39-x86_64-linux-gnu.so
-        #46 0x00007ffff452165c in hb_shape_plan_create_cached2 () at /usr/lib64/libharfbuzz.so.0
-
-        raise SystemExit(retval)
-
-    def _stop_app(self) -> None:
-        LOG.debug("_stop_app")
-        self._save_profile()
-        self._history.save_history()
-
-        self._plugin_loader.stop()
-        try:
-            self._stop_threads(is_app_closing=True)
-        except (Exception, RuntimeError):
-            # RuntimeError: cannot join current thread
-            pass
-        # I think there are threads still running here leading to a SEGV
-        #   File "/usr/lib/python3.11/threading.py", line 1401 in run
-        #   File "/usr/lib/python3.11/threading.py", line 1045 in _bootstrap_inner
-        #   File "/usr/lib/python3.11/threading.py", line 1002 in _bootstrap
-
-        if hasattr(self, '_tray') and self._tray:
-            self._tray.hide()
-        self._settings.close()
-
-        self.bAppExiting = True
-        LOG.debug(f"stop_app: Killing {self._tox}")
-        self._kill_toxav()
-        self._kill_tox()
-        del self._tox
-
-        oArgs = self._args
-        if hasattr(oArgs, 'log_oFd'):
-            LOG.debug(f"Closing {oArgs.log_oFd}")
-            oArgs.log_oFd.close()
-            delattr(oArgs, 'log_oFd')
-
-    # App loading
-
-    def _load_base_style(self) -> None:
-        if self._args.theme in ['', 'default']: return
-
-        if qdarkstyle:
-            LOG.debug("_load_base_style qdarkstyle " +self._args.theme)
-            # QDarkStyleSheet
-            if self._args.theme == 'light':
-                from qdarkstyle.light.palette import LightPalette
-                style = qdarkstyle.load_stylesheet(palette=LightPalette)
-            else:
-                from qdarkstyle.dark.palette import DarkPalette
-                style = qdarkstyle.load_stylesheet(palette=DarkPalette)
-        else:
-            LOG.debug("_load_base_style qss " +self._args.theme)
-            name = self._args.theme + '.qss'
-            with open(util.join_path(util.get_styles_directory(), name)) as fl:
-                style = fl.read()
-        style += '\n' +sSTYLE
-        self._app.setStyleSheet(style)
-
-    def _load_app_styles(self) -> None:
-        LOG.debug(f"_load_app_styles {list(settings.built_in_themes().keys())}")
-        # application color scheme
-        if self._settings['theme'] in ['', 'default']: return
-        for theme in settings.built_in_themes().keys():
-            if self._settings['theme'] != theme:
-                continue
-            if qdarkstyle:
-                LOG.debug("_load_base_style qdarkstyle " +self._args.theme)
-                # QDarkStyleSheet
-                if self._args.theme == 'light':
-                    from qdarkstyle.light.palette import LightPalette
-                    style = qdarkstyle.load_stylesheet(palette=LightPalette)
-                else:
-                    from qdarkstyle.dark.palette import DarkPalette
-                    style = qdarkstyle.load_stylesheet(palette=DarkPalette)
-            else:
-                theme_path = settings.built_in_themes()[theme]
-                file_path = util.join_path(util.get_styles_directory(), theme_path)
-                if not os.path.isfile(file_path):
-                    LOG.warn('_load_app_styles: no theme file ' + file_path)
-                    continue
-                with open(file_path) as fl:
-                    style = fl.read()
-                LOG.debug('_load_app_styles: loading theme file ' + file_path)
-            style += '\n' +sSTYLE
-            self._app.setStyleSheet(style)
-            LOG.info('_load_app_styles: loaded theme ' +self._args.theme)
-            break
-
-    def _load_login_screen_translations(self) -> None:
-        LOG.debug("_load_login_screen_translations")
-        current_language, supported_languages = self._get_languages()
-        if current_language not in supported_languages:
-            return
-        lang_path = supported_languages[current_language]
-        translator = QtCore.QTranslator()
-        translator.load(util.get_translations_directory() + lang_path)
-        self._app.installTranslator(translator)
-        self._app.translator = translator
-
-    def _load_icon(self) -> None:
-        LOG.debug("_load_icon")
-        icon_file = os.path.join(util.get_images_directory(), 'icon.png')
-        self._app.setWindowIcon(QtGui.QIcon(icon_file))
-
-    @staticmethod
-    def _get_languages() -> tuple:
-        LOG.debug("_get_languages")
-        current_locale = QtCore.QLocale()
-        curr_language = current_locale.languageToString(current_locale.language())
-        supported_languages = settings.supported_languages()
-
-        return curr_language, supported_languages
-
-    def _load_app_translations(self) -> None:
-        LOG.debug("_load_app_translations")
-        lang = settings.supported_languages()[self._settings['language']]
-        translator = QtCore.QTranslator()
-        translator.load(os.path.join(util.get_translations_directory(), lang))
-        self._app.installTranslator(translator)
-        self._app.translator = translator
-
-    def _select_and_load_profile(self) -> bool:
-        LOG.debug("_select_and_load_profile: " +repr(self._path))
-
-        if self._path is not None:
-            # toxygen was started with path to profile
-            try:
-                assert os.path.exists(self._path), f"FNF {self._path}"
-                self._load_existing_profile(self._path)
-            except Exception as e:
-                LOG.error('_load_existing_profile failed: ' + str(e))
-                title = 'Loading the profile failed '
-                if self._path:
-                    title += os.path.basename(self._path)
-                text = 'Loading the profile failed - \n' +str(e)
-                if 'Dis' == 'Abled':
-                    text += '\nLoading the profile failed - \n' \
-                      +str(e) +'\nContinue with a default profile?'
-                    reply = util_ui.question(text, title)
-                    if not reply:
-                        LOG.debug('_load_existing_profile not continuing ')
-                        raise
-                    LOG.debug('_load_existing_profile continuing ')
-                    # drop through
-                else:
-                    util_ui.message_box(text, title)
-                    raise
-        else:
-            auto_profile = Settings.get_auto_profile()
-            if auto_profile is None:  # no default profile
-                LOG.debug('_select_and_load_profile no default profile ')
-                result = self._select_profile()
-                if result is None:
-                    LOG.debug('no selected profile ')
-                    return False
-                if result.is_new_profile():  # create new profile
-                    if not self._create_new_profile(result.profile_path):
-                        LOG.warn('no new profile ')
-                        return False
-                    LOG.debug('created new profile ')
-                else:  # load existing profile
-                    self._load_existing_profile(result.profile_path)
-                    # drop through
-                self._path = result.profile_path
-            else:  # default profile
-                LOG.debug('loading default profile ')
-                self._path = auto_profile
-                self._load_existing_profile(auto_profile)
-
-        if settings.is_active_profile(self._path):  # profile is in use
-            LOG.warn(f"_select_and_load_profile active: {self._path}")
-            profile_name = util.get_profile_name_from_path(self._path)
-            title = util_ui.tr('Profile {}').format(profile_name)
-            text = util_ui.tr(
-                'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?')
-            reply = util_ui.question(text, title)
-            if not reply:
-                return False
-
-        # is self._path right - was pathless
-        self._settings.set_active_profile(self._path)
-
-        return True
-
-    # Threads
-
-    def _start_threads(self, initial_start=True) -> None:
-        LOG.debug(f"_start_threads before: {threading.enumerate()}")
-        # init thread
-        self._init = threads.InitThread(self._tox,
-                                        self._plugin_loader,
-                                        self._settings,
-                                        self,
-                                        initial_start)
-        self._init.start()
-        def te(): return [t.name for t in threading.enumerate()]
-        LOG.debug(f"_start_threads init: {te()}")
-
-        # starting threads for tox iterate and toxav iterate
-        self._main_loop = threads.ToxIterateThread(self._tox, app=self)
-        self._main_loop.start()
-
-        self._av_loop = threads.ToxAVIterateThread(self._tox.AV)
-        self._av_loop.start()
-
-        if initial_start:
-            threads.start_file_transfer_thread()
-        LOG.debug(f"_start_threads after: {[t.name for t in threading.enumerate()]}")
-
-    def _stop_threads(self, is_app_closing=True) -> None:
-        LOG.debug("_stop_threads")
-        self._init.stop_thread(1.0)
-
-        self._av_loop.stop_thread()
-        self._main_loop.stop_thread()
-
-        if is_app_closing:
-            threads.stop_file_transfer_thread()
-
-    def iterate(self, n=100) -> None:
-        interval = self._tox.iteration_interval()
-        for i in range(n):
-            self._tox.iterate()
-            # Cooperative yield, allow gevent to monitor file handles via libevent
-            gevent.sleep(interval / 1000.0)
-#?            sleep(interval / 1000.0)
-
-    # Profiles
-
-    def _select_profile(self):
-        LOG.debug("_select_profile")
-        if self._args.language != 'English':
-            self._load_login_screen_translations()
-        ls = LoginScreen()
-        profiles = ProfileManager.find_profiles()
-        ls.update_select(profiles)
-        ls.show()
-        self._app.exec_()
-        return ls.result
-
-    def _load_existing_profile(self, profile_path) -> None:
-        profile_path = profile_path.replace('.json', '.tox')
-        LOG.info("_load_existing_profile " +repr(profile_path))
-        assert os.path.exists(profile_path), profile_path
-        self._profile_manager = ProfileManager(self._toxes, profile_path, app=self)
-        data = self._profile_manager.open_profile()
-        if self._toxes.is_data_encrypted(data):
-            LOG.debug("_entering password")
-            data = self._enter_password(data)
-            LOG.debug("_entered password")
-        json_file = profile_path.replace('.tox', '.json')
-        if os.path.exists(json_file):
-            LOG.debug("creating _settings from: " +json_file)
-            self._settings = Settings(self._toxes, json_file, self)
-        else:
-             self._settings = Settings.get_default_settings()
-
-        self._tox = self._create_tox(data, self._settings)
-        LOG.debug("created _tox")
-
-    def _create_new_profile(self, profile_name) -> bool:
-        LOG.info("_create_new_profile " + profile_name)
-        result = self._get_create_profile_screen_result()
-        if result is None:
-            return False
-        if result.save_into_default_folder:
-            profile_path = util.join_path(get_user_config_path(), profile_name + '.tox')
-        else:
-            profile_path = util.join_path(util.curr_directory(__file__), profile_name + '.tox')
-        if os.path.isfile(profile_path):
-            util_ui.message_box(util_ui.tr('Profile with this name already exists'),
-                                util_ui.tr('Error'))
-            return False
-        name = profile_name or 'toxygen_user'
-        assert self._args
-        self._path = profile_path
-        if result.password:
-            self._toxes.set_password(result.password)
-        self._settings = Settings(self._toxes,
-                                  self._path.replace('.tox', '.json'),
-                                  app=self)
-        self._tox = self._create_tox(None,
-                                     self._settings)
-        self._tox.self_set_name(name if name else 'Toxygen User')
-        self._tox.self_set_status_message('Toxing on Toxygen')
-
-        self._profile_manager = ProfileManager(self._toxes, profile_path)
-        try:
-            self._save_profile()
-        except Exception as ex:
-            #? print(ex)
-            LOG.error('Profile creation exception: ' + str(ex))
-            text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?')
-            util_ui.message_box(text, util_ui.tr('Error'))
-
-            return False
-        current_language, supported_languages = self._get_languages()
-        if current_language in supported_languages:
-            self._settings['language'] = current_language
-        self._settings.save()
-
-        return True
-
-    def _get_create_profile_screen_result(self):
-        LOG.debug("_get_create_profile_screen_result")
-        cps = CreateProfileScreen()
-        cps.show()
-        self._app.exec_()
-
-        return cps.result
-
-    def _save_profile(self, data=None) -> None:
-        LOG.debug("_save_profile")
-        data = data or self._tox.get_savedata()
-        self._profile_manager.save_profile(data)
-
-    # Other private methods
-
-    def _enter_password(self, data):
-        """
-        Show password screen
-        """
-        LOG.debug("_enter_password")
-        p = password_screen.PasswordScreen(self._toxes, data)
-        p.show()
-        self._app.lastWindowClosed.connect(self._app.quit)
-        self._app.exec_()
-        if p.result is not None:
-            return p.result
-        self._force_exit(0)
-        return None
-
-    def _reset(self) -> None:
-        LOG.debug("_reset")
-        """
-        Create new tox instance (new network settings)
-        :return: tox instance
-        """
-        self._contacts_manager.reset_contacts_statuses()
-        self._stop_threads(False)
-        data = self._tox.get_savedata()
-        self._save_profile(data)
-        self._kill_toxav()
-        self._kill_tox()
-        try:
-            # create new tox instance
-            self._tox = self._create_tox(data, self._settings)
-            assert self._tox
-            self._start_threads(False)
-
-            tox_savers = [self._friend_factory, self._group_factory,
-                          self._plugin_loader, self._contacts_manager,
-                          self._contacts_provider, self._messenger,
-                          self._file_transfer_handler,
-                          self._groups_service, self._profile]
-            for tox_saver in tox_savers:
-                tox_saver.set_tox(self._tox)
-
-            self._calls_manager.set_toxav(self._tox.AV)
-            self._contacts_manager.update_friends_numbers()
-            self._contacts_manager.update_groups_lists()
-            self._contacts_manager.update_groups_numbers()
-
-            self._init_callbacks()
-        except BaseException as e:
-            LOG.error(f"_reset :  {e}")
-            LOG.debug('_reset: ' \
-                     +'\n' + traceback.format_exc())
-            title = util_ui.tr('Reset Error')
-            text = util_ui.tr('Error:') + str(e)
-            util_ui.message_box(text, title)
-
-    def _create_dependencies(self) -> None:
-        LOG.info(f"_create_dependencies toxygen version {self._version}")
-        if hasattr(self._args, 'update') and self._args.update:
-            self._backup_service = BackupService(self._settings,
-                                                 self._profile_manager)
-        self._smiley_loader = SmileyLoader(self._settings)
-        self._tox_dns = ToxDns(self._settings)
-        self._ms = MainWindow(self._settings, self._tray, self)
-
-        db_path = self._path.replace('.tox', '.db')
-        db = Database(db_path, self._toxes)
-        if os.path.exists(db_path) and hasattr(db, 'open'):
-            db.open()
-
-        assert self._tox
-
-        contact_items_factory = ContactItemsFactory(self._settings, self._ms)
-        self._friend_factory = FriendFactory(self._profile_manager,
-                                             self._settings,
-                                             self._tox,
-                                             db,
-                                             contact_items_factory)
-        self._group_factory = GroupFactory(self._profile_manager,
-                                           self._settings,
-                                           self._tox,
-                                           db,
-                                           contact_items_factory)
-        self._group_peer_factory = GroupPeerFactory(self._tox,
-                                                    self._profile_manager,
-                                                    db,
-                                                    contact_items_factory)
-        self._contacts_provider = ContactProvider(self._tox,
-                                                  self._friend_factory,
-                                                  self._group_factory,
-                                                  self._group_peer_factory,
-                                                  app=self)
-        self._profile = Profile(self._profile_manager,
-                                self._tox,
-                                self._ms,
-                                self._contacts_provider,
-                                self._reset)
-        self._init_profile()
-        self._plugin_loader = PluginLoader(self._settings, self)
-        history = None
-        messages_items_factory = MessagesItemsFactory(self._settings,
-                                                      self._plugin_loader,
-                                                      self._smiley_loader,
-                                                      self._ms,
-                                                      lambda m: history.delete_message(m))
-        history = History(self._contacts_provider, db,
-                          self._settings, self._ms, messages_items_factory)
-        self._contacts_manager = ContactsManager(self._tox,
-                                                 self._settings,
-                                                 self._ms,
-                                                 self._profile_manager,
-                                                 self._contacts_provider,
-                                                 history,
-                                                 self._tox_dns,
-                                                 messages_items_factory)
-        history.set_contacts_manager(self._contacts_manager)
-        self._history = history
-        self._calls_manager = CallsManager(self._tox.AV,
-                                           self._settings,
-                                           self._ms,
-                                           self._contacts_manager,
-                                           self)
-        self._messenger = Messenger(self._tox,
-                                    self._plugin_loader, self._ms, self._contacts_manager,
-                                    self._contacts_provider, messages_items_factory, self._profile,
-                                    self._calls_manager)
-        file_transfers_message_service = FileTransfersMessagesService(self._contacts_manager, messages_items_factory,
-                                                                      self._profile, self._ms)
-        self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider,
-                                                           file_transfers_message_service, self._profile)
-        messages_items_factory.set_file_transfers_handler(self._file_transfer_handler)
-        widgets_factory = None
-        widgets_factory_provider = Provider(lambda: widgets_factory)
-        self._groups_service = GroupsService(self._tox,
-                                             self._contacts_manager,
-                                             self._contacts_provider,
-                                             self._ms,
-                                             widgets_factory_provider)
-        widgets_factory = WidgetsFactory(self._settings,
-                                         self._profile,
-                                         self._profile_manager,
-                                         self._contacts_manager,
-                                         self._file_transfer_handler,
-                                         self._smiley_loader,
-                                         self._plugin_loader,
-                                         self._toxes,
-                                         self._version,
-                                         self._groups_service,
-                                         history,
-                                         self._contacts_provider)
-        if bSHOW_TRAY:
-            self._tray = tray.init_tray(self._profile,
-                                        self._settings,
-                                        self._ms, self._toxes)
-        self._ms.set_dependencies(widgets_factory,
-                                  self._tray,
-                                  self._contacts_manager,
-                                  self._messenger,
-                                  self._profile,
-                                  self._plugin_loader,
-                                  self._file_transfer_handler,
-                                  history,
-                                  self._calls_manager,
-                                  self._groups_service, self._toxes, self)
-
-        if bSHOW_TRAY: # broken
-            # the tray icon does not die with the app
-            self._tray.show()
-        self._ms.show()
-
-        # FixMe:
-        self._log = lambda line: LOG.log(self._args.loglevel,
-                                         self._ms.status(line))
-        # self._ms._log = self._log # was used in callbacks.py
-
-        if False:
-            self.status_handler = logging.Handler()
-            self.status_handler.setLevel(logging.INFO) # self._args.loglevel
-            self.status_handler.handle = self._ms.status
-
-        self._init_callbacks()
-        LOG.info("_create_dependencies toxygen version " +self._version)
-
-    def _try_to_update(self):
-        LOG.debug("_try_to_update")
-        updating = updater.start_update_if_needed(self._version, self._settings)
-        if updating:
-            LOG.info("Updating toxygen version " +self._version)
-            self._save_profile()
-            self._settings.close()
-            self._kill_toxav()
-            self._kill_tox()
-        return updating
-
-    def _create_tox(self, data, settings_):
-        LOG.info("_create_tox calling tox_factory")
-        assert self._args
-        retval = tox_factory(data=data, settings=settings_,
-                             args=self._args, app=self)
-        LOG.debug("_create_tox succeeded")
-        self._tox = retval
-        return retval
-
-    def _force_exit(self, retval=0) -> None:
-        LOG.debug("_force_exit")
-        sys.exit(0)
-
-    def _init_callbacks(self, ms=None) -> None:
-        LOG.debug("_init_callbacks")
-        # this will block if you are not connected
-        callbacks.init_callbacks(self._tox, self._profile, self._settings,
-                                 self._plugin_loader, self._contacts_manager,
-                                 self._calls_manager,
-                                 self._file_transfer_handler, self._ms,
-                                 self._tray,
-                                 self._messenger, self._groups_service,
-                                 self._contacts_provider, self._ms)
-
-    def _init_profile(self) -> None:
-        LOG.debug("_init_profile")
-        if not self._profile.has_avatar():
-            self._profile.reset_avatar(self._settings['identicons'])
-
-    def _kill_toxav(self) -> None:
-#        LOG_debug("_kill_toxav")
-        self._calls_manager.set_toxav(None)
-        self._tox.AV.kill()
-
-    def _kill_tox(self) -> None:
-#        LOG.debug("_kill_tox")
-        self._tox.kill()
-
-    def loop(self, n) -> None:
-        """
-        Im guessing - there are 4 sleeps - time, tox, and Qt gevent
-        """
-        interval = self._tox.iteration_interval()
-        for i in range(n):
-            self._tox.iterate()
-            #? QtCore.QThread.msleep(interval)
-            # Cooperative yield, allow gevent to monitor file handles via libevent
-            gevent.sleep(interval / 1000.0)
-            # NO?
-            QtCore.QCoreApplication.processEvents()
-
-    def _test_tox(self) -> None:
-        self.test_net(iMax=8)
-        self._ms.log_console()
-
-    def test_net(self, lElts=None, oThread=None, iMax=4) -> None:
-
-        # bootstrap
-        LOG.debug('test_net: Calling generate_nodes: udp')
-        lNodes = ts.generate_nodes(oArgs=self._args,
-                                   ipv='ipv4',
-                                   udp_not_tcp=True)
-        self._settings['current_nodes_udp'] = lNodes
-        if not lNodes:
-            LOG.warn('empty generate_nodes udp')
-        LOG.debug('test_net: Calling generate_nodes: tcp')
-        lNodes = ts.generate_nodes(oArgs=self._args,
-                                   ipv='ipv4',
-                                   udp_not_tcp=False)
-        self._settings['current_nodes_tcp'] = lNodes
-        if not lNodes:
-            LOG.warn('empty generate_nodes tcp')
-
-        # if oThread and oThread._stop_thread: return
-        LOG.debug("test_net network=" +self._args.network +' iMax=' +str(iMax))
-        if self._args.network not in ['local', 'localnew', 'newlocal']:
-            b = ts.bAreWeConnected()
-            if b is None:
-                i = os.system('ip route|grep ^def')
-                if i > 0:
-                    b = False
-                else:
-                    b = True
-            if not b:
-                LOG.warn("No default route for network " +self._args.network)
-                text = 'You have no default route - are you connected?'
-                reply = util_ui.question(text, "Are you connected?")
-                if not reply: return
-                iMax = 1
-            else:
-                LOG.debug("Have default route for network " +self._args.network)
-
-        lUdpElts = self._settings['current_nodes_udp']
-        if self._args.proxy_type <= 0 and not lUdpElts:
-            title = 'test_net Error'
-            text = 'Error: ' + str('No UDP nodes')
-            util_ui.message_box(text, title)
-            return
-        lTcpElts = self._settings['current_nodes_tcp']
-        if self._args.proxy_type > 0 and not lTcpElts:
-            title = 'test_net Error'
-            text = 'Error: ' + str('No TCP nodes')
-            util_ui.message_box(text, title)
-            return
-        LOG.debug(f"test_net {self._args.network} lenU={len(lUdpElts)} lenT={len(lTcpElts)} iMax={iMax}")
-        i = 0
-        while i < iMax:
-            # if oThread and oThread._stop_thread: return
-            i = i + 1
-            LOG.debug(f"bootstrapping status proxy={self._args.proxy_type} # {i}")
-            if self._args.proxy_type == 0:
-                self._test_bootstrap(lUdpElts)
-            else:
-                self._test_bootstrap([lUdpElts[0]])
-                LOG.debug(f"relaying status # {i}")
-                self._test_relays(self._settings['current_nodes_tcp'])
-            status = self._tox.self_get_connection_status()
-            if status > 0:
-                LOG.info(f"Connected # {i}" +' : ' +repr(status))
-                break
-            LOG.trace(f"Connected status #{i}: {status}")
-
-    def _test_env(self) -> None:
-        _settings = self._settings
-        if 'proxy_type' not in _settings or _settings['proxy_type'] == 0 or \
-          not _settings['proxy_host'] or not _settings['proxy_port']:
-            env = dict( prot = 'ipv4')
-            lElts = self._settings['current_nodes_udp']
-        elif _settings['proxy_type'] == 2:
-            env = dict(prot = 'socks5',
-                       https_proxy='', \
-                       socks_proxy='socks5://' \
-                       +_settings['proxy_host'] +':' \
-                       +str(_settings['proxy_port']))
-            lElts = self._settings['current_nodes_tcp']
-        elif _settings['proxy_type'] == 1:
-            env = dict(prot = 'https',
-                       socks_proxy='', \
-                       https_proxy='http://' \
-                       +_settings['proxy_host'] +':' \
-                       +str(_settings['proxy_port']))
-            lElts = _settings['current_nodes_tcp']
-#        LOG.debug(f"test_env {len(lElts)}")
-        return env
-
-    def _test_bootstrap(self, lElts=None) -> None:
-        if lElts is None:
-            lElts = self._settings['current_nodes_udp']
-        LOG.debug(f"_test_bootstrap #Elts={len(lElts)}")
-        if not lElts:
-            return
-        shuffle(lElts)
-        ts.bootstrap_udp(lElts[:iNODES], [self._tox])
-        LOG.info("Connected status: " +repr(self._tox.self_get_connection_status()))
-
-    def _test_relays(self, lElts=None) -> None:
-        if lElts is None:
-            lElts = self._settings['current_nodes_tcp']
-        shuffle(lElts)
-        LOG.debug(f"_test_relays {len(lElts)}")
-        ts.bootstrap_tcp(lElts[:iNODES], [self._tox])
-
-    def _test_nmap(self, lElts=None) -> None:
-        LOG.debug("_test_nmap")
-        if not self._tox: return
-        title = 'Extended Test Suite'
-        text = 'Run the Extended Test Suite?\nThe program may freeze for 1-10 minutes.'
-        i = os.system('ip route|grep ^def >/dev/null')
-        if i > 0:
-            text += '\nYou have no default route - are you connected?'
-        reply = util_ui.question(text, title)
-        if not reply: return
-
-        if self._args.proxy_type == 0:
-            sProt = "udp4"
-        else:
-            sProt = "tcp4"
-        if lElts is None:
-            if self._args.proxy_type == 0:
-                lElts = self._settings['current_nodes_udp']
-            else:
-                lElts = self._settings['current_nodes_tcp']
-        shuffle(lElts)
-        try:
-            ts.bootstrap_iNmapInfo(lElts, self._args, sProt)
-        except Exception as e:
-            LOG.error(f"test_nmap ' +' :  {e}")
-            LOG.error('_test_nmap(): ' \
-                         +'\n' + traceback.format_exc())
-            title = 'Test Suite Error'
-            text = 'Error: ' + str(e)
-            util_ui.message_box(text, title)
-
-        # LOG.info("Connected status: " +repr(self._tox.self_get_connection_status()))
-        self._ms.log_console()
-
-    def _test_main(self) -> None:
-        from toxygen_toxygen_wrapper.toxygen_wrapper.tests.tests_wrapper import main as tests_main
-        LOG.debug("_test_main")
-        if not self._tox: return
-        title = 'Extended Test Suite'
-        text = 'Run the Extended Test Suite?\nThe program may freeze for 20-60 minutes.'
-        reply = util_ui.question(text, title)
-        if reply:
-            if hasattr(self._args, 'proxy_type') and self._args.proxy_type:
-                lArgs = ['--proxy_host', self._args.proxy_host,
-                         '--proxy_port', str(self._args.proxy_port),
-                         '--proxy_type', str(self._args.proxy_type), ]
-            else:
-                lArgs = list()
-            try:
-                tests_main(lArgs)
-            except Exception as e:
-                LOG.error(f"_test_socks():  {e}")
-                LOG.error('_test_socks(): ' \
-                         +'\n' + traceback.format_exc())
-                title = 'Extended Test Suite Error'
-                text = 'Error:' + str(e)
-                util_ui.message_box(text, title)
-            self._ms.log_console()
-
-#? unused
-class GEventProcessing:
-    """Interoperability class between Qt/gevent that allows processing gevent
-    tasks during Qt idle periods."""
-    def __init__(self, idle_period=IDLE_PERIOD):
-        # Limit the IDLE handler's frequency while still allow for gevent
-        # to trigger a microthread anytime
-        self._idle_period = idle_period
-        # IDLE timer: on_idle is called whenever no Qt events left for
-        # processing
-        self._timer = QTimer()
-        self._timer.timeout.connect(self.process_events)
-        self._timer.start(0)
-    def __enter__(self) -> None:
-        pass
-
-    def __exit__(self, *exc_info) -> None:
-        self._timer.stop()
-
-    def process_events(self, idle_period=None) -> None:
-        if idle_period is None:
-            idle_period = self._idle_period
-        # Cooperative yield, allow gevent to monitor file handles via libevent
-        gevent.sleep(idle_period)
-        #? QtCore.QCoreApplication.processEvents()
diff --git a/toxygen/av/__init__.py b/toxygen/av/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/av/call.py b/toxygen/av/call.py
deleted file mode 100644
index 73caa25..0000000
--- a/toxygen/av/call.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-class Call:
-
-    def __init__(self, out_audio, out_video, in_audio=False, in_video=False):
-        self._in_audio = in_audio
-        self._in_video = in_video
-        self._out_audio = out_audio
-        self._out_video = out_video
-        self._is_active = False
-
-    def get_is_active(self):
-        return self._is_active
-
-    def set_is_active(self, value):
-        self._is_active = value
-
-    is_active = property(get_is_active, set_is_active)
-
-    # Audio
-
-    def get_in_audio(self):
-        return self._in_audio
-
-    def set_in_audio(self, value):
-        self._in_audio = value
-
-    in_audio = property(get_in_audio, set_in_audio)
-
-    def get_out_audio(self):
-        return self._out_audio
-
-    def set_out_audio(self, value):
-        self._out_audio = value
-
-    out_audio = property(get_out_audio, set_out_audio)
-
-    # Video
-
-    def get_in_video(self):
-        return self._in_video
-
-    def set_in_video(self, value):
-        self._in_video = value
-
-    in_video = property(get_in_video, set_in_video)
-
-    def get_out_video(self):
-        return self._out_video
-
-    def set_out_video(self, value):
-        self._out_video = value
-
-    out_video = property(get_out_video, set_out_video)
diff --git a/toxygen/av/calls.py b/toxygen/av/calls.py
deleted file mode 100644
index 9b40fc1..0000000
--- a/toxygen/av/calls.py
+++ /dev/null
@@ -1,587 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import time
-import threading
-import logging
-import itertools
-
-from toxygen_wrapper.toxav_enums import *
-from toxygen_wrapper.tests import support_testing as ts
-from toxygen_wrapper.tests.support_testing import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
-
-with ts.ignoreStderr():
-    import pyaudio
-from av import screen_sharing
-from av.call import Call
-import common.tox_save
-from middleware.threads import BaseQThread
-
-from utils import ui as util_ui
-from middleware.threads import invoke_in_main_thread
-# from middleware.threads import BaseThread
-
-sleep = time.sleep
-
-global LOG
-LOG = logging.getLogger('app.'+__name__)
-
-TIMER_TIMEOUT = 30.0
-iFPS = 25
-
-class AudioThread(BaseQThread):
-    def __init__(self, av, name=''):
-        super().__init__()
-        self.av = av
-        self._name = name
-
-    def join(self, ito=ts.iTHREAD_TIMEOUT):
-        LOG_DEBUG(f"AudioThread join {self}")
-        # dunno
-
-    def run(self) -> None:
-        LOG_DEBUG('AudioThread run: ')
-        # maybe not needed
-        while not self._stop_thread:
-            self.av.send_audio()
-            sleep(100.0 / 1000.0)
-
-class VideoThread(BaseQThread):
-    def __init__(self, av, name=''):
-        super().__init__()
-        self.av = av
-        self._name = name
-
-    def join(self, ito=ts.iTHREAD_TIMEOUT):
-        LOG_DEBUG(f"VideoThread join {self}")
-        # dunno
-
-    def run(self) -> None:
-        LOG_DEBUG('VideoThread run: ')
-        # maybe not needed
-        while not self._stop_thread:
-            self.av.send_video()
-            sleep(100.0 / 1000.0)
-
-class AV(common.tox_save.ToxAvSave):
-
-    def __init__(self, toxav, settings):
-        super().__init__(toxav)
-        self._toxav = toxav
-        self._settings = settings
-        self._running = True
-        s = settings
-        if 'video' not in s:
-            LOG.warn("AV.__init__ 'video' not in s" )
-            LOG.debug(f"AV.__init__ {s}" )
-        elif 'device' not in s['video']:
-            LOG.warn("AV.__init__ 'device' not in s.video" )
-            LOG.debug(f"AV.__init__ {s['video']}" )
-
-        self._calls = {}  # dict: key - friend number, value - Call instance
-
-        self._audio = None
-        self._audio_stream = None
-        self._audio_thread = None
-        self._audio_running = False
-        self._out_stream = None
-
-        self._audio_channels = 1
-        self._audio_duration = 60
-        self._audio_rate_pa = 48000
-        self._audio_rate_tox = 48000
-        self._audio_rate_pa = 48000
-        self._audio_krate_tox_audio = self._audio_rate_tox // 1000
-        self._audio_krate_tox_video = 5000
-        self._audio_sample_count_pa = self._audio_rate_pa * self._audio_channels * self._audio_duration // 1000
-        self._audio_sample_count_tox = self._audio_rate_tox * self._audio_channels * self._audio_duration // 1000
-
-        self._video = None
-        self._video_thread = None
-        self._video_running = None
-
-        self._video_width = 320
-        self._video_height = 240
-
-        # was iOutput = self._settings._args.audio['output']
-        iInput = self._settings['audio']['input']
-        self.lPaSampleratesI = ts.lSdSamplerates(iInput)
-        iOutput = self._settings['audio']['output']
-        self.lPaSampleratesO = ts.lSdSamplerates(iOutput)
-
-        global oPYA
-        oPYA = self._audio = pyaudio.PyAudio()
-
-    def stop(self) -> None:
-        LOG_DEBUG(f"AV.CA stop {self._video_thread}")
-        self._running = False
-        self.stop_audio_thread()
-        self.stop_video_thread()
-
-    def __contains__(self, friend_number:int) -> bool:
-        return friend_number in self._calls
-
-    # Calls
-
-    def __call__(self, friend_number, audio, video):
-        """Call friend with specified number"""
-        if friend_number in self._calls:
-            LOG.warn(f"__call__ already has {friend_number}")
-            return
-        if self._audio_krate_tox_audio not in ts.lToxSampleratesK:
-            LOG.warn(f"__call__ {self._audio_krate_tox_audio} not in {ts.lToxSampleratesK}")
-
-        try:
-            self._toxav.call(friend_number,
-                             self._audio_krate_tox_audio if audio else 0,
-                             self._audio_krate_tox_video if video else 0)
-        except Exception as e:
-            LOG.warn(f"_toxav.call already has {friend_number}")
-            return
-        self._calls[friend_number] = Call(audio, video)
-        threading.Timer(TIMER_TIMEOUT,
-                        lambda: self.finish_not_started_call(friend_number)).start()
-
-    def accept_call(self, friend_number, audio_enabled, video_enabled):
-        # obsolete
-        self.call_accept_call(friend_number, audio_enabled, video_enabled)
-
-    def call_accept_call(self, friend_number, audio_enabled, video_enabled) -> None:
-        # called from CM.accept_call in a try:
-        LOG.debug(f"call_accept_call from F={friend_number} R={self._running}" +
-                  f" A={audio_enabled} V={video_enabled}")
-        # import pdb; pdb.set_trace() - gets into q Qt exec_ problem
-        # ts.trepan_handler()
-
-        if self._audio_krate_tox_audio not in ts.lToxSampleratesK:
-            LOG.warn(f"__call__ {self._audio_krate_tox_audio} not in {ts.lToxSampleratesK}")
-        if self._running:
-            self._calls[friend_number] = Call(audio_enabled, video_enabled)
-            # audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending.
-            # video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending.
-            try:
-                self._toxav.answer(friend_number,
-                                   self._audio_krate_tox_audio if audio_enabled else 0,
-                                   self._audio_krate_tox_video if video_enabled else 0)
-            except Exception as e:
-                LOG.error(f"AV accept_call error from {friend_number} {self._running} {e}")
-                raise
-            if video_enabled:
-                # may raise
-                self.start_video_thread()
-            if audio_enabled:
-                LOG.debug(f"calls accept_call calling start_audio_thread F={friend_number}")
-                # may raise
-                self.start_audio_thread()
-
-    def finish_call(self, friend_number, by_friend=False) -> None:
-        LOG.debug(f"finish_call  {friend_number}")
-        if friend_number in self._calls:
-            del self._calls[friend_number]
-        try:
-            # AttributeError: 'int' object has no attribute 'out_audio'
-            if not len(list(filter(lambda c: c.out_audio, self._calls))):
-                self.stop_audio_thread()
-            if not len(list(filter(lambda c: c.out_video, self._calls))):
-                self.stop_video_thread()
-        except Exception as e:
-            LOG.error(f"finish_call FixMe:   {e}")
-            # dunno
-            self.stop_audio_thread()
-            self.stop_video_thread()
-        if not by_friend:
-            LOG.debug(f"finish_call before call_control {friend_number}")
-            self._toxav.call_control(friend_number, TOXAV_CALL_CONTROL['CANCEL'])
-            LOG.debug(f"finish_call after call_control {friend_number}")
-
-    def finish_not_started_call(self, friend_number:int) -> None:
-        if friend_number in self:
-            call = self._calls[friend_number]
-            if not call.is_active:
-                self.finish_call(friend_number)
-
-    def toxav_call_state_cb(self, friend_number, state) -> None:
-        """
-        New call state
-        """
-        LOG.debug(f"toxav_call_state_cb {friend_number}")
-        call = self._calls[friend_number]
-        call.is_active = True
-
-        call.in_audio = state | TOXAV_FRIEND_CALL_STATE['SENDING_A'] > 0
-        call.in_video = state | TOXAV_FRIEND_CALL_STATE['SENDING_V'] > 0
-
-        if state | TOXAV_FRIEND_CALL_STATE['ACCEPTING_A'] and call.out_audio:
-            self.start_audio_thread()
-
-        if state | TOXAV_FRIEND_CALL_STATE['ACCEPTING_V'] and call.out_video:
-            self.start_video_thread()
-
-    def is_video_call(self, number) -> bool:
-        return number in self and self._calls[number].in_video
-
-    # Threads
-
-    def start_audio_thread(self, bSTREAM_CALLBACK=False) -> None:
-        """
-        Start audio sending
-        from a callback
-        """
-        # called from call_accept_call in an try: from CM.accept_call
-        global oPYA
-        # was iInput = self._settings._args.audio['input']
-        iInput = self._settings['audio']['input']
-        if self._audio_thread is not None:
-            LOG_WARN(f"start_audio_thread device={iInput}")
-            return
-        LOG_DEBUG(f"start_audio_thread device={iInput}")
-        lPaSamplerates =  ts.lSdSamplerates(iInput)
-        if not(len(lPaSamplerates)):
-            e = f"No sample rates for device: audio[input]={iInput}"
-            LOG_WARN(f"start_audio_thread {e}")
-            #?? dunno - cancel call? - no let the user do it
-            # return
-            # just guessing  here in case that's a false negative
-            lPaSamplerates = [round(oPYA.get_device_info_by_index(iInput)['defaultSampleRate'])]
-        if lPaSamplerates and self._audio_rate_pa in lPaSamplerates:
-            pass
-        elif lPaSamplerates:
-            LOG_WARN(f"Setting audio_rate to: {lPaSamplerates[0]}")
-            self._audio_rate_pa = lPaSamplerates[0]
-        elif 'defaultSampleRate' in oPYA.get_device_info_by_index(iInput):
-            self._audio_rate_pa = oPYA.get_device_info_by_index(iInput)['defaultSampleRate']
-            LOG_WARN(f"setting to defaultSampleRate")
-        else:
-            LOG_WARN(f"{self._audio_rate_pa} not in {lPaSamplerates}")
-        # a float is in here - must it be int?
-        if type(self._audio_rate_pa) == float:
-            self._audio_rate_pa = round(self._audio_rate_pa)
-        try:
-            if self._audio_rate_pa not in lPaSamplerates:
-                LOG_WARN(f"PAudio sampling rate was {self._audio_rate_pa} changed to {lPaSamplerates[0]}")
-                LOG_DEBUG(f"lPaSamplerates={lPaSamplerates}")
-                self._audio_rate_pa = lPaSamplerates[0]
-            else:
-                LOG_DEBUG( f"start_audio_thread framerate: {self._audio_rate_pa}" \
-                        +f" device: {iInput}"
-                        +f" supported: {lPaSamplerates}")
-
-            if bSTREAM_CALLBACK:
-                # why would you not call a thread?
-                self._audio_stream = oPYA.open(format=pyaudio.paInt16,
-                                               rate=self._audio_rate_pa,
-                                               channels=self._audio_channels,
-                                               input=True,
-                                               input_device_index=iInput,
-                                               frames_per_buffer=self._audio_sample_count_pa * 10,
-                                               stream_callback=self.send_audio_data)
-                self._audio_running = True
-                self._audio_stream.start_stream()
-                while self._audio_stream.is_active():
-                    sleep(0.1)
-                self._audio_stream.stop_stream()
-                self._audio_stream.close()
-            else:
-                LOG_DEBUG( f"start_audio_thread starting thread {self._audio_rate_pa}")
-                self._audio_stream = oPYA.open(format=pyaudio.paInt16,
-                                               rate=self._audio_rate_pa,
-                                               channels=self._audio_channels,
-                                               input=True,
-                                               input_device_index=iInput,
-                                               frames_per_buffer=self._audio_sample_count_pa * 10)
-                self._audio_running = True
-                self._audio_thread = AudioThread(self,
-                                                name='_audio_thread')
-                self._audio_thread.start()
-                LOG_DEBUG( f"start_audio_thread started thread name='_audio_thread'")
-
-        except Exception as e:
-            LOG_ERROR(f"Starting self._audio.open {e}")
-            LOG_DEBUG(repr(dict(format=pyaudio.paInt16,
-                                rate=self._audio_rate_pa,
-                                channels=self._audio_channels,
-                                input=True,
-                                input_device_index=iInput,
-                                frames_per_buffer=self._audio_sample_count_pa * 10)))
-            # catcher in place in calls_manager? yes accept_call
-            # calls_manager._call.toxav_call_state_cb(friend_number, mask)
-            invoke_in_main_thread(util_ui.message_box,
-                                  str(e),
-                                  util_ui.tr("Starting self._audio.open"))
-            return
-        else:
-            LOG_DEBUG(f"start_audio_thread {self._audio_stream}")
-
-    def stop_audio_thread(self) -> None:
-        LOG_DEBUG(f"stop_audio_thread {self._audio_stream}")
-
-        if self._audio_thread is None:
-            return
-        self._audio_running = False
-        self._audio_thread._stop_thread = True
-
-        self._audio_thread = None
-        self._audio_stream = None
-        self._audio = None
-
-        if self._out_stream is not None:
-            self._out_stream.stop_stream()
-            self._out_stream.close()
-            self._out_stream = None
-
-    def start_video_thread(self) -> None:
-        if self._video_thread is not None:
-            return
-        s = self._settings
-        if 'video' not in s:
-            LOG.warn("AV.__init__ 'video' not in s" )
-            LOG.debug(f"start_video_thread {s}" )
-            raise RuntimeError("start_video_thread not 'video' in s)" )
-        if 'device' not in s['video']:
-            LOG.error("start_video_thread not 'device' in s['video']" )
-            LOG.debug(f"start_video_thread {s['video']}" )
-            raise RuntimeError("start_video_thread not 'device' ins s['video']" )
-        self._video_width = s['video']['width']
-        self._video_height = s['video']['height']
-
-        # dunno
-        if s['video']['device'] == -1:
-            self._video = screen_sharing.DesktopGrabber(s['video']['x'],
-                                                        s['video']['y'],
-                                                        s['video']['width'],
-                                                        s['video']['height'])
-        else:
-            with ts.ignoreStdout(): import cv2
-            if s['video']['device'] == 0:
-                # webcam
-                self._video = cv2.VideoCapture(s['video']['device'], cv2.DSHOW)
-            else:
-                self._video = cv2.VideoCapture(s['video']['device'])
-            self._video.set(cv2.CAP_PROP_FPS, iFPS)
-            self._video.set(cv2.CAP_PROP_FRAME_WIDTH, self._video_width)
-            self._video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._video_height)
-#            self._video.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
-        if self._video is None:
-            LOG.error("start_video_thread " \
-                     +f" device: {s['video']['device']}" \
-                     +f" supported: {s['video']['width']} {s['video']['height']}")
-            return
-        LOG.info("start_video_thread " \
-                 +f" device: {s['video']['device']}" \
-                 +f" supported: {s['video']['width']} {s['video']['height']}")
-
-        self._video_running = True
-        self._video_thread = VideoThread(self,
-                                        name='_video_thread')
-        self._video_thread.start()
-
-    def stop_video_thread(self) -> None:
-        LOG_DEBUG(f"stop_video_thread {self._video_thread}")
-        if self._video_thread is None:
-            return
-
-        self._video_thread._stop_thread = True
-        self._video_running = False
-        i = 0
-        while i < ts.iTHREAD_JOINS:
-            self._video_thread.join(ts.iTHREAD_TIMEOUT)
-            try:
-                if not self._video_thread.is_alive(): break
-            except:
-                break
-            i = i + 1
-        else:
-            LOG.warn("self._video_thread.is_alive BLOCKED")
-        self._video_thread = None
-        self._video = None
-    # Incoming chunks
-
-    def audio_chunk(self, samples, channels_count, rate) -> None:
-        """
-        Incoming chunk
-        """
-        # from callback
-        if self._out_stream is None:
-            # was iOutput = self._settings._args.audio['output']
-            iOutput = self._settings['audio']['output']
-            if self.lPaSampleratesO and rate in self.lPaSampleratesO:
-                LOG_DEBUG(f"Using rate {rate} in self.lPaSampleratesO")
-            elif self.lPaSampleratesO:
-                LOG_WARN(f"{rate} not in {self.lPaSampleratesO}")
-                LOG_WARN(f"Setting audio_rate to: {self.lPaSampleratesO[0]}")
-                rate = self.lPaSampleratesO[0]
-            elif 'defaultSampleRate' in oPYA.get_device_info_by_index(iOutput):
-                rate = round(oPYA.get_device_info_by_index(iOutput)['defaultSampleRate'])
-                LOG_WARN(f"Setting rate to {rate} empty self.lPaSampleratesO")
-            else:
-                LOG_WARN(f"Using rate {rate} empty self.lPaSampleratesO")
-            if type(rate) == float:
-                rate = round(rate)
-            # test output device?
-            # [Errno -9985] Device unavailable
-            try:
-                with ts.ignoreStderr():
-                    self._out_stream = oPYA.open(format=pyaudio.paInt16,
-                                                 channels=channels_count,
-                                                 rate=rate,
-                                                 output_device_index=iOutput,
-                                                 output=True)
-            except Exception as e:
-                LOG_ERROR(f"Error playing audio_chunk creating self._out_stream  output_device_index={iOutput} {e}")
-                invoke_in_main_thread(util_ui.message_box,
-                                      str(e),
-                                      util_ui.tr("Error Chunking audio"))
-                # dunno
-                self.stop()
-                return
-
-        iOutput = self._settings['audio']['output']
-#trace        LOG_DEBUG(f"audio_chunk output_device_index={iOutput} rate={rate} channels={channels_count}")
-        try:
-            self._out_stream.write(samples)
-        except Exception as e:
-            # OSError: [Errno -9999] Unanticipated host error
-            LOG_WARN(f"audio_chunk output_device_index={iOutput} {e}")
-
-    # AV sending
-
-    def send_audio_data(self, data, count, *largs, **kwargs) -> None:
-        # callback
-        pcm = data
-        # :param sampling_rate: Audio sampling rate used in this frame.
-        try:
-            if self._toxav is None:
-                LOG_ERROR("_toxav not initialized")
-                return
-            if self._audio_rate_tox not in ts.lToxSamplerates:
-                LOG_WARN(f"ToxAudio sampling rate was {self._audio_rate_tox} changed to {ts.lToxSamplerates[0]}")
-                self._audio_rate_tox = ts.lToxSamplerates[0]
-
-            for friend_num in self._calls:
-                if self._calls[friend_num].out_audio:
-                    # app.av.calls ERROR Error send_audio audio_send_frame: This client is currently not in a call with the friend.
-                    self._toxav.audio_send_frame(friend_num,
-                                                 pcm,
-                                                 count,
-                                                 self._audio_channels,
-                                                 self._audio_rate_tox)
-
-        except Exception as e:
-           LOG.error(f"Error send_audio_data audio_send_frame: {e}")
-           LOG.debug(f"send_audio_data self._audio_rate_tox={self._audio_rate_tox} self._audio_channels={self._audio_channels}")
-           self.stop_audio_thread()
-           invoke_in_main_thread(util_ui.message_box,
-                            str(e),
-                            util_ui.tr("Error send_audio_data audio_send_frame"))
-           #? stop ? endcall?
-
-    def send_audio(self) -> None:
-        """
-        This method sends audio to friends
-        """
-        i=0
-        count = self._audio_sample_count_tox
-        LOG_DEBUG(f"send_audio stream={self._audio_stream}")
-        while self._audio_running:
-            try:
-                pcm = self._audio_stream.read(count, exception_on_overflow=False)
-                if not pcm:
-                    sleep(0.1)
-                else:
-                    self.send_audio_data(pcm, count)
-            except:
-                LOG_DEBUG(f"error send_audio {i}")
-            else:
-                LOG_TRACE(f"send_audio {i}")
-            i += 1
-            sleep(0.01)
-
-    def send_video(self) -> None:
-        """
-        This method sends video to friends
-        """
-#        LOG_DEBUG(f"send_video thread={threading.current_thread().name}"
-#                  +f" self._video_running={self._video_running}"
-#                  +f" device: {self._settings['video']['device']}" )
-        while self._video_running:
-            try:
-                result, frame = self._video.read()
-                if not result:
-                    LOG_WARN(f"send_video video_send_frame _video.read result={result}")
-                    break
-                if frame is None:
-                    LOG_WARN(f"send_video video_send_frame _video.read result={result} frame={frame}")
-                    continue
-
-                LOG_TRACE(f"send_video video_send_frame _video.read result={result}")
-                height, width, channels = frame.shape
-                friends = []
-                for friend_num in self._calls:
-                    if self._calls[friend_num].out_video:
-                        friends.append(friend_num)
-                if len(friends) == 0:
-                    LOG_WARN(f"send_video video_send_frame no friends")
-                else:
-                    LOG_TRACE(f"send_video video_send_frame {friends}")
-                    friend_num = friends[0]
-                    try:
-                        y, u, v = self.convert_bgr_to_yuv(frame)
-                        self._toxav.video_send_frame(friend_num, width, height, y, u, v)
-                    except Exception as e:
-                        LOG_WARN(f"send_video video_send_frame ERROR {e}")
-                        pass
-
-            except Exception as e:
-                LOG_ERROR(f"send_video video_send_frame {e}")
-                pass
-
-            sleep( 1.0/iFPS)
-
-    def convert_bgr_to_yuv(self, frame) -> tuple:
-        """
-        :param frame: input bgr frame
-        :return y, u, v: y, u, v values of frame
-
-        How this function works:
-        OpenCV creates YUV420 frame from BGR
-        This frame has following structure and size:
-        width, height - dim of input frame
-        width, height * 1.5 - dim of output frame
-
-                  width
-        -------------------------
-        |                       |
-        |          Y            |      height
-        |                       |
-        -------------------------
-        |           |           |
-        |  U even   |   U odd   |      height // 4
-        |           |           |
-        -------------------------
-        |           |           |
-        |  V even   |   V odd   |      height // 4
-        |           |           |
-        -------------------------
-
-         width // 2   width // 2
-
-        Y, U, V can be extracted using slices and joined in one list using itertools.chain.from_iterable()
-        Function returns bytes(y), bytes(u), bytes(v), because it is required for ctypes
-        """
-        with ts.ignoreStdout():
-            import cv2
-        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV_I420)
-
-        y = frame[:self._video_height, :]
-        y = list(itertools.chain.from_iterable(y))
-
-        import numpy as np
-        # was np.int
-        u = np.zeros((self._video_height // 2, self._video_width // 2), dtype=np.int32)
-        u[::2, :] = frame[self._video_height:self._video_height * 5 // 4, :self._video_width // 2]
-        u[1::2, :] = frame[self._video_height:self._video_height * 5 // 4, self._video_width // 2:]
-        u = list(itertools.chain.from_iterable(u))
-        v = np.zeros((self._video_height // 2, self._video_width // 2), dtype=np.int32)
-        v[::2, :] = frame[self._video_height * 5 // 4:, :self._video_width // 2]
-        v[1::2, :] = frame[self._video_height * 5 // 4:, self._video_width // 2:]
-        v = list(itertools.chain.from_iterable(v))
-
-        return bytes(y), bytes(u), bytes(v)
diff --git a/toxygen/av/calls_manager.py b/toxygen/av/calls_manager.py
deleted file mode 100644
index d0d6683..0000000
--- a/toxygen/av/calls_manager.py
+++ /dev/null
@@ -1,184 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import sys
-import threading
-import traceback
-import logging
-
-from  qtpy import QtCore
-
-import av.calls
-from messenger.messages import *
-from ui import av_widgets
-import common.event as event
-import utils.ui as util_ui
-from toxygen_wrapper.tests import support_testing as ts
-
-global LOG
-LOG = logging.getLogger('app.'+__name__)
-
-class CallsManager:
-
-    def __init__(self, toxav, settings, main_screen, contacts_manager, app=None):
-        self._callav = av.calls.AV(toxav, settings)  # object with data about calls
-        self._call = self._callav
-        self._call_widgets = {}  # dict of incoming call widgets
-        self._incoming_calls = set()
-        self._settings = settings
-        self._main_screen = main_screen
-        self._contacts_manager = contacts_manager
-        self._call_started_event = event.Event()  # friend_number, audio, video, is_outgoing
-        self._call_finished_event = event.Event()  # friend_number, is_declined
-        self._app = app
-
-    def set_toxav(self, toxav) -> None:
-        self._callav.set_toxav(toxav)
-
-    # Events
-
-    def get_call_started_event(self):
-        return self._call_started_event
-
-    call_started_event = property(get_call_started_event)
-
-    def get_call_finished_event(self):
-        return self._call_finished_event
-
-    call_finished_event = property(get_call_finished_event)
-
-    # AV support
-
-    def call_click(self, audio=True, video=False) -> None:
-        """User clicked audio button in main window"""
-        num = self._contacts_manager.get_active_number()
-        if not self._contacts_manager.is_active_a_friend():
-            return
-        if num not in self._callav and self._contacts_manager.is_active_online():  # start call
-            if not self._settings['audio']['enabled']:
-                return
-            self._callav(num, audio, video)
-            self._main_screen.active_call()
-            self._call_started_event(num, audio, video, True)
-        elif num in self._callav:  # finish or cancel call if you call with active friend
-            self.stop_call(num, False)
-
-    def incoming_call(self, audio, video, friend_number) -> None:
-        """
-        Incoming call from friend.
-        """
-        LOG.debug(f"CM incoming_call  {friend_number}")
-        # if not self._settings['audio']['enabled']: return
-        friend = self._contacts_manager.get_friend_by_number(friend_number)
-        self._call_started_event(friend_number, audio, video, False)
-        self._incoming_calls.add(friend_number)
-        if friend_number == self._contacts_manager.get_active_number():
-            self._main_screen.incoming_call()
-        else:
-            friend.actions = True
-        text = util_ui.tr("Incoming video call") if video else util_ui.tr("Incoming audio call")
-        self._call_widgets[friend_number] = self._get_incoming_call_widget(friend_number, text, friend.name)
-        self._call_widgets[friend_number].set_pixmap(friend.get_pixmap())
-        self._call_widgets[friend_number].show()
-
-    def accept_call(self, friend_number, audio, video) -> None:
-        """
-        Accept incoming call with audio or video
-        Called from a thread
-        """
-
-        LOG.debug(f"CM accept_call from friend_number={friend_number} {audio} {video}")
-        sys.stdout.flush()
-
-        try:
-            self._main_screen.active_call()
-            # failsafe added somewhere this was being left up
-            self.close_call(friend_number)
-            QtCore.QCoreApplication.processEvents()
-
-            self._callav.call_accept_call(friend_number, audio, video)
-            LOG.debug(f"accept_call _call.accept_call CALLED f={friend_number}")
-        except Exception as e:
-            #
-            LOG.error(f"accept_call _call.accept_call ERROR for {friend_number} {e}")
-            LOG.debug(traceback.print_exc())
-            self._main_screen.call_finished()
-            if hasattr(self._main_screen, '_settings') and \
-              'audio' in self._main_screen._settings and \
-              'input' in self._main_screen._settings['audio']:
-                iInput = self._settings['audio']['input']
-                iOutput = self._settings['audio']['output']
-                iVideo = self._settings['video']['device']
-                LOG.debug(f"iInput={iInput} iOutput={iOutput} iVideo={iVideo}")
-            elif hasattr(self._main_screen, '_settings') and \
-              hasattr(self._main_screen._settings, 'audio') and \
-              'input' not in self._main_screen._settings['audio']:
-                LOG.warn(f"'audio' not in {self._main_screen._settings}")
-            elif hasattr(self._main_screen, '_settings') and \
-              hasattr(self._main_screen._settings, 'audio') and \
-              'input' not in self._main_screen._settings['audio']:
-                LOG.warn(f"'audio' not in {self._main_screen._settings}")
-            else:
-                LOG.warn(f"_settings not in self._main_screen")
-            util_ui.message_box(str(e),
-                            util_ui.tr('ERROR Accepting call from {friend_number}'))
-        finally:
-            # does not terminate call - just the av_widget
-            LOG.debug(f"CM.accept_call close av_widget")
-            self.close_call(friend_number)
-            LOG.debug(f" closed self._call_widgets[{friend_number}]")
-
-    def close_call(self, friend_number:int) -> None:
-        # refactored out from above because the accept window not getting
-        # taken down in some accept audio calls
-        LOG.debug(f"close_call {friend_number}")
-        try:
-            if friend_number in self._call_widgets:
-                self._call_widgets[friend_number].close()
-                del self._call_widgets[friend_number]
-            if friend_number in self._incoming_calls:
-                self._incoming_calls.remove(friend_number)
-        except Exception as e:
-            # RuntimeError: wrapped C/C++ object of type IncomingCallWidget has been deleted
-
-            LOG.warn(f" closed self._call_widgets[{friend_number}] {e}")
-        # invoke_in_main_thread(QtCore.QCoreApplication.processEvents)
-        QtCore.QCoreApplication.processEvents()
-
-
-    def stop_call(self, friend_number, by_friend) -> None:
-        """
-        Stop call with friend
-        """
-        LOG.debug(f"CM.stop_call friend={friend_number}")
-        if friend_number in self._incoming_calls:
-            self._incoming_calls.remove(friend_number)
-            is_declined = True
-        else:
-            is_declined = False
-        if friend_number in self._call_widgets:
-            LOG.debug(f"CM.stop_call _call_widgets close")
-            self.close_call(friend_number)
-
-        LOG.debug(f"CM.stop_call _main_screen.call_finished")
-        self._main_screen.call_finished()
-        self._callav.finish_call(friend_number, by_friend)  # finish or decline call
-        is_video = self._callav.is_video_call(friend_number)
-        if is_video:
-            def destroy_window():
-                #??? FixMe
-                with ts.ignoreStdout(): import cv2
-                cv2.destroyWindow(str(friend_number))
-            LOG.debug(f"CM.stop_call destroy_window")
-            threading.Timer(2.0, destroy_window).start()
-
-        LOG.debug(f"CM.stop_call _call_finished_event")
-        self._call_finished_event(friend_number, is_declined)
-
-    def friend_exit(self, friend_number:int) -> None:
-        if friend_number in self._callav:
-            self._callav.finish_call(friend_number, True)
-
-    # Private methods
-
-    def _get_incoming_call_widget(self, friend_number, text, friend_name):
-        return av_widgets.IncomingCallWidget(self._settings, self, friend_number, text, friend_name)
diff --git a/toxygen/av/screen_sharing.py b/toxygen/av/screen_sharing.py
deleted file mode 100644
index e0f783b..0000000
--- a/toxygen/av/screen_sharing.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import QtWidgets
-
-class DesktopGrabber:
-
-    def __init__(self, x, y, width, height):
-        self._x = x
-        self._y = y
-        self._width = width
-        self._height = height
-        self._width -= width % 4
-        self._height -= height % 4
-        self._screen = QtWidgets.QApplication.primaryScreen()
-
-    def read(self) -> tuple:
-        pixmap = self._screen.grabWindow(0, self._x, self._y, self._width, self._height)
-        image = pixmap.toImage()
-        s = image.bits().asstring(self._width * self._height * 4)
-        import numpy as np
-        arr = np.fromstring(s, dtype=np.uint8).reshape((self._height, self._width, 4))
-
-        return True, arr
diff --git a/toxygen/avwidgets.py b/toxygen/avwidgets.py
new file mode 100644
index 0000000..511fd8c
--- /dev/null
+++ b/toxygen/avwidgets.py
@@ -0,0 +1,139 @@
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+import widgets
+import profile
+import util
+import pyaudio
+import wave
+import settings
+from util import curr_directory
+
+
+class IncomingCallWidget(widgets.CenteredWidget):
+
+    def __init__(self, friend_number, text, name):
+        super(IncomingCallWidget, self).__init__()
+        self.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowStaysOnTopHint)
+        self.resize(QtCore.QSize(500, 270))
+        self.avatar_label = QtGui.QLabel(self)
+        self.avatar_label.setGeometry(QtCore.QRect(10, 20, 64, 64))
+        self.avatar_label.setScaledContents(False)
+        self.name = widgets.DataLabel(self)
+        self.name.setGeometry(QtCore.QRect(90, 20, 300, 25))
+        font = QtGui.QFont()
+        font.setFamily("Times New Roman")
+        font.setPointSize(16)
+        font.setBold(True)
+        self.name.setFont(font)
+        self.call_type = widgets.DataLabel(self)
+        self.call_type.setGeometry(QtCore.QRect(90, 55, 300, 25))
+        self.call_type.setFont(font)
+        self.accept_audio = QtGui.QPushButton(self)
+        self.accept_audio.setGeometry(QtCore.QRect(20, 100, 150, 150))
+        self.accept_video = QtGui.QPushButton(self)
+        self.accept_video.setGeometry(QtCore.QRect(170, 100, 150, 150))
+        self.decline = QtGui.QPushButton(self)
+        self.decline.setGeometry(QtCore.QRect(320, 100, 150, 150))
+        pixmap = QtGui.QPixmap(util.curr_directory() + '/images/accept_audio.png')
+        icon = QtGui.QIcon(pixmap)
+        self.accept_audio.setIcon(icon)
+        pixmap = QtGui.QPixmap(util.curr_directory() + '/images/accept_video.png')
+        icon = QtGui.QIcon(pixmap)
+        self.accept_video.setIcon(icon)
+        pixmap = QtGui.QPixmap(util.curr_directory() + '/images/decline_call.png')
+        icon = QtGui.QIcon(pixmap)
+        self.decline.setIcon(icon)
+        self.accept_audio.setIconSize(QtCore.QSize(150, 150))
+        self.accept_video.setIconSize(QtCore.QSize(140, 140))
+        self.decline.setIconSize(QtCore.QSize(140, 140))
+        self.accept_audio.setStyleSheet("QPushButton { border: none }")
+        self.accept_video.setStyleSheet("QPushButton { border: none }")
+        self.decline.setStyleSheet("QPushButton { border: none }")
+        self.setWindowTitle(text)
+        self.name.setText(name)
+        self.call_type.setText(text)
+        pr = profile.Profile.get_instance()
+        self.accept_audio.clicked.connect(lambda: pr.accept_call(friend_number, True, False) or self.stop())
+        # self.accept_video.clicked.connect(lambda: pr.start_call(friend_number, True, True))
+        self.decline.clicked.connect(lambda: pr.stop_call(friend_number, False) or self.stop())
+
+        class SoundPlay(QtCore.QThread):
+
+            def __init__(self):
+                QtCore.QThread.__init__(self)
+
+            def run(self):
+                class AudioFile:
+                    chunk = 1024
+
+                    def __init__(self, fl):
+                        self.stop = False
+                        self.fl = fl
+                        self.wf = wave.open(self.fl, 'rb')
+                        self.p = pyaudio.PyAudio()
+                        self.stream = self.p.open(
+                            format=self.p.get_format_from_width(self.wf.getsampwidth()),
+                            channels=self.wf.getnchannels(),
+                            rate=self.wf.getframerate(),
+                            output=True)
+
+                    def play(self):
+                        while not self.stop:
+                            data = self.wf.readframes(self.chunk)
+                            while data and not self.stop:
+                                self.stream.write(data)
+                                data = self.wf.readframes(self.chunk)
+                            self.wf = wave.open(self.fl, 'rb')
+
+                    def close(self):
+                        self.stream.close()
+                        self.p.terminate()
+
+                self.a = AudioFile(curr_directory() + '/sounds/call.wav')
+                self.a.play()
+                self.a.close()
+
+        if settings.Settings.get_instance()['calls_sound']:
+            self.thread = SoundPlay()
+            self.thread.start()
+        else:
+            self.thread = None
+
+    def stop(self):
+        if self.thread is not None:
+            self.thread.a.stop = True
+            self.thread.wait()
+        self.close()
+
+    def set_pixmap(self, pixmap):
+        self.avatar_label.setPixmap(pixmap)
+
+
+class AudioMessageRecorder(widgets.CenteredWidget):
+
+    def __init__(self, friend_number, name):
+        super(AudioMessageRecorder, self).__init__()
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(10, 20, 250, 20))
+        text = QtGui.QApplication.translate("MenuWindow", "Send audio message to friend {}", None, QtGui.QApplication.UnicodeUTF8)
+        self.label.setText(text.format(name))
+        self.record = QtGui.QPushButton(self)
+        self.record.setGeometry(QtCore.QRect(20, 100, 150, 150))
+
+        self.record.setText(QtGui.QApplication.translate("MenuWindow", "Start recording", None,
+                                                         QtGui.QApplication.UnicodeUTF8))
+        self.record.clicked.connect(self.start_or_stop_recording)
+        self.recording = False
+        self.friend_num = friend_number
+
+    def start_or_stop_recording(self):
+        if not self.recording:
+            self.recording = True
+            self.record.setText(QtGui.QApplication.translate("MenuWindow", "Stop recording", None,
+                                                             QtGui.QApplication.UnicodeUTF8))
+        else:
+            self.close()
+
+
diff --git a/toxygen/basecontact.py b/toxygen/basecontact.py
new file mode 100644
index 0000000..b0accb3
--- /dev/null
+++ b/toxygen/basecontact.py
@@ -0,0 +1,114 @@
+import os
+from settings import *
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+from toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE
+
+
+class BaseContact:
+    """
+    Class encapsulating TOX contact
+    Properties: name (alias of contact or name), status_message, status (connection status)
+    widget - widget for update
+    """
+
+    def __init__(self, name, status_message, widget, tox_id):
+        """
+        :param name: name, example: 'Toxygen user'
+        :param status_message: status message, example: 'Toxing on Toxygen'
+        :param widget: ContactItem instance
+        :param tox_id: tox id of contact
+        """
+        self._name, self._status_message = name, status_message
+        self._status, self._widget = None, widget
+        self._widget.name.setText(name)
+        self._widget.status_message.setText(status_message)
+        self._tox_id = tox_id
+        self.load_avatar()
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Name - current name or alias of user
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_name(self):
+        return self._name
+
+    def set_name(self, value):
+        self._name = str(value, 'utf-8')
+        self._widget.name.setText(self._name)
+        self._widget.name.repaint()
+
+    name = property(get_name, set_name)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Status message or group topic
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_status_message(self):
+        return self._status_message
+
+    def set_status_message(self, value):
+        self._status_message = str(value, 'utf-8')
+        self._widget.status_message.setText(self._status_message)
+        self._widget.status_message.repaint()
+
+    status_message = property(get_status_message, set_status_message)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Status
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_status(self):
+        return self._status
+
+    def set_status(self, value):
+        self._status = value
+        self._widget.connection_status.update(value)
+
+    status = property(get_status, set_status)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # TOX ID. WARNING: for friend it will return public key, for profile - full address
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_tox_id(self):
+        return self._tox_id
+
+    tox_id = property(get_tox_id)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Avatars
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def load_avatar(self, default_path='avatar.png'):
+        """
+        Tries to load avatar of contact or uses default avatar
+        """
+        avatar_path = '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
+        os.chdir(ProfileHelper.get_path() + 'avatars/')
+        if not os.path.isfile(avatar_path):  # load default image
+            avatar_path = default_path
+            os.chdir(curr_directory() + '/images/')
+        width = self._widget.avatar_label.width()
+        pixmap = QtGui.QPixmap(QtCore.QSize(width, width))
+        pixmap.load(avatar_path)
+        self._widget.avatar_label.setScaledContents(False)
+        self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio))
+        self._widget.avatar_label.repaint()
+
+    def reset_avatar(self):
+        avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
+        if os.path.isfile(avatar_path):
+            os.remove(avatar_path)
+            self.load_avatar()
+
+    def set_avatar(self, avatar):
+        avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
+        with open(avatar_path, 'wb') as f:
+            f.write(avatar)
+        self.load_avatar()
+
+    def get_pixmap(self):
+        return self._widget.avatar_label.pixmap()
diff --git a/toxygen/bootstrap.py b/toxygen/bootstrap.py
new file mode 100644
index 0000000..89534b7
--- /dev/null
+++ b/toxygen/bootstrap.py
@@ -0,0 +1,83 @@
+import random
+
+
+class Node:
+    def __init__(self, ip, port, tox_key, rand):
+        self._ip, self._port, self._tox_key, self.rand = ip, port, tox_key, rand
+
+    def get_data(self):
+        return bytes(self._ip, 'utf-8'), self._port, self._tox_key
+
+
+def node_generator():
+    nodes = []
+    ips = [
+        "144.76.60.215", "23.226.230.47", "195.154.119.113", "biribiri.org",
+        "46.38.239.179", "178.62.250.138", "130.133.110.14", "104.167.101.29",
+        "205.185.116.116", "198.98.51.198", "80.232.246.79", "108.61.165.198",
+        "212.71.252.109", "194.249.212.109", "185.25.116.107", "192.99.168.140",
+        "46.101.197.175", "95.215.46.114", "5.189.176.217", "148.251.23.146",
+        "104.223.122.15", "78.47.114.252", "d4rk4.ru", "81.4.110.149",
+        "95.31.20.151", "104.233.104.126", "51.254.84.212", "home.vikingmakt.com.br",
+        "5.135.59.163", "185.58.206.164", "188.244.38.183", "mrflibble.c4.ee",
+        "82.211.31.116", "128.199.199.197", "103.230.156.174", "91.121.66.124",
+        "92.54.84.70", "tox1.privacydragon.me"
+    ]
+    ports = [
+        33445, 33445, 33445, 33445,
+        33445, 33445, 33445, 33445,
+        33445, 33445, 33445, 33445,
+        33445, 33445, 33445, 33445,
+        443, 33445, 5190, 2306,
+        33445, 33445, 1813, 33445,
+        33445, 33445, 33445, 33445,
+        33445, 33445, 33445, 33445,
+        33445, 33445, 33445, 33445,
+        33445, 33445
+    ]
+    ids = [
+        "04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F",
+        "A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074",
+        "E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354",
+        "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67",
+        "F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A",
+        "788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B",
+        "461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F",
+        "5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57",
+        "A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702",
+        "1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F",
+        "CF6CECA0A14A31717CC8501DA51BE27742B70746956E6676FF423A529F91ED5D",
+        "8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832",
+        "C4CEB8C7AC607C6B374E2E782B3C00EA3A63B80D4910B8649CCACDD19F260819",
+        "3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B",
+        "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43",
+        "6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F",
+        "CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707",
+        "5823FB947FF24CF83DDFAC3F3BAA18F96EA2018B16CC08429CB97FA502F40C23",
+        "2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F",
+        "7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147",
+        "0FB96EEBFB1650DDB52E70CF773DDFCABE25A95CC3BB50FC251082E4B63EF82A",
+        "1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976",
+        "53737F6D47FA6BD2808F378E339AF45BF86F39B64E79D6D491C53A1D522E7039",
+        "9E7BD4793FFECA7F32238FA2361040C09025ED3333744483CA6F3039BFF0211E",
+        "9CA69BB74DE7C056D1CC6B16AB8A0A38725C0349D187D8996766958584D39340",
+        "EDEE8F2E839A57820DE3DA4156D88350E53D4161447068A3457EE8F59F362414",
+        "AEC204B9A4501412D5F0BB67D9C81B5DB3EE6ADA64122D32A3E9B093D544327D",
+        "188E072676404ED833A4E947DC1D223DF8EFEBE5F5258573A236573688FB9761",
+        "2D320F971EF2CA18004416C2AAE7BA52BF7949DB34EA8E2E21AF67BD367BE211",
+        "24156472041E5F220D1FA11D9DF32F7AD697D59845701CDD7BE7D1785EB9DB39",
+        "15A0F9684E2423F9F46CFA5A50B562AE42525580D840CC50E518192BF333EE38",
+        "FAAB17014F42F7F20949F61E55F66A73C230876812A9737F5F6D2DCE4D9E4207",
+        "AF97B76392A6474AF2FD269220FDCF4127D86A42EF3A242DF53A40A268A2CD7C",
+        "B05C8869DBB4EDDD308F43C1A974A20A725A36EACCA123862FDE9945BF9D3E09",
+        "5C4C7A60183D668E5BD8B3780D1288203E2F1BAE4EEF03278019E21F86174C1D",
+        "4E3F7D37295664BBD0741B6DBCB6431D6CD77FC4105338C2FC31567BF5C8224A",
+        "5625A62618CB4FCA70E147A71B29695F38CC65FF0CBD68AD46254585BE564802",
+        "31910C0497D347FF160D6F3A6C0E317BAFA71E8E03BC4CBB2A185C9D4FB8B31E"
+    ]
+    for i in range(len(ips)):
+        nodes.append(Node(ips[i], ports[i], ids[i], random.randint(0, 1000000)))
+    arr = sorted(nodes, key=lambda x: x.rand)[:4]
+    for elem in arr:
+        yield elem.get_data()
+
diff --git a/toxygen/bootstrap/__init__.py b/toxygen/bootstrap/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/bootstrap/bootstrap.py b/toxygen/bootstrap/bootstrap.py
deleted file mode 100644
index 6d64783..0000000
--- a/toxygen/bootstrap/bootstrap.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import random
-import logging
-
-from  qtpy import QtCore
-try:
-    import certifi
-    from io import BytesIO
-except ImportError:
-    certifi = None
-
-from user_data.settings import get_user_config_path
-from utils.util import *
-
-from toxygen_wrapper.tests.support_testing import _get_nodes_path
-from toxygen_wrapper.tests.support_http import download_url
-import toxygen_wrapper.tests.support_testing as ts
-
-global LOG
-LOG = logging.getLogger('app.'+'bootstrap')
-
-def download_nodes_list(settings, oArgs) -> str:
-    if not settings['download_nodes_list']:
-        return ''
-    if not ts.bAreWeConnected():
-        return ''
-    url = settings['download_nodes_url']
-    path = _get_nodes_path(oArgs=oArgs)
-    # dont download blindly so we can edit the file and not block on startup
-    if os.path.isfile(path):
-        with open(path, 'rt') as fl:
-            result = fl.read()
-            return result
-    LOG.debug("downloading list of nodes")
-    result = download_url(url, settings._app._settings)
-    if not result:
-        LOG.warn("failed downloading list of nodes")
-        return ''
-    LOG.info("downloaded list of nodes")
-    _save_nodes(result, settings._app)
-    return result
-
-def _save_nodes(nodes, app) -> None:
-    if not nodes:
-        return
-    with open(_get_nodes_path(app._args), 'wb') as fl:
-        LOG.info("Saving nodes to " +_get_nodes_path(app._args))
-        fl.write(nodes)
diff --git a/toxygen/bootstrap/nodes.json b/toxygen/bootstrap/nodes.json
deleted file mode 100644
index 5314998..0000000
--- a/toxygen/bootstrap/nodes.json
+++ /dev/null
@@ -1 +0,0 @@
-{"nodes":[{"ipv4":"80.211.19.83","ipv6":"-","port":33445,"public_key":"A2D7BF17C10A12C339B9F4E8DD77DEEE8457D580535A6F0D0F9AF04B8B4C4420","status_udp":true,"status_tcp":true}]}
\ No newline at end of file
diff --git a/toxygen/callbacks.py b/toxygen/callbacks.py
new file mode 100644
index 0000000..450df1e
--- /dev/null
+++ b/toxygen/callbacks.py
@@ -0,0 +1,373 @@
+try:
+    from PySide import QtCore
+except ImportError:
+    from PyQt4 import QtCore
+from notifications import *
+from settings import Settings
+from profile import Profile
+from toxcore_enums_and_consts import *
+from toxav_enums import *
+from tox import bin_to_string
+from plugin_support import PluginLoader
+
+
+class InvokeEvent(QtCore.QEvent):
+    EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
+
+    def __init__(self, fn, *args, **kwargs):
+        QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
+        self.fn = fn
+        self.args = args
+        self.kwargs = kwargs
+
+
+class Invoker(QtCore.QObject):
+
+    def event(self, event):
+        event.fn(*event.args, **event.kwargs)
+        return True
+
+_invoker = Invoker()
+
+
+def invoke_in_main_thread(fn, *args, **kwargs):
+    QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
+
+# -----------------------------------------------------------------------------------------------------------------
+# Callbacks - current user
+# -----------------------------------------------------------------------------------------------------------------
+
+
+def self_connection_status(tox_link):
+    """
+    Current user changed connection status (offline, UDP, TCP)
+    """
+    def wrapped(tox, connection, user_data):
+        print('Connection status: ', str(connection))
+        profile = Profile.get_instance()
+        if profile.status is None:
+            status = tox_link.self_get_status()
+            invoke_in_main_thread(profile.set_status, status)
+        elif connection == TOX_CONNECTION['NONE']:
+            invoke_in_main_thread(profile.set_status, None)
+    return wrapped
+
+
+# -----------------------------------------------------------------------------------------------------------------
+# Callbacks - friends
+# -----------------------------------------------------------------------------------------------------------------
+
+
+def friend_status(tox, friend_num, new_status, user_data):
+    """
+    Check friend's status (none, busy, away)
+    """
+    print("Friend's #{} status changed!".format(friend_num))
+    profile = Profile.get_instance()
+    friend = profile.get_friend_by_number(friend_num)
+    if friend.status is None and Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
+        sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
+    invoke_in_main_thread(friend.set_status, new_status)
+    invoke_in_main_thread(profile.send_files, friend_num)
+    invoke_in_main_thread(profile.update_filtration)
+
+
+def friend_connection_status(tox, friend_num, new_status, user_data):
+    """
+    Check friend's connection status (offline, udp, tcp)
+    """
+    print("Friend #{} connection status: {}".format(friend_num, new_status))
+    profile = Profile.get_instance()
+    friend = profile.get_friend_by_number(friend_num)
+    if new_status == TOX_CONNECTION['NONE']:
+        invoke_in_main_thread(profile.friend_exit, friend_num)
+        invoke_in_main_thread(profile.update_filtration)
+        if Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
+            sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
+    elif friend.status is None:
+        invoke_in_main_thread(profile.send_avatar, friend_num)
+        invoke_in_main_thread(PluginLoader.get_instance().friend_online, friend_num)
+
+
+def friend_name(tox, friend_num, name, size, user_data):
+    """
+    Friend changed his name
+    """
+    profile = Profile.get_instance()
+    print('New name friend #' + str(friend_num))
+    invoke_in_main_thread(profile.new_name, friend_num, name)
+
+
+def friend_status_message(tox, friend_num, status_message, size, user_data):
+    """
+    :return: function for callback friend_status_message. It updates friend's status message
+    and calls window repaint
+    """
+    profile = Profile.get_instance()
+    friend = profile.get_friend_by_number(friend_num)
+    invoke_in_main_thread(friend.set_status_message, status_message)
+    print('User #{} has new status'.format(friend_num))
+    invoke_in_main_thread(profile.send_messages, friend_num)
+    if profile.get_active_number() == friend_num:
+        invoke_in_main_thread(profile.set_active)
+
+
+def friend_message(window, tray):
+    """
+    New message from friend
+    """
+    def wrapped(tox, friend_number, message_type, message, size, user_data):
+        profile = Profile.get_instance()
+        settings = Settings.get_instance()
+        message = str(message, 'utf-8')
+        invoke_in_main_thread(profile.new_message, friend_number, message_type, message)
+        if not window.isActiveWindow():
+            friend = profile.get_friend_by_number(friend_number)
+            if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked:
+                invoke_in_main_thread(tray_notification, friend.name, message, tray, window)
+            if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
+                sound_notification(SOUND_NOTIFICATION['MESSAGE'])
+            invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
+    return wrapped
+
+
+def friend_request(tox, public_key, message, message_size, user_data):
+    """
+    Called when user get new friend request
+    """
+    print('Friend request')
+    profile = Profile.get_instance()
+    key = ''.join(chr(x) for x in public_key[:TOX_PUBLIC_KEY_SIZE])
+    tox_id = bin_to_string(key, TOX_PUBLIC_KEY_SIZE)
+    if tox_id not in Settings.get_instance()['blocked']:
+        invoke_in_main_thread(profile.process_friend_request, tox_id, str(message, 'utf-8'))
+
+
+def friend_typing(tox, friend_number, typing, user_data):
+    invoke_in_main_thread(Profile.get_instance().friend_typing, friend_number, typing)
+
+
+def friend_read_receipt(tox, friend_number, message_id, user_data):
+    profile = Profile.get_instance()
+    profile.get_friend_by_number(friend_number).dec_receipt()
+    if friend_number == profile.get_active_number():
+        invoke_in_main_thread(profile.receipt)
+
+# -----------------------------------------------------------------------------------------------------------------
+# Callbacks - file transfers
+# -----------------------------------------------------------------------------------------------------------------
+
+
+def tox_file_recv(window, tray):
+    """
+    New incoming file
+    """
+    def wrapped(tox, friend_number, file_number, file_type, size, file_name, file_name_size, user_data):
+        profile = Profile.get_instance()
+        settings = Settings.get_instance()
+        if file_type == TOX_FILE_KIND['DATA']:
+            print('File')
+            try:
+                file_name = str(file_name[:file_name_size], 'utf-8')
+            except:
+                file_name = 'toxygen_file'
+            invoke_in_main_thread(profile.incoming_file_transfer,
+                                  friend_number,
+                                  file_number,
+                                  size,
+                                  file_name)
+            if not window.isActiveWindow():
+                friend = profile.get_friend_by_number(friend_number)
+                if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked:
+                    file_from = QtGui.QApplication.translate("Callback", "File from", None, QtGui.QApplication.UnicodeUTF8)
+                    invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window)
+                if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
+                    sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER'])
+                invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
+        else:  # AVATAR
+            print('Avatar')
+            invoke_in_main_thread(profile.incoming_avatar,
+                                  friend_number,
+                                  file_number,
+                                  size)
+    return wrapped
+
+
+def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data):
+    """
+    Incoming chunk
+    """
+    if not length:
+        invoke_in_main_thread(Profile.get_instance().incoming_chunk,
+                              friend_number,
+                              file_number,
+                              position,
+                              None)
+    else:
+        Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk[:length])
+
+
+def file_chunk_request(tox, friend_number, file_number, position, size, user_data):
+    """
+    Outgoing chunk
+    """
+    if size:
+        Profile.get_instance().outgoing_chunk(friend_number, file_number, position, size)
+    else:
+        invoke_in_main_thread(Profile.get_instance().outgoing_chunk,
+                              friend_number,
+                              file_number,
+                              position,
+                              size)
+
+
+def file_recv_control(tox, friend_number, file_number, file_control, user_data):
+    """
+    Friend cancelled, paused or resumed file transfer
+    """
+    if file_control == TOX_FILE_CONTROL['CANCEL']:
+        invoke_in_main_thread(Profile.get_instance().cancel_transfer, friend_number, file_number, True)
+    elif file_control == TOX_FILE_CONTROL['PAUSE']:
+        invoke_in_main_thread(Profile.get_instance().pause_transfer, friend_number, file_number, True)
+    elif file_control == TOX_FILE_CONTROL['RESUME']:
+        invoke_in_main_thread(Profile.get_instance().resume_transfer, friend_number, file_number, True)
+
+# -----------------------------------------------------------------------------------------------------------------
+# Callbacks - custom packets
+# -----------------------------------------------------------------------------------------------------------------
+
+
+def lossless_packet(tox, friend_number, data, length, user_data):
+    """
+    Incoming lossless packet
+    """
+    plugin = PluginLoader.get_instance()
+    invoke_in_main_thread(plugin.callback_lossless, friend_number, data, length)
+
+
+def lossy_packet(tox, friend_number, data, length, user_data):
+    """
+    Incoming lossy packet
+    """
+    plugin = PluginLoader.get_instance()
+    invoke_in_main_thread(plugin.callback_lossy, friend_number, data, length)
+
+
+# -----------------------------------------------------------------------------------------------------------------
+# Callbacks - audio
+# -----------------------------------------------------------------------------------------------------------------
+
+def call_state(toxav, friend_number, mask, user_data):
+    """
+    New call state
+    """
+    print(friend_number, mask)
+    if mask == TOXAV_FRIEND_CALL_STATE['FINISHED'] or mask == TOXAV_FRIEND_CALL_STATE['ERROR']:
+        invoke_in_main_thread(Profile.get_instance().stop_call, friend_number, True)
+    else:
+        Profile.get_instance().call.toxav_call_state_cb(friend_number, mask)
+
+
+def call(toxav, friend_number, audio, video, user_data):
+    """
+    Incoming call from friend
+    """
+    print(friend_number, audio, video)
+    invoke_in_main_thread(Profile.get_instance().incoming_call, audio, video, friend_number)
+
+
+def callback_audio(toxav, friend_number, samples, audio_samples_per_channel, audio_channels_count, rate, user_data):
+    """
+    New audio chunk
+    """
+    # print(audio_samples_per_channel, audio_channels_count, rate)
+    Profile.get_instance().call.chunk(
+        bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]),
+        audio_channels_count,
+        rate)
+
+
+# -----------------------------------------------------------------------------------------------------------------
+# Callbacks - group chats
+# -----------------------------------------------------------------------------------------------------------------
+
+def group_message(window, tray, tox):
+    """
+    New message in group chat
+    """
+    def wrapped(tox_link, group_number, peer_id, message_type, message, length, user_data):
+        profile = Profile.get_instance()
+        settings = Settings.get_instance()
+        message = str(message[:length], 'utf-8')
+        invoke_in_main_thread(profile.new_message, group_number, message_type, message, True, peer_id)
+        if not window.isActiveWindow():
+            bl = settings['notify_all_gc'] or profile.name in message
+            name = tox.group_peer_get_name(group_number, peer_id)
+            if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and (not settings.locked) and bl:
+                invoke_in_main_thread(tray_notification, name, message, tray, window)
+            if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']:
+                sound_notification(SOUND_NOTIFICATION['MESSAGE'])
+            invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
+    return wrapped
+
+
+def group_invite(tox, friend_number, invite_data, length, user_data):
+    invoke_in_main_thread(Profile.get_instance().process_group_invite,
+                          friend_number,
+                          bytes(invite_data[:length]))
+
+
+def group_self_join(tox, group_number, user_data):
+    pr = Profile.get_instance()
+    gc = pr.get_gc_by_number(group_number)
+    invoke_in_main_thread(gc.set_status, TOX_USER_STATUS['NONE'])
+    if not pr.is_active_a_friend() and pr.get_active_number() == group_number:
+        invoke_in_main_thread(pr.set_active)
+
+
+def group_peer_join(tox, group_number, peer_id, user_data):
+    gc = Profile.get_instance().get_gc_by_number(group_number)
+    gc.add_peer(peer_id)
+
+
+# -----------------------------------------------------------------------------------------------------------------
+# Callbacks - initialization
+# -----------------------------------------------------------------------------------------------------------------
+
+
+def init_callbacks(tox, window, tray):
+    """
+    Initialization of all callbacks.
+    :param tox: tox instance
+    :param window: main window
+    :param tray: tray (for notifications)
+    """
+    tox.callback_self_connection_status(self_connection_status(tox), 0)
+
+    tox.callback_friend_status(friend_status, 0)
+    tox.callback_friend_message(friend_message(window, tray), 0)
+    tox.callback_friend_connection_status(friend_connection_status, 0)
+    tox.callback_friend_name(friend_name, 0)
+    tox.callback_friend_status_message(friend_status_message, 0)
+    tox.callback_friend_request(friend_request, 0)
+    tox.callback_friend_typing(friend_typing, 0)
+    tox.callback_friend_read_receipt(friend_read_receipt, 0)
+
+    tox.callback_file_recv(tox_file_recv(window, tray), 0)
+    tox.callback_file_recv_chunk(file_recv_chunk, 0)
+    tox.callback_file_chunk_request(file_chunk_request, 0)
+    tox.callback_file_recv_control(file_recv_control, 0)
+
+    toxav = tox.AV
+    toxav.callback_call_state(call_state, 0)
+    toxav.callback_call(call, 0)
+    toxav.callback_audio_receive_frame(callback_audio, 0)
+
+    tox.callback_friend_lossless_packet(lossless_packet, 0)
+    tox.callback_friend_lossy_packet(lossy_packet, 0)
+
+    tox.callback_group_message(group_message(window, tray, tox), 0)
+    tox.callback_group_invite(group_invite, 0)
+    tox.callback_group_self_join(group_self_join, 0)
+    tox.callback_group_peer_join(group_peer_join, 0)
+
diff --git a/toxygen/calls.py b/toxygen/calls.py
new file mode 100644
index 0000000..16cef47
--- /dev/null
+++ b/toxygen/calls.py
@@ -0,0 +1,144 @@
+import pyaudio
+import time
+import threading
+import settings
+from toxav_enums import *
+# TODO: play sound until outgoing call will be started or cancelled and add timeout
+# TODO: add widget for call
+
+CALL_TYPE = {
+    'NONE': 0,
+    'AUDIO': 1,
+    'VIDEO': 2
+}
+
+
+class AV:
+
+    def __init__(self, toxav):
+        self._toxav = toxav
+        self._running = True
+
+        self._calls = {}  # dict: key - friend number, value - call type
+
+        self._audio = None
+        self._audio_stream = None
+        self._audio_thread = None
+        self._audio_running = False
+        self._out_stream = None
+
+        self._audio_rate = 8000
+        self._audio_channels = 1
+        self._audio_duration = 60
+        self._audio_sample_count = self._audio_rate * self._audio_channels * self._audio_duration // 1000
+
+    def __contains__(self, friend_number):
+        return friend_number in self._calls
+
+    def __call__(self, friend_number, audio, video):
+        """Call friend with specified number"""
+        self._toxav.call(friend_number, 32 if audio else 0, 5000 if video else 0)
+        self._calls[friend_number] = CALL_TYPE['AUDIO']
+        self.start_audio_thread()
+
+    def finish_call(self, friend_number, by_friend=False):
+
+        if not by_friend:
+            self._toxav.call_control(friend_number, TOXAV_CALL_CONTROL['CANCEL'])
+        if friend_number in self._calls:
+            del self._calls[friend_number]
+        if not len(self._calls):
+            self.stop_audio_thread()
+
+    def stop(self):
+        self._running = False
+        self.stop_audio_thread()
+
+    def start_audio_thread(self):
+        """
+        Start audio sending
+        """
+        if self._audio_thread is not None:
+            return
+
+        self._audio_running = True
+
+        self._audio = pyaudio.PyAudio()
+        self._audio_stream = self._audio.open(format=pyaudio.paInt16,
+                                              rate=self._audio_rate,
+                                              channels=self._audio_channels,
+                                              input=True,
+                                              input_device_index=settings.Settings.get_instance().audio['input'],
+                                              frames_per_buffer=self._audio_sample_count * 10)
+
+        self._audio_thread = threading.Thread(target=self.send_audio)
+        self._audio_thread.start()
+
+    def stop_audio_thread(self):
+
+        if self._audio_thread is None:
+            return
+
+        self._audio_running = False
+
+        self._audio_thread.join()
+
+        self._audio_thread = None
+        self._audio_stream = None
+        self._audio = None
+
+        if self._out_stream is not None:
+            self._out_stream.stop_stream()
+            self._out_stream.close()
+            self._out_stream = None
+
+    def chunk(self, samples, channels_count, rate):
+        """
+        Incoming chunk
+        """
+
+        if self._out_stream is None:
+            self._out_stream = self._audio.open(format=pyaudio.paInt16,
+                                                channels=channels_count,
+                                                rate=rate,
+                                                output_device_index=settings.Settings.get_instance().audio['output'],
+                                                output=True)
+        self._out_stream.write(samples)
+
+    def send_audio(self):
+        """
+        This method sends audio to friends
+        """
+
+        while self._audio_running:
+            try:
+                pcm = self._audio_stream.read(self._audio_sample_count)
+                if pcm:
+                    for friend in self._calls:
+                        if self._calls[friend] & 1:
+                            try:
+                                self._toxav.audio_send_frame(friend, pcm, self._audio_sample_count,
+                                                             self._audio_channels, self._audio_rate)
+                            except:
+                                pass
+            except:
+                pass
+
+            time.sleep(0.01)
+
+    def accept_call(self, friend_number, audio_enabled, video_enabled):
+
+        if self._running:
+            self._calls[friend_number] = int(video_enabled) * 2 + int(audio_enabled)
+            self._toxav.answer(friend_number, 32 if audio_enabled else 0, 5000 if video_enabled else 0)
+            self.start_audio_thread()
+
+    def toxav_call_state_cb(self, friend_number, state):
+        """
+        New call state
+        """
+        if self._running:
+
+            if state & TOXAV_FRIEND_CALL_STATE['ACCEPTING_A']:
+                self._calls[friend_number] |= 1
+
diff --git a/toxygen/common/__init__.py b/toxygen/common/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/common/event.py b/toxygen/common/event.py
deleted file mode 100644
index f51a51f..0000000
--- a/toxygen/common/event.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-class Event:
-
-    def __init__(self):
-        self._callbacks = set()
-
-    def __iadd__(self, callback):
-        self.add_callback(callback)
-
-        return self
-
-    def __isub__(self, callback):
-        self.remove_callback(callback)
-
-        return self
-
-    def __call__(self, *args, **kwargs):
-        for callback in self._callbacks:
-            callback(*args, **kwargs)
-
-    def add_callback(self, callback):
-        self._callbacks.add(callback)
-
-    def remove_callback(self, callback):
-        self._callbacks.discard(callback)
diff --git a/toxygen/common/provider.py b/toxygen/common/provider.py
deleted file mode 100644
index 687fd9a..0000000
--- a/toxygen/common/provider.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-class Provider:
-
-    def __init__(self, get_item_action):
-        self._get_item_action = get_item_action
-        self._item = None
-
-    def get_item(self):
-        if self._item is None:
-            self._item = self._get_item_action()
-
-        return self._item
diff --git a/toxygen/common/tox_save.py b/toxygen/common/tox_save.py
deleted file mode 100644
index 45563b2..0000000
--- a/toxygen/common/tox_save.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-class ToxSave:
-
-    def __init__(self, tox):
-        self._tox = tox
-
-    def set_tox(self, tox):
-        self._tox = tox
-
-
-class ToxAvSave:
-
-    def __init__(self, toxav):
-        self._toxav = toxav
-
-    def set_toxav(self, toxav):
-        self._toxav = toxav
diff --git a/toxygen/contact.py b/toxygen/contact.py
new file mode 100644
index 0000000..03fad92
--- /dev/null
+++ b/toxygen/contact.py
@@ -0,0 +1,212 @@
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+import basecontact
+from messages import *
+from history import *
+import file_transfers as ft
+import util
+
+
+class Contact(basecontact.BaseContact):
+    """
+    Class encapsulating TOX contact
+    Properties: name (alias of contact or name), status_message, status (connection status)
+    widget - widget for update
+    """
+
+    def __init__(self, number, message_getter, name, status_message, widget, tox_id):
+        """
+        :param name: name, example: 'Toxygen user'
+        :param status_message: status message, example: 'Toxing on Toxygen'
+        :param widget: ContactItem instance
+        :param tox_id: tox id of contact
+        """
+        super().__init__(name, status_message, widget, tox_id)
+        self._message_getter = message_getter
+        self._new_messages = False
+        self._visible = True
+        self._alias = False
+        self._number = number
+        self._corr = []
+        self._unsaved_messages = 0
+        self._history_loaded = self._new_actions = False
+        self._curr_text = ''
+
+    def __del__(self):
+        self.set_visibility(False)
+        del self._widget
+        if hasattr(self, '_message_getter'):
+            del self._message_getter
+
+    def load_corr(self, first_time=True):
+        """
+        :param first_time: friend became active, load first part of messages
+        """
+        if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')):
+            return
+        data = list(self._message_getter.get(PAGE_SIZE))
+        if data is not None and len(data):
+            data.reverse()
+        else:
+            return
+        data = list(map(lambda tupl: TextMessage(*tupl), data))
+        self._corr = data + self._corr
+        self._history_loaded = True
+
+    def get_corr_for_saving(self):
+        """
+        Get data to save in db
+        :return: list of unsaved messages or []
+        """
+        messages = list(filter(lambda x: x.get_type() <= 1, self._corr))
+        return list(map(lambda x: x.get_data(), messages[-self._unsaved_messages:])) if self._unsaved_messages else []
+
+    def get_corr(self):
+        return self._corr[:]
+
+    def append_message(self, message):
+        """
+        :param message: text or file transfer message
+        """
+        self._corr.append(message)
+        if message.get_type() <= 1:
+            self._unsaved_messages += 1
+
+    def get_last_message_text(self):
+        messages = list(filter(lambda x: x.get_type() <= 1 and x.get_owner() != MESSAGE_OWNER['FRIEND'], self._corr))
+        if messages:
+            return messages[-1].get_data()[0]
+        else:
+            return ''
+
+    def get_unsent_messages(self):
+        """
+        :return list of unsent messages
+        """
+        messages = filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr)
+        return list(messages)
+
+    def get_unsent_messages_for_saving(self):
+        """
+        :return list of unsent messages for saving
+        """
+        messages = filter(lambda x: x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr)
+        return list(map(lambda x: x.get_data(), messages))
+
+    def delete_message(self, time):
+        elem = list(filter(lambda x: type(x) is TextMessage and x.get_data()[2] == time, self._corr))[0]
+        tmp = list(filter(lambda x: x.get_type() <= 1, self._corr))
+        if elem in tmp[-self._unsaved_messages:]:
+            self._unsaved_messages -= 1
+        self._corr.remove(elem)
+
+    def delete_old_messages(self):
+        old = filter(lambda x: x.get_type() in (2, 3) and (x.get_status() >= 2 or x.get_status() is None),
+                     self._corr[:-SAVE_MESSAGES])
+        old = list(old)
+        l = max(len(self._corr) - SAVE_MESSAGES, 0) - len(old)
+        self._unsaved_messages -= l
+        self._corr = old + self._corr[-SAVE_MESSAGES:]
+
+    def mark_as_sent(self):
+        try:
+            message = list(filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr))[0]
+            message.mark_as_sent()
+        except Exception as ex:
+            util.log('Mark as sent ex: ' + str(ex))
+
+    def clear_corr(self, save_unsent=False):
+        """
+        Clear messages list
+        """
+        if hasattr(self, '_message_getter'):
+            del self._message_getter
+        # don't delete data about active file transfer
+        if not save_unsent:
+            self._corr = list(filter(lambda x: x.get_type() in (2, 3) and
+                                               x.get_status() in ft.ACTIVE_FILE_TRANSFERS, self._corr))
+            self._unsaved_messages = 0
+        else:
+            self._corr = list(filter(lambda x: (x.get_type() in (2, 3) and x.get_status() in ft.ACTIVE_FILE_TRANSFERS)
+                                               or (x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT']),
+                                     self._corr))
+            self._unsaved_messages = len(self.get_unsent_messages())
+
+    def get_curr_text(self):
+        return self._curr_text
+
+    def set_curr_text(self, value):
+        self._curr_text = value
+
+    curr_text = property(get_curr_text, set_curr_text)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Visibility in friends' list
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_visibility(self):
+        return self._visible
+
+    def set_visibility(self, value):
+        self._visible = value
+
+    visibility = property(get_visibility, set_visibility)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Unread messages and actions
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_actions(self):
+        return self._new_actions
+
+    def set_actions(self, value):
+        self._new_actions = value
+        self._widget.connection_status.update(self.status, value)
+
+    actions = property(get_actions, set_actions)  # unread messages, incoming files, av calls
+
+    def get_messages(self):
+        return self._new_messages
+
+    def inc_messages(self):
+        self._new_messages += 1
+        self._new_actions = True
+        self._widget.connection_status.update(self.status, True)
+        self._widget.messages.update(self._new_messages)
+
+    def reset_messages(self):
+        self._new_actions = False
+        self._new_messages = 0
+        self._widget.messages.update(self._new_messages)
+        self._widget.connection_status.update(self.status, False)
+
+    messages = property(get_messages)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Number (can be used in toxcore)
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_number(self):
+        return self._number
+
+    def set_number(self, value):
+        self._number = value
+
+    number = property(get_number, set_number)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Alias support
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def set_name(self, value):
+        """
+        Set new name or ignore if alias exists
+        :param value: new name
+        """
+        if not self._alias:
+            super(Contact, self).set_name(value)
+
+    def set_alias(self, alias):
+        self._alias = bool(alias)
diff --git a/toxygen/contacts/__init__.py b/toxygen/contacts/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/contacts/basecontact.py b/toxygen/contacts/basecontact.py
deleted file mode 100644
index b4b33f1..0000000
--- a/toxygen/contacts/basecontact.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from user_data.settings import *
-from  qtpy import QtCore, QtGui
-from toxygen_wrapper.toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE
-import utils.util as util
-import common.event as event
-import contacts.common as common
-
-
-class BaseContact:
-    """
-    Class encapsulating TOX contact
-    Properties: name (alias of contact or name), status_message, status (connection status)
-    widget - widget for update, tox id (or public key)
-    Base class for all contacts.
-    """
-
-    def __init__(self, profile_manager, name, status_message, widget, tox_id, kind=''):
-        """
-        :param name: name, example: 'Toxygen user'
-        :param status_message: status message, example: 'Toxing on Toxygen'
-        :param widget: ContactItem instance
-        :param tox_id: tox id of contact
-        :param kind: one of ['bot', 'friend', 'group', 'invite', 'grouppeer', '']
-        """
-        self._profile_manager = profile_manager
-        self._name, self._status_message = name, status_message
-        self._kind = kind
-        self._status, self._widget = None, widget
-        self._tox_id = tox_id
-
-        self._name_changed_event = event.Event()
-        self._status_message_changed_event = event.Event()
-        self._status_changed_event = event.Event()
-        self._avatar_changed_event = event.Event()
-        self.init_widget()
-
-    # Name - current name or alias of user
-
-    def get_name(self):
-        return self._name
-
-    def set_name(self, value):
-        if self._name == value:
-            return
-        self._name = value
-        self._widget.name.setText(self._name)
-        self._widget.name.repaint()
-        self._name_changed_event(self._name)
-
-    name = property(get_name, set_name)
-
-    def get_name_changed_event(self):
-        return self._name_changed_event
-
-    name_changed_event = property(get_name_changed_event)
-
-    # Status message
-
-    def get_status_message(self):
-        return self._status_message
-
-    def set_status_message(self, value):
-        if self._status_message == value:
-            return
-        self._status_message = value
-        self._widget.status_message.setText(self._status_message)
-        self._widget.status_message.repaint()
-        self._status_message_changed_event(self._status_message)
-
-    status_message = property(get_status_message, set_status_message)
-
-    def get_status_message_changed_event(self):
-        return self._status_message_changed_event
-
-    status_message_changed_event = property(get_status_message_changed_event)
-
-    # Status
-
-    def get_status(self):
-        return self._status
-
-    def set_status(self, value):
-        if self._status == value:
-            return
-        self._status = value
-        self._widget.connection_status.update(value)
-        self._status_changed_event(self._status)
-
-    status = property(get_status, set_status)
-
-    def get_status_changed_event(self):
-        return self._status_changed_event
-
-    status_changed_event = property(get_status_changed_event)
-
-    # TOX ID. WARNING: for friend it will return public key, for profile - full address
-
-    def get_tox_id(self):
-        return self._tox_id
-
-    tox_id = property(get_tox_id)
-
-    # Avatars
-
-    def load_avatar(self):
-        """
-        Tries to load avatar of contact or uses default avatar
-        """
-        try:
-            avatar_path = self.get_avatar_path()
-            width = self._widget.avatar_label.width()
-            pixmap = QtGui.QPixmap(avatar_path)
-            self._widget.avatar_label.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio,
-                                                              QtCore.Qt.SmoothTransformation))
-            self._widget.avatar_label.repaint()
-            self._avatar_changed_event(avatar_path)
-        except Exception as e:
-            pass
-
-    def reset_avatar(self, generate_new):
-        avatar_path = self.get_avatar_path()
-        if os.path.isfile(avatar_path) and not avatar_path == self._get_default_avatar_path():
-            os.remove(avatar_path)
-        if generate_new:
-            self.set_avatar(common.generate_avatar(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]))
-        else:
-            self.load_avatar()
-
-    def set_avatar(self, avatar):
-        avatar_path = self.get_contact_avatar_path()
-        with open(avatar_path, 'wb') as f:
-            f.write(avatar)
-        self.load_avatar()
-
-    def get_pixmap(self):
-        return self._widget.avatar_label.pixmap()
-
-    def get_avatar_path(self):
-        avatar_path = self.get_contact_avatar_path()
-        if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path):  # load default image
-            avatar_path = self._get_default_avatar_path()
-
-        return avatar_path
-
-    def get_contact_avatar_path(self):
-        directory = util.join_path(self._profile_manager.get_dir(), 'avatars')
-
-        return util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]))
-
-    def has_avatar(self):
-        path = self.get_contact_avatar_path()
-
-        return util.file_exists(path)
-
-    def get_avatar_changed_event(self):
-        return self._avatar_changed_event
-
-    avatar_changed_event = property(get_avatar_changed_event)
-
-    # Widgets
-
-    def init_widget(self):
-        # File "/mnt/o/var/local/src/toxygen/toxygen/contacts/contacts_manager.py", line 252, in filtration_and_sorting
-        # contact.set_widget(item_widget)
-        # File "/mnt/o/var/local/src/toxygen/toxygen/contacts/contact.py", line 320, in set_widget
-        if not self._widget:
-            LOG.warn("BC.init_widget self._widget is NULL")
-            return
-        self._widget.name.setText(self._name)
-        self._widget.status_message.setText(self._status_message)
-        if hasattr(self._widget, 'kind'):
-            self._widget.kind.setText(self._kind)
-        self._widget.connection_status.update(self._status)
-        self.load_avatar()
-
-    # Private methods
-
-    @staticmethod
-    def _get_default_avatar_path():
-        return util.join_path(util.get_images_directory(), 'avatar.png')
diff --git a/toxygen/contacts/common.py b/toxygen/contacts/common.py
deleted file mode 100644
index bd46c32..0000000
--- a/toxygen/contacts/common.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import hashlib
-
-from pydenticon import Generator
-
-# Typing notifications
-
-class BaseTypingNotificationHandler:
-
-    DEFAULT_HANDLER = None
-
-    def __init__(self):
-        pass
-
-    def send(self, tox, is_typing):
-        pass
-
-
-class FriendTypingNotificationHandler(BaseTypingNotificationHandler):
-
-    def __init__(self, friend_number:int):
-        super().__init__()
-        self._friend_number = friend_number
-
-    def send(self, tox, is_typing):
-        tox.self_set_typing(self._friend_number, is_typing)
-
-
-BaseTypingNotificationHandler.DEFAULT_HANDLER = BaseTypingNotificationHandler()
-
-
-# Identicons support
-
-
-def generate_avatar(public_key):
-    foreground = ['rgb(45,79,255)', 'rgb(185, 66, 244)', 'rgb(185, 66, 244)',
-                  'rgb(254,180,44)', 'rgb(252, 2, 2)', 'rgb(109, 198, 0)',
-                  'rgb(226,121,234)', 'rgb(130, 135, 124)',
-                  'rgb(30,179,253)', 'rgb(160, 157, 0)',
-                  'rgb(232,77,65)', 'rgb(102, 4, 4)',
-                  'rgb(49,203,115)',
-                  'rgb(141,69,170)']
-    generator = Generator(5, 5, foreground=foreground, background='rgba(42,42,42,0)')
-    digest = hashlib.sha256(public_key.encode('utf-8')).hexdigest()
-    identicon = generator.generate(digest, 220, 220, padding=(10, 10, 10, 10))
-
-    return identicon
diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py
deleted file mode 100644
index 70b9318..0000000
--- a/toxygen/contacts/contact.py
+++ /dev/null
@@ -1,320 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from history.database import TIMEOUT, \
-    SAVE_MESSAGES, MESSAGE_AUTHOR
-
-from contacts import basecontact, common
-from messenger.messages import *
-from contacts.contact_menu import *
-from file_transfers import file_transfers as ft
-import re
-
-# LOG=util.log
-global LOG
-import logging
-LOG = logging.getLogger('app.'+__name__)
-
-class Contact(basecontact.BaseContact):
-    """
-    Class encapsulating TOX contact
-    Properties: number, message getter, history etc. Base class for friend and gc classes
-    """
-
-    def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id):
-        """
-        :param message_getter: gets messages from db
-        :param number: number of friend.
-        """
-        super().__init__(profile_manager, name, status_message, widget, tox_id)
-        self._number = number
-        self._new_messages = False
-        self._visible = True
-        self._alias = False
-        self._message_getter = message_getter
-        self._corr = []
-        self._unsaved_messages = 0
-        self._history_loaded = self._new_actions = False
-        self._curr_text = self._search_string = ''
-        self._search_index = 0
-
-    def __del__(self):
-        self.set_visibility(False)
-        del self._widget
-        if hasattr(self, '_message_getter'):
-            del self._message_getter
-
-    # History support
-
-    def load_corr(self, first_time=True):
-        """
-        :param first_time: friend became active, load first part of messages
-        """
-        try:
-            if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')):
-                return
-            if self._message_getter is None:
-                return
-            data = list(self._message_getter.get(PAGE_SIZE))
-            if data is not None and len(data):
-                data.reverse()
-            else:
-                return
-            data = list(map(lambda p: self._get_text_message(p), data))
-            self._corr = data + self._corr
-        except:
-            pass
-        finally:
-            self._history_loaded = True
-
-    def load_all_corr(self):
-        """
-        Get all chat history from db for current friend
-        """
-        if self._message_getter is None:
-            return
-        data = list(self._message_getter.get_all())
-        if data is not None and len(data):
-            data.reverse()
-            data = list(map(lambda p: self._get_text_message(p), data))
-            self._corr = data + self._corr
-            self._history_loaded = True
-
-    def get_corr_for_saving(self):
-        """
-        Get data to save in db
-        :return: list of unsaved messages or []
-        """
-        messages = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr))
-        return messages[-self._unsaved_messages:] if self._unsaved_messages else []
-
-    def get_corr(self):
-        return self._corr[:]
-
-    def append_message(self, message):
-        """
-        :param message: text or file transfer message
-        """
-        self._corr.append(message)
-        if message.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']):
-            self._unsaved_messages += 1
-
-    def get_last_message_text(self):
-        messages = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION'])
-                                         and m.author.type != MESSAGE_AUTHOR['FRIEND'], self._corr))
-        if messages:
-            return messages[-1].text
-        else:
-            return ''
-
-    def remove_messages_widgets(self):
-        for message in self._corr:
-            message.remove_widget()
-
-    def get_message(self, _filter):
-        return list(filter(lambda m: _filter(m), self._corr))[0]
-
-    @staticmethod
-    def _get_text_message(params):
-        (message, author_type, author_name, unix_time, message_type, unique_id) = params
-        author = MessageAuthor(author_name, author_type)
-
-        return TextMessage(message, author, unix_time, message_type, unique_id)
-
-    # Unsent messages
-
-    def get_unsent_messages(self):
-        """
-        :return list of unsent messages
-        """
-        messages = filter(lambda m: m.author is not None and m.author.type == MESSAGE_AUTHOR['NOT_SENT'], self._corr)
-        return list(messages)
-
-    def get_unsent_messages_for_saving(self):
-        """
-        :return list of unsent messages for saving
-        """
-#                               and m.tox_message_id == tox_message_id,
-        messages = filter(lambda m: m.author is not None
-                              and m.author.type == MESSAGE_AUTHOR['NOT_SENT'],
-                              self._corr)
-        # was message = list(...)[0]
-        return list(messages)
-
-    def mark_as_sent(self, tox_message_id):
-        try:
-            message = list(filter(lambda m: m.author is not None and m.author.type == MESSAGE_AUTHOR['NOT_SENT']
-                                            and m.tox_message_id == tox_message_id, self._corr))[0]
-            message.mark_as_sent()
-        except Exception as ex:
-            #   wrapped C/C++ object of type QLabel has been deleted
-            LOG.error(f"Mark as sent:  {ex}")
-
-    # Message deletion
-
-    def delete_message(self, message_id):
-        elem = list(filter(lambda m: m.message_id == message_id, self._corr))[0]
-        tmp = list(filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr))
-        if elem in tmp[-self._unsaved_messages:] and self._unsaved_messages:
-            self._unsaved_messages -= 1
-        self._corr.remove(elem)
-        self._message_getter.delete_one()
-        self._search_index = 0
-
-    def delete_old_messages(self):
-        """
-        Delete old messages (reduces RAM usage if messages saving is not enabled)
-        """
-        def save_message(m):
-            if m.type == MESSAGE_TYPE['FILE_TRANSFER'] and (m.state not in ACTIVE_FILE_TRANSFERS):
-                return True
-            return m.author is not None and m.author.type == MESSAGE_AUTHOR['NOT_SENT']
-
-        old = filter(save_message, self._corr[:-SAVE_MESSAGES])
-        self._corr = list(old) + self._corr[-SAVE_MESSAGES:]
-        text_messages = filter(lambda m: m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']), self._corr)
-        self._unsaved_messages = min(self._unsaved_messages, len(list(text_messages)))
-        self._search_index = 0
-
-    def clear_corr(self, save_unsent=False):
-        """
-        Clear messages list
-        """
-        if hasattr(self, '_message_getter'):
-            del self._message_getter
-        self._search_index = 0
-        # don't delete data about active file transfer
-        if not save_unsent:
-            self._corr = list(filter(lambda m: m.type == MESSAGE_TYPE['FILE_TRANSFER'] and
-                                               m.state in ft.ACTIVE_FILE_TRANSFERS, self._corr))
-            self._unsaved_messages = 0
-        else:
-            self._corr = list(filter(lambda m: (m.type == MESSAGE_TYPE['FILE_TRANSFER']
-                                                and m.state in ft.ACTIVE_FILE_TRANSFERS)
-                                               or (m.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION'])
-                                                   and m.author.type == MESSAGE_AUTHOR['NOT_SENT']),
-                                     self._corr))
-            self._unsaved_messages = len(self.get_unsent_messages())
-
-    # Chat history search
-
-    def search_string(self, search_string):
-        self._search_string, self._search_index = search_string, 0
-        return self.search_prev()
-
-    def search_prev(self):
-        while True:
-            l = len(self._corr)
-            for i in range(self._search_index - 1, -l - 1, -1):
-                if self._corr[i].type not in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']):
-                    continue
-                message = self._corr[i].text
-                if re.search(self._search_string, message, re.IGNORECASE) is not None:
-                    self._search_index = i
-                    return i
-            self._search_index = -l
-            self.load_corr(False)
-            if len(self._corr) == l:
-                return None  # not found
-
-    def search_next(self):
-        if not self._search_index:
-            return None
-        for i in range(self._search_index + 1, 0):
-            if self._corr[i].type not in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']):
-                continue
-            message = self._corr[i].text
-            if re.search(self._search_string, message, re.IGNORECASE) is not None:
-                self._search_index = i
-                return i
-        return None  # not found
-
-    # Current text - text from message area
-
-    def get_curr_text(self):
-        return self._curr_text
-
-    def set_curr_text(self, value):
-        self._curr_text = value
-
-    curr_text = property(get_curr_text, set_curr_text)
-
-    # Alias support
-
-    def set_name(self, value):
-        """
-        Set new name or ignore if alias exists
-        :param value: new name
-        """
-        if not self._alias:
-            super().set_name(value)
-
-    def set_alias(self, alias):
-        self._alias = bool(alias)
-
-    def has_alias(self):
-        return self._alias
-
-    # Visibility in friends' list
-
-    def get_visibility(self):
-        return self._visible
-
-    def set_visibility(self, value):
-        self._visible = value
-
-    visibility = property(get_visibility, set_visibility)
-
-    # Unread messages and other actions from friend
-
-    def get_actions(self):
-        return self._new_actions
-
-    def set_actions(self, value):
-        self._new_actions = value
-        self._widget.connection_status.update(self.status, value)
-
-    actions = property(get_actions, set_actions)  # unread messages, incoming files, av calls
-
-    def get_messages(self):
-        return self._new_messages
-
-    def inc_messages(self):
-        self._new_messages += 1
-        self._new_actions = True
-        self._widget.connection_status.update(self.status, True)
-        self._widget.messages.update(self._new_messages)
-
-    def reset_messages(self):
-        self._new_actions = False
-        self._new_messages = 0
-        self._widget.messages.update(self._new_messages)
-        self._widget.connection_status.update(self.status, False)
-
-    messages = property(get_messages)
-
-    # Friend's or group's number (can be used in toxcore)
-
-    def get_number(self):
-        return self._number
-
-    def set_number(self, value):
-        self._number = value
-
-    number = property(get_number, set_number)
-
-    # Typing notifications
-
-    def get_typing_notification_handler(self):
-        return common.BaseTypingNotificationHandler.DEFAULT_HANDLER
-
-    typing_notification_handler = property(get_typing_notification_handler)
-
-    # Context menu support
-
-    def get_context_menu_generator(self):
-        return BaseContactMenuGenerator(self)
-
-    # Filtration support
-
-    def set_widget(self, widget):
-        self._widget = widget
-        self.init_widget()
diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py
deleted file mode 100644
index 6f45ca6..0000000
--- a/toxygen/contacts/contact_menu.py
+++ /dev/null
@@ -1,229 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from  qtpy import QtWidgets
-
-import utils.ui as util_ui
-from toxygen_wrapper.toxcore_enums_and_consts import *
-
-global LOG
-import logging
-LOG = logging.getLogger('app')
-
-# Builder
-
-def _create_menu(menu_name, parent):
-    menu_name = menu_name or ''
-
-    return QtWidgets.QMenu(menu_name) if parent is None else parent.addMenu(menu_name)
-
-
-class ContactMenuBuilder:
-
-    def __init__(self):
-        self._actions = {}
-        self._submenus = {}
-        self._name = None
-        self._index = 0
-
-    def with_name(self, name):
-        self._name = name
-
-        return self
-
-    def with_action(self, text, handler):
-        self._add_action(text, handler)
-
-        return self
-
-    def with_optional_action(self, text, handler, show_action):
-        if show_action:
-            self._add_action(text, handler)
-
-        return self
-
-    def with_actions(self, actions):
-        for action in actions:
-            (text, handler) = action
-            self._add_action(text, handler)
-
-        return self
-
-    def with_submenu(self, submenu_builder):
-        self._add_submenu(submenu_builder)
-
-        return self
-
-    def with_optional_submenu(self, submenu_builder):
-        if submenu_builder is not None:
-            self._add_submenu(submenu_builder)
-
-        return self
-
-    def build(self, parent=None):
-        menu = _create_menu(self._name, parent)
-
-        for i in range(self._index):
-            if i in self._actions:
-                text, handler = self._actions[i]
-                action = menu.addAction(text)
-                action.triggered.connect(handler)
-            else:
-                submenu_builder = self._submenus[i]
-                submenu = submenu_builder.build(menu)
-                menu.addMenu(submenu)
-
-        return menu
-
-    def _add_submenu(self, submenu):
-        self._submenus[self._index] = submenu
-        self._index += 1
-
-    def _add_action(self, text, handler):
-        self._actions[self._index] = (text, handler)
-        self._index += 1
-
-# Generators
-
-
-class BaseContactMenuGenerator:
-
-    def __init__(self, contact):
-        self._contact = contact
-
-    def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader):
-        return ContactMenuBuilder().build()
-
-    # Private methods
-
-    def _generate_copy_menu_builder(self, main_screen):
-        copy_menu_builder = ContactMenuBuilder()
-        (copy_menu_builder
-         .with_name(util_ui.tr('Copy'))
-         .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name))
-         .with_action(util_ui.tr("Status message"), lambda: main_screen.copy_text(self._contact.status_message))
-         .with_action(util_ui.tr("Public key"), lambda: main_screen.copy_text(self._contact.tox_id))
-         )
-
-        return copy_menu_builder
-
-    def _generate_history_menu_builder(self, history_loader, main_screen):
-        history_menu_builder = ContactMenuBuilder()
-        (history_menu_builder
-         .with_name(util_ui.tr("Chat history"))
-         .with_action(util_ui.tr("Clear history"), lambda: history_loader.clear_history(self._contact)
-                                                           or main_screen.messages.clear())
-         .with_action(util_ui.tr("Export as text"), lambda: history_loader.export_history(self._contact))
-         .with_action(util_ui.tr("Export as HTML"), lambda: history_loader.export_history(self._contact, False))
-         )
-
-        return history_menu_builder
-
-
-class FriendMenuGenerator(BaseContactMenuGenerator):
-
-    def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader):
-        history_menu_builder = self._generate_history_menu_builder(history_loader, main_screen)
-        copy_menu_builder = self._generate_copy_menu_builder(main_screen)
-        plugins_menu_builder = self._generate_plugins_menu_builder(plugin_loader, number)
-        groups_menu_builder = self._generate_groups_menu(contacts_manager, groups_service)
-
-        allowed = self._contact.tox_id in settings['auto_accept_from_friends']
-        auto = util_ui.tr("Disallow auto accept") if allowed else util_ui.tr('Allow auto accept')
-
-        builder = ContactMenuBuilder()
-        menu = (builder
-                .with_action(util_ui.tr("Set alias"), lambda: main_screen.set_alias(number))
-                .with_submenu(history_menu_builder)
-                .with_submenu(copy_menu_builder)
-                .with_action(auto, lambda: main_screen.auto_accept(number, not allowed))
-                .with_action(util_ui.tr("Remove friend"), lambda: main_screen.remove_friend(number))
-                .with_action(util_ui.tr("Block friend"), lambda: main_screen.block_friend(number))
-                .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact))
-                .with_optional_submenu(plugins_menu_builder)
-                .with_optional_submenu(groups_menu_builder)
-                ).build()
-
-        return menu
-
-    # Private methods
-
-    @staticmethod
-    def _generate_plugins_menu_builder(plugin_loader, number):
-        if plugin_loader is None:
-            return None
-        plugins_actions = plugin_loader.get_menu(number)
-        if not len(plugins_actions):
-            return None
-        plugins_menu_builder = ContactMenuBuilder()
-        (plugins_menu_builder
-         .with_name(util_ui.tr('Plugins'))
-         .with_actions(plugins_actions)
-         )
-
-        return plugins_menu_builder
-
-    def _generate_groups_menu(self, contacts_manager, groups_service):
-        chats = contacts_manager.get_group_chats()
-        LOG.debug(f"_generate_groups_menu len(chats)={len(chats)} or self._contact.status={self._contact.status}")
-        if not len(chats) or self._contact.status is None:
-            #? return None
-            pass
-        groups_menu_builder = ContactMenuBuilder()
-        (groups_menu_builder
-         .with_name(util_ui.tr("Invite to group"))
-         .with_actions([(g.name, lambda: groups_service.invite_friend(self._contact.number, g.number)) for g in chats])
-         )
-
-        return groups_menu_builder
-
-
-class GroupMenuGenerator(BaseContactMenuGenerator):
-
-    def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader):
-        copy_menu_builder = self._generate_copy_menu_builder(main_screen)
-        history_menu_builder = self._generate_history_menu_builder(history_loader, main_screen)
-
-        builder = ContactMenuBuilder()
-        menu = (builder
-                .with_action(util_ui.tr("Set alias"), lambda: main_screen.set_alias(number))
-                .with_submenu(copy_menu_builder)
-                .with_submenu(history_menu_builder)
-                .with_optional_action(util_ui.tr("Manage group"),
-                                      lambda: groups_service.show_group_management_screen(self._contact),
-                                      self._contact.is_self_founder())
-                .with_optional_action(util_ui.tr("Group settings"),
-                                      lambda: groups_service.show_group_settings_screen(self._contact),
-                                      not self._contact.is_self_founder())
-                .with_optional_action(util_ui.tr("Set topic"),
-                                      lambda: groups_service.set_group_topic(self._contact),
-                                      self._contact.is_self_moderator_or_founder())
-#                .with_action(util_ui.tr("Bans list"),
-#                             lambda: groups_service.show_bans_list(self._contact))
-                .with_action(util_ui.tr("Reconnect to group"),
-                             lambda: groups_service.reconnect_to_group(self._contact.number))
-                .with_optional_action(util_ui.tr("Disconnect from group"),
-                                      lambda: groups_service.disconnect_from_group(self._contact.number),
-                                      self._contact.status is not None)
-                .with_action(util_ui.tr("Leave group"), lambda: groups_service.leave_group(self._contact.number))
-                .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact))
-                ).build()
-
-        return menu
-
-
-class GroupPeerMenuGenerator(BaseContactMenuGenerator):
-
-    def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service, history_loader):
-        copy_menu_builder = self._generate_copy_menu_builder(main_screen)
-        history_menu_builder = self._generate_history_menu_builder(history_loader, main_screen)
-
-        builder = ContactMenuBuilder()
-        menu = (builder
-                .with_action(util_ui.tr("Set alias"), lambda: main_screen.set_alias(number))
-                .with_submenu(copy_menu_builder)
-                .with_submenu(history_menu_builder)
-                .with_action(util_ui.tr("Quit chat"),
-                             lambda: contacts_manager.remove_group_peer(self._contact))
-                .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact))
-                ).build()
-
-        return menu
diff --git a/toxygen/contacts/contact_provider.py b/toxygen/contacts/contact_provider.py
deleted file mode 100644
index 0c5a61d..0000000
--- a/toxygen/contacts/contact_provider.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import common.tox_save as tox_save
-
-global LOG
-import logging
-LOG = logging.getLogger(__name__)
-
-# callbacks can be called in any thread so were being careful
-from av.calls import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
-
-class ContactProvider(tox_save.ToxSave):
-
-    def __init__(self, tox, friend_factory, group_factory, group_peer_factory, app=None):
-        super().__init__(tox)
-        self._friend_factory = friend_factory
-        self._group_factory = group_factory
-        self._group_peer_factory = group_peer_factory
-        self._cache = {}  # key - contact's public key, value - contact instance
-        self._app = app
-
-    # Friends
-
-    def get_friend_by_number(self, friend_number:int):
-        try:
-            public_key = self._tox.friend_get_public_key(friend_number)
-        except Exception as e:
-            LOG_WARN(f"CP.get_friend_by_number NO {friend_number} {e} ")
-            return None
-        return self.get_friend_by_public_key(public_key)
-
-    def get_friend_by_public_key(self, public_key):
-        friend = self._get_contact_from_cache(public_key)
-        if friend is not None:
-            return friend
-        friend = self._friend_factory.create_friend_by_public_key(public_key)
-        if friend is None:
-            LOG_WARN(f"CP.get_friend_by_public_key NULL {friend} ")
-        else:
-            self._add_to_cache(public_key, friend)
-            LOG_DEBUG(f"CP.get_friend_by_public_key ADDED {friend} ")
-        return friend
-
-    def get_all_friends(self) -> list:
-        if self._app and self._app.bAppExiting:
-            return []
-        try:
-            friend_numbers = self._tox.self_get_friend_list()
-        except Exception as e:
-            LOG_WARN(f"CP.get_all_friends EXCEPTION {e} ")
-            return []
-        friends = map(lambda n: self.get_friend_by_number(n), friend_numbers)
-        return list(friends)
-
-    # Groups
-
-    def get_all_groups(self):
-        """from callbacks"""
-        try:
-            len_groups = self._tox.group_get_number_groups()
-            group_numbers = range(len_groups)
-        except Exception as e:
-            return None
-        groups = list(map(lambda n: self.get_group_by_number(n), group_numbers))
-        # failsafe in case there are bogus None groups?
-        fgroups = list(filter(lambda x: x, groups))
-        if len(fgroups) != len_groups:
-            LOG_WARN(f"CP.are there are bogus None groups in libtoxcore? {len(fgroups)} != {len_groups}")
-            for group_num in group_numbers:
-                group = self.get_group_by_number(group_num)
-                if group is None:
-                    LOG_ERROR(f"There are bogus None groups in libtoxcore {group_num}!")
-                    # fixme: do something
-            groups = fgroups
-        return groups
-
-    def get_group_by_number(self, group_number):
-        group = None
-        try:
-#            LOG_DEBUG(f"CP.CP.group_get_number {group_number} ")
-            # original code
-            chat_id = self._tox.group_get_chat_id(group_number)
-            if chat_id is None:
-                LOG_ERROR(f"get_group_by_number NULL chat_id ({group_number})")
-            elif chat_id == '-1':
-                LOG_ERROR(f"get_group_by_number <0 chat_id ({group_number})")
-            else:
-                LOG_INFO(f"CP.group_get_number {group_number} {chat_id}")
-                group = self.get_group_by_chat_id(chat_id)
-                if group is None or group  == '-1':
-                    LOG_WARN(f"CP.get_group_by_number leaving {group} ({group_number})")
-                    #? iRet = self._tox.group_leave(group_number)
-                    # invoke in main thread?
-                    # self._contacts_manager.delete_group(group_number)
-            return group
-        except Exception as e:
-            LOG_WARN(f"CP.group_get_number {group_number} {e}")
-            return None
-
-    def get_group_by_chat_id(self, chat_id):
-        group = self._get_contact_from_cache(chat_id)
-        if group is not None:
-            return group
-        group = self._group_factory.create_group_by_chat_id(chat_id)
-        if group is None:
-            LOG_ERROR(f"get_group_by_chat_id NULL chat_id={chat_id}")
-        else:
-            self._add_to_cache(chat_id, group)
-
-        return group
-
-    def get_group_by_public_key(self, public_key):
-        group = self._get_contact_from_cache(public_key)
-        if group is not None:
-            return group
-        group = self._group_factory.create_group_by_public_key(public_key)
-        if group is None:
-            LOG_WARN(f"get_group_by_public_key NULL group public_key={public_key}")
-        else:
-            self._add_to_cache(public_key, group)
-
-        return group
-
-    # Group peers
-
-    def get_all_group_peers(self):
-        return []
-
-    def get_group_peer_by_id(self, group, peer_id):
-        peer = group.get_peer_by_id(peer_id)
-        if peer is not None:
-            return self._get_group_peer(group, peer)
-        LOG_WARN(f"get_group_peer_by_id peer_id={peer_id}")
-        return None
-
-    def get_group_peer_by_public_key(self, group, public_key):
-        peer = group.get_peer_by_public_key(public_key)
-        if peer is not None:
-            return self._get_group_peer(group, peer)
-        LOG_WARN(f"get_group_peer_by_public_key public_key={public_key}")
-        return None
-
-    # All contacts
-
-    def get_all(self):
-        return self.get_all_friends() + self.get_all_groups() + self.get_all_group_peers()
-
-    # Caching
-
-    def clear_cache(self):
-        self._cache.clear()
-
-    def remove_contact_from_cache(self, contact_public_key):
-        if contact_public_key in self._cache:
-            del self._cache[contact_public_key]
-
-    # Private methods
-
-    def _get_contact_from_cache(self, public_key):
-        return self._cache[public_key] if public_key in self._cache else None
-
-    def _add_to_cache(self, public_key, contact):
-        self._cache[public_key] = contact
-
-    def _get_group_peer(self, group, peer):
-        return self._group_peer_factory.create_group_peer(group, peer)
diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py
deleted file mode 100644
index 6f0dae8..0000000
--- a/toxygen/contacts/contacts_manager.py
+++ /dev/null
@@ -1,670 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import logging
-
-from contacts.friend import Friend
-from contacts.group_chat import GroupChat
-from messenger.messages import *
-from common.tox_save import ToxSave
-from contacts.group_peer_contact import GroupPeerContact
-from groups.group_peer import GroupChatPeer
-from middleware.callbacks import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
-import toxygen_wrapper.toxcore_enums_and_consts as enums
-
-# LOG=util.log
-global LOG
-LOG = logging.getLogger('app.'+__name__)
-
-UINT32_MAX = 2 ** 32 -1
-
-def set_contact_kind(contact) -> None:
-    bInvite = len(contact.name) == enums.TOX_PUBLIC_KEY_SIZE * 2 and \
-      contact.status_message == ''
-    bBot = not bInvite and contact.name.lower().endswith(' bot')
-    if type(contact) == Friend and bInvite:
-        contact._kind = 'invite'
-    elif type(contact) == Friend and bBot:
-        contact._kind = 'bot'
-    elif type(contact) == Friend:
-        contact._kind = 'friend'
-    elif type(contact) == GroupChat:
-        contact._kind = 'group'
-    elif type(contact) == GroupChatPeer:
-        contact._kind = 'grouppeer'
-
-class ContactsManager(ToxSave):
-    """
-    Represents contacts list.
-    """
-
-    def __init__(self, tox, settings, screen, profile_manager, contact_provider, history, tox_dns,
-                 messages_items_factory):
-        super().__init__(tox)
-        self._settings = settings
-        self._screen = screen
-        self._ms = screen
-        self._profile_manager = profile_manager
-        self._contact_provider = contact_provider
-        self._tox_dns = tox_dns
-        self._messages_items_factory = messages_items_factory
-        self._messages = screen.messages
-        self._contacts = []
-        self._active_contact = -1
-        self._active_contact_changed = Event()
-        self._sorting = settings['sorting']
-        self._filter_string = ''
-        screen.contacts_filter.setCurrentIndex(int(self._sorting))
-        self._history = history
-        self._load_contacts()
-
-    def _log(self, s) -> None:
-        try:
-            self._ms._log(s)
-        except: pass
-
-    def get_contact(self, num):
-        if num < 0 or num >= len(self._contacts):
-            return None
-        return self._contacts[num]
-
-    def get_curr_contact(self):
-        return self._contacts[self._active_contact] if self._active_contact + 1 else None
-
-    def save_profile(self) -> None:
-        data = self._tox.get_savedata()
-        self._profile_manager.save_profile(data)
-
-    def is_friend_active(self, friend_number:int) -> bool:
-        if not self.is_active_a_friend():
-            return False
-
-        return self.get_curr_contact().number == friend_number
-
-    def is_group_active(self, group_number) -> bool:
-        if self.is_active_a_friend():
-            return False
-
-        return self.get_curr_contact().number == group_number
-
-    def is_contact_active(self, contact) -> bool:
-        if self._active_contact == -1:
-#            LOG.debug("No self._active_contact")
-            return False
-        if self._active_contact >= len(self._contacts):
-            LOG.warn(f"ERROR _active_contact={self._active_contact} >= contacts len={len(self._contacts)}")
-            return False
-        if not self._contacts[self._active_contact]:
-            LOG.warn(f"ERROR NULL {self._contacts[self._active_contact]}  {contact.tox_id}")
-            return False
-
-        if not hasattr(contact, 'tox_id'):
-            LOG.warn(f"ERROR is_contact_active no contact.tox_id {type(contact)}  contact={contact}")
-            return False
-
-        return self._contacts[self._active_contact].tox_id == contact.tox_id
-
-    # Reconnection support
-
-    def reset_contacts_statuses(self) -> None:
-        for contact in self._contacts:
-            contact.status = None
-
-    # Work with active friend
-
-    def get_active(self):
-        return self._active_contact
-
-    def set_active(self, value):
-        """
-        Change current active friend or update info
-        :param value: number of new active friend in friend's list
-        """
-        if value is None and self._active_contact == -1:  # nothing to update
-            return
-        if value == -1:  # all friends were deleted
-            self._screen.account_name.setText('')
-            self._screen.account_status.setText('')
-            self._screen.account_status.setToolTip('')
-            self._active_contact = -1
-            self._screen.account_avatar.setHidden(True)
-            self._messages.clear()
-            self._screen.messageEdit.clear()
-            return
-        try:
-            self._screen.typing.setVisible(False)
-            current_contact = self.get_curr_contact()
-            if current_contact is not None:
-                # TODO: send when needed
-                current_contact.typing_notification_handler.send(self._tox, False)
-                current_contact.remove_messages_widgets()  # TODO: if required
-                self._unsubscribe_from_events(current_contact)
-
-            if self._active_contact >= 0 and self._active_contact != value:
-                try:
-                    current_contact.curr_text = self._screen.messageEdit.toPlainText()
-                except:
-                    pass
-
-            # IndexError: list index out of range
-            if value >= len(self._contacts):
-                LOG.warn("CM.set_active value too big: {{self._contacts}}")
-                return
-            contact = self._contacts[value]
-            self._subscribe_to_events(contact)
-            contact.remove_invalid_unsent_files()
-            if self._active_contact != value:
-                self._screen.messageEdit.setPlainText(contact.curr_text)
-            self._active_contact = value
-            contact.reset_messages()
-            if not self._settings['save_history']:
-                contact.delete_old_messages()
-            self._messages.clear()
-            contact.load_corr()
-            corr = contact.get_corr()[-PAGE_SIZE:]
-            for message in corr:
-                if message.type == MESSAGE_TYPE['FILE_TRANSFER']:
-                    self._messages_items_factory.create_file_transfer_item(message)
-                elif message.type == MESSAGE_TYPE['INLINE']:
-                    self._messages_items_factory.create_inline_item(message)
-                else:
-                    self._messages_items_factory.create_message_item(message)
-            self._messages.scrollToBottom()
-            # if value in self._call:
-            #     self._screen.active_call()
-            # elif value in self._incoming_calls:
-            #     self._screen.incoming_call()
-            # else:
-            #     self._screen.call_finished()
-            self._set_current_contact_data(contact)
-            self._active_contact_changed(contact)
-        except Exception as e:  # no friend found. ignore
-            LOG.warn(f"CM.set_active EXCEPTION  value:{value} len={len(self._contacts)} {e}")
-            # gulp raise
-
-    active_contact = property(get_active, set_active)
-
-    def get_active_contact_changed(self):
-        return self._active_contact_changed
-
-    active_contact_changed = property(get_active_contact_changed)
-
-    def update(self):
-        if self._active_contact + 1:
-            self.set_active(self._active_contact)
-
-    def is_active_a_friend(self):
-        return type(self.get_curr_contact()) is Friend
-
-    def is_active_a_group(self):
-        return type(self.get_curr_contact()) is GroupChat
-
-    def is_active_a_group_chat_peer(self):
-        return type(self.get_curr_contact()) is GroupPeerContact
-
-    # Filtration
-
-    def filtration_and_sorting(self, sorting=0, filter_str=''):
-        """
-        Filtration of friends list
-        :param sorting: 0 - no sorting, 1 - online only, 2 - online first, 3 - by name,
-        4 - online and by name, 5 - online first and by name, 6 kind
-        :param filter_str: show contacts which name contains this substring
-        """
-        filter_str = filter_str.lower()
-        current_contact = self.get_curr_contact()
-
-        for index, contact in enumerate(self._contacts):
-            if not contact._kind:
-                set_contact_kind(contact)
-
-        if sorting > 6 or sorting < 0:
-            sorting = 0
-
-        if sorting in (1, 2, 4, 5):  # online first
-            self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True)
-            sort_by_name = sorting in (4, 5)
-            # save results of previous sorting
-            online_friends = filter(lambda x: x.status is not None, self._contacts)
-            online_friends_count = len(list(online_friends))
-            part1 = self._contacts[:online_friends_count]
-            part2 = self._contacts[online_friends_count:]
-            key_lambda = lambda x: x.name.lower() if sort_by_name else x.number
-            part1 = sorted(part1, key=key_lambda)
-            part2 = sorted(part2, key=key_lambda)
-            self._contacts = part1 + part2
-        elif sorting == 0:
-            # AttributeError: 'NoneType' object has no attribute 'number'
-            for (i, contact) in enumerate(self._contacts):
-                if contact is None or not hasattr(contact, 'number'):
-                    LOG.error(f"Contact {i} is None or not hasattr 'number'")
-                    del self._contacts[i]
-                    continue
-            contacts = sorted(self._contacts, key=lambda c: c.number)
-            friends = filter(lambda c: type(c) is Friend, contacts)
-            groups = filter(lambda c: type(c) is GroupChat, contacts)
-            group_peers = filter(lambda c: type(c) is GroupPeerContact, contacts)
-            self._contacts = list(friends) + list(groups) + list(group_peers)
-        elif sorting == 6:
-            self._contacts = sorted(self._contacts, key=lambda x: x._kind)
-        else:
-            self._contacts = sorted(self._contacts, key=lambda x: x.name.lower())
-
-
-        # change item widgets
-        for index, contact in enumerate(self._contacts):
-            list_item = self._screen.friends_list.item(index)
-            item_widget = self._screen.friends_list.itemWidget(list_item)
-            if not item_widget:
-                LOG_WARN("CM.filtration_and_sorting( item_widget is NULL")
-                continue
-            contact.set_widget(item_widget)
-
-        for index, friend in enumerate(self._contacts):
-            filtered_by_name = filter_str in friend.name.lower()
-            friend.visibility = (friend.status is not None or sorting not in (1, 4)) and filtered_by_name
-            # show friend even if it's hidden when there any unread messages/actions
-            friend.visibility = friend.visibility or friend.messages or friend.actions
-            item = self._screen.friends_list.item(index)
-            item_widget = self._screen.friends_list.itemWidget(item)
-            item.setSizeHint(QtCore.QSize(250, item_widget.height() if friend.visibility else 0))
-
-        # save soring results
-        self._sorting, self._filter_string = sorting, filter_str
-        self._settings['sorting'] = self._sorting
-        self._settings.save()
-
-        # update active contact
-        if current_contact is not None:
-            index = self._contacts.index(current_contact)
-            self.set_active(index)
-
-    def update_filtration(self):
-        """
-        Update list of contacts when 1 of friends change connection status
-        """
-        self.filtration_and_sorting(self._sorting, self._filter_string)
-
-    # Contact getters
-
-    def get_friend_by_number(self, number):
-        return list(filter(lambda c: c.number == number and type(c) is Friend, self._contacts))[0]
-
-    def get_group_by_number(self, number):
-        return list(filter(lambda c: c.number == number and type(c) is GroupChat, self._contacts))[0]
-
-    def get_or_create_group_peer_contact(self, group_number, peer_id):
-        group = self.get_group_by_number(group_number)
-        peer = group.get_peer_by_id(peer_id)
-        if peer is None:
-            LOG.warn(f'get_or_create_group_peer_contact group_number={group_number} peer_id={peer_id} peer={peer}')
-            return None
-        LOG.debug(f'get_or_create_group_peer_contact group_number={group_number} peer_id={peer_id} peer={peer}')
-        if not self.check_if_contact_exists(peer.public_key):
-            contact = self.add_group_peer(group, peer)
-            # dunno
-            return contact
-        # me - later wrong kind of object?
-        return self.get_contact_by_tox_id(peer.public_key)
-
-    def check_if_contact_exists(self, tox_id):
-        return any(filter(lambda c: c.tox_id == tox_id, self._contacts))
-
-    def get_contact_by_tox_id(self, tox_id):
-        return list(filter(lambda c: c.tox_id == tox_id, self._contacts))[0]
-
-    def get_active_number(self):
-        return self.get_curr_contact().number if self._active_contact + 1 else -1
-
-    def get_active_name(self):
-        return self.get_curr_contact().name if self._active_contact + 1 else ''
-
-    def is_active_online(self):
-        return self._active_contact + 1 and self.get_curr_contact().status is not None
-
-    # Work with friends (remove, block, set alias, get public key)
-
-    def set_alias(self, num):
-        """
-        Set new alias for friend
-        """
-        friend = self._contacts[num]
-        name = friend.name
-        text = util_ui.tr("Enter new alias for friend {} or leave empty to use friend's name:").format(name)
-        title = util_ui.tr('Set alias')
-        text, ok = util_ui.text_dialog(text, title, name)
-        if not ok:
-            return
-        aliases = self._settings['friends_aliases']
-        if text:
-            friend.name = text
-            try:
-                index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
-                aliases[index] = (friend.tox_id, text)
-            except:
-                aliases.append((friend.tox_id, text))
-            friend.set_alias(text)
-        else:  # use default name
-            friend.name = self._tox.friend_get_name(friend.number)
-            friend.set_alias('')
-            try:
-                index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
-                del aliases[index]
-            except:
-                pass
-        self._settings.save()
-
-    def friend_public_key(self, num):
-        return self._contacts[num].tox_id
-
-    def delete_friend(self, num):
-        """
-        Removes friend from contact list
-        :param num: number of friend in list
-        """
-        friend = self._contacts[num]
-        self._cleanup_contact_data(friend)
-        try:
-            self._tox.friend_delete(friend.number)
-        except Exception as e:
-            LOG.warn(f"'There was no friend with the given friend number {e}")
-        self._delete_contact(num)
-
-    def add_friend(self, tox_id):
-        """
-        Adds friend to list
-        """
-        self._tox.friend_add_norequest(tox_id)
-        self._add_friend(tox_id)
-        self.update_filtration()
-
-    def block_user(self, tox_id):
-        """
-        Block user with specified tox id (or public key) - delete from friends list and ignore friend requests
-        """
-        tox_id = tox_id[:enums.TOX_PUBLIC_KEY_SIZE * 2]
-        if tox_id == self._tox.self_get_address()[:enums.TOX_PUBLIC_KEY_SIZE * 2]:
-            return
-        if tox_id not in self._settings['blocked']:
-            self._settings['blocked'].append(tox_id)
-            self._settings.save()
-        try:
-            num = self._tox.friend_by_public_key(tox_id)
-            self.delete_friend(num)
-            self.save_profile()
-        except:  # not in friend list
-            pass
-
-    def unblock_user(self, tox_id, add_to_friend_list):
-        """
-        Unblock user
-        :param tox_id: tox id of contact
-        :param add_to_friend_list: add this contact to friend list or not
-        """
-        self._settings['blocked'].remove(tox_id)
-        self._settings.save()
-        if add_to_friend_list:
-            self.add_friend(tox_id)
-            self.save_profile()
-
-    # Groups support
-
-    def get_group_chats(self):
-        return list(filter(lambda c: type(c) is GroupChat, self._contacts))
-
-    def add_group(self, group_number):
-        index = len(self._contacts)
-        group = self._contact_provider.get_group_by_number(group_number)
-        if group is None:
-            LOG.warn(f"CM.add_group: NULL group from group_number={group_number}")
-        elif type(group) == int and group < 0:
-            LOG.warn(f"CM.add_group: NO group from group={group} group_number={group_number}")
-        else:
-            LOG.info(f"CM.add_group: Adding group {group._name}")
-            self._contacts.append(group)
-            LOG.info(f"contacts_manager.add_group: saving profile")
-            self._save_profile()
-            group.reset_avatar(self._settings['identicons'])
-            LOG.info(f"contacts_manager.add_group: setting active")
-            self.set_active(index)
-            self.update_filtration()
-
-    def delete_group(self, group_number):
-        group = self.get_group_by_number(group_number)
-        self._cleanup_contact_data(group)
-        num = self._contacts.index(group)
-        self._delete_contact(num)
-
-    # Groups private messaging
-
-    def add_group_peer(self, group, peer):
-        contact = self._contact_provider.get_group_peer_by_id(group, peer.id)
-        if self.check_if_contact_exists(contact.tox_id):
-            return contact
-        contact._kind = 'grouppeer'
-        self._contacts.append(contact)
-        contact.reset_avatar(self._settings['identicons'])
-        self._save_profile()
-        return contact
-
-    def remove_group_peer_by_id(self, group, peer_id):
-        peer = group.get_peer_by_id(peer_id)
-        if peer: # broken
-            if not self.check_if_contact_exists(peer.public_key):
-                return
-            contact = self.get_contact_by_tox_id(peer.public_key)
-            self.remove_group_peer(contact)
-
-    def remove_group_peer(self, group_peer_contact):
-        contact = self.get_contact_by_tox_id(group_peer_contact.tox_id)
-        if contact:
-            self._cleanup_contact_data(contact)
-            num = self._contacts.index(contact)
-            self._delete_contact(num)
-
-    def get_gc_peer_name(self, name):
-        group = self.get_curr_contact()
-
-        names = sorted(group.get_peers_names())
-        if name in names:  # return next nick
-            index = names.index(name)
-            index = (index + 1) % len(names)
-
-            return names[index]
-
-        suggested_names = list(filter(lambda x: x.startswith(name), names))
-        if not len(suggested_names):
-            return '\t'
-
-        return suggested_names[0]
-
-    # Friend requests
-
-    def send_friend_request(self, sToxPkOrId, message):
-        """
-        Function tries to send request to contact with specified id
-        :param sToxPkOrId: id of new contact or tox dns 4 value
-        :param message: additional message
-        :return: True on success else error string
-        """
-        retval = ''
-        try:
-            message = message or 'Hello! Add me to your contact list please'
-            if len(sToxPkOrId) == enums.TOX_PUBLIC_KEY_SIZE * 2:  # public key
-                self.add_friend(sToxPkOrId)
-                title = 'Friend added'
-                text = 'Friend added without sending friend request'
-            else:
-                num = self._tox.friend_add(sToxPkOrId, message.encode('utf-8'))
-                if num < UINT32_MAX:
-                    tox_pk = sToxPkOrId[:enums.TOX_PUBLIC_KEY_SIZE * 2]
-                    self._add_friend(tox_pk)
-                    self.update_filtration()
-                    title = 'Friend added'
-                    text = 'Friend added by sending friend request'
-                    self.save_profile()
-                    retval = True
-                else:
-                    title = 'Friend failed'
-                    text = 'Friend failed sending friend request'
-                    retval = text
-
-        except Exception as ex:  # wrong data
-            title = 'Friend add exception'
-            text = 'Friend request exception with ' + str(ex)
-            self._log(text)
-            LOG.exception(text)
-            LOG.warn(f"DELETE {sToxPkOrId} ?")
-            retval = str(ex)
-        title = util_ui.tr(title)
-        text = util_ui.tr(text)
-        util_ui.message_box(text, title)
-        return retval
-
-    def process_friend_request(self, tox_id, message):
-        """
-        Accept or ignore friend request
-        :param tox_id: tox id of contact
-        :param message: message
-        """
-        if tox_id in self._settings['blocked']:
-            return
-        try:
-            text = util_ui.tr('User {} wants to add you to contact list. Message:\n{}')
-            reply = util_ui.question(text.format(tox_id, message), util_ui.tr('Friend request'))
-            if reply:  # accepted
-                self.add_friend(tox_id)
-                data = self._tox.get_savedata()
-                self._profile_manager.save_profile(data)
-        except Exception as ex:  # something is wrong
-            LOG.error('Accept friend request failed! ' + str(ex))
-
-    def can_send_typing_notification(self):
-        return self._settings['typing_notifications'] and not self.is_active_a_group_chat_peer()
-
-    # Contacts numbers update
-
-    def update_friends_numbers(self):
-        for friend in self._contact_provider.get_all_friends():
-            friend.number = self._tox.friend_by_public_key(friend.tox_id)
-        self.update_filtration()
-
-    def update_groups_numbers(self):
-        groups = self._contact_provider.get_all_groups()
-        LOG.info(f"update_groups_numbers len(groups)={len(groups)}")
-        # Thread 76 "ToxIterateThrea" received signal SIGSEGV, Segmentation fault.
-        for i in range(len(groups)):
-            chat_id = self._tox.group_get_chat_id(i)
-            if not chat_id:
-                LOG.warn(f"update_groups_numbers {i} chat_id")
-                continue
-            group = self.get_contact_by_tox_id(chat_id)
-            if not group:
-                LOG.warn(f"update_groups_numbers {i} group")
-                continue
-            group.number = i
-        self.update_filtration()
-
-    def update_groups_lists(self):
-        groups = self._contact_provider.get_all_groups()
-        for group in groups:
-            group.remove_all_peers_except_self()
-
-    # Private methods
-
-    def _load_contacts(self):
-        self._load_friends()
-        self._load_groups()
-        if len(self._contacts):
-            self.set_active(0)
-        # filter(lambda c: not c.has_avatar(), self._contacts)
-        for (i, contact) in enumerate(self._contacts):
-            if contact is None:
-                LOG.warn(f"_load_contacts NULL contact {i}")
-                LOG.info(f"_load_contacts deleting NULL {self._contacts[i]}")
-                del self._contacts[i]
-                #? self.save_profile()
-                continue
-            if contact.has_avatar(): continue
-            contact.reset_avatar(self._settings['identicons'])
-        self.update_filtration()
-
-    def _load_friends(self):
-        self._contacts.extend(self._contact_provider.get_all_friends())
-
-    def _load_groups(self):
-        self._contacts.extend(self._contact_provider.get_all_groups())
-
-    # Current contact subscriptions
-
-    def _subscribe_to_events(self, contact):
-        contact.name_changed_event.add_callback(self._current_contact_name_changed)
-        contact.status_changed_event.add_callback(self._current_contact_status_changed)
-        contact.status_message_changed_event.add_callback(self._current_contact_status_message_changed)
-        contact.avatar_changed_event.add_callback(self._current_contact_avatar_changed)
-
-    def _unsubscribe_from_events(self, contact):
-        contact.name_changed_event.remove_callback(self._current_contact_name_changed)
-        contact.status_changed_event.remove_callback(self._current_contact_status_changed)
-        contact.status_message_changed_event.remove_callback(self._current_contact_status_message_changed)
-        contact.avatar_changed_event.remove_callback(self._current_contact_avatar_changed)
-
-    def _current_contact_name_changed(self, name):
-        self._screen.account_name.setText(name)
-
-    def _current_contact_status_changed(self, status):
-        pass
-
-    def _current_contact_status_message_changed(self, status_message):
-        self._screen.account_status.setText(status_message)
-
-    def _current_contact_avatar_changed(self, avatar_path):
-        self._set_current_contact_avatar(avatar_path)
-
-    def _set_current_contact_data(self, contact):
-        self._screen.account_name.setText(contact.name)
-        self._screen.account_status.setText(contact.status_message)
-        self._set_current_contact_avatar(contact.get_avatar_path())
-
-    def _set_current_contact_avatar(self, avatar_path):
-        width = self._screen.account_avatar.width()
-        pixmap = QtGui.QPixmap(avatar_path)
-        self._screen.account_avatar.setPixmap(pixmap.scaled(width, width,
-                                                            QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
-
-    def _add_friend(self, tox_id):
-        self._history.add_friend_to_db(tox_id)
-        friend = self._contact_provider.get_friend_by_public_key(tox_id)
-        index = len(self._contacts)
-        self._contacts.append(friend)
-        if not friend.has_avatar():
-            friend.reset_avatar(self._settings['identicons'])
-        self._save_profile()
-        self.set_active(index)
-
-    def _save_profile(self):
-        data = self._tox.get_savedata()
-        self._profile_manager.save_profile(data)
-
-    def _cleanup_contact_data(self, contact):
-        try:
-            index = list(map(lambda x: x[0], self._settings['friends_aliases'])).index(contact.tox_id)
-            del self._settings['friends_aliases'][index]
-        except Exception as e:
-            pass
-        if contact.tox_id in self._settings['notes']:
-            del self._settings['notes'][contact.tox_id]
-        self._settings.save()
-        self._history.delete_history(contact)
-        if contact.has_avatar():
-            avatar_path = contact.get_contact_avatar_path()
-            remove(avatar_path)
-
-    def _delete_contact(self, num):
-        self.set_active(-1 if len(self._contacts) == 1 else 0)
-
-        self._contact_provider.remove_contact_from_cache(self._contacts[num].tox_id)
-        del self._contacts[num]
-        self._screen.friends_list.takeItem(num)
-        self._save_profile()
-
-        self.update_filtration()
diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py
deleted file mode 100644
index 24b04ad..0000000
--- a/toxygen/contacts/friend.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import os
-
-from contacts import contact, common
-from messenger.messages import *
-from contacts.contact_menu import *
-
-class Friend(contact.Contact):
-    """
-    Friend in list of friends.
-    """
-
-    def __init__(self, profile_manager, message_getter, number, name, status_message, widget, tox_id):
-        super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id)
-        self._receipts = 0
-        self._typing_notification_handler = common.FriendTypingNotificationHandler(number)
-
-    # File transfers support
-
-    def insert_inline(self, before_message_id, inline):
-        """
-        Update status of active transfer and load inline if needed
-        """
-        try:
-            tr = list(filter(lambda m: m.message_id == before_message_id, self._corr))[0]
-            i = self._corr.index(tr)
-            if inline:  # inline was loaded
-                self._corr.insert(i, inline)
-            return i - len(self._corr)
-        except:
-            return -1
-
-    def get_unsent_files(self):
-        messages = filter(lambda m: type(m) is UnsentFileMessage, self._corr)
-        return list(messages)
-
-    def clear_unsent_files(self):
-        self._corr = list(filter(lambda m: type(m) is not UnsentFileMessage, self._corr))
-
-    def remove_invalid_unsent_files(self):
-        def is_valid(message):
-            if type(message) is not UnsentFileMessage:
-                return True
-            if message.data is not None:
-                return True
-            return os.path.exists(message.path)
-
-        self._corr = list(filter(is_valid, self._corr))
-
-    def delete_one_unsent_file(self, message_id):
-        self._corr = list(filter(lambda m: not (type(m) is UnsentFileMessage and m.message_id == message_id),
-                                 self._corr))
-
-    # Full status
-
-    def get_full_status(self):
-        return self._status_message
-
-    # Typing notifications
-
-    def get_typing_notification_handler(self):
-        return self._typing_notification_handler
-
-    # Context menu support
-
-    def get_context_menu_generator(self):
-        return FriendMenuGenerator(self)
diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py
deleted file mode 100644
index 31d5eec..0000000
--- a/toxygen/contacts/friend_factory.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from contacts.friend import Friend
-from common.tox_save import ToxSave
-
-class FriendFactory(ToxSave):
-
-    def __init__(self, profile_manager, settings, tox, db, items_factory):
-        super().__init__(tox)
-        self._profile_manager = profile_manager
-        self._settings = settings
-        self._db = db
-        self._items_factory = items_factory
-
-    def create_friend_by_public_key(self, public_key):
-        friend_number = self._tox.friend_by_public_key(public_key)
-        return self.create_friend_by_number(friend_number)
-
-    def create_friend_by_number(self, friend_number:int):
-        aliases = self._settings['friends_aliases']
-        sToxPk = self._tox.friend_get_public_key(friend_number)
-        assert sToxPk, sToxPk
-        try:
-            alias = list(filter(lambda x: x[0] == sToxPk, aliases))[0][1]
-        except:
-            alias = ''
-        item = self._create_friend_item()
-        name = alias or self._tox.friend_get_name(friend_number) or sToxPk
-        status_message = self._tox.friend_get_status_message(friend_number)
-        message_getter = self._db.messages_getter(sToxPk)
-        friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, sToxPk)
-        friend.set_alias(alias)
-
-        return friend
-
-    # Private methods
-
-    def _create_friend_item(self):
-        """
-        Method-factory
-        :return: new widget for friend instance
-        """
-        return self._items_factory.create_contact_item()
diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py
deleted file mode 100644
index c060e65..0000000
--- a/toxygen/contacts/group_chat.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from contacts import contact
-from contacts.contact_menu import GroupMenuGenerator
-import utils.util as util
-from groups.group_peer import GroupChatPeer
-from toxygen_wrapper import toxcore_enums_and_consts as constants
-from common.tox_save import ToxSave
-from groups.group_ban import GroupBan
-
-global LOG
-import logging
-LOG = logging.getLogger(__name__)
-from av.calls import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
-
-class GroupChat(contact.Contact, ToxSave):
-
-    def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id, is_private):
-        super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id)
-        ToxSave.__init__(self, tox)
-
-        self._is_private = is_private
-        self._password = str()
-        self._peers_limit = 512
-        self._peers = []
-        self._add_self_to_gc()
-
-    def remove_invalid_unsent_files(self):
-        pass
-
-    def get_context_menu_generator(self):
-        return GroupMenuGenerator(self)
-
-    # Properties
-
-    def get_is_private(self):
-        return self._is_private
-
-    def set_is_private(self, is_private):
-        self._is_private = is_private
-
-    is_private = property(get_is_private, set_is_private)
-
-    def get_password(self):
-        return self._password
-
-    def set_password(self, password):
-        self._password = password
-
-    password = property(get_password, set_password)
-
-    def get_peers_limit(self):
-        return self._peers_limit
-
-    def set_peers_limit(self, peers_limit):
-        self._peers_limit = peers_limit
-
-    peers_limit = property(get_peers_limit, set_peers_limit)
-
-    # Peers methods
-
-    def get_self_peer(self):
-        return self._peers[0]
-
-    def get_self_name(self):
-        return self._peers[0].name
-
-    def get_self_role(self):
-        return self._peers[0].role
-
-    def is_self_moderator_or_founder(self):
-        return self.get_self_role() <= constants.TOX_GROUP_ROLE['MODERATOR']
-
-    def is_self_founder(self):
-        return self.get_self_role() == constants.TOX_GROUP_ROLE['FOUNDER']
-
-    def add_peer(self, peer_id, is_current_user=False):
-        "called from callbacks"
-        if peer_id >  self._peers_limit:
-            LOG_WARN(f"add_peer id={peer_id} > {self._peers_limit}")
-            return
-
-        status_message = f"Private in {self.name}"
-        LOG_TRACE(f"GC.add_peer id={peer_id} status_message={status_message}")
-        peer = GroupChatPeer(peer_id,
-                             self._tox.group_peer_get_name(self._number, peer_id),
-                             self._tox.group_peer_get_status(self._number, peer_id),
-                             self._tox.group_peer_get_role(self._number, peer_id),
-                             self._tox.group_peer_get_public_key(self._number, peer_id),
-                             is_current_user,
-                             status_message=status_message)
-        self._peers.append(peer)
-
-    def remove_peer(self, peer_id):
-        if peer_id == self.get_self_peer().id:  # we were kicked or banned
-            self.remove_all_peers_except_self()
-        else:
-            peer = self.get_peer_by_id(peer_id)
-            if peer: # broken
-                self._peers.remove(peer)
-            else:
-                LOG_WARN(f"remove_peer empty peers for {peer_id}")
-
-    def get_peer_by_id(self, peer_id):
-        peers = list(filter(lambda p: p.id == peer_id, self._peers))
-        if peers:
-            return peers[0]
-        else:
-            LOG_WARN(f"get_peer_by_id empty peers for {peer_id}")
-            return None
-
-    def get_peer_by_public_key(self, public_key):
-        peers = list(filter(lambda p: p.public_key == public_key, self._peers))
-        # DEBUGc: group_moderation #0 mod_id=4294967295 event_type=3
-        # WARN_: get_peer_by_id empty peers for 4294967295
-        if peers:
-            return peers[0]
-        else:
-            LOG_WARN(f"get_peer_by_public_key empty peers for {public_key}")
-            return None
-
-    def remove_all_peers_except_self(self):
-        self._peers = self._peers[:1]
-
-    def get_peers_names(self):
-        peers_names = map(lambda p: p.name, self._peers)
-        if peers_names: # broken
-            return list(peers_names)
-        else:
-            LOG_WARN(f"get_peers_names empty peers")
-            #? broken
-            return []
-
-    def get_peers(self):
-        return self._peers[:]
-
-    peers = property(get_peers)
-
-    def get_bans(self):
-        return []
-#        ban_ids = self._tox.group_ban_get_list(self._number)
-#        bans = []
-#        for ban_id in ban_ids:
-#            ban = GroupBan(ban_id,
-#                           self._tox.group_ban_get_target(self._number, ban_id),
-#                           self._tox.group_ban_get_time_set(self._number, ban_id))
-#            bans.append(ban)
-#
-#        return bans
-#
-    bans = property(get_bans)
-
-    # Private methods
-
-    @staticmethod
-    def _get_default_avatar_path():
-        return util.join_path(util.get_images_directory(), 'group.png')
-
-    def _add_self_to_gc(self):
-        peer_id = self._tox.group_self_get_peer_id(self._number)
-        self.add_peer(peer_id, True)
diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py
deleted file mode 100644
index 4345c4b..0000000
--- a/toxygen/contacts/group_factory.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from contacts.group_chat import GroupChat
-from common.tox_save import ToxSave
-import toxygen_wrapper.toxcore_enums_and_consts as constants
-
-global LOG
-import logging
-LOG = logging.getLogger(__name__)
-
-class GroupFactory(ToxSave):
-
-    def __init__(self, profile_manager, settings, tox, db, items_factory):
-        super().__init__(tox)
-        self._profile_manager = profile_manager
-        self._settings = settings
-        self._db = db
-        self._items_factory = items_factory
-
-    def create_group_by_chat_id(self, chat_id):
-        return self.create_group_by_public_key(chat_id)
-
-    def create_group_by_public_key(self, public_key):
-        group_number = self._get_group_number_by_chat_id(public_key)
-        return self.create_group_by_number(group_number)
-
-    def create_group_by_number(self, group_number):
-        LOG.info(f"create_group_by_number {group_number}")
-        aliases = self._settings['friends_aliases']
-        tox_id = self._tox.group_get_chat_id(group_number)
-        try:
-            alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1]
-        except:
-            alias = ''
-        item = self._create_group_item()
-        name = alias or self._tox.group_get_name(group_number) or tox_id
-        status_message = self._tox.group_get_topic(group_number)
-        message_getter = self._db.messages_getter(tox_id)
-        is_private = self._tox.group_get_privacy_state(group_number) == constants.TOX_GROUP_PRIVACY_STATE['PRIVATE']
-        group = GroupChat(self._tox, self._profile_manager, message_getter, group_number, name, status_message,
-                          item, tox_id, is_private)
-        group.set_alias(alias)
-
-        return group
-
-    # Private methods
-
-    def _create_group_item(self):
-        """
-        Method-factory
-        :return: new widget for group instance
-        """
-        return self._items_factory.create_contact_item()
-
-    def _get_group_number_by_chat_id(self, chat_id):
-        for i in range(self._tox.group_get_number_groups()+100):
-            if self._tox.group_get_chat_id(i) == chat_id:
-                return i
-        return -1
diff --git a/toxygen/contacts/group_peer_contact.py b/toxygen/contacts/group_peer_contact.py
deleted file mode 100644
index 3e6131c..0000000
--- a/toxygen/contacts/group_peer_contact.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import contacts.contact
-from contacts.contact_menu import GroupPeerMenuGenerator
-
-class GroupPeerContact(contacts.contact.Contact):
-
-    def __init__(self, profile_manager, message_getter, peer_number, name, widget, tox_id, group_pk, status_message=None):
-        if status_message is None: status_message=str()
-        super().__init__(profile_manager, message_getter, peer_number, name, status_message, widget, tox_id)
-        self._group_pk = group_pk
-
-    def get_group_pk(self):
-        return self._group_pk
-
-    group_pk = property(get_group_pk)
-
-    def remove_invalid_unsent_files(self):
-        pass
-
-    def get_context_menu_generator(self):
-        return GroupPeerMenuGenerator(self)
diff --git a/toxygen/contacts/group_peer_factory.py b/toxygen/contacts/group_peer_factory.py
deleted file mode 100644
index 1804b50..0000000
--- a/toxygen/contacts/group_peer_factory.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from common.tox_save import ToxSave
-from contacts.group_peer_contact import GroupPeerContact
-
-class GroupPeerFactory(ToxSave):
-
-    def __init__(self, tox, profile_manager, db, items_factory):
-        super().__init__(tox)
-        self._profile_manager = profile_manager
-        self._db = db
-        self._items_factory = items_factory
-
-    def create_group_peer(self, group, peer):
-        item = self._create_group_peer_item()
-        message_getter = self._db.messages_getter(peer.public_key)
-        group_peer_contact = GroupPeerContact(self._profile_manager, message_getter, peer.id, peer.name,
-                                              item,
-                                              peer.public_key,
-                                              group.tox_id,
-                                              status_message=peer.status_message)
-        group_peer_contact.status = peer.status
-
-        return group_peer_contact
-
-    def _create_group_peer_item(self):
-        return self._items_factory.create_contact_item()
diff --git a/toxygen/contacts/profile.py b/toxygen/contacts/profile.py
deleted file mode 100644
index 3afcf2b..0000000
--- a/toxygen/contacts/profile.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from contacts import basecontact
-import random
-import threading
-import common.tox_save as tox_save
-from middleware.threads import invoke_in_main_thread
-
-iUMAXINT = 4294967295
-iRECONNECT = 50
-
-global LOG
-import logging
-LOG = logging.getLogger('app.'+__name__)
-
-class Profile(basecontact.BaseContact, tox_save.ToxSave):
-    """
-    Profile of current toxygen user.
-    """
-    def __init__(self, profile_manager, tox, screen, contacts_provider, reset_action, app=None):
-        """
-        :param tox: tox instance
-        :param screen: ref to main screen
-        """
-        assert tox
-        basecontact.BaseContact.__init__(self,
-                                         profile_manager,
-                                         tox.self_get_name(),
-                                         tox.self_get_status_message(),
-                                         screen,
-                                         tox.self_get_address())
-        tox_save.ToxSave.__init__(self, tox)
-        self._screen = screen
-        self._messages = screen.messages
-        self._contacts_provider = contacts_provider
-        self._reset_action = reset_action
-        self._waiting_for_reconnection = False
-        self._timer = None
-        self._app = app
-
-    # Edit current user's data
-
-    def change_status(self) -> None:
-        """
-        Changes status of user (online, away, busy)
-        """
-        if self._status is not None:
-            self.set_status((self._status + 1) % 3)
-
-    def set_status(self, status) -> None:
-        super().set_status(status)
-        if status is not None:
-            self._tox.self_set_status(status)
-        elif not self._waiting_for_reconnection:
-            self._waiting_for_reconnection = True
-            self._timer = threading.Timer(iRECONNECT, self._reconnect)
-            self._timer.start()
-
-    def set_name(self, value) -> None:
-        if self.name == value:
-            return
-        super().set_name(value)
-        self._tox.self_set_name(self._name)
-
-    def set_status_message(self, value) -> None:
-        super().set_status_message(value)
-        self._tox.self_set_status_message(self._status_message)
-
-    def set_new_nospam(self):
-        """Sets new nospam part of tox id"""
-        self._tox.self_set_nospam(random.randint(0, iUMAXINT))  # no spam - uint32
-        self._tox_id = self._tox.self_get_address()
-        self._sToxId = self._tox.self_get_address()
-        return self._sToxId
-
-    # Reset
-
-    def restart(self) -> None:
-        """
-        Recreate tox instance
-        """
-        self.status = None
-        invoke_in_main_thread(self._reset_action)
-
-    def _reconnect(self) -> None:
-        self._waiting_for_reconnection = False
-        if self._app and self._app.bAppExiting:
-            # dont do anything after the app has been shipped
-            # there's a segv that results
-            return
-        contacts = self._contacts_provider.get_all_friends()
-        all_friends_offline = all(list(map(lambda x: x.status is None, contacts)))
-        if self.status is None or (all_friends_offline and len(contacts)):
-            self._waiting_for_reconnection = True
-            self.restart()
-            self._timer = threading.Timer(iRECONNECT, self._reconnect)
-            self._timer.start()
-
-# Current thread 0x00007901a13ccb80 (most recent call first):
-#   File "/usr/local/lib/python3.11/site-packages/toxygen_wrapper/tox.py", line 826 in self_get_friend_list_size
-#   File "/usr/local/lib/python3.11/site-packages/toxygen_wrapper/tox.py", line 838 in self_get_friend_list
-#   File "/mnt/o/var/local/src/toxygen/toxygen/contacts/contact_provider.py", line 45 in get_all_friends
-#   File "/mnt/o/var/local/src/toxygen/toxygen/contacts/profile.py", line 90 in _reconnect
-#   File "/usr/lib/python3.11/threading.py", line 1401 in run
-#   File "/usr/lib/python3.11/threading.py", line 1045 in _bootstrap_inner
-#   File "/usr/lib/python3.11/threading.py", line 1002 in _bootstrap
-#
-
diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers.py
similarity index 58%
rename from toxygen/file_transfers/file_transfers.py
rename to toxygen/file_transfers.py
index 5fa87f9..2c1f73e 100644
--- a/toxygen/file_transfers/file_transfers.py
+++ b/toxygen/file_transfers.py
@@ -1,25 +1,25 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import os
-from os import chdir, remove, rename
+from toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL
 from os.path import basename, getsize, exists, dirname
-from time import time
+from os import remove, rename, chdir
+from time import time, sleep
+from tox import Tox
+import settings
+try:
+    from PySide import QtCore
+except ImportError:
+    from PyQt4 import QtCore
 
-from common.event import Event
-from middleware.threads import invoke_in_main_thread
-from toxygen_wrapper.tox import Tox
-from toxygen_wrapper.toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL
-from middleware.callbacks import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
+# TODO: threads!
 
-FILE_TRANSFER_STATE = {
+
+TOX_FILE_TRANSFER_STATE = {
     'RUNNING': 0,
     'PAUSED_BY_USER': 1,
     'CANCELLED': 2,
     'FINISHED': 3,
     'PAUSED_BY_FRIEND': 4,
     'INCOMING_NOT_STARTED': 5,
-    'OUTGOING_NOT_STARTED': 6,
-    'UNSENT': 7
+    'OUTGOING_NOT_STARTED': 6
 }
 
 ACTIVE_FILE_TRANSFERS = (0, 1, 4, 5, 6)
@@ -30,122 +30,98 @@ DO_NOT_SHOW_ACCEPT_BUTTON = (2, 3, 4, 6)
 
 SHOW_PROGRESS_BAR = (0, 1, 4)
 
-
-def is_inline(file_name):
-    allowed_inlines = ('toxygen_inline.png', 'utox-inline.png', 'sticker.png')
-
-    return file_name in allowed_inlines or file_name.startswith('qTox_Image_')
+ALLOWED_FILES = ('toxygen_inline.png', 'utox-inline.png', 'sticker.png')
 
 
-class FileTransfer:
+class StateSignal(QtCore.QObject):
+    try:
+        signal = QtCore.Signal(int, float, int)  # state and progress
+    except:
+        signal = QtCore.pyqtSignal(int, float, int)  # state and progress - pyqt4
+
+
+class FileTransfer(QtCore.QObject):
     """
     Superclass for file transfers
     """
 
     def __init__(self, path, tox, friend_number, size, file_number=None):
+        QtCore.QObject.__init__(self)
         self._path = path
         self._tox = tox
         self._friend_number = friend_number
-        self._state = FILE_TRANSFER_STATE['RUNNING']
+        self.state = TOX_FILE_TRANSFER_STATE['RUNNING']
         self._file_number = file_number
         self._creation_time = None
         self._size = float(size)
         self._done = 0
-        self._state_changed_event = Event()
-        self._finished_event = Event()
-        self._file_id = self._file = None
+        self._state_changed = StateSignal()
+
+    def set_tox(self, tox):
+        self._tox = tox
 
     def set_state_changed_handler(self, handler):
-        self._state_changed_event += lambda *args: invoke_in_main_thread(handler, *args)
+        self._state_changed.signal.connect(handler)
 
-    def set_transfer_finished_handler(self, handler):
-        self._finished_event += lambda *args: invoke_in_main_thread(handler, *args)
-
-    def get_file_number(self):
-        return self._file_number
-
-    file_number = property(get_file_number)
-
-    def get_state(self):
-        return self._state
-
-    def set_state(self, value):
-        self._state = value
-        self._signal()
-
-    state = property(get_state, set_state)
-
-    def get_friend_number(self):
-        return self._friend_number
-
-    friend_number = property(get_friend_number)
-
-    def get_file_id(self):
-        return self._file_id
-#?        return self._tox.file_get_file_id(self._friend_number, self._file_number)
-
-    file_id = property(get_file_id)
-
-    def get_path(self):
-        return self._path
-
-    path = property(get_path)
-
-    def get_size(self):
-        return self._size
-
-    size = property(get_size)
-
-    def cancel(self):
-        self.send_control(TOX_FILE_CONTROL['CANCEL'])
-        if self._file is not None:
-            self._file.close()
-        self._signal()
-
-    def cancelled(self):
-        if self._file is not None:
-            self._file.close()
-        self.set_state(FILE_TRANSFER_STATE['CANCELLED'])
-
-    def pause(self, by_friend):
-        if not by_friend:
-            self.send_control(TOX_FILE_CONTROL['PAUSE'])
-        else:
-            self.set_state(FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'])
-
-    def send_control(self, control):
-        if self._tox.file_control(self._friend_number, self._file_number, control):
-            self.set_state(control)
-
-    def _signal(self):
+    def signal(self):
         percentage = self._done / self._size if self._size else 0
         if self._creation_time is None or not percentage:
             t = -1
         else:
             t = ((time() - self._creation_time) / percentage) * (1 - percentage)
-        self._state_changed_event(self.state, percentage, int(t))
+        self._state_changed.signal.emit(self.state, percentage, int(t))
 
-    def _finished(self):
-        self._finished_event(self._friend_number, self._file_number)
+    def get_file_number(self):
+        return self._file_number
 
+    def get_friend_number(self):
+        return self._friend_number
+
+    def cancel(self):
+        self.send_control(TOX_FILE_CONTROL['CANCEL'])
+        if hasattr(self, '_file'):
+            self._file.close()
+        self.signal()
+
+    def cancelled(self):
+        if hasattr(self, '_file'):
+            sleep(0.1)
+            self._file.close()
+        self.state = TOX_FILE_TRANSFER_STATE['CANCELLED']
+        self.signal()
+
+    def pause(self, by_friend):
+        if not by_friend:
+            self.send_control(TOX_FILE_CONTROL['PAUSE'])
+        else:
+            self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']
+        self.signal()
+
+    def send_control(self, control):
+        if self._tox.file_control(self._friend_number, self._file_number, control):
+            self.state = control
+            self.signal()
+
+    def get_file_id(self):
+        return self._tox.file_get_file_id(self._friend_number, self._file_number)
+
+# -----------------------------------------------------------------------------------------------------------------
 # Send file
+# -----------------------------------------------------------------------------------------------------------------
 
 
 class SendTransfer(FileTransfer):
 
     def __init__(self, path, tox, friend_number, kind=TOX_FILE_KIND['DATA'], file_id=None):
         if path is not None:
-            fl = open(path, 'rb')
+            self._file = open(path, 'rb')
             size = getsize(path)
         else:
-            fl = None
             size = 0
-        super().__init__(path, tox, friend_number, size)
-        self._file = fl
-        self.state = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']
+        super(SendTransfer, self).__init__(path, tox, friend_number, size)
+        self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']
         self._file_number = tox.file_send(friend_number, kind, size, file_id,
                                           bytes(basename(path), 'utf-8') if path else b'')
-        self._file_id = self.get_file_id()
 
     def send_chunk(self, position, size):
         """
@@ -160,12 +136,12 @@ class SendTransfer(FileTransfer):
             data = self._file.read(size)
             self._tox.file_send_chunk(self._friend_number, self._file_number, position, data)
             self._done += size
-            self._signal()
+            self.signal()
         else:
-            if self._file is not None:
+            if hasattr(self, '_file'):
                 self._file.close()
-            self.state = FILE_TRANSFER_STATE['FINISHED']
-            self._finished()
+            self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
+            self.signal()
 
 
 class SendAvatar(SendTransfer):
@@ -174,15 +150,12 @@ class SendAvatar(SendTransfer):
     """
 
     def __init__(self, path, tox, friend_number):
-        LOG_DEBUG(f"SendAvatar path={path} friend_number={friend_number}")
-        if path is None or not os.path.exists(path):
-            avatar_hash = None
+        if path is None:
+            hash = None
         else:
             with open(path, 'rb') as fl:
-                data=fl.read()
-            LOG_DEBUG(f"SendAvatar data={data} type={type(data)}")
-            avatar_hash = tox.hash(data, None)
-        super().__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], avatar_hash)
+                hash = Tox.hash(fl.read())
+        super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], hash)
 
 
 class SendFromBuffer(FileTransfer):
@@ -191,8 +164,8 @@ class SendFromBuffer(FileTransfer):
     """
 
     def __init__(self, tox, friend_number, data, file_name):
-        super().__init__(None, tox, friend_number, len(data))
-        self.state = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']
+        super(SendFromBuffer, self).__init__(None, tox, friend_number, len(data))
+        self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']
         self._data = data
         self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'],
                                           len(data), None, bytes(file_name, 'utf-8'))
@@ -200,8 +173,6 @@ class SendFromBuffer(FileTransfer):
     def get_data(self):
         return self._data
 
-    data = property(get_data)
-
     def send_chunk(self, position, size):
         if self._creation_time is None:
             self._creation_time = time()
@@ -209,46 +180,40 @@ class SendFromBuffer(FileTransfer):
             data = self._data[position:position + size]
             self._tox.file_send_chunk(self._friend_number, self._file_number, position, data)
             self._done += size
+            self.signal()
         else:
-            self.state = FILE_TRANSFER_STATE['FINISHED']
-            self._finished()
-        self._signal()
+            self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
+            self.signal()
 
 
 class SendFromFileBuffer(SendTransfer):
 
     def __init__(self, *args):
-        super().__init__(*args)
+        super(SendFromFileBuffer, self).__init__(*args)
 
     def send_chunk(self, position, size):
-        super().send_chunk(position, size)
+        super(SendFromFileBuffer, self).send_chunk(position, size)
         if not size:
-            os.chdir(dirname(self._path))
-            os.remove(self._path)
+            chdir(dirname(self._path))
+            remove(self._path)
 
+# -----------------------------------------------------------------------------------------------------------------
 # Receive file
+# -----------------------------------------------------------------------------------------------------------------
 
 
 class ReceiveTransfer(FileTransfer):
 
-    def __init__(self, path, tox, friend_number, size, file_number, position=0):
-        super().__init__(path, tox, friend_number, size, file_number)
+    def __init__(self, path, tox, friend_number, size, file_number):
+        super(ReceiveTransfer, self).__init__(path, tox, friend_number, size, file_number)
         self._file = open(self._path, 'wb')
-        self._file_size = position
-        self._file.truncate(position)
-        self._missed = set()
-        self._file_id = self.get_file_id()
-        self._done = position
+        self._file.truncate(0)
+        self._file_size = 0
 
     def cancel(self):
-        super().cancel()
+        super(ReceiveTransfer, self).cancel()
         remove(self._path)
 
-    def total_size(self):
-        self._missed.add(self._file_size)
-
-        return min(self._missed)
-
     def write_chunk(self, position, data):
         """
         Incoming chunk
@@ -259,23 +224,20 @@ class ReceiveTransfer(FileTransfer):
             self._creation_time = time()
         if data is None:
             self._file.close()
-            self.state = FILE_TRANSFER_STATE['FINISHED']
-            self._finished()
+            self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
+            self.signal()
         else:
             data = bytearray(data)
             if self._file_size < position:
                 self._file.seek(0, 2)
                 self._file.write(b'\0' * (position - self._file_size))
-                self._missed.add(self._file_size)
-            else:
-                self._missed.discard(position)
             self._file.seek(position)
             self._file.write(data)
             l = len(data)
             if position + l > self._file_size:
                 self._file_size = position + l
             self._done += l
-        self._signal()
+            self.signal()
 
 
 class ReceiveToBuffer(FileTransfer):
@@ -284,21 +246,18 @@ class ReceiveToBuffer(FileTransfer):
     """
 
     def __init__(self, tox, friend_number, size, file_number):
-        super().__init__(None, tox, friend_number, size, file_number)
+        super(ReceiveToBuffer, self).__init__(None, tox, friend_number, size, file_number)
         self._data = bytes()
         self._data_size = 0
 
     def get_data(self):
         return self._data
 
-    data = property(get_data)
-
     def write_chunk(self, position, data):
         if self._creation_time is None:
             self._creation_time = time()
         if data is None:
-            self.state = FILE_TRANSFER_STATE['FINISHED']
-            self._finished()
+            self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
         else:
             data = bytes(data)
             l = len(data)
@@ -308,7 +267,7 @@ class ReceiveToBuffer(FileTransfer):
             if position + l > self._data_size:
                 self._data_size = position + l
             self._done += l
-        self._signal()
+        self.signal()
 
 
 class ReceiveAvatar(ReceiveTransfer):
@@ -316,36 +275,40 @@ class ReceiveAvatar(ReceiveTransfer):
     Get friend's avatar. Doesn't need file transfer item
     """
     MAX_AVATAR_SIZE = 512 * 1024
-    def __init__(self, path, tox, friend_number, size, file_number):
-        full_path = path + '.tmp'
-        super().__init__(full_path, tox, friend_number, size, file_number)
+
+    def __init__(self, tox, friend_number, size, file_number):
+        path = settings.ProfileHelper.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number))
+        super(ReceiveAvatar, self).__init__(path + '.tmp', tox, friend_number, size, file_number)
         if size > self.MAX_AVATAR_SIZE:
             self.send_control(TOX_FILE_CONTROL['CANCEL'])
             self._file.close()
-            remove(full_path)
+            remove(path + '.tmp')
         elif not size:
             self.send_control(TOX_FILE_CONTROL['CANCEL'])
             self._file.close()
-            remove(full_path)
+            if exists(path):
+                remove(path)
+            self._file.close()
+            remove(path + '.tmp')
         elif exists(path):
-            ihash = self.get_file_id()
+            hash = self.get_file_id()
             with open(path, 'rb') as fl:
                 data = fl.read()
             existing_hash = Tox.hash(data)
-            if ihash == existing_hash:
+            if hash == existing_hash:
                 self.send_control(TOX_FILE_CONTROL['CANCEL'])
                 self._file.close()
-                remove(full_path)
+                remove(path + '.tmp')
             else:
                 self.send_control(TOX_FILE_CONTROL['RESUME'])
         else:
             self.send_control(TOX_FILE_CONTROL['RESUME'])
 
     def write_chunk(self, position, data):
-        if data is None:
+        super(ReceiveAvatar, self).write_chunk(position, data)
+        if self.state:
             avatar_path = self._path[:-4]
             if exists(avatar_path):
                 chdir(dirname(avatar_path))
                 remove(avatar_path)
             rename(self._path, avatar_path)
-        super().write_chunk(position, data)
diff --git a/toxygen/file_transfers/__init__.py b/toxygen/file_transfers/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py
deleted file mode 100644
index a9085c2..0000000
--- a/toxygen/file_transfers/file_transfers_handler.py
+++ /dev/null
@@ -1,371 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import logging
-
-from messenger.messages import *
-from file_transfers.file_transfers import SendAvatar, is_inline
-from ui.contact_items import *
-import utils.util as util
-from common.tox_save import ToxSave
-
-from middleware.callbacks import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
-
-# LOG=util.log
-global LOG
-LOG = logging.getLogger('app.'+__name__)
-log = lambda x: LOG.info(x)
-
-class FileTransfersHandler(ToxSave):
-    lBlockAvatars = []
-    def __init__(self, tox, settings, contact_provider, file_transfers_message_service, profile):
-        super().__init__(tox)
-        self._settings = settings
-        self._contact_provider = contact_provider
-        self._file_transfers_message_service = file_transfers_message_service
-        self._file_transfers = {}
-        # key = (friend number, file number), value - transfer instance
-        self._paused_file_transfers = dict(settings['paused_file_transfers'])
-        # key - file id, value: [path, friend number, is incoming, start position]
-        self._insert_inline_before = {}
-        # key = (friend number, file number), value - message id
-
-        profile.avatar_changed_event.add_callback(self._send_avatar_to_contacts)
-        self. lBlockAvatars = []
-
-    def stop(self) -> None:
-        self._settings['paused_file_transfers'] = self._paused_file_transfers if self._settings['resend_files'] else {}
-        self._settings.save()
-
-    # File transfers support
-
-    def incoming_file_transfer(self, friend_number, file_number, size, file_name) -> None:
-        # main thread
-        """
-        New transfer
-        :param friend_number: number of friend who sent file
-        :param file_number: file number
-        :param size: file size in bytes
-        :param file_name: file name without path
-        """
-        friend = self._get_friend_by_number(friend_number)
-        if friend is None:
-            LOG.info(f'incoming_file_handler Friend NULL friend_number={friend_number}')
-            return
-        auto = self._settings['allow_auto_accept'] and friend.tox_id in self._settings['auto_accept_from_friends']
-        inline = False # ?is_inline(file_name) and self._settings['allow_inline']
-        file_id = self._tox.file_get_file_id(friend_number, file_number)
-        accepted = True
-        if file_id in self._paused_file_transfers:
-            LOG_INFO(f'incoming_file_handler paused friend_number={friend_number}')
-            (path, ft_friend_number, is_incoming, start_position) = self._paused_file_transfers[file_id]
-            pos = start_position if os.path.exists(path) else 0
-            if pos >= size:
-                self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
-                return
-            self._tox.file_seek(friend_number, file_number, pos)
-            self._file_transfers_message_service.add_incoming_transfer_message(
-                friend, accepted, size, file_name, file_number)
-            self.accept_transfer(path, friend_number, file_number, size, False, pos)
-        elif inline and size < 1024 * 1024:
-            LOG_INFO(f'incoming_file_handler small friend_number={friend_number}')
-            self._file_transfers_message_service.add_incoming_transfer_message(
-                friend, accepted, size, file_name, file_number)
-            self.accept_transfer('', friend_number, file_number, size, True)
-        elif auto:
-            # accepted is really started
-            LOG_INFO(f'incoming_file_handler auto friend_number={friend_number}')
-            path = self._settings['auto_accept_path'] or util.curr_directory()
-            self._file_transfers_message_service.add_incoming_transfer_message(
-                friend, accepted, size, file_name, file_number)
-            self.accept_transfer(path + '/' + file_name, friend_number, file_number, size)
-        else:
-            LOG_INFO(f'incoming_file_handler reject friend_number={friend_number}')
-            accepted = False
-            # FixME: need GUI ask
-            # accepted is really started
-            self._file_transfers_message_service.add_incoming_transfer_message(
-                friend, accepted, size, file_name, file_number)
-
-    def cancel_transfer(self, friend_number, file_number, already_cancelled=False) -> None:
-        """
-        Stop transfer
-        :param friend_number: number of friend
-        :param file_number: file number
-        :param already_cancelled: was cancelled by friend
-        """
-        # callback
-        if (friend_number, file_number) in self._file_transfers:
-            tr = self._file_transfers[(friend_number, file_number)]
-            if not already_cancelled:
-                tr.cancel()
-            else:
-                tr.cancelled()
-            if (friend_number, file_number) in self._file_transfers:
-                del tr
-                del self._file_transfers[(friend_number, file_number)]
-        elif not already_cancelled:
-            self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
-
-    def cancel_not_started_transfer(self, friend_number, message_id) -> None:
-        friend = self._get_friend_by_number(friend_number)
-        if friend is None: return None
-        friend.delete_one_unsent_file(message_id)
-
-    def pause_transfer(self, friend_number, file_number, by_friend=False) -> None:
-        """
-        Pause transfer with specified data
-        """
-        tr = self._file_transfers[(friend_number, file_number)]
-        tr.pause(by_friend)
-
-    def resume_transfer(self, friend_number, file_number, by_friend=False) -> None:
-        """
-        Resume transfer with specified data
-        """
-        tr = self._file_transfers[(friend_number, file_number)]
-        if by_friend:
-            tr.state = FILE_TRANSFER_STATE['RUNNING']
-        else:
-            tr.send_control(TOX_FILE_CONTROL['RESUME'])
-
-    def accept_transfer(self, path, friend_number, file_number, size, inline=False, from_position=0) -> None:
-        """
-        :param path: path for saving
-        :param friend_number: friend number
-        :param file_number: file number
-        :param size: file size
-        :param inline: is inline image
-        :param from_position: position for start
-        """
-        path = self._generate_valid_path(path, from_position)
-        friend = self._get_friend_by_number(friend_number)
-        if friend is None: return None
-        if not inline:
-            rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position)
-        else:
-            rt = ReceiveToBuffer(self._tox, friend_number, size, file_number)
-        rt.set_transfer_finished_handler(self.transfer_finished)
-        message = friend.get_message(lambda m: m.type == MESSAGE_TYPE['FILE_TRANSFER']
-                                               and m.state in (FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'],
-                                                               FILE_TRANSFER_STATE['RUNNING'])
-                                               and m.file_number == file_number)
-        rt.set_state_changed_handler(message.transfer_updated)
-        self._file_transfers[(friend_number, file_number)] = rt
-        rt.send_control(TOX_FILE_CONTROL['RESUME'])
-        if inline:
-            self._insert_inline_before[(friend_number, file_number)] = message.message_id
-
-    def send_screenshot(self, data, friend_number) -> None:
-        """
-        Send screenshot
-        :param data: raw data - png format
-        :param friend_number: friend number
-        """
-        self.send_inline(data, 'toxygen_inline.png', friend_number)
-
-    def send_sticker(self, path, friend_number) -> None:
-        with open(path, 'rb') as fl:
-            data = fl.read()
-        self.send_inline(data, 'sticker.png', friend_number)
-
-    def send_inline(self, data, file_name, friend_number, is_resend=False) -> None:
-        friend = self._get_friend_by_number(friend_number)
-        if friend is None:
-            LOG_WARN("fsend_inline Error friend is None file_name: {file_name}")
-            return
-        if friend.status is None and not is_resend:
-            self._file_transfers_message_service.add_unsent_file_message(friend, file_name, data)
-            return
-        elif friend.status is None and is_resend:
-            LOG_WARN("fsend_inline Error friend.status is None file_name: {file_name}")
-            return
-        st = SendFromBuffer(self._tox, friend.number, data, file_name)
-        self._send_file_add_set_handlers(st, friend, file_name, True)
-
-    def send_file(self, path, friend_number, is_resend=False, file_id=None) -> None:
-        """
-        Send file to current active friend
-        :param path: file path
-        :param friend_number: friend_number
-        :param is_resend: is 'offline' message
-        :param file_id: file id of transfer
-        """
-        friend = self._get_friend_by_number(friend_number)
-        if friend is None: return None
-        if friend.status is None and not is_resend:
-            self._file_transfers_message_service.add_unsent_file_message(friend, path, None)
-            return
-        elif friend.status is None and is_resend:
-            LOG_WARN('Error in sending')
-            return
-        st = SendTransfer(path, self._tox, friend_number, TOX_FILE_KIND['DATA'], file_id)
-        file_name = os.path.basename(path)
-        self._send_file_add_set_handlers(st, friend, file_name)
-
-    def incoming_chunk(self, friend_number, file_number, position, data) -> None:
-        """
-        Incoming chunk
-        """
-        self._file_transfers[(friend_number, file_number)].write_chunk(position, data)
-
-    def outgoing_chunk(self, friend_number, file_number, position, size) -> None:
-        """
-        Outgoing chunk
-        """
-        self._file_transfers[(friend_number, file_number)].send_chunk(position, size)
-
-    def transfer_finished(self, friend_number, file_number) -> None:
-        transfer = self._file_transfers[(friend_number, file_number)]
-        friend = self._get_friend_by_number(friend_number)
-        if friend is None: return None
-        t = type(transfer)
-        if t is ReceiveAvatar:
-            friend.load_avatar()
-        elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']):  # inline image
-            LOG.debug('inline')
-            inline = InlineImageMessage(transfer.data)
-            message_id = self._insert_inline_before[(friend_number, file_number)]
-            del self._insert_inline_before[(friend_number, file_number)]
-            if friend is None: return None
-            index = friend.insert_inline(message_id, inline)
-            self._file_transfers_message_service.add_inline_message(transfer, index)
-        del self._file_transfers[(friend_number, file_number)]
-
-    def send_files(self, friend_number:int) -> None:
-        try:
-            friend = self._get_friend_by_number(friend_number)
-            if friend is None: return
-            friend.remove_invalid_unsent_files()
-            files = friend.get_unsent_files()
-            for fl in files:
-                data, path = fl.data, fl.path
-                if data is not None:
-                    self.send_inline(data, path, friend_number, True)
-                else:
-                    self.send_file(path, friend_number, True)
-            friend.clear_unsent_files()
-            for key in self._paused_file_transfers.keys():
-                # RuntimeError: dictionary changed size during iteration
-                (path, ft_friend_number, is_incoming, start_position) = self._paused_file_transfers[key]
-                if not os.path.exists(path):
-                    del self._paused_file_transfers[key]
-                elif ft_friend_number == friend_number and not is_incoming:
-                    self.send_file(path, friend_number, True, key)
-                    del self._paused_file_transfers[key]
-        except Exception as ex:
-            LOG_ERROR('send_files EXCEPTION in file sending: ' + str(ex))
-
-    def friend_exit(self, friend_number:int) -> None:
-        # RuntimeError: dictionary changed size during iteration
-        lMayChangeDynamically = self._file_transfers.copy()
-        for friend_num, file_num in lMayChangeDynamically:
-            if friend_num != friend_number:
-                continue
-            if (friend_num, file_num) not in self._file_transfers:
-                continue
-            ft = self._file_transfers[(friend_num, file_num)]
-            if type(ft) is SendTransfer:
-                try:
-                    file_id = ft.file_id
-                except Exception as e:
-                    LOG_WARN("friend_exit SendTransfer Error getting file_id: {e}")
-                    # drop through
-                else:
-                    self._paused_file_transfers[file_id] = [ft.path, friend_num, False, -1]
-            elif type(ft) is ReceiveTransfer and ft.state != FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']:
-                try:
-                    file_id = ft.file_id
-                except Exception as e:
-                    LOG_WARN("friend_exit ReceiveTransfer Error getting file_id: {e}")
-                    # drop through
-                else:
-                    self._paused_file_transfers[file_id] = [ft.path, friend_num, True, ft.total_size()]
-            self.cancel_transfer(friend_num, file_num, True)
-
-    # Avatars support
-
-    def send_avatar(self, friend_number, avatar_path=None) -> None:
-        """
-        :param friend_number: number of friend who should get new avatar
-        :param avatar_path: path to avatar or None if reset
-        """
-        return
-        if (avatar_path, friend_number,) in self.lBlockAvatars:
-            return
-        if friend_number is None:
-            LOG_WARN(f"send_avatar friend_number NULL {friend_number}")
-            return
-        if avatar_path and type(avatar_path) != str:
-            LOG_WARN(f"send_avatar avatar_path type {type(avatar_path)}")
-            return
-        LOG_INFO(f"send_avatar avatar_path={avatar_path} friend_number={friend_number}")
-        try:
-            #  self NOT missing - who's self?
-            sa = SendAvatar(avatar_path, self._tox, friend_number)
-            LOG_INFO(f"send_avatar avatar_path={avatar_path} sa={sa}")
-            self._file_transfers[(friend_number, sa.file_number)] = sa
-        except Exception as e:
-            # ArgumentError('This client is currently not connected to the friend.')
-            LOG_WARN(f"send_avatar EXCEPTION {e}")
-            self.lBlockAvatars.append( (avatar_path, friend_number,) )
-
-    def incoming_avatar(self, friend_number, file_number, size) -> None:
-        """
-        Friend changed avatar
-        :param friend_number: friend number
-        :param file_number: file number
-        :param size: size of avatar or 0 (default avatar)
-        """
-        friend = self._get_friend_by_number(friend_number)
-        if friend is None: return
-        ra = ReceiveAvatar(friend.get_contact_avatar_path(), self._tox, friend_number, size, file_number)
-        if ra.state != FILE_TRANSFER_STATE['CANCELLED']:
-            self._file_transfers[(friend_number, file_number)] = ra
-            ra.set_transfer_finished_handler(self.transfer_finished)
-        elif not size:
-            friend.reset_avatar(self._settings['identicons'])
-
-    def _send_avatar_to_contacts(self, _) -> None:
-        # from a callback
-        friends = self._get_all_friends()
-        for friend in filter(self._is_friend_online, friends):
-            self.send_avatar(friend.number)
-
-    # Private methods
-
-    def _is_friend_online(self, friend_number:int) -> bool:
-        friend = self._get_friend_by_number(friend_number)
-        if friend is None: return None
-
-        return friend.status is not None
-
-    def _get_friend_by_number(self, friend_number:int):
-        return self._contact_provider.get_friend_by_number(friend_number)
-
-    def _get_all_friends(self):
-        return self._contact_provider.get_all_friends()
-
-    def _send_file_add_set_handlers(self, st, friend, file_name, inline=False):
-        st.set_transfer_finished_handler(self.transfer_finished)
-        file_number = st.get_file_number()
-        self._file_transfers[(friend.number, file_number)] = st
-        tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number)
-        st.set_state_changed_handler(tm.transfer_updated)
-        if inline:
-            self._insert_inline_before[(friend.number, file_number)] = tm.message_id
-
-    @staticmethod
-    def _generate_valid_path(path, from_position):
-        path, file_name = os.path.split(path)
-        new_file_name, i = file_name, 1
-        if not from_position:
-            while os.path.isfile(join_path(path, new_file_name)):  # file with same name already exists
-                if '.' in file_name:  # has extension
-                    d = file_name.rindex('.')
-                else:  # no extension
-                    d = len(file_name)
-                new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:]
-                i += 1
-        path = join_path(path, new_file_name)
-
-        return path
diff --git a/toxygen/file_transfers/file_transfers_messages_service.py b/toxygen/file_transfers/file_transfers_messages_service.py
deleted file mode 100644
index 1b292ee..0000000
--- a/toxygen/file_transfers/file_transfers_messages_service.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import logging
-
-from messenger.messenger import *
-import utils.util as util
-from file_transfers.file_transfers import *
-
-global LOG
-LOG = logging.getLogger('app.'+__name__)
-
-from av.calls import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
-
-class FileTransfersMessagesService:
-
-    def __init__(self, contacts_manager, messages_items_factory, profile, main_screen):
-        self._contacts_manager = contacts_manager
-        self._messages_items_factory = messages_items_factory
-        self._profile = profile
-        self._messages = main_screen.messages
-
-    def add_incoming_transfer_message(self, friend, accepted, size, file_name, file_number):
-        assert friend
-        author = MessageAuthor(friend.name, MESSAGE_AUTHOR['FRIEND'])
-        # accepted is really started
-        status = FILE_TRANSFER_STATE['RUNNING'] if accepted else FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']
-        tm = TransferMessage(author, util.get_unix_time(), status, size, file_name, friend.number, file_number)
-
-        if self._is_friend_active(friend.number):
-            self._create_file_transfer_item(tm)
-            self._messages.scrollToBottom()
-        else:
-            friend.actions = True
-
-        friend.append_message(tm)
-
-        return tm
-
-    def add_outgoing_transfer_message(self, friend, size, file_name, file_number):
-        assert friend
-        author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['ME'])
-        status = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']
-        tm = TransferMessage(author, util.get_unix_time(), status, size, file_name, friend.number, file_number)
-
-        if self._is_friend_active(friend.number):
-            self._create_file_transfer_item(tm)
-            self._messages.scrollToBottom()
-
-        friend.append_message(tm)
-
-        return tm
-
-    def add_inline_message(self, transfer, index) -> None:
-        """callback"""
-        if not self._is_friend_active(transfer.friend_number):
-            return
-        if transfer is None or not hasattr(transfer, 'data') or \
-           not transfer.data:
-            LOG_ERROR(f"add_inline_message empty data")
-            return
-        count = self._messages.count()
-        if count + index + 1 >= 0:
-            # assumes .data
-            self._create_inline_item(transfer, count + index + 1)
-
-    def add_unsent_file_message(self, friend, file_path, data):
-        assert friend
-        author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['ME'])
-        size = os.path.getsize(file_path) if data is None else len(data)
-        tm = UnsentFileMessage(file_path, data, util.get_unix_time(), author, size, friend.number)
-        friend.append_message(tm)
-
-        if self._is_friend_active(friend.number):
-            self._create_unsent_file_item(tm)
-            self._messages.scrollToBottom()
-
-        return tm
-
-    # Private methods
-
-    def _is_friend_active(self, friend_number:int) -> bool:
-        if not self._contacts_manager.is_active_a_friend():
-            return False
-
-        return friend_number == self._contacts_manager.get_active_number()
-
-    def _create_file_transfer_item(self, tm):
-        return self._messages_items_factory.create_file_transfer_item(tm)
-
-    def _create_inline_item(self, data, position):
-        return self._messages_items_factory.create_inline_item(data, False, position)
-
-    def _create_unsent_file_item(self, tm):
-        return self._messages_items_factory.create_unsent_file_item(tm)
diff --git a/toxygen/friend.py b/toxygen/friend.py
new file mode 100644
index 0000000..e9a5657
--- /dev/null
+++ b/toxygen/friend.py
@@ -0,0 +1,64 @@
+import contact
+from messages import *
+
+
+class Friend(contact.Contact):
+    """
+    Friend in list of friends. Can be hidden, properties 'has unread messages' and 'has alias' added
+    """
+
+    def __init__(self, *args):
+        """
+        :param number: number of friend.
+        """
+        super(Friend, self).__init__(*args)
+        self._receipts = 0
+
+    def __del__(self):
+        super().__del__()
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # History support
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_receipts(self):
+        return self._receipts
+
+    receipts = property(get_receipts)  # read receipts
+
+    def inc_receipts(self):
+        self._receipts += 1
+
+    def dec_receipt(self):
+        if self._receipts:
+            self._receipts -= 1
+            self.mark_as_sent()
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # File transfers support
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def update_transfer_data(self, file_number, status, inline=None):
+        """
+        Update status of active transfer and load inline if needed
+        """
+        try:
+            tr = list(filter(lambda x: x.get_type() == MESSAGE_TYPE['FILE_TRANSFER'] and x.is_active(file_number),
+                        self._corr))[0]
+            tr.set_status(status)
+            i = self._corr.index(tr)
+            if inline:  # inline was loaded
+                self._corr.insert(i, inline)
+            return i - len(self._corr)
+        except:
+            pass
+
+    def get_unsent_files(self):
+        messages = filter(lambda x: type(x) is UnsentFile, self._corr)
+        return messages
+
+    def clear_unsent_files(self):
+        self._corr = list(filter(lambda x: type(x) is not UnsentFile, self._corr))
+
+    def delete_one_unsent_file(self, time):
+        self._corr = list(filter(lambda x: not (type(x) is UnsentFile and x.get_data()[2] == time), self._corr))
diff --git a/toxygen/groupchat.py b/toxygen/groupchat.py
new file mode 100644
index 0000000..b692761
--- /dev/null
+++ b/toxygen/groupchat.py
@@ -0,0 +1,36 @@
+import contact
+
+
+class GroupChat(contact.Contact):
+
+    def __init__(self, tox, *args):
+        super().__init__(*args)
+        self._tox = tox
+
+    def load_avatar(self, default_path='group.png'):
+        super().load_avatar(default_path)
+
+    def set_status(self, value):
+        print('In gc set_status')
+        super().set_status(value)
+        self.name = bytes(self._tox.group_get_name(self._number), 'utf-8')
+        self._tox_id = self._tox.group_get_chat_id(self._number)
+        self.status_message = bytes(self._tox.group_get_topic(self._number), 'utf-8')
+
+    def add_peer(self, peer_id):
+        print(peer_id)
+        print(self._tox.group_peer_get_name(self._number, peer_id))
+
+    # TODO: get peers list and add other methods
+
+    def get_peers_list(self):
+        return []
+
+
+class Peer:
+
+    def __init__(self, peer_id, name, status, role):
+        self._data = (peer_id, name, status, role)
+
+    def get_data(self):
+        return self._data
diff --git a/toxygen/groups/__init__.py b/toxygen/groups/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/groups/group_ban.py b/toxygen/groups/group_ban.py
deleted file mode 100644
index 2b17a25..0000000
--- a/toxygen/groups/group_ban.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-class GroupBan:
-
-    def __init__(self, ban_id, ban_target, ban_time):
-        self._ban_id = ban_id
-        self._ban_target = ban_target
-        self._ban_time = ban_time
-
-    def get_ban_id(self):
-        return self._ban_id
-
-    ban_id = property(get_ban_id)
-
-    def get_ban_target(self):
-        return self._ban_target
-
-    ban_target = property(get_ban_target)
-
-    def get_ban_time(self):
-        return self._ban_time
-
-    ban_time = property(get_ban_time)
diff --git a/toxygen/groups/group_invite.py b/toxygen/groups/group_invite.py
deleted file mode 100644
index 2332933..0000000
--- a/toxygen/groups/group_invite.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-class GroupInvite:
-
-    def __init__(self, friend_public_key, chat_name, invite_data):
-        self._friend_public_key = friend_public_key
-        self._chat_name = chat_name
-        self._invite_data = invite_data[:]
-
-    def get_friend_public_key(self):
-        return self._friend_public_key
-
-    friend_public_key = property(get_friend_public_key)
-
-    def get_chat_name(self):
-        return self._chat_name
-
-    chat_name = property(get_chat_name)
-
-    def get_invite_data(self):
-        return self._invite_data[:]
-
-    invite_data = property(get_invite_data)
diff --git a/toxygen/groups/group_peer.py b/toxygen/groups/group_peer.py
deleted file mode 100644
index a96c751..0000000
--- a/toxygen/groups/group_peer.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-class GroupChatPeer:
-    """
-    Represents peer in group chat.
-    """
-
-    def __init__(self, peer_id, name, status, role, public_key, is_current_user=False, is_muted=False, status_message=None):
-        self._peer_id = peer_id
-        self._name = name
-        self._status = status
-        self._status_message = status_message
-        self._role = role
-        self._public_key = public_key
-        self._is_current_user = is_current_user
-        self._is_muted = is_muted
-        self._kind = 'grouppeer'
-
-    # Readonly properties
-
-    def get_id(self):
-        return self._peer_id
-
-    id = property(get_id)
-
-    def get_public_key(self):
-        return self._public_key
-
-    public_key = property(get_public_key)
-
-    def get_is_current_user(self):
-        return self._is_current_user
-
-    is_current_user = property(get_is_current_user)
-
-    def get_status_message(self):
-        return self._status_message
-
-    status_message = property(get_status_message)
-
-    # Read-write properties
-
-    def get_name(self):
-        return self._name
-
-    def set_name(self, name):
-        self._name = name
-
-    name = property(get_name, set_name)
-
-    def get_status(self):
-        return self._status
-
-    def set_status(self, status):
-        self._status = status
-
-    status = property(get_status, set_status)
-
-    def get_role(self):
-        return self._role
-
-    def set_role(self, role):
-        self._role = role
-
-    role = property(get_role, set_role)
-
-    def get_is_muted(self):
-        return self._is_muted
-
-    def set_is_muted(self, is_muted):
-        self._is_muted = is_muted
-
-    is_muted = property(get_is_muted, set_is_muted)
diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py
deleted file mode 100644
index 0e52d2a..0000000
--- a/toxygen/groups/groups_service.py
+++ /dev/null
@@ -1,291 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import logging
-
-import common.tox_save as tox_save
-import utils.ui as util_ui
-from groups.peers_list import PeersListGenerator
-from groups.group_invite import GroupInvite
-import toxygen_wrapper.toxcore_enums_and_consts as constants
-from toxygen_wrapper.toxcore_enums_and_consts import *
-from toxygen_wrapper.tox import UINT32_MAX
-
-global LOG
-LOG = logging.getLogger('app.'+'gs')
-
-class GroupsService(tox_save.ToxSave):
-
-    def __init__(self, tox, contacts_manager, contacts_provider, main_screen, widgets_factory_provider):
-        super().__init__(tox)
-        self._contacts_manager = contacts_manager
-        self._contacts_provider = contacts_provider
-        self._main_screen = main_screen
-        self._peers_list_widget = main_screen.peers_list
-        self._widgets_factory_provider = widgets_factory_provider
-        self._group_invites = []
-        self._screen = None
-        # maybe just use self
-        self._tox = tox
-
-    def set_tox(self, tox) -> None:
-        super().set_tox(tox)
-        for group in self._get_all_groups():
-            group.set_tox(tox)
-
-    # Groups creation
-
-    def create_new_gc(self, name, privacy_state, nick, status) -> None:
-        try:
-            group_number = self._tox.group_new(privacy_state, name, nick, status)
-        except Exception as e:
-            LOG.error(f"create_new_gc {e}")
-            return
-        if group_number == -1:
-            return
-
-        self._add_new_group_by_number(group_number)
-        group = self._get_group_by_number(group_number)
-        group.status = constants.TOX_USER_STATUS['NONE']
-        self._contacts_manager.update_filtration()
-
-    def join_gc_by_id(self, chat_id, password, nick, status) -> None:
-        try:
-            group_number = self._tox.group_join(chat_id, password, nick, status)
-            assert type(group_number) == int, group_number
-            assert group_number < UINT32_MAX, group_number
-        except Exception as e:
-            # gui
-            title = f"join_gc_by_id {chat_id}"
-            util_ui.message_box(title +'\n' +str(e), title)
-            LOG.error(f"_join_gc_via_id {e}")
-            return
-        LOG.debug(f"_join_gc_via_id {group_number}")
-        self._add_new_group_by_number(group_number)
-        group = self._get_group_by_number(group_number)
-        try:
-            assert group and hasattr(group, 'status')
-        except Exception as e:
-            # gui
-            title = f"join_gc_by_id {chat_id}"
-            util_ui.message_box(title +'\n' +str(e), title)
-            LOG.error(f"_join_gc_via_id {e}")
-            return
-        group.status = constants.TOX_USER_STATUS['NONE']
-        self._contacts_manager.update_filtration()
-
-    # Groups reconnect and leaving
-
-    def leave_group(self, group_number) -> None:
-        if type(group_number) == int:
-            self._tox.group_leave(group_number)
-            self._contacts_manager.delete_group(group_number)
-
-    def disconnect_from_group(self, group_number) -> None:
-        self._tox.group_disconnect(group_number)
-        group = self._get_group_by_number(group_number)
-        group.status = None
-        self._clear_peers_list(group)
-
-    def reconnect_to_group(self, group_number) -> None:
-        self._tox.group_reconnect(group_number)
-        group = self._get_group_by_number(group_number)
-        group.status = constants.TOX_USER_STATUS['NONE']
-        self._clear_peers_list(group)
-
-    # Group invites
-
-    def invite_friend(self, friend_number, group_number) -> None:
-        if self._tox.friend_get_connection_status(friend_number) == TOX_CONNECTION['NONE']:
-            title = f"Error in group_invite_friend {friend_number}"
-            e = f"Friend not connected friend_number={friend_number}"
-            util_ui.message_box(title +'\n' +str(e), title)
-            return
-
-        try:
-            self._tox.group_invite_friend(group_number, friend_number)
-        except Exception as e:
-            title = f"Error in group_invite_friend {group_number} {friend_number}"
-            util_ui.message_box(title +'\n' +str(e), title)
-
-    def process_group_invite(self, friend_number, group_name, invite_data) -> None:
-        friend = self._get_friend_by_number(friend_number)
-        # binary  {invite_data}
-        LOG.debug(f"process_group_invite {friend_number} {group_name}")
-        invite = GroupInvite(friend.tox_id, group_name, invite_data)
-        self._group_invites.append(invite)
-        self._update_invites_button_state()
-
-    def accept_group_invite(self, invite, name, status, password) -> None:
-        pk = invite.friend_public_key
-        friend = self._get_friend_by_public_key(pk)
-        LOG.debug(f"accept_group_invite {name}")
-        self._join_gc_via_invite(invite.invite_data, friend.number, name, status, password)
-        self._delete_group_invite(invite)
-        self._update_invites_button_state()
-
-    def decline_group_invite(self, invite) -> None:
-        self._delete_group_invite(invite)
-        self._main_screen.update_gc_invites_button_state()
-
-    def get_group_invites(self):
-        return self._group_invites[:]
-
-    group_invites = property(get_group_invites)
-
-    def get_group_invites_count(self):
-        return len(self._group_invites)
-
-    group_invites_count = property(get_group_invites_count)
-
-    # Group info methods
-
-    def update_group_info(self, group):
-        group.name = self._tox.group_get_name(group.number)
-        group.status_message = self._tox.group_get_topic(group.number)
-
-    def set_group_topic(self, group) -> None:
-        if not group.is_self_moderator_or_founder():
-            return
-        text = util_ui.tr('New topic for group "{}":'.format(group.name))
-        title = util_ui.tr('Set group topic')
-        topic, ok = util_ui.text_dialog(text, title, group.status_message)
-        if not ok or not topic:
-            return
-        self._tox.group_set_topic(group.number, topic)
-        group.status_message = topic
-
-    def show_group_management_screen(self, group) -> None:
-        widgets_factory = self._get_widgets_factory()
-        self._screen = widgets_factory.create_group_management_screen(group)
-        self._screen.show()
-
-    def show_group_settings_screen(self, group) -> None:
-        widgets_factory = self._get_widgets_factory()
-        self._screen = widgets_factory.create_group_settings_screen(group)
-        self._screen.show()
-
-    def set_group_password(self, group, password) -> None:
-        if group.password == password:
-            return
-        self._tox.group_founder_set_password(group.number, password)
-        group.password = password
-
-    def set_group_peers_limit(self, group, peers_limit) -> None:
-        if group.peers_limit == peers_limit:
-            return
-        self._tox.group_founder_set_peer_limit(group.number, peers_limit)
-        group.peers_limit = peers_limit
-
-    def set_group_privacy_state(self, group, privacy_state) -> None:
-        is_private = privacy_state == constants.TOX_GROUP_PRIVACY_STATE['PRIVATE']
-        if group.is_private == is_private:
-            return
-        self._tox.group_founder_set_privacy_state(group.number, privacy_state)
-        group.is_private = is_private
-
-    # Peers list
-
-    def generate_peers_list(self) -> None:
-        if not self._contacts_manager.is_active_a_group():
-            return
-        group = self._contacts_manager.get_curr_contact()
-        PeersListGenerator().generate(group.peers, self, self._peers_list_widget, group.tox_id)
-
-    def peer_selected(self, chat_id, peer_id) -> None:
-        widgets_factory = self._get_widgets_factory()
-        group = self._get_group_by_public_key(chat_id)
-        self_peer = group.get_self_peer()
-        if self_peer.id != peer_id:
-            self._screen = widgets_factory.create_peer_screen_window(group, peer_id)
-        else:
-            self._screen = widgets_factory.create_self_peer_screen_window(group)
-        self._screen.show()
-
-    # Peers actions
-
-    def set_new_peer_role(self, group, peer, role) -> None:
-        self._tox.group_mod_set_role(group.number, peer.id, role)
-        peer.role = role
-        self.generate_peers_list()
-
-    def toggle_ignore_peer(self, group, peer, ignore) -> None:
-        self._tox.group_toggle_ignore(group.number, peer.id, ignore)
-        peer.is_muted = ignore
-
-    def set_self_info(self, group, name, status) -> None:
-        self._tox.group_self_set_name(group.number, name)
-        self._tox.group_self_set_status(group.number, status)
-        self_peer = group.get_self_peer()
-        self_peer.name = name
-        self_peer.status = status
-        self.generate_peers_list()
-
-    # Bans support
-
-    def show_bans_list(self, group) -> None:
-        return
-        widgets_factory = self._get_widgets_factory()
-        self._screen = widgets_factory.create_groups_bans_screen(group)
-        self._screen.show()
-
-    def ban_peer(self, group, peer_id, ban_type) -> None:
-        self._tox.group_mod_ban_peer(group.number, peer_id, ban_type)
-
-    def kick_peer(self, group, peer_id) -> None:
-        self._tox.group_mod_remove_peer(group.number, peer_id)
-
-    def cancel_ban(self, group_number, ban_id) -> None:
-        self._tox.group_mod_remove_ban(group_number, ban_id)
-
-    # Private methods
-
-    def _add_new_group_by_number(self, group_number) -> None:
-        LOG.debug(f"_add_new_group_by_number group_number={group_number}")
-        self._contacts_manager.add_group(group_number)
-
-    def _get_group_by_number(self, group_number):
-        return self._contacts_provider.get_group_by_number(group_number)
-
-    def _get_group_by_public_key(self, public_key):
-        return self._contacts_provider.get_group_by_public_key(public_key)
-
-    def _get_all_groups(self):
-        return self._contacts_provider.get_all_groups()
-
-    def _get_friend_by_number(self, friend_number:int):
-        return self._contacts_provider.get_friend_by_number(friend_number)
-
-    def _get_friend_by_public_key(self, public_key):
-        return self._contacts_provider.get_friend_by_public_key(public_key)
-
-    def _clear_peers_list(self, group) -> None:
-        group.remove_all_peers_except_self()
-        self.generate_peers_list()
-
-    def _delete_group_invite(self, invite) -> None:
-        if invite in self._group_invites:
-            self._group_invites.remove(invite)
-
-    # status should be dropped
-    def _join_gc_via_invite(self, invite_data, friend_number, nick, status='', password='') -> None:
-        LOG.debug(f"_join_gc_via_invite friend_number={friend_number} nick={nick} datalen={len(invite_data)}")
-        if nick is None:
-            nick = ''
-        if invite_data is None:
-            invite_data = b''
-        try:
-            # status should be dropped
-            group_number = self._tox.group_invite_accept(invite_data, friend_number, nick, password=password)
-        except Exception as e:
-            LOG.error(f"_join_gc_via_invite ERROR {e}")
-            return
-        try:
-            self._add_new_group_by_number(group_number)
-        except Exception as e:
-            LOG.error(f"_join_gc_via_invite group_number={group_number} {e}")
-            return
-
-    def _update_invites_button_state(self) -> None:
-        self._main_screen.update_gc_invites_button_state()
-
-    def _get_widgets_factory(self) -> None:
-        return self._widgets_factory_provider.get_item()
diff --git a/toxygen/groups/peers_list.py b/toxygen/groups/peers_list.py
deleted file mode 100644
index 97641d9..0000000
--- a/toxygen/groups/peers_list.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from ui.group_peers_list import PeerItem, PeerTypeItem
-from toxygen_wrapper.toxcore_enums_and_consts import *
-from ui.widgets import *
-
-# Builder
-
-
-class PeerListBuilder:
-
-    def __init__(self):
-        self._peers = {}
-        self._titles = {}
-        self._index = 0
-        self._handler = None
-
-    def with_click_handler(self, handler):
-        self._handler = handler
-
-        return self
-
-    def with_title(self, title):
-        self._titles[self._index] = title
-        self._index += 1
-
-        return self
-
-    def with_peers(self, peers):
-        for peer in peers:
-            self._add_peer(peer)
-
-        return self
-
-    def build(self, list_widget):
-        list_widget.clear()
-
-        for i in range(self._index):
-            if i in self._peers:
-                peer = self._peers[i]
-                self._add_peer_item(peer, list_widget)
-            else:
-                title = self._titles[i]
-                self._add_peer_type_item(title, list_widget)
-
-    def _add_peer_item(self, peer, parent):
-        item = PeerItem(peer, self._handler, parent.width(), parent)
-        self._add_item(parent, item)
-
-    def _add_peer_type_item(self, text, parent):
-        item = PeerTypeItem(text, parent.width(), parent)
-        self._add_item(parent, item)
-
-    @staticmethod
-    def _add_item(parent, item):
-        elem = QtWidgets.QListWidgetItem(parent)
-        elem.setSizeHint(QtCore.QSize(parent.width(), item.height()))
-        parent.addItem(elem)
-        parent.setItemWidget(elem, item)
-
-    def _add_peer(self, peer):
-        self._peers[self._index] = peer
-        self._index += 1
-
-# Generators
-
-
-class PeersListGenerator:
-
-    @staticmethod
-    def generate(peers_list, groups_service, list_widget, chat_id):
-        admin_title = util_ui.tr('Administrator')
-        moderators_title = util_ui.tr('Moderators')
-        users_title = util_ui.tr('Users')
-        observers_title = util_ui.tr('Observers')
-
-        admins = list(filter(lambda p: p.role == TOX_GROUP_ROLE['FOUNDER'], peers_list))
-        moderators = list(filter(lambda p: p.role == TOX_GROUP_ROLE['MODERATOR'], peers_list))
-        users = list(filter(lambda p: p.role == TOX_GROUP_ROLE['USER'], peers_list))
-        observers = list(filter(lambda p: p.role == TOX_GROUP_ROLE['OBSERVER'], peers_list))
-
-        builder = (PeerListBuilder()
-                   .with_click_handler(lambda peer_id: groups_service.peer_selected(chat_id, peer_id)))
-        if len(admins):
-            (builder
-             .with_title(admin_title)
-             .with_peers(admins))
-        if len(moderators):
-            (builder
-             .with_title(moderators_title)
-             .with_peers(moderators))
-        if len(users):
-            (builder
-             .with_title(users_title)
-             .with_peers(users))
-        if len(observers):
-            (builder
-             .with_title(observers_title)
-             .with_peers(observers))
-
-        builder.build(list_widget)
diff --git a/toxygen/history.py b/toxygen/history.py
new file mode 100644
index 0000000..581d8a0
--- /dev/null
+++ b/toxygen/history.py
@@ -0,0 +1,184 @@
+# coding=utf-8
+from sqlite3 import connect
+import settings
+from os import chdir
+import os.path
+from toxencryptsave import ToxEncryptSave
+
+
+PAGE_SIZE = 42
+
+SAVE_MESSAGES = 150
+
+MESSAGE_OWNER = {
+    'ME': 0,
+    'FRIEND': 1,
+    'NOT_SENT': 2
+}
+
+
+class History:
+
+    def __init__(self, name):
+        self._name = name
+        chdir(settings.ProfileHelper.get_path())
+        path = settings.ProfileHelper.get_path() + self._name + '.hstr'
+        if os.path.exists(path):
+            decr = ToxEncryptSave.get_instance()
+            try:
+                with open(path, 'rb') as fin:
+                    data = fin.read()
+                if decr.is_data_encrypted(data):
+                    data = decr.pass_decrypt(data)
+                    with open(path, 'wb') as fout:
+                        fout.write(data)
+            except:
+                os.remove(path)
+        db = connect(name + '.hstr')
+        cursor = db.cursor()
+        cursor.execute('CREATE TABLE IF NOT EXISTS friends('
+                       '    tox_id TEXT PRIMARY KEY'
+                       ')')
+        db.close()
+
+    def save(self):
+        encr = ToxEncryptSave.get_instance()
+        if encr.has_password():
+            path = settings.ProfileHelper.get_path() + self._name + '.hstr'
+            with open(path, 'rb') as fin:
+                data = fin.read()
+            data = encr.pass_encrypt(bytes(data))
+            with open(path, 'wb') as fout:
+                fout.write(data)
+
+    def export(self, directory):
+        path = settings.ProfileHelper.get_path() + self._name + '.hstr'
+        new_path = directory + self._name + '.hstr'
+        with open(path, 'rb') as fin:
+            data = fin.read()
+        encr = ToxEncryptSave.get_instance()
+        if encr.has_password():
+            data = encr.pass_encrypt(data)
+        with open(new_path, 'wb') as fout:
+            fout.write(data)
+
+    def add_friend_to_db(self, tox_id):
+        chdir(settings.ProfileHelper.get_path())
+        db = connect(self._name + '.hstr')
+        try:
+            cursor = db.cursor()
+            cursor.execute('INSERT INTO friends VALUES (?);', (tox_id, ))
+            cursor.execute('CREATE TABLE id' + tox_id + '('
+                           '    id INTEGER PRIMARY KEY,'
+                           '    message TEXT,'
+                           '    owner INTEGER,'
+                           '    unix_time REAL,'
+                           '    message_type INTEGER'
+                           ')')
+            db.commit()
+        except:
+            db.rollback()
+            raise
+        finally:
+            db.close()
+
+    def delete_friend_from_db(self, tox_id):
+        chdir(settings.ProfileHelper.get_path())
+        db = connect(self._name + '.hstr')
+        try:
+            cursor = db.cursor()
+            cursor.execute('DELETE FROM friends WHERE tox_id=?;', (tox_id, ))
+            cursor.execute('DROP TABLE id' + tox_id + ';')
+            db.commit()
+        except:
+            db.rollback()
+            raise
+        finally:
+            db.close()
+
+    def friend_exists_in_db(self, tox_id):
+        chdir(settings.ProfileHelper.get_path())
+        db = connect(self._name + '.hstr')
+        cursor = db.cursor()
+        cursor.execute('SELECT 0 FROM friends WHERE tox_id=?', (tox_id, ))
+        result = cursor.fetchone()
+        db.close()
+        return result is not None
+
+    def save_messages_to_db(self, tox_id, messages_iter):
+        chdir(settings.ProfileHelper.get_path())
+        db = connect(self._name + '.hstr')
+        try:
+            cursor = db.cursor()
+            cursor.executemany('INSERT INTO id' + tox_id + '(message, owner, unix_time, message_type) '
+                               'VALUES (?, ?, ?, ?);', messages_iter)
+            db.commit()
+        except:
+            db.rollback()
+            raise
+        finally:
+            db.close()
+
+    def update_messages(self, tox_id, unsent_time):
+        chdir(settings.ProfileHelper.get_path())
+        db = connect(self._name + '.hstr')
+        try:
+            cursor = db.cursor()
+            cursor.execute('UPDATE id' + tox_id + ' SET owner = 0 '
+                           'WHERE unix_time < ' + str(unsent_time) + ' AND owner = 2;')
+            db.commit()
+        except:
+            db.rollback()
+            raise
+        finally:
+            db.close()
+        pass
+
+    def delete_message(self, tox_id, time):
+        chdir(settings.ProfileHelper.get_path())
+        db = connect(self._name + '.hstr')
+        try:
+            cursor = db.cursor()
+            cursor.execute('DELETE FROM id' + tox_id + ' WHERE unix_time = ' + str(time) + ';')
+            db.commit()
+        except:
+            db.rollback()
+            raise
+        finally:
+            db.close()
+
+    def delete_messages(self, tox_id):
+        chdir(settings.ProfileHelper.get_path())
+        db = connect(self._name + '.hstr')
+        try:
+            cursor = db.cursor()
+            cursor.execute('DELETE FROM id' + tox_id + ';')
+            db.commit()
+        except:
+            db.rollback()
+            raise
+        finally:
+            db.close()
+
+    def messages_getter(self, tox_id):
+        return History.MessageGetter(self._name, tox_id)
+
+    class MessageGetter:
+        def __init__(self, name, tox_id):
+            chdir(settings.ProfileHelper.get_path())
+            self._db = connect(name + '.hstr')
+            self._cursor = self._db.cursor()
+            self._cursor.execute('SELECT message, owner, unix_time, message_type FROM id' + tox_id +
+                                 ' ORDER BY unix_time DESC;')
+
+        def get_one(self):
+            return self._cursor.fetchone()
+
+        def get_all(self):
+            return self._cursor.fetchall()
+
+        def get(self, count):
+            return self._cursor.fetchmany(count)
+
+        def __del__(self):
+            self._db.close()
diff --git a/toxygen/history/__init__.py b/toxygen/history/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/history/database.py b/toxygen/history/database.py
deleted file mode 100644
index 7d8dd35..0000000
--- a/toxygen/history/database.py
+++ /dev/null
@@ -1,227 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from sqlite3 import connect
-import os.path
-import utils.util as util
-
-global LOG
-import logging
-LOG = logging.getLogger('h.database')
-
-TIMEOUT = 11
-SAVE_MESSAGES = 500
-MESSAGE_AUTHOR = {
-    'ME': 0,
-    'FRIEND': 1,
-    'NOT_SENT': 2,
-    'GC_PEER': 3
-}
-CONTACT_TYPE = {
-    'FRIEND': 0,
-    'GC_PEER': 1,
-    'GC_PEER_PRIVATE': 2
-}
-
-
-class Database:
-
-    def __init__(self, path, toxes):
-        self._path = path
-        self._toxes = toxes
-        self._name = os.path.basename(path)
-
-    def open(self):
-        path = self._path
-        toxes = self._toxes
-        if not os.path.exists(path):
-            LOG.warn('Db not found: ' +path)
-            return
-        try:
-            with open(path, 'rb') as fin:
-                data = fin.read()
-        except Exception as ex:
-            LOG.error('Db reading error: ' +path +' ' +str(ex))
-            raise
-        try:
-            if toxes.is_data_encrypted(data):
-                data = toxes.pass_decrypt(data)
-                with open(path, 'wb') as fout:
-                    fout.write(data)
-        except Exception as ex:
-            LOG.error('Db writing error: ' +path +' ' + str(ex))
-            os.remove(path)
-        LOG.info('Db opened: ' +path)
-
-    # Public methods
-
-    def save(self):
-        if self._toxes.has_password():
-            with open(self._path, 'rb') as fin:
-                data = fin.read()
-            data = self._toxes.pass_encrypt(bytes(data))
-            with open(self._path, 'wb') as fout:
-                fout.write(data)
-
-    def export(self, directory):
-        new_path = util.join_path(directory, self._name)
-        with open(self._path, 'rb') as fin:
-            data = fin.read()
-        if self._toxes.has_password():
-            data = self._toxes.pass_encrypt(data)
-        with open(new_path, 'wb') as fout:
-            fout.write(data)
-        LOG.info('Db exported: ' +new_path)
-
-    def add_friend_to_db(self, tox_id):
-        db = self._connect()
-        try:
-            cursor = db.cursor()
-            cursor.execute('CREATE TABLE IF NOT EXISTS id' + tox_id + '('
-                           '    id INTEGER PRIMARY KEY,'
-                           '    author_name TEXT,'
-                           '    message TEXT,'
-                           '    author_type INTEGER,'
-                           '    unix_time REAL,'
-                           '    message_type INTEGER'
-                           ')')
-            db.commit()
-            return True
-        except Exception as e:
-            LOG.error("dd_friend_to_db " +self._name +f" Database exception! {e}")
-            db.rollback()
-            return False
-        finally:
-            db.close()
-            LOG.debug(f"add_friend_to_db {tox_id}")
-
-    def delete_friend_from_db(self, tox_id):
-        db = self._connect()
-        try:
-            cursor = db.cursor()
-            cursor.execute('DROP TABLE id' + tox_id + ';')
-            db.commit()
-            return True
-        except Exception as e:
-            LOG.error("delete_friend_from_db " +self._name +f" Database exception! {e}")
-            db.rollback()
-            return False
-        finally:
-            db.close()
-            LOG.debug(f"delete_friend_from_db {tox_id}")
-
-    def save_messages_to_db(self, tox_id, messages_iter):
-        db = self._connect()
-        try:
-            cursor = db.cursor()
-            cursor.executemany('INSERT INTO id' + tox_id +
-                               '(message, author_name, author_type, unix_time, message_type) ' +
-                               'VALUES (?, ?, ?, ?, ?);', messages_iter)
-            db.commit()
-            return True
-        except Exception as e:
-            LOG.error("save_messages_to_db" +self._name +f" Database exception! {e}")
-            db.rollback()
-            return False
-        finally:
-            db.close()
-            LOG.debug(f"save_messages_to_db {tox_id}")
-
-    def update_messages(self, tox_id, message_id):
-        db = self._connect()
-        try:
-            cursor = db.cursor()
-            cursor.execute('UPDATE id' + tox_id + ' SET author = 0 '
-                           'WHERE id = ' + str(message_id) + ' AND author = 2;')
-            db.commit()
-            return True
-        except Exception as e:
-            LOG.error("update_messages" +self._name +f" Database exception! {e}")
-            db.rollback()
-            return False
-        finally:
-            db.close()
-            LOG.debug(f"update_messages {tox_id}")
-
-    def delete_message(self, tox_id, unique_id):
-        db = self._connect()
-        try:
-            cursor = db.cursor()
-            cursor.execute('DELETE FROM id' + tox_id + ' WHERE id = ' + str(unique_id) + ';')
-            db.commit()
-            return True
-        except Exception as e:
-            LOG.error("delete_message" +self._name +f" Database exception! {e}")
-            db.rollback()
-            return False
-        finally:
-            db.close()
-            LOG.debug(f"delete_message {tox_id}")
-
-    def delete_messages(self, tox_id):
-        db = self._connect()
-        try:
-            cursor = db.cursor()
-            cursor.execute('DELETE FROM id' + tox_id + ';')
-            db.commit()
-            return True
-        except Exception as e:
-            LOG.error("delete_messages" +self._name +f" Database exception! {e}")
-            db.rollback()
-            return False
-        finally:
-            db.close()
-            LOG.debug(f"delete_messages {tox_id}")
-
-    def messages_getter(self, tox_id):
-        self.add_friend_to_db(tox_id)
-
-        return Database.MessageGetter(self._path, tox_id)
-
-    # Messages loading
-
-    class MessageGetter:
-
-        def __init__(self, path, tox_id):
-            self._count = 0
-            self._path = path
-            self._tox_id = tox_id
-            self._db = self._cursor = None
-
-        def get_one(self):
-            return self.get(1)
-
-        def get_all(self):
-            self._connect()
-            data = self._cursor.fetchall()
-            self._disconnect()
-            self._count = len(data)
-            return data
-
-        def get(self, count):
-            self._connect()
-            self.skip()
-            data = self._cursor.fetchmany(count)
-            self._disconnect()
-            self._count += len(data)
-            return data
-
-        def skip(self):
-            if self._count:
-                self._cursor.fetchmany(self._count)
-
-        def delete_one(self):
-            if self._count:
-                self._count -= 1
-
-        def _connect(self):
-            self._db = connect(self._path, timeout=TIMEOUT)
-            self._cursor = self._db.cursor()
-            self._cursor.execute('SELECT message, author_type, author_name, unix_time, message_type, id FROM id' +
-                                 self._tox_id + ' ORDER BY unix_time DESC;')
-
-        def _disconnect(self):
-            self._db.close()
-
-    # Private methods
-
-    def _connect(self):
-        return connect(self._path, timeout=TIMEOUT)
diff --git a/toxygen/history/history.py b/toxygen/history/history.py
deleted file mode 100644
index 971fa29..0000000
--- a/toxygen/history/history.py
+++ /dev/null
@@ -1,141 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from history.history_logs_generators import *
-
-global LOG
-import logging
-LOG = logging.getLogger('app.db')
-
-class History:
-
-    def __init__(self, contact_provider, db, settings, main_screen, messages_items_factory):
-        self._contact_provider = contact_provider
-        self._db = db
-        self._settings = settings
-        self._messages = main_screen.messages
-        self._messages_items_factory = messages_items_factory
-        self._is_loading = False
-        self._contacts_manager = None
-
-    def __del__(self):
-        del self._db
-
-    def set_contacts_manager(self, contacts_manager):
-        self._contacts_manager = contacts_manager
-
-    # History support
-
-    def save_history(self):
-        """
-        Save history to db
-        """
-        # me a mistake? was _db not _history
-        if self._settings['save_history']:
-            for friend in self._contact_provider.get_all_friends():
-                self._db.add_friend_to_db(friend.tox_id)
-                if not self._settings['save_unsent_only']:
-                    messages = friend.get_corr_for_saving()
-                else:
-                    messages = friend.get_unsent_messages_for_saving()
-                    self._db.delete_messages(friend.tox_id)
-                messages = map(lambda m: (m.text, m.author.name, m.author.type, m.time, m.type), messages)
-                self._db.save_messages_to_db(friend.tox_id, messages)
-
-        self._db.save()
-
-    def clear_history(self, friend, save_unsent=False):
-        """
-        Clear chat history
-        """
-        friend.clear_corr(save_unsent)
-        self._db.delete_friend_from_db(friend.tox_id)
-
-    def export_history(self, contact, as_text=True):
-        extension = 'txt' if as_text else 'html'
-        file_name, _ = util_ui.save_file_dialog(util_ui.tr('Choose file name'), extension)
-
-        if not file_name:
-            return
-
-        if not file_name.endswith('.' + extension):
-            file_name += '.' + extension
-
-        history = self.generate_history(contact, as_text)
-        assert history
-        with open(file_name, 'wt') as fl:
-            fl.write(history)
-        LOG.info(f"wrote history to {file_name}")
-
-    def delete_message(self, message):
-        contact = self._contacts_manager.get_curr_contact()
-        if message.type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']):
-            if message.is_saved():
-                self._db.delete_message(contact.tox_id, message.id)
-        contact.delete_message(message.message_id)
-
-    def load_history(self, friend):
-        """
-        Tries to load next part of messages
-        """
-        if self._is_loading:
-            return
-        self._is_loading = True
-        friend.load_corr(False)
-        messages = friend.get_corr()
-        if not messages:
-            self._is_loading = False
-            return
-        messages.reverse()
-        messages = messages[self._messages.count():self._messages.count() + PAGE_SIZE]
-        for message in messages:
-            message_type = message.get_type()
-            if message_type in (MESSAGE_TYPE['TEXT'], MESSAGE_TYPE['ACTION']):  # text message
-                self._create_message_item(message)
-            elif message_type == MESSAGE_TYPE['FILE_TRANSFER']:  # file transfer
-                if message.state == FILE_TRANSFER_STATE['UNSENT']:
-                    self._create_unsent_file_item(message)
-                else:
-                    self._create_file_transfer_item(message)
-            elif message_type == MESSAGE_TYPE['INLINE']:  # inline image
-                self._create_inline_item(message)
-            else:  # info message
-                self._create_message_item(message)
-        self._is_loading = False
-
-    def get_message_getter(self, friend_public_key):
-        self._db.add_friend_to_db(friend_public_key)
-
-        return self._db.messages_getter(friend_public_key)
-
-    def delete_history(self, friend):
-        self._db.delete_friend_from_db(friend.tox_id)
-
-    def add_friend_to_db(self, tox_id):
-        self._db.add_friend_to_db(tox_id)
-
-    @staticmethod
-    def generate_history(contact, as_text=True, _range=None):
-        if _range is None:
-            contact.load_all_corr()
-            corr = contact.get_corr()
-        elif _range[1] + 1:
-            corr = contact.get_corr()[_range[0]:_range[1] + 1]
-        else:
-            corr = contact.get_corr()[_range[0]:]
-
-        generator = TextHistoryGenerator(corr, contact.name) if as_text else HtmlHistoryGenerator(corr, contact.name)
-
-        return generator.generate()
-
-    # Items creation
-
-    def _create_message_item(self, message):
-        return self._messages_items_factory.create_message_item(message, False)
-
-    def _create_unsent_file_item(self, message):
-        return self._messages_items_factory.create_unsent_file_item(message, False)
-
-    def _create_file_transfer_item(self, message):
-        return self._messages_items_factory.create_file_transfer_item(message, False)
-
-    def _create_inline_item(self, message):
-        return self._messages_items_factory.create_inline_item(message, False)
diff --git a/toxygen/history/history_logs_generators.py b/toxygen/history/history_logs_generators.py
deleted file mode 100644
index 91c0a28..0000000
--- a/toxygen/history/history_logs_generators.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import utils.util as util
-from messenger.messages import *
-
-
-class HistoryLogsGenerator:
-
-    def __init__(self, history, contact_name):
-        self._history = history
-        self._contact_name = contact_name
-
-    def generate(self):
-        return str()
-
-    @staticmethod
-    def _get_message_time(message):
-        return util.convert_time(message.time) if message.author.type != MESSAGE_AUTHOR['NOT_SENT'] else 'Unsent'
-
-
-class HtmlHistoryGenerator(HistoryLogsGenerator):
-
-    def __init__(self, history, contact_name):
-        super().__init__(history, contact_name)
-
-    def generate(self):
-        arr = []
-        for message in self._history:
-            if type(message) is TextMessage:
-                x = '[{}] <b>{}:</b> {}<br>'
-                arr.append(x.format(self._get_message_time(message), message.author.name, message.text))
-        s = '<br>'.join(arr)
-        html = '<html><head><meta charset="UTF-8"><title>{}</title></head><body>{}</body></html>'
-
-        return html.format(self._contact_name, s)
-
-
-class TextHistoryGenerator(HistoryLogsGenerator):
-
-    def __init__(self, history, contact_name):
-        super().__init__(history, contact_name)
-
-    def generate(self):
-        arr = [self._contact_name]
-        for message in self._history:
-            if type(message) is TextMessage:
-                x = '[{}] {}: {}\n'
-                arr.append(x.format(self._get_message_time(message), message.author.name, message.text))
-
-        return '\n'.join(arr)
diff --git a/toxygen/images/accept.png b/toxygen/images/accept.png
old mode 100644
new mode 100755
index eedb818..aaa1388
Binary files a/toxygen/images/accept.png and b/toxygen/images/accept.png differ
diff --git a/toxygen/images/accept_audio.png b/toxygen/images/accept_audio.png
old mode 100644
new mode 100755
index 7969974..2fd2818
Binary files a/toxygen/images/accept_audio.png and b/toxygen/images/accept_audio.png differ
diff --git a/toxygen/images/accept_video.png b/toxygen/images/accept_video.png
old mode 100644
new mode 100755
index bac3af7..2fdebe7
Binary files a/toxygen/images/accept_video.png and b/toxygen/images/accept_video.png differ
diff --git a/toxygen/images/audio_message.png b/toxygen/images/audio_message.png
new file mode 100755
index 0000000..22ba2a0
Binary files /dev/null and b/toxygen/images/audio_message.png differ
diff --git a/toxygen/images/avatar.png b/toxygen/images/avatar.png
old mode 100644
new mode 100755
index 06255a1..91d1200
Binary files a/toxygen/images/avatar.png and b/toxygen/images/avatar.png differ
diff --git a/toxygen/images/busy.png b/toxygen/images/busy.png
index 40b9bff..857b396 100644
Binary files a/toxygen/images/busy.png and b/toxygen/images/busy.png differ
diff --git a/toxygen/images/busy_notification.png b/toxygen/images/busy_notification.png
index 5f73464..a01eb3f 100644
Binary files a/toxygen/images/busy_notification.png and b/toxygen/images/busy_notification.png differ
diff --git a/toxygen/images/call.png b/toxygen/images/call.png
old mode 100644
new mode 100755
index 1820653..dc0d672
Binary files a/toxygen/images/call.png and b/toxygen/images/call.png differ
diff --git a/toxygen/images/call_video.png b/toxygen/images/call_video.png
deleted file mode 100644
index ba153e9..0000000
Binary files a/toxygen/images/call_video.png and /dev/null differ
diff --git a/toxygen/images/decline.png b/toxygen/images/decline.png
old mode 100644
new mode 100755
index e6313fd..9bbc9d5
Binary files a/toxygen/images/decline.png and b/toxygen/images/decline.png differ
diff --git a/toxygen/images/decline_call.png b/toxygen/images/decline_call.png
old mode 100644
new mode 100755
index 3ac0b6d..9f39789
Binary files a/toxygen/images/decline_call.png and b/toxygen/images/decline_call.png differ
diff --git a/toxygen/images/file.png b/toxygen/images/file.png
old mode 100644
new mode 100755
index 526fd10..edbfad9
Binary files a/toxygen/images/file.png and b/toxygen/images/file.png differ
diff --git a/toxygen/images/finish_call.png b/toxygen/images/finish_call.png
old mode 100644
new mode 100755
index d8d85d7..a08361e
Binary files a/toxygen/images/finish_call.png and b/toxygen/images/finish_call.png differ
diff --git a/toxygen/images/finish_call_video.png b/toxygen/images/finish_call_video.png
deleted file mode 100644
index 9e4f830..0000000
Binary files a/toxygen/images/finish_call_video.png and /dev/null differ
diff --git a/toxygen/images/group.png b/toxygen/images/group.png
old mode 100644
new mode 100755
index 3ea6469..22adab0
Binary files a/toxygen/images/group.png and b/toxygen/images/group.png differ
diff --git a/toxygen/images/icon.png b/toxygen/images/icon.png
index 6051ac7..a790ae1 100644
Binary files a/toxygen/images/icon.png and b/toxygen/images/icon.png differ
diff --git a/toxygen/images/icon.xcf b/toxygen/images/icon.xcf
deleted file mode 100644
index b9fae66..0000000
Binary files a/toxygen/images/icon.xcf and /dev/null differ
diff --git a/toxygen/images/icon_new_messages.png b/toxygen/images/icon_new_messages.png
old mode 100644
new mode 100755
index aa15890..a3f1900
Binary files a/toxygen/images/icon_new_messages.png and b/toxygen/images/icon_new_messages.png differ
diff --git a/toxygen/images/idle.png b/toxygen/images/idle.png
index 62fa74c..2550926 100644
Binary files a/toxygen/images/idle.png and b/toxygen/images/idle.png differ
diff --git a/toxygen/images/idle_notification.png b/toxygen/images/idle_notification.png
index be372f9..29f3b49 100644
Binary files a/toxygen/images/idle_notification.png and b/toxygen/images/idle_notification.png differ
diff --git a/toxygen/images/incoming_call.png b/toxygen/images/incoming_call.png
old mode 100644
new mode 100755
index 6467b23..b83350a
Binary files a/toxygen/images/incoming_call.png and b/toxygen/images/incoming_call.png differ
diff --git a/toxygen/images/incoming_call_video.png b/toxygen/images/incoming_call_video.png
deleted file mode 100644
index 2301877..0000000
Binary files a/toxygen/images/incoming_call_video.png and /dev/null differ
diff --git a/toxygen/images/menu.png b/toxygen/images/menu.png
old mode 100644
new mode 100755
index 72bd478..4d72f03
Binary files a/toxygen/images/menu.png and b/toxygen/images/menu.png differ
diff --git a/toxygen/images/offline.png b/toxygen/images/offline.png
index 54f83b7..70a863b 100644
Binary files a/toxygen/images/offline.png and b/toxygen/images/offline.png differ
diff --git a/toxygen/images/offline_notification.png b/toxygen/images/offline_notification.png
index 98dc068..77006ed 100644
Binary files a/toxygen/images/offline_notification.png and b/toxygen/images/offline_notification.png differ
diff --git a/toxygen/images/online.png b/toxygen/images/online.png
index 2381304..1e5f40a 100644
Binary files a/toxygen/images/online.png and b/toxygen/images/online.png differ
diff --git a/toxygen/images/online_notification.png b/toxygen/images/online_notification.png
index 72b988b..6e85b15 100644
Binary files a/toxygen/images/online_notification.png and b/toxygen/images/online_notification.png differ
diff --git a/toxygen/images/pause.png b/toxygen/images/pause.png
old mode 100644
new mode 100755
index bbedc4a..5c8ee4c
Binary files a/toxygen/images/pause.png and b/toxygen/images/pause.png differ
diff --git a/toxygen/images/resume.png b/toxygen/images/resume.png
old mode 100644
new mode 100755
index 4ceca74..22bb736
Binary files a/toxygen/images/resume.png and b/toxygen/images/resume.png differ
diff --git a/toxygen/images/screenshot.png b/toxygen/images/screenshot.png
old mode 100644
new mode 100755
index 9c14c6f..5599da9
Binary files a/toxygen/images/screenshot.png and b/toxygen/images/screenshot.png differ
diff --git a/toxygen/images/search.png b/toxygen/images/search.png
index 8e4875b..bf0dff6 100644
Binary files a/toxygen/images/search.png and b/toxygen/images/search.png differ
diff --git a/toxygen/images/send.png b/toxygen/images/send.png
old mode 100644
new mode 100755
index ef17f60..a2aeed8
Binary files a/toxygen/images/send.png and b/toxygen/images/send.png differ
diff --git a/toxygen/images/smiley.png b/toxygen/images/smiley.png
old mode 100644
new mode 100755
index 98787dc..6b5c0f6
Binary files a/toxygen/images/smiley.png and b/toxygen/images/smiley.png differ
diff --git a/toxygen/images/sticker.png b/toxygen/images/sticker.png
old mode 100644
new mode 100755
index 901de59..f82eae7
Binary files a/toxygen/images/sticker.png and b/toxygen/images/sticker.png differ
diff --git a/toxygen/images/typing.png b/toxygen/images/typing.png
old mode 100644
new mode 100755
index 405f80d..26ad69b
Binary files a/toxygen/images/typing.png and b/toxygen/images/typing.png differ
diff --git a/toxygen/images/video_message.png b/toxygen/images/video_message.png
new file mode 100755
index 0000000..37603ce
Binary files /dev/null and b/toxygen/images/video_message.png differ
diff --git a/toxygen/images/videocall.png b/toxygen/images/videocall.png
new file mode 100755
index 0000000..ef9fa86
Binary files /dev/null and b/toxygen/images/videocall.png differ
diff --git a/toxygen/libtox.py b/toxygen/libtox.py
new file mode 100644
index 0000000..edf2a12
--- /dev/null
+++ b/toxygen/libtox.py
@@ -0,0 +1,50 @@
+from platform import system
+from ctypes import CDLL
+import util
+
+
+class LibToxCore:
+
+    def __init__(self):
+        if system() == 'Linux':
+            # libtoxcore and libsodium must be installed in your os
+            self._libtoxcore = CDLL('libtoxcore.so')
+        elif system() == 'Windows':
+            self._libtoxcore = CDLL(util.curr_directory() + '/libs/libtox.dll')
+        else:
+            raise OSError('Unknown system.')
+
+    def __getattr__(self, item):
+        return self._libtoxcore.__getattr__(item)
+
+
+class LibToxAV:
+
+    def __init__(self):
+        if system() == 'Linux':
+            # that /usr/lib/libtoxav.so must exists
+            self._libtoxav = CDLL('libtoxav.so')
+        elif system() == 'Windows':
+            # on Windows av api is in libtox.dll
+            self._libtoxav = CDLL(util.curr_directory() + '/libs/libtox.dll')
+        else:
+            raise OSError('Unknown system.')
+
+    def __getattr__(self, item):
+        return self._libtoxav.__getattr__(item)
+
+
+class LibToxEncryptSave:
+
+    def __init__(self):
+        if system() == 'Linux':
+            # /usr/lib/libtoxencryptsave.so must exists
+            self._lib_tox_encrypt_save = CDLL('libtoxencryptsave.so')
+        elif system() == 'Windows':
+            # on Windows profile encryption api is in libtox.dll
+            self._lib_tox_encrypt_save = CDLL(util.curr_directory() + '/libs/libtox.dll')
+        else:
+            raise OSError('Unknown system.')
+
+    def __getattr__(self, item):
+        return self._lib_tox_encrypt_save.__getattr__(item)
diff --git a/toxygen/list_items.py b/toxygen/list_items.py
new file mode 100644
index 0000000..78e12c3
--- /dev/null
+++ b/toxygen/list_items.py
@@ -0,0 +1,500 @@
+from toxcore_enums_and_consts import *
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+import profile
+from file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR
+from util import curr_directory, convert_time, curr_time
+from widgets import DataLabel, create_menu
+import html as h
+import smileys
+import settings
+
+
+class MessageEdit(QtGui.QTextBrowser):
+
+    def __init__(self, text, width, message_type, parent=None):
+        super(MessageEdit, self).__init__(parent)
+        self.urls = {}
+        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+        self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere)
+        self.document().setTextWidth(width)
+        self.setOpenExternalLinks(True)
+        self.setAcceptRichText(True)
+        self.setOpenLinks(False)
+        self.setSearchPaths([smileys.SmileyLoader.get_instance().get_smileys_path()])
+        self.document().setDefaultStyleSheet('a { color: #306EFF; }')
+        text = self.decoratedText(text)
+        if message_type != TOX_MESSAGE_TYPE['NORMAL']:
+            self.setHtml('<p style="color: #5CB3FF; font: italic; font-size: 20px;" >' + text + '</p>')
+        else:
+            self.setHtml(text)
+        font = QtGui.QFont()
+        font.setFamily("Times New Roman")
+        font.setPixelSize(settings.Settings.get_instance()['message_font_size'])
+        font.setBold(False)
+        self.setFont(font)
+        self.resize(width, self.document().size().height())
+        self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse)
+        self.anchorClicked.connect(self.on_anchor_clicked)
+
+    def contextMenuEvent(self, event):
+        menu = create_menu(self.createStandardContextMenu(event.pos()))
+        menu.popup(event.globalPos())
+        menu.exec_(event.globalPos())
+        del menu
+
+    def on_anchor_clicked(self, url):
+        text = str(url.toString())
+        if text.startswith('tox:'):
+            import menu
+            self.add_contact = menu.AddContact(text[4:])
+            self.add_contact.show()
+        else:
+            QtGui.QDesktopServices.openUrl(url)
+        self.clearFocus()
+
+    def addAnimation(self, url, fileName):
+        movie = QtGui.QMovie(self)
+        movie.setFileName(fileName)
+        self.urls[movie] = url
+        movie.frameChanged[int].connect(lambda x: self.animate(movie))
+        movie.start()
+
+    def animate(self, movie):
+        self.document().addResource(QtGui.QTextDocument.ImageResource,
+                                    self.urls[movie],
+                                    movie.currentPixmap())
+        self.setLineWrapColumnOrWidth(self.lineWrapColumnOrWidth())
+
+    def decoratedText(self, text):
+        text = h.escape(text)  # replace < and >
+        exp = QtCore.QRegExp(
+            '('
+            '(?:\\b)((www\\.)|(http[s]?|ftp)://)'
+            '\\w+\\S+)'
+            '|(?:\\b)(file:///)([\\S| ]*)'
+            '|(?:\\b)(tox:[a-zA-Z\\d]{76}$)'
+            '|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)'
+            '|(?:\\b)(tox:\\S+@\\S+)')
+        offset = exp.indexIn(text, 0)
+        while offset != -1:  # add links
+            url = exp.cap()
+            if exp.cap(2) == 'www.':
+                html = '<a href="http://{0}">{0}</a>'.format(url)
+            else:
+                html = '<a href="{0}">{0}</a>'.format(url)
+            text = text[:offset] + html + text[offset + len(exp.cap()):]
+            offset += len(html)
+            offset = exp.indexIn(text, offset)
+        arr = text.split('\n')
+        for i in range(len(arr)):  # quotes
+            if arr[i].startswith('&gt;'):
+                arr[i] = '<font color="green"><b>' + arr[i][4:] + '</b></font>'
+        text = '<br>'.join(arr)
+        text = smileys.SmileyLoader.get_instance().add_smileys_to_text(text, self)  # smileys
+        return text
+
+
+class MessageItem(QtGui.QWidget):
+    """
+    Message in messages list
+    """
+    def __init__(self, text, time, user='', sent=True, message_type=TOX_MESSAGE_TYPE['NORMAL'], parent=None):
+        QtGui.QWidget.__init__(self, parent)
+        self.name = DataLabel(self)
+        self.name.setGeometry(QtCore.QRect(2, 2, 95, 20))
+        self.name.setTextFormat(QtCore.Qt.PlainText)
+        font = QtGui.QFont()
+        font.setFamily("Times New Roman")
+        font.setPointSize(11)
+        font.setBold(True)
+        self.name.setFont(font)
+        self.name.setText(user)
+
+        self.time = QtGui.QLabel(self)
+        self.time.setGeometry(QtCore.QRect(parent.width() - 50, 0, 50, 20))
+        font = QtGui.QFont()
+        font.setFamily("Times New Roman")
+        font.setPointSize(10)
+        font.setBold(False)
+        self.time.setFont(font)
+        self._time = time
+        if not sent:
+            movie = QtGui.QMovie(curr_directory() + '/images/spinner.gif')
+            self.time.setMovie(movie)
+            movie.start()
+            self.t = True
+        else:
+            self.time.setText(convert_time(time))
+            self.t = False
+
+        self.message = MessageEdit(text, parent.width() - 150, message_type, self)
+        if message_type != TOX_MESSAGE_TYPE['NORMAL']:
+            self.name.setStyleSheet("QLabel { color: #5CB3FF; }")
+            self.message.setAlignment(QtCore.Qt.AlignCenter)
+            self.time.setStyleSheet("QLabel { color: #5CB3FF; }")
+        self.message.setGeometry(QtCore.QRect(100, 0, parent.width() - 150, self.message.height()))
+        self.setFixedHeight(self.message.height())
+
+    def mouseReleaseEvent(self, event):
+        if event.button() == QtCore.Qt.RightButton and event.x() > self.time.x():
+            self.listMenu = QtGui.QMenu()
+            delete_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Delete message', None, QtGui.QApplication.UnicodeUTF8))
+            self.connect(delete_item, QtCore.SIGNAL("triggered()"), self.delete)
+            parent_position = self.time.mapToGlobal(QtCore.QPoint(0, 0))
+            self.listMenu.move(parent_position)
+            self.listMenu.show()
+
+    def delete(self):
+        pr = profile.Profile.get_instance()
+        pr.delete_message(self._time)
+
+    def mark_as_sent(self):
+        if self.t:
+            self.time.setText(convert_time(self._time))
+            self.t = False
+            return True
+        return False
+
+
+class ContactItem(QtGui.QWidget):
+    """
+    Contact in friends list
+    """
+
+    def __init__(self, parent=None):
+        QtGui.QWidget.__init__(self, parent)
+        mode = settings.Settings.get_instance()['compact_mode']
+        self.setBaseSize(QtCore.QSize(250, 40 if mode else 70))
+        self.avatar_label = QtGui.QLabel(self)
+        size = 32 if mode else 64
+        self.avatar_label.setGeometry(QtCore.QRect(3, 4, size, size))
+        self.avatar_label.setScaledContents(True)
+        self.name = DataLabel(self)
+        self.name.setGeometry(QtCore.QRect(50 if mode else 75, 3 if mode else 10, 150, 15 if mode else 25))
+        font = QtGui.QFont()
+        font.setFamily("Times New Roman")
+        font.setPointSize(10 if mode else 12)
+        font.setBold(True)
+        self.name.setFont(font)
+        self.status_message = DataLabel(self)
+        self.status_message.setGeometry(QtCore.QRect(50 if mode else 75, 20 if mode else 30, 170, 15 if mode else 20))
+        font.setPointSize(10)
+        font.setBold(False)
+        self.status_message.setFont(font)
+        self.connection_status = StatusCircle(self)
+        self.connection_status.setGeometry(QtCore.QRect(230, -2 if mode else 5, 32, 32))
+        self.messages = UnreadMessagesCount(self)
+        self.messages.setGeometry(QtCore.QRect(20 if mode else 52, 20 if mode else 50, 30, 20))
+
+
+class StatusCircle(QtGui.QWidget):
+    """
+    Connection status
+    """
+    def __init__(self, parent):
+        QtGui.QWidget.__init__(self, parent)
+        self.setGeometry(0, 0, 32, 32)
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(0, 0, 32, 32))
+        self.unread = False
+
+    def update(self, status, unread_messages=None):
+        if unread_messages is None:
+            unread_messages = self.unread
+        else:
+            self.unread = unread_messages
+        if status == TOX_USER_STATUS['NONE']:
+            name = 'online'
+        elif status == TOX_USER_STATUS['AWAY']:
+            name = 'idle'
+        elif status == TOX_USER_STATUS['BUSY']:
+            name = 'busy'
+        else:
+            name = 'offline'
+        if unread_messages:
+            name += '_notification'
+            self.label.setGeometry(QtCore.QRect(0, 0, 32, 32))
+        else:
+            self.label.setGeometry(QtCore.QRect(2, 0, 32, 32))
+        pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(name))
+        self.label.setPixmap(pixmap)
+
+
+class UnreadMessagesCount(QtGui.QWidget):
+
+    def __init__(self, parent=None):
+        super(UnreadMessagesCount, self).__init__(parent)
+        self.resize(30, 20)
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(0, 0, 30, 20))
+        self.label.setVisible(False)
+        font = QtGui.QFont()
+        font.setFamily("Times New Roman")
+        font.setPointSize(12)
+        font.setBold(True)
+        self.label.setFont(font)
+        self.label.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignCenter)
+        color = settings.Settings.get_instance()['unread_color']
+        self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }')
+
+    def update(self, messages_count):
+        color = settings.Settings.get_instance()['unread_color']
+        self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }')
+        if messages_count:
+            self.label.setVisible(True)
+            self.label.setText(str(messages_count))
+        else:
+            self.label.setVisible(False)
+
+
+class FileTransferItem(QtGui.QListWidget):
+
+    def __init__(self, file_name, size, time, user, friend_number, file_number, state, width, parent=None):
+
+        QtGui.QListWidget.__init__(self, parent)
+        self.resize(QtCore.QSize(width, 34))
+        if state == TOX_FILE_TRANSFER_STATE['CANCELLED']:
+            self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
+        elif state in PAUSED_FILE_TRANSFERS:
+            self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
+        else:
+            self.setStyleSheet('QListWidget { border: 1px solid green; }')
+        self.state = state
+
+        self.name = DataLabel(self)
+        self.name.setGeometry(QtCore.QRect(3, 7, 95, 20))
+        self.name.setTextFormat(QtCore.Qt.PlainText)
+        font = QtGui.QFont()
+        font.setFamily("Times New Roman")
+        font.setPointSize(11)
+        font.setBold(True)
+        self.name.setFont(font)
+        self.name.setText(user)
+
+        self.time = QtGui.QLabel(self)
+        self.time.setGeometry(QtCore.QRect(width - 53, 7, 50, 20))
+        font.setPointSize(10)
+        font.setBold(False)
+        self.time.setFont(font)
+        self.time.setText(convert_time(time))
+
+        self.cancel = QtGui.QPushButton(self)
+        self.cancel.setGeometry(QtCore.QRect(width - 120, 2, 30, 30))
+        pixmap = QtGui.QPixmap(curr_directory() + '/images/decline.png')
+        icon = QtGui.QIcon(pixmap)
+        self.cancel.setIcon(icon)
+        self.cancel.setIconSize(QtCore.QSize(30, 30))
+        self.cancel.setVisible(state in ACTIVE_FILE_TRANSFERS)
+        self.cancel.clicked.connect(lambda: self.cancel_transfer(friend_number, file_number))
+        self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}')
+
+        self.accept_or_pause = QtGui.QPushButton(self)
+        self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30))
+        if state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']:
+            self.accept_or_pause.setVisible(True)
+            self.button_update('accept')
+        elif state in DO_NOT_SHOW_ACCEPT_BUTTON:
+            self.accept_or_pause.setVisible(False)
+        elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']:  # setup for continue
+            self.accept_or_pause.setVisible(True)
+            self.button_update('resume')
+        else:  # pause
+            self.accept_or_pause.setVisible(True)
+            self.button_update('pause')
+        self.accept_or_pause.clicked.connect(lambda: self.accept_or_pause_transfer(friend_number, file_number, size))
+
+        self.accept_or_pause.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none}')
+
+        self.pb = QtGui.QProgressBar(self)
+        self.pb.setGeometry(QtCore.QRect(100, 7, 100, 20))
+        self.pb.setValue(0)
+        self.pb.setStyleSheet('QProgressBar { background-color: #302F2F; }')
+        self.pb.setVisible(state in SHOW_PROGRESS_BAR)
+
+        self.file_name = DataLabel(self)
+        self.file_name.setGeometry(QtCore.QRect(210, 7, width - 420, 20))
+        font.setPointSize(12)
+        self.file_name.setFont(font)
+        file_size = size // 1024
+        if not file_size:
+            file_size = '{}B'.format(size)
+        elif file_size >= 1024:
+            file_size = '{}MB'.format(file_size // 1024)
+        else:
+            file_size = '{}KB'.format(file_size)
+        file_data = '{} {}'.format(file_size, file_name)
+        self.file_name.setText(file_data)
+        self.file_name.setToolTip(file_name)
+        self.saved_name = file_name
+        self.time_left = QtGui.QLabel(self)
+        self.time_left.setGeometry(QtCore.QRect(width - 87, 7, 30, 20))
+        font.setPointSize(10)
+        self.time_left.setFont(font)
+        self.time_left.setVisible(state == TOX_FILE_TRANSFER_STATE['RUNNING'])
+        self.setFocusPolicy(QtCore.Qt.NoFocus)
+        self.paused = False
+
+    def cancel_transfer(self, friend_number, file_number):
+        pr = profile.Profile.get_instance()
+        pr.cancel_transfer(friend_number, file_number)
+        self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
+        self.cancel.setVisible(False)
+        self.accept_or_pause.setVisible(False)
+        self.pb.setVisible(False)
+
+    def accept_or_pause_transfer(self, friend_number, file_number, size):
+        if self.state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']:
+            directory = QtGui.QFileDialog.getExistingDirectory(self,
+                                                               QtGui.QApplication.translate("MainWindow", 'Choose folder', None, QtGui.QApplication.UnicodeUTF8),
+                                                               curr_directory(),
+                                                               QtGui.QFileDialog.ShowDirsOnly | QtGui.QFileDialog.DontUseNativeDialog)
+            self.pb.setVisible(True)
+            if directory:
+                pr = profile.Profile.get_instance()
+                pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size)
+                self.button_update('pause')
+        elif self.state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']:  # resume
+            self.paused = False
+            profile.Profile.get_instance().resume_transfer(friend_number, file_number)
+            self.button_update('pause')
+            self.state = TOX_FILE_TRANSFER_STATE['RUNNING']
+        else:  # pause
+            self.paused = True
+            self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']
+            profile.Profile.get_instance().pause_transfer(friend_number, file_number)
+            self.button_update('resume')
+        self.accept_or_pause.clearFocus()
+
+    def button_update(self, path):
+        pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(path))
+        icon = QtGui.QIcon(pixmap)
+        self.accept_or_pause.setIcon(icon)
+        self.accept_or_pause.setIconSize(QtCore.QSize(30, 30))
+
+    @QtCore.Slot(int, float, int)
+    def update(self, state, progress, time):
+        self.pb.setValue(int(progress * 100))
+        if time + 1:
+            m, s = divmod(time, 60)
+            self.time_left.setText('{0:02d}:{1:02d}'.format(m, s))
+        if self.state != state:
+            if state == TOX_FILE_TRANSFER_STATE['CANCELLED']:
+                self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
+                self.cancel.setVisible(False)
+                self.accept_or_pause.setVisible(False)
+                self.pb.setVisible(False)
+                self.state = state
+                self.time_left.setVisible(False)
+            elif state == TOX_FILE_TRANSFER_STATE['FINISHED']:
+                self.accept_or_pause.setVisible(False)
+                self.pb.setVisible(False)
+                self.cancel.setVisible(False)
+                self.setStyleSheet('QListWidget { border: 1px solid green; }')
+                self.state = state
+                self.time_left.setVisible(False)
+            elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']:
+                self.accept_or_pause.setVisible(False)
+                self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
+                self.state = state
+                self.time_left.setVisible(False)
+            elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']:
+                self.button_update('resume')  # setup button continue
+                self.setStyleSheet('QListWidget { border: 1px solid green; }')
+                self.state = state
+                self.time_left.setVisible(False)
+            elif state == TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']:
+                self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
+                self.accept_or_pause.setVisible(False)
+                self.time_left.setVisible(False)
+                self.pb.setVisible(False)
+            elif not self.paused:  # active
+                self.pb.setVisible(True)
+                self.accept_or_pause.setVisible(True)  # setup to pause
+                self.button_update('pause')
+                self.setStyleSheet('QListWidget { border: 1px solid green; }')
+                self.state = state
+                self.time_left.setVisible(True)
+
+    def mark_as_sent(self):
+        return False
+
+
+class UnsentFileItem(FileTransferItem):
+
+    def __init__(self, file_name, size, user, time, width, parent=None):
+        super(UnsentFileItem, self).__init__(file_name, size, time, user, -1, -1,
+                                             TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'], width, parent)
+        self._time = time
+        self.pb.setVisible(False)
+        movie = QtGui.QMovie(curr_directory() + '/images/spinner.gif')
+        self.time.setMovie(movie)
+        movie.start()
+
+    def cancel_transfer(self, *args):
+        pr = profile.Profile.get_instance()
+        pr.cancel_not_started_transfer(self._time)
+
+
+class InlineImageItem(QtGui.QScrollArea):
+
+    def __init__(self, data, width, elem):
+
+        QtGui.QScrollArea.__init__(self)
+        self.setFocusPolicy(QtCore.Qt.NoFocus)
+        self._elem = elem
+        self._image_label = QtGui.QLabel(self)
+        self._image_label.raise_()
+        self.setWidget(self._image_label)
+        self._image_label.setScaledContents(False)
+        self._pixmap = QtGui.QPixmap()
+        self._pixmap.loadFromData(data, 'PNG')
+        self._max_size = width - 30
+        self._resize_needed = not (self._pixmap.width() <= self._max_size)
+        self._full_size = not self._resize_needed
+        if not self._resize_needed:
+            self._image_label.setPixmap(self._pixmap)
+            self.resize(QtCore.QSize(self._max_size + 5, self._pixmap.height() + 5))
+            self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height())
+        else:
+            pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio)
+            self._image_label.setPixmap(pixmap)
+            self.resize(QtCore.QSize(self._max_size + 5, pixmap.height()))
+            self._image_label.setGeometry(5, 0, self._max_size + 5, pixmap.height())
+        self._elem.setSizeHint(QtCore.QSize(self.width(), self.height()))
+
+    def mouseReleaseEvent(self, event):
+        if event.button() == QtCore.Qt.LeftButton and self._resize_needed:  # scale inline
+            if self._full_size:
+                pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio)
+                self._image_label.setPixmap(pixmap)
+                self.resize(QtCore.QSize(self._max_size, pixmap.height()))
+                self._image_label.setGeometry(5, 0, pixmap.width(), pixmap.height())
+            else:
+                self._image_label.setPixmap(self._pixmap)
+                self.resize(QtCore.QSize(self._max_size, self._pixmap.height() + 17))
+                self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height())
+            self._full_size = not self._full_size
+            self._elem.setSizeHint(QtCore.QSize(self.width(), self.height()))
+        elif event.button() == QtCore.Qt.RightButton:  # save inline
+            directory = QtGui.QFileDialog.getExistingDirectory(self,
+                                                               QtGui.QApplication.translate("MainWindow",
+                                                                                            'Choose folder', None,
+                                                                                            QtGui.QApplication.UnicodeUTF8),
+                                                               curr_directory(),
+                                                               QtGui.QFileDialog.ShowDirsOnly | QtGui.QFileDialog.DontUseNativeDialog)
+            if directory:
+                fl = QtCore.QFile(directory + '/toxygen_inline_' + curr_time().replace(':', '_') + '.png')
+                self._pixmap.save(fl, 'PNG')
+
+        return False
+
+    def mark_as_sent(self):
+        return False
+
+
+
+
diff --git a/toxygen/loginscreen.py b/toxygen/loginscreen.py
new file mode 100644
index 0000000..df51c5b
--- /dev/null
+++ b/toxygen/loginscreen.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+from widgets import *
+
+
+class NickEdit(LineEdit):
+
+    def __init__(self, parent):
+        super(NickEdit, self).__init__(parent)
+        self.parent = parent
+
+    def keyPressEvent(self, event):
+        if event.key() == QtCore.Qt.Key_Return:
+            self.parent.create_profile()
+        else:
+            super(NickEdit, self).keyPressEvent(event)
+
+
+class LoginScreen(CenteredWidget):
+
+    def __init__(self):
+        super(LoginScreen, self).__init__()
+        self.initUI()
+        self.center()
+
+    def initUI(self):
+        self.resize(400, 200)
+        self.setMinimumSize(QtCore.QSize(400, 200))
+        self.setMaximumSize(QtCore.QSize(400, 200))
+        self.new_profile = QtGui.QPushButton(self)
+        self.new_profile.setGeometry(QtCore.QRect(20, 150, 171, 27))
+        self.new_profile.clicked.connect(self.create_profile)
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(20, 70, 101, 17))
+        self.new_name = NickEdit(self)
+        self.new_name.setGeometry(QtCore.QRect(20, 100, 171, 31))
+        self.load_profile = QtGui.QPushButton(self)
+        self.load_profile.setGeometry(QtCore.QRect(220, 150, 161, 27))
+        self.load_profile.clicked.connect(self.load_ex_profile)
+        self.default = QtGui.QCheckBox(self)
+        self.default.setGeometry(QtCore.QRect(220, 110, 131, 22))
+        self.groupBox = QtGui.QGroupBox(self)
+        self.groupBox.setGeometry(QtCore.QRect(210, 40, 181, 151))
+        self.comboBox = QtGui.QComboBox(self.groupBox)
+        self.comboBox.setGeometry(QtCore.QRect(10, 30, 161, 27))
+        self.groupBox_2 = QtGui.QGroupBox(self)
+        self.groupBox_2.setGeometry(QtCore.QRect(10, 40, 191, 151))
+        self.toxygen = QtGui.QLabel(self)
+        self.groupBox.raise_()
+        self.groupBox_2.raise_()
+        self.comboBox.raise_()
+        self.default.raise_()
+        self.load_profile.raise_()
+        self.new_name.raise_()
+        self.new_profile.raise_()
+        self.toxygen.setGeometry(QtCore.QRect(160, 10, 90, 21))
+        font = QtGui.QFont()
+        font.setFamily("Impact")
+        font.setPointSize(16)
+        self.toxygen.setFont(font)
+        self.toxygen.setObjectName("toxygen")
+        self.type = 0
+        self.number = -1
+        self.load_as_default = False
+        self.name = None
+        self.retranslateUi()
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def retranslateUi(self):
+        self.new_name.setPlaceholderText(QtGui.QApplication.translate("login", "Profile name", None, QtGui.QApplication.UnicodeUTF8))
+        self.setWindowTitle(QtGui.QApplication.translate("login", "Log in", None, QtGui.QApplication.UnicodeUTF8))
+        self.new_profile.setText(QtGui.QApplication.translate("login", "Create", None, QtGui.QApplication.UnicodeUTF8))
+        self.label.setText(QtGui.QApplication.translate("login", "Profile name:", None, QtGui.QApplication.UnicodeUTF8))
+        self.load_profile.setText(QtGui.QApplication.translate("login", "Load profile", None, QtGui.QApplication.UnicodeUTF8))
+        self.default.setText(QtGui.QApplication.translate("login", "Use as default", None, QtGui.QApplication.UnicodeUTF8))
+        self.groupBox.setTitle(QtGui.QApplication.translate("login", "Load existing profile", None, QtGui.QApplication.UnicodeUTF8))
+        self.groupBox_2.setTitle(QtGui.QApplication.translate("login", "Create new profile", None, QtGui.QApplication.UnicodeUTF8))
+        self.toxygen.setText(QtGui.QApplication.translate("login", "toxygen", None, QtGui.QApplication.UnicodeUTF8))
+
+    def create_profile(self):
+        self.type = 1
+        self.name = self.new_name.text()
+        self.close()
+
+    def load_ex_profile(self):
+        if not self.create_only:
+            self.type = 2
+            self.number = self.comboBox.currentIndex()
+            self.load_as_default = self.default.isChecked()
+            self.close()
+
+    def update_select(self, data):
+        list_of_profiles = []
+        for elem in data:
+            list_of_profiles.append(elem)
+        self.comboBox.addItems(list_of_profiles)
+        self.create_only = not list_of_profiles
+
+    def update_on_close(self, func):
+        self.onclose = func
+
+    def closeEvent(self, event):
+        self.onclose(self.type, self.number, self.load_as_default, self.name)
+        event.accept()
diff --git a/toxygen/main.py b/toxygen/main.py
new file mode 100644
index 0000000..b7b7120
--- /dev/null
+++ b/toxygen/main.py
@@ -0,0 +1,435 @@
+import sys
+from loginscreen import LoginScreen
+import profile
+from settings import *
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+from bootstrap import node_generator
+from mainscreen import MainWindow
+from callbacks import init_callbacks
+from util import curr_directory, program_version
+import styles.style
+import toxencryptsave
+from passwordscreen import PasswordScreen, UnlockAppScreen, SetProfilePasswordScreen
+from plugin_support import PluginLoader
+
+
+class Toxygen:
+
+    def __init__(self, path_or_uri=None):
+        super(Toxygen, self).__init__()
+        self.tox = self.ms = self.init = self.mainloop = self.avloop = None
+        if path_or_uri is None:
+            self.uri = self.path = None
+        elif path_or_uri.startswith('tox:'):
+            self.path = None
+            self.uri = path_or_uri[4:]
+        else:
+            self.path = path_or_uri
+            self.uri = None
+
+    def enter_pass(self, data):
+        """
+        Show password screen
+        """
+        tmp = [data]
+        p = PasswordScreen(toxencryptsave.ToxEncryptSave.get_instance(), tmp)
+        p.show()
+        self.app.connect(self.app, QtCore.SIGNAL("lastWindowClosed()"), self.app, QtCore.SLOT("quit()"))
+        self.app.exec_()
+        if tmp[0] == data:
+            raise SystemExit()
+        else:
+            return tmp[0]
+
+    def main(self):
+        """
+        Main function of app. loads login screen if needed and starts main screen
+        """
+        app = QtGui.QApplication(sys.argv)
+        app.setWindowIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
+        self.app = app
+
+        # application color scheme
+        with open(curr_directory() + '/styles/style.qss') as fl:
+            dark_style = fl.read()
+        app.setStyleSheet(dark_style)
+
+        encrypt_save = toxencryptsave.ToxEncryptSave()
+
+        if self.path is not None:
+            path = os.path.dirname(self.path) + '/'
+            name = os.path.basename(self.path)[:-4]
+            data = ProfileHelper(path, name).open_profile()
+            if encrypt_save.is_data_encrypted(data):
+                data = self.enter_pass(data)
+            settings = Settings(name)
+            self.tox = profile.tox_factory(data, settings)
+        else:
+            auto_profile = Settings.get_auto_profile()
+            if not auto_profile[0]:
+                # show login screen if default profile not found
+                current_locale = QtCore.QLocale()
+                curr_lang = current_locale.languageToString(current_locale.language())
+                langs = Settings.supported_languages()
+                if curr_lang in langs:
+                    lang_path = langs[curr_lang]
+                    translator = QtCore.QTranslator()
+                    translator.load(curr_directory() + '/translations/' + lang_path)
+                    app.installTranslator(translator)
+                    app.translator = translator
+                ls = LoginScreen()
+                ls.setWindowIconText("Toxygen")
+                profiles = ProfileHelper.find_profiles()
+                ls.update_select(map(lambda x: x[1], profiles))
+                _login = self.Login(profiles)
+                ls.update_on_close(_login.login_screen_close)
+                ls.show()
+                app.connect(app, QtCore.SIGNAL("lastWindowClosed()"), app, QtCore.SLOT("quit()"))
+                app.exec_()
+                if not _login.t:
+                    return
+                elif _login.t == 1:  # create new profile
+                    _login.name = _login.name.strip()
+                    name = _login.name if _login.name else 'toxygen_user'
+                    pr = map(lambda x: x[1], ProfileHelper.find_profiles())
+                    if name in list(pr):
+                        msgBox = QtGui.QMessageBox()
+                        msgBox.setWindowTitle(
+                            QtGui.QApplication.translate("MainWindow", "Error", None, QtGui.QApplication.UnicodeUTF8))
+                        text = (QtGui.QApplication.translate("MainWindow",
+                                                             'Profile with this name already exists',
+                                                             None, QtGui.QApplication.UnicodeUTF8))
+                        msgBox.setText(text)
+                        msgBox.exec_()
+                        return
+                    self.tox = profile.tox_factory()
+                    self.tox.self_set_name(bytes(_login.name, 'utf-8') if _login.name else b'Toxygen User')
+                    self.tox.self_set_status_message(b'Toxing on Toxygen')
+                    reply = QtGui.QMessageBox.question(None,
+                                                       'Profile {}'.format(name),
+                                                       QtGui.QApplication.translate("login",
+                                                                                    'Do you want to set profile password?',
+                                                                                    None,
+                                                                                    QtGui.QApplication.UnicodeUTF8),
+                                                       QtGui.QMessageBox.Yes,
+                                                       QtGui.QMessageBox.No)
+                    if reply == QtGui.QMessageBox.Yes:
+                        set_pass = SetProfilePasswordScreen(encrypt_save)
+                        set_pass.show()
+                        self.app.connect(self.app, QtCore.SIGNAL("lastWindowClosed()"), self.app, QtCore.SLOT("quit()"))
+                        self.app.exec_()
+                    ProfileHelper(Settings.get_default_path(), name).save_profile(self.tox.get_savedata())
+                    path = Settings.get_default_path()
+                    settings = Settings(name)
+                    if curr_lang in langs:
+                        settings['language'] = curr_lang
+                    settings.save()
+                else:  # load existing profile
+                    path, name = _login.get_data()
+                    if _login.default:
+                        Settings.set_auto_profile(path, name)
+                    data = ProfileHelper(path, name).open_profile()
+                    if encrypt_save.is_data_encrypted(data):
+                        data = self.enter_pass(data)
+                    settings = Settings(name)
+                    self.tox = profile.tox_factory(data, settings)
+            else:
+                path, name = auto_profile
+                data = ProfileHelper(path, name).open_profile()
+                if encrypt_save.is_data_encrypted(data):
+                    data = self.enter_pass(data)
+                settings = Settings(name)
+                self.tox = profile.tox_factory(data, settings)
+
+        if Settings.is_active_profile(path, name):  # profile is in use
+            reply = QtGui.QMessageBox.question(None,
+                                               'Profile {}'.format(name),
+                                               QtGui.QApplication.translate("login", 'Other instance of Toxygen uses this profile or profile was not properly closed. Continue?', None, QtGui.QApplication.UnicodeUTF8),
+                                               QtGui.QMessageBox.Yes,
+                                               QtGui.QMessageBox.No)
+            if reply != QtGui.QMessageBox.Yes:
+                return
+        else:
+            settings.set_active_profile()
+
+        lang = Settings.supported_languages()[settings['language']]
+        translator = QtCore.QTranslator()
+        translator.load(curr_directory() + '/translations/' + lang)
+        app.installTranslator(translator)
+        app.translator = translator
+
+        # tray icon
+        self.tray = QtGui.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
+        self.tray.setObjectName('tray')
+
+        self.ms = MainWindow(self.tox, self.reset, self.tray)
+
+        class Menu(QtGui.QMenu):
+
+            def newStatus(self, status):
+                profile.Profile.get_instance().set_status(status)
+                self.aboutToShow()
+                self.hide()
+
+            def aboutToShow(self):
+                status = profile.Profile.get_instance().status
+                act = self.act
+                if status is None or Settings.get_instance().locked:
+                    self.actions()[1].setVisible(False)
+                else:
+                    self.actions()[1].setVisible(True)
+                    act.actions()[0].setChecked(False)
+                    act.actions()[1].setChecked(False)
+                    act.actions()[2].setChecked(False)
+                    act.actions()[status].setChecked(True)
+                self.actions()[2].setVisible(not Settings.get_instance().locked)
+
+            def languageChange(self, *args, **kwargs):
+                self.actions()[0].setText(QtGui.QApplication.translate('tray', 'Open Toxygen', None, QtGui.QApplication.UnicodeUTF8))
+                self.actions()[1].setText(QtGui.QApplication.translate('tray', 'Set status', None, QtGui.QApplication.UnicodeUTF8))
+                self.actions()[2].setText(QtGui.QApplication.translate('tray', 'Exit', None, QtGui.QApplication.UnicodeUTF8))
+                self.act.actions()[0].setText(QtGui.QApplication.translate('tray', 'Online', None, QtGui.QApplication.UnicodeUTF8))
+                self.act.actions()[1].setText(QtGui.QApplication.translate('tray', 'Away', None, QtGui.QApplication.UnicodeUTF8))
+                self.act.actions()[2].setText(QtGui.QApplication.translate('tray', 'Busy', None, QtGui.QApplication.UnicodeUTF8))
+
+        m = Menu()
+        show = m.addAction(QtGui.QApplication.translate('tray', 'Open Toxygen', None, QtGui.QApplication.UnicodeUTF8))
+        sub = m.addMenu(QtGui.QApplication.translate('tray', 'Set status', None, QtGui.QApplication.UnicodeUTF8))
+        onl = sub.addAction(QtGui.QApplication.translate('tray', 'Online', None, QtGui.QApplication.UnicodeUTF8))
+        away = sub.addAction(QtGui.QApplication.translate('tray', 'Away', None, QtGui.QApplication.UnicodeUTF8))
+        busy = sub.addAction(QtGui.QApplication.translate('tray', 'Busy', None, QtGui.QApplication.UnicodeUTF8))
+        onl.setCheckable(True)
+        away.setCheckable(True)
+        busy.setCheckable(True)
+        m.act = sub
+        exit = m.addAction(QtGui.QApplication.translate('tray', 'Exit', None, QtGui.QApplication.UnicodeUTF8))
+
+        def show_window():
+            def show():
+                if not self.ms.isActiveWindow():
+                    self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
+                    self.ms.activateWindow()
+                self.ms.show()
+            if not Settings.get_instance().locked:
+                show()
+            else:
+                def correct_pass():
+                    show()
+                    Settings.get_instance().locked = False
+                self.p = UnlockAppScreen(toxencryptsave.ToxEncryptSave.get_instance(), correct_pass)
+                self.p.show()
+
+        m.connect(show, QtCore.SIGNAL("triggered()"), show_window)
+        m.connect(exit, QtCore.SIGNAL("triggered()"), lambda: app.exit())
+        m.connect(m, QtCore.SIGNAL("aboutToShow()"), lambda: m.aboutToShow())
+        sub.connect(onl, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(0))
+        sub.connect(away, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(1))
+        sub.connect(busy, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(2))
+
+        self.tray.setContextMenu(m)
+        self.tray.show()
+
+        self.ms.show()
+
+        plugin_helper = PluginLoader(self.tox, settings)  # plugin support
+        plugin_helper.load()
+
+        # init thread
+        self.init = self.InitThread(self.tox, self.ms, self.tray)
+        self.init.start()
+
+        # starting threads for tox iterate and toxav iterate
+        self.mainloop = self.ToxIterateThread(self.tox)
+        self.mainloop.start()
+        self.avloop = self.ToxAVIterateThread(self.tox.AV)
+        self.avloop.start()
+
+        if self.uri is not None:
+            self.ms.add_contact(self.uri)
+
+        app.connect(app, QtCore.SIGNAL("lastWindowClosed()"), app, QtCore.SLOT("quit()"))
+        app.exec_()
+        self.init.stop = True
+        self.mainloop.stop = True
+        self.avloop.stop = True
+        plugin_helper.stop()
+        self.mainloop.wait()
+        self.init.wait()
+        self.avloop.wait()
+        data = self.tox.get_savedata()
+        ProfileHelper.get_instance().save_profile(data)
+        settings.close()
+        del self.tox
+
+    def reset(self):
+        """
+        Create new tox instance (new network settings)
+        :return: tox instance
+        """
+        self.mainloop.stop = True
+        self.init.stop = True
+        self.avloop.stop = True
+        self.mainloop.wait()
+        self.init.wait()
+        self.avloop.wait()
+        data = self.tox.get_savedata()
+        ProfileHelper.get_instance().save_profile(data)
+        del self.tox
+        # create new tox instance
+        self.tox = profile.tox_factory(data, Settings.get_instance())
+        # init thread
+        self.init = self.InitThread(self.tox, self.ms, self.tray)
+        self.init.start()
+
+        # starting threads for tox iterate and toxav iterate
+        self.mainloop = self.ToxIterateThread(self.tox)
+        self.mainloop.start()
+
+        self.avloop = self.ToxAVIterateThread(self.tox.AV)
+        self.avloop.start()
+
+        plugin_helper = PluginLoader.get_instance()
+        plugin_helper.set_tox(self.tox)
+
+        return self.tox
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Inner classes
+    # -----------------------------------------------------------------------------------------------------------------
+
+    class InitThread(QtCore.QThread):
+
+        def __init__(self, tox, ms, tray):
+            QtCore.QThread.__init__(self)
+            self.tox, self.ms, self.tray = tox, ms, tray
+            self.stop = False
+
+        def run(self):
+            # initializing callbacks
+            init_callbacks(self.tox, self.ms, self.tray)
+            # bootstrap
+            try:
+                for data in node_generator():
+                    if self.stop:
+                        return
+                    self.tox.bootstrap(*data)
+                    self.tox.add_tcp_relay(*data)
+            except:
+                pass
+            for _ in range(10):
+                if self.stop:
+                    return
+                self.msleep(1000)
+            while not self.tox.self_get_connection_status():
+                try:
+                    for data in node_generator():
+                        if self.stop:
+                            return
+                        self.tox.bootstrap(*data)
+                        self.tox.add_tcp_relay(*data)
+                except:
+                    pass
+                finally:
+                    self.msleep(5000)
+
+    class ToxIterateThread(QtCore.QThread):
+
+        def __init__(self, tox):
+            QtCore.QThread.__init__(self)
+            self.tox = tox
+            self.stop = False
+
+        def run(self):
+            while not self.stop:
+                self.tox.iterate()
+                self.msleep(self.tox.iteration_interval())
+
+    class ToxAVIterateThread(QtCore.QThread):
+
+        def __init__(self, toxav):
+            QtCore.QThread.__init__(self)
+            self.toxav = toxav
+            self.stop = False
+
+        def run(self):
+            while not self.stop:
+                self.toxav.iterate()
+                self.msleep(self.toxav.iteration_interval())
+
+    class Login:
+
+        def __init__(self, arr):
+            self.arr = arr
+
+        def login_screen_close(self, t, number=-1, default=False, name=None):
+            """ Function which processes data from login screen
+            :param t: 0 - window was closed, 1 - new profile was created, 2 - profile loaded
+            :param number: num of chosen profile in list (-1 by default)
+            :param default: was or not chosen profile marked as default
+            :param name: name of new profile
+            """
+            self.t = t
+            self.num = number
+            self.default = default
+            self.name = name
+
+        def get_data(self):
+            return self.arr[self.num]
+
+
+def clean():
+    """Removes all windows libs from libs folder"""
+    d = curr_directory() + '/libs/'
+    for fl in ('libtox64.dll', 'libtox.dll', 'libsodium64.a', 'libsodium.a'):
+        if os.path.exists(d + fl):
+            os.remove(d + fl)
+
+
+def configure():
+    """Removes unused libs"""
+    d = curr_directory() + '/libs/'
+    is_64bits = sys.maxsize > 2 ** 32
+    if not is_64bits:
+        if os.path.exists(d + 'libtox64.dll'):
+            os.remove(d + 'libtox64.dll')
+        if os.path.exists(d + 'libsodium64.a'):
+            os.remove(d + 'libsodium64.a')
+    else:
+        if os.path.exists(d + 'libtox.dll'):
+            os.remove(d + 'libtox.dll')
+        if os.path.exists(d + 'libsodium.a'):
+            os.remove(d + 'libsodium.a')
+        try:
+            os.rename(d + 'libtox64.dll', d + 'libtox.dll')
+            os.rename(d + 'libsodium64.a', d + 'libsodium.a')
+        except:
+            pass
+
+
+def main():
+    if len(sys.argv) == 1:
+        toxygen = Toxygen()
+    else:  # started with argument(s)
+        arg = sys.argv[1]
+        if arg == '--version':
+            print('Toxygen ' + program_version)
+            return
+        elif arg == '--help':
+            print('Usage:\ntoxygen path_to_profile\ntoxygen tox_id\ntoxygen --version')
+            return
+        elif arg == '--configure':
+            configure()
+            return
+        elif arg == '--clean':
+            clean()
+            return
+        else:
+            toxygen = Toxygen(arg)
+    toxygen.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/toxygen/mainscreen.py b/toxygen/mainscreen.py
new file mode 100644
index 0000000..b828907
--- /dev/null
+++ b/toxygen/mainscreen.py
@@ -0,0 +1,677 @@
+# -*- coding: utf-8 -*-
+
+from menu import *
+from profile import *
+from list_items import *
+from widgets import MultilineEdit, LineEdit
+import plugin_support
+from mainscreen_widgets import *
+
+
+class MainWindow(QtGui.QMainWindow):
+
+    def __init__(self, tox, reset, tray):
+        super(MainWindow, self).__init__()
+        self.reset = reset
+        self.tray = tray
+        self.setAcceptDrops(True)
+        self.initUI(tox)
+        if settings.Settings.get_instance()['show_welcome_screen']:
+            self.ws = WelcomeScreen()
+
+    def setup_menu(self, MainWindow):
+        self.menubar = QtGui.QMenuBar(MainWindow)
+        self.menubar.setObjectName("menubar")
+        self.menubar.setNativeMenuBar(False)
+        self.menubar.setMinimumSize(self.width(), 25)
+        self.menubar.setMaximumSize(self.width(), 25)
+        self.menubar.setBaseSize(self.width(), 25)
+
+        self.menuProfile = QtGui.QMenu(self.menubar)
+        self.menuProfile.setObjectName("menuProfile")
+        self.menuGroupChats = QtGui.QMenu(self.menubar)
+        self.menuGroupChats.setObjectName("menuGroupChats")
+        self.menuSettings = QtGui.QMenu(self.menubar)
+        self.menuSettings.setObjectName("menuSettings")
+        self.menuPlugins = QtGui.QMenu(self.menubar)
+        self.menuPlugins.setObjectName("menuPlugins")
+        self.menuAbout = QtGui.QMenu(self.menubar)
+        self.menuAbout.setObjectName("menuAbout")
+
+        self.actionAdd_friend = QtGui.QAction(MainWindow)
+        self.actionAdd_friend.setObjectName("actionAdd_friend")
+        self.actionprofilesettings = QtGui.QAction(MainWindow)
+        self.actionprofilesettings.setObjectName("actionprofilesettings")
+        self.actionPrivacy_settings = QtGui.QAction(MainWindow)
+        self.actionPrivacy_settings.setObjectName("actionPrivacy_settings")
+        self.actionInterface_settings = QtGui.QAction(MainWindow)
+        self.actionInterface_settings.setObjectName("actionInterface_settings")
+        self.actionNotifications = QtGui.QAction(MainWindow)
+        self.actionNotifications.setObjectName("actionNotifications")
+        self.actionNetwork = QtGui.QAction(MainWindow)
+        self.actionNetwork.setObjectName("actionNetwork")
+        self.actionAbout_program = QtGui.QAction(MainWindow)
+        self.actionAbout_program.setObjectName("actionAbout_program")
+        self.actionSettings = QtGui.QAction(MainWindow)
+        self.actionSettings.setObjectName("actionSettings")
+        self.audioSettings = QtGui.QAction(MainWindow)
+        self.pluginData = QtGui.QAction(MainWindow)
+        self.importPlugin = QtGui.QAction(MainWindow)
+        self.lockApp = QtGui.QAction(MainWindow)
+        self.createGC = QtGui.QAction(MainWindow)
+        self.joinGC = QtGui.QAction(MainWindow)
+        self.gcRequests = QtGui.QAction(MainWindow)
+        self.menuGroupChats.addAction(self.createGC)
+        self.menuGroupChats.addAction(self.joinGC)
+        self.menuGroupChats.addAction(self.gcRequests)
+        self.menuProfile.addAction(self.actionAdd_friend)
+        self.menuProfile.addAction(self.actionSettings)
+        self.menuProfile.addAction(self.lockApp)
+        self.menuSettings.addAction(self.actionPrivacy_settings)
+        self.menuSettings.addAction(self.actionInterface_settings)
+        self.menuSettings.addAction(self.actionNotifications)
+        self.menuSettings.addAction(self.actionNetwork)
+        self.menuSettings.addAction(self.audioSettings)
+        self.menuPlugins.addAction(self.pluginData)
+        self.menuPlugins.addAction(self.importPlugin)
+        self.menuAbout.addAction(self.actionAbout_program)
+        self.menubar.addAction(self.menuProfile.menuAction())
+        self.menubar.addAction(self.menuGroupChats.menuAction())
+        self.menubar.addAction(self.menuSettings.menuAction())
+        self.menubar.addAction(self.menuPlugins.menuAction())
+        self.menubar.addAction(self.menuAbout.menuAction())
+
+        self.actionAbout_program.triggered.connect(self.about_program)
+        self.actionNetwork.triggered.connect(self.network_settings)
+        self.actionAdd_friend.triggered.connect(self.add_contact)
+        self.actionSettings.triggered.connect(self.profile_settings)
+        self.actionPrivacy_settings.triggered.connect(self.privacy_settings)
+        self.actionInterface_settings.triggered.connect(self.interface_settings)
+        self.actionNotifications.triggered.connect(self.notification_settings)
+        self.audioSettings.triggered.connect(self.audio_settings)
+        self.pluginData.triggered.connect(self.plugins_menu)
+
+        self.lockApp.triggered.connect(self.lock_app)
+        self.importPlugin.triggered.connect(self.import_plugin)
+        self.createGC.triggered.connect(self.create_groupchat)
+        self.joinGC.triggered.connect(self.join_groupchat)
+
+        QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+    def languageChange(self, *args, **kwargs):
+        self.retranslateUi()
+
+    def event(self, event):
+        if event.type() == QtCore.QEvent.WindowActivate:
+            self.tray.setIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
+        return super(MainWindow, self).event(event)
+
+    def retranslateUi(self):
+        self.joinGC.setText(QtGui.QApplication.translate("MainWindow", "Join group chat", None, QtGui.QApplication.UnicodeUTF8))
+        self.lockApp.setText(QtGui.QApplication.translate("MainWindow", "Lock", None, QtGui.QApplication.UnicodeUTF8))
+        self.menuGroupChats.setTitle(QtGui.QApplication.translate("MainWindow", "Group chats", None, QtGui.QApplication.UnicodeUTF8))
+        self.createGC.setText(QtGui.QApplication.translate("MainWindow", "Create group chat", None, QtGui.QApplication.UnicodeUTF8))
+        self.gcRequests.setText(QtGui.QApplication.translate("MainWindow", "Groupchat requests", None, QtGui.QApplication.UnicodeUTF8))
+        self.menuPlugins.setTitle(QtGui.QApplication.translate("MainWindow", "Plugins", None, QtGui.QApplication.UnicodeUTF8))
+        self.pluginData.setText(QtGui.QApplication.translate("MainWindow", "List of plugins", None, QtGui.QApplication.UnicodeUTF8))
+        self.menuProfile.setTitle(QtGui.QApplication.translate("MainWindow", "Profile", None, QtGui.QApplication.UnicodeUTF8))
+        self.menuSettings.setTitle(QtGui.QApplication.translate("MainWindow", "Settings", None, QtGui.QApplication.UnicodeUTF8))
+        self.menuAbout.setTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionAdd_friend.setText(QtGui.QApplication.translate("MainWindow", "Add contact", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionprofilesettings.setText(QtGui.QApplication.translate("MainWindow", "Profile", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionPrivacy_settings.setText(QtGui.QApplication.translate("MainWindow", "Privacy", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionInterface_settings.setText(QtGui.QApplication.translate("MainWindow", "Interface", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionNotifications.setText(QtGui.QApplication.translate("MainWindow", "Notifications", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionNetwork.setText(QtGui.QApplication.translate("MainWindow", "Network", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionAbout_program.setText(QtGui.QApplication.translate("MainWindow", "About program", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionSettings.setText(QtGui.QApplication.translate("MainWindow", "Settings", None, QtGui.QApplication.UnicodeUTF8))
+        self.audioSettings.setText(QtGui.QApplication.translate("MainWindow", "Audio", None, QtGui.QApplication.UnicodeUTF8))
+        self.contact_name.setPlaceholderText(QtGui.QApplication.translate("MainWindow", "Search", None, QtGui.QApplication.UnicodeUTF8))
+        self.sendMessageButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Send message", None, QtGui.QApplication.UnicodeUTF8))
+        self.callButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Start audio call with friend", None, QtGui.QApplication.UnicodeUTF8))
+        self.online_contacts.clear()
+        self.online_contacts.addItem(QtGui.QApplication.translate("MainWindow", "All", None, QtGui.QApplication.UnicodeUTF8))
+        self.online_contacts.addItem(QtGui.QApplication.translate("MainWindow", "Online", None, QtGui.QApplication.UnicodeUTF8))
+        self.online_contacts.setCurrentIndex(int(Settings.get_instance()['show_online_friends']))
+        self.importPlugin.setText(QtGui.QApplication.translate("MainWindow", "Import plugin", None, QtGui.QApplication.UnicodeUTF8))
+
+    def setup_right_bottom(self, Form):
+        Form.resize(650, 60)
+        self.messageEdit = MessageArea(Form, self)
+        self.messageEdit.setGeometry(QtCore.QRect(0, 3, 450, 55))
+        self.messageEdit.setObjectName("messageEdit")
+        font = QtGui.QFont()
+        font.setPointSize(10)
+        self.messageEdit.setFont(font)
+
+        self.sendMessageButton = QtGui.QPushButton(Form)
+        self.sendMessageButton.setGeometry(QtCore.QRect(565, 3, 60, 55))
+        self.sendMessageButton.setObjectName("sendMessageButton")
+
+        self.menuButton = MenuButton(Form, self.show_menu)
+        self.menuButton.setGeometry(QtCore.QRect(QtCore.QRect(455, 3, 55, 55)))
+
+        pixmap = QtGui.QPixmap('send.png')
+        icon = QtGui.QIcon(pixmap)
+        self.sendMessageButton.setIcon(icon)
+        self.sendMessageButton.setIconSize(QtCore.QSize(45, 60))
+
+        pixmap = QtGui.QPixmap('menu.png')
+        icon = QtGui.QIcon(pixmap)
+        self.menuButton.setIcon(icon)
+        self.menuButton.setIconSize(QtCore.QSize(40, 40))
+
+        self.sendMessageButton.clicked.connect(self.send_message)
+
+        QtCore.QMetaObject.connectSlotsByName(Form)
+
+    def setup_left_center_menu(self, Form):
+        Form.resize(270, 25)
+        self.search_label = QtGui.QLabel(Form)
+        self.search_label.setGeometry(QtCore.QRect(3, 2, 20, 20))
+        pixmap = QtGui.QPixmap()
+        pixmap.load(curr_directory() + '/images/search.png')
+        self.search_label.setScaledContents(False)
+        self.search_label.setPixmap(pixmap)
+
+        self.contact_name = LineEdit(Form)
+        self.contact_name.setGeometry(QtCore.QRect(0, 0, 150, 25))
+        self.contact_name.setObjectName("contact_name")
+        self.contact_name.textChanged.connect(self.filtering)
+
+        self.online_contacts = QtGui.QComboBox(Form)
+        self.online_contacts.setGeometry(QtCore.QRect(150, 0, 120, 25))
+        self.online_contacts.activated[int].connect(lambda x: self.filtering())
+        self.search_label.raise_()
+
+        QtCore.QMetaObject.connectSlotsByName(Form)
+
+    def setup_left_top(self, Form):
+        Form.setCursor(QtCore.Qt.PointingHandCursor)
+        Form.setMinimumSize(QtCore.QSize(270, 100))
+        Form.setMaximumSize(QtCore.QSize(270, 100))
+        Form.setBaseSize(QtCore.QSize(270, 100))
+        self.avatar_label = Form.avatar_label = QtGui.QLabel(Form)
+        self.avatar_label.setGeometry(QtCore.QRect(5, 30, 64, 64))
+        self.avatar_label.setScaledContents(True)
+        self.name = Form.name = DataLabel(Form)
+        Form.name.setGeometry(QtCore.QRect(75, 40, 150, 25))
+        font = QtGui.QFont()
+        font.setFamily("Times New Roman")
+        font.setPointSize(14)
+        font.setBold(True)
+        Form.name.setFont(font)
+        Form.name.setObjectName("name")
+        self.status_message = Form.status_message = DataLabel(Form)
+        Form.status_message.setGeometry(QtCore.QRect(75, 60, 170, 25))
+        font.setPointSize(12)
+        font.setBold(False)
+        Form.status_message.setFont(font)
+        Form.status_message.setObjectName("status_message")
+        self.connection_status = Form.connection_status = StatusCircle(Form)
+        Form.connection_status.setGeometry(QtCore.QRect(230, 35, 32, 32))
+        self.avatar_label.mouseReleaseEvent = self.profile_settings
+        self.status_message.mouseReleaseEvent = self.profile_settings
+        self.name.mouseReleaseEvent = self.profile_settings
+        self.connection_status.raise_()
+        Form.connection_status.setObjectName("connection_status")
+
+    def setup_right_top(self, Form):
+        Form.resize(650, 100)
+        self.account_avatar = QtGui.QLabel(Form)
+        self.account_avatar.setGeometry(QtCore.QRect(10, 30, 64, 64))
+        self.account_avatar.setScaledContents(True)
+        self.account_name = DataLabel(Form)
+        self.account_name.setGeometry(QtCore.QRect(100, 25, 400, 25))
+        self.account_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse)
+        font = QtGui.QFont()
+        font.setFamily("Times New Roman")
+        font.setPointSize(14)
+        font.setBold(True)
+        self.account_name.setFont(font)
+        self.account_name.setObjectName("account_name")
+        self.account_status = DataLabel(Form)
+        self.account_status.setGeometry(QtCore.QRect(100, 45, 400, 25))
+        self.account_status.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse)
+        font.setPointSize(12)
+        font.setBold(False)
+        self.account_status.setFont(font)
+        self.account_status.setObjectName("account_status")
+
+        self.account_status.mouseReleaseEvent = self.show_chat_menu
+        self.account_name.mouseReleaseEvent = self.show_chat_menu
+        self.account_avatar.mouseReleaseEvent = self.show_chat_menu
+
+        self.callButton = QtGui.QPushButton(Form)
+        self.callButton.setGeometry(QtCore.QRect(550, 30, 50, 50))
+        self.callButton.setObjectName("callButton")
+        self.callButton.clicked.connect(lambda: self.profile.call_click(True))
+        self.videocallButton = QtGui.QPushButton(Form)
+        self.videocallButton.setGeometry(QtCore.QRect(550, 30, 50, 50))
+        self.videocallButton.setObjectName("videocallButton")
+        self.videocallButton.clicked.connect(lambda: self.profile.call_click(True, True))
+        self.update_call_state('call')
+        self.typing = QtGui.QLabel(Form)
+        self.typing.setGeometry(QtCore.QRect(500, 50, 50, 30))
+        pixmap = QtGui.QPixmap(QtCore.QSize(50, 30))
+        pixmap.load(curr_directory() + '/images/typing.png')
+        self.typing.setScaledContents(False)
+        self.typing.setPixmap(pixmap.scaled(50, 30, QtCore.Qt.KeepAspectRatio))
+        self.typing.setVisible(False)
+        QtCore.QMetaObject.connectSlotsByName(Form)
+
+    def setup_left_center(self, widget):
+        self.friends_list = QtGui.QListWidget(widget)
+        self.friends_list.setObjectName("friends_list")
+        self.friends_list.setGeometry(0, 0, 270, 310)
+        self.friends_list.clicked.connect(self.friend_click)
+        self.friends_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+        self.friends_list.connect(self.friends_list, QtCore.SIGNAL("customContextMenuRequested(QPoint)"),
+                                  self.friend_right_click)
+        self.friends_list.setVerticalScrollMode(QtGui.QAbstractItemView.ScrollPerPixel)
+
+    def setup_right_center(self, widget):
+        self.messages = QtGui.QListWidget(widget)
+        self.messages.setGeometry(0, 0, 620, 310)
+        self.messages.setObjectName("messages")
+        self.messages.setSpacing(1)
+        self.messages.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
+        self.messages.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+        self.messages.setFocusPolicy(QtCore.Qt.NoFocus)
+
+        def load(pos):
+            if not pos:
+                self.profile.load_history()
+                self.messages.verticalScrollBar().setValue(1)
+        self.messages.verticalScrollBar().valueChanged.connect(load)
+        self.messages.setVerticalScrollMode(QtGui.QAbstractItemView.ScrollPerPixel)
+
+    def initUI(self, tox):
+        self.setMinimumSize(920, 500)
+        s = Settings.get_instance()
+        self.setGeometry(s['x'], s['y'], s['width'], s['height'])
+        self.setWindowTitle('Toxygen')
+        os.chdir(curr_directory() + '/images/')
+        main = QtGui.QWidget()
+        grid = QtGui.QGridLayout()
+        search = QtGui.QWidget()
+        name = QtGui.QWidget()
+        info = QtGui.QWidget()
+        main_list = QtGui.QWidget()
+        messages = QtGui.QWidget()
+        message_buttons = QtGui.QWidget()
+        self.setup_left_center_menu(search)
+        self.setup_left_top(name)
+        self.setup_right_center(messages)
+        self.setup_right_top(info)
+        self.setup_right_bottom(message_buttons)
+        self.setup_left_center(main_list)
+        if not Settings.get_instance()['mirror_mode']:
+            grid.addWidget(search, 1, 0)
+            grid.addWidget(name, 0, 0)
+            grid.addWidget(messages, 1, 1, 2, 1)
+            grid.addWidget(info, 0, 1)
+            grid.addWidget(message_buttons, 3, 1)
+            grid.addWidget(main_list, 2, 0, 2, 1)
+            grid.setColumnMinimumWidth(1, 500)
+            grid.setColumnMinimumWidth(0, 270)
+        else:
+            grid.addWidget(search, 1, 1)
+            grid.addWidget(name, 0, 1)
+            grid.addWidget(messages, 1, 0, 2, 1)
+            grid.addWidget(info, 0, 0)
+            grid.addWidget(message_buttons, 3, 0)
+            grid.addWidget(main_list, 2, 1, 2, 1)
+            grid.setColumnMinimumWidth(0, 500)
+            grid.setColumnMinimumWidth(1, 270)
+        grid.setSpacing(0)
+        grid.setContentsMargins(0, 0, 0, 0)
+        grid.setRowMinimumHeight(0, 100)
+        grid.setRowMinimumHeight(1, 25)
+        grid.setRowMinimumHeight(2, 320)
+        grid.setRowMinimumHeight(3, 55)
+        grid.setColumnStretch(1, 1)
+        grid.setRowStretch(2, 1)
+        main.setLayout(grid)
+        self.setCentralWidget(main)
+        self.setup_menu(self)
+        self.messageEdit.setFocus()
+        self.user_info = name
+        self.friend_info = info
+        self.retranslateUi()
+        self.profile = Profile(tox, self)
+
+    def closeEvent(self, *args, **kwargs):
+        self.profile.save_history()
+        self.profile.close()
+        s = Settings.get_instance()
+        s['x'] = self.pos().x()
+        s['y'] = self.pos().y()
+        s['width'] = self.width()
+        s['height'] = self.height()
+        s.save()
+        QtGui.QApplication.closeAllWindows()
+
+    def resizeEvent(self, *args, **kwargs):
+        self.messages.setGeometry(0, 0, self.width() - 270, self.height() - 155)
+        self.friends_list.setGeometry(0, 0, 270, self.height() - 125)
+
+        self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 40, 50, 50))
+        self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 40, 50, 50))
+        self.typing.setGeometry(QtCore.QRect(self.width() - 450, 50, 50, 30))
+
+        self.messageEdit.setGeometry(QtCore.QRect(55, 0, self.width() - 395, 55))
+        self.menuButton.setGeometry(QtCore.QRect(0, 0, 55, 55))
+        self.sendMessageButton.setGeometry(QtCore.QRect(self.width() - 340, 0, 70, 55))
+
+        self.account_name.setGeometry(QtCore.QRect(100, 40, self.width() - 560, 25))
+        self.account_status.setGeometry(QtCore.QRect(100, 60, self.width() - 560, 25))
+        self.messageEdit.setFocus()
+        self.profile.update()
+
+    def keyPressEvent(self, event):
+        if event.key() == QtCore.Qt.Key_Escape:
+            self.hide()
+        else:
+            super(MainWindow, self).keyPressEvent(event)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Functions which called when user click in menu
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def about_program(self):
+        import util
+        msgBox = QtGui.QMessageBox()
+        msgBox.setWindowTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))
+        text = (QtGui.QApplication.translate("MainWindow", 'Toxygen is Tox client written on Python.\nVersion: ', None, QtGui.QApplication.UnicodeUTF8))
+        msgBox.setText(text + util.program_version + '\nGitHub: github.com/xveduk/toxygen/')
+        msgBox.exec_()
+
+    def network_settings(self):
+        self.n_s = NetworkSettings(self.reset)
+        self.n_s.show()
+
+    def plugins_menu(self):
+        self.p_s = PluginsSettings()
+        self.p_s.show()
+
+    def add_contact(self, link=''):
+        self.a_c = AddContact(link)
+        self.a_c.show()
+
+    def profile_settings(self, *args):
+        self.p_s = ProfileSettings()
+        self.p_s.show()
+
+    def privacy_settings(self):
+        self.priv_s = PrivacySettings()
+        self.priv_s.show()
+
+    def notification_settings(self):
+        self.notif_s = NotificationsSettings()
+        self.notif_s.show()
+
+    def interface_settings(self):
+        self.int_s = InterfaceSettings()
+        self.int_s.show()
+
+    def audio_settings(self):
+        self.audio_s = AudioSettings()
+        self.audio_s.show()
+
+    def import_plugin(self):
+        import util
+        directory = QtGui.QFileDialog.getExistingDirectory(self,
+                                                           QtGui.QApplication.translate("MainWindow", 'Choose folder with plugin',
+                                                                                        None,
+                                                                                        QtGui.QApplication.UnicodeUTF8),
+                                                           util.curr_directory(),
+                                                           QtGui.QFileDialog.ShowDirsOnly | QtGui.QFileDialog.DontUseNativeDialog)
+        if directory:
+            src = directory + '/'
+            dest = curr_directory() + '/plugins/'
+            util.copy(src, dest)
+            msgBox = QtGui.QMessageBox()
+            msgBox.setWindowTitle(
+                QtGui.QApplication.translate("MainWindow", "Restart Toxygen", None, QtGui.QApplication.UnicodeUTF8))
+            msgBox.setText(
+                QtGui.QApplication.translate("MainWindow", 'Plugin will be loaded after restart', None,
+                                             QtGui.QApplication.UnicodeUTF8))
+            msgBox.exec_()
+
+    def lock_app(self):
+        if toxencryptsave.ToxEncryptSave.get_instance().has_password():
+            Settings.get_instance().locked = True
+            self.hide()
+        else:
+            msgBox = QtGui.QMessageBox()
+            msgBox.setWindowTitle(
+                QtGui.QApplication.translate("MainWindow", "Cannot lock app", None, QtGui.QApplication.UnicodeUTF8))
+            msgBox.setText(
+                QtGui.QApplication.translate("MainWindow", 'Error. Profile password is not set.', None,
+                                             QtGui.QApplication.UnicodeUTF8))
+            msgBox.exec_()
+
+    def show_menu(self):
+        if not hasattr(self, 'menu'):
+            self.menu = DropdownMenu(self)
+        self.menu.setGeometry(QtCore.QRect(0 if Settings.get_instance()['mirror_mode'] else 270,
+                                           self.height() - 120,
+                                           180,
+                                           120))
+        self.menu.show()
+
+    def create_groupchat(self):
+        self.gc = AddGroupchat()
+        self.gc.show()
+
+    def join_groupchat(self):
+        self.gc = JoinGroupchat()
+        self.gc.show()
+
+    def show_chat_menu(self):
+        pr = Profile.get_instance()
+        if not pr.is_active_a_friend():
+            pass  # TODO: show list of users in chat
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Messages, calls and file transfers
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def send_message(self):
+        text = self.messageEdit.toPlainText()
+        self.profile.send_message(text)
+
+    def send_file(self):
+        self.menu.hide()
+        if not self.profile.is_active_a_friend():
+            return
+        if self.profile.active_friend + 1:
+            choose = QtGui.QApplication.translate("MainWindow", 'Choose file', None, QtGui.QApplication.UnicodeUTF8)
+            name = QtGui.QFileDialog.getOpenFileName(self, choose, options=QtGui.QFileDialog.DontUseNativeDialog)
+            if name[0]:
+                self.profile.send_file(name[0])
+
+    def send_screenshot(self, hide=False):
+        self.menu.hide()
+        if not self.profile.is_active_a_friend():
+            return
+        if self.profile.active_friend + 1:
+            self.sw = ScreenShotWindow(self)
+            self.sw.show()
+            if hide:
+                self.hide()
+
+    def send_smiley(self):
+        self.menu.hide()
+        if self.profile.active_friend + 1:
+            self.smiley = SmileyWindow(self)
+            self.smiley.setGeometry(QtCore.QRect(self.x() if Settings.get_instance()['mirror_mode'] else 270 + self.x(),
+                                                 self.y() + self.height() - 200,
+                                                 self.smiley.width(),
+                                                 self.smiley.height()))
+            self.smiley.show()
+
+    def send_sticker(self):
+        self.menu.hide()
+        if not self.profile.is_active_a_friend():
+            return
+        if self.profile.active_friend + 1:
+            self.sticker = StickerWindow(self)
+            self.sticker.setGeometry(QtCore.QRect(self.x() if Settings.get_instance()['mirror_mode'] else 270 + self.x(),
+                                                 self.y() + self.height() - 200,
+                                                 self.sticker.width(),
+                                                 self.sticker.height()))
+            self.sticker.show()
+
+    def active_call(self):
+        self.update_call_state('finish_call')
+
+    def incoming_call(self):
+        self.update_call_state('incoming_call')
+
+    def call_finished(self):
+        self.update_call_state('call')
+
+    def update_call_state(self, fl):
+        # TODO: do smth with video call button
+        os.chdir(curr_directory() + '/images/')
+        pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(fl))
+        icon = QtGui.QIcon(pixmap)
+        self.callButton.setIcon(icon)
+        self.callButton.setIconSize(QtCore.QSize(50, 50))
+        pixmap = QtGui.QPixmap(curr_directory() + '/images/videocall.png')
+        icon = QtGui.QIcon(pixmap)
+        self.videocallButton.setIcon(icon)
+        self.videocallButton.setIconSize(QtCore.QSize(35, 35))
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Functions which called when user open context menu in friends list
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def friend_right_click(self, pos):
+        item = self.friends_list.itemAt(pos)
+        num = self.friends_list.indexFromItem(item).row()
+        friend = Profile.get_instance().get_friend_or_gc(num)
+        settings = Settings.get_instance()
+        allowed = friend.tox_id in settings['auto_accept_from_friends']
+        auto = QtGui.QApplication.translate("MainWindow", 'Disallow auto accept', None, QtGui.QApplication.UnicodeUTF8) if allowed else QtGui.QApplication.translate("MainWindow", 'Allow auto accept', None, QtGui.QApplication.UnicodeUTF8)
+        if item is not None:
+            self.listMenu = QtGui.QMenu()
+            if type(friend) is Friend:
+                arr = Profile.get_instance().get_all_gc()
+                if arr:
+                    gc_menu = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Invite to group chat', None, QtGui.QApplication.UnicodeUTF8))
+                    for gc in arr:
+                        item = gc_menu.addAction(gc.name)
+                        self.connect(item, QtCore.SIGNAL("triggered()"),
+                                     lambda: Profile.get_instance().invite_friend(gc.number, friend.number))
+
+                set_alias_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Set alias', None, QtGui.QApplication.UnicodeUTF8))
+                clear_history_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Clear history', None, QtGui.QApplication.UnicodeUTF8))
+                copy_menu = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Copy', None, QtGui.QApplication.UnicodeUTF8))
+                copy_name_item = copy_menu.addAction(QtGui.QApplication.translate("MainWindow", 'Name', None, QtGui.QApplication.UnicodeUTF8))
+                copy_status_item = copy_menu.addAction(QtGui.QApplication.translate("MainWindow", 'Status message', None, QtGui.QApplication.UnicodeUTF8))
+                copy_key_item = copy_menu.addAction(QtGui.QApplication.translate("MainWindow", 'Public key', None, QtGui.QApplication.UnicodeUTF8))
+
+                auto_accept_item = self.listMenu.addAction(auto)
+                remove_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Remove friend', None, QtGui.QApplication.UnicodeUTF8))
+                notes_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Notes', None, QtGui.QApplication.UnicodeUTF8))
+
+                submenu = plugin_support.PluginLoader.get_instance().get_menu(self.listMenu, num)
+                if len(submenu):
+                    plug = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Plugins', None, QtGui.QApplication.UnicodeUTF8))
+                    plug.addActions(submenu)
+                self.connect(remove_item, QtCore.SIGNAL("triggered()"), lambda: self.remove_friend(num))
+                self.connect(auto_accept_item, QtCore.SIGNAL("triggered()"), lambda: self.auto_accept(num, not allowed))
+            else:
+                copy_menu = self.listMenu.addMenu(
+                    QtGui.QApplication.translate("MainWindow", 'Copy', None, QtGui.QApplication.UnicodeUTF8))
+                copy_name_item = copy_menu.addAction(
+                    QtGui.QApplication.translate("MainWindow", 'Name', None, QtGui.QApplication.UnicodeUTF8))
+                copy_status_item = copy_menu.addAction(
+                    QtGui.QApplication.translate("MainWindow", 'Topic', None, QtGui.QApplication.UnicodeUTF8))
+                copy_key_item = copy_menu.addAction(
+                    QtGui.QApplication.translate("MainWindow", 'Public key', None, QtGui.QApplication.UnicodeUTF8))
+                leave_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Leave group', None, QtGui.QApplication.UnicodeUTF8))
+                set_alias_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Set alias', None, QtGui.QApplication.UnicodeUTF8))
+                clear_history_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Clear history', None, QtGui.QApplication.UnicodeUTF8))
+                notes_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Notes', None, QtGui.QApplication.UnicodeUTF8))
+                self.connect(leave_item, QtCore.SIGNAL("triggered()"), lambda: Profile.get_instance().leave_group(num))
+
+            self.connect(notes_item, QtCore.SIGNAL("triggered()"), lambda: self.show_note(friend))
+            self.connect(set_alias_item, QtCore.SIGNAL("triggered()"), lambda: self.set_alias(num))
+            self.connect(clear_history_item, QtCore.SIGNAL("triggered()"), lambda: self.clear_history(num))
+            self.connect(copy_name_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_name(friend))
+            self.connect(copy_status_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_status(friend))
+            self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(num))
+
+            parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0))
+            self.listMenu.move(parent_position + pos)
+            self.listMenu.show()
+
+    def show_note(self, friend):
+        s = Settings.get_instance()
+        note = s['notes'][friend.tox_id] if friend.tox_id in s['notes'] else ''
+        user = QtGui.QApplication.translate("MainWindow", 'Notes about user', None, QtGui.QApplication.UnicodeUTF8)
+        user = '{} {}'.format(user, friend.name)
+
+        def save_note(text):
+            if friend.tox_id in s['notes']:
+                del s['notes'][friend.tox_id]
+            if text:
+                s['notes'][friend.tox_id] = text
+            s.save()
+        self.note = MultilineEdit(user, note, save_note)
+        self.note.show()
+
+    def set_alias(self, num):
+        self.profile.set_alias(num)
+
+    def remove_friend(self, num):
+        self.profile.delete_friend_or_gc(num)
+
+    def copy_friend_key(self, num):
+        tox_id = self.profile.friend_public_key(num)
+        clipboard = QtGui.QApplication.clipboard()
+        clipboard.setText(tox_id)
+
+    def copy_name(self, friend):
+        clipboard = QtGui.QApplication.clipboard()
+        clipboard.setText(friend.name)
+
+    def copy_status(self, friend):
+        clipboard = QtGui.QApplication.clipboard()
+        clipboard.setText(friend.status_message)
+
+    def clear_history(self, num):
+        self.profile.clear_history(num)
+
+    def auto_accept(self, num, value):
+        settings = Settings.get_instance()
+        tox_id = self.profile.friend_public_key(num)
+        if value:
+            settings['auto_accept_from_friends'].append(tox_id)
+        else:
+            settings['auto_accept_from_friends'].remove(tox_id)
+        settings.save()
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Functions which called when user click somewhere else
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def friend_click(self, index):
+        num = index.row()
+        self.profile.set_active(num)
+
+    def mouseReleaseEvent(self, event):
+        pos = self.connection_status.pos()
+        x, y = pos.x() + self.user_info.pos().x(), pos.y() + self.user_info.pos().y()
+        if (x < event.x() < x + 32) and (y < event.y() < y + 32):
+            self.profile.change_status()
+        else:
+            super(MainWindow, self).mouseReleaseEvent(event)
+
+    def filtering(self):
+        self.profile.filtration(self.online_contacts.currentIndex() == 1, self.contact_name.text())
+
diff --git a/toxygen/mainscreen_widgets.py b/toxygen/mainscreen_widgets.py
new file mode 100644
index 0000000..f12f521
--- /dev/null
+++ b/toxygen/mainscreen_widgets.py
@@ -0,0 +1,378 @@
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+from widgets import RubberBand, create_menu, QRightClickButton, CenteredWidget
+from profile import Profile
+import smileys
+import util
+
+
+class MessageArea(QtGui.QPlainTextEdit):
+    """User types messages here"""
+
+    def __init__(self, parent, form):
+        super(MessageArea, self).__init__(parent)
+        self.parent = form
+        self.setAcceptDrops(True)
+        self.timer = QtCore.QTimer(self)
+        self.timer.timeout.connect(lambda: self.parent.profile.send_typing(False))
+
+    def keyPressEvent(self, event):
+        if event.matches(QtGui.QKeySequence.Paste):
+            self.pasteEvent()
+        elif event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
+            modifiers = event.modifiers()
+            if modifiers & QtCore.Qt.ControlModifier or modifiers & QtCore.Qt.ShiftModifier:
+                self.insertPlainText('\n')
+            else:
+                if self.timer.isActive():
+                    self.timer.stop()
+                self.parent.profile.send_typing(False)
+                self.parent.send_message()
+        elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText():
+            self.appendPlainText(Profile.get_instance().get_last_message())
+        else:
+            self.parent.profile.send_typing(True)
+            if self.timer.isActive():
+                self.timer.stop()
+            self.timer.start(5000)
+            super(MessageArea, self).keyPressEvent(event)
+
+    def contextMenuEvent(self, event):
+        menu = create_menu(self.createStandardContextMenu())
+        menu.exec_(event.globalPos())
+        del menu
+
+    def dragEnterEvent(self, e):
+        e.accept()
+
+    def dragMoveEvent(self, e):
+        e.accept()
+
+    def dropEvent(self, e):
+        if e.mimeData().hasFormat('text/plain'):
+            e.accept()
+            self.pasteEvent(e.mimeData().text())
+        else:
+            e.ignore()
+
+    def pasteEvent(self, text=None):
+        text = text or QtGui.QApplication.clipboard().text()
+        if text.startswith('file://'):
+            self.parent.profile.send_file(text[7:])
+        else:
+            self.insertPlainText(text)
+
+
+class ScreenShotWindow(QtGui.QWidget):
+
+    def __init__(self, parent):
+        super(ScreenShotWindow, self).__init__()
+        self.parent = parent
+        self.setMouseTracking(True)
+        self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
+        self.showFullScreen()
+        self.setWindowOpacity(0.5)
+        self.rubberband = RubberBand()
+
+    def closeEvent(self, *args):
+        if self.parent.isHidden():
+            self.parent.show()
+
+    def mousePressEvent(self, event):
+        self.origin = event.pos()
+        self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize()))
+        self.rubberband.show()
+        QtGui.QWidget.mousePressEvent(self, event)
+
+    def mouseMoveEvent(self, event):
+        if self.rubberband.isVisible():
+            self.rubberband.setGeometry(QtCore.QRect(self.origin, event.pos()).normalized())
+            left = QtGui.QRegion(QtCore.QRect(0, 0, self.rubberband.x(), self.height()))
+            right = QtGui.QRegion(QtCore.QRect(self.rubberband.x() + self.rubberband.width(), 0, self.width(), self.height()))
+            top = QtGui.QRegion(0, 0, self.width(), self.rubberband.y())
+            bottom = QtGui.QRegion(0, self.rubberband.y() + self.rubberband.height(), self.width(), self.height())
+            self.setMask(left + right + top + bottom)
+
+    def mouseReleaseEvent(self, event):
+        if self.rubberband.isVisible():
+            self.rubberband.hide()
+            rect = self.rubberband.geometry()
+            if rect.width() and rect.height():
+                p = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId(),
+                                             rect.x() + 4,
+                                             rect.y() + 4,
+                                             rect.width() - 8,
+                                             rect.height() - 8)
+                byte_array = QtCore.QByteArray()
+                buffer = QtCore.QBuffer(byte_array)
+                buffer.open(QtCore.QIODevice.WriteOnly)
+                p.save(buffer, 'PNG')
+                Profile.get_instance().send_screenshot(bytes(byte_array.data()))
+            self.close()
+
+    def keyPressEvent(self, event):
+        if event.key() == QtCore.Qt.Key_Escape:
+            self.rubberband.setHidden(True)
+            self.close()
+        else:
+            super(ScreenShotWindow, self).keyPressEvent(event)
+
+
+class SmileyWindow(QtGui.QWidget):
+    """
+    Smiley selection window
+    """
+
+    def __init__(self, parent):
+        super(SmileyWindow, self).__init__()
+        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
+        inst = smileys.SmileyLoader.get_instance()
+        self.data = inst.get_smileys()
+        count = len(self.data)
+        if not count:
+            self.close()
+        self.page_size = int(pow(count / 8, 0.5) + 1) * 8  # smileys per page
+        if count % self.page_size == 0:
+            self.page_count = count // self.page_size
+        else:
+            self.page_count = round(count / self.page_size + 0.5)
+        self.page = -1
+        self.radio = []
+        self.parent = parent
+        for i in range(self.page_count):  # buttons with smileys
+            elem = QtGui.QRadioButton(self)
+            elem.setGeometry(QtCore.QRect(i * 20 + 5, 180, 20, 20))
+            elem.clicked.connect(lambda i=i: self.checked(i))
+            self.radio.append(elem)
+        width = max(self.page_count * 20 + 30, (self.page_size + 5) * 8 // 10)
+        self.setMaximumSize(width, 200)
+        self.setMinimumSize(width, 200)
+        self.buttons = []
+        for i in range(self.page_size):  # pages - radio buttons
+            b = QtGui.QPushButton(self)
+            b.setGeometry(QtCore.QRect((i // 8) * 20 + 5, (i % 8) * 20, 20, 20))
+            b.clicked.connect(lambda i=i: self.clicked(i))
+            self.buttons.append(b)
+        self.checked(0)
+
+    def checked(self, pos):  # new page opened
+        self.radio[self.page].setChecked(False)
+        self.radio[pos].setChecked(True)
+        self.page = pos
+        start = self.page * self.page_size
+        for i in range(self.page_size):
+            try:
+                self.buttons[i].setVisible(True)
+                pixmap = QtGui.QPixmap(self.data[start + i][1])
+                icon = QtGui.QIcon(pixmap)
+                self.buttons[i].setIcon(icon)
+            except:
+                self.buttons[i].setVisible(False)
+
+    def clicked(self, pos):  # smiley selected
+        pos += self.page * self.page_size
+        smiley = self.data[pos][0]
+        self.parent.messageEdit.insertPlainText(smiley)
+        self.close()
+
+    def leaveEvent(self, event):
+        self.close()
+
+
+class MenuButton(QtGui.QPushButton):
+
+    def __init__(self, parent, enter):
+        super(MenuButton, self).__init__(parent)
+        self.enter = enter
+
+    def enterEvent(self, event):
+        self.enter()
+        super(MenuButton, self).enterEvent(event)
+
+
+class DropdownMenu(QtGui.QWidget):
+
+    def __init__(self, parent):
+        super(DropdownMenu, self).__init__(parent)
+        self.installEventFilter(self)
+        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
+        self.setMaximumSize(180, 120)
+        self.setMinimumSize(180, 120)
+        self.screenshotButton = QRightClickButton(self)
+        self.screenshotButton.setGeometry(QtCore.QRect(0, 60, 60, 60))
+        self.screenshotButton.setObjectName("screenshotButton")
+
+        self.fileTransferButton = QtGui.QPushButton(self)
+        self.fileTransferButton.setGeometry(QtCore.QRect(60, 60, 60, 60))
+        self.fileTransferButton.setObjectName("fileTransferButton")
+
+        self.audioMessageButton = QtGui.QPushButton(self)
+        self.audioMessageButton.setGeometry(QtCore.QRect(120, 60, 60, 60))
+
+        self.smileyButton = QtGui.QPushButton(self)
+        self.smileyButton.setGeometry(QtCore.QRect(0, 0, 60, 60))
+
+        self.videoMessageButton = QtGui.QPushButton(self)
+        self.videoMessageButton.setGeometry(QtCore.QRect(120, 0, 60, 60))
+
+        self.stickerButton = QtGui.QPushButton(self)
+        self.stickerButton.setGeometry(QtCore.QRect(60, 0, 60, 60))
+
+        pixmap = QtGui.QPixmap(util.curr_directory() + '/images/file.png')
+        icon = QtGui.QIcon(pixmap)
+        self.fileTransferButton.setIcon(icon)
+        self.fileTransferButton.setIconSize(QtCore.QSize(50, 50))
+        pixmap = QtGui.QPixmap(util.curr_directory() + '/images/screenshot.png')
+        icon = QtGui.QIcon(pixmap)
+        self.screenshotButton.setIcon(icon)
+        self.screenshotButton.setIconSize(QtCore.QSize(50, 60))
+        pixmap = QtGui.QPixmap(util.curr_directory() + '/images/audio_message.png')
+        icon = QtGui.QIcon(pixmap)
+        self.audioMessageButton.setIcon(icon)
+        self.audioMessageButton.setIconSize(QtCore.QSize(50, 50))
+        pixmap = QtGui.QPixmap(util.curr_directory() + '/images/smiley.png')
+        icon = QtGui.QIcon(pixmap)
+        self.smileyButton.setIcon(icon)
+        self.smileyButton.setIconSize(QtCore.QSize(50, 50))
+        pixmap = QtGui.QPixmap(util.curr_directory() + '/images/video_message.png')
+        icon = QtGui.QIcon(pixmap)
+        self.videoMessageButton.setIcon(icon)
+        self.videoMessageButton.setIconSize(QtCore.QSize(55, 55))
+        pixmap = QtGui.QPixmap(util.curr_directory() + '/images/sticker.png')
+        icon = QtGui.QIcon(pixmap)
+        self.stickerButton.setIcon(icon)
+        self.stickerButton.setIconSize(QtCore.QSize(55, 55))
+
+        self.screenshotButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send screenshot", None, QtGui.QApplication.UnicodeUTF8))
+        self.fileTransferButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send file", None, QtGui.QApplication.UnicodeUTF8))
+        self.audioMessageButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send audio message", None, QtGui.QApplication.UnicodeUTF8))
+        self.videoMessageButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send video message", None, QtGui.QApplication.UnicodeUTF8))
+        self.smileyButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Add smiley", None, QtGui.QApplication.UnicodeUTF8))
+        self.stickerButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send sticker", None, QtGui.QApplication.UnicodeUTF8))
+
+        self.fileTransferButton.clicked.connect(parent.send_file)
+        self.screenshotButton.clicked.connect(parent.send_screenshot)
+        self.connect(self.screenshotButton, QtCore.SIGNAL("rightClicked()"), lambda: parent.send_screenshot(True))
+        self.smileyButton.clicked.connect(parent.send_smiley)
+        self.stickerButton.clicked.connect(parent.send_sticker)
+
+    def leaveEvent(self, event):
+        self.close()
+
+    def eventFilter(self, object, event):
+        if event.type() == QtCore.QEvent.WindowDeactivate:
+            self.close()
+        return False
+
+
+class StickerItem(QtGui.QWidget):
+
+    def __init__(self, fl):
+        super(StickerItem, self).__init__()
+        self._image_label = QtGui.QLabel(self)
+        self.path = fl
+        self.pixmap = QtGui.QPixmap()
+        self.pixmap.load(fl)
+        if self.pixmap.width() > 150:
+            self.pixmap = self.pixmap.scaled(150, 200, QtCore.Qt.KeepAspectRatio)
+        self.setFixedSize(150, self.pixmap.height())
+        self._image_label.setPixmap(self.pixmap)
+
+
+class StickerWindow(QtGui.QWidget):
+    """Sticker selection window"""
+
+    def __init__(self, parent):
+        super(StickerWindow, self).__init__()
+        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
+        self.setMaximumSize(250, 200)
+        self.setMinimumSize(250, 200)
+        self.list = QtGui.QListWidget(self)
+        self.list.setGeometry(QtCore.QRect(0, 0, 250, 200))
+        self.arr = smileys.sticker_loader()
+        for sticker in self.arr:
+            item = StickerItem(sticker)
+            elem = QtGui.QListWidgetItem()
+            elem.setSizeHint(QtCore.QSize(250, item.height()))
+            self.list.addItem(elem)
+            self.list.setItemWidget(elem, item)
+        self.list.setVerticalScrollMode(QtGui.QAbstractItemView.ScrollPerPixel)
+        self.list.setSpacing(3)
+        self.list.clicked.connect(self.click)
+        self.parent = parent
+
+    def click(self, index):
+        num = index.row()
+        self.parent.profile.send_sticker(self.arr[num])
+        self.close()
+
+    def leaveEvent(self, event):
+        self.close()
+
+
+class WelcomeScreen(CenteredWidget):
+
+    def __init__(self):
+        super().__init__()
+        self.setMaximumSize(250, 200)
+        self.setMinimumSize(250, 200)
+        self.center()
+        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
+        self.text = QtGui.QTextBrowser(self)
+        self.text.setGeometry(QtCore.QRect(0, 0, 250, 170))
+        self.text.setOpenExternalLinks(True)
+        self.checkbox = QtGui.QCheckBox(self)
+        self.checkbox.setGeometry(QtCore.QRect(5, 170, 240, 30))
+        self.checkbox.setText(QtGui.QApplication.translate('WelcomeScreen', "Don't show again",
+                                                           None, QtGui.QApplication.UnicodeUTF8))
+        self.setWindowTitle(QtGui.QApplication.translate('WelcomeScreen', 'Tip of the day',
+                                                         None, QtGui.QApplication.UnicodeUTF8))
+        import random
+        num = random.randint(0, 8)
+        if num == 0:
+            text = QtGui.QApplication.translate('WelcomeScreen', 'Press Esc if you want hide app to tray.',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+        elif num == 1:
+            text = QtGui.QApplication.translate('WelcomeScreen',
+                                                'Right click on screenshot button hides app to tray during screenshot.',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+        elif num == 2:
+            text = QtGui.QApplication.translate('WelcomeScreen',
+                                                'You can use Tox over Tor. For more info read <a href="https://wiki.tox.chat/users/tox_over_tor_tot">this post</a>',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+        elif num == 3:
+            text = QtGui.QApplication.translate('WelcomeScreen',
+                                                'Use Settings -> Interface to customize interface.',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+        elif num == 4:
+            text = QtGui.QApplication.translate('WelcomeScreen',
+                                                'Set profile password via Profile -> Settings. Password allows Toxygen encrypt your history and settings.',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+        elif num == 5:
+            text = QtGui.QApplication.translate('WelcomeScreen',
+                                                'Since v0.1.3 Toxygen supports plugins. <a href="https://github.com/xveduk/toxygen/blob/master/docs/plugins.md">Read more</a>',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+        elif num == 6:
+            text = QtGui.QApplication.translate('WelcomeScreen',
+                                                'New in Toxygen v0.2.2:<br>Users can lock application using profile password.<br>Compact contact list support<br>Bug fixes<br>Tox DNS improvements',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+        elif num == 7:
+            text = QtGui.QApplication.translate('WelcomeScreen',
+                                                'Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+        else:
+            text = QtGui.QApplication.translate('WelcomeScreen',
+                                                'Set new NoSpam to avoid spam friend requests: Profile -> Settings -> Set new NoSpam.',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+        self.text.setHtml(text)
+        self.checkbox.stateChanged.connect(self.not_show)
+        QtCore.QTimer.singleShot(1000, self.show)
+
+    def not_show(self):
+        import settings
+        s = settings.Settings.get_instance()
+        s['show_welcome_screen'] = False
+        s.save()
+
diff --git a/toxygen/menu.py b/toxygen/menu.py
new file mode 100644
index 0000000..8dc9621
--- /dev/null
+++ b/toxygen/menu.py
@@ -0,0 +1,949 @@
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+from settings import *
+from profile import Profile
+from util import curr_directory, copy
+from widgets import CenteredWidget, DataLabel, LineEdit
+import pyaudio
+import toxencryptsave
+import plugin_support
+
+
+class AddGroupchat(CenteredWidget):
+
+    def __init__(self):
+        super().__init__()
+        self.initUI()
+        self.retranslateUi()
+        self.center()
+
+    def initUI(self):
+        self.setObjectName('AddGC')
+        self.resize(570, 240)
+        self.setMaximumSize(QtCore.QSize(570, 240))
+        self.setMinimumSize(QtCore.QSize(570, 240))
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(50, 20, 470, 20))
+        self.createGCButton = QtGui.QPushButton(self)
+        self.createGCButton.setGeometry(QtCore.QRect(50, 190, 470, 30))
+        self.name = LineEdit(self)
+        self.name.setGeometry(QtCore.QRect(50, 40, 470, 27))
+        self.privacy_type = QtGui.QLabel(self)
+        self.privacy_type.setGeometry(QtCore.QRect(50, 70, 470, 20))
+        self.privacy_combobox = QtGui.QComboBox(self)
+        self.privacy_combobox.setGeometry(QtCore.QRect(50, 90, 470, 30))
+        self.pass_label = QtGui.QLabel(self)
+        self.pass_label.setGeometry(QtCore.QRect(50, 130, 470, 20))
+        self.password = LineEdit(self)
+        self.password.setGeometry(QtCore.QRect(50, 150, 470, 27))
+        self.password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
+
+        self.createGCButton.clicked.connect(self.button_click)
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def retranslateUi(self):
+        self.setWindowTitle(QtGui.QApplication.translate('AddGC', "Create group chat", None, QtGui.QApplication.UnicodeUTF8))
+        self.createGCButton.setText(QtGui.QApplication.translate("AddGC", "Create", None, QtGui.QApplication.UnicodeUTF8))
+        self.label.setText(QtGui.QApplication.translate('AddGC', "Name:", None, QtGui.QApplication.UnicodeUTF8))
+        self.privacy_type.setText(QtGui.QApplication.translate('AddGC', "Privacy type:", None, QtGui.QApplication.UnicodeUTF8))
+        self.privacy_combobox.addItem(QtGui.QApplication.translate('AddGC', "Public", None, QtGui.QApplication.UnicodeUTF8))
+        self.privacy_combobox.addItem(QtGui.QApplication.translate('AddGC', "Private", None, QtGui.QApplication.UnicodeUTF8))
+        self.name.setPlaceholderText(QtGui.QApplication.translate('AddGC', "Not empty group name", None, QtGui.QApplication.UnicodeUTF8))
+        self.password.setPlaceholderText(QtGui.QApplication.translate('AddGC', "Optional password", None, QtGui.QApplication.UnicodeUTF8))
+        self.pass_label.setText(QtGui.QApplication.translate('AddGC', "Password:", None, QtGui.QApplication.UnicodeUTF8))
+
+    def button_click(self):
+        if self.name.text():
+            Profile.get_instance().create_gc(self.name.text(),
+                                             self.privacy_combobox.currentIndex() == 0,
+                                             self.password.text())
+            self.close()
+
+
+class JoinGroupchat(CenteredWidget):
+
+    def __init__(self):
+        super().__init__()
+        self.initUI()
+        self.retranslateUi()
+        self.center()
+
+    def initUI(self):
+        self.setObjectName('AddGC')
+        self.resize(570, 150)
+        self.setMaximumSize(QtCore.QSize(570, 150))
+        self.setMinimumSize(QtCore.QSize(570, 150))
+        self.joinGCButton = QtGui.QPushButton(self)
+        self.joinGCButton.setGeometry(QtCore.QRect(50, 110, 470, 30))
+        self.id = LineEdit(self)
+        self.id.setGeometry(QtCore.QRect(50, 10, 470, 30))
+        self.password = LineEdit(self)
+        self.password.setGeometry(QtCore.QRect(50, 50, 470, 30))
+        self.password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
+
+        self.joinGCButton.clicked.connect(self.button_click)
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def retranslateUi(self):
+        self.setWindowTitle(
+            QtGui.QApplication.translate('JoinGC', "Join group chat", None, QtGui.QApplication.UnicodeUTF8))
+        self.joinGCButton.setText(
+            QtGui.QApplication.translate("JoinGC", "Join", None, QtGui.QApplication.UnicodeUTF8))
+        self.id.setPlaceholderText(
+            QtGui.QApplication.translate('JoinGC', "Group ID", None, QtGui.QApplication.UnicodeUTF8))
+        self.password.setPlaceholderText(
+            QtGui.QApplication.translate('JoinGC', "Optional password", None, QtGui.QApplication.UnicodeUTF8))
+
+    def button_click(self):
+        if self.id.text():
+            Profile.get_instance().join_gc(self.id.text().strip(), self.password.text())
+            self.close()
+
+
+class AddContact(CenteredWidget):
+    """Add contact form"""
+
+    def __init__(self, tox_id=''):
+        super(AddContact, self).__init__()
+        self.initUI(tox_id)
+        self._adding = False
+
+    def initUI(self, tox_id):
+        self.setObjectName('AddContact')
+        self.resize(568, 306)
+        self.sendRequestButton = QtGui.QPushButton(self)
+        self.sendRequestButton.setGeometry(QtCore.QRect(50, 270, 471, 31))
+        self.sendRequestButton.setMinimumSize(QtCore.QSize(0, 0))
+        self.sendRequestButton.setBaseSize(QtCore.QSize(0, 0))
+        self.sendRequestButton.setObjectName("sendRequestButton")
+        self.sendRequestButton.clicked.connect(self.add_friend)
+        self.tox_id = LineEdit(self)
+        self.tox_id.setGeometry(QtCore.QRect(50, 40, 471, 27))
+        self.tox_id.setObjectName("lineEdit")
+        self.tox_id.setText(tox_id)
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(50, 10, 80, 20))
+        self.error_label = DataLabel(self)
+        self.error_label.setGeometry(QtCore.QRect(120, 10, 420, 20))
+        font = QtGui.QFont()
+        font.setPointSize(10)
+        font.setWeight(30)
+        self.error_label.setFont(font)
+        self.error_label.setStyleSheet("QLabel { color: #BC1C1C; }")
+        self.label.setObjectName("label")
+        self.message_edit = QtGui.QTextEdit(self)
+        self.message_edit.setGeometry(QtCore.QRect(50, 110, 471, 151))
+        self.message_edit.setObjectName("textEdit")
+        self.message = QtGui.QLabel(self)
+        self.message.setGeometry(QtCore.QRect(50, 70, 101, 31))
+        self.message.setFont(font)
+        self.message.setObjectName("label_2")
+        self.retranslateUi()
+        self.message_edit.setText('Hello! Add me to your contact list please')
+        font = QtGui.QFont()
+        font.setPointSize(12)
+        font.setBold(True)
+        self.label.setFont(font)
+        self.message.setFont(font)
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def add_friend(self):
+        if self._adding:
+            return
+        self._adding = True
+        profile = Profile.get_instance()
+        send = profile.send_friend_request(self.tox_id.text().strip(), self.message_edit.toPlainText())
+        self._adding = False
+        if send is True:
+            # request was successful
+            self.close()
+        else:  # print error data
+            self.error_label.setText(send)
+
+    def retranslateUi(self):
+        self.setWindowTitle(QtGui.QApplication.translate('AddContact', "Add contact", None, QtGui.QApplication.UnicodeUTF8))
+        self.sendRequestButton.setText(QtGui.QApplication.translate("Form", "Send request", None, QtGui.QApplication.UnicodeUTF8))
+        self.label.setText(QtGui.QApplication.translate('AddContact', "TOX ID:", None, QtGui.QApplication.UnicodeUTF8))
+        self.message.setText(QtGui.QApplication.translate('AddContact', "Message:", None, QtGui.QApplication.UnicodeUTF8))
+        self.tox_id.setPlaceholderText(QtGui.QApplication.translate('AddContact', "TOX ID or public key of contact", None, QtGui.QApplication.UnicodeUTF8))
+
+
+class ProfileSettings(CenteredWidget):
+    """Form with profile settings such as name, status, TOX ID"""
+    def __init__(self):
+        super(ProfileSettings, self).__init__()
+        self.initUI()
+        self.center()
+
+    def initUI(self):
+        self.setObjectName("ProfileSettingsForm")
+        self.setMinimumSize(QtCore.QSize(700, 600))
+        self.setMaximumSize(QtCore.QSize(700, 600))
+        self.nick = LineEdit(self)
+        self.nick.setGeometry(QtCore.QRect(30, 60, 350, 27))
+        profile = Profile.get_instance()
+        self.nick.setText(profile.name)
+        self.status = QtGui.QComboBox(self)
+        self.status.setGeometry(QtCore.QRect(400, 60, 200, 27))
+        self.status_message = LineEdit(self)
+        self.status_message.setGeometry(QtCore.QRect(30, 130, 350, 27))
+        self.status_message.setText(profile.status_message)
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(40, 30, 91, 25))
+        font = QtGui.QFont()
+        font.setPointSize(18)
+        font.setWeight(75)
+        font.setBold(True)
+        self.label.setFont(font)
+        self.label_2 = QtGui.QLabel(self)
+        self.label_2.setGeometry(QtCore.QRect(40, 100, 100, 25))
+        self.label_2.setFont(font)
+        self.label_3 = QtGui.QLabel(self)
+        self.label_3.setGeometry(QtCore.QRect(40, 180, 100, 25))
+        self.label_3.setFont(font)
+        self.tox_id = QtGui.QLabel(self)
+        self.tox_id.setGeometry(QtCore.QRect(15, 210, 685, 21))
+        font.setPointSize(10)
+        self.tox_id.setFont(font)
+        s = profile.tox_id
+        self.tox_id.setText(s)
+        self.copyId = QtGui.QPushButton(self)
+        self.copyId.setGeometry(QtCore.QRect(40, 250, 180, 30))
+        self.copyId.clicked.connect(self.copy)
+        self.export = QtGui.QPushButton(self)
+        self.export.setGeometry(QtCore.QRect(230, 250, 180, 30))
+        self.export.clicked.connect(self.export_profile)
+        self.new_nospam = QtGui.QPushButton(self)
+        self.new_nospam.setGeometry(QtCore.QRect(420, 250, 180, 30))
+        self.new_nospam.clicked.connect(self.new_no_spam)
+        self.copy_pk = QtGui.QPushButton(self)
+        self.copy_pk.setGeometry(QtCore.QRect(40, 300, 180, 30))
+        self.copy_pk.clicked.connect(self.copy_public_key)
+        self.new_avatar = QtGui.QPushButton(self)
+        self.new_avatar.setGeometry(QtCore.QRect(230, 300, 180, 30))
+        self.delete_avatar = QtGui.QPushButton(self)
+        self.delete_avatar.setGeometry(QtCore.QRect(420, 300, 180, 30))
+        self.delete_avatar.clicked.connect(self.reset_avatar)
+        self.new_avatar.clicked.connect(self.set_avatar)
+        self.profilepass = QtGui.QLabel(self)
+        self.profilepass.setGeometry(QtCore.QRect(40, 340, 300, 30))
+        font.setPointSize(18)
+        self.profilepass.setFont(font)
+        self.password = LineEdit(self)
+        self.password.setGeometry(QtCore.QRect(40, 380, 300, 30))
+        self.password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
+        self.leave_blank = QtGui.QLabel(self)
+        self.leave_blank.setGeometry(QtCore.QRect(350, 380, 300, 30))
+        self.confirm_password = LineEdit(self)
+        self.confirm_password.setGeometry(QtCore.QRect(40, 420, 300, 30))
+        self.confirm_password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
+        self.set_password = QtGui.QPushButton(self)
+        self.set_password.setGeometry(QtCore.QRect(40, 470, 300, 30))
+        self.set_password.clicked.connect(self.new_password)
+        self.not_match = QtGui.QLabel(self)
+        self.not_match.setGeometry(QtCore.QRect(350, 420, 300, 30))
+        self.not_match.setVisible(False)
+        self.not_match.setStyleSheet('QLabel { color: #BC1C1C; }')
+        self.warning = QtGui.QLabel(self)
+        self.warning.setGeometry(QtCore.QRect(40, 510, 500, 30))
+        self.warning.setStyleSheet('QLabel { color: #BC1C1C; }')
+        self.default = QtGui.QPushButton(self)
+        self.default.setGeometry(QtCore.QRect(40, 550, 620, 30))
+        path, name = Settings.get_auto_profile()
+        self.auto = path + name == ProfileHelper.get_path() + Settings.get_instance().name
+        self.default.clicked.connect(self.auto_profile)
+        self.retranslateUi()
+        if profile.status is not None:
+            self.status.setCurrentIndex(profile.status)
+        else:
+            self.status.setVisible(False)
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def retranslateUi(self):
+        self.export.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Export profile", None, QtGui.QApplication.UnicodeUTF8))
+        self.setWindowTitle(QtGui.QApplication.translate("ProfileSettingsForm", "Profile settings", None, QtGui.QApplication.UnicodeUTF8))
+        self.label.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Name:", None, QtGui.QApplication.UnicodeUTF8))
+        self.label_2.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Status:", None, QtGui.QApplication.UnicodeUTF8))
+        self.label_3.setText(QtGui.QApplication.translate("ProfileSettingsForm", "TOX ID:", None, QtGui.QApplication.UnicodeUTF8))
+        self.copyId.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Copy TOX ID", None, QtGui.QApplication.UnicodeUTF8))
+        self.new_avatar.setText(QtGui.QApplication.translate("ProfileSettingsForm", "New avatar", None, QtGui.QApplication.UnicodeUTF8))
+        self.delete_avatar.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Reset avatar", None, QtGui.QApplication.UnicodeUTF8))
+        self.new_nospam.setText(QtGui.QApplication.translate("ProfileSettingsForm", "New NoSpam", None, QtGui.QApplication.UnicodeUTF8))
+        self.profilepass.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Profile password", None, QtGui.QApplication.UnicodeUTF8))
+        self.password.setPlaceholderText(QtGui.QApplication.translate("ProfileSettingsForm", "Password (at least 8 symbols)", None, QtGui.QApplication.UnicodeUTF8))
+        self.confirm_password.setPlaceholderText(QtGui.QApplication.translate("ProfileSettingsForm", "Confirm password", None, QtGui.QApplication.UnicodeUTF8))
+        self.set_password.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Set password", None, QtGui.QApplication.UnicodeUTF8))
+        self.not_match.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Passwords do not match", None, QtGui.QApplication.UnicodeUTF8))
+        self.leave_blank.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Leaving blank will reset current password", None, QtGui.QApplication.UnicodeUTF8))
+        self.warning.setText(QtGui.QApplication.translate("ProfileSettingsForm", "There is no way to recover lost passwords", None, QtGui.QApplication.UnicodeUTF8))
+        self.status.addItem(QtGui.QApplication.translate("ProfileSettingsForm", "Online", None, QtGui.QApplication.UnicodeUTF8))
+        self.status.addItem(QtGui.QApplication.translate("ProfileSettingsForm", "Away", None, QtGui.QApplication.UnicodeUTF8))
+        self.status.addItem(QtGui.QApplication.translate("ProfileSettingsForm", "Busy", None, QtGui.QApplication.UnicodeUTF8))
+        self.copy_pk.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Copy public key", None, QtGui.QApplication.UnicodeUTF8))
+        if self.auto:
+            self.default.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Mark as not default profile", None, QtGui.QApplication.UnicodeUTF8))
+        else:
+            self.default.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Mark as default profile", None, QtGui.QApplication.UnicodeUTF8))
+
+    def auto_profile(self):
+        if self.auto:
+            Settings.reset_auto_profile()
+        else:
+            Settings.set_auto_profile(ProfileHelper.get_path(), Settings.get_instance().name)
+        self.auto = not self.auto
+        if self.auto:
+            self.default.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Mark as not default profile", None,
+                                                              QtGui.QApplication.UnicodeUTF8))
+        else:
+            self.default.setText(
+                QtGui.QApplication.translate("ProfileSettingsForm", "Mark as default profile", None,
+                                             QtGui.QApplication.UnicodeUTF8))
+
+    def new_password(self):
+        if self.password.text() == self.confirm_password.text():
+            if not len(self.password.text()) or len(self.password.text()) >= 8:
+                e = toxencryptsave.ToxEncryptSave.get_instance()
+                e.set_password(self.password.text())
+                self.close()
+            else:
+                self.not_match.setText(
+                    QtGui.QApplication.translate("ProfileSettingsForm", "Password must be at least 8 symbols", None,
+                                                 QtGui.QApplication.UnicodeUTF8))
+            self.not_match.setVisible(True)
+        else:
+            self.not_match.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Passwords do not match", None,
+                                                                QtGui.QApplication.UnicodeUTF8))
+            self.not_match.setVisible(True)
+
+    def copy(self):
+        clipboard = QtGui.QApplication.clipboard()
+        profile = Profile.get_instance()
+        clipboard.setText(profile.tox_id)
+        pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png')
+        icon = QtGui.QIcon(pixmap)
+        self.copyId.setIcon(icon)
+        self.copyId.setIconSize(QtCore.QSize(10, 10))
+
+    def copy_public_key(self):
+        clipboard = QtGui.QApplication.clipboard()
+        profile = Profile.get_instance()
+        clipboard.setText(profile.tox_id[:64])
+        pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png')
+        icon = QtGui.QIcon(pixmap)
+        self.copy_pk.setIcon(icon)
+        self.copy_pk.setIconSize(QtCore.QSize(10, 10))
+
+    def new_no_spam(self):
+        self.tox_id.setText(Profile.get_instance().new_nospam())
+
+    def reset_avatar(self):
+        Profile.get_instance().reset_avatar()
+
+    def set_avatar(self):
+        choose = QtGui.QApplication.translate("ProfileSettingsForm", "Choose avatar", None, QtGui.QApplication.UnicodeUTF8)
+        name = QtGui.QFileDialog.getOpenFileName(self, choose, None, 'Images (*.png)',
+                                                 options=QtGui.QFileDialog.DontUseNativeDialog)
+        if name[0]:
+            bitmap = QtGui.QPixmap(name[0])
+            bitmap.scaled(QtCore.QSize(128, 128), aspectMode=QtCore.Qt.KeepAspectRatio,
+                          mode=QtCore.Qt.SmoothTransformation)
+
+            byte_array = QtCore.QByteArray()
+            buffer = QtCore.QBuffer(byte_array)
+            buffer.open(QtCore.QIODevice.WriteOnly)
+            bitmap.save(buffer, 'PNG')
+            Profile.get_instance().set_avatar(str(byte_array.data()))
+
+    def export_profile(self):
+        directory = QtGui.QFileDialog.getExistingDirectory(options=QtGui.QFileDialog.DontUseNativeDialog) + '/'
+        if directory != '/':
+            ProfileHelper.get_instance().export_profile(directory)
+            settings = Settings.get_instance()
+            settings.export(directory)
+            profile = Profile.get_instance()
+            profile.export_history(directory)
+
+    def closeEvent(self, event):
+        profile = Profile.get_instance()
+        profile.set_name(self.nick.text())
+        profile.set_status_message(self.status_message.text().encode('utf-8'))
+        profile.set_status(self.status.currentIndex())
+
+
+class NetworkSettings(CenteredWidget):
+    """Network settings form: UDP, Ipv6 and proxy"""
+    def __init__(self, reset):
+        super(NetworkSettings, self).__init__()
+        self.reset = reset
+        self.initUI()
+        self.center()
+
+    def initUI(self):
+        self.setObjectName("NetworkSettings")
+        self.resize(300, 330)
+        self.setMinimumSize(QtCore.QSize(300, 330))
+        self.setMaximumSize(QtCore.QSize(300, 330))
+        self.setBaseSize(QtCore.QSize(300, 330))
+        self.ipv = QtGui.QCheckBox(self)
+        self.ipv.setGeometry(QtCore.QRect(20, 10, 97, 22))
+        self.ipv.setObjectName("ipv")
+        self.udp = QtGui.QCheckBox(self)
+        self.udp.setGeometry(QtCore.QRect(150, 10, 97, 22))
+        self.udp.setObjectName("udp")
+        self.proxy = QtGui.QCheckBox(self)
+        self.proxy.setGeometry(QtCore.QRect(20, 40, 97, 22))
+        self.http = QtGui.QCheckBox(self)
+        self.http.setGeometry(QtCore.QRect(20, 70, 97, 22))
+        self.proxy.setObjectName("proxy")
+        self.proxyip = LineEdit(self)
+        self.proxyip.setGeometry(QtCore.QRect(40, 130, 231, 27))
+        self.proxyip.setObjectName("proxyip")
+        self.proxyport = LineEdit(self)
+        self.proxyport.setGeometry(QtCore.QRect(40, 190, 231, 27))
+        self.proxyport.setObjectName("proxyport")
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(40, 100, 66, 17))
+        self.label_2 = QtGui.QLabel(self)
+        self.label_2.setGeometry(QtCore.QRect(40, 165, 66, 17))
+        self.reconnect = QtGui.QPushButton(self)
+        self.reconnect.setGeometry(QtCore.QRect(40, 230, 231, 30))
+        self.reconnect.clicked.connect(self.restart_core)
+        settings = Settings.get_instance()
+        self.ipv.setChecked(settings['ipv6_enabled'])
+        self.udp.setChecked(settings['udp_enabled'])
+        self.proxy.setChecked(settings['proxy_type'])
+        self.proxyip.setText(settings['proxy_host'])
+        self.proxyport.setText(str(settings['proxy_port']))
+        self.http.setChecked(settings['proxy_type'] == 1)
+        self.warning = QtGui.QLabel(self)
+        self.warning.setGeometry(QtCore.QRect(5, 270, 290, 60))
+        self.warning.setStyleSheet('QLabel { color: #BC1C1C; }')
+        self.retranslateUi()
+        self.proxy.stateChanged.connect(lambda x: self.activate())
+        self.activate()
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def retranslateUi(self):
+        self.setWindowTitle(QtGui.QApplication.translate("NetworkSettings", "Network settings", None, QtGui.QApplication.UnicodeUTF8))
+        self.ipv.setText(QtGui.QApplication.translate("Form", "IPv6", None, QtGui.QApplication.UnicodeUTF8))
+        self.udp.setText(QtGui.QApplication.translate("Form", "UDP", None, QtGui.QApplication.UnicodeUTF8))
+        self.proxy.setText(QtGui.QApplication.translate("Form", "Proxy", None, QtGui.QApplication.UnicodeUTF8))
+        self.label.setText(QtGui.QApplication.translate("Form", "IP:", None, QtGui.QApplication.UnicodeUTF8))
+        self.label_2.setText(QtGui.QApplication.translate("Form", "Port:", None, QtGui.QApplication.UnicodeUTF8))
+        self.reconnect.setText(QtGui.QApplication.translate("NetworkSettings", "Restart TOX core", None, QtGui.QApplication.UnicodeUTF8))
+        self.http.setText(QtGui.QApplication.translate("Form", "HTTP", None, QtGui.QApplication.UnicodeUTF8))
+        self.warning.setText(QtGui.QApplication.translate("Form", "WARNING:\nusing proxy with enabled UDP\ncan produce IP leak",
+                                                          None, QtGui.QApplication.UnicodeUTF8))
+
+    def activate(self):
+        bl = self.proxy.isChecked()
+        self.proxyip.setEnabled(bl)
+        self.http.setEnabled(bl)
+        self.proxyport.setEnabled(bl)
+
+    def restart_core(self):
+        try:
+            settings = Settings.get_instance()
+            settings['ipv6_enabled'] = self.ipv.isChecked()
+            settings['udp_enabled'] = self.udp.isChecked()
+            settings['proxy_type'] = 2 - int(self.http.isChecked()) if self.proxy.isChecked() else 0
+            settings['proxy_host'] = str(self.proxyip.text())
+            settings['proxy_port'] = int(self.proxyport.text())
+            settings.save()
+            # recreate tox instance
+            Profile.get_instance().reset(self.reset)
+            self.close()
+        except Exception as ex:
+            log('Exception in restart: ' + str(ex))
+
+
+class PrivacySettings(CenteredWidget):
+    """Privacy settings form: history, typing notifications"""
+
+    def __init__(self):
+        super(PrivacySettings, self).__init__()
+        self.initUI()
+        self.center()
+
+    def initUI(self):
+        self.setObjectName("privacySettings")
+        self.resize(370, 600)
+        self.setMinimumSize(QtCore.QSize(370, 600))
+        self.setMaximumSize(QtCore.QSize(370, 600))
+        self.saveHistory = QtGui.QCheckBox(self)
+        self.saveHistory.setGeometry(QtCore.QRect(10, 20, 350, 22))
+        self.saveUnsentOnly = QtGui.QCheckBox(self)
+        self.saveUnsentOnly.setGeometry(QtCore.QRect(10, 60, 350, 22))
+
+        self.fileautoaccept = QtGui.QCheckBox(self)
+        self.fileautoaccept.setGeometry(QtCore.QRect(10, 100, 350, 22))
+
+        self.typingNotifications = QtGui.QCheckBox(self)
+        self.typingNotifications.setGeometry(QtCore.QRect(10, 140, 350, 30))
+        self.inlines = QtGui.QCheckBox(self)
+        self.inlines.setGeometry(QtCore.QRect(10, 180, 350, 30))
+        self.auto_path = QtGui.QLabel(self)
+        self.auto_path.setGeometry(QtCore.QRect(10, 230, 350, 30))
+        self.path = QtGui.QPlainTextEdit(self)
+        self.path.setGeometry(QtCore.QRect(10, 265, 350, 45))
+        self.change_path = QtGui.QPushButton(self)
+        self.change_path.setGeometry(QtCore.QRect(10, 320, 350, 30))
+        settings = Settings.get_instance()
+        self.typingNotifications.setChecked(settings['typing_notifications'])
+        self.fileautoaccept.setChecked(settings['allow_auto_accept'])
+        self.saveHistory.setChecked(settings['save_history'])
+        self.inlines.setChecked(settings['allow_inline'])
+        self.saveUnsentOnly.setChecked(settings['save_unsent_only'])
+        self.saveUnsentOnly.setEnabled(settings['save_history'])
+        self.saveHistory.stateChanged.connect(self.update)
+        self.path.setPlainText(settings['auto_accept_path'] or curr_directory())
+        self.change_path.clicked.connect(self.new_path)
+        self.block_user_label = QtGui.QLabel(self)
+        self.block_user_label.setGeometry(QtCore.QRect(10, 360, 350, 30))
+        self.block_id = QtGui.QPlainTextEdit(self)
+        self.block_id.setGeometry(QtCore.QRect(10, 390, 350, 30))
+        self.block = QtGui.QPushButton(self)
+        self.block.setGeometry(QtCore.QRect(10, 430, 350, 30))
+        self.block.clicked.connect(lambda: Profile.get_instance().block_user(self.block_id.toPlainText()) or self.close())
+        self.blocked_users_label = QtGui.QLabel(self)
+        self.blocked_users_label.setGeometry(QtCore.QRect(10, 470, 350, 30))
+        self.comboBox = QtGui.QComboBox(self)
+        self.comboBox.setGeometry(QtCore.QRect(10, 500, 350, 30))
+        self.comboBox.addItems(settings['blocked'])
+        self.unblock = QtGui.QPushButton(self)
+        self.unblock.setGeometry(QtCore.QRect(10, 540, 350, 30))
+        self.unblock.clicked.connect(lambda: self.unblock_user())
+        self.retranslateUi()
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def retranslateUi(self):
+        self.setWindowTitle(QtGui.QApplication.translate("privacySettings", "Privacy settings", None, QtGui.QApplication.UnicodeUTF8))
+        self.saveHistory.setText(QtGui.QApplication.translate("privacySettings", "Save chat history", None, QtGui.QApplication.UnicodeUTF8))
+        self.fileautoaccept.setText(QtGui.QApplication.translate("privacySettings", "Allow file auto accept", None, QtGui.QApplication.UnicodeUTF8))
+        self.typingNotifications.setText(QtGui.QApplication.translate("privacySettings", "Send typing notifications", None, QtGui.QApplication.UnicodeUTF8))
+        self.auto_path.setText(QtGui.QApplication.translate("privacySettings", "Auto accept default path:", None, QtGui.QApplication.UnicodeUTF8))
+        self.change_path.setText(QtGui.QApplication.translate("privacySettings", "Change", None, QtGui.QApplication.UnicodeUTF8))
+        self.inlines.setText(QtGui.QApplication.translate("privacySettings", "Allow inlines", None, QtGui.QApplication.UnicodeUTF8))
+        self.block_user_label.setText(QtGui.QApplication.translate("privacySettings", "Block by public key:", None, QtGui.QApplication.UnicodeUTF8))
+        self.blocked_users_label.setText(QtGui.QApplication.translate("privacySettings", "Blocked users:", None, QtGui.QApplication.UnicodeUTF8))
+        self.unblock.setText(QtGui.QApplication.translate("privacySettings", "Unblock", None, QtGui.QApplication.UnicodeUTF8))
+        self.block.setText(QtGui.QApplication.translate("privacySettings", "Block user", None, QtGui.QApplication.UnicodeUTF8))
+        self.saveUnsentOnly.setText(QtGui.QApplication.translate("privacySettings", "Save unsent messages only", None, QtGui.QApplication.UnicodeUTF8))
+
+    def update(self, new_state):
+        self.saveUnsentOnly.setEnabled(new_state)
+        if not new_state:
+            self.saveUnsentOnly.setChecked(False)
+
+    def unblock_user(self):
+        if not self.comboBox.count():
+            return
+        title = QtGui.QApplication.translate("privacySettings", "Add to friend list", None, QtGui.QApplication.UnicodeUTF8)
+        info = QtGui.QApplication.translate("privacySettings", "Do you want to add this user to friend list?", None, QtGui.QApplication.UnicodeUTF8)
+        reply = QtGui.QMessageBox.question(None, title, info, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
+        Profile.get_instance().unblock_user(self.comboBox.currentText(), reply == QtGui.QMessageBox.Yes)
+        self.close()
+
+    def closeEvent(self, event):
+        settings = Settings.get_instance()
+        settings['typing_notifications'] = self.typingNotifications.isChecked()
+        settings['allow_auto_accept'] = self.fileautoaccept.isChecked()
+
+        if settings['save_history'] and not self.saveHistory.isChecked():  # clear history
+            reply = QtGui.QMessageBox.question(None,
+                                               QtGui.QApplication.translate("privacySettings",
+                                                                            'Chat history',
+                                                                            None, QtGui.QApplication.UnicodeUTF8),
+                                               QtGui.QApplication.translate("privacySettings",
+                                                                            'History will be cleaned! Continue?',
+                                                                            None, QtGui.QApplication.UnicodeUTF8),
+                                               QtGui.QMessageBox.Yes,
+                                               QtGui.QMessageBox.No)
+            if reply == QtGui.QMessageBox.Yes:
+                Profile.get_instance().clear_history()
+                settings['save_history'] = self.saveHistory.isChecked()
+        else:
+            settings['save_history'] = self.saveHistory.isChecked()
+        if self.saveUnsentOnly.isChecked() and not settings['save_unsent_only']:
+            reply = QtGui.QMessageBox.question(None,
+                                               QtGui.QApplication.translate("privacySettings",
+                                                                            'Chat history',
+                                                                            None, QtGui.QApplication.UnicodeUTF8),
+                                               QtGui.QApplication.translate("privacySettings",
+                                                                            'History will be cleaned! Continue?',
+                                                                            None, QtGui.QApplication.UnicodeUTF8),
+                                               QtGui.QMessageBox.Yes,
+                                               QtGui.QMessageBox.No)
+            if reply == QtGui.QMessageBox.Yes:
+                Profile.get_instance().clear_history(None, True)
+                settings['save_unsent_only'] = self.saveUnsentOnly.isChecked()
+        else:
+            settings['save_unsent_only'] = self.saveUnsentOnly.isChecked()
+        settings['auto_accept_path'] = self.path.toPlainText()
+        settings['allow_inline'] = self.inlines.isChecked()
+        settings.save()
+
+    def new_path(self):
+        directory = QtGui.QFileDialog.getExistingDirectory(options=QtGui.QFileDialog.DontUseNativeDialog) + '/'
+        if directory != '/':
+            self.path.setPlainText(directory)
+
+
+class NotificationsSettings(CenteredWidget):
+    """Notifications settings form"""
+
+    def __init__(self):
+        super(NotificationsSettings, self).__init__()
+        self.initUI()
+        self.center()
+
+    def initUI(self):
+        self.setObjectName("notificationsForm")
+        self.resize(350, 200)
+        self.setMinimumSize(QtCore.QSize(350, 200))
+        self.setMaximumSize(QtCore.QSize(350, 200))
+        self.enableNotifications = QtGui.QCheckBox(self)
+        self.enableNotifications.setGeometry(QtCore.QRect(10, 20, 340, 18))
+        self.callsSound = QtGui.QCheckBox(self)
+        self.callsSound.setGeometry(QtCore.QRect(10, 120, 340, 18))
+        self.soundNotifications = QtGui.QCheckBox(self)
+        self.soundNotifications.setGeometry(QtCore.QRect(10, 70, 340, 18))
+        self.gcNotifications = QtGui.QCheckBox(self)
+        self.gcNotifications.setGeometry(QtCore.QRect(10, 170, 340, 18))
+        font = QtGui.QFont()
+        font.setPointSize(12)
+        self.callsSound.setFont(font)
+        self.soundNotifications.setFont(font)
+        self.enableNotifications.setFont(font)
+        self.gcNotifications.setFont(font)
+        s = Settings.get_instance()
+        self.enableNotifications.setChecked(s['notifications'])
+        self.soundNotifications.setChecked(s['sound_notifications'])
+        self.callsSound.setChecked(s['calls_sound'])
+        self.gcNotifications.setChecked(s['notify_all_gc'])
+        self.retranslateUi()
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def retranslateUi(self):
+        self.gcNotifications.setText(QtGui.QApplication.translate("notificationsForm", "Enable group chat notifications", None, QtGui.QApplication.UnicodeUTF8))
+        self.setWindowTitle(QtGui.QApplication.translate("notificationsForm", "Notification settings", None, QtGui.QApplication.UnicodeUTF8))
+        self.enableNotifications.setText(QtGui.QApplication.translate("notificationsForm", "Enable notifications", None, QtGui.QApplication.UnicodeUTF8))
+        self.callsSound.setText(QtGui.QApplication.translate("notificationsForm", "Enable call\'s sound", None, QtGui.QApplication.UnicodeUTF8))
+        self.soundNotifications.setText(QtGui.QApplication.translate("notificationsForm", "Enable sound notifications", None, QtGui.QApplication.UnicodeUTF8))
+
+    def closeEvent(self, *args, **kwargs):
+        settings = Settings.get_instance()
+        settings['notifications'] = self.enableNotifications.isChecked()
+        settings['sound_notifications'] = self.soundNotifications.isChecked()
+        settings['calls_sound'] = self.callsSound.isChecked()
+        settings['notify_all_gc'] = self.gcNotifications.isChecked()
+        settings.save()
+
+
+class InterfaceSettings(CenteredWidget):
+    """Interface settings form"""
+    def __init__(self):
+        super(InterfaceSettings, self).__init__()
+        self.initUI()
+        self.center()
+
+    def initUI(self):
+        self.setObjectName("interfaceForm")
+        self.setMinimumSize(QtCore.QSize(400, 550))
+        self.setMaximumSize(QtCore.QSize(400, 550))
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(30, 10, 370, 20))
+        font = QtGui.QFont()
+        font.setPointSize(14)
+        font.setBold(True)
+        self.label.setFont(font)
+        self.themeSelect = QtGui.QComboBox(self)
+        self.themeSelect.setGeometry(QtCore.QRect(30, 40, 120, 30))
+        list_of_themes = ['dark']
+        self.themeSelect.addItems(list_of_themes)
+        settings = Settings.get_instance()
+        theme = settings['theme']
+        if theme in list_of_themes:
+            index = list_of_themes.index(theme)
+        else:
+            index = 0
+        self.themeSelect.setCurrentIndex(index)
+        self.lang_choose = QtGui.QComboBox(self)
+        self.lang_choose.setGeometry(QtCore.QRect(30, 110, 120, 30))
+        supported = sorted(Settings.supported_languages().keys(), reverse=True)
+        for key in supported:
+            self.lang_choose.insertItem(0, key)
+            if settings['language'] == key:
+                self.lang_choose.setCurrentIndex(0)
+        self.lang = QtGui.QLabel(self)
+        self.lang.setGeometry(QtCore.QRect(30, 80, 370, 20))
+        self.lang.setFont(font)
+        self.mirror_mode = QtGui.QCheckBox(self)
+        self.mirror_mode.setGeometry(QtCore.QRect(30, 160, 370, 20))
+        self.mirror_mode.setChecked(settings['mirror_mode'])
+        self.smileys = QtGui.QCheckBox(self)
+        self.smileys.setGeometry(QtCore.QRect(30, 190, 120, 20))
+        self.smileys.setChecked(settings['smileys'])
+        self.smiley_pack_label = QtGui.QLabel(self)
+        self.smiley_pack_label.setGeometry(QtCore.QRect(30, 230, 370, 20))
+        self.smiley_pack_label.setFont(font)
+        self.smiley_pack = QtGui.QComboBox(self)
+        self.smiley_pack.setGeometry(QtCore.QRect(30, 260, 160, 30))
+        sm = smileys.SmileyLoader.get_instance()
+        self.smiley_pack.addItems(sm.get_packs_list())
+        try:
+            ind = sm.get_packs_list().index(settings['smiley_pack'])
+        except:
+            ind = sm.get_packs_list().index('default')
+        self.smiley_pack.setCurrentIndex(ind)
+        self.messages_font_size_label = QtGui.QLabel(self)
+        self.messages_font_size_label.setGeometry(QtCore.QRect(30, 300, 370, 20))
+        self.messages_font_size_label.setFont(font)
+        self.messages_font_size = QtGui.QComboBox(self)
+        self.messages_font_size.setGeometry(QtCore.QRect(30, 330, 160, 30))
+        self.messages_font_size.addItems([str(x) for x in range(10, 19)])
+        self.messages_font_size.setCurrentIndex(settings['message_font_size'] - 10)
+
+        self.unread = QtGui.QPushButton(self)
+        self.unread.setGeometry(QtCore.QRect(30, 425, 340, 30))
+        self.unread.clicked.connect(self.select_color)
+
+        self.compact_mode = QtGui.QCheckBox(self)
+        self.compact_mode.setGeometry(QtCore.QRect(30, 380, 370, 20))
+        self.compact_mode.setChecked(settings['compact_mode'])
+
+        self.import_smileys = QtGui.QPushButton(self)
+        self.import_smileys.setGeometry(QtCore.QRect(30, 465, 340, 30))
+        self.import_smileys.clicked.connect(self.import_sm)
+
+        self.import_stickers = QtGui.QPushButton(self)
+        self.import_stickers.setGeometry(QtCore.QRect(30, 505, 340, 30))
+        self.import_stickers.clicked.connect(self.import_st)
+
+        self.retranslateUi()
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def retranslateUi(self):
+        self.setWindowTitle(QtGui.QApplication.translate("interfaceForm", "Interface settings", None, QtGui.QApplication.UnicodeUTF8))
+        self.label.setText(QtGui.QApplication.translate("interfaceForm", "Theme:", None, QtGui.QApplication.UnicodeUTF8))
+        self.lang.setText(QtGui.QApplication.translate("interfaceForm", "Language:", None, QtGui.QApplication.UnicodeUTF8))
+        self.smileys.setText(QtGui.QApplication.translate("interfaceForm", "Smileys", None, QtGui.QApplication.UnicodeUTF8))
+        self.smiley_pack_label.setText(QtGui.QApplication.translate("interfaceForm", "Smiley pack:", None, QtGui.QApplication.UnicodeUTF8))
+        self.mirror_mode.setText(QtGui.QApplication.translate("interfaceForm", "Mirror mode", None, QtGui.QApplication.UnicodeUTF8))
+        self.messages_font_size_label.setText(QtGui.QApplication.translate("interfaceForm", "Messages font size:", None, QtGui.QApplication.UnicodeUTF8))
+        self.unread.setText(QtGui.QApplication.translate("interfaceForm", "Select unread messages notification color", None, QtGui.QApplication.UnicodeUTF8))
+        self.compact_mode.setText(QtGui.QApplication.translate("interfaceForm", "Compact contact list", None, QtGui.QApplication.UnicodeUTF8))
+        self.import_smileys.setText(QtGui.QApplication.translate("interfaceForm", "Import smiley pack", None, QtGui.QApplication.UnicodeUTF8))
+        self.import_stickers.setText(QtGui.QApplication.translate("interfaceForm", "Import sticker pack", None, QtGui.QApplication.UnicodeUTF8))
+
+    def import_st(self):
+        directory = QtGui.QFileDialog.getExistingDirectory(self,
+                                                           QtGui.QApplication.translate("MainWindow",
+                                                                                        'Choose folder with sticker pack',
+                                                                                        None,
+                                                                                        QtGui.QApplication.UnicodeUTF8),
+                                                           curr_directory(),
+                                                           QtGui.QFileDialog.ShowDirsOnly | QtGui.QFileDialog.DontUseNativeDialog)
+
+        if directory:
+            src = directory + '/'
+            dest = curr_directory() + '/stickers/' + os.path.basename(directory) + '/'
+            copy(src, dest)
+
+    def import_sm(self):
+        directory = QtGui.QFileDialog.getExistingDirectory(self,
+                                                           QtGui.QApplication.translate("MainWindow",
+                                                                                        'Choose folder with smiley pack',
+                                                                                        None,
+                                                                                        QtGui.QApplication.UnicodeUTF8),
+                                                           curr_directory(),
+                                                           QtGui.QFileDialog.ShowDirsOnly | QtGui.QFileDialog.DontUseNativeDialog)
+
+        if directory:
+            src = directory + '/'
+            dest = curr_directory() + '/smileys/' + os.path.basename(directory) + '/'
+            copy(src, dest)
+
+    def select_color(self):
+        col = QtGui.QColorDialog.getColor()
+
+        if col.isValid():
+            settings = Settings.get_instance()
+            name = col.name()
+            settings['unread_color'] = name
+            settings.save()
+
+    def closeEvent(self, event):
+        settings = Settings.get_instance()
+        settings['theme'] = str(self.themeSelect.currentText())
+        settings['smileys'] = self.smileys.isChecked()
+        restart = False
+        if settings['mirror_mode'] != self.mirror_mode.isChecked():
+            settings['mirror_mode'] = self.mirror_mode.isChecked()
+            restart = True
+        if settings['compact_mode'] != self.compact_mode.isChecked():
+            settings['compact_mode'] = self.compact_mode.isChecked()
+            restart = True
+        settings['smiley_pack'] = self.smiley_pack.currentText()
+        smileys.SmileyLoader.get_instance().load_pack()
+        language = self.lang_choose.currentText()
+        if settings['language'] != language:
+            settings['language'] = language
+            text = self.lang_choose.currentText()
+            path = Settings.supported_languages()[text]
+            app = QtGui.QApplication.instance()
+            app.removeTranslator(app.translator)
+            app.translator.load(curr_directory() + '/translations/' + path)
+            app.installTranslator(app.translator)
+        settings['message_font_size'] = self.messages_font_size.currentIndex() + 10
+        Profile.get_instance().update()
+        settings.save()
+        if restart:
+            msgBox = QtGui.QMessageBox()
+            text = QtGui.QApplication.translate("interfaceForm", 'Restart app to apply settings', None,
+                                                QtGui.QApplication.UnicodeUTF8)
+            msgBox.setWindowTitle(QtGui.QApplication.translate("interfaceForm", 'Restart required', None,
+                                                               QtGui.QApplication.UnicodeUTF8))
+            msgBox.setText(text)
+            msgBox.exec_()
+
+
+class AudioSettings(CenteredWidget):
+    """
+    Audio calls settings form
+    """
+
+    def __init__(self):
+        super(AudioSettings, self).__init__()
+        self.initUI()
+        self.retranslateUi()
+        self.center()
+
+    def initUI(self):
+        self.setObjectName("audioSettingsForm")
+        self.resize(400, 150)
+        self.setMinimumSize(QtCore.QSize(400, 150))
+        self.setMaximumSize(QtCore.QSize(400, 150))
+        self.in_label = QtGui.QLabel(self)
+        self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20))
+        self.out_label = QtGui.QLabel(self)
+        self.out_label.setGeometry(QtCore.QRect(25, 65, 350, 20))
+        font = QtGui.QFont()
+        font.setPointSize(16)
+        font.setBold(True)
+        self.in_label.setFont(font)
+        self.out_label.setFont(font)
+        self.input = QtGui.QComboBox(self)
+        self.input.setGeometry(QtCore.QRect(25, 30, 350, 30))
+        self.output = QtGui.QComboBox(self)
+        self.output.setGeometry(QtCore.QRect(25, 90, 350, 30))
+        p = pyaudio.PyAudio()
+        settings = Settings.get_instance()
+        self.in_indexes, self.out_indexes = [], []
+        for i in range(p.get_device_count()):
+            device = p.get_device_info_by_index(i)
+            if device["maxInputChannels"]:
+                self.input.addItem(str(device["name"]))
+                self.in_indexes.append(i)
+            if device["maxOutputChannels"]:
+                self.output.addItem(str(device["name"]))
+                self.out_indexes.append(i)
+        self.input.setCurrentIndex(self.in_indexes.index(settings.audio['input']))
+        self.output.setCurrentIndex(self.out_indexes.index(settings.audio['output']))
+        QtCore.QMetaObject.connectSlotsByName(self)
+
+    def retranslateUi(self):
+        self.setWindowTitle(QtGui.QApplication.translate("audioSettingsForm", "Audio settings", None, QtGui.QApplication.UnicodeUTF8))
+        self.in_label.setText(QtGui.QApplication.translate("audioSettingsForm", "Input device:", None, QtGui.QApplication.UnicodeUTF8))
+        self.out_label.setText(QtGui.QApplication.translate("audioSettingsForm", "Output device:", None, QtGui.QApplication.UnicodeUTF8))
+
+    def closeEvent(self, event):
+        settings = Settings.get_instance()
+        settings.audio['input'] = self.in_indexes[self.input.currentIndex()]
+        settings.audio['output'] = self.out_indexes[self.output.currentIndex()]
+        settings.save()
+
+
+class PluginsSettings(CenteredWidget):
+    """
+    Plugins settings form
+    """
+
+    def __init__(self):
+        super(PluginsSettings, self).__init__()
+        self.initUI()
+        self.center()
+        self.retranslateUi()
+
+    def initUI(self):
+        self.resize(400, 210)
+        self.setMinimumSize(QtCore.QSize(400, 210))
+        self.setMaximumSize(QtCore.QSize(400, 210))
+        self.comboBox = QtGui.QComboBox(self)
+        self.comboBox.setGeometry(QtCore.QRect(30, 10, 340, 30))
+        self.label = QtGui.QLabel(self)
+        self.label.setGeometry(QtCore.QRect(30, 40, 340, 90))
+        self.label.setWordWrap(True)
+        self.button = QtGui.QPushButton(self)
+        self.button.setGeometry(QtCore.QRect(30, 130, 340, 30))
+        self.button.clicked.connect(self.button_click)
+        self.open = QtGui.QPushButton(self)
+        self.open.setGeometry(QtCore.QRect(30, 170, 340, 30))
+        self.open.clicked.connect(self.open_plugin)
+        self.pl_loader = plugin_support.PluginLoader.get_instance()
+        self.update_list()
+        self.comboBox.currentIndexChanged.connect(self.show_data)
+        self.show_data()
+
+    def retranslateUi(self):
+        self.setWindowTitle(QtGui.QApplication.translate('PluginsForm', "Plugins", None, QtGui.QApplication.UnicodeUTF8))
+        self.open.setText(QtGui.QApplication.translate('PluginsForm', "Open selected plugin", None, QtGui.QApplication.UnicodeUTF8))
+
+    def open_plugin(self):
+        ind = self.comboBox.currentIndex()
+        plugin = self.data[ind]
+        window = self.pl_loader.plugin_window(plugin[-1])
+        if window is not None:
+            self.window = window
+            self.window.show()
+        else:
+            msgBox = QtGui.QMessageBox()
+            text = QtGui.QApplication.translate("PluginsForm", 'No GUI found for this plugin', None,
+                                                QtGui.QApplication.UnicodeUTF8)
+            msgBox.setWindowTitle(QtGui.QApplication.translate("PluginsForm", 'Error', None,
+                                                               QtGui.QApplication.UnicodeUTF8))
+            msgBox.setText(text)
+            msgBox.exec_()
+
+    def update_list(self):
+        self.comboBox.clear()
+        data = self.pl_loader.get_plugins_list()
+        self.comboBox.addItems(list(map(lambda x: x[0], data)))
+        self.data = data
+
+    def show_data(self):
+        ind = self.comboBox.currentIndex()
+        if len(self.data):
+            plugin = self.data[ind]
+            descr = plugin[2] or QtGui.QApplication.translate("PluginsForm", "No description available", None, QtGui.QApplication.UnicodeUTF8)
+            self.label.setText(descr)
+            if plugin[1]:
+                self.button.setText(QtGui.QApplication.translate("PluginsForm", "Disable plugin", None, QtGui.QApplication.UnicodeUTF8))
+            else:
+                self.button.setText(QtGui.QApplication.translate("PluginsForm", "Enable plugin", None, QtGui.QApplication.UnicodeUTF8))
+        else:
+            self.open.setVisible(False)
+            self.button.setVisible(False)
+            self.label.setText(QtGui.QApplication.translate("PluginsForm", "No plugins found", None, QtGui.QApplication.UnicodeUTF8))
+
+    def button_click(self):
+        ind = self.comboBox.currentIndex()
+        plugin = self.data[ind]
+        self.pl_loader.toggle_plugin(plugin[-1])
+        plugin[1] = not plugin[1]
+        if plugin[1]:
+            self.button.setText(QtGui.QApplication.translate("PluginsForm", "Disable plugin", None, QtGui.QApplication.UnicodeUTF8))
+        else:
+            self.button.setText(QtGui.QApplication.translate("PluginsForm", "Enable plugin", None, QtGui.QApplication.UnicodeUTF8))
diff --git a/toxygen/messages.py b/toxygen/messages.py
new file mode 100644
index 0000000..6c1c9e6
--- /dev/null
+++ b/toxygen/messages.py
@@ -0,0 +1,114 @@
+
+
+MESSAGE_TYPE = {
+    'TEXT': 0,
+    'ACTION': 1,
+    'FILE_TRANSFER': 2,
+    'INLINE': 3,
+    'INFO_MESSAGE': 4
+}
+
+
+class Message:
+
+    def __init__(self, message_type, owner, time):
+        self._time = time
+        self._type = message_type
+        self._owner = owner
+
+    def get_type(self):
+        return self._type
+
+    def get_owner(self):
+        return self._owner
+
+    def mark_as_sent(self):
+        self._owner = 0
+
+
+class TextMessage(Message):
+    """
+    Plain text or action message
+    """
+
+    def __init__(self, message, owner, time, message_type):
+        super(TextMessage, self).__init__(message_type, owner, time)
+        self._message = message
+
+    def get_data(self):
+        return self._message, self._owner, self._time, self._type
+
+
+class GroupChatTextMessage(TextMessage):
+
+    def __init__(self, friend_name, *args):
+        super().__init__(*args)
+        self._name = friend_name
+
+    def get_data(self):
+        data = list(super().get_data())
+        data.append(self._name)
+        return tuple(data)
+
+
+class TransferMessage(Message):
+    """
+    Message with info about file transfer
+    """
+
+    def __init__(self, owner, time, status, size, name, friend_number, file_number):
+        super(TransferMessage, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time)
+        self._status = status
+        self._size = size
+        self._file_name = name
+        self._friend_number, self._file_number = friend_number, file_number
+
+    def is_active(self, file_number):
+        return self._file_number == file_number and self._status not in (2, 3)
+
+    def get_friend_number(self):
+        return self._friend_number
+
+    def get_file_number(self):
+        return self._file_number
+
+    def get_status(self):
+        return self._status
+
+    def set_status(self, value):
+        self._status = value
+
+    def get_data(self):
+        return self._file_name, self._size, self._time, self._owner, self._friend_number, self._file_number, self._status
+
+
+class UnsentFile(Message):
+
+    def __init__(self, path, data, time):
+        super(UnsentFile, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], 0, time)
+        self._data, self._path = data, path
+
+    def get_data(self):
+        return self._path, self._data, self._time
+
+    def get_status(self):
+        return None
+
+
+class InlineImage(Message):
+    """
+    Inline image
+    """
+
+    def __init__(self, data):
+        super(InlineImage, self).__init__(MESSAGE_TYPE['INLINE'], None, None)
+        self._data = data
+
+    def get_data(self):
+        return self._data
+
+
+class InfoMessage(TextMessage):
+
+    def __init__(self, message, time):
+        super(InfoMessage, self).__init__(message, None, time, MESSAGE_TYPE['INFO_MESSAGE'])
diff --git a/toxygen/messenger/__init__.py b/toxygen/messenger/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py
deleted file mode 100644
index d44a7a9..0000000
--- a/toxygen/messenger/messages.py
+++ /dev/null
@@ -1,243 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import os.path
-
-from history.database import MESSAGE_AUTHOR
-from ui.messages_widgets import *
-
-MESSAGE_TYPE = {
-    'TEXT': 0,
-    'ACTION': 1,
-    'FILE_TRANSFER': 2,
-    'INLINE': 3,
-    'INFO_MESSAGE': 4
-}
-
-PAGE_SIZE = 42
-
-
-class MessageAuthor:
-
-    def __init__(self, author_name, author_type):
-        self._name = author_name
-        self._type = author_type
-
-    def get_name(self):
-        return self._name
-
-    name = property(get_name)
-
-    def get_type(self):
-        return self._type
-
-    def set_type(self, value):
-        self._type = value
-
-    type = property(get_type, set_type)
-
-
-class Message:
-
-    MESSAGE_ID = 0
-
-    def __init__(self, message_type, author, iTime):
-        self._time = iTime
-        self._type = message_type
-        self._author = author
-        self._widget = None
-        self._message_id = self._get_id()
-
-    def get_type(self):
-        return self._type
-
-    type = property(get_type)
-
-    def get_author(self):
-        return self._author
-
-    author = property(get_author)
-
-    def get_time(self):
-        return self._time
-
-    time = property(get_time)
-
-    def get_message_id(self):
-        return self._message_id
-
-    message_id = property(get_message_id)
-
-    def get_widget(self, *args):
-        # FixMe
-        self._widget = self._create_widget(*args) # pylint: disable=assignment-from-none
-
-        return self._widget
-
-    widget = property(get_widget)
-
-    def remove_widget(self):
-        self._widget = None
-
-    def mark_as_sent(self):
-        self._author.type = MESSAGE_AUTHOR['ME']
-        if self._widget is not None:
-            self._widget.mark_as_sent()
-
-    def _create_widget(self, *args):
-        # overridden
-        return None
-
-    @staticmethod
-    def _get_id() -> int:
-        Message.MESSAGE_ID += 1
-
-        return int(Message.MESSAGE_ID)
-
-
-class TextMessage(Message):
-    """
-    Plain text or action message
-    """
-
-    def __init__(self, message, owner, iTime, message_type, message_id=0):
-        super().__init__(message_type, owner, iTime)
-        self._message = message
-        self._id = message_id
-
-    def get_text(self) -> str:
-        return self._message
-
-    text = property(get_text)
-
-    def get_id(self):
-        return self._id
-
-    id = property(get_id)
-
-    def is_saved(self):
-        return self._id > 0
-
-    def _create_widget(self, *args):
-        return MessageItem(self, *args)
-
-
-class OutgoingTextMessage(TextMessage):
-
-    def __init__(self, message, owner, iTime, message_type, tox_message_id=0):
-        super().__init__(message, owner, iTime, message_type)
-        self._tox_message_id = tox_message_id
-
-    def get_tox_message_id(self):
-        return self._tox_message_id
-
-    def set_tox_message_id(self, tox_message_id):
-        self._tox_message_id = tox_message_id
-
-    tox_message_id = property(get_tox_message_id, set_tox_message_id)
-
-
-class GroupChatMessage(TextMessage):
-
-    def __init__(self, cid, message, owner, iTime, message_type, name):
-        super().__init__(cid, message, owner, iTime, message_type)
-        self._user_name = name
-
-
-class TransferMessage(Message):
-    """
-    Message with info about file transfer
-    """
-
-    def __init__(self, author, iTime, state, size, file_name, friend_number, file_number):
-        super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], author, iTime)
-        self._state = state
-        self._size = size
-        self._file_name = file_name
-        self._friend_number, self._file_number = friend_number, file_number
-
-    def is_active(self, file_number) -> bool:
-        if self._file_number != file_number:
-            return False
-
-        return self._state not in (FILE_TRANSFER_STATE['FINISHED'], FILE_TRANSFER_STATE['CANCELLED'])
-
-    def get_friend_number(self) -> int:
-        return self._friend_number
-
-    friend_number = property(get_friend_number)
-
-    def get_file_number(self):
-        return self._file_number
-
-    file_number = property(get_file_number)
-
-    def get_state(self):
-        return self._state
-
-    def set_state(self, value):
-        self._state = value
-
-    state = property(get_state, set_state)
-
-    def get_size(self):
-        return self._size
-
-    size = property(get_size)
-
-    def get_file_name(self):
-        return self._file_name
-
-    file_name = property(get_file_name)
-
-    def transfer_updated(self, state, percentage, iTime):
-        self._state = state
-        if self._widget is not None:
-            self._widget.update_transfer_state(state, percentage, iTime)
-
-    def _create_widget(self, *args):
-        return FileTransferItem(self, *args)
-
-
-class UnsentFileMessage(TransferMessage):
-
-    def __init__(self, path, data, iTime, author, size, friend_number):
-        file_name = os.path.basename(path)
-        super().__init__(author, iTime, FILE_TRANSFER_STATE['UNSENT'], size, file_name, friend_number, -1)
-        self._data, self._path = data, path
-
-    def get_data(self):
-        return self._data
-
-    data = property(get_data)
-
-    def get_path(self):
-        return self._path
-
-    path = property(get_path)
-
-    def _create_widget(self, *args):
-        return UnsentFileItem(self, *args)
-
-
-class InlineImageMessage(Message):
-    """
-    Inline image
-    """
-
-    def __init__(self, data):
-        super().__init__(MESSAGE_TYPE['INLINE'], None, None)
-        self._data = data
-
-    def get_data(self):
-        return self._data
-
-    data = property(get_data)
-
-    def _create_widget(self, *args):
-        return InlineImageItem(self, *args)
-
-
-class InfoMessage(TextMessage):
-
-    def __init__(self, message, iTime):
-        super().__init__(message, None, iTime, MESSAGE_TYPE['INFO_MESSAGE'])
diff --git a/toxygen/messenger/messenger.py b/toxygen/messenger/messenger.py
deleted file mode 100644
index c38bc31..0000000
--- a/toxygen/messenger/messenger.py
+++ /dev/null
@@ -1,367 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import logging
-import common.tox_save as tox_save
-import utils.ui as util_ui
-
-from messenger.messages import *
-from toxygen_wrapper.tests.support_testing import assert_main_thread
-from toxygen_wrapper.toxcore_enums_and_consts import TOX_MAX_MESSAGE_LENGTH
-
-global LOG
-LOG = logging.getLogger('app.'+__name__)
-log = lambda x: LOG.info(x)
-
-class Messenger(tox_save.ToxSave):
-
-    def __init__(self, tox, plugin_loader, screen, contacts_manager, contacts_provider, items_factory, profile,
-                 calls_manager):
-        super().__init__(tox)
-        self._plugin_loader = plugin_loader
-        self._screen = screen
-        self._contacts_manager = contacts_manager
-        self._contacts_provider = contacts_provider
-        self._items_factory = items_factory
-        self._profile = profile
-        self._profile_name = profile.name
-
-        profile.name_changed_event.add_callback(self._on_profile_name_changed)
-        calls_manager.call_started_event.add_callback(self._on_call_started)
-        calls_manager.call_finished_event.add_callback(self._on_call_finished)
-
-    def __repr__(self):
-        return "<Messenger>"
-
-    def get_last_message(self) -> str:
-        contact = self._contacts_manager.get_curr_contact()
-        if contact is None:
-            return str()
-
-        return contact.get_last_message_text()
-
-    # Messaging - friends
-
-    def new_message(self, friend_number, message_type, message) -> None:
-        """
-        Current user gets new message
-        :param friend_number: friend_num of friend who sent message
-        :param message_type: message type - plain text or action message (/me)
-        :param message: text of message
-        """
-        t = util.get_unix_time()
-        friend = self._get_friend_by_number(friend_number)
-        text_message = TextMessage(message, MessageAuthor(friend.name, MESSAGE_AUTHOR['FRIEND']), t, message_type)
-        self._add_message(text_message, friend)
-
-    def send_message(self) -> None:
-        text = self._screen.messageEdit.toPlainText()
-
-        plugin_command_prefix = '/plugin '
-        if text.startswith(plugin_command_prefix):
-            self._plugin_loader.command(text[len(plugin_command_prefix):])
-            self._screen.messageEdit.clear()
-            return
-
-        message_type = TOX_MESSAGE_TYPE['NORMAL']
-        if False: # undocumented
-            action_message_prefix = '/me '
-            if text.startswith(action_message_prefix):
-                message_type = TOX_MESSAGE_TYPE['ACTION']
-                text = text[len(action_message_prefix):]
-
-        if len(text) > TOX_MAX_MESSAGE_LENGTH:
-            text = text[:TOX_MAX_MESSAGE_LENGTH] # 1372
-        try:
-            if self._contacts_manager.is_active_a_friend():
-                self.send_message_to_friend(text, message_type)
-            elif self._contacts_manager.is_active_a_group():
-                self.send_message_to_group('~'+text, message_type)
-            elif self._contacts_manager.is_active_a_group_chat_peer():
-                self.send_message_to_group_peer(text, message_type)
-            else:
-                LOG.warn(f'Unknown friend type for Messenger send_message')
-        except Exception as e:
-            LOG.error(f'Messenger send_message {e}')
-            import traceback
-            LOG.warn(traceback.format_exc())
-            title = 'Messenger send_message Error'
-            text = 'Error: ' + str(e)
-            assert_main_thread()
-            util_ui.message_box(text, title)
-
-    def send_message_to_friend(self, text, message_type, friend_number=None) -> None:
-        """
-        Send message
-        :param text: message text
-        :param friend_number: number of friend
-        from Qt callback
-        """
-        if not text:
-            return
-        if friend_number is None:
-            friend_number = self._contacts_manager.get_active_number()
-        if friend_number is None  or friend_number < 0:
-            LOG.error(f"No _contacts_manager.get_active_number")
-            return
-        assert_main_thread()
-
-        friend = self._get_friend_by_number(friend_number)
-        if not friend:
-            LOG.error(f"No self._get_friend_by_number")
-            return
-        messages = self._split_message(text.encode('utf-8'))
-        t = util.get_unix_time()
-        for message in messages:
-            if friend.status is not None:
-                message_id = self._tox.friend_send_message(friend_number, message_type, message)
-            else:
-                message_id = 0
-            message_author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['NOT_SENT'])
-            message = OutgoingTextMessage(text, message_author, t, message_type, message_id)
-            friend.append_message(message)
-            if not self._contacts_manager.is_friend_active(friend_number):
-                return
-            self._create_message_item(message)
-            self._screen.messageEdit.clear()
-            self._screen.messages.scrollToBottom()
-
-    def send_messages(self, friend_number:int) -> None:
-        """
-        Send 'offline' messages to friend
-        """
-        friend = self._get_friend_by_number(friend_number)
-        friend.load_corr()
-        messages = friend.get_unsent_messages()
-        try:
-            for message in messages:
-                message_id = self._tox.friend_send_message(friend_number, message.type, message.text.encode('utf-8'))
-                message.tox_message_id = message_id
-        except Exception as ex:
-            LOG.warn('Sending pending messages failed with ' + str(ex))
-
-    # Messaging - groups
-
-    def send_message_to_group(self, text, message_type, group_number=None) -> None:
-        if group_number is None:
-            group_number = self._contacts_manager.get_active_number()
-
-        if not text or group_number < 0:
-            return
-
-        group = self._get_group_by_number(group_number)
-        messages = self._split_message(text.encode('utf-8'))
-        t = util.get_unix_time()
-        for message in messages:
-            self._tox.group_send_message(group_number, message_type, message)
-            message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER'])
-            message = OutgoingTextMessage(text, message_author, t, message_type)
-            group.append_message(message)
-            if not self._contacts_manager.is_group_active(group_number):
-                return
-            self._create_message_item(message)
-            self._screen.messageEdit.clear()
-            self._screen.messages.scrollToBottom()
-
-    def new_group_message(self, group_number, message_type, message, peer_id) -> None:
-        """
-        Current user gets new message
-        :param message_type: message type - plain text or action message (/me)
-        :param message: text of message
-        """
-        t = util.get_unix_time()
-        group = self._get_group_by_number(group_number)
-        if not group:
-            LOG.error(f"FixMe new_group_message _get_group_by_number({group_number})")
-            return
-        peer = group.get_peer_by_id(peer_id)
-        if not peer:
-            LOG.error('FixMe new_group_message group.get_peer_by_id ' + str(peer_id))
-            return
-        text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']), t, message_type)
-        self._add_message(text_message, group)
-
-    # Messaging - group peers
-
-    def send_message_to_group_peer(self, text, message_type, group_number=None, peer_id=None) -> None:
-        if group_number is None or peer_id is None:
-            group_peer_contact = self._contacts_manager.get_curr_contact()
-            peer_id = group_peer_contact.number
-            group = self._get_group_by_public_key(group_peer_contact.group_pk)
-            group_number = group.number
-
-        if not text:
-            return
-        if group.number < 0:
-            return
-        if peer_id is not None and peer_id < 0:
-            return
-
-        assert_main_thread()
-        group = self._get_group_by_number(group_number)
-        messages = self._split_message(text.encode('utf-8'))
-
-        # FixMe: peer_id is None?
-        group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id)
-        if group_peer_contact is None:
-            LOG.warn("M.group_send_private_message group_peer_contact is None")
-            return
-        # group_peer_contact now may be None
-
-        t = util.get_unix_time()
-        for message in messages:
-            bRet = self._tox.group_send_private_message(group_number, peer_id, message_type, message)
-            if not bRet:
-                LOG.warn("M.group_send_private_messag failed")
-                continue
-            message_author = MessageAuthor(group.get_self_name(), MESSAGE_AUTHOR['GC_PEER'])
-            message = OutgoingTextMessage(text, message_author, t, message_type)
-            # AttributeError: 'GroupChatPeer' object has no attribute 'append_message'
-            if not hasattr(group_peer_contact, 'append_message'):
-                LOG.warn("M. group_peer_contact has no append_message group_peer_contact={group_peer_contact}")
-            else:
-                group_peer_contact.append_message(message)
-            if not self._contacts_manager.is_contact_active(group_peer_contact):
-                return
-            self._create_message_item(message)
-            self._screen.messageEdit.clear()
-            self._screen.messages.scrollToBottom()
-
-    def new_group_private_message(self, group_number, message_type, message, peer_id) -> None:
-        """
-        Current user gets new message
-        :param message: text of message
-        """
-        t = util.get_unix_time()
-        group = self._get_group_by_number(group_number)
-        peer = group.get_peer_by_id(peer_id)
-        if not peer:
-            LOG.warn('FixMe new_group_private_message group.get_peer_by_id ' + str(peer_id))
-            return
-        text_message = TextMessage(message, MessageAuthor(peer.name, MESSAGE_AUTHOR['GC_PEER']),
-                                   t, message_type)
-        group_peer_contact = self._contacts_manager.get_or_create_group_peer_contact(group_number, peer_id)
-        if not group_peer_contact:
-            LOG.warn('FixMe new_group_private_message group_peer_contact ' + str(peer_id))
-            return
-        self._add_message(text_message, group_peer_contact)
-
-    # Message receipts
-
-    def receipt(self, friend_number, message_id) -> None:
-        friend = self._get_friend_by_number(friend_number)
-        friend.mark_as_sent(message_id)
-
-    # Typing notifications
-
-    def send_typing(self, typing) -> None:
-        """
-        Send typing notification to a friend
-        """
-        if not self._contacts_manager.can_send_typing_notification():
-            return
-        contact = self._contacts_manager.get_curr_contact()
-        contact.typing_notification_handler.send(self._tox, typing)
-
-    def friend_typing(self, friend_number, typing) -> None:
-        """
-        Display incoming typing notification
-        """
-        if self._contacts_manager.is_friend_active(friend_number):
-            self._screen.typing.setVisible(typing)
-
-    # Contact info updated
-
-    def new_friend_name(self, friend, old_name, new_name) -> None:
-        if old_name == new_name or friend.has_alias():
-            return
-        message = util_ui.tr('User {} is now known as {}')
-        message = message.format(old_name, new_name)
-        if not self._contacts_manager.is_friend_active(friend.number):
-            friend.actions = True
-        self._add_info_message(friend.number, message)
-
-    # Private methods
-
-    @staticmethod
-    def _split_message(message) -> list:
-        messages = []
-        while len(message) > TOX_MAX_MESSAGE_LENGTH:
-            size = TOX_MAX_MESSAGE_LENGTH * 4 // 5
-            last_part = message[size:TOX_MAX_MESSAGE_LENGTH]
-            if b' ' in last_part:
-                index = last_part.index(b' ')
-            elif b',' in last_part:
-                index = last_part.index(b',')
-            elif b'.' in last_part:
-                index = last_part.index(b'.')
-            else:
-                index = TOX_MAX_MESSAGE_LENGTH - size - 1
-            index += size + 1
-            messages.append(message[:index])
-            message = message[index:]
-        if message:
-            messages.append(message)
-
-        return messages
-
-    def _get_friend_by_number(self, friend_number:int):
-        return self._contacts_provider.get_friend_by_number(friend_number)
-
-    def _get_group_by_number(self, group_number):
-        return self._contacts_provider.get_group_by_number(group_number)
-
-    def _get_group_by_public_key(self, public_key):
-        return self._contacts_provider.get_group_by_public_key( public_key)
-
-    def _on_profile_name_changed(self, new_name) -> None:
-        if self._profile_name == new_name:
-            return
-        message = util_ui.tr('User {} is now known as {}')
-        message = message.format(self._profile_name, new_name)
-        for friend in self._contacts_provider.get_all_friends():
-            self._add_info_message(friend.number, message)
-        self._profile_name = new_name
-
-    def _on_call_started(self, friend_number, audio, video, is_outgoing) -> None:
-        if is_outgoing:
-            text = util_ui.tr("Outgoing video call") if video else util_ui.tr("Outgoing audio call")
-        else:
-            text = util_ui.tr("Incoming video call") if video else util_ui.tr("Incoming audio call")
-        self._add_info_message(friend_number, text)
-
-    def _on_call_finished(self, friend_number, is_declined) -> None:
-        text = util_ui.tr("Call declined") if is_declined else util_ui.tr("Call finished")
-        self._add_info_message(friend_number, text)
-
-    def _add_info_message(self, friend_number, text) -> None:
-        friend = self._get_friend_by_number(friend_number)
-        assert friend
-        message = InfoMessage(text, util.get_unix_time())
-        friend.append_message(message)
-        if self._contacts_manager.is_friend_active(friend_number):
-            self._create_info_message_item(message)
-
-    def _create_info_message_item(self, message) -> None:
-        assert_main_thread()
-        self._items_factory.create_message_item(message)
-        self._screen.messages.scrollToBottom()
-
-    def _add_message(self, text_message, contact) -> None:
-        assert_main_thread()
-        if not contact:
-            LOG.warn("_add_message null contact")
-            return
-        if self._contacts_manager.is_contact_active(contact):  # add message to list
-#            LOG.debug("_add_message is_contact_active(contact)")
-            self._create_message_item(text_message)
-            self._screen.messages.scrollToBottom()
-            self._contacts_manager.get_curr_contact().append_message(text_message)
-        else:
-#            LOG.debug("_add_message not is_contact_active(contact)")
-            contact.inc_messages()
-            contact.append_message(text_message)
-            if not contact.visibility:
-                self._contacts_manager.update_filtration()
-
-    def _create_message_item(self, text_message) -> None:
-        # pixmap = self._contacts_manager.get_curr_contact().get_pixmap()
-        self._items_factory.create_message_item(text_message)
diff --git a/toxygen/middleware/__init__.py b/toxygen/middleware/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py
deleted file mode 100644
index e0842f7..0000000
--- a/toxygen/middleware/callbacks.py
+++ /dev/null
@@ -1,775 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import sys
-import os
-import threading
-from  qtpy import QtGui
-from toxygen_wrapper.toxcore_enums_and_consts import *
-from toxygen_wrapper.toxav_enums import *
-from toxygen_wrapper.tox import bin_to_string
-import utils.ui as util_ui
-import utils.util as util
-from middleware.threads import invoke_in_main_thread, execute
-from notifications.tray import tray_notification
-from notifications.sound import *
-from datetime import datetime
-
-iMAX_INT32 = 4294967295
-# callbacks can be called in any thread so were being careful
-def LOG_ERROR(l): print(f"EROR. {l}")
-def LOG_WARN(l):  print(f"WARN. {l}")
-def LOG_INFO(l):
-    bIsVerbose = not hasattr(__builtins__, 'app') or app.oArgs.loglevel <= 20-1 # pylint dusable=undefined-variable
-    if bIsVerbose: print(f"INFO. {l}")
-def LOG_DEBUG(l):
-    bIsVerbose = not hasattr(__builtins__, 'app') or app.oArgs.loglevel <= 10-1 # pylint dusable=undefined-variable
-    if bIsVerbose: print(f"DBUG. {l}")
-def LOG_TRACE(l):
-    bIsVerbose = not hasattr(__builtins__, 'app') or app.oArgs.loglevel < 10-1 # pylint dusable=undefined-variable
-    pass # print(f"TRACE. {l}")
-
-global aTIMES
-aTIMES=dict()
-def bTooSoon(key, sSlot, fSec=10.0):
-    # rate limiting
-    global aTIMES
-    if sSlot not in aTIMES:
-        aTIMES[sSlot] = dict()
-    OTIME = aTIMES[sSlot]
-    now = datetime.now()
-    if key not in OTIME:
-        OTIME[key] = now
-        return False
-    delta = now - OTIME[key]
-    OTIME[key] = now
-    if delta.total_seconds() < fSec: return True
-    return False
-
-# TODO: refactoring. Use contact provider instead of manager
-
-# Callbacks - current user
-
-global iBYTES
-iBYTES=0
-def sProcBytes(sFile=None):
-    if sys.platform == 'win32': return ''
-    global iBYTES
-    if sFile is None:
-        pid = os.getpid()
-        sFile = f"/proc/{pid}/net/softnet_stat"
-    if os.path.exists(sFile):
-        total = 0
-        with open(sFile, 'r') as iFd:
-            for elt in iFd.readlines():
-                i = elt.find(' ')
-                p = int(elt[:i], 16)
-                total = total + p
-        if iBYTES == 0:
-            iBYTES = total
-            return ''
-        diff = total - iBYTES
-        s = f' {diff // 1024} Kbytes'
-    else:
-        s = ''
-    return s
-
-def self_connection_status(tox, profile):
-    """
-    Current user changed connection status (offline, TCP, UDP)
-    """
-    sSlot = 'self connection status'
-    def wrapped(tox_link, connection, user_data):
-        key = f"connection {connection}"
-        if bTooSoon(key, sSlot, 10): return
-        s = sProcBytes()
-        try:
-            status = tox.self_get_status() if connection != TOX_CONNECTION['NONE'] else None
-            if status:
-                LOG_DEBUG(f"self_connection_status: connection={connection} status={status}" +' '+s)
-            invoke_in_main_thread(profile.set_status, status)
-        except Exception as e:
-            LOG_ERROR(f"self_connection_status: {e}")
-            pass
-
-    return wrapped
-
-
-# Callbacks - friends
-
-
-def friend_status(contacts_manager, file_transfer_handler, profile, settings):
-    sSlot = 'friend status'
-    def wrapped(tox, friend_number, new_status, user_data):
-        """
-        Check friend's status (none, busy, away)
-        """
-        LOG_INFO(f"Friend's #{friend_number} status changed")
-        key = f"friend_number {friend_number}"
-        if bTooSoon(key, sSlot, 10): return
-        friend = contacts_manager.get_friend_by_number(friend_number)
-        if friend.status is None and settings['sound_notifications'] and \
-          profile.status != TOX_USER_STATUS['BUSY']:
-            sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
-        invoke_in_main_thread(friend.set_status, new_status)
-
-        def set_timer():
-            t = threading.Timer(5, lambda: file_transfer_handler.send_files(friend_number))
-            t.start()
-        invoke_in_main_thread(set_timer)
-        invoke_in_main_thread(contacts_manager.update_filtration)
-
-    return wrapped
-
-
-def friend_connection_status(contacts_manager, profile, settings, plugin_loader, file_transfer_handler,
-                             messenger, calls_manager):
-    def wrapped(tox, friend_number, new_status, user_data):
-        """
-        Check friend's connection status (offline, udp, tcp)
-        """
-        LOG_DEBUG(f"Friend #{friend_number} connection status: {new_status}")
-        friend = contacts_manager.get_friend_by_number(friend_number)
-        if new_status == TOX_CONNECTION['NONE']:
-            invoke_in_main_thread(friend.set_status, None)
-            invoke_in_main_thread(file_transfer_handler.friend_exit, friend_number)
-            invoke_in_main_thread(contacts_manager.update_filtration)
-            invoke_in_main_thread(messenger.friend_typing, friend_number, False)
-            invoke_in_main_thread(calls_manager.friend_exit, friend_number)
-            if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
-                sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
-        elif friend.status is None:
-            invoke_in_main_thread(file_transfer_handler.send_avatar, friend_number)
-            invoke_in_main_thread(plugin_loader.friend_online, friend_number)
-
-    return wrapped
-
-
-def friend_name(contacts_provider, messenger):
-    sSlot = 'friend_name'
-    def wrapped(tox, friend_number, name, size, user_data):
-        """
-        Friend changed his name
-        """
-        key = f"friend_number={friend_number}"
-        if bTooSoon(key, sSlot, 60): return
-        friend = contacts_provider.get_friend_by_number(friend_number)
-        old_name = friend.name
-        new_name = str(name, 'utf-8')
-        LOG_DEBUG(f"get_friend_by_number #{friend_number} {new_name}")
-        invoke_in_main_thread(friend.set_name, new_name)
-        invoke_in_main_thread(messenger.new_friend_name, friend, old_name, new_name)
-
-    return wrapped
-
-def friend_status_message(contacts_manager, messenger):
-    sSlot = 'status_message'
-    def wrapped(tox, friend_number, status_message, size, user_data):
-        """
-        :return: function for callback friend_status_message. It updates friend's status message
-        and calls window repaint
-        """
-        friend = contacts_manager.get_friend_by_number(friend_number)
-        key = f"friend_number={friend_number}"
-        if bTooSoon(key, sSlot, 10): return
-
-        invoke_in_main_thread(friend.set_status_message, str(status_message, 'utf-8'))
-        LOG_DEBUG(f'User #{friend_number} has new status message')
-        invoke_in_main_thread(messenger.send_messages, friend_number)
-
-    return wrapped
-
-
-def friend_message(messenger, contacts_manager, profile, settings, window, tray):
-    def wrapped(tox, friend_number, message_type, message, size, user_data):
-        """
-        New message from friend
-        """
-        LOG_DEBUG(f"friend_message #{friend_number}")
-        message = str(message, 'utf-8')
-        invoke_in_main_thread(messenger.new_message, friend_number, message_type, message)
-        if not window.isActiveWindow():
-            friend = contacts_manager.get_friend_by_number(friend_number)
-            if settings['notifications'] \
-              and profile.status != TOX_USER_STATUS['BUSY'] \
-              and not settings.locked:
-                invoke_in_main_thread(tray_notification, friend.name, message, tray, window)
-            if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
-                sound_notification(SOUND_NOTIFICATION['MESSAGE'])
-            icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png')
-            if tray:
-                invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon))
-
-    return wrapped
-
-
-def friend_request(contacts_manager):
-    def wrapped(tox, public_key, message, message_size, user_data):
-        """
-        Called when user get new friend request
-        """
-        LOG_DEBUG(f'Friend request')
-        key = ''.join(chr(x) for x in public_key[:TOX_PUBLIC_KEY_SIZE])
-        tox_id = bin_to_string(key, TOX_PUBLIC_KEY_SIZE)
-        invoke_in_main_thread(contacts_manager.process_friend_request, tox_id, str(message, 'utf-8'))
-
-    return wrapped
-
-
-def friend_typing(messenger):
-    sSlot = "friend_typing"
-    def wrapped(tox, friend_number, typing, user_data):
-        key = f"friend_number={friend_number}"
-        if bTooSoon(key, sSlot, 10): return
-        LOG_DEBUG(f"friend_typing #{friend_number}")
-        invoke_in_main_thread(messenger.friend_typing, friend_number, typing)
-    return wrapped
-
-
-def friend_read_receipt(messenger):
-    def wrapped(tox, friend_number, message_id, user_data):
-        invoke_in_main_thread(messenger.receipt, friend_number, message_id)
-
-    return wrapped
-
-
-# Callbacks - file transfers
-
-
-def tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager, settings):
-    """
-    New incoming file
-    """
-    def wrapped(tox, friend_number, file_number, file_type, size, file_name, file_name_size, user_data):
-        if file_type == TOX_FILE_KIND['DATA']:
-            LOG_INFO(f'file_transfer_handler File friend_number={friend_number}')
-            try:
-                file_name = str(file_name[:file_name_size], 'utf-8')
-            except:
-                file_name = 'toxygen_file'
-            invoke_in_main_thread(file_transfer_handler.incoming_file_transfer,
-                                  friend_number,
-                                  file_number,
-                                  size,
-                                  file_name)
-            if not window.isActiveWindow():
-                friend = contacts_manager.get_friend_by_number(friend_number)
-                if settings['notifications'] \
-                  and profile.status != TOX_USER_STATUS['BUSY'] \
-                  and not settings.locked:
-                    file_from = util_ui.tr("File from")
-                    invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window)
-                if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
-                    sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER'])
-                if tray:
-                    icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png')
-                    invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon))
-        else:  # avatar
-            LOG_DEBUG(f'file_transfer_handler Avatar')
-            invoke_in_main_thread(file_transfer_handler.incoming_avatar,
-                                  friend_number,
-                                  file_number,
-                                  size)
-    return wrapped
-
-
-def file_recv_chunk(file_transfer_handler):
-    """
-    Incoming chunk
-    """
-    def wrapped(tox, friend_number, file_number, position, chunk, length, user_data):
-        chunk = chunk[:length] if length else None
-        execute(file_transfer_handler.incoming_chunk, friend_number, file_number, position, chunk)
-
-    return wrapped
-
-
-def file_chunk_request(file_transfer_handler):
-    """
-    Outgoing chunk
-    """
-    def wrapped(tox, friend_number, file_number, position, size, user_data):
-        execute(file_transfer_handler.outgoing_chunk, friend_number, file_number, position, size)
-
-    return wrapped
-
-
-def file_recv_control(file_transfer_handler):
-    """
-    Friend cancelled, paused or resumed file transfer
-    """
-    def wrapped(tox, friend_number, file_number, file_control, user_data):
-        if file_control == TOX_FILE_CONTROL['CANCEL']:
-            file_transfer_handler.cancel_transfer(friend_number, file_number, True)
-        elif file_control == TOX_FILE_CONTROL['PAUSE']:
-            file_transfer_handler.pause_transfer(friend_number, file_number, True)
-        elif file_control == TOX_FILE_CONTROL['RESUME']:
-            file_transfer_handler.resume_transfer(friend_number, file_number, True)
-
-    return wrapped
-
-# Callbacks - custom packets
-
-
-def lossless_packet(plugin_loader):
-    def wrapped(tox, friend_number, data, length, user_data):
-        """
-        Incoming lossless packet
-        """
-        data = data[:length]
-        invoke_in_main_thread(plugin_loader.callback_lossless, friend_number, data)
-
-    return wrapped
-
-
-def lossy_packet(plugin_loader):
-    def wrapped(tox, friend_number, data, length, user_data):
-        """
-        Incoming lossy packet
-        """
-        data = data[:length]
-        invoke_in_main_thread(plugin_loader.callback_lossy, friend_number, data)
-
-    return wrapped
-
-
-# Callbacks - audio
-
-def call_state(calls_manager):
-    def wrapped(iToxav, friend_number, mask, user_data):
-        """
-        New call state
-        """
-        LOG_DEBUG(f"call_state #{friend_number}")
-        if mask == TOXAV_FRIEND_CALL_STATE['FINISHED'] or mask == TOXAV_FRIEND_CALL_STATE['ERROR']:
-            invoke_in_main_thread(calls_manager.stop_call, friend_number, True)
-        else:
-            # guessing was calls_manager.
-            #? incoming_call
-            calls_manager._call.toxav_call_state_cb(friend_number, mask)
-
-    return wrapped
-
-
-def call(calls_manager):
-    def wrapped(toxav, friend_number, audio, video, user_data):
-        """
-        Incoming call from friend
-        """
-        LOG_DEBUG(f"Incoming call from {friend_number} {audio} {video}")
-        invoke_in_main_thread(calls_manager.incoming_call, audio, video, friend_number)
-
-    return wrapped
-
-
-def callback_audio(calls_manager):
-    def wrapped(toxav, friend_number, samples, audio_samples_per_channel, audio_channels_count, rate, user_data):
-        """
-        New audio chunk
-        """
-#trace        LOG_DEBUG(f"callback_audio #{friend_number}")
-        # dunno was .call
-        calls_manager._call.audio_chunk(
-            bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]),
-            audio_channels_count,
-            rate)
-
-    return wrapped
-
-# Callbacks - video
-
-
-def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data):
-    """
-    Creates yuv frame from y, u, v and shows it using OpenCV
-    For yuv => bgr we need this YUV420 frame:
-
-              width
-    -------------------------
-    |                       |
-    |          Y            |      height
-    |                       |
-    -------------------------
-    |           |           |
-    |  U even   |   U odd   |      height // 4
-    |           |           |
-    -------------------------
-    |           |           |
-    |  V even   |   V odd   |      height // 4
-    |           |           |
-    -------------------------
-
-     width // 2   width // 2
-
-    It can be created from initial y, u, v using slices
-    """
-    LOG_DEBUG(f"video_receive_frame from toxav_video_receive_frame_cb={friend_number}")
-    with ts.ignoreStdout(): import cv2
-    import numpy as np
-    try:
-        y_size = abs(max(width, abs(ystride)))
-        u_size = abs(max(width // 2, abs(ustride)))
-        v_size = abs(max(width // 2, abs(vstride)))
-
-        y = np.asarray(y[:y_size * height], dtype=np.uint8).reshape(height, y_size)
-        u = np.asarray(u[:u_size * height // 2], dtype=np.uint8).reshape(height // 2, u_size)
-        v = np.asarray(v[:v_size * height // 2], dtype=np.uint8).reshape(height // 2, v_size)
-
-        width -= width % 4
-        height -= height % 4
-
-        frame = np.zeros((int(height * 1.5), width), dtype=np.uint8)
-
-        frame[:height, :] = y[:height, :width]
-        frame[height:height * 5 // 4, :width // 2] = u[:height // 2:2, :width // 2]
-        frame[height:height * 5 // 4, width // 2:] = u[1:height // 2:2, :width // 2]
-
-        frame[height * 5 // 4:, :width // 2] = v[:height // 2:2, :width // 2]
-        frame[height * 5 // 4:, width // 2:] = v[1:height // 2:2, :width // 2]
-
-        frame = cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_I420) # pylint: disable=no-member
-        # imshow
-        invoke_in_main_thread(cv2.imshow, str(friend_number), frame) # pylint: disable=no-member
-    except Exception as ex:
-        LOG_ERROR(f"video_receive_frame  {ex} #{friend_number}")
-        pass
-
-# Callbacks - groups
-
-
-def group_message(window, tray, tox, messenger, settings, profile):
-    """
-    New message in group chat
-    """
-    def wrapped(tox_link, group_number, peer_id, message_type, message, length, user_data):
-        LOG_DEBUG(f"group_message #{group_number}")
-        message = str(message[:length], 'utf-8')
-        invoke_in_main_thread(messenger.new_group_message, group_number, message_type, message, peer_id)
-        if window.isActiveWindow():
-            return
-        bl = settings['notify_all_gc'] or profile.name in message
-        name = tox.group_peer_get_name(group_number, peer_id)
-        if settings['sound_notifications'] and bl and \
-           profile.status != TOX_USER_STATUS['BUSY']:
-            sound_notification(SOUND_NOTIFICATION['MESSAGE'])
-        if False and settings['tray_icon'] and tray:
-            if settings['notifications'] and \
-               profile.status != TOX_USER_STATUS['BUSY'] and \
-                   (not settings.locked) and bl:
-                invoke_in_main_thread(tray_notification, name, message, tray, window)
-            if tray:
-                icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png')
-                invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon))
-
-    return wrapped
-
-
-def group_private_message(window, tray, tox, messenger, settings, profile):
-    """
-    New private message in group chat
-    """
-    def wrapped(tox_link, group_number, peer_id, message_type, message, length, user_data):
-        LOG_DEBUG(f"group_private_message #{group_number}")
-        message = str(message[:length], 'utf-8')
-        invoke_in_main_thread(messenger.new_group_private_message, group_number, message_type, message, peer_id)
-        if window.isActiveWindow():
-            return
-        bl = settings['notify_all_gc'] or profile.name in message
-        try:
-            name = tox.group_peer_get_name(group_number, peer_id)
-        except Exception as e:
-            LOG_WARN("tox.group_peer_get_name {group_number} {peer_id}")
-            name = ''
-        if settings['notifications'] and settings['tray_icon'] \
-           and profile.status != TOX_USER_STATUS['BUSY'] \
-           and (not settings.locked) and bl:
-            invoke_in_main_thread(tray_notification, name, message, tray, window)
-        if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']:
-            sound_notification(SOUND_NOTIFICATION['MESSAGE'])
-        icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png')
-        if tray and hasattr(tray, 'setIcon'):
-            invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon))
-
-    return wrapped
-
-# Exception ignored on calling ctypes callback function: <function group_invite.<locals>.wrapped at 0x7ffede910700>
-def group_invite(window, settings, tray, profile, groups_service, contacts_provider):
-    def wrapped(tox, friend_number, invite_data, length, group_name, group_name_length, user_data):
-        LOG_DEBUG(f"group_invite friend_number={friend_number}")
-        group_name = str(bytes(group_name[:group_name_length]), 'utf-8')
-        invoke_in_main_thread(groups_service.process_group_invite,
-                              friend_number, group_name,
-                              bytes(invite_data[:length]))
-        if window.isActiveWindow():
-            return
-        bHasTray = tray and settings['tray_icon']
-        if settings['notifications'] \
-           and bHasTray \
-           and profile.status != TOX_USER_STATUS['BUSY'] \
-           and not settings.locked:
-            friend = contacts_provider.get_friend_by_number(friend_number)
-            title = util_ui.tr('New invite to group chat')
-            text = util_ui.tr('{} invites you to group "{}"').format(friend.name, group_name)
-            invoke_in_main_thread(tray_notification, title, text, tray, window)
-        if tray:
-            icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png')
-            invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon))
-
-    return wrapped
-
-
-def group_self_join(contacts_provider, contacts_manager, groups_service):
-    sSlot = 'group_self_join'
-    def wrapped(tox, group_number, user_data):
-        if group_number is None:
-            LOG_ERROR(f"group_self_join NULL group_number #{group_number}")
-            return
-        LOG_DEBUG(f"group_self_join #{group_number}")
-        key = f"group_number {group_number}"
-        if bTooSoon(key, sSlot, 10): return
-        group = contacts_provider.get_group_by_number(group_number)
-        if group is None:
-            LOG_ERROR(f"group_self_join NULL group #{group}")
-            return
-        invoke_in_main_thread(group.set_status, TOX_USER_STATUS['NONE'])
-        invoke_in_main_thread(groups_service.update_group_info, group)
-        invoke_in_main_thread(contacts_manager.update_filtration)
-
-    return wrapped
-
-def group_peer_join(contacts_provider, groups_service):
-    sSlot = "group_peer_join"
-    def wrapped(tox, group_number, peer_id, user_data):
-        key = f"group_peer_join #{group_number} peer_id={peer_id}"
-        if bTooSoon(key, sSlot, 20): return
-        group = contacts_provider.get_group_by_number(group_number)
-        if group is None:
-            LOG_ERROR(f"group_peer_join NULL group #{group} group_number={group_number}")
-            return
-        if peer_id > group._peers_limit:
-            LOG_ERROR(key +f" {peer_id} > {group._peers_limit}")
-            return
-        LOG_DEBUG(f"group_peer_join group={group}")
-        group.add_peer(peer_id)
-        invoke_in_main_thread(groups_service.generate_peers_list)
-        invoke_in_main_thread(groups_service.update_group_info, group)
-
-    return wrapped
-
-
-def group_peer_exit(contacts_provider, groups_service, contacts_manager):
-    def wrapped(tox,
-                group_number, peer_id,
-                exit_type, name, name_length,
-                message, length,
-                user_data):
-        group = contacts_provider.get_group_by_number(group_number)
-        if group:
-            LOG_DEBUG(f"group_peer_exit #{group_number} peer_id={peer_id} exit_type={exit_type}")
-            group.remove_peer(peer_id)
-            invoke_in_main_thread(groups_service.generate_peers_list)
-        else:
-            LOG_WARN(f"group_peer_exit group not found #{group_number} peer_id={peer_id}")
-
-    return wrapped
-
-def group_peer_name(contacts_provider, groups_service):
-    def wrapped(tox, group_number, peer_id, name, length, user_data):
-        LOG_DEBUG(f"group_peer_name #{group_number} peer_id={peer_id}")
-        group = contacts_provider.get_group_by_number(group_number)
-        peer = group.get_peer_by_id(peer_id)
-        if peer:
-            peer.name = str(name[:length], 'utf-8')
-            invoke_in_main_thread(groups_service.generate_peers_list)
-        else:
-            # FixMe: known signal to revalidate roles...
-            #_peers = [(p._name, p._peer_id) for p in group.get_peers()]
-            LOG_TRACE(f"remove_peer group {group} has no peer_id={peer_id} in _peers!r")
-            return
-
-    return wrapped
-
-
-def group_peer_status(contacts_provider, groups_service):
-    def wrapped(tox, group_number, peer_id, peer_status, user_data):
-        LOG_DEBUG(f"group_peer_status #{group_number} peer_id={peer_id}")
-        group = contacts_provider.get_group_by_number(group_number)
-        peer = group.get_peer_by_id(peer_id)
-        if peer:
-            peer.status = peer_status
-        else:
-            # _peers = [(p._name, p._peer_id) for p in group.get_peers()]
-            LOG_TRACE(f"remove_peer group {group} has no peer_id={peer_id} in _peers!r")
-        # TODO: add info message
-        invoke_in_main_thread(groups_service.generate_peers_list)
-
-    return wrapped
-
-
-def group_topic(contacts_provider):
-    def wrapped(tox, group_number, peer_id, topic, length, user_data):
-        LOG_DEBUG(f"group_topic #{group_number} peer_id={peer_id}")
-        group = contacts_provider.get_group_by_number(group_number)
-        if group:
-            topic = str(topic[:length], 'utf-8')
-            invoke_in_main_thread(group.set_status_message, topic)
-        else:
-            _peers = [(p._name, p._peer_id) for p in group.get_peers()]
-            LOG_WARN(f"group_topic {group} has no peer_id={peer_id} in {_peers}")
-        # TODO: add info message
-
-    return wrapped
-
-def group_moderation(groups_service, contacts_provider, contacts_manager, messenger):
-    def update_peer_role(group, mod_peer_id, peer_id, new_role):
-        peer = group.get_peer_by_id(peer_id)
-        if peer:
-            peer.role = new_role
-            # TODO: add info message
-        else:
-            # FixMe: known signal to revalidate roles...
-            # _peers = [(p._name, p._peer_id) for p in group.get_peers()]
-            LOG_TRACE(f"update_peer_role group {group} has no peer_id={peer_id} in _peers!r")
-        # TODO: add info message
-
-    def remove_peer(group, mod_peer_id, peer_id, is_ban):
-        peer = group.get_peer_by_id(peer_id)
-        if peer:
-            contacts_manager.remove_group_peer_by_id(group, peer_id)
-            group.remove_peer(peer_id)
-        else:
-            # FixMe: known signal to revalidate roles...
-            #_peers = [(p._name, p._peer_id) for p in group.get_peers()]
-            LOG_TRACE(f"remove_peer group {group} has no peer_id={peer_id} in _peers!r")
-        # TODO: add info message
-
-    # source_peer_number, target_peer_number,
-    def wrapped(tox, group_number, mod_peer_id, peer_id, event_type, user_data):
-        if mod_peer_id == iMAX_INT32 or peer_id == iMAX_INT32:
-            # FixMe: known signal to revalidate roles...
-            return
-        LOG_DEBUG(f"group_moderation #{group_number} mod_id={mod_peer_id} peer_id={peer_id} event_type={event_type}")
-        group = contacts_provider.get_group_by_number(group_number)
-        mod_peer = group.get_peer_by_id(mod_peer_id)
-        if not mod_peer:
-            #_peers = [(p._name, p._peer_id) for p in group.get_peers()]
-            LOG_TRACE(f"remove_peer group {group} has no mod_peer_id={mod_peer_id} in _peers!r")
-            return
-        peer = group.get_peer_by_id(peer_id)
-        if not peer:
-            # FixMe: known signal to revalidate roles...
-            #_peers = [(p._name, p._peer_id) for p in group.get_peers()]
-            LOG_TRACE(f"remove_peer group {group} has no peer_id={peer_id} in _peers!r")
-            return
-
-        if event_type == TOX_GROUP_MOD_EVENT['KICK']:
-            remove_peer(group, mod_peer_id, peer_id, False)
-        elif event_type == TOX_GROUP_MOD_EVENT['OBSERVER']:
-            update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['OBSERVER'])
-        elif event_type == TOX_GROUP_MOD_EVENT['USER']:
-            update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['USER'])
-        elif event_type == TOX_GROUP_MOD_EVENT['MODERATOR']:
-            update_peer_role(group, mod_peer_id, peer_id, TOX_GROUP_ROLE['MODERATOR'])
-
-        invoke_in_main_thread(groups_service.generate_peers_list)
-
-    return wrapped
-
-
-def group_password(contacts_provider):
-
-    def wrapped(tox_link, group_number, password, length, user_data):
-        LOG_DEBUG(f"group_password #{group_number}")
-        password = str(password[:length], 'utf-8')
-        group = contacts_provider.get_group_by_number(group_number)
-        group.password = password
-
-    return wrapped
-
-
-def group_peer_limit(contacts_provider):
-
-    def wrapped(tox_link, group_number, peer_limit, user_data):
-        LOG_DEBUG(f"group_peer_limit #{group_number}")
-        group = contacts_provider.get_group_by_number(group_number)
-        group.peer_limit = peer_limit
-
-    return wrapped
-
-
-def group_privacy_state(contacts_provider):
-
-    def wrapped(tox_link, group_number, privacy_state, user_data):
-        LOG_DEBUG(f"group_privacy_state #{group_number}")
-        group = contacts_provider.get_group_by_number(group_number)
-        group.is_private = privacy_state == TOX_GROUP_PRIVACY_STATE['PRIVATE']
-
-    return wrapped
-
-# Callbacks - initialization
-
-
-def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager,
-                   calls_manager, file_transfer_handler, main_window, tray, messenger, groups_service,
-                   contacts_provider, ms=None):
-    """
-    Initialization of all callbacks.
-    :param tox: Tox instance
-    :param profile: Profile instance
-    :param settings: Settings instance
-    :param contacts_manager: ContactsManager instance
-    :param contacts_manager: ContactsManager instance
-    :param calls_manager: CallsManager instance
-    :param file_transfer_handler: FileTransferHandler instance
-    :param plugin_loader: PluginLoader instance
-    :param main_window: MainWindow instance
-    :param tray: tray (for notifications)
-    :param messenger: Messenger instance
-    :param groups_service: GroupsService instance
-    :param contacts_provider: ContactsProvider instance
-    """
-
-    # self callbacks
-    tox.callback_self_connection_status(self_connection_status(tox, profile))
-
-    # friend callbacks
-    tox.callback_friend_status(friend_status(contacts_manager, file_transfer_handler, profile, settings))
-    tox.callback_friend_message(friend_message(messenger, contacts_manager, profile, settings, main_window, tray))
-    tox.callback_friend_connection_status(friend_connection_status(contacts_manager, profile, settings, plugin_loader,
-                                                                   file_transfer_handler, messenger, calls_manager))
-    tox.callback_friend_name(friend_name(contacts_provider, messenger))
-    tox.callback_friend_status_message(friend_status_message(contacts_manager, messenger))
-    tox.callback_friend_request(friend_request(contacts_manager))
-    tox.callback_friend_typing(friend_typing(messenger))
-    tox.callback_friend_read_receipt(friend_read_receipt(messenger))
-
-    # file transfer
-    tox.callback_file_recv(tox_file_recv(main_window, tray, profile, file_transfer_handler,
-                                         contacts_manager, settings))
-    tox.callback_file_recv_chunk(file_recv_chunk(file_transfer_handler))
-    tox.callback_file_chunk_request(file_chunk_request(file_transfer_handler))
-    tox.callback_file_recv_control(file_recv_control(file_transfer_handler))
-
-    # av
-    toxav = tox.AV
-    toxav.callback_call_state(call_state(calls_manager), 0)
-    toxav.callback_call(call(calls_manager), 0)
-    toxav.callback_audio_receive_frame(callback_audio(calls_manager), 0)
-    toxav.callback_video_receive_frame(video_receive_frame, 0)
-
-    # custom packets
-    tox.callback_friend_lossless_packet(lossless_packet(plugin_loader))
-    tox.callback_friend_lossy_packet(lossy_packet(plugin_loader))
-
-    # gc callbacks
-    tox.callback_group_message(group_message(main_window, tray, tox, messenger, settings, profile), 0)
-    tox.callback_group_private_message(group_private_message(main_window, tray, tox, messenger, settings, profile), 0)
-    tox.callback_group_invite(group_invite(main_window, settings, tray, profile, groups_service, contacts_provider), 0)
-    tox.callback_group_self_join(group_self_join(contacts_provider, contacts_manager, groups_service), 0)
-    tox.callback_group_peer_join(group_peer_join(contacts_provider, groups_service), 0)
-    tox.callback_group_peer_exit(group_peer_exit(contacts_provider, groups_service, contacts_manager), 0)
-    tox.callback_group_peer_name(group_peer_name(contacts_provider, groups_service), 0)
-    tox.callback_group_peer_status(group_peer_status(contacts_provider, groups_service), 0)
-    tox.callback_group_topic(group_topic(contacts_provider), 0)
-    tox.callback_group_moderation(group_moderation(groups_service, contacts_provider, contacts_manager, messenger), 0)
-    tox.callback_group_password(group_password(contacts_provider), 0)
-    tox.callback_group_peer_limit(group_peer_limit(contacts_provider), 0)
-    tox.callback_group_privacy_state(group_privacy_state(contacts_provider), 0)
diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py
deleted file mode 100644
index 75e3fc9..0000000
--- a/toxygen/middleware/threads.py
+++ /dev/null
@@ -1,223 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import sys
-import threading
-import queue
-from  qtpy import QtCore
-
-from bootstrap.bootstrap import *
-from bootstrap.bootstrap import download_nodes_list
-from toxygen_wrapper.toxcore_enums_and_consts import TOX_USER_STATUS, TOX_CONNECTION
-import toxygen_wrapper.tests.support_testing as ts
-from utils import util
-
-import time
-sleep = time.sleep
-
-# LOG=util.log
-global LOG
-import logging
-LOG = logging.getLogger('app.'+'threads')
-# log = lambda x: LOG.info(x)
-
-def LOG_ERROR(l): print('EROR+ '+l)
-def LOG_WARN(l):  print('WARN+ '+l)
-def LOG_INFO(l):  print('INFO+ '+l)
-def LOG_DEBUG(l): print('DBUG+ '+l)
-def LOG_TRACE(l): pass # print('TRAC+ '+l)
-
-iLAST_CONN = 0
-iLAST_DELTA = 60
-
-# Base threads
-
-class BaseThread(threading.Thread):
-
-    def __init__(self, name=None, target=None):
-        self._stop_thread = False
-        if name:
-            super().__init__(name=name, target=target)
-        else:
-            super().__init__(target=target)
-
-    def stop_thread(self, timeout=-1):
-        self._stop_thread = True
-        if timeout < 0:
-            timeout = ts.iTHREAD_TIMEOUT
-        i = 0
-        while i < ts.iTHREAD_JOINS:
-            self.join(timeout)
-            if not self.is_alive(): break
-            i = i + 1
-        else:
-            LOG_WARN(f"BaseThread {self.name} BLOCKED after {ts.iTHREAD_JOINS}")
-
-class BaseQThread(QtCore.QThread):
-
-    def __init__(self, name=None):
-        # NO name=name
-        super().__init__()
-        self._stop_thread = False
-        self.name = str(id(self))
-
-    def stop_thread(self, timeout=-1):
-        self._stop_thread = True
-        if timeout < 0:
-            timeout = ts.iTHREAD_TIMEOUT
-        i = 0
-        while i < ts.iTHREAD_JOINS:
-            self.wait(timeout)
-            if not self.isRunning(): break
-            i = i + 1
-            sleep(ts.iTHREAD_TIMEOUT)
-        else:
-            LOG_WARN(f"BaseQThread {self.name} BLOCKED")
-
-# Toxcore threads
-
-class InitThread(BaseThread):
-
-    def __init__(self, tox, plugin_loader, settings, app, is_first_start):
-        super().__init__(name='InitThread')
-        self._tox = tox
-        self._plugin_loader = plugin_loader
-        self._settings = settings
-        self._app = app
-        self._is_first_start = is_first_start
-
-    def run(self):
-        # DBUG+ InitThread run: ERROR name 'ts' is not defined
-        import toxygen_wrapper.tests.support_testing as ts
-        LOG_DEBUG('InitThread run: ')
-        try:
-            if self._is_first_start and ts.bAreWeConnected() and \
-              self._settings['download_nodes_list']:
-                LOG_INFO(f"downloading list of nodes {self._settings['download_nodes_list']}")
-                download_nodes_list(self._settings, oArgs=self._app._args)
-
-            if ts.bAreWeConnected():
-                LOG_INFO(f"calling test_net nodes")
-                self._app.test_net(oThread=self, iMax=4)
-
-            if self._is_first_start:
-                LOG_INFO('starting plugins')
-                self._plugin_loader.load()
-
-        except Exception as e:
-            LOG_DEBUG(f"InitThread run: ERROR {e}")
-            pass
-
-        for _ in range(ts.iTHREAD_JOINS):
-            if self._stop_thread:
-                return
-            sleep(ts.iTHREAD_SLEEP)
-        return
-
-class ToxIterateThread(BaseQThread):
-
-    def __init__(self, tox, app=None):
-        super().__init__()
-        self._tox = tox
-        self._app = app
-
-    def run(self):
-        LOG_DEBUG('ToxIterateThread run: ')
-        while not self._stop_thread:
-            try:
-                iMsec = self._tox.iteration_interval()
-                self._tox.iterate()
-            except Exception as e:
-                # Fatal Python error: Segmentation fault
-                LOG_ERROR(f"ToxIterateThread run: {e}")
-            else:
-                sleep(iMsec / 1000.0)
-
-            global iLAST_CONN
-            if not iLAST_CONN:
-                iLAST_CONN = time.time()
-            # TRAC> TCP_common.c#203:read_TCP_packet recv buffer has 0 bytes, but requested 10 bytes
-
-            # and segv
-            if time.time() - iLAST_CONN > iLAST_DELTA and \
-                ts.bAreWeConnected() and \
-                self._tox.self_get_status() == TOX_USER_STATUS['NONE'] and \
-                self._tox.self_get_connection_status() == TOX_CONNECTION['NONE']:
-                    iLAST_CONN = time.time()
-                    LOG_INFO(f"ToxIterateThread calling test_net")
-                    invoke_in_main_thread(
-                        self._app.test_net, oThread=self, iMax=2)
-
-
-class ToxAVIterateThread(BaseQThread):
-    def __init__(self, toxav):
-        super().__init__()
-        self._toxav = toxav
-
-    def run(self):
-        LOG_DEBUG('ToxAVIterateThread run: ')
-        while not self._stop_thread:
-            self._toxav.iterate()
-            sleep(self._toxav.iteration_interval() / 1000)
-
-
-# File transfers thread
-
-class FileTransfersThread(BaseQThread):
-
-    def __init__(self):
-        super().__init__('FileTransfers')
-        self._queue = queue.Queue()
-        self._timeout = 0.01
-
-    def execute(self, func, *args, **kwargs):
-        self._queue.put((func, args, kwargs))
-
-    def run(self):
-        while not self._stop_thread:
-            try:
-                func, args, kwargs = self._queue.get(timeout=self._timeout)
-                func(*args, **kwargs)
-            except queue.Empty:
-                pass
-            except queue.Full:
-                LOG_WARN('Queue is full in _thread')
-            except Exception as ex:
-                LOG_ERROR('in _thread: ' + str(ex))
-
-
-_thread = FileTransfersThread()
-def start_file_transfer_thread():
-    _thread.start()
-
-
-def stop_file_transfer_thread():
-    _thread.stop_thread()
-
-
-def execute(func, *args, **kwargs):
-    _thread.execute(func, *args, **kwargs)
-
-
-# Invoking in main thread
-
-class InvokeEvent(QtCore.QEvent):
-    EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
-
-    def __init__(self, fn, *args, **kwargs):
-        QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
-        self.fn = fn
-        self.args = args
-        self.kwargs = kwargs
-
-
-class Invoker(QtCore.QObject):
-
-    def event(self, event):
-        event.fn(*event.args, **event.kwargs)
-        return True
-
-
-_invoker = Invoker()
-
-
-def invoke_in_main_thread(fn, *args, **kwargs):
-    QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
diff --git a/toxygen/middleware/tox_factory.py b/toxygen/middleware/tox_factory.py
deleted file mode 100644
index 1216dd8..0000000
--- a/toxygen/middleware/tox_factory.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import ctypes
-import traceback
-import os
-from ctypes import *
-
-import user_data.settings
-import toxygen_wrapper.tox
-import toxygen_wrapper.toxcore_enums_and_consts as enums
-from toxygen_wrapper.tests import support_testing as ts
-# callbacks can be called in any thread so were being careful
-# tox.py can be called by callbacks
-from toxygen_wrapper.tests.support_testing import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
-
-global LOG
-import logging
-LOG = logging.getLogger('app.'+'tox_factory')
-
-from utils import util
-from utils import ui as util_ui
-
-
-def tox_factory(data=None, settings=None, args=None, app=None):
-    """
-    :param data: user data from .tox file. None = no saved data, create new profile
-    :param settings: current profile settings. None = default settings will be used
-    :return: new tox instance
-    """
-    if not settings:
-        LOG_WARN("tox_factory using get_default_settings")
-        settings = user_data.settings.Settings.get_default_settings()
-    else:
-        user_data.settings.clean_settings(settings)
-
-    try:
-        tox_options = toxygen_wrapper.tox.Tox.options_new()
-        tox_options.contents.ipv6_enabled = settings['ipv6_enabled']
-        tox_options.contents.udp_enabled = settings['udp_enabled']
-        tox_options.contents.proxy_type = int(settings['proxy_type'])
-        if type(settings['proxy_host']) == str:
-            tox_options.contents.proxy_host = bytes(settings['proxy_host'],'UTF-8')
-        elif type(settings['proxy_host']) == bytes:
-            tox_options.contents.proxy_host = settings['proxy_host']
-        else:
-            tox_options.contents.proxy_host = b''
-        tox_options.contents.proxy_port = int(settings['proxy_port'])
-        tox_options.contents.start_port = settings['start_port']
-        tox_options.contents.end_port = settings['end_port']
-        tox_options.contents.tcp_port = settings['tcp_port']
-        tox_options.contents.local_discovery_enabled = settings['local_discovery_enabled']
-        tox_options.contents.dht_announcements_enabled = settings['dht_announcements_enabled']
-        tox_options.contents.hole_punching_enabled = settings['hole_punching_enabled']
-        if data:  # load existing profile
-            tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['TOX_SAVE']
-            tox_options.contents.savedata_data = ctypes.c_char_p(data)
-            tox_options.contents.savedata_length = len(data)
-        else:  # create new profile
-            tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['NONE']
-            tox_options.contents.savedata_data = None
-            tox_options.contents.savedata_length = 0
-
-        # overrides
-        tox_options.contents.local_discovery_enabled = False
-        tox_options.contents.ipv6_enabled = False
-        tox_options.contents.hole_punching_enabled = False
-
-        LOG.debug("toxygen_wrapper.tox.Tox settings: " +repr(settings))
-
-        if 'trace_enabled' in settings and not settings['trace_enabled']:
-            LOG_DEBUG("settings['trace_enabled' disabled" )
-        elif tox_options._options_pointer and \
-          'trace_enabled' in settings and settings['trace_enabled']:
-            ts.vAddLoggerCallback(tox_options)
-            LOG_INFO("c-toxcore trace_enabled enabled" )
-        else:
-            LOG_WARN("No tox_options._options_pointer to add self_logger_cb" )
-
-        retval = toxygen_wrapper.tox.Tox(tox_options)
-    except Exception as e:
-        if app and hasattr(app, '_log'):
-            pass
-        LOG_ERROR(f"toxygen_wrapper.tox.Tox failed:  {e}")
-        LOG_WARN(traceback.format_exc())
-        raise
-
-    if app and hasattr(app, '_log'):
-        app._log("DEBUG: toxygen_wrapper.tox.Tox succeeded")
-    return retval
diff --git a/toxygen/network/__init__.py b/toxygen/network/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/network/tox_dns.py b/toxygen/network/tox_dns.py
deleted file mode 100644
index 58c9da1..0000000
--- a/toxygen/network/tox_dns.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import json
-import urllib.request
-import logging
-
-try:
-    import requests
-except ImportError:
-    requests = None
-from  qtpy import QtNetwork, QtCore
-
-import utils.util as util
-
-global LOG
-
-iTIMEOUT=60
-LOG = logging.getLogger('app.'+__name__)
-
-class ToxDns:
-
-    def __init__(self, settings, log=None):
-        self._settings = settings
-        self._log = log
-
-    @staticmethod
-    def _send_request(url, data):
-        if requests:
-            LOG.info('send_request loading with requests: ' + str(url))
-            headers = dict()
-            headers['Content-Type'] = 'application/json'
-            req = requests.get(url, headers=headers)
-            if req.status_code < 300:
-                retval = req.content
-            else:
-                raise LookupError(str(req.status_code))
-        else:
-            req = urllib.request.Request(url)
-            req.add_header('Content-Type', 'application/json')
-            response = urllib.request.urlopen(req, bytes(json.dumps(data), 'utf-8'))
-            retval = response.read()
-        res = json.loads(str(retval, 'utf-8'))
-        if not res['c']:
-            return res['tox_id']
-        else:
-            raise LookupError()
-
-    def lookup(self, email):
-        """
-        TOX DNS 4
-        :param email: data like 'groupbot@toxme.io'
-        :return: tox id on success else None
-        """
-        site = email.split('@')[1]
-        data = {"action": 3, "name": "{}".format(email)}
-        urls = ('https://{}/api'.format(site), 'http://{}/api'.format(site))
-        if requests:
-            for url in urls:
-                LOG.info('TOX nodes loading with requests: ' + str(url))
-                try:
-                    headers = dict()
-                    headers['Content-Type'] = 'application/json'
-                    req = requests.get(url, headers=headers, timeout=iTIMEOUT)
-                    if req.status_code < 300:
-                        result = req.content
-                        return result
-                except Exception as ex:
-                    LOG.error('ERROR: TOX DNS loading error with requests: ' + str(ex))
-
-        elif not self._settings['proxy_type']:  # no proxy
-            for url in urls:
-                try:
-                    return self._send_request(url, data)
-                except Exception as ex:
-                    LOG.error('ERROR: TOX DNS ' + str(ex))
-        else:  # proxy
-            netman = QtNetwork.QNetworkAccessManager()
-            proxy = QtNetwork.QNetworkProxy()
-            if self._settings['proxy_type'] == 2:
-                proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy)
-            else:
-                proxy.setType(QtNetwork.QNetworkProxy.HttpProxy)
-            proxy.setHostName(self._settings['proxy_host'])
-            proxy.setPort(self._settings['proxy_port'])
-            netman.setProxy(proxy)
-            for url in urls:
-                try:
-                    request = QtNetwork.QNetworkRequest()
-                    request.setUrl(QtCore.QUrl(url))
-                    request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, "application/json")
-                    reply = netman.post(request, bytes(json.dumps(data), 'utf-8'))
-
-                    while not reply.isFinished():
-                        QtCore.QThread.msleep(1)
-                        QtCore.QCoreApplication.processEvents()
-                    data = bytes(reply.readAll().data())
-                    result = json.loads(str(data, 'utf-8'))
-                    if not result['c']:
-                        return result['tox_id']
-                except Exception as ex:
-                    LOG.error('ERROR: TOX DNS ' + str(ex))
-
-        return None  # error
diff --git a/toxygen/notifications.py b/toxygen/notifications.py
new file mode 100644
index 0000000..81a8a05
--- /dev/null
+++ b/toxygen/notifications.py
@@ -0,0 +1,75 @@
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+from util import curr_directory
+import wave
+import pyaudio
+
+
+SOUND_NOTIFICATION = {
+    'MESSAGE': 0,
+    'FRIEND_CONNECTION_STATUS': 1,
+    'FILE_TRANSFER': 2
+}
+
+
+def tray_notification(title, text, tray, window):
+    """
+    Show tray notification and activate window icon
+    NOTE: different behaviour on different OS
+    :param title: Name of user who sent message or file
+    :param text: text of message or file info
+    :param tray: ref to tray icon
+    :param window: main window
+    """
+    if QtGui.QSystemTrayIcon.isSystemTrayAvailable():
+        if len(text) > 30:
+            text = text[:27] + '...'
+        tray.showMessage(title, text, QtGui.QSystemTrayIcon.NoIcon, 3000)
+        QtGui.QApplication.alert(window, 0)
+
+        def message_clicked():
+            window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
+            window.activateWindow()
+        tray.connect(tray, QtCore.SIGNAL("messageClicked()"), message_clicked)
+
+
+class AudioFile:
+    chunk = 1024
+
+    def __init__(self, fl):
+        self.wf = wave.open(fl, 'rb')
+        self.p = pyaudio.PyAudio()
+        self.stream = self.p.open(
+            format=self.p.get_format_from_width(self.wf.getsampwidth()),
+            channels=self.wf.getnchannels(),
+            rate=self.wf.getframerate(),
+            output=True
+        )
+
+    def play(self):
+        data = self.wf.readframes(self.chunk)
+        while data:
+            self.stream.write(data)
+            data = self.wf.readframes(self.chunk)
+
+    def close(self):
+        self.stream.close()
+        self.p.terminate()
+
+
+def sound_notification(t):
+    """
+    Plays sound notification
+    :param t: type of notification
+    """
+    if t == SOUND_NOTIFICATION['MESSAGE']:
+        f = curr_directory() + '/sounds/message.wav'
+    elif t == SOUND_NOTIFICATION['FILE_TRANSFER']:
+        f = curr_directory() + '/sounds/file.wav'
+    else:
+        f = curr_directory() + '/sounds/contact.wav'
+    a = AudioFile(f)
+    a.play()
+    a.close()
diff --git a/toxygen/notifications/__init__.py b/toxygen/notifications/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/notifications/sound.py b/toxygen/notifications/sound.py
deleted file mode 100644
index 9df46b2..0000000
--- a/toxygen/notifications/sound.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import os.path
-import wave
-
-import utils.util
-
-import toxygen_wrapper.tests.support_testing as ts
-with ts.ignoreStderr():
-    import pyaudio
-
-global LOG
-import logging
-LOG = logging.getLogger('app.'+__name__)
-
-SOUND_NOTIFICATION = {
-    'MESSAGE': 0,
-    'FRIEND_CONNECTION_STATUS': 1,
-    'FILE_TRANSFER': 2
-}
-
-
-class AudioFile:
-    chunk = 1024
-
-    def __init__(self, fl):
-        self.wf = wave.open(fl, 'rb')
-        self.p = pyaudio.PyAudio()
-        self.stream = self.p.open(
-            format=self.p.get_format_from_width(self.wf.getsampwidth()),
-            channels=self.wf.getnchannels(),
-            rate=self.wf.getframerate(),
-            output=True)
-
-    def play(self):
-        data = self.wf.readframes(self.chunk)
-        try:
-            while data:
-                self.stream.write(data)
-                data = self.wf.readframes(self.chunk)
-        except Exception as e:
-            LOG.error(f"Error during AudioFile play {e}")
-            LOG.debug("Error during AudioFile play " \
-                      +' rate=' +str(self.wf.getframerate()) \
-                      + 'format=' +str(self.p.get_format_from_width(self.wf.getsampwidth())) \
-                      +' channels=' +str(self.wf.getnchannels()) \
-                      )
-
-            raise
-
-    def close(self):
-        self.stream.close()
-        self.p.terminate()
-
-
-def sound_notification(t):
-    """
-    Plays sound notification
-    :param t: type of notification
-    """
-    if t == SOUND_NOTIFICATION['MESSAGE']:
-        f = get_file_path('message.wav')
-    elif t == SOUND_NOTIFICATION['FILE_TRANSFER']:
-        f = get_file_path('file.wav')
-    else:
-        f = get_file_path('contact.wav')
-    a = AudioFile(f)
-    a.play()
-    a.close()
-
-
-def get_file_path(file_name):
-    return os.path.join(utils.util.get_sounds_directory(), file_name)
diff --git a/toxygen/notifications/tray.py b/toxygen/notifications/tray.py
deleted file mode 100644
index 0a6bca3..0000000
--- a/toxygen/notifications/tray.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import QtCore, QtWidgets
-
-def tray_notification(title, text, tray, window):
-    """
-    Show tray notification and activate window icon
-    NOTE: different behaviour on different OS
-    :param title: Name of user who sent message or file
-    :param text: text of message or file info
-    :param tray: ref to tray icon
-    :param window: main window
-    """
-    if tray and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable():
-        if len(text) > 30:
-            text = text[:27] + '...'
-        tray.showMessage(title, text, QtWidgets.QSystemTrayIcon.NoIcon, 3000)
-        QtWidgets.QApplication.alert(window, 0)
-
-        def message_clicked():
-            window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
-            window.activateWindow()
-        tray.messageClicked.connect(message_clicked)
diff --git a/toxygen/ui/password_screen.py b/toxygen/passwordscreen.py
similarity index 56%
rename from toxygen/ui/password_screen.py
rename to toxygen/passwordscreen.py
index 57f7b95..dcd9d05 100644
--- a/toxygen/ui/password_screen.py
+++ b/toxygen/passwordscreen.py
@@ -1,33 +1,28 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
+from widgets import CenteredWidget, LineEdit
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
 
-import logging
-from  qtpy import QtCore, QtWidgets
-
-from ui.widgets import CenteredWidget, LineEdit, DialogWithResult
-import utils.ui as util_ui
-
-global LOG
-LOG = logging.getLogger('app.'+__name__)
 
 class PasswordArea(LineEdit):
 
     def __init__(self, parent):
-        super().__init__(parent)
-        self._parent = parent
-        self.setEchoMode(QtWidgets.QLineEdit.Password)
+        super(PasswordArea, self).__init__(parent)
+        self.parent = parent
+        self.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
 
     def keyPressEvent(self, event):
         if event.key() == QtCore.Qt.Key_Return:
-            self._parent.button_click()
+            self.parent.button_click()
         else:
-            super().keyPressEvent(event)
+            super(PasswordArea, self).keyPressEvent(event)
 
 
-class PasswordScreenBase(CenteredWidget, DialogWithResult):
+class PasswordScreenBase(CenteredWidget):
 
     def __init__(self, encrypt):
-        CenteredWidget.__init__(self)
-        DialogWithResult.__init__(self)
+        super(PasswordScreenBase, self).__init__()
         self._encrypt = encrypt
         self.initUI()
 
@@ -36,18 +31,18 @@ class PasswordScreenBase(CenteredWidget, DialogWithResult):
         self.setMinimumSize(QtCore.QSize(360, 170))
         self.setMaximumSize(QtCore.QSize(360, 170))
 
-        self.enter_pass = QtWidgets.QLabel(self)
+        self.enter_pass = QtGui.QLabel(self)
         self.enter_pass.setGeometry(QtCore.QRect(30, 10, 300, 30))
 
         self.password = PasswordArea(self)
         self.password.setGeometry(QtCore.QRect(30, 50, 300, 30))
 
-        self.button = QtWidgets.QPushButton(self)
+        self.button = QtGui.QPushButton(self)
         self.button.setGeometry(QtCore.QRect(30, 90, 300, 30))
-        self.button.setText(util_ui.tr('OK'))
+        self.button.setText('OK')
         self.button.clicked.connect(self.button_click)
 
-        self.warning = QtWidgets.QLabel(self)
+        self.warning = QtGui.QLabel(self)
         self.warning.setGeometry(QtCore.QRect(30, 130, 300, 30))
         self.warning.setStyleSheet('QLabel { color: #F70D1A; }')
         self.warning.setVisible(False)
@@ -66,28 +61,28 @@ class PasswordScreenBase(CenteredWidget, DialogWithResult):
             super(PasswordScreenBase, self).keyPressEvent(event)
 
     def retranslateUi(self):
-        self.setWindowTitle(util_ui.tr('Enter password'))
-        self.enter_pass.setText(util_ui.tr('Password:'))
-        self.warning.setText(util_ui.tr('Incorrect password'))
+        self.setWindowTitle(QtGui.QApplication.translate("pass", "Enter password", None, QtGui.QApplication.UnicodeUTF8))
+        self.enter_pass.setText(QtGui.QApplication.translate("pass", "Password:", None, QtGui.QApplication.UnicodeUTF8))
+        self.warning.setText(QtGui.QApplication.translate("pass", "Incorrect password", None, QtGui.QApplication.UnicodeUTF8))
 
 
 class PasswordScreen(PasswordScreenBase):
 
     def __init__(self, encrypt, data):
-        super().__init__(encrypt)
+        super(PasswordScreen, self).__init__(encrypt)
         self._data = data
 
     def button_click(self):
         if self.password.text():
             try:
                 self._encrypt.set_password(self.password.text())
-                new_data = self._encrypt.pass_decrypt(self._data)
+                new_data = self._encrypt.pass_decrypt(self._data[0])
             except Exception as ex:
                 self.warning.setVisible(True)
-                LOG.error(f"Decryption error:  {ex}")
                 print('Decryption error:', ex)
             else:
-                self.close_with_result(new_data)
+                self._data[0] = new_data
+                self.close()
 
 
 class UnlockAppScreen(PasswordScreenBase):
@@ -121,31 +116,37 @@ class SetProfilePasswordScreen(CenteredWidget):
         self.setMaximumSize(QtCore.QSize(700, 200))
         self.password = LineEdit(self)
         self.password.setGeometry(QtCore.QRect(40, 10, 300, 30))
-        self.password.setEchoMode(QtWidgets.QLineEdit.Password)
+        self.password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
         self.confirm_password = LineEdit(self)
         self.confirm_password.setGeometry(QtCore.QRect(40, 50, 300, 30))
-        self.confirm_password.setEchoMode(QtWidgets.QLineEdit.Password)
-        self.set_password = QtWidgets.QPushButton(self)
+        self.confirm_password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
+        self.set_password = QtGui.QPushButton(self)
         self.set_password.setGeometry(QtCore.QRect(40, 100, 300, 30))
         self.set_password.clicked.connect(self.new_password)
-        self.not_match = QtWidgets.QLabel(self)
+        self.not_match = QtGui.QLabel(self)
         self.not_match.setGeometry(QtCore.QRect(350, 50, 300, 30))
         self.not_match.setVisible(False)
         self.not_match.setStyleSheet('QLabel { color: #BC1C1C; }')
-        self.warning = QtWidgets.QLabel(self)
+        self.warning = QtGui.QLabel(self)
         self.warning.setGeometry(QtCore.QRect(40, 160, 500, 30))
         self.warning.setStyleSheet('QLabel { color: #BC1C1C; }')
 
     def retranslateUi(self):
-        self.setWindowTitle(util_ui.tr('Profile password'))
+        self.setWindowTitle(QtGui.QApplication.translate("PasswordScreen", "Profile password", None,
+                                                              QtGui.QApplication.UnicodeUTF8))
         self.password.setPlaceholderText(
-            util_ui.tr('Password (at least 8 symbols)'))
+            QtGui.QApplication.translate("PasswordScreen", "Password (at least 8 symbols)", None,
+                                         QtGui.QApplication.UnicodeUTF8))
         self.confirm_password.setPlaceholderText(
-            util_ui.tr('Confirm password'))
+            QtGui.QApplication.translate("PasswordScreen", "Confirm password", None,
+                                         QtGui.QApplication.UnicodeUTF8))
         self.set_password.setText(
-            util_ui.tr('Set password'))
-        self.not_match.setText(util_ui.tr('Passwords do not match'))
-        self.warning.setText(util_ui.tr('There is no way to recover lost passwords'))
+            QtGui.QApplication.translate("PasswordScreen", "Set password", None, QtGui.QApplication.UnicodeUTF8))
+        self.not_match.setText(QtGui.QApplication.translate("PasswordScreen", "Passwords do not match", None,
+                                                            QtGui.QApplication.UnicodeUTF8))
+        self.warning.setText(
+            QtGui.QApplication.translate("PasswordScreen", "There is no way to recover lost passwords", None,
+                                         QtGui.QApplication.UnicodeUTF8))
 
     def new_password(self):
         if self.password.text() == self.confirm_password.text():
@@ -153,8 +154,11 @@ class SetProfilePasswordScreen(CenteredWidget):
                 self._encrypt.set_password(self.password.text())
                 self.close()
             else:
-                self.not_match.setText(util_ui.tr('Password must be at least 8 symbols'))
+                self.not_match.setText(
+                    QtGui.QApplication.translate("PasswordScreen", "Password must be at least 8 symbols", None,
+                                                 QtGui.QApplication.UnicodeUTF8))
             self.not_match.setVisible(True)
         else:
-            self.not_match.setText(util_ui.tr('Passwords do not match'))
+            self.not_match.setText(QtGui.QApplication.translate("PasswordScreen", "Passwords do not match", None,
+                                                                QtGui.QApplication.UnicodeUTF8))
             self.not_match.setVisible(True)
diff --git a/toxygen/plugin_support.py b/toxygen/plugin_support.py
new file mode 100644
index 0000000..944c353
--- /dev/null
+++ b/toxygen/plugin_support.py
@@ -0,0 +1,157 @@
+import util
+import profile
+import os
+import importlib
+import inspect
+import plugins.plugin_super_class as pl
+import toxencryptsave
+import sys
+
+
+class PluginLoader(util.Singleton):
+
+    def __init__(self, tox, settings):
+        super().__init__()
+        self._profile = profile.Profile.get_instance()
+        self._settings = settings
+        self._plugins = {}  # dict. key - plugin unique short name, value - tuple (plugin instance, is active)
+        self._tox = tox
+        self._encr = toxencryptsave.ToxEncryptSave.get_instance()
+
+    def set_tox(self, tox):
+        """
+        New tox instance
+        """
+        self._tox = tox
+        for value in self._plugins.values():
+            value[0].set_tox(tox)
+
+    def load(self):
+        """
+        Load all plugins in plugins folder
+        """
+        path = util.curr_directory() + '/plugins/'
+        if not os.path.exists(path):
+            util.log('Plugin dir not found')
+            return
+        else:
+            sys.path.append(path)
+        files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
+        for fl in files:
+            if fl in ('plugin_super_class.py', '__init__.py') or not fl.endswith('.py'):
+                continue
+            name = fl[:-3]  # module name without .py
+            try:
+                module = importlib.import_module(name)  # import plugin
+            except ImportError:
+                util.log('Import error in module ' + name)
+                continue
+            except Exception as ex:
+                util.log('Exception in module ' + name + ' Exception: ' + str(ex))
+                continue
+            for elem in dir(module):
+                obj = getattr(module, elem)
+                if inspect.isclass(obj) and hasattr(obj, 'is_plugin') and obj.is_plugin:  # looking for plugin class in module
+                    print('Plugin', elem)
+                    try:  # create instance of plugin class
+                        inst = obj(self._tox, self._profile, self._settings, self._encr)
+                        autostart = inst.get_short_name() in self._settings['plugins']
+                        if autostart:
+                            inst.start()
+                    except Exception as ex:
+                        util.log('Exception in module ' + name + ' Exception: ' + str(ex))
+                        continue
+                    self._plugins[inst.get_short_name()] = [inst, autostart]  # (inst, is active)
+                    break
+
+    def callback_lossless(self, friend_number, data, length):
+        """
+        New incoming custom lossless packet (callback)
+        """
+        l = data[0] - pl.LOSSLESS_FIRST_BYTE
+        name = ''.join(chr(x) for x in data[1:l + 1])
+        if name in self._plugins and self._plugins[name][1]:
+            self._plugins[name][0].lossless_packet(''.join(chr(x) for x in data[l + 1:length]), friend_number)
+
+    def callback_lossy(self, friend_number, data, length):
+        """
+        New incoming custom lossy packet (callback)
+        """
+        l = data[0] - pl.LOSSY_FIRST_BYTE
+        name = ''.join(chr(x) for x in data[1:l + 1])
+        if name in self._plugins and self._plugins[name][1]:
+            self._plugins[name][0].lossy_packet(''.join(chr(x) for x in data[l + 1:length]), friend_number)
+
+    def friend_online(self, friend_number):
+        """
+        Friend with specified number is online
+        """
+        for elem in self._plugins.values():
+            if elem[1]:
+                elem[0].friend_connected(friend_number)
+
+    def get_plugins_list(self):
+        """
+        Returns list of all plugins
+        """
+        result = []
+        for data in self._plugins.values():
+            result.append([data[0].get_name(),  # plugin full name
+                           data[1],  # is enabled
+                           data[0].get_description(),  # plugin description
+                           data[0].get_short_name()])  # key - short unique name
+        return result
+
+    def plugin_window(self, key):
+        """
+        Return window or None for specified plugin
+        """
+        return self._plugins[key][0].get_window()
+
+    def toggle_plugin(self, key):
+        """
+        Enable/disable plugin
+        :param key: plugin short name
+        """
+        plugin = self._plugins[key]
+        if plugin[1]:
+            plugin[0].stop()
+        else:
+            plugin[0].start()
+        plugin[1] = not plugin[1]
+        if plugin[1]:
+            self._settings['plugins'].append(key)
+        else:
+            self._settings['plugins'].remove(key)
+        self._settings.save()
+
+    def command(self, text):
+        """
+        New command for plugin
+        """
+        text = text.strip()
+        name = text.split()[0]
+        if name in self._plugins and self._plugins[name][1]:
+            self._plugins[name][0].command(text[len(name) + 1:])
+
+    def get_menu(self, menu, num):
+        """
+        Return list of items for menu
+        """
+        result = []
+        for elem in self._plugins.values():
+            if elem[1]:
+                try:
+                    result.extend(elem[0].get_menu(menu, num))
+                except:
+                    continue
+        return result
+
+    def stop(self):
+        """
+        App is closing, stop all plugins
+        """
+        for key in list(self._plugins.keys()):
+            if self._plugins[key][1]:
+                self._plugins[key][0].close()
+            del self._plugins[key]
diff --git a/toxygen/plugin_support/__init__.py b/toxygen/plugin_support/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/plugin_support/plugin_support.py b/toxygen/plugin_support/plugin_support.py
deleted file mode 100644
index f180e4d..0000000
--- a/toxygen/plugin_support/plugin_support.py
+++ /dev/null
@@ -1,231 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import os
-import importlib
-import inspect
-import sys
-import logging
-
-import utils.util as util
-import plugins.plugin_super_class as pl
-
-# LOG=util.log
-global LOG
-LOG = logging.getLogger('plugin_support')
-def trace(msg, *args, **kwargs): LOG._log(0, msg, [])
-LOG.trace = trace
-
-log = lambda x: LOG.info(x)
-
-class Plugin:
-
-    def __init__(self, plugin, is_active):
-        self._instance = plugin
-        self._is_active = is_active
-
-    def get_instance(self):
-        return self._instance
-
-    instance = property(get_instance)
-
-    def get_is_active(self):
-        return self._is_active
-
-    def set_is_active(self, is_active):
-        self._is_active = is_active
-
-    is_active = property(get_is_active, set_is_active)
-
-
-class PluginLoader:
-
-    def __init__(self, settings, app):
-        self._settings = settings
-        self._app = app
-        self._plugins = {}  # dict. key - plugin unique short name, value - Plugin instance
-
-    def set_tox(self, tox) -> None:
-        """
-        New tox instance
-        """
-        for plugin in self._plugins.values():
-            plugin.instance.set_tox(tox)
-
-    def load(self) -> None:
-        """
-        Load all plugins in plugins folder
-        """
-        path = util.get_plugins_directory()
-        if not os.path.exists(path):
-            self._app._LOG('WARN: Plugin directory not found: ' + path)
-            return
-
-        sys.path.append(path)
-        files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
-        for fl in files:
-            if fl in ('plugin_super_class.py', '__init__.py') or not fl.endswith('.py'):
-                continue
-            base_name = fl[:-3]  # module name without .py
-            try:
-                module = importlib.import_module(base_name)  # import plugin
-                LOG.trace('Imported module: ' +base_name +' file: ' +fl)
-            except ImportError as e:
-                LOG.warn(f"Import error:  {e}" +' file: ' +fl)
-                continue
-            except Exception as ex:
-                LOG.error('importing ' + base_name + ' Exception: ' + str(ex))
-                continue
-            for elem in dir(module):
-                obj = getattr(module, elem)
-                # looking for plugin class in module
-                if not inspect.isclass(obj) or not hasattr(obj, 'is_plugin') or not obj.is_plugin:
-                    continue
-                try:  # create instance of plugin class
-                    instance = obj(self._app) # name, short_name, app
-                    # needed by bday...
-                    instance._profile=self._app._ms._profile
-                    instance._settings=self._settings
-                    short_name = instance.get_short_name()
-                    is_active = short_name in self._settings['plugins']
-                    if is_active:
-                        try:
-                            instance.start()
-                            self._app._log('INFO: Started Plugin ' +short_name)
-                        except Exception as e:
-                            self._app._log.error(f"Starting Plugin ' +short_name +'  {e}")
-                    # else: LOG.info('Defined Plugin ' +short_name)
-                except Exception as ex:
-                    LOG.error('in module ' + short_name + ' Exception: ' + str(ex))
-                    continue
-                short_name = instance.get_short_name()
-                self._plugins[short_name] = Plugin(instance, is_active)
-                LOG.info('Added plugin: ' +short_name +' from file: ' +fl)
-                break
-
-    def callback_lossless(self, friend_number, data) -> None:
-        """
-        New incoming custom lossless packet (callback)
-        """
-        l = data[0] - pl.LOSSLESS_FIRST_BYTE
-        name = ''.join(chr(x) for x in data[1:l + 1])
-        if name in self._plugins and self._plugins[name].is_active:
-            self._plugins[name].instance.lossless_packet(''.join(chr(x) for x in data[l + 1:]), friend_number)
-
-    def callback_lossy(self, friend_number, data):
-        """
-        New incoming custom lossy packet (callback)
-        """
-        l = data[0] - pl.LOSSY_FIRST_BYTE
-        name = ''.join(chr(x) for x in data[1:l + 1])
-        if name in self._plugins and self._plugins[name].is_active:
-            self._plugins[name].instance.lossy_packet(''.join(chr(x) for x in data[l + 1:]), friend_number)
-
-    def friend_online(self, friend_number:int) -> None:
-        """
-        Friend with specified number is online
-        """
-        for plugin in self._plugins.values():
-            if plugin.is_active:
-                plugin.instance.friend_connected(friend_number)
-
-    def get_plugins_list(self) -> list:
-        """
-        Returns list of all plugins
-        """
-        result = []
-        for plugin in self._plugins.values():
-            try:
-                result.append([plugin.instance.get_name(),  # plugin full name
-                               plugin.is_active,            # is enabled
-                               plugin.instance.get_description(),  # plugin description
-                               plugin.instance.get_short_name()])  # key - short unique name
-            except:
-                continue
-
-        return result
-
-    def plugin_window(self, key):
-        """
-        Return window or None for specified plugin
-        """
-        try:
-            if key in self._plugins and hasattr(self._plugins[key], 'instance'):
-                return self._plugins[key].instance.get_window()
-        except Exception as e:
-            self._app._log('WARN: ' +key +' _plugins no slot instance: ' +str(e))
-
-        return None
-
-    def toggle_plugin(self, key) -> None:
-        """
-        Enable/disable plugin
-        :param key: plugin short name
-        """
-        plugin = self._plugins[key]
-        if plugin.is_active:
-            plugin.instance.stop()
-        else:
-            plugin.instance.start()
-        plugin.is_active = not plugin.is_active
-        if plugin.is_active:
-            self._settings['plugins'].append(key)
-        else:
-            self._settings['plugins'].remove(key)
-        self._settings.save()
-
-    def command(self, text) -> None:
-        """
-        New command for plugin
-        """
-        text = text.strip()
-        name = text.split()[0]
-        if name in self._plugins and self._plugins[name].is_active:
-            self._plugins[name].instance.command(text[len(name) + 1:])
-
-    def get_menu(self, num):
-        """
-        Return list of items for menu
-        """
-        result = []
-        for plugin in self._plugins.values():
-            if not plugin.is_active:
-                continue
-
-            try:
-                result.extend(plugin.instance.get_menu(num))
-            except:
-                continue
-        return result
-
-    def get_message_menu(self, menu, selected_text):
-        result = []
-        for plugin in self._plugins.values():
-            if not plugin.is_active:
-                continue
-            if not hasattr(plugin.instance, 'get_message_menu'):
-                name = plugin.instance.get_short_name()
-                self._app._log('WARN: get_message_menu not found: ' + name)
-                continue
-            try:
-                result.extend(plugin.instance.get_message_menu(menu, selected_text))
-            except:
-                pass
-        return result
-
-    def stop(self) -> None:
-        """
-        App is closing, stop all plugins
-        """
-        for key in list(self._plugins.keys()):
-            if self._plugins[key].is_active:
-                self._plugins[key].instance.close()
-            del self._plugins[key]
-
-    def reload(self) -> None:
-        path = util.get_plugins_directory()
-        if not os.path.exists(path):
-            self._app._log('WARN: Plugin directory not found: ' + path)
-            return
-
-        self.stop()
-        self._app._log('INFO: Reloading plugins from ' +path)
-        self.load()
diff --git a/toxygen/plugins/README.md b/toxygen/plugins/README.md
deleted file mode 100644
index 12ed7b0..0000000
--- a/toxygen/plugins/README.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Plugins
-
-Repo with plugins for [Toxygen](https://macaw.me/emdee/toxygen/)
-
-For more info visit [plugins.md](https://macaw.me/emdee/toxygen/blob/master/docs/plugins.md) and [plugin_api.md](https://github.com/toxygen-project[/toxygen/blob/master/docs/plugin-api.md)
-
-# Plugins list:
-
-- ToxId - share your Tox ID and copy friend's Tox ID easily.
-- MarqueeStatus - create ticker from your status message.
-- BirthDay - get notifications on your friends' birthdays.
-- Bot - bot which can communicate with your friends when you are away.
-- SearchPlugin - select text in message and find it in search engine.
-- AutoAwayStatusLinux - sets "Away" status when user is inactive (Linux only).
-- AutoAwayStatusWindows - sets "Away" status when user is inactive (Windows only).
-- Chess - play chess with your friends using Tox.
-- Garland - changes your status like it's garland.
-- AutoAnswer - calls auto answering.
-- uToxInlineSending - send inlines with the same name as uTox does.
-- AvatarEncryption - encrypt all avatars using profile password
-
-## Hard fork
-
-Not all of these are working...
-
-Work on this project is suspended until the
-[MultiDevice](https://git.plastiras.org/emdee/tox_profile/wiki/MultiDevice-Announcements-POC) problem is solved. Fork me!
diff --git a/toxygen/plugins/ae.py b/toxygen/plugins/ae.py
deleted file mode 100644
index b30ea66..0000000
--- a/toxygen/plugins/ae.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import json
-import os
-from qtpy import QtWidgets
-
-from bootstrap.bootstrap import get_user_config_path
-from user_data import settings
-import plugin_super_class
-
-class AvatarEncryption(plugin_super_class.PluginSuperClass):
-
-    def __init__(self, *args):
-        super(AvatarEncryption, self).__init__('AvatarEncryption', 'ae', *args)
-        self._path = os.path.join(get_user_config_path(), 'avatars')
-        self._app = args[0]
-        self._profile = self._app._ms._profile
-        self._window = None
-        #was self._contacts = self._profile._contacts[:]
-        self._contacts = self._profile._contacts_provider.get_all_friends()
-
-    def get_description(self):
-        return QtWidgets.QApplication.translate("AvatarEncryption", 'Encrypt all avatars using profile password.')
-
-    def close(self):
-        if not self._encrypt_save.has_password():
-            return
-        i, data = 1, {}
-
-        self.save_contact_avatar(data, self._profile, 0)
-        for friend in self._contacts:
-            self.save_contact_avatar(data, friend, i)
-            i += 1
-        self.save_settings(json.dumps(data))
-
-    def start(self):
-        if not self._encrypt_save.has_password():
-            return
-        data = json.loads(self.load_settings())
-
-        self.load_contact_avatar(data, self._profile)
-        for friend in self._contacts:
-            self.load_contact_avatar(data, friend)
-        self._profile.update()
-
-    def save_contact_avatar(self, data, contact, i):
-        tox_id = contact.tox_id[:64]
-        data[str(tox_id)] = str(i)
-        path = os.path.join(self._path, tox_id + '.png')
-        if os.path.isfile(path):
-            with open(path, 'rb') as fl:
-                avatar = fl.read()
-            encr_avatar = self._encrypt_save.pass_encrypt(avatar)
-            with open(os.path.join(self._path, self._settings.name + '_' + str(i) + '.png'), 'wb') as fl:
-                fl.write(encr_avatar)
-            os.remove(path)
-
-    def load_contact_avatar(self, data, contact):
-        tox_id = str(contact.tox_id[:64])
-        if tox_id not in data:
-            return
-        path = os.path.join(self._path, self._settings.name + '_' + data[tox_id] + '.png')
-        if os.path.isfile(path):
-            with open(path, 'rb') as fl:
-                avatar = fl.read()
-            decr_avatar = self._encrypt_save.pass_decrypt(avatar)
-            with open(os.path.join(self._path, str(tox_id) + '.png'), 'wb') as fl:
-                fl.write(decr_avatar)
-            os.remove(path)
-            contact.load_avatar()
-
-    def load_settings(self):
-        try:
-            with open(plugin_super_class.path_to_data(self._short_name) + self._settings.name + '.json', 'rb') as fl:
-                data = fl.read()
-            return str(self._encrypt_save.pass_decrypt(data), 'utf-8') if data else '{}'
-        except:
-            return '{}'
-
-    def save_settings(self, data):
-        try:
-            data = self._encrypt_save.pass_encrypt(bytes(data, 'utf-8'))
-            with open(plugin_super_class.path_to_data(self._short_name) + self._settings.name + '.json', 'wb') as fl:
-                fl.write(data)
-        except:
-            pass
diff --git a/toxygen/plugins/awayl.py b/toxygen/plugins/awayl.py
deleted file mode 100644
index 9b63720..0000000
--- a/toxygen/plugins/awayl.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import plugin_super_class
-import threading
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import json
-from subprocess import check_output
-import time
-
-from  qtpy import QtCore, QtWidgets
-
-
-class InvokeEvent(QtCore.QEvent):
-    EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
-
-    def __init__(self, fn, *args, **kwargs):
-        QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
-        self.fn = fn
-        self.args = args
-        self.kwargs = kwargs
-
-
-class Invoker(QtCore.QObject):
-
-    def event(self, event):
-        event.fn(*event.args, **event.kwargs)
-        return True
-
-_invoker = Invoker()
-
-
-def invoke_in_main_thread(fn, *args, **kwargs):
-    QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
-
-
-class AutoAwayStatusLinux(plugin_super_class.PluginSuperClass):
-
-    def __init__(self, *args):
-        super().__init__('AutoAwayStatusLinux', 'awayl', *args)
-        self._thread = None
-        self._exec = None
-        self._active = False
-        self._time = json.loads(self.load_settings())['time']
-        self._prev_status = 0
-        self._app = args[0]
-        self._profile=self._app._ms._profile
-        self._window = None
-
-    def get_description(self):
-        return QApplication.translate("AutoAwayStatusLinux", 'sets "Away" status when user is inactive (Linux only).')
-
-    def close(self):
-        self.stop()
-
-    def stop(self):
-        self._exec = False
-        if self._active:
-            self._thread.join()
-
-    def start(self):
-        self._exec = True
-        self._thread = threading.Thread(target=self.loop)
-        self._thread.start()
-
-    def save(self):
-        self.save_settings('{"time": ' + str(self._time) + '}')
-
-    def change_status(self, status=1):
-        if self._profile.status in (0, 2):
-            self._prev_status = self._profile.status
-        if status is not None:
-            invoke_in_main_thread(self._profile.set_status, status)
-
-    def get_window(self):
-        inst = self
-
-        class Window(QtWidgets.QWidget):
-            def __init__(self):
-                super(Window, self).__init__()
-                self.setGeometry(QtCore.QRect(450, 300, 350, 100))
-                self.label = QtWidgets.QLabel(self)
-                self.label.setGeometry(QtCore.QRect(20, 0, 310, 35))
-                self.label.setText(QtWidgets.QApplication.translate("AutoAwayStatusLinux", "Auto away time in minutes\n(0 - to disable)"))
-                self.time = QtWidgets.QLineEdit(self)
-                self.time.setGeometry(QtCore.QRect(20, 40, 310, 25))
-                self.time.setText(str(inst._time))
-                self.setWindowTitle("AutoAwayStatusLinux")
-                self.ok = QtWidgets.QPushButton(self)
-                self.ok.setGeometry(QtCore.QRect(20, 70, 310, 25))
-                self.ok.setText(
-                    QtWidgets.QApplication.translate("AutoAwayStatusLinux", "Save"))
-                self.ok.clicked.connect(self.update)
-
-            def update(self):
-                try:
-                    t = int(self.time.text())
-                except:
-                    t = 0
-                inst._time = t
-                inst.save()
-                self.close()
-
-        return Window()
-
-    def loop(self):
-        self._active = True
-        while self._exec:
-            time.sleep(5)
-            d = check_output(['xprintidle'])
-            d = int(d) // 1000
-            if self._time:
-                if d > 60 * self._time:
-                    self.change_status()
-                elif self._profile.status == 1:
-                    self.change_status(self._prev_status)
diff --git a/toxygen/plugins/awayw.py.windows b/toxygen/plugins/awayw.py.windows
deleted file mode 100644
index 5c4b768..0000000
--- a/toxygen/plugins/awayw.py.windows
+++ /dev/null
@@ -1,115 +0,0 @@
-import plugin_super_class
-import threading
-import time
-from PyQt5 import QtCore, QtWidgets
-from ctypes import Structure, windll, c_uint, sizeof, byref
-import json
-
-
-class LASTINPUTINFO(Structure):
-    _fields_ = [('cbSize', c_uint), ('dwTime', c_uint)]
-
-
-def get_idle_duration():
-    lastInputInfo = LASTINPUTINFO()
-    lastInputInfo.cbSize = sizeof(lastInputInfo)
-    windll.user32.GetLastInputInfo(byref(lastInputInfo))
-    millis = windll.kernel32.GetTickCount() - lastInputInfo.dwTime
-    return millis / 1000.0
-
-
-class InvokeEvent(QtCore.QEvent):
-    EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
-
-    def __init__(self, fn, *args, **kwargs):
-        QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
-        self.fn = fn
-        self.args = args
-        self.kwargs = kwargs
-
-
-class Invoker(QtCore.QObject):
-
-    def event(self, event):
-        event.fn(*event.args, **event.kwargs)
-        return True
-
-_invoker = Invoker()
-
-
-def invoke_in_main_thread(fn, *args, **kwargs):
-    QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
-
-
-class AutoAwayStatusWindows(plugin_super_class.PluginSuperClass):
-
-    def __init__(self, *args):
-        super().__init__('AutoAwayStatusWindows', 'awayw', *args)
-        self._thread = None
-        self._exec = None
-        self._active = False
-        self._time = json.loads(self.load_settings())['time']
-        self._prev_status = 0
-
-    def close(self):
-        self.stop()
-
-    def stop(self):
-        self._exec = False
-        if self._active:
-            self._thread.join()
-
-    def start(self):
-        self._exec = True
-        self._thread = threading.Thread(target=self.loop)
-        self._thread.start()
-
-    def save(self):
-        self.save_settings('{"time": ' + str(self._time) + '}')
-
-    def change_status(self, status=1):
-        if self._profile.status != 1:
-            self._prev_status = self._profile.status
-        invoke_in_main_thread(self._profile.set_status, status)
-
-    def get_window(self):
-        inst = self
-
-        class Window(QtWidgets.QWidget):
-            def __init__(self):
-                super(Window, self).__init__()
-                self.setGeometry(QtCore.QRect(450, 300, 350, 100))
-                self.label = QtWidgets.QLabel(self)
-                self.label.setGeometry(QtCore.QRect(20, 0, 310, 35))
-                self.label.setText(QtWidgets.QApplication.translate("AutoAwayStatusWindows", "Auto away time in minutes\n(0 - to disable)"))
-                self.time = QtWidgets.QLineEdit(self)
-                self.time.setGeometry(QtCore.QRect(20, 40, 310, 25))
-                self.time.setText(str(inst._time))
-                self.setWindowTitle("AutoAwayStatusWindows")
-                self.ok = QtWidgets.QPushButton(self)
-                self.ok.setGeometry(QtCore.QRect(20, 70, 310, 25))
-                self.ok.setText(
-                    QtWidgets.QApplication.translate("AutoAwayStatusWindows", "Save"))
-                self.ok.clicked.connect(self.update)
-
-            def update(self):
-                try:
-                    t = int(self.time.text())
-                except:
-                    t = 0
-                inst._time = t
-                inst.save()
-                self.close()
-
-        return Window()
-
-    def loop(self):
-        self._active = True
-        while self._exec:
-            time.sleep(5)
-            d = get_idle_duration()
-            if self._time:
-                if d > 60 * self._time:
-                    self.change_status()
-                elif self._profile.status == 1:
-                    self.change_status(self._prev_status)
diff --git a/toxygen/plugins/bday.pro b/toxygen/plugins/bday.pro
deleted file mode 100644
index 7393e95..0000000
--- a/toxygen/plugins/bday.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-SOURCES = bday.py
-TRANSLATIONS = bday/en_GB.ts  bday/en_US.ts  bday/ru_RU.ts
diff --git a/toxygen/plugins/bday.py b/toxygen/plugins/bday.py
deleted file mode 100644
index 8563638..0000000
--- a/toxygen/plugins/bday.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import json
-import importlib
-
-from  qtpy import QtWidgets, QtCore
-
-import plugin_super_class
-
-class BirthDay(plugin_super_class.PluginSuperClass):
-
-    def __init__(self, *args):
-        # Constructor. In plugin __init__ should take only 1 last argument
-        super(BirthDay, self).__init__('BirthDay', 'bday', *args)
-        self._data = json.loads(self.load_settings())
-        self._datetime = importlib.import_module('datetime')
-        self._timers = []
-        self._app = args[0]
-        self._profile=self._app._ms._profile
-        self._window = None
-
-    def start(self) -> None:
-        now = self._datetime.datetime.now()
-        today = {}
-        x = self._profile.tox_id[:64]
-        for key in self._data:
-            if key != x and key != 'send_date':
-                arr = self._data[key].split('.')
-                if int(arr[0]) == now.day and int(arr[1]) == now.month:
-                    today[key] = now.year - int(arr[2])
-        if len(today):
-            msgbox = QtWidgets.QMessageBox()
-            title = QtWidgets.QApplication.translate('BirthDay', "Birthday!")
-            msgbox.setWindowTitle(title)
-            text = ', '.join(self._profile.get_friend_by_number(self._tox.friend_by_public_key(x)).name + ' ({})'.format(today[x]) for x in today)
-            msgbox.setText('Birthdays: ' + text)
-            msgbox.exec_()
-
-    def get_description(self):
-        return QtWidgets.QApplication.translate("BirthDay", "Send and get notifications on your friends' birthdays.")
-
-    def get_window(self) -> None:
-        inst = self
-        x = self._profile.tox_id[:64]
-
-        class Window(QtWidgets.QWidget):
-
-            def __init__(self):
-                super(Window, self).__init__()
-                self.setGeometry(QtCore.QRect(450, 300, 350, 150))
-                self.send = QtWidgets.QCheckBox(self)
-                self.send.setGeometry(QtCore.QRect(20, 10, 310, 25))
-                self.send.setText(QtWidgets.QApplication.translate('BirthDay', "Send my birthday date to contacts"))
-                self.setWindowTitle(QtWidgets.QApplication.translate('BirthDay', "Birthday"))
-                self.send.clicked.connect(self.update)
-                self.send.setChecked(inst._data['send_date'])
-                self.date = QtWidgets.QLineEdit(self)
-                self.date.setGeometry(QtCore.QRect(20, 50, 310, 25))
-                self.date.setPlaceholderText(QtWidgets.QApplication.translate('BirthDay', "Date in format dd.mm.yyyy"))
-                self.set_date = QtWidgets.QPushButton(self)
-                self.set_date.setGeometry(QtCore.QRect(20, 90, 310, 25))
-                self.set_date.setText(QtWidgets.QApplication.translate('BirthDay', "Save date"))
-                self.set_date.clicked.connect(self.save_curr_date)
-                self.date.setText(inst._data[x] if x in inst._data else '')
-
-            def save_curr_date(self):
-                inst._data[x] = self.date.text()
-                inst.save_settings(json.dumps(inst._data))
-                self.close()
-
-            def update(self):
-                inst._data['send_date'] = self.send.isChecked()
-                inst.save_settings(json.dumps(inst._data))
-
-        if not hasattr(self, '_window') or not self._window:
-            self._window = Window()
-        return self._window
-
-    def lossless_packet(self, data, friend_number) -> None:
-        if len(data):
-            friend = self._profile.get_friend_by_number(friend_number)
-            self._data[friend.tox_id] = data
-            self.save_settings(json.dumps(self._data))
-        elif self._data['send_date'] and self._profile.tox_id[:64] in self._data:
-            self.send_lossless(self._data[self._profile.tox_id[:64]], friend_number)
-
-    def friend_connected(self, friend_number:int) -> None:
-        timer = QtCore.QTimer()
-        timer.timeout.connect(lambda: self.timer(friend_number))
-        timer.start(10000)
-        self._timers.append(timer)
-
-    def timer(self, friend_number:int) -> None:
-        timer = self._timers.pop()
-        timer.stop()
-        if self._profile.get_friend_by_number(friend_number).tox_id not in self._data:
-            self.send_lossless('', friend_number)
-
diff --git a/toxygen/plugins/bot.py b/toxygen/plugins/bot.py
deleted file mode 100644
index 71db5a0..0000000
--- a/toxygen/plugins/bot.py
+++ /dev/null
@@ -1,83 +0,0 @@
-import time
-
-from  qtpy import QtCore, QtWidgets
-
-import plugin_super_class
-
-
-class InvokeEvent(QtCore.QEvent):
-    EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
-
-    def __init__(self, fn, *args, **kwargs):
-        QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
-        self.fn = fn
-        self.args = args
-        self.kwargs = kwargs
-
-
-class Invoker(QtCore.QObject):
-
-    def event(self, event):
-        event.fn(*event.args, **event.kwargs)
-        return True
-
-_invoker = Invoker()
-
-
-def invoke_in_main_thread(fn, *args, **kwargs):
-    QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
-
-
-class Bot(plugin_super_class.PluginSuperClass):
-
-    def __init__(self, *args):
-        super(Bot, self).__init__('Bot', 'bot', *args)
-        self._callback = None
-        self._mode = 0
-        self._message = "I'm away, will back soon"
-        self._timer = QtCore.QTimer()
-        self._timer.timeout.connect(self.initialize)
-        self._app = args[0]
-        self._profile=self._app._ms._profile
-        self._window = None
-
-    def get_description(self):
-        return QtWidgets.QApplication.translate("Bot", 'Plugin to answer bot to your friends.')
-
-    def start(self):
-        self._timer.start(10000)
-
-    def command(self, command):
-        if command.startswith('mode '):
-            self._mode = int(command.split(' ')[-1])
-        elif command.startswith('message '):
-            self._message = command[8:]
-        else:
-            super().command(command)
-
-    def initialize(self):
-        self._timer.stop()
-        self._callback = self._tox.friend_message_cb
-
-        def incoming_message(tox, friend_number, message_type, message, size, user_data):
-            self._callback(tox, friend_number, message_type, message, size, user_data)
-            if self._profile.status == 1: # TOX_USER_STATUS['AWAY']
-                self.answer(friend_number, str(message, 'utf-8'))
-
-        self._tox.callback_friend_message(incoming_message) # , None
-
-    def stop(self):
-        if not self._callback: return
-        try:
-            # TypeError: argument must be callable or integer function address
-            self._tox.callback_friend_message(self._callback) # , None
-        except: pass
-
-    def close(self):
-        self.stop()
-
-    def answer(self, friend_number, message):
-        if not self._mode:
-            message = self._message
-        invoke_in_main_thread(self._profile.send_message, message, friend_number)
-
diff --git a/toxygen/plugins/chess.py b/toxygen/plugins/chess.py
deleted file mode 100644
index f5c6feb..0000000
--- a/toxygen/plugins/chess.py
+++ /dev/null
@@ -1,1696 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import collections
-import re
-import math
-
-from  qtpy import QtWidgets
-from  qtpy.QtCore import *
-from  qtpy.QtWidgets import *
-from  qtpy.QtGui import *
-from  qtpy.QtSvg import *
-
-import plugin_super_class
-
-START_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
-
-
-def opposite_color(color):
-    """:return: The opposite color.
-
-    :param color:
-        "w", "white, "b" or "black".
-    """
-    if color == "w":
-        return "b"
-    elif color == "white":
-        return "black"
-    elif color == "b":
-        return "w"
-    elif color == "black":
-        return "white"
-    else:
-        raise ValueError("Expected w, b, white or black, got: %s." % color)
-
-
-class Piece(object):
-
-    __cache = dict()
-
-    def __init__(self, symbol):
-        self.__symbol = symbol
-
-        self.__color = "w" if symbol != symbol.lower() else "b"
-        self.__full_color = "white" if self.__color == "w" else "black"
-
-        self.__type = symbol.lower()
-        if self.__type == "p":
-            self.__full_type = "pawn"
-        elif self.__type == "n":
-            self.__full_type = "knight"
-        elif self.__type == "b":
-            self.__full_type = "bishop"
-        elif self.__type == "r":
-            self.__full_type = "rook"
-        elif self.__type == "q":
-            self.__full_type = "queen"
-        elif self.__type == "k":
-            self.__full_type = "king"
-        else:
-            raise ValueError("Expected valid piece symbol, got: %s." % symbol)
-
-        self.__hash = ord(self.__symbol)
-
-    @classmethod
-    def from_color_and_type(cls, color, type):
-        """Creates a piece object from color and type.
-        """
-        if type == "p" or type == "pawn":
-            symbol = "p"
-        elif type == "n" or type == "knight":
-            symbol = "n"
-        elif type == "b" or type == "bishop":
-            symbol = "b"
-        elif type == "r" or type == "rook":
-            symbol = "r"
-        elif type == "q" or type == "queen":
-            symbol = "q"
-        elif type == "k" or type == "king":
-            symbol = "k"
-        else:
-            raise ValueError("Expected piece type, got: %s." % type)
-
-        if color == "w" or color == "white":
-            return cls(symbol.upper())
-        elif color == "b" or color == "black":
-            return cls(symbol)
-        else:
-            raise ValueError("Expected w, b, white or black, got: %s." % color)
-
-    @property
-    def symbol(self):
-        return self.__symbol
-
-    @property
-    def color(self):
-        """The color of the piece as `"b"` or `"w"`."""
-        return self.__color
-
-    @property
-    def full_color(self):
-        """The full color of the piece as `"black"` or `"white`."""
-        return self.__full_color
-
-    @property
-    def type(self):
-        """The type of the piece as `"p"`, `"b"`, `"n"`, `"r"`, `"k"`,
-        or `"q"` for pawn, bishop, knight, rook, king or queen.
-        """
-        return self.__type
-
-    @property
-    def full_type(self):
-        """The full type of the piece as `"pawn"`, `"bishop"`,
-        `"knight"`, `"rook"`, `"king"` or `"queen"`.
-        """
-        return self.__full_type
-
-    def __str__(self):
-        return self.__symbol
-
-    def __repr__(self):
-        return "Piece('%s')" % self.__symbol
-
-    def __eq__(self, other):
-        return isinstance(other, Piece) and self.__symbol == other.symbol
-
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
-    def __hash__(self):
-        return self.__hash
-
-
-class Square(object):
-    """Represents a square on the chess board.
-
-    :param name: The name of the square in algebraic notation.
-
-    Square objects that represent the same square compare as equal.
-    """
-
-    __cache = dict()
-
-    def __init__(self, name):
-        if not len(name) == 2:
-            raise ValueError("Expected square name, got: %s." % repr(name))
-        self.__name = name
-
-        if not name[0] in ["a", "b", "c", "d", "e", "f", "g", "h"]:
-            raise ValueError("Expected file, got: %s." % repr(name[0]))
-        self.__file = name[0]
-        self.__x = ord(self.__name[0]) - ord("a")
-
-        if not name[1] in ["1", "2", "3", "4", "5", "6", "7", "8"]:
-            raise ValueError("Expected rank, got: %s." % repr(name[1]))
-        self.__rank = int(name[1])
-        self.__y = ord(self.__name[1]) - ord("1")
-
-        self.__x88 = self.__x + 16 * (7 - self.__y)
-
-    @classmethod
-    def from_x88(cls, x88):
-        """Creates a square object from an `x88 <http://en.wikipedia.org/wiki/Board_representation_(chess)#0x88_method>`_
-        index.
-
-        :param x88:
-            The x88 index as integer between 0 and 128.
-        """
-        if x88 < 0 or x88 > 128:
-            raise ValueError("x88 index is out of range: %s." % repr(x88))
-
-        if x88 & 0x88:
-            raise ValueError("x88 is not on the board: %s." % repr(x88))
-
-        return cls("abcdefgh"[x88 & 7] + "87654321"[x88 >> 4])
-
-    @classmethod
-    def from_rank_and_file(cls, rank, file):
-        """Creates a square object from rank and file.
-
-        :param rank:
-            An integer between 1 and 8.
-        :param file:
-            The rank as a letter between `"a"` and `"h"`.
-        """
-        if rank < 1 or rank > 8:
-            raise ValueError("Expected rank to be between 1 and 8: %s." % repr(rank))
-
-        if not file in ["a", "b", "c", "d", "e", "f", "g", "h"]:
-            raise ValueError("Expected the file to be a letter between 'a' and 'h': %s." % repr(file))
-
-        return cls(file + str(rank))
-
-    @classmethod
-    def from_x_and_y(cls, x, y):
-        """Creates a square object from x and y coordinates.
-
-        :param x:
-            An integer between 0 and 7 where 0 is the a-file.
-        :param y:
-            An integer between 0 and 7 where 0 is the first rank.
-        """
-        return cls("abcdefgh"[x] + "12345678"[y])
-
-    @property
-    def name(self):
-        """The algebraic name of the square."""
-        return self.__name
-
-    @property
-    def file(self):
-        """The file as a letter between `"a"` and `"h"`."""
-        return self.__file
-
-    @property
-    def x(self):
-        """The x-coordinate, starting with 0 for the a-file."""
-        return self.__x
-
-    @property
-    def rank(self):
-        """The rank as an integer between 1 and 8."""
-        return self.__rank
-
-    @property
-    def y(self):
-        """The y-coordinate, starting with 0 for the first rank."""
-        return self.__y
-
-    @property
-    def x88(self):
-        """The `x88 <http://en.wikipedia.org/wiki/Board_representation_(chess)#0x88_method>`_
-        index of the square."""
-        return self.__x88
-
-    def is_dark(self):
-        """:return: Whether it is a dark square."""
-        return (self.__x - self.__y % 2) == 0
-
-    def is_light(self):
-        """:return: Whether it is a light square."""
-        return not self.is_dark()
-
-    def is_backrank(self):
-        """:return: Whether the square is on either sides backrank."""
-        return self.__y == 0 or self.__y == 7
-
-    def __str__(self):
-        return self.__name
-
-    def __repr__(self):
-        return "Square('%s')" % self.__name
-
-    def __eq__(self, other):
-        return isinstance(other, Square) and self.__name == other.name
-
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
-    def __hash__(self):
-        return self.__x88
-
-
-class Move(object):
-    """Represents a move.
-    """
-
-    __uci_move_regex = re.compile(r"^([a-h][1-8])([a-h][1-8])([rnbq]?)$")
-
-    def __init__(self, source, target, promotion=None):
-        if not isinstance(source, Square):
-            raise TypeError("Expected source to be a Square.")
-        self.__source = source
-
-        if not isinstance(target, Square):
-            raise TypeError("Expected target to be a Square.")
-        self.__target = target
-
-        if not promotion:
-            self.__promotion = None
-            self.__full_promotion = None
-        else:
-            promotion = promotion.lower()
-            if promotion == "n" or promotion == "knight":
-                self.__promotion = "n"
-                self.__full_promotion = "knight"
-            elif promotion == "b" or promotion == "bishop":
-                self.__promotion = "b"
-                self.__full_promotion = "bishop"
-            elif promotion == "r" or promotion == "rook":
-                self.__promotion = "r"
-                self.__full_promotion = "rook"
-            elif promotion == "q" or promotion == "queen":
-                self.__promotion = "q"
-                self.__full_promotion = "queen"
-            else:
-                raise ValueError("Expected promotion type, got: %s." % repr(promotion))
-
-    @classmethod
-    def from_uci(cls, uci):
-        """The UCI move string like `"a1a2"` or `"b7b8q"`."""
-        if uci == "0000":
-            return cls.get_null()
-
-        match = cls.__uci_move_regex.match(uci)
-
-        return cls(
-            source=Square(match.group(1)),
-            target=Square(match.group(2)),
-            promotion=match.group(3) or None)
-
-    @classmethod
-    def get_null(cls):
-        """:return: A null move."""
-        return cls(Square("a1"), Square("a1"))
-
-    @property
-    def source(self):
-        """The source square."""
-        return self.__source
-
-    @property
-    def target(self):
-        """The target square."""
-        return self.__target
-
-    @property
-    def promotion(self):
-        """The promotion type as `None`, `"r"`, `"n"`, `"b"` or `"q"`."""
-        return self.__promotion
-
-    @property
-    def full_promotion(self):
-        """Like `promotion`, but with full piece type names."""
-        return self.__full_promotion
-
-    @property
-    def uci(self):
-        """The UCI move string like `"a1a2"` or `"b7b8q"`."""
-        if self.is_null():
-            return "0000"
-        else:
-            if self.__promotion:
-                return self.__source.name + self.__target.name + self.__promotion
-            else:
-                return self.__source.name + self.__target.name
-
-    def is_null(self):
-        """:return: Whether the move is a null move."""
-        return self.__source == self.__target
-
-    def __nonzero__(self):
-        return not self.is_null()
-
-    def __str__(self):
-        return self.uci
-
-    def __repr__(self):
-        return "Move.from_uci(%s)" % repr(self.uci)
-
-    def __eq__(self, other):
-        return isinstance(other, Move) and self.uci == other.uci
-
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
-    def __hash__(self):
-        return hash(self.uci)
-
-
-MoveInfo = collections.namedtuple("MoveInfo", [
-    "move",
-    "piece",
-    "captured",
-    "san",
-    "is_enpassant",
-    "is_king_side_castle",
-    "is_queen_side_castle",
-    "is_castle",
-    "is_check",
-    "is_checkmate"])
-
-
-class Position(object):
-    """Represents a chess position.
-
-    :param fen:
-        Optional. The FEN of the position. Defaults to the standard
-        chess start position.
-    """
-
-    __san_regex = re.compile('^([NBKRQ])?([a-h])?([1-8])?x?([a-h][1-8])(=[NBRQ])?(\+|#)?$')
-
-    def __init__(self, fen=START_FEN):
-        self.__castling = "KQkq"
-        self.fen = fen
-
-    def copy(self):
-        """Gets a copy of the position. The copy will not change when the
-        original instance is changed.
-
-        :return:
-            An exact copy of the positon.
-        """
-        return Position(self.fen)
-
-    def __get_square_index(self, square_or_int):
-        if type(square_or_int) is int:
-            # Validate the index by passing it through the constructor.
-            return Square.from_x88(square_or_int).x88
-        elif isinstance(square_or_int, str):
-            return Square(square_or_int).x88
-        elif type(square_or_int) is Square:
-            return square_or_int.x88
-        else:
-            raise TypeError(
-                "Expected integer or Square, got: %s." % repr(square_or_int))
-
-    def __getitem__(self, key):
-        return self.__board[self.__get_square_index(key)]
-
-    def __setitem__(self, key, value):
-        if value is None or type(value) is Piece:
-            self.__board[self.__get_square_index(key)] = value
-        else:
-            raise TypeError("Expected Piece or None, got: %s." % repr(value))
-
-    def __delitem__(self, key):
-        self.__board[self.__get_square_index(key)] = None
-
-    def clear_board(self):
-        """Removes all pieces from the board."""
-        self.__board = [None] * 128
-
-    def reset(self):
-        """Resets to the standard chess start position."""
-        self.set_fen(START_FEN)
-
-    def __get_disambiguator(self, move):
-        same_rank = False
-        same_file = False
-        piece = self[move.source]
-
-        for m in self.get_legal_moves():
-            ambig_piece = self[m.source]
-            if (piece == ambig_piece and move.source != m.source and
-                move.target == m.target):
-                if move.source.rank == m.source.rank:
-                    same_rank = True
-
-                if move.source.file == m.source.file:
-                    same_file = True
-
-                if same_rank and same_file:
-                    break
-
-        if same_rank and same_file:
-            return move.source.name
-        elif same_file:
-            return str(move.source.rank)
-        elif same_rank:
-            return move.source.file
-        else:
-            return ""
-
-    def get_move_from_san(self, san):
-        """Gets a move from standard algebraic notation.
-
-        :param san:
-            A move string in standard algebraic notation.
-
-        :return:
-            A Move object.
-
-        :raise Exception:
-            If not exactly one legal move matches.
-        """
-        # Castling moves.
-        if san == "O-O" or san == "O-O-O":
-            rank = 1 if self.turn == "w" else 8
-            if san == "O-O":
-                return Move(
-                    source=Square.from_rank_and_file(rank, 'e'),
-                    target=Square.from_rank_and_file(rank, 'g'))
-            else:
-                return Move(
-                    source=Square.from_rank_and_file(rank, 'e'),
-                    target=Square.from_rank_and_file(rank, 'c'))
-        # Regular moves.
-        else:
-            matches = Position.__san_regex.match(san)
-            if not matches:
-                raise ValueError("Invalid SAN: %s." % repr(san))
-
-            piece = Piece.from_color_and_type(
-                color=self.turn,
-                type=matches.group(1).lower() if matches.group(1) else 'p')
-            target = Square(matches.group(4))
-
-            source = None
-            for m in self.get_legal_moves():
-                if self[m.source] != piece or m.target != target:
-                    continue
-
-                if matches.group(2) and matches.group(2) != m.source.file:
-                    continue
-                if matches.group(3) and matches.group(3) != str(m.source.rank):
-                    continue
-
-                # Move matches. Assert it is not ambiguous.
-                if source:
-                    raise Exception(
-                        "Move is ambiguous: %s matches %s and %s."
-                            % san, source, m)
-                source = m.source
-
-            if not source:
-                raise Exception("No legal move matches %s." % san)
-
-            return Move(source, target, matches.group(5) or None)
-
-    def get_move_info(self, move):
-        """Gets information about a move.
-
-        :param move:
-            The move to get information about.
-
-        :return:
-            A named tuple with these properties:
-
-            `move`:
-                The move object.
-            `piece`:
-                The piece that has been moved.
-            `san`:
-                The standard algebraic notation of the move.
-            `captured`:
-                The piece that has been captured or `None`.
-            `is_enpassant`:
-                A boolean indicating if the move is an en-passant
-                capture.
-            `is_king_side_castle`:
-                Whether it is a king-side castling move.
-            `is_queen_side_castle`:
-                Whether it is a queen-side castling move.
-            `is_castle`:
-                Whether it is a castling move.
-            `is_check`:
-                Whether the move gives check.
-            `is_checkmate`:
-                Whether the move gives checkmate.
-
-        :raise Exception:
-            If the move is not legal in the position.
-        """
-        resulting_position = self.copy().make_move(move)
-
-        capture = self[move.target]
-        piece = self[move.source]
-
-        # Pawn moves.
-        enpassant = False
-        if piece.type == "p":
-            # En-passant.
-            if move.target.file != move.source.file and not capture:
-                enpassant = True
-                capture = Piece.from_color_and_type(
-                    color=resulting_position.turn, type='p')
-
-        # Castling.
-        if piece.type == "k":
-            is_king_side_castle = move.target.x - move.source.x == 2
-            is_queen_side_castle = move.target.x - move.source.x == -2
-        else:
-            is_king_side_castle = is_queen_side_castle = False
-
-        # Checks.
-        is_check = resulting_position.is_check()
-        is_checkmate = resulting_position.is_checkmate()
-
-        # Generate the SAN.
-        san = ""
-        if is_king_side_castle:
-            san += "o-o"
-        elif is_queen_side_castle:
-            san += "o-o-o"
-        else:
-            if piece.type != 'p':
-                san += piece.type.upper()
-
-            san += self.__get_disambiguator(move)
-
-            if capture:
-                if piece.type == 'p':
-                    san += move.source.file
-                san += "x"
-            san += move.target.name
-
-            if move.promotion:
-                san += "="
-                san += move.promotion.upper()
-
-        if is_checkmate:
-            san += "#"
-        elif is_check:
-            san += "+"
-
-        if enpassant:
-            san += " (e.p.)"
-
-        # Return the named tuple.
-        return MoveInfo(
-            move=move,
-            piece=piece,
-            captured=capture,
-            san=san,
-            is_enpassant=enpassant,
-            is_king_side_castle=is_king_side_castle,
-            is_queen_side_castle=is_queen_side_castle,
-            is_castle=is_king_side_castle or is_queen_side_castle,
-            is_check=is_check,
-            is_checkmate=is_checkmate)
-
-    def make_move(self, move, validate=True):
-        """Makes a move.
-
-        :param move:
-            The move to make.
-        :param validate:
-            Defaults to `True`. Whether the move should be validated.
-
-        :return:
-            Making a move changes the position object. The same
-            (changed) object is returned for chainability.
-
-        :raise Exception:
-            If the validate parameter is `True` and the move is not
-            legal in the position.
-        """
-        if validate and move not in self.get_legal_moves():
-            raise Exception(
-                "%s is not a legal move in the position %s." % (move, self.fen))
-        piece = self[move.source]
-        capture = self[move.target]
-
-        # Move the piece.
-        self[move.target] = self[move.source]
-        del self[move.source]
-
-        # It is the next players turn.
-        self.toggle_turn()
-
-        # Pawn moves.
-        self.ep_file = None
-        if piece.type == "p":
-            # En-passant.
-            if move.target.file != move.source.file and not capture:
-                if self.turn == "w":
-                    self[move.target.x88 - 16] = None
-                else:
-                    self[move.target.x88 + 16] = None
-                capture = True
-            # If big pawn move, set the en-passant file.
-            if abs(move.target.rank - move.source.rank) == 2:
-                if self.get_theoretical_ep_right(move.target.file):
-                    self.ep_file = move.target.file
-
-        # Promotion.
-        if move.promotion:
-            self[move.target] = Piece.from_color_and_type(
-                color=piece.color, type=move.promotion)
-
-        # Potential castling.
-        if piece.type == "k":
-            steps = move.target.x - move.source.x
-            if abs(steps) == 2:
-                # Queen-side castling.
-                if steps == -2:
-                    rook_target = move.target.x88 + 1
-                    rook_source = move.target.x88 - 2
-                # King-side castling.
-                else:
-                    rook_target = move.target.x88 - 1
-                    rook_source = move.target.x88 + 1
-                self[rook_target] = self[rook_source]
-                del self[rook_source]
-
-        # Increment the half move counter.
-        if piece.type == "p" or capture:
-            self.half_moves = 0
-        else:
-            self.half_moves += 1
-
-        # Increment the move number.
-        if self.turn == "w":
-            self.ply += 1
-
-        # Update castling rights.
-        for type in ["K", "Q", "k", "q"]:
-            if not self.get_theoretical_castling_right(type):
-                self.set_castling_right(type, False)
-
-        return self
-
-    @property
-    def turn(self):
-        """Whos turn it is as `"w"` or `"b"`."""
-        return self.__turn
-
-    @turn.setter
-    def turn(self, value):
-        if value not in ["w", "b"]:
-            raise ValueError(
-                "Expected 'w' or 'b' for turn, got: %s." % repr(value))
-        self.__turn = value
-
-    def toggle_turn(self):
-        """Toggles whos turn it is."""
-        self.turn = opposite_color(self.turn)
-
-    def get_castling_right(self, type):
-        """Checks the castling rights.
-
-        :param type:
-            The castling move to check. "K" for king-side castling of
-            the white player, "Q" for queen-side castling of the white
-            player. "k" and "q" for the corresponding castling moves of
-            the black player.
-
-        :return:
-            A boolean indicating whether the player has that castling
-            right.
-        """
-        if not type in ["K", "Q", "k", "q"]:
-            raise KeyError(
-                "Expected 'K', 'Q', 'k' or 'q' as a castling type, got: %s." % repr(type))
-        return type in self.__castling
-
-    def get_theoretical_castling_right(self, type):
-        """Checks if a player could have a castling right in theory from
-        looking just at the piece positions.
-
-        :param type:
-            The castling move to check. See
-            `Position.get_castling_right(type)` for values.
-
-        :return:
-            A boolean indicating whether the player could theoretically
-            have that castling right.
-        """
-        if not type in ["K", "Q", "k", "q"]:
-            raise KeyError(
-                "Expected 'K', 'Q', 'k' or 'q' as a castling type, got: %s."
-                    % repr(type))
-        if type == "K" or type == "Q":
-            if self["e1"] != Piece("K"):
-                return False
-            if type == "K":
-                return self["h1"] == Piece("R")
-            elif type == "Q":
-                return self["a1"] == Piece("R")
-        elif type == "k" or type == "q":
-            if self["e8"] != Piece("k"):
-                return False
-            if type == "k":
-                return self["h8"] == Piece("r")
-            elif type == "q":
-                return self["a8"] == Piece("r")
-
-    def get_theoretical_ep_right(self, file):
-        """Checks if a player could have an ep-move in theory from
-        looking just at the piece positions.
-
-        :param file:
-            The file to check as a letter between `"a"` and `"h"`.
-
-        :return:
-            A boolean indicating whether the player could theoretically
-            have that en-passant move.
-        """
-        if not file in ["a", "b", "c", "d", "e", "f", "g", "h"]:
-            raise KeyError(
-                "Expected a letter between 'a' and 'h' for the file, got: %s."
-                    % repr(file))
-
-        # Check there is a pawn.
-        pawn_square = Square.from_rank_and_file(
-            rank=4 if self.turn == "b" else 5, file=file)
-        opposite_color_pawn = Piece.from_color_and_type(
-            color=opposite_color(self.turn), type="p")
-        if self[pawn_square] != opposite_color_pawn:
-            return False
-
-        # Check the square below is empty.
-        square_below = Square.from_rank_and_file(
-            rank=3 if self.turn == "b" else 6, file=file)
-        if self[square_below]:
-            return False
-
-        # Check there is a pawn of the other color on a neighbor file.
-        f = ord(file) - ord("a")
-        p = Piece("p")
-        P = Piece("P")
-        if self.turn == "b":
-            if f > 0 and self[Square.from_x_and_y(f - 1, 3)] == p:
-                return True
-            elif f < 7 and self[Square.from_x_and_y(f + 1, 3)] == p:
-                return True
-        else:
-            if f > 0 and self[Square.from_x_and_y(f - 1, 4)] == P:
-                return True
-            elif f < 7 and self[Square.from_x_and_y(f + 1, 4)] == P:
-                return True
-        return False
-
-    def set_castling_right(self, type, status):
-        """Sets a castling right.
-
-        :param type:
-            `"K"`, `"Q"`, `"k"`, or `"q"` as used by
-            `Position.get_castling_right(type)`.
-        :param status:
-            A boolean indicating whether that castling right should be
-            granted or denied.
-        """
-        if not type in ["K", "Q", "k", "q"]:
-            raise KeyError(
-                "Expected 'K', 'Q', 'k' or 'q' as a castling type, got: %s."
-                    % repr(type))
-
-        castling = ""
-        for t in ["K", "Q", "k", "q"]:
-            if type == t:
-                if status:
-                    castling += t
-            elif self.get_castling_right(t):
-                castling += t
-        self.__castling = castling
-
-    @property
-    def ep_file(self):
-        """The en-passant file as a lowercase letter between `"a"` and
-        `"h"` or `None`."""
-        return self.__ep_file
-
-    @ep_file.setter
-    def ep_file(self, value):
-        if not value in ["a", "b", "c", "d", "e", "f", "g", "h", None]:
-            raise ValueError(
-                "Expected None or a letter between 'a' and 'h' for the "
-                "en-passant file, got: %s." % repr(value))
-
-        self.__ep_file = value
-
-    @property
-    def half_moves(self):
-        """The number of half-moves since the last capture or pawn move."""
-        return self.__half_moves
-
-    @half_moves.setter
-    def half_moves(self, value):
-        if type(value) is not int:
-            raise TypeError(
-                "Expected integer for half move count, got: %s." % repr(value))
-        if value < 0:
-            raise ValueError("Half move count must be >= 0.")
-
-        self.__half_moves = value
-
-    @property
-    def ply(self):
-        """The number of this move. The game starts at 1 and the counter
-        is incremented every time white moves.
-        """
-        return self.__ply
-
-    @ply.setter
-    def ply(self, value):
-        if type(value) is not int:
-            raise TypeError(
-                "Expected integer for ply count, got: %s." % repr(value))
-        if value < 1:
-            raise ValueError("Ply count must be >= 1.")
-        self.__ply = value
-
-    def get_piece_counts(self, color = "wb"):
-        """Counts the pieces on the board.
-
-        :param color:
-            Defaults to `"wb"`. A color to filter the pieces by. Valid
-            values are "w", "b", "wb" and "bw".
-
-        :return:
-            A dictionary of piece counts, keyed by lowercase piece type
-            letters.
-        """
-        if not color in ["w", "b", "wb", "bw"]:
-            raise KeyError(
-                "Expected color filter to be one of 'w', 'b', 'wb', 'bw', "
-                "got: %s." % repr(color))
-
-        counts = {
-            "p": 0,
-            "b": 0,
-            "n": 0,
-            "r": 0,
-            "k": 0,
-            "q": 0,
-        }
-        for piece in self.__board:
-            if piece and piece.color in color:
-                counts[piece.type] += 1
-        return counts
-
-    def get_king(self, color):
-        """Gets the square of the king.
-
-        :param color:
-            `"w"` for the white players king. `"b"` for the black
-            players king.
-
-        :return:
-            The first square with a matching king or `None` if that
-            player has no king.
-        """
-        if not color in ["w", "b"]:
-            raise KeyError("Invalid color: %s." % repr(color))
-
-        for x88, piece in enumerate(self.__board):
-            if piece and piece.color == color and piece.type == "k":
-                return Square.from_x88(x88)
-
-    @property
-    def fen(self):
-        """The FEN string representing the position."""
-        # Board setup.
-        empty = 0
-        fen = ""
-        for y in range(7, -1, -1):
-            for x in range(0, 8):
-                square = Square.from_x_and_y(x, y)
-
-                # Add pieces.
-                if not self[square]:
-                    empty += 1
-                else:
-                    if empty > 0:
-                        fen += str(empty)
-                        empty = 0
-                    fen += self[square].symbol
-
-            # Boarder of the board.
-            if empty > 0:
-                fen += str(empty)
-            if not (x == 7 and y == 0):
-                fen += "/"
-            empty = 0
-
-        if self.ep_file and self.get_theoretical_ep_right(self.ep_file):
-            ep_square = self.ep_file + ("3" if self.turn == "b" else "6")
-        else:
-            ep_square = "-"
-
-        # Join the parts together.
-        return " ".join([
-            fen,
-            self.turn,
-            self.__castling if self.__castling else "-",
-            ep_square,
-            str(self.half_moves),
-            str(self.__ply)])
-
-    @fen.setter
-    def fen(self, fen):
-        # Split into 6 parts.
-        tokens = fen.split()
-        if len(tokens) != 6:
-            raise Exception("A FEN does not consist of 6 parts.")
-
-        # Check that the position part is valid.
-        rows = tokens[0].split("/")
-        assert len(rows) == 8
-        for row in rows:
-            field_sum = 0
-            previous_was_number = False
-            for char in row:
-                if char in "12345678":
-                    if previous_was_number:
-                        raise Exception(
-                            "Position part of the FEN is invalid: "
-                            "Multiple numbers immediately after each other.")
-                    field_sum += int(char)
-                    previous_was_number = True
-                elif char in "pnbrkqPNBRKQ":
-                    field_sum += 1
-                    previous_was_number = False
-                else:
-                    raise Exception(
-                        "Position part of the FEN is invalid: "
-                        "Invalid character in the position part of the FEN.")
-
-            if field_sum != 8:
-                Exception(
-                    "Position part of the FEN is invalid: "
-                    "Row with invalid length.")
-
-        # Check that the other parts are valid.
-        if not tokens[1] in ["w", "b"]:
-            raise Exception(
-                "Turn part of the FEN is invalid: Expected b or w.")
-        if not re.compile(r"^(KQ?k?q?|Qk?q?|kq?|q|-)$").match(tokens[2]):
-            raise Exception("Castling part of the FEN is invalid.")
-        if not re.compile(r"^(-|[a-h][36])$").match(tokens[3]):
-            raise Exception("En-passant part of the FEN is invalid.")
-        if not re.compile(r"^(0|[1-9][0-9]*)$").match(tokens[4]):
-            raise Exception("Half move part of the FEN is invalid.")
-        if not re.compile(r"^[1-9][0-9]*$").match(tokens[5]):
-            raise Exception("Ply part of the FEN is invalid.")
-
-        # Set pieces on the board.
-        self.__board = [None] * 128
-        i = 0
-        for symbol in tokens[0]:
-            if symbol == "/":
-                i += 8
-            elif symbol in "12345678":
-                i += int(symbol)
-            else:
-                self.__board[i] = Piece(symbol)
-                i += 1
-
-        # Set the turn.
-        self.__turn = tokens[1]
-
-        # Set the castling rights.
-        for type in ["K", "Q", "k", "q"]:
-            self.set_castling_right(type, type in tokens[2])
-
-        # Set the en-passant file.
-        if tokens[3] == "-":
-            self.__ep_file = None
-        else:
-            self.__ep_file = tokens[3][0]
-
-        # Set the move counters.
-        self.__half_moves = int(tokens[4])
-        self.__ply = int(tokens[5])
-
-    def is_king_attacked(self, color):
-        """:return: Whether the king of the given color is attacked.
-
-        :param color: `"w"` or `"b"`.
-        """
-        square = self.get_king(color)
-        if square:
-            return self.is_attacked(opposite_color(color), square)
-        else:
-            return False
-
-    def get_pseudo_legal_moves(self):
-        """:yield: Pseudo legal moves in the current position."""
-        PAWN_OFFSETS = {
-            "b": [16, 32, 17, 15],
-            "w": [-16, -32, -17, -15]
-        }
-
-        PIECE_OFFSETS = {
-            "n": [-18, -33, -31, -14, 18, 33, 31, 14],
-            "b": [-17, -15, 17, 15],
-            "r": [-16, 1, 16, -1],
-            "q": [-17, -16, -15, 1, 17, 16, 15, -1],
-            "k": [-17, -16, -15, 1, 17, 16, 15, -1]
-        }
-
-        for x88, piece in enumerate(self.__board):
-            # Skip pieces of the opponent.
-            if not piece or piece.color != self.turn:
-                continue
-
-            square = Square.from_x88(x88)
-
-            # Pawn moves.
-            if piece.type == "p":
-                # Single square ahead. Do not capture.
-                target = Square.from_x88(x88 + PAWN_OFFSETS[self.turn][0])
-                if not self[target]:
-                    # Promotion.
-                    if target.is_backrank():
-                        for promote_to in "bnrq":
-                            yield Move(square, target, promote_to)
-                    else:
-                        yield Move(square, target)
-
-                    # Two squares ahead. Do not capture.
-                    if (self.turn == "w" and square.rank == 2) or (self.turn == "b" and square.rank == 7):
-                        target = Square.from_x88(square.x88 + PAWN_OFFSETS[self.turn][1])
-                        if not self[target]:
-                            yield Move(square, target)
-
-                # Pawn captures.
-                for j in [2, 3]:
-                   target_index = square.x88 + PAWN_OFFSETS[self.turn][j]
-                   if target_index & 0x88:
-                       continue
-                   target = Square.from_x88(target_index)
-                   if self[target] and self[target].color != self.turn:
-                       # Promotion.
-                       if target.is_backrank():
-                           for promote_to in "bnrq":
-                               yield Move(square, target, promote_to)
-                       else:
-                           yield Move(square, target)
-                   # En-passant.
-                   elif not self[target] and target.file == self.ep_file:
-                       yield Move(square, target)
-            # Other pieces.
-            else:
-                for offset in PIECE_OFFSETS[piece.type]:
-                    target_index = square.x88
-                    while True:
-                        target_index += offset
-                        if target_index & 0x88:
-                            break
-                        target = Square.from_x88(target_index)
-                        if not self[target]:
-                            yield Move(square, target)
-                        else:
-                            if self[target].color == self.turn:
-                                break
-                            yield Move(square, target)
-                            break
-
-                        # Knight and king do not go multiple times in their
-                        # direction.
-                        if piece.type in ["n", "k"]:
-                            break
-
-        opponent = opposite_color(self.turn)
-
-        # King-side castling.
-        k = "k" if self.turn == "b" else "K"
-        if self.get_castling_right(k):
-            of = self.get_king(self.turn).x88
-            to = of + 2
-            if not self[of + 1] and not self[to] and not self.is_check() and not self.is_attacked(opponent, Square.from_x88(of + 1)) and not self.is_attacked(opponent, Square.from_x88(to)):
-                yield Move(Square.from_x88(of), Square.from_x88(to))
-
-        # Queen-side castling
-        q = "q" if self.turn == "b" else "Q"
-        if self.get_castling_right(q):
-            of = self.get_king(self.turn).x88
-            to = of - 2
-
-            if not self[of - 1] and not self[of - 2] and not self[of - 3] and not self.is_check() and not self.is_attacked(opponent, Square.from_x88(of - 1)) and not self.is_attacked(opponent, Square.from_x88(to)):
-                yield Move(Square.from_x88(of), Square.from_x88(to))
-
-    def get_legal_moves(self):
-        """:yield: All legal moves in the current position."""
-        for move in self.get_pseudo_legal_moves():
-            potential_position = self.copy()
-            potential_position.make_move(move, False)
-            if not potential_position.is_king_attacked(self.turn):
-                yield move
-
-    def get_attackers(self, color, square):
-        """Gets the attackers of a specific square.
-
-        :param color:
-            Filter attackers by this piece color.
-        :param square:
-            The square to check for.
-
-        :yield:
-            Source squares of the attack.
-        """
-        if color not in ["b", "w"]:
-            raise KeyError("Invalid color: %s." % repr(color))
-
-        ATTACKS = [
-            20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 20, 0,
-            0, 20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 20, 0, 0,
-            0, 0, 20, 0, 0, 0, 0, 24, 0, 0, 0, 0, 20, 0, 0, 0,
-            0, 0, 0, 20, 0, 0, 0, 24, 0, 0, 0, 20, 0, 0, 0, 0,
-            0, 0, 0, 0, 20, 0, 0, 24, 0, 0, 20, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 0, 20, 2, 24, 2, 20, 0, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 0, 2, 53, 56, 53, 2, 0, 0, 0, 0, 0, 0,
-            24, 24, 24, 24, 24, 24, 56, 0, 56, 24, 24, 24, 24, 24, 24, 0,
-            0, 0, 0, 0, 0, 2, 53, 56, 53, 2, 0, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 0, 20, 2, 24, 2, 20, 0, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 20, 0, 0, 24, 0, 0, 20, 0, 0, 0, 0, 0,
-            0, 0, 0, 20, 0, 0, 0, 24, 0, 0, 0, 20, 0, 0, 0, 0,
-            0, 0, 20, 0, 0, 0, 0, 24, 0, 0, 0, 0, 20, 0, 0, 0,
-            0, 20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 20, 0, 0,
-            20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 20
-        ]
-
-        RAYS = [
-            17, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 15, 0,
-            0, 17, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 15, 0, 0,
-            0, 0, 17, 0, 0, 0, 0, 16, 0, 0, 0, 0, 15, 0, 0, 0,
-            0, 0, 0, 17, 0, 0, 0, 16, 0, 0, 0, 15, 0, 0, 0, 0,
-            0, 0, 0, 0, 17, 0, 0, 16, 0, 0, 15, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 0, 17, 0, 16, 0, 15, 0, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 0, 0, 17, 16, 15, 0, 0, 0, 0, 0, 0, 0,
-            1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1, 0,
-            0, 0, 0, 0, 0, 0, -15, -16, -17, 0, 0, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, 0, -15, 0, -16, 0, -17, 0, 0, 0, 0, 0, 0,
-            0, 0, 0, 0, -15, 0, 0, -16, 0, 0, -17, 0, 0, 0, 0, 0,
-            0, 0, 0, -15, 0, 0, 0, -16, 0, 0, 0, -17, 0, 0, 0, 0,
-            0, 0, -15, 0, 0, 0, 0, -16, 0, 0, 0, 0, -17, 0, 0, 0,
-            0, -15, 0, 0, 0, 0, 0, -16, 0, 0, 0, 0, 0, -17, 0, 0,
-            -15, 0, 0, 0, 0, 0, 0, -16, 0, 0, 0, 0, 0, 0, -17
-        ]
-
-        SHIFTS = {
-            "p": 0,
-            "n": 1,
-            "b": 2,
-            "r": 3,
-            "q": 4,
-            "k": 5
-        }
-
-        for x88, piece in enumerate(self.__board):
-            if not piece or piece.color != color:
-                continue
-            source = Square.from_x88(x88)
-
-            difference = source.x88 - square.x88
-            index = difference + 119
-
-            if ATTACKS[index] & (1 << SHIFTS[piece.type]):
-                # Handle pawns.
-                if piece.type == "p":
-                    if difference > 0:
-                        if piece.color == "w":
-                            yield source
-                    else:
-                        if piece.color == "b":
-                            yield source
-                    continue
-
-                # Handle knights and king.
-                if piece.type in ["n", "k"]:
-                    yield source
-
-                # Handle the others.
-                offset = RAYS[index]
-                j = source.x88 + offset
-                blocked = False
-                while j != square.x88:
-                    if self[j]:
-                        blocked = True
-                        break
-                    j += offset
-                if not blocked:
-                    yield source
-
-    def is_attacked(self, color, square):
-        """Checks whether a square is attacked.
-
-        :param color:
-            Check if this player is attacking.
-        :param square:
-            The square the player might be attacking.
-
-        :return:
-            A boolean indicating whether the given square is attacked
-            by the player of the given color.
-        """
-        x = list(self.get_attackers(color, square))
-        return len(x) > 0
-
-    def is_check(self):
-        """:return: Whether the current player is in check."""
-        return self.is_king_attacked(self.turn)
-
-    def is_checkmate(self):
-        """:return: Whether the current player has been checkmated."""
-        if not self.is_check():
-            return False
-        else:
-            arr = list(self.get_legal_moves())
-            return len(arr) == 0
-
-    def is_stalemate(self):
-        """:return: Whether the current player is in stalemate."""
-        if self.is_check():
-            return False
-        else:
-            arr = list(self.get_legal_moves())
-            return len(arr) == 0
-
-    def is_insufficient_material(self):
-        """Checks if there is sufficient material to mate.
-
-        Mating is impossible in:
-
-        * A king versus king endgame.
-        * A king with bishop versus king endgame.
-        * A king with knight versus king endgame.
-        * A king with bishop versus king with bishop endgame, where both
-          bishops are on the same color. Same goes for additional
-          bishops on the same color.
-
-        Assumes that the position is valid and each player has exactly
-        one king.
-
-        :return:
-            Whether there is insufficient material to mate.
-        """
-        piece_counts = self.get_piece_counts()
-        if sum(piece_counts.values()) == 2:
-            # King versus king.
-            return True
-        elif sum(piece_counts.values()) == 3:
-            # King and knight or bishop versus king.
-            if piece_counts["b"] == 1 or piece_counts["n"] == 1:
-                return True
-        elif sum(piece_counts.values()) == 2 + piece_counts["b"]:
-            # Each player with only king and any number of bishops, where all
-            # bishops are on the same color.
-            white_has_bishop = self.get_piece_counts("w")["b"] != 0
-            black_has_bishop = self.get_piece_counts("b")["b"] != 0
-            if white_has_bishop and black_has_bishop:
-                color = None
-                for x88, piece in enumerate(self.__board):
-                    if piece and piece.type == "b":
-                        square = Square.from_x88(x88)
-                        if color is not None and color != square.is_light():
-                            return False
-                        color = square.is_light()
-                return True
-        return False
-
-    def is_game_over(self):
-        """Checks if the game is over.
-
-        :return:
-            Whether the game is over by the rules of chess,
-            disregarding that players can agree on a draw, claim a draw
-            or resign.
-        """
-        return (self.is_checkmate() or self.is_stalemate() or
-                self.is_insufficient_material())
-
-    def __str__(self):
-        return self.fen
-
-    def __repr__(self):
-        return "Position.from_fen(%s)" % repr(self.fen)
-
-    def __eq__(self, other):
-        return self.fen == other.fen
-
-    def __ne__(self, other):
-        return self.fen != other.fen
-
-
-class Board(QWidget):
-
-    def __init__(self, parent):
-        super(Board, self).__init__()
-        self.margin = 0.1
-        self.padding = 0.06
-        self.showCoordinates = True
-        self.lightSquareColor = QColor(255, 255, 255)
-        self.darkSquareColor = QColor(100, 100, 255)
-        self.borderColor = QColor(100, 100, 200)
-        self.shadowWidth = 2
-        self.rotation = 0
-        self.ply = 1
-        self.setWindowTitle('Chess')
-        self.backgroundPixmap = QPixmap(plugin_super_class.path_to_data('chess') + "background.png")
-
-        self.draggedSquare = None
-        self.dragPosition = None
-
-        self.position = Position()
-
-        self.parent = parent
-
-        # Load piece set.
-        self.pieceRenderers = dict()
-        for symbol in "PNBRQKpnbrqk":
-            piece = Piece(symbol)
-            self.pieceRenderers[piece] = QSvgRenderer(plugin_super_class.path_to_data('chess') + "classic-pieces/%s-%s.svg" % (piece.full_color, piece.full_type))
-
-    def update_title(self, my_move=False):
-        if self.position.is_checkmate():
-            self.setWindowTitle('Checkmate')
-        elif self.position.is_stalemate():
-            self.setWindowTitle('Stalemate')
-        else:
-            self.setWindowTitle('Chess' + (' [Your move]' if my_move else ''))
-
-    def mousePressEvent(self, e):
-        self.dragPosition = e.pos()
-        square = self.squareAt(e.pos())
-        if self.canDragSquare(square):
-            self.draggedSquare = square
-
-    def mouseMoveEvent(self, e):
-        if self.draggedSquare:
-            self.dragPosition = e.pos()
-            self.repaint()
-
-    def mouseReleaseEvent(self, e):
-        if self.draggedSquare:
-            dropSquare = self.squareAt(e.pos())
-            if dropSquare == self.draggedSquare:
-                self.onSquareClicked(self.draggedSquare)
-            elif dropSquare:
-                move = self.moveFromDragDrop(self.draggedSquare, dropSquare)
-                if move:
-                    self.position.make_move(move)
-                    self.parent.move(move)
-                    self.ply += 1
-            self.draggedSquare = None
-            self.repaint()
-
-    def closeEvent(self, *args):
-        self.parent.stop_game()
-
-    def paintEvent(self, event):
-        painter = QPainter()
-        painter.begin(self)
-
-        # Light shines from upper left.
-        if math.cos(math.radians(self.rotation)) >= 0:
-            lightBorderColor = self.borderColor.lighter()
-            darkBorderColor = self.borderColor.darker()
-        else:
-            lightBorderColor = self.borderColor.darker()
-            darkBorderColor = self.borderColor.lighter()
-
-        # Draw the background.
-        backgroundBrush = QBrush(Qt.red, self.backgroundPixmap)
-        backgroundBrush.setStyle(Qt.TexturePattern)
-        painter.fillRect(QRect(QPoint(0, 0), self.size()), backgroundBrush)
-
-        # Do the rotation.
-        painter.save()
-        painter.translate(self.width() / 2, self.height() / 2)
-        painter.rotate(self.rotation)
-
-        # Draw the border.
-        frameSize = min(self.width(), self.height()) * (1 - self.margin * 2)
-        borderSize = min(self.width(), self.height()) * self.padding
-        painter.translate(-frameSize / 2, -frameSize / 2)
-        painter.fillRect(QRect(0, 0, frameSize, frameSize), self.borderColor)
-        painter.setPen(QPen(QBrush(lightBorderColor), self.shadowWidth))
-        painter.drawLine(0, 0, 0, frameSize)
-        painter.drawLine(0, 0, frameSize, 0)
-        painter.setPen(QPen(QBrush(darkBorderColor), self.shadowWidth))
-        painter.drawLine(frameSize, 0, frameSize, frameSize)
-        painter.drawLine(0, frameSize, frameSize, frameSize)
-
-        # Draw the squares.
-        painter.translate(borderSize, borderSize)
-        squareSize = (frameSize - 2 * borderSize) / 8.0
-        for x in range(0, 8):
-            for y in range(0, 8):
-                rect = QRect(x * squareSize, y * squareSize, squareSize, squareSize)
-                if (x - y) % 2 == 0:
-                     painter.fillRect(rect, QBrush(self.lightSquareColor))
-                else:
-                     painter.fillRect(rect, QBrush(self.darkSquareColor))
-
-        # Draw the inset.
-        painter.setPen(QPen(QBrush(darkBorderColor), self.shadowWidth))
-        painter.drawLine(0, 0, 0, squareSize * 8)
-        painter.drawLine(0, 0, squareSize * 8, 0)
-        painter.setPen(QPen(QBrush(lightBorderColor), self.shadowWidth))
-        painter.drawLine(squareSize * 8, 0, squareSize * 8, squareSize * 8)
-        painter.drawLine(0, squareSize * 8, squareSize * 8, squareSize * 8)
-
-        # Display coordinates.
-        if self.showCoordinates:
-            painter.setPen(QPen(QBrush(self.borderColor.lighter()), self.shadowWidth))
-            coordinateSize = min(borderSize, squareSize)
-            font = QFont()
-            font.setPixelSize(coordinateSize * 0.6)
-            painter.setFont(font)
-            for i, rank in enumerate(["8", "7", "6", "5", "4", "3", "2", "1"]):
-                pos = QRect(-borderSize, squareSize * i, borderSize, squareSize).center()
-                painter.save()
-                painter.translate(pos.x(), pos.y())
-                painter.rotate(-self.rotation)
-                painter.drawText(QRect(-coordinateSize / 2, -coordinateSize / 2, coordinateSize, coordinateSize), Qt.AlignCenter, rank)
-                painter.restore()
-            for i, file in enumerate(["a", "b", "c", "d", "e", "f", "g", "h"]):
-                pos = QRect(squareSize * i, squareSize * 8, squareSize, borderSize).center()
-                painter.save()
-                painter.translate(pos.x(), pos.y())
-                painter.rotate(-self.rotation)
-                painter.drawText(QRect(-coordinateSize / 2, -coordinateSize / 2, coordinateSize, coordinateSize), Qt.AlignCenter, file)
-                painter.restore()
-
-        # Draw pieces.
-        for x in range(0, 8):
-            for y in range(0, 8):
-                square = Square.from_x_and_y(x, 7 - y)
-                piece = self.position[square]
-                if piece and square != self.draggedSquare:
-                    painter.save()
-                    painter.translate((x + 0.5) * squareSize, (y + 0.5) * squareSize)
-                    painter.rotate(-self.rotation)
-                    self.pieceRenderers[piece].render(painter, QRectF(-squareSize / 2, -squareSize / 2, squareSize, squareSize))
-                    painter.restore()
-
-        # Draw a floating piece.
-        painter.restore()
-        if self.draggedSquare:
-            piece = self.position[self.draggedSquare]
-            if piece:
-                painter.save()
-                painter.translate(self.dragPosition.x(), self.dragPosition.y())
-                painter.rotate(-self.rotation)
-                self.pieceRenderers[piece].render(painter, QRect(-squareSize / 2, -squareSize / 2, squareSize, squareSize))
-                painter.restore()
-
-        painter.end()
-
-    def squareAt(self, point):
-        # Undo the rotation.
-        transform = QTransform()
-        transform.translate(self.width() / 2, self.height() / 2)
-        transform.rotate(self.rotation)
-        logicalPoint = transform.inverted()[0].map(point)
-
-        frameSize = min(self.width(), self.height()) * (1 - self.margin * 2)
-        borderSize = min(self.width(), self.height()) * self.padding
-        squareSize = (frameSize - 2 * borderSize) / 8.0
-        x = int(logicalPoint.x() / squareSize + 4)
-        y = 7 - int(logicalPoint.y() / squareSize + 4)
-        try:
-            return Square.from_x_and_y(x, y)
-        except IndexError:
-            return None
-
-    def canDragSquare(self, square):
-        if (self.ply % 2 == 0 and self.parent.white) or (self.ply % 2 == 1 and not self.parent.white):
-            return False
-        for move in self.position.get_legal_moves():
-            if move.source == square:
-                return True
-        return False
-
-    def onSquareClicked(self, square):
-        pass
-
-    def moveFromDragDrop(self, source, target):
-        for move in self.position.get_legal_moves():
-            if move.source == source and move.target == target:
-                if move.promotion:
-                    dialog = PromotionDialog(self.position[move.source].color, self)
-                    if dialog.exec_():
-                        return Move(source, target, dialog.selectedType())
-                else:
-                    return move
-                return move
-
-
-class PromotionDialog(QDialog):
-
-    def __init__(self, color, parent=None):
-        super(PromotionDialog, self).__init__(parent)
-
-        self.promotionTypes = ["q", "n", "r", "b"]
-
-        grid = QGridLayout()
-        hbox = QHBoxLayout()
-        grid.addLayout(hbox, 0, 0)
-
-        # Add the piece buttons.
-        self.buttonGroup = QButtonGroup(self)
-        for i, promotionType in enumerate(self.promotionTypes):
-            # Create an icon for the piece.
-            piece = Piece.from_color_and_type(color, promotionType)
-            renderer = QSvgRenderer(plugin_super_class.path_to_data('chess') + "classic-pieces/%s-%s.svg" % (piece.full_color, piece.full_type))
-            pixmap = QPixmap(32, 32)
-            pixmap.fill(Qt.transparent)
-            painter = QPainter()
-            painter.begin(pixmap)
-            renderer.render(painter, QRect(0, 0, 32, 32))
-            painter.end()
-
-            # Add the button.
-            button = QPushButton(QIcon(pixmap), '', self)
-            button.setCheckable(True)
-            self.buttonGroup.addButton(button, i)
-            hbox.addWidget(button)
-
-        self.buttonGroup.button(0).setChecked(True)
-
-        # Add the ok and cancel buttons.
-        buttons = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
-        buttons.rejected.connect(self.reject)
-        buttons.accepted.connect(self.accept)
-        grid.addWidget(buttons, 1, 0)
-
-        self.setLayout(grid)
-        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
-
-    def selectedType(self):
-        return self.promotionTypes[self.buttonGroup.checkedId()]
-
-
-class Chess(plugin_super_class.PluginSuperClass):
-
-    def __init__(self, *args):
-        super(Chess, self).__init__('Chess', 'chess', *args)
-        self.game = -1
-        self.board = None
-        self.white = True
-        self.pre = None
-        self.last_move = None
-        self.is_my_move = False
-        self._app = args[0]
-        self._profile=self._app._ms._profile
-        self._window = None
-
-    def get_description(self):
-        return QtWidgets.QApplication.translate("Chess", 'Plugin which allows you to play chess with your friends.')
-
-    def get_window(self):
-        inst = self
-        if not self.board:
-            self.board = Board(self)
-        if not hasattr(self, '_window') or not self._window:
-            self._window = self.board
-        return self.board
-
-    def lossless_packet(self, data, friend_number):
-        if data == 'new':
-            self.pre = None
-            friend = self._profile.get_friend_by_number(friend_number)
-            reply = QMessageBox.question(None,
-                                         'New chess game',
-                                         'Friend {} wants to play chess game against you. Start?'.format(friend.name),
-                                         QMessageBox.Yes,
-                                         QMessageBox.No)
-            if reply != QMessageBox.Yes:
-                self.send_lossless('no', friend_number)
-            else:
-                self.send_lossless('yes', friend_number)
-                self.board = Board(self)
-                self.board.show()
-                self.game = friend_number
-                self.white = False
-                self.is_my_move = False
-        elif data == 'yes' and friend_number == self.game:
-            self.board = Board(self)
-            self.board.show()
-            self.board.update_title(True)
-            self.is_my_move = True
-            self.last_move = None
-        elif data == 'no':
-            self.game = -1
-        elif data != self.pre:  # move
-            self.pre = data
-            self.is_my_move = True
-            self.last_move = None
-            a = Square.from_x_and_y(ord(data[0]) - ord('a'), ord(data[1]) - ord('1'))
-            b = Square.from_x_and_y(ord(data[2]) - ord('a'), ord(data[3]) - ord('1'))
-            self.board.position.make_move(Move(a, b, data[4] if len(data) == 5 else None))
-            self.board.repaint()
-            self.board.update_title(True)
-            self.board.ply += 1
-
-    def start_game(self, num):
-        self.white = True
-        self.send_lossless('new', num)
-        self.game = num
-
-    def resend_move(self):
-        if self.is_my_move or self.last_move is None:
-            return
-        self.send_lossless(str(self.last_move), self.game)
-        QTimer.singleShot(1000, self.resend_move)
-
-    def stop_game(self):
-        self.last_move = None
-
-    def move(self, move):
-        self.is_my_move = False
-        self.last_move = move
-        self.send_lossless(str(move), self.game)
-        self.board.update_title()
-        QTimer.singleShot(1000, self.resend_move)
-
-    def get_menu(self, menu, num):
-        act = QAction(QtWidgets.QApplication.translate("Chess", "Start chess game"), menu)
-        act.triggered.connect(lambda: self.start_game(num))
-        return [act]
diff --git a/toxygen/plugins/en_GB.ts b/toxygen/plugins/en_GB.ts
deleted file mode 100644
index b7be07c..0000000
--- a/toxygen/plugins/en_GB.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS><TS version="1.1">
-<context>
-    <name>BirthDay</name>
-    <message>
-        <location filename="bday.py" line="28"/>
-        <source>Birthday!</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="44"/>
-        <source>Send my birthday date to contacts</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="45"/>
-        <source>Birthday</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="50"/>
-        <source>Date in format dd.mm.yyyy</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="53"/>
-        <source>Save date</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-</TS>
diff --git a/toxygen/plugins/en_US.ts b/toxygen/plugins/en_US.ts
deleted file mode 100644
index b7be07c..0000000
--- a/toxygen/plugins/en_US.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS><TS version="1.1">
-<context>
-    <name>BirthDay</name>
-    <message>
-        <location filename="bday.py" line="28"/>
-        <source>Birthday!</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="44"/>
-        <source>Send my birthday date to contacts</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="45"/>
-        <source>Birthday</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="50"/>
-        <source>Date in format dd.mm.yyyy</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="53"/>
-        <source>Save date</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-</TS>
diff --git a/toxygen/plugins/garland.py b/toxygen/plugins/garland.py
deleted file mode 100644
index d6e1a0d..0000000
--- a/toxygen/plugins/garland.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import threading
-import time
-
-from  qtpy import QtCore, QtWidgets
-
-from plugins.plugin_super_class import PluginSuperClass
-
-class InvokeEvent(QtCore.QEvent):
-    EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
-
-    def __init__(self, fn, *args, **kwargs):
-        QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
-        self.fn = fn
-        self.args = args
-        self.kwargs = kwargs
-
-
-class Invoker(QtCore.QObject):
-
-    def event(self, event):
-        event.fn(*event.args, **event.kwargs)
-        return True
-
-_invoker = Invoker()
-
-
-def invoke_in_main_thread(fn, *args, **kwargs):
-    QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
-
-
-class Garland(PluginSuperClass):
-
-    def __init__(self, *args):
-        super(Garland, self).__init__('Garland', 'grlnd', *args)
-        self._thread = None
-        self._exec = None
-        self._time = 3
-        self._app = args[0]
-        self._profile=self._app._ms._profile
-        self._window = None
-
-    def get_description(self):
-        return QtWidgets.QApplication.translate("Garland", "Changes your status like it's garland.")
-
-    def close(self):
-        self.stop()
-
-    def stop(self):
-        self._exec = False
-        self._thread.join()
-
-    def start(self):
-        self._exec = True
-        self._thread = threading.Thread(target=self.change_status)
-        self._thread.start()
-
-    def command(self, command):
-        if command.startswith('time'):
-            self._time = max(int(command.split(' ')[1]), 300) / 1000
-        else:
-            super().command(command)
-
-    def update(self):
-        if hasattr(self, '_profile'):
-            if not hasattr(self._profile, 'status') or not self._profile.status:
-                retval = 0
-            else:
-                retval = (self._profile.status + 1) % 3
-            self._profile.set_status(retval)
-
-    def change_status(self):
-        time.sleep(5)
-        while self._exec:
-            invoke_in_main_thread(self.update)
-            time.sleep(self._time)
-
diff --git a/toxygen/plugins/mrq.py b/toxygen/plugins/mrq.py
deleted file mode 100644
index db718fe..0000000
--- a/toxygen/plugins/mrq.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import threading
-import time
-
-from  qtpy import QtCore, QtWidgets
-
-import plugin_super_class
-
-class InvokeEvent(QtCore.QEvent):
-    EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
-
-    def __init__(self, fn, *args, **kwargs):
-        QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
-        self.fn = fn
-        self.args = args
-        self.kwargs = kwargs
-
-
-class Invoker(QtCore.QObject):
-
-    def event(self, event):
-        event.fn(*event.args, **event.kwargs)
-        return True
-
-_invoker = Invoker()
-
-def invoke_in_main_thread(fn, *args, **kwargs):
-    QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))
-
-
-class MarqueeStatus(plugin_super_class.PluginSuperClass):
-
-    def __init__(self, *args):
-        super(MarqueeStatus, self).__init__('MarqueeStatus', 'mrq', *args)
-        self._thread = None
-        self._exec = None
-        self.active = False
-        self.left = True
-        self._app = args[0]
-        self._profile=self._app._ms._profile
-        self._window = None
-
-    def get_description(self):
-        return QtWidgets.QApplication.translate("MarqueeStatus", 'Create ticker from your status message.')
-
-    def close(self):
-        self.stop()
-
-    def stop(self):
-        self._exec = False
-        if self.active:
-            self._thread.join()
-
-    def start(self):
-        self._exec = True
-        self._thread = threading.Thread(target=self.change_status)
-        self._thread.start()
-
-    def command(self, command):
-        if command == 'rev':
-            self.left = not self.left
-        else:
-            super(MarqueeStatus, self).command(command)
-
-    def set_status_message(self):
-        message = str(self._profile.status_message)
-        if self.left:
-            self._profile.set_status_message(bytes(message[1:] + message[0], 'utf-8'))
-        else:
-            self._profile.set_status_message(bytes(message[-1] + message[:-1], 'utf-8'))
-
-    def init_status(self):
-        self._profile.status_message = bytes(self._profile.status_message.strip() + '   ', 'utf-8')
-
-    def change_status(self):
-        self.active = True
-        if hasattr(self, '_profile'):
-            tmp = self._profile.status_message
-            time.sleep(10)
-            invoke_in_main_thread(self.init_status)
-            while self._exec:
-                time.sleep(1)
-                if self._profile.status is not None:
-                    invoke_in_main_thread(self.set_status_message)
-            invoke_in_main_thread(self._profile.set_status_message, bytes(tmp, 'utf-8'))
-        self.active = False
-
diff --git a/toxygen/plugins/plugin_super_class.py b/toxygen/plugins/plugin_super_class.py
index 4c6287d..4eb833e 100644
--- a/toxygen/plugins/plugin_super_class.py
+++ b/toxygen/plugins/plugin_super_class.py
@@ -1,10 +1,9 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
 import os
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
 
-from  qtpy import QtCore, QtWidgets
-
-import utils.ui as util_ui
-import common.tox_save as tox_save
 
 MAX_SHORT_NAME_LENGTH = 5
 
@@ -12,6 +11,7 @@ LOSSY_FIRST_BYTE = 200
 
 LOSSLESS_FIRST_BYTE = 160
 
+
 def path_to_data(name):
     """
     :param name: plugin unique name
@@ -20,40 +20,46 @@ def path_to_data(name):
     return os.path.dirname(os.path.realpath(__file__)) + '/' + name + '/'
 
 
-def log(name, data=''):
+def log(name, data):
     """
     :param name: plugin unique name
     :param data: data for saving in log
     """
     with open(path_to_data(name) + 'logs.txt', 'a') as fl:
-        fl.write(str(data) + '\n')
+        fl.write(bytes(data, 'utf-8') + b'\n')
 
 
-class PluginSuperClass(tox_save.ToxSave):
+class PluginSuperClass:
     """
-    Superclass for all plugins. Plugin is Python3 module with at least one class derived from PluginSuperClass.
+    Superclass for all plugins. Plugin is python module with at least one class derived from PluginSuperClass.
     """
     is_plugin = True
 
-    def __init__(self, name, short_name, app):
+    def __init__(self, name, short_name, tox=None, profile=None, settings=None, encrypt_save=None):
         """
-        Constructor. In plugin __init__ should take only 1 last argument
+        Constructor. In plugin __init__ should take only 4 last arguments
         :param name: plugin full name
         :param short_name: plugin unique short name (length of short name should not exceed MAX_SHORT_NAME_LENGTH)
-        :param app: App instance
+        :param tox: tox instance
+        :param profile: profile instance
+        :param settings: profile settings
+        :param encrypt_save: LibToxEncryptSave instance.
         """
-        tox = getattr(app, '_tox')
-        super().__init__(tox)
-        self._settings = getattr(app, '_settings')
+        self._settings = settings
+        self._profile = profile
+        self._tox = tox
         name = name.strip()
         short_name = short_name.strip()
         if not name or not short_name:
-            raise NameError('Wrong name or not name or not short_name')
+            raise NameError('Wrong name')
         self._name = name
         self._short_name = short_name[:MAX_SHORT_NAME_LENGTH]
         self._translator = None  # translator for plugin's GUI
+        self._encrypt_save = encrypt_save
 
+    # -----------------------------------------------------------------------------------------------------------------
     # Get methods
+    # -----------------------------------------------------------------------------------------------------------------
 
     def get_name(self):
         """
@@ -73,19 +79,11 @@ class PluginSuperClass(tox_save.ToxSave):
         """
         return self.__doc__
 
-    def get_menu(self, menu, row_number=None):
+    def get_menu(self, menu, row_number):
         """
         This method creates items for menu which called on right click in list of friends
-        :param row_number: number of selected row in list of contacts
-        :return list of tuples (text, handler)
-        """
-        return []
-
-    def get_message_menu(self, menu, text):
-        """
-        This method creates items for menu which called on right click in message
         :param menu: menu instance
-        :param text: selected text
+        :param row_number: number of selected row in list of contacts
         :return list of QAction's
         """
         return []
@@ -96,7 +94,15 @@ class PluginSuperClass(tox_save.ToxSave):
         """
         return None
 
+    def set_tox(self, tox):
+        """
+        New tox instance
+        """
+        self._tox = tox
+
+    # -----------------------------------------------------------------------------------------------------------------
     # Plugin was stopped, started or new command received
+    # -----------------------------------------------------------------------------------------------------------------
 
     def start(self):
         """
@@ -114,7 +120,7 @@ class PluginSuperClass(tox_save.ToxSave):
         """
         App is closing
         """
-        self.stop()
+        pass
 
     def command(self, command):
         """
@@ -122,17 +128,21 @@ class PluginSuperClass(tox_save.ToxSave):
         :param command: string with command
         """
         if command == 'help':
-            text = util_ui.tr('No commands available')
-            title = util_ui.tr('List of commands for plugin {}').format(self._name)
-            util_ui.message_box(text, title)
+            msgbox = QtGui.QMessageBox()
+            title = QtGui.QApplication.translate("PluginWindow", "List of commands for plugin {}", None, QtGui.QApplication.UnicodeUTF8)
+            msgbox.setWindowTitle(title.format(self._name))
+            msgbox.setText(QtGui.QApplication.translate("PluginWindow", "No commands available", None, QtGui.QApplication.UnicodeUTF8))
+            msgbox.exec_()
 
+    # -----------------------------------------------------------------------------------------------------------------
     # Translations support
+    # -----------------------------------------------------------------------------------------------------------------
 
     def load_translator(self):
         """
         This method loads translations for GUI
         """
-        app = QtWidgets.QApplication.instance()
+        app = QtGui.QApplication.instance()
         langs = self._settings.supported_languages()
         curr_lang = self._settings['language']
         if curr_lang in langs:
@@ -143,12 +153,13 @@ class PluginSuperClass(tox_save.ToxSave):
             self._translator.load(path_to_data(self._short_name) + lang_path)
             app.installTranslator(self._translator)
 
+    # -----------------------------------------------------------------------------------------------------------------
     # Settings loading and saving
+    # -----------------------------------------------------------------------------------------------------------------
 
     def load_settings(self):
         """
         This method loads settings of plugin and returns raw data
-        If file doesn't exist this method raises exception
         """
         with open(path_to_data(self._short_name) + 'settings.json', 'rb') as fl:
             data = fl.read()
@@ -162,7 +173,9 @@ class PluginSuperClass(tox_save.ToxSave):
         with open(path_to_data(self._short_name) + 'settings.json', 'wb') as fl:
             fl.write(bytes(data, 'utf-8'))
 
+    # -----------------------------------------------------------------------------------------------------------------
     # Callbacks
+    # -----------------------------------------------------------------------------------------------------------------
 
     def lossless_packet(self, data, friend_number):
         """
@@ -180,13 +193,15 @@ class PluginSuperClass(tox_save.ToxSave):
         """
         pass
 
-    def friend_connected(self, friend_number:int):
+    def friend_connected(self, friend_number):
         """
         Friend with specified number is online now
         """
         pass
 
+    # -----------------------------------------------------------------------------------------------------------------
     # Custom packets sending
+    # -----------------------------------------------------------------------------------------------------------------
 
     def send_lossless(self, data, friend_number):
         """
diff --git a/toxygen/plugins/ru_RU.qm b/toxygen/plugins/ru_RU.qm
deleted file mode 100644
index 6ba937c..0000000
Binary files a/toxygen/plugins/ru_RU.qm and /dev/null differ
diff --git a/toxygen/plugins/ru_RU.ts b/toxygen/plugins/ru_RU.ts
deleted file mode 100644
index d5b0374..0000000
--- a/toxygen/plugins/ru_RU.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS>
-<TS version="2.0" language="ru_RU">
-<context>
-    <name>BirthDay</name>
-    <message>
-        <location filename="bday.py" line="28"/>
-        <source>Birthday!</source>
-        <translation>День рождения!</translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="44"/>
-        <source>Send my birthday date to contacts</source>
-        <translation>Отправлять дату моего рождения контактам</translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="45"/>
-        <source>Birthday</source>
-        <translation>День рождения</translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="50"/>
-        <source>Date in format dd.mm.yyyy</source>
-        <translation>Дата в формате дд.мм.гггг</translation>
-    </message>
-    <message>
-        <location filename="bday.py" line="53"/>
-        <source>Save date</source>
-        <translation>Сохранить дату</translation>
-    </message>
-</context>
-</TS>
diff --git a/toxygen/plugins/srch.pro b/toxygen/plugins/srch.pro
deleted file mode 100644
index d071285..0000000
--- a/toxygen/plugins/srch.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-SOURCES = srch.py
-TRANSLATIONS = srch/en_GB.ts srch/en_US.ts  srch/ru_RU.ts
diff --git a/toxygen/plugins/srch.py b/toxygen/plugins/srch.py
deleted file mode 100644
index 5dcf8d3..0000000
--- a/toxygen/plugins/srch.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import QtGui, QtCore, QtWidgets
-
-import plugin_super_class
-
-class SearchPlugin(plugin_super_class.PluginSuperClass):
-
-    def __init__(self, *args):
-        super(SearchPlugin, self).__init__('SearchPlugin', 'srch', *args)
-
-    def get_description(self):
-        return QtWidgets.QApplication.translate("SearchPlugin", 'Plugin search with search engines.')
-
-    def get_message_menu(self, menu, text):
-        google = QtWidgets.QAction(
-            QtWidgets.QApplication.translate("srch", "Find in Google"),
-            menu)
-        google.triggered.connect(lambda: self.google(text))
-
-        duck = QtWidgets.QAction(
-            QtWidgets.QApplication.translate("srch", "Find in DuckDuckGo"),
-            menu)
-        duck.triggered.connect(lambda: self.duck(text))
-
-        yandex = QtWidgets.QAction(
-            QtWidgets.QApplication.translate("srch", "Find in Yandex"),
-            menu)
-        yandex.triggered.connect(lambda: self.yandex(text))
-
-        bing = QtWidgets.QAction(
-            QtWidgets.QApplication.translate("srch", "Find in Bing"),
-            menu)
-        bing.triggered.connect(lambda: self.bing(text))
-
-        return [duck, google, yandex, bing]
-
-    def google(self, text):
-        url = QtCore.QUrl('https://www.google.com/search?q=' + text)
-        self.open_url(url)
-
-    def duck(self, text):
-        url = QtCore.QUrl('https://duckduckgo.com/?q=' + text)
-        self.open_url(url)
-
-    def yandex(self, text):
-        url = QtCore.QUrl('https://yandex.com/search/?text=' + text)
-        self.open_url(url)
-
-    def bing(self, text):
-        url = QtCore.QUrl('https://www.bing.com/search?q=' + text)
-        self.open_url(url)
-
-    def open_url(self, url):
-        QtGui.QDesktopServices.openUrl(url)
-
diff --git a/toxygen/plugins/toxid.pro b/toxygen/plugins/toxid.pro
deleted file mode 100644
index 3b1cc64..0000000
--- a/toxygen/plugins/toxid.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-SOURCES = toxid.py
-TRANSLATIONS = toxid/en_GB.ts toxid/en_US.ts toxid/ru_RU.ts
diff --git a/toxygen/plugins/toxid.py b/toxygen/plugins/toxid.py
deleted file mode 100644
index e604092..0000000
--- a/toxygen/plugins/toxid.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import json
-
-from  qtpy import QtCore, QtWidgets
-
-from plugins.plugin_super_class import PluginSuperClass
-
-class CopyableToxId(PluginSuperClass):
-
-    def __init__(self, *args):
-        super(CopyableToxId, self).__init__('CopyableToxId', 'toxid', *args)
-        self._data = json.loads(self.load_settings())
-        self._copy = False
-        self._curr = -1
-        self._timer = QtCore.QTimer()
-        self._timer.timeout.connect(lambda: self.timer())
-        self.load_translator()
-        self._app = args[0]
-        self._profile=self._app._ms._profile
-        self._window = None
-
-    def get_description(self):
-        return QtWidgets.QApplication.translate("TOXID", 'Plugin which allows you to copy TOX ID of your friends easily.')
-
-    def get_window(self):
-        inst = self
-
-        class Window(QtWidgets.QWidget):
-
-            def __init__(self):
-                super(Window, self).__init__()
-                self.setGeometry(QtCore.QRect(450, 300, 350, 100))
-                self.send = QtWidgets.QCheckBox(self)
-                self.send.setGeometry(QtCore.QRect(20, 10, 310, 25))
-                self.send.setText(QtWidgets.QApplication.translate("TOXID", "Send my TOX ID to contacts"))
-                self.setWindowTitle(QtWidgets.QApplication.translate("TOXID", "CopyableToxID"))
-                self.send.clicked.connect(self.update)
-                self.send.setChecked(inst._data['send_id'])
-                self.help = QtWidgets.QPushButton(self)
-                self.help.setGeometry(QtCore.QRect(20, 40, 200, 25))
-                self.help.setText(QtWidgets.QApplication.translate("TOXID", "List of commands"))
-                self.help.clicked.connect(lambda: inst.command('help'))
-
-            def update(self):
-                inst._data['send_id'] = self.send.isChecked()
-                inst.save_settings(json.dumps(inst._data))
-
-        if not hasattr(self, '_window') or not self._window:
-            self._window = Window()
-        return self._window
-
-    def lossless_packet(self, data, friend_number) -> None:
-        if len(data):
-            self._data['id'] = list(filter(lambda x: not x.startswith(data[:64]), self._data['id']))
-            self._data['id'].append(data)
-            if self._copy:
-                self._timer.stop()
-                self._copy = False
-                clipboard = QtWidgets.QApplication.clipboard()
-                clipboard.setText(data)
-            self.save_settings(json.dumps(self._data))
-        elif self._data['send_id']:
-            self.send_lossless(self._tox.self_get_address(), friend_number)
-
-    def error(self) -> None:
-        msgbox = QtWidgets.QMessageBox()
-        title = QtWidgets.QApplication.translate("TOXID", "Error")
-        msgbox.setWindowTitle(title.format(self._name))
-        text = QtWidgets.QApplication.translate("TOXID", "Tox ID cannot be copied")
-        msgbox.setText(text)
-        msgbox.exec_()
-
-    def timer(self) -> None:
-        self._copy = False
-        if self._curr + 1:
-            public_key = self._tox.friend_get_public_key(self._curr)
-            self._curr = -1
-            arr = list(filter(lambda x: x.startswith(public_key), self._data['id']))
-            if len(arr):
-                clipboard = QtWidgets.QApplication.clipboard()
-                clipboard.setText(arr[0])
-            else:
-                self.error()
-        else:
-            self.error()
-        self._timer.stop()
-
-    def friend_connected(self, friend_number:int):
-        self.send_lossless('', friend_number)
-
-    def command(self, text) -> None:
-        if text == 'copy':
-            num = self._profile.get_active_number()
-            if num == -1:
-                return
-        elif text.startswith('copy '):
-            num = int(text[5:])
-            if num < 0:
-                return
-        elif text == 'enable':
-            self._copy = True
-            return
-        elif text == 'disable':
-            self._copy = False
-            return
-        elif text == 'help':
-            msgbox = QtWidgets.QMessageBox()
-            title = QtWidgets.QApplication.translate("TOXID", "List of commands for plugin CopyableToxID")
-            msgbox.setWindowTitle(title)
-            text = QtWidgets.QApplication.translate("TOXID", """Commands:
-copy: copy TOX ID of current friend
-copy <friend_number>: copy TOX ID of friend with specified number
-enable: allow send your TOX ID to friends
-disable: disallow send your TOX ID to friends
-help: show this help""")
-            msgbox.setText(text)
-            msgbox.exec_()
-            return
-        else:
-            return
-        public_key = self._tox.friend_get_public_key(num)
-        arr = list(filter(lambda x: x.startswith(public_key), self._data['id']))
-        if self._profile.get_friend_by_number(num).status is None and len(arr):
-            clipboard = QtWidgets.QApplication.clipboard()
-            clipboard.setText(arr[0])
-        elif self._profile.get_friend_by_number(num).status is not None:
-            self._copy = True
-            self._curr = num
-            self.send_lossless('', num)
-            self._timer.start(2000)
-        else:
-            self.error()
-
-    def get_menu(self, menu, num) -> list:
-        act = QtWidgets.QAction(QtWidgets.QApplication.translate("TOXID", "Copy TOX ID"), menu)
-        friend = self._profile.get_friend(num)
-        act.connect(act, QtCore.Signal("triggered()"),
-                    lambda: self.command('copy ' + str(friend.number)))
-        return [act]
diff --git a/toxygen/profile.py b/toxygen/profile.py
new file mode 100644
index 0000000..fc54a31
--- /dev/null
+++ b/toxygen/profile.py
@@ -0,0 +1,1355 @@
+from list_items import *
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+from friend import *
+from settings import *
+from toxcore_enums_and_consts import *
+from ctypes import *
+from util import log, Singleton, curr_directory
+from tox_dns import tox_dns
+from history import *
+from file_transfers import *
+import time
+import calls
+import avwidgets
+import plugin_support
+import basecontact
+from groupchat import *
+
+
+class Profile(basecontact.BaseContact, Singleton):
+    """
+    Profile of current toxygen user. Contains friends list, tox instance
+    """
+    def __init__(self, tox, screen):
+        """
+        :param tox: tox instance
+        :param screen: ref to main screen
+        """
+        basecontact.BaseContact.__init__(self,
+                                         tox.self_get_name(),
+                                         tox.self_get_status_message(),
+                                         screen.user_info,
+                                         tox.self_get_address())
+        Singleton.__init__(self)
+        self._screen = screen
+        self._messages = screen.messages
+        self._tox = tox
+        self._file_transfers = {}  # dict of file transfers. key - tuple (friend_number, file_number)
+        self._call = calls.AV(tox.AV)  # object with data about calls
+        self._incoming_calls = set()
+        self._load_history = True
+        self._gc_invites = {}  # dict of gc invites. key - friend number, value - list of gc data
+        settings = Settings.get_instance()
+        self._show_online = settings['show_online_friends']
+        screen.online_contacts.setCurrentIndex(int(self._show_online))
+        aliases = settings['friends_aliases']
+        data = tox.self_get_friend_list()
+        self._history = History(tox.self_get_public_key())  # connection to db
+        self._friends_and_gc, self._active_friend_or_gc = [], -1
+        for i in data:  # creates list of friends
+            tox_id = tox.friend_get_public_key(i)
+            try:
+                alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1]
+            except:
+                alias = ''
+            item = self.create_friend_item()
+            name = alias or tox.friend_get_name(i) or tox_id
+            status_message = tox.friend_get_status_message(i)
+            if not self._history.friend_exists_in_db(tox_id):
+                self._history.add_friend_to_db(tox_id)
+            message_getter = self._history.messages_getter(tox_id)
+            friend = Friend(i, message_getter, name, status_message, item, tox_id)
+            friend.set_alias(alias)
+            self._friends_and_gc.append(friend)
+
+        l = self._tox.group_get_number_groups()
+        for i in range(l):  # creates list of group chats
+            tox_id = tox.group_get_chat_id(i)
+            try:
+                alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1]
+            except:
+                alias = ''
+            item = self.create_friend_item()
+            name = alias or tox.group_get_name(i) or tox_id
+            status_message = tox.group_get_topic(i)
+            if not self._history.friend_exists_in_db(tox_id):
+                self._history.add_friend_to_db(tox_id)
+            message_getter = self._history.messages_getter(tox_id)
+            gc = GroupChat(self._tox, i, message_getter, name, status_message, item, tox_id)
+            gc.set_alias(alias)
+            self._friends_and_gc.append(gc)
+
+        self.filtration(self._show_online)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Edit current user's data
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def change_status(self):
+        """
+        Changes status of user (online, away, busy)
+        """
+        if self._status is not None:
+            self.set_status((self._status + 1) % 3)
+
+    def set_status(self, status):
+        if self.status is None:
+            for gc in filter(lambda x: type(x) is GroupChat, self._friends_and_gc):
+                self._tox.group_reconnect(gc.number)
+        super(Profile, self).set_status(status)
+        if status is not None:
+            self._tox.self_set_status(status)
+
+    def set_name(self, value):
+        if self.name == value:
+            return
+        tmp = self.name
+        super(Profile, self).set_name(value.encode('utf-8'))
+        self._tox.self_set_name(self._name.encode('utf-8'))
+        message = QtGui.QApplication.translate("MainWindow", 'User {} is now known as {}', None,
+                                               QtGui.QApplication.UnicodeUTF8)
+        message = message.format(tmp, str(value, 'utf-8'))
+        for friend in self._friends_and_gc:
+            friend.append_message(InfoMessage(message, time.time()))
+        if self._active_friend_or_gc + 1:
+            self.create_message_item(message, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
+            self._messages.scrollToBottom()
+
+    def set_status_message(self, value):
+        super(Profile, self).set_status_message(value)
+        self._tox.self_set_status_message(self._status_message.encode('utf-8'))
+
+    def new_nospam(self):
+        """Sets new nospam part of tox id"""
+        import random
+        self._tox.self_set_nospam(random.randint(0, 4294967295))  # no spam - uint32
+        self._tox_id = self._tox.self_get_address()
+        return self._tox_id
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Filtration
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def filtration(self, show_online=True, filter_str=''):
+        """
+        Filtration of friends list
+        :param show_online: show online only contacts
+        :param filter_str: show contacts which name contains this substring
+        """
+        filter_str = filter_str.lower()
+        settings = Settings.get_instance()
+        for index, friend in enumerate(self._friends_and_gc):
+            friend.visibility = (friend.status is not None or not show_online) and (filter_str in friend.name.lower())
+            friend.visibility = friend.visibility or friend.messages or friend.actions
+            if friend.visibility:
+                self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250,
+                                                                               40 if settings['compact_mode'] else 70))
+            else:
+                self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 0))
+        self._show_online, self._filter_string = show_online, filter_str
+        settings['show_online_friends'] = self._show_online
+        settings.save()
+
+    def update_filtration(self):
+        """
+        Update list of contacts when 1 of friends change connection status
+        """
+        self.filtration(self._show_online, self._filter_string)
+
+    def get_friend_by_number(self, num):
+        return list(filter(lambda x: x.number == num and type(x) is Friend, self._friends_and_gc))[0]
+
+    def get_gc_by_number(self, num):
+        return list(filter(lambda x: x.number == num and type(x) is not Friend, self._friends_and_gc))[0]
+
+    def get_friend_or_gc(self, num):
+        return self._friends_and_gc[num]
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Work with active friend
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_active(self):
+        return self._active_friend_or_gc
+
+    def set_active(self, value=None):
+        """
+        Change current active friend or update info
+        :param value: number of new active friend in friend's list or None to update active user's data
+        """
+        if value is None and self._active_friend_or_gc == -1:  # nothing to update
+            return
+        if value == -1:  # all friends were deleted
+            self._screen.account_name.setText('')
+            self._screen.account_status.setText('')
+            self._active_friend_or_gc = -1
+            self._screen.account_avatar.setHidden(True)
+            self._messages.clear()
+            self._screen.messageEdit.clear()
+            return
+        try:
+            self.send_typing(False)
+            self._screen.typing.setVisible(False)
+            if value is not None:
+                if self._active_friend_or_gc + 1:
+                    try:
+                        self._friends_and_gc[self._active_friend_or_gc].curr_text = self._screen.messageEdit.toPlainText()
+                    except:
+                        pass
+                self._active_friend_or_gc = value
+                friend_or_gc = self._friends_and_gc[value]
+                friend_or_gc.reset_messages()
+                friend_or_gc.delete_old_messages()
+                self._screen.messageEdit.setPlainText(friend_or_gc.curr_text)
+                self._messages.clear()
+                friend_or_gc.load_corr()
+                messages = friend_or_gc.get_corr()[-PAGE_SIZE:]
+                self._load_history = False
+
+                for message in messages:
+                    if message.get_type() <= 1:
+                        data = message.get_data()
+                        self.create_message_item(data[0],
+                                                 data[2],
+                                                 data[1],
+                                                 data[3],
+                                                 True,
+                                                 data[4] if len(data) == 5 else None)
+                    elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']:
+                        if message.get_status() is None:
+                            self.create_unsent_file_item(message)
+                            continue
+                        item = self.create_file_transfer_item(message)
+                        if message.get_status() in ACTIVE_FILE_TRANSFERS:  # active file transfer
+                            try:
+                                ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
+                                ft.set_state_changed_handler(item.update)
+                                ft.signal()
+                            except:
+                                print('Incoming not started transfer - no info found')
+                    elif message.get_type() == MESSAGE_TYPE['INLINE']:  # inline
+                        self.create_inline_item(message.get_data())
+                    else:  # info message
+                        data = message.get_data()
+                        self.create_message_item(data[0],
+                                                 data[2],
+                                                 '',
+                                                 data[3])
+                self._messages.scrollToBottom()
+                self._load_history = True
+                if value in self._call:
+                    self._screen.active_call()
+                elif value in self._incoming_calls:
+                    self._screen.incoming_call()
+                else:
+                    self._screen.call_finished()
+            else:
+                friend_or_gc = self._friends_and_gc[self._active_friend_or_gc]
+
+            self._screen.account_name.setText(friend_or_gc.name)
+            self._screen.account_status.setText(friend_or_gc.status_message)
+            avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(friend_or_gc.tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
+            if not os.path.isfile(avatar_path):  # load default image
+                if type(friend_or_gc) is Friend:
+                    avatar_path = curr_directory() + '/images/avatar.png'
+                else:
+                    avatar_path = curr_directory() + '/images/group.png'
+            os.chdir(os.path.dirname(avatar_path))
+            pixmap = QtGui.QPixmap(QtCore.QSize(64, 64))
+            pixmap.load(avatar_path)
+            self._screen.account_avatar.setScaledContents(False)
+            self._screen.account_avatar.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio))
+            self._screen.account_avatar.repaint()  # comment?
+            self.update_filtration()
+        except Exception as ex:  # no friend found. ignore
+            log('Friend value: ' + str(value))
+            log('Error: ' + str(ex))
+            raise
+
+    active_friend = property(get_active, set_active)
+
+    def is_active_a_friend(self):
+        return type(self._friends_and_gc[self._active_friend_or_gc]) is Friend
+
+    def get_last_message(self):
+        return self._friends_and_gc[self._active_friend_or_gc].get_last_message_text()
+
+    def get_active_number(self):
+        return self._friends_and_gc[self._active_friend_or_gc].number if self._active_friend_or_gc + 1 else -1
+
+    def get_active_name(self):
+        return self._friends_and_gc[self._active_friend_or_gc].name if self._active_friend_or_gc + 1 else ''
+
+    def is_active_online(self):
+        return self._active_friend_or_gc + 1 and self._friends_and_gc[self._active_friend_or_gc].status is not None
+
+    def new_name(self, number, name):
+        friend = self.get_friend_by_number(number)
+        tmp = friend.name
+        friend.set_name(name)
+        name = str(name, 'utf-8')
+        if friend.name == name and tmp != name:
+            message = QtGui.QApplication.translate("MainWindow", 'User {} is now known as {}', None, QtGui.QApplication.UnicodeUTF8)
+            message = message.format(tmp, name)
+            friend.append_message(InfoMessage(message, time.time()))
+            friend.actions = True
+            if number == self.get_active_number():
+                self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
+                self._messages.scrollToBottom()
+            self.set_active(None)
+
+    def update(self):
+        if self._active_friend_or_gc + 1:
+            self.set_active(self._active_friend_or_gc)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Friend connection status callbacks
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def send_files(self, friend_number):
+        friend = self.get_friend_by_number(friend_number)
+        files = friend.get_unsent_files()
+        try:
+            for fl in files:
+                data = fl.get_data()
+                if data[1] is not None:
+                    self.send_inline(data[1], data[0], friend_number, True)
+                else:
+                    self.send_file(data[0], friend_number, True)
+            friend.clear_unsent_files()
+            if friend_number == self.get_active_number():
+                self.update()
+        except Exception as ex:
+            print('Exception in file sending: ' + str(ex))
+
+    def friend_exit(self, friend_number):
+        """
+        Friend with specified number quit
+        """
+        # TODO: fix and add full file resuming support
+        self.get_friend_by_number(friend_number).status = None
+        self.friend_typing(friend_number, False)
+        if friend_number in self._call:
+            self._call.finish_call(friend_number, True)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Typing notifications
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def send_typing(self, typing):
+        """
+        Send typing notification to a friend
+        """
+        if Settings.get_instance()['typing_notifications'] and self._active_friend_or_gc + 1:
+            friend = self._friends_and_gc[self._active_friend_or_gc]
+            if friend.status is not None:
+                self._tox.self_set_typing(friend.number, typing)
+
+    def friend_typing(self, friend_number, typing):
+        """
+        Display incoming typing notification
+        """
+        if friend_number == self.get_active_number():
+            self._screen.typing.setVisible(typing)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Private messages
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def receipt(self):
+        i = 0
+        while i < self._messages.count() and not self._messages.itemWidget(self._messages.item(i)).mark_as_sent():
+            i += 1
+
+    def send_messages(self, friend_number):
+        """
+        Send 'offline' messages to friend
+        """
+        friend = self.get_friend_by_number(friend_number)
+        friend.load_corr()
+        messages = friend.get_unsent_messages()
+        try:
+            for message in messages:
+                self.split_and_send(friend_number, message.get_data()[-1], message.get_data()[0].encode('utf-8'))
+                friend.inc_receipts()
+        except:
+            pass
+
+    def split_and_send(self, number, message_type, message, is_group=False):
+        """
+        Message splitting
+        :param number: friend or gc number
+        :param message_type: type of message
+        :param message: message text
+        :param is_group: send to group
+        """
+        while len(message) > TOX_MAX_MESSAGE_LENGTH:
+            size = TOX_MAX_MESSAGE_LENGTH * 4 / 5
+            last_part = message[size:TOX_MAX_MESSAGE_LENGTH]
+            if ' ' in last_part:
+                index = last_part.index(' ')
+            elif ',' in last_part:
+                index = last_part.index(',')
+            elif '.' in last_part:
+                index = last_part.index('.')
+            else:
+                index = TOX_MAX_MESSAGE_LENGTH - size - 1
+            index += size + 1
+            if not is_group:
+                self._tox.friend_send_message(number, message_type, message[:index])
+            else:
+                self._tox.group_send_message(number, message_type, message[:index])
+            message = message[index:]
+        if not is_group:
+            self._tox.friend_send_message(number, message_type, message)
+        else:
+            self._tox.group_send_message(number, message_type, message)
+
+    def new_message(self, num, message_type, message, is_group=False, peer_id=-1):
+        """
+        Current user gets new message
+        :param num: num of friend or gc who sent message
+        :param message_type: message type - plain text or action message (/me)
+        :param message: text of message
+        :param is_group: is group chat message or not
+        :param peer_id: if gc - peer id
+        """
+        t = time.time()
+        active = False
+        if num == self.get_active_number() and is_group != self.is_active_a_friend():  # add message to list
+            if not is_group:
+                self.create_message_item(message, t, MESSAGE_OWNER['FRIEND'], message_type)
+            else:
+                self.create_message_item(message, t, MESSAGE_OWNER['FRIEND'], message_type, True,
+                                         self._tox.group_peer_get_name(num, peer_id))
+            self._messages.scrollToBottom()
+            active = True
+        if is_group:
+            friend_or_gc = self.get_gc_by_number(num)
+            friend_or_gc.append_message(GroupChatTextMessage(self._tox.group_peer_get_name(num, peer_id),
+                                                             message, MESSAGE_OWNER['FRIEND'],
+                                                             time.time(), message_type))
+        else:
+            friend_or_gc = self.get_friend_by_number(num)
+            friend_or_gc.append_message(TextMessage(message, MESSAGE_OWNER['FRIEND'], time.time(), message_type))
+        if not active:
+            friend_or_gc.inc_messages()
+        if not friend_or_gc.visibility:
+            self.update_filtration()
+
+    def send_message(self, text, number=None, is_gc=False):
+        """
+        Send message
+        :param text: message text
+        :param number: num of friend or gc
+        :param is_gc: is group chat
+        """
+        if number is None:
+            number = self.get_active_number()
+            is_gc = not self.is_active_a_friend()
+        if text.startswith('/plugin '):
+            plugin_support.PluginLoader.get_instance().command(text[8:])
+            self._screen.messageEdit.clear()
+        elif text and number + 1:
+            text = ''.join(c if c <= '\u10FFFF' else '\u25AF' for c in text)
+
+            if text.startswith('/me '):
+                message_type = TOX_MESSAGE_TYPE['ACTION']
+                text = text[4:]
+            else:
+                message_type = TOX_MESSAGE_TYPE['NORMAL']
+
+            if not is_gc:
+                friend_or_gc = self.get_friend_by_number(number)
+            else:
+                friend_or_gc = self.get_gc_by_number(number)
+            t = time.time()
+
+            if not is_gc:
+                friend_or_gc.inc_receipts()
+                if friend_or_gc.status is not None:
+                    self.split_and_send(friend_or_gc.number, message_type, text.encode('utf-8'))
+                if friend_or_gc.number == self.get_active_number() and self.is_active_a_friend():
+                    self.create_message_item(text, t, MESSAGE_OWNER['NOT_SENT'], message_type)
+                    self._screen.messageEdit.clear()
+                    self._messages.scrollToBottom()
+                friend_or_gc.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], t, message_type))
+            else:
+                self.split_and_send(friend_or_gc.number, message_type, text.encode('utf-8'), True)
+                if friend_or_gc.number == self.get_active_number() and not self.is_active_a_friend():
+                    self.create_message_item(text, t, MESSAGE_OWNER['ME'], message_type)
+                    self._screen.messageEdit.clear()
+                    self._messages.scrollToBottom()
+                friend_or_gc.append_message(TextMessage(text, MESSAGE_OWNER['ME'], t, message_type))
+
+    def delete_message(self, time):
+        friend = self._friends_and_gc[self._active_friend_or_gc]
+        friend.delete_message(time)
+        self._history.delete_message(friend.tox_id, time)
+        self.update()
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # History support
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def save_history(self):
+        """
+        Save history to db
+        """
+        s = Settings.get_instance()
+        # TODO: different saving for friends and gc
+        if hasattr(self, '_history'):
+            if s['save_history']:
+                for friend_or_gc in self._friends_and_gc:
+                    if not self._history.friend_exists_in_db(friend_or_gc.tox_id):
+                        self._history.add_friend_to_db(friend_or_gc.tox_id)
+                    if not s['save_unsent_only']:
+                        messages = friend_or_gc.get_corr_for_saving()
+                    else:
+                        messages = friend_or_gc.get_unsent_messages_for_saving()
+                        self._history.delete_messages(friend_or_gc.tox_id)
+                    self._history.save_messages_to_db(friend_or_gc.tox_id, messages)
+                    unsent_messages = friend_or_gc.get_unsent_messages()
+                    unsent_time = unsent_messages[0].get_data()[2] if len(unsent_messages) else time.time() + 1
+                    self._history.update_messages(friend_or_gc.tox_id, unsent_time)
+            self._history.save()
+            del self._history
+
+    def clear_history(self, num=None, save_unsent=False):
+        """
+        Clear chat history
+        """
+        if num is not None:
+            friend = self._friends_and_gc[num]
+            friend.clear_corr(save_unsent)
+            if self._history.friend_exists_in_db(friend.tox_id):
+                self._history.delete_messages(friend.tox_id)
+                self._history.delete_friend_from_db(friend.tox_id)
+        else:  # clear all history
+            for number in range(len(self._friends_and_gc)):
+                self.clear_history(number, save_unsent)
+
+        if num is None or num == self.get_active_number():
+            self.update()
+
+    def load_history(self):
+        """
+        Tries to load next part of messages
+        """
+        if not self._load_history:
+            return
+        self._load_history = False
+        friend_or_gc = self._friends_and_gc[self._active_friend_or_gc]
+
+        friend_or_gc.load_corr(False)
+        data = friend_or_gc.get_corr()
+        if not data:
+            return
+        data.reverse()
+        data = data[self._messages.count():self._messages.count() + PAGE_SIZE]
+        for message in data:
+            if message.get_type() <= 1:  # text message
+                data = message.get_data()
+                self.create_message_item(data[0],
+                                         data[2],
+                                         data[1],
+                                         data[3],
+                                         False)
+            elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']:
+                if message.get_status() is None:
+                    self.create_unsent_file_item(message)
+                    continue
+                item = self.create_file_transfer_item(message, False)
+                if message.get_status() in ACTIVE_FILE_TRANSFERS:  # active file transfer
+                    try:
+                        ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
+                        ft.set_state_changed_handler(item.update)
+                        ft.signal()
+                    except:
+                        print('Incoming not started transfer - no info found')
+            elif message.get_type() == MESSAGE_TYPE['INLINE']:  # inline
+                self.create_inline_item(message.get_data())
+            else:  # info message
+                data = message.get_data()
+                self.create_message_item(data[0],
+                                         data[2],
+                                         '',
+                                         data[3])
+        self._load_history = True
+
+    def export_history(self, directory):
+        self._history.export(directory)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Factories for friend, message and file transfer items
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def create_friend_item(self):
+        """
+        Method-factory
+        :return: new widget for friend instance
+        """
+        item = ContactItem()
+        elem = QtGui.QListWidgetItem(self._screen.friends_list)
+        elem.setSizeHint(QtCore.QSize(250, item.height()))
+        self._screen.friends_list.addItem(elem)
+        self._screen.friends_list.setItemWidget(elem, item)
+        return item
+
+    def create_message_item(self, text, time, owner, message_type, append=True, friend_name=None):
+        if message_type == MESSAGE_TYPE['INFO_MESSAGE']:
+            name = ''
+        elif owner == MESSAGE_OWNER['FRIEND']:
+            name = friend_name or self.get_active_name()
+        else:
+            name = self._name
+        item = MessageItem(text, time, name, owner != MESSAGE_OWNER['NOT_SENT'], message_type, self._messages)
+        elem = QtGui.QListWidgetItem()
+        elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
+        if append:
+            self._messages.addItem(elem)
+        else:
+            self._messages.insertItem(0, elem)
+        self._messages.setItemWidget(elem, item)
+
+    def create_file_transfer_item(self, tm, append=True):
+        data = list(tm.get_data())
+        data[3] = self.get_friend_by_number(data[4]).name if data[3] else self._name
+        data.append(self._messages.width())
+        item = FileTransferItem(*data)
+        elem = QtGui.QListWidgetItem()
+        elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34))
+        if append:
+            self._messages.addItem(elem)
+        else:
+            self._messages.insertItem(0, elem)
+        self._messages.setItemWidget(elem, item)
+        return item
+
+    def create_unsent_file_item(self, message, append=True):
+        data = message.get_data()
+        item = UnsentFileItem(os.path.basename(data[0]),
+                              os.path.getsize(data[0]) if data[1] is None else len(data[1]),
+                              self.name,
+                              data[2],
+                              self._messages.width())
+        elem = QtGui.QListWidgetItem()
+        elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34))
+        if append:
+            self._messages.addItem(elem)
+        else:
+            self._messages.insertItem(0, elem)
+        self._messages.setItemWidget(elem, item)
+
+    def create_inline_item(self, data, append=True):
+        elem = QtGui.QListWidgetItem()
+        item = InlineImageItem(data, self._messages.width(), elem)
+        elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
+        if append:
+            self._messages.addItem(elem)
+        else:
+            self._messages.insertItem(0, elem)
+        self._messages.setItemWidget(elem, item)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Work with friends (remove, block, set alias, get public key)
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def set_alias(self, num):
+        """
+        Set new alias for friend
+        """
+        friend = self._friends_and_gc[num]
+        name = friend.name
+        dialog = QtGui.QApplication.translate('MainWindow',
+                                              "Enter new alias for friend {} or leave empty to use friend's name:",
+                                              None, QtGui.QApplication.UnicodeUTF8)
+        dialog = dialog.format(name)
+        title = QtGui.QApplication.translate('MainWindow',
+                                             'Set alias',
+                                             None, QtGui.QApplication.UnicodeUTF8)
+        text, ok = QtGui.QInputDialog.getText(None,
+                                              title,
+                                              dialog,
+                                              QtGui.QLineEdit.Normal,
+                                              name)
+        if ok:
+            settings = Settings.get_instance()
+            aliases = settings['friends_aliases']
+            if text:
+                friend.name = bytes(text, 'utf-8')
+                try:
+                    index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
+                    aliases[index] = (friend.tox_id, text)
+                except:
+                    aliases.append((friend.tox_id, text))
+                friend.set_alias(text)
+            else:  # use default name
+                friend.name = bytes(self._tox.friend_get_name(friend.number), 'utf-8')
+                friend.set_alias('')
+                try:
+                    index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
+                    del aliases[index]
+                except:
+                    pass
+            settings.save()
+        if num == self.get_active_number():
+            self.update()
+
+    def friend_public_key(self, num):
+        return self._friends_and_gc[num].tox_id
+
+    def delete_friend_or_gc(self, num, is_gc=False, message=None):
+        """
+        Removes friend or gc from contact list
+        :param num: number of friend or gc in list
+        :param is_gc: is a group chat
+        :param message: message in gc
+        """
+        friend = self._friends_and_gc[num]
+        settings = Settings.get_instance()
+        try:
+            index = list(map(lambda x: x[0], settings['friends_aliases'])).index(friend.tox_id)
+            del settings['friends_aliases'][index]
+        except:
+            pass
+        if friend.tox_id in settings['notes']:
+            del settings['notes'][friend.tox_id]
+        settings.save()
+        self.clear_history(num)
+        if self._history.friend_exists_in_db(friend.tox_id):
+            self._history.delete_friend_from_db(friend.tox_id)
+        if not is_gc:
+            self._tox.friend_delete(friend.number)
+        else:
+            self._tox.group_leave(friend.number, message.encode('utf-8') if message is not None else None)
+        del self._friends_and_gc[num]
+        self._screen.friends_list.takeItem(num)
+        if num == self._active_friend_or_gc:  # active friend or gc was deleted
+            if not len(self._friends_and_gc):  # last contact was deleted
+                self.set_active(-1)
+            else:
+                self.set_active(0)
+        data = self._tox.get_savedata()
+        ProfileHelper.get_instance().save_profile(data)
+
+    def add_friend(self, tox_id):
+        """
+        Adds friend to list
+        """
+        num = self._tox.friend_add_norequest(tox_id)  # num - friend number
+        item = self.create_friend_item()
+        try:
+            if not self._history.friend_exists_in_db(tox_id):
+                self._history.add_friend_to_db(tox_id)
+            message_getter = self._history.messages_getter(tox_id)
+        except Exception as ex:  # something is wrong
+            log('Accept friend request failed! ' + str(ex))
+            message_getter = None
+        friend = Friend(message_getter, num, tox_id, '', item, tox_id)
+        self._friends_and_gc.append(friend)
+
+    def block_user(self, tox_id):
+        """
+        Block user with specified tox id (or public key) - delete from friends list and ignore friend requests
+        """
+        tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
+        if tox_id == self.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]:
+            return
+        settings = Settings.get_instance()
+        if tox_id not in settings['blocked']:
+            settings['blocked'].append(tox_id)
+            settings.save()
+        try:
+            num = self._tox.friend_by_public_key(tox_id)
+            self.delete_friend_or_gc(num)
+            data = self._tox.get_savedata()
+            ProfileHelper.get_instance().save_profile(data)
+        except:  # not in friend list
+            pass
+
+    def unblock_user(self, tox_id, add_to_friend_list):
+        """
+        Unblock user
+        :param tox_id: tox id of contact
+        :param add_to_friend_list: add this contact to friend list or not
+        """
+        s = Settings.get_instance()
+        s['blocked'].remove(tox_id)
+        s.save()
+        if add_to_friend_list:
+            self.add_friend(tox_id)
+            data = self._tox.get_savedata()
+            ProfileHelper.get_instance().save_profile(data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Friend requests
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def send_friend_request(self, tox_id, message):
+        """
+        Function tries to send request to contact with specified id
+        :param tox_id: id of new contact or tox dns 4 value
+        :param message: additional message
+        :return: True on success else error string
+        """
+        try:
+            message = message or 'Hello! Add me to your contact list please'
+            if '@' in tox_id:  # value like groupbot@toxme.io
+                tox_id = tox_dns(tox_id)
+                if tox_id is None:
+                    raise Exception('TOX DNS lookup failed')
+            if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2:  # public key
+                self.add_friend(tox_id)
+                msgBox = QtGui.QMessageBox()
+                msgBox.setWindowTitle(QtGui.QApplication.translate("MainWindow", "Friend added", None, QtGui.QApplication.UnicodeUTF8))
+                text = (QtGui.QApplication.translate("MainWindow", 'Friend added without sending friend request', None, QtGui.QApplication.UnicodeUTF8))
+                msgBox.setText(text)
+                msgBox.exec_()
+            else:
+                result = self._tox.friend_add(tox_id, message.encode('utf-8'))
+                tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
+                item = self.create_friend_item()
+                if not self._history.friend_exists_in_db(tox_id):
+                    self._history.add_friend_to_db(tox_id)
+                message_getter = self._history.messages_getter(tox_id)
+                friend = Friend(message_getter, result, tox_id, '', item, tox_id)
+                self._friends_and_gc.append(friend)
+            data = self._tox.get_savedata()
+            ProfileHelper.get_instance().save_profile(data)
+            return True
+        except Exception as ex:  # wrong data
+            log('Friend request failed with ' + str(ex))
+            return str(ex)
+
+    def process_friend_request(self, tox_id, message):
+        """
+        Accept or ignore friend request
+        :param tox_id: tox id of contact
+        :param message: message
+        """
+        try:
+            text = QtGui.QApplication.translate('MainWindow', 'User {} wants to add you to contact list. Message:\n{}', None, QtGui.QApplication.UnicodeUTF8)
+            info = text.format(tox_id, message)
+            fr_req = QtGui.QApplication.translate('MainWindow', 'Friend request', None, QtGui.QApplication.UnicodeUTF8)
+            reply = QtGui.QMessageBox.question(None, fr_req, info, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
+            if reply == QtGui.QMessageBox.Yes:  # accepted
+                self.add_friend(tox_id)
+                data = self._tox.get_savedata()
+                ProfileHelper.get_instance().save_profile(data)
+        except Exception as ex:  # something is wrong
+            log('Accept friend request failed! ' + str(ex))
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Reset
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def reset(self, restart):
+        """
+        Recreate tox instance
+        :param restart: method which calls restart and returns new tox instance
+        """
+        # TODO: file transfers!!
+        for key in list(self._file_transfers.keys()):
+            self._file_transfers[key].cancelled()
+            del self._file_transfers[key]
+        self._call.stop()
+        del self._tox
+        self._tox = restart()
+        self.status = None
+        for friend in self._friends_and_gc:
+            friend.status = None
+        self.update_filtration()
+
+    def close(self):
+        if hasattr(self, '_call'):
+            self._call.stop()
+            del self._call
+        for i in range(len(self._friends_and_gc)):
+            del self._friends_and_gc[0]
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # File transfers support
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def incoming_file_transfer(self, friend_number, file_number, size, file_name):
+        """
+        New transfer
+        :param friend_number: number of friend who sent file
+        :param file_number: file number
+        :param size: file size in bytes
+        :param file_name: file name without path
+        """
+        settings = Settings.get_instance()
+        friend = self.get_friend_by_number(friend_number)
+        auto = settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']
+        inline = (file_name in ALLOWED_FILES) and settings['allow_inline']
+        if inline and size < 1024 * 1024:
+            self.accept_transfer(None, '', friend_number, file_number, size, True)
+            tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
+                                 time.time(),
+                                 TOX_FILE_TRANSFER_STATE['RUNNING'],
+                                 size,
+                                 file_name,
+                                 friend_number,
+                                 file_number)
+
+        elif auto:
+            path = settings['auto_accept_path'] or curr_directory()
+            self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size)
+            tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
+                                 time.time(),
+                                 TOX_FILE_TRANSFER_STATE['RUNNING'],
+                                 size,
+                                 file_name,
+                                 friend_number,
+                                 file_number)
+        else:
+            tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
+                                 time.time(),
+                                 TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'],
+                                 size,
+                                 file_name,
+                                 friend_number,
+                                 file_number)
+        if friend_number == self.get_active_number():
+            item = self.create_file_transfer_item(tm)
+            if (inline and size < 1024 * 1024) or auto:
+                self._file_transfers[(friend_number, file_number)].set_state_changed_handler(item.update)
+            self._messages.scrollToBottom()
+        else:
+            friend.actions = True
+
+        friend.append_message(tm)
+
+    def cancel_transfer(self, friend_number, file_number, already_cancelled=False):
+        """
+        Stop transfer
+        :param friend_number: number of friend
+        :param file_number: file number
+        :param already_cancelled: was cancelled by friend
+        """
+        i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
+                                                                          TOX_FILE_TRANSFER_STATE['CANCELLED'])
+        if (friend_number, file_number) in self._file_transfers:
+            tr = self._file_transfers[(friend_number, file_number)]
+            if not already_cancelled:
+                tr.cancel()
+            else:
+                tr.cancelled()
+            if (friend_number, file_number) in self._file_transfers:
+                del tr
+                del self._file_transfers[(friend_number, file_number)]
+        else:
+            if not already_cancelled:
+                self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
+            if friend_number == self.get_active_number():
+                tmp = self._messages.count() + i
+                if tmp >= 0:
+                    self._messages.itemWidget(self._messages.item(tmp)).update(TOX_FILE_TRANSFER_STATE['CANCELLED'],
+                                                                               0, -1)
+
+    def cancel_not_started_transfer(self, time):
+        self._friends_and_gc[self._active_friend_or_gc].delete_one_unsent_file(time)
+        self.update()
+
+    def pause_transfer(self, friend_number, file_number, by_friend=False):
+        """
+        Pause transfer with specified data
+        """
+        tr = self._file_transfers[(friend_number, file_number)]
+        tr.pause(by_friend)
+        t = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']
+        self.get_friend_by_number(friend_number).update_transfer_data(file_number, t)
+
+    def resume_transfer(self, friend_number, file_number, by_friend=False):
+        """
+        Resume transfer with specified data
+        """
+        self.get_friend_by_number(friend_number).update_transfer_data(file_number,
+                                                                      TOX_FILE_TRANSFER_STATE['RUNNING'])
+        # if (friend_number, file_number) not in self._file_transfers:
+        #     print self._file_transfers
+        #     print (friend_number, file_number)
+        #     return
+        tr = self._file_transfers[(friend_number, file_number)]
+        if by_friend:
+            tr.state = TOX_FILE_TRANSFER_STATE['RUNNING']
+            tr.signal()
+        else:  # send seek control?
+            tr.send_control(TOX_FILE_CONTROL['RESUME'])
+
+    def accept_transfer(self, item, path, friend_number, file_number, size, inline=False):
+        """
+        :param item: transfer item.
+        :param path: path for saving
+        :param friend_number: friend number
+        :param file_number: file number
+        :param size: file size
+        :param inline: is inline image
+        """
+        path, file_name = os.path.split(path)
+        new_file_name, i = file_name, 1
+        while os.path.isfile(path + '/' + new_file_name):  # file with same name already exists
+            if '.' in file_name:  # has extension
+                d = file_name.rindex('.')
+            else:  # no extension
+                d = len(file_name)
+            new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:]
+            i += 1
+        path = os.path.join(path, new_file_name)
+        if not inline:
+            rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number)
+        else:
+            rt = ReceiveToBuffer(self._tox, friend_number, size, file_number)
+        self._file_transfers[(friend_number, file_number)] = rt
+        self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME'])
+        if item is not None:
+            rt.set_state_changed_handler(item.update)
+        self.get_friend_by_number(friend_number).update_transfer_data(file_number,
+                                                                      TOX_FILE_TRANSFER_STATE['RUNNING'])
+
+    def send_screenshot(self, data):
+        """
+        Send screenshot to current active friend
+        :param data: raw data - png
+        """
+        self.send_inline(data, 'toxygen_inline.png')
+
+    def send_sticker(self, path):
+        with open(path, 'rb') as fl:
+            data = fl.read()
+        self.send_inline(data, 'sticker.png')
+
+    def send_inline(self, data, file_name, friend_number=None, is_resend=False):
+        friend_number = friend_number or self.get_active_number()
+        friend = self.get_friend_by_number(friend_number)
+        if friend.status is None and not is_resend:
+            m = UnsentFile(file_name, data, time.time())
+            friend.append_message(m)
+            self.update()
+            return
+        elif friend.status is None and is_resend:
+            raise RuntimeError()
+        st = SendFromBuffer(self._tox, friend.number, data, file_name)
+        self._file_transfers[(friend.number, st.get_file_number())] = st
+        tm = TransferMessage(MESSAGE_OWNER['ME'],
+                             time.time(),
+                             TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'],
+                             len(data),
+                             file_name,
+                             friend.number,
+                             st.get_file_number())
+        item = self.create_file_transfer_item(tm)
+        friend.append_message(tm)
+        st.set_state_changed_handler(item.update)
+        self._messages.scrollToBottom()
+
+    def send_file(self, path, number=None, is_resend=False):
+        """
+        Send file to current active friend
+        :param path: file path
+        :param number: friend_number
+        :param is_resend: is 'offline' message
+        """
+        friend_number = number or self.get_active_number()
+        friend = self.get_friend_by_number(friend_number)
+        if friend.status is None and not is_resend:
+            m = UnsentFile(path, None, time.time())
+            friend.append_message(m)
+            self.update()
+            return
+        elif friend.status is None and is_resend:
+            print('Error in sending')
+            raise RuntimeError()
+        st = SendTransfer(path, self._tox, friend_number)
+        self._file_transfers[(friend_number, st.get_file_number())] = st
+        tm = TransferMessage(MESSAGE_OWNER['ME'],
+                             time.time(),
+                             TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'],
+                             os.path.getsize(path),
+                             os.path.basename(path),
+                             friend_number,
+                             st.get_file_number())
+        item = self.create_file_transfer_item(tm)
+        st.set_state_changed_handler(item.update)
+        self._friends_and_gc[self._active_friend_or_gc].append_message(tm)
+        self._messages.scrollToBottom()
+
+    def incoming_chunk(self, friend_number, file_number, position, data):
+        """
+        Incoming chunk
+        """
+        if (friend_number, file_number) in self._file_transfers:
+            transfer = self._file_transfers[(friend_number, file_number)]
+            transfer.write_chunk(position, data)
+            if transfer.state not in ACTIVE_FILE_TRANSFERS:  # finished or cancelled
+                if type(transfer) is ReceiveAvatar:
+                    self.get_friend_by_number(friend_number).load_avatar()
+                    self.set_active(None)
+                elif type(transfer) is ReceiveToBuffer:  # inline image
+                    print('inline')
+                    inline = InlineImage(transfer.get_data())
+                    i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
+                                                                                      TOX_FILE_TRANSFER_STATE['FINISHED'],
+                                                                                      inline)
+                    if friend_number == self.get_active_number():
+                        count = self._messages.count()
+                        if count + i + 1 >= 0:
+                            elem = QtGui.QListWidgetItem()
+                            item = InlineImageItem(transfer.get_data(), self._messages.width(), elem)
+                            elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
+                            self._messages.insertItem(count + i + 1, elem)
+                            self._messages.setItemWidget(elem, item)
+                else:
+                    self.get_friend_by_number(friend_number).update_transfer_data(file_number,
+                                                                                  TOX_FILE_TRANSFER_STATE['FINISHED'])
+                del self._file_transfers[(friend_number, file_number)]
+
+    def outgoing_chunk(self, friend_number, file_number, position, size):
+        """
+        Outgoing chunk
+        """
+        if (friend_number, file_number) in self._file_transfers:
+            transfer = self._file_transfers[(friend_number, file_number)]
+            transfer.send_chunk(position, size)
+            if transfer.state not in ACTIVE_FILE_TRANSFERS:  # finished or cancelled
+                del self._file_transfers[(friend_number, file_number)]
+                if type(transfer) is not SendAvatar:
+                    if type(transfer) is SendFromBuffer and Settings.get_instance()['allow_inline']:  # inline
+                        inline = InlineImage(transfer.get_data())
+                        print('inline')
+                        i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
+                                                                                          TOX_FILE_TRANSFER_STATE[
+                                                                                              'FINISHED'],
+                                                                                          inline)
+                        if friend_number == self.get_active_number():
+                            count = self._messages.count()
+                            if count + i + 1 >= 0:
+                                elem = QtGui.QListWidgetItem()
+                                item = InlineImageItem(transfer.get_data(), self._messages.width(), elem)
+                                elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
+                                self._messages.insertItem(count + i + 1, elem)
+                                self._messages.setItemWidget(elem, item)
+                    else:
+                        self.get_friend_by_number(friend_number).update_transfer_data(file_number,
+                                                                                      TOX_FILE_TRANSFER_STATE['FINISHED'])
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Avatars support
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def send_avatar(self, friend_number):
+        """
+        :param friend_number: number of friend who should get new avatar
+        """
+        avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
+        if not os.path.isfile(avatar_path):  # reset image
+            avatar_path = None
+        sa = SendAvatar(avatar_path, self._tox, friend_number)
+        self._file_transfers[(friend_number, sa.get_file_number())] = sa
+
+    def incoming_avatar(self, friend_number, file_number, size):
+        """
+        Friend changed avatar
+        :param friend_number: friend number
+        :param file_number: file number
+        :param size: size of avatar or 0 (default avatar)
+        """
+        ra = ReceiveAvatar(self._tox, friend_number, size, file_number)
+        if ra.state != TOX_FILE_TRANSFER_STATE['CANCELLED']:
+            self._file_transfers[(friend_number, file_number)] = ra
+        else:
+            self.get_friend_by_number(friend_number).load_avatar()
+            if self.get_active_number() == friend_number:
+                self.set_active(None)
+
+    def reset_avatar(self):
+        super(Profile, self).reset_avatar()
+        for friend in filter(lambda x: x.status is not None, self._friends_and_gc):
+            self.send_avatar(friend.number)
+
+    def set_avatar(self, data):
+        super(Profile, self).set_avatar(data)
+        for friend in filter(lambda x: x.status is not None, self._friends_and_gc):
+            self.send_avatar(friend.number)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # AV support
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_call(self):
+        return self._call
+
+    call = property(get_call)
+
+    def call_click(self, audio=True, video=False):
+        """User clicked audio button in main window"""
+        if not self.is_active_a_friend():
+            return
+        num = self.get_active_number()
+        if num not in self._call and self.is_active_online():  # start call
+            self._call(num, audio, video)
+            self._screen.active_call()
+            if video:
+                text = QtGui.QApplication.translate("incoming_call", "Outgoing video call", None,
+                                                    QtGui.QApplication.UnicodeUTF8)
+            else:
+                text = QtGui.QApplication.translate("incoming_call", "Outgoing audio call", None,
+                                                    QtGui.QApplication.UnicodeUTF8)
+
+            self._friends_and_gc[self._active_friend_or_gc].append_message(InfoMessage(text, time.time()))
+            self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
+            self._messages.scrollToBottom()
+        elif num in self._call:  # finish or cancel call if you call with active friend
+            self.stop_call(num, False)
+
+    def incoming_call(self, audio, video, friend_number):
+        """
+        Incoming call from friend. Only audio is supported now
+        """
+        friend = self.get_friend_by_number(friend_number)
+        if video:
+            text = QtGui.QApplication.translate("incoming_call", "Incoming video call", None,
+                                                QtGui.QApplication.UnicodeUTF8)
+        else:
+            text = QtGui.QApplication.translate("incoming_call", "Incoming audio call", None,
+                                                QtGui.QApplication.UnicodeUTF8)
+        friend.append_message(InfoMessage(text, time.time()))
+        self._incoming_calls.add(friend_number)
+        if friend_number == self.get_active_number():
+            self._screen.incoming_call()
+            self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
+            self._messages.scrollToBottom()
+        else:
+            friend.actions = True
+        self._call_widget = avwidgets.IncomingCallWidget(friend_number, text, friend.name)
+        self._call_widget.set_pixmap(friend.get_pixmap())
+        self._call_widget.show()
+
+    def accept_call(self, friend_number, audio, video):
+        """
+        Accept incoming call with audio or video
+        """
+        self._call.accept_call(friend_number, audio, video)
+        self._screen.active_call()
+        self._incoming_calls.remove(friend_number)
+        if hasattr(self, '_call_widget'):
+            del self._call_widget
+
+    def stop_call(self, friend_number, by_friend):
+        """
+        Stop call with friend
+        """
+        if friend_number in self._incoming_calls:
+            self._incoming_calls.remove(friend_number)
+            text = QtGui.QApplication.translate("incoming_call", "Call declined", None, QtGui.QApplication.UnicodeUTF8)
+        else:
+            text = QtGui.QApplication.translate("incoming_call", "Call finished", None, QtGui.QApplication.UnicodeUTF8)
+        self._screen.call_finished()
+        self._call.finish_call(friend_number, by_friend)  # finish or decline call
+        if hasattr(self, '_call_widget'):
+            del self._call_widget
+        friend = self.get_friend_by_number(friend_number)
+        friend.append_message(InfoMessage(text, time.time()))
+        if friend_number == self.get_active_number():
+            self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
+            self._messages.scrollToBottom()
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group chats support
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_all_gc(self):
+        return list(filter(lambda x: type(x) is GroupChat, self._friends_and_gc))
+
+    def add_gc(self, num):
+        try:
+            tox_id = self._tox.group_get_chat_id(num)
+            name = self._tox.group_get_name(num)
+            topic = self._tox.group_get_topic(num)
+        except:
+            tox_id = name = topic = ''
+        item = self.create_friend_item()
+        try:
+            if not self._history.friend_exists_in_db(tox_id):
+                self._history.add_friend_to_db(tox_id)
+            message_getter = self._history.messages_getter(tox_id)
+        except Exception as ex:  # something is wrong
+            log('Accept friend request failed! ' + str(ex))
+            message_getter = None
+        gc = GroupChat(self._tox, num, message_getter, name, topic, item, tox_id)
+        self._friends_and_gc.append(gc)
+
+    def join_gc(self, chat_id, password):
+        num = self._tox.group_join(chat_id, password if password else None)
+        if num != 2 ** 32 - 1:
+            self.add_gc(num)
+        else:
+            pass  # TODO: join failed, show error
+
+    def create_gc(self, name, is_public, password):
+        privacy_state = TOX_GROUP_PRIVACY_STATE['TOX_GROUP_PRIVACY_STATE_PUBLIC'] if is_public else TOX_GROUP_PRIVACY_STATE['TOX_GROUP_PRIVACY_STATE_PRIVATE']
+        num = self._tox.group_new(privacy_state, bytes(name, 'utf-8'))
+        if password:
+            self._tox.group_founder_set_password(num, bytes(password, 'utf-8'))
+        self.add_gc(num)
+        self.get_gc_by_number(num).set_status(TOX_USER_STATUS['NONE'])
+
+    def process_group_invite(self, friend_num, data):
+        # TODO: support password
+        try:
+            text = QtGui.QApplication.translate('MainWindow', 'User {} invites you to group',
+                                                None, QtGui.QApplication.UnicodeUTF8)
+            info = text.format(self.get_friend_by_number(friend_num).name)
+            fr_req = QtGui.QApplication.translate('MainWindow', 'Group chat invite', None, QtGui.QApplication.UnicodeUTF8)
+            reply = QtGui.QMessageBox.question(None, fr_req, info, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
+            if reply == QtGui.QMessageBox.Yes:  # accepted
+                num = self._tox.group_invite_accept(data, friend_num)
+                data = self._tox.get_savedata()
+                ProfileHelper.get_instance().save_profile(data)
+                print('In gc invite', num)
+                self.add_gc(num)
+            elif reply != QtGui.QMessageBox.No:
+                if friend_num in self._gc_invites:
+                    self._gc_invites[friend_num].append(data)
+                else:
+                    self._gc_invites[friend_num] = data
+        except Exception as ex:  # something is wrong
+            log('Accept group chat invite failed! ' + str(ex))
+
+    def invite_friend(self, group_number, friend_number):
+        self._tox.group_invite_friend(group_number, friend_number)
+
+    def leave_group(self, num, message=None):
+        self.delete_friend_or_gc(num, True, message)
+
+
+def tox_factory(data=None, settings=None):
+    """
+    :param data: user data from .tox file. None = no saved data, create new profile
+    :param settings: current profile settings. None = default settings will be used
+    :return: new tox instance
+    """
+    if settings is None:
+        settings = Settings.get_default_settings()
+    tox_options = Tox.options_new()
+    tox_options.contents.udp_enabled = settings['udp_enabled']
+    tox_options.contents.proxy_type = settings['proxy_type']
+    tox_options.contents.proxy_host = bytes(settings['proxy_host'], 'UTF-8')
+    tox_options.contents.proxy_port = settings['proxy_port']
+    tox_options.contents.start_port = settings['start_port']
+    tox_options.contents.end_port = settings['end_port']
+    tox_options.contents.tcp_port = settings['tcp_port']
+    if data:  # load existing profile
+        tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['TOX_SAVE']
+        tox_options.contents.savedata_data = c_char_p(data)
+        tox_options.contents.savedata_length = len(data)
+    else:  # create new profile
+        tox_options.contents.savedata_type = TOX_SAVEDATA_TYPE['NONE']
+        tox_options.contents.savedata_data = None
+        tox_options.contents.savedata_length = 0
+    return Tox(tox_options)
diff --git a/toxygen/settings.py b/toxygen/settings.py
new file mode 100644
index 0000000..627a4ed
--- /dev/null
+++ b/toxygen/settings.py
@@ -0,0 +1,269 @@
+from platform import system
+import json
+import os
+import locale
+from util import Singleton, curr_directory, log
+import pyaudio
+from toxencryptsave import ToxEncryptSave
+import smileys
+
+
+class Settings(dict, Singleton):
+    """
+    Settings of current profile + global app settings
+    """
+
+    def __init__(self, name):
+        Singleton.__init__(self)
+        self.path = ProfileHelper.get_path() + str(name) + '.json'
+        self.name = name
+        if os.path.isfile(self.path):
+            with open(self.path, 'rb') as fl:
+                data = fl.read()
+            inst = ToxEncryptSave.get_instance()
+            try:
+                if inst.is_data_encrypted(data):
+                    data = inst.pass_decrypt(data)
+                info = json.loads(str(data, 'utf-8'))
+            except Exception as ex:
+                info = Settings.get_default_settings()
+                log('Parsing settings error: ' + str(ex))
+            super(Settings, self).__init__(info)
+            self.upgrade()
+        else:
+            super(Settings, self).__init__(Settings.get_default_settings())
+            self.save()
+        smileys.SmileyLoader(self)
+        p = pyaudio.PyAudio()
+        self.locked = False
+        self.audio = {'input': p.get_default_input_device_info()['index'],
+                      'output': p.get_default_output_device_info()['index']}
+
+    @staticmethod
+    def get_auto_profile():
+        path = Settings.get_default_path() + 'toxygen.json'
+        if os.path.isfile(path):
+            with open(path) as fl:
+                data = fl.read()
+            auto = json.loads(data)
+            if 'path' in auto and 'name' in auto:
+                return str(auto['path']), str(auto['name'])
+        return '', ''
+
+    @staticmethod
+    def set_auto_profile(path, name):
+        p = Settings.get_default_path() + 'toxygen.json'
+        with open(p) as fl:
+            data = fl.read()
+        data = json.loads(data)
+        data['path'] = str(path)
+        data['name'] = str(name)
+        with open(p, 'w') as fl:
+            fl.write(json.dumps(data))
+
+    @staticmethod
+    def reset_auto_profile():
+        p = Settings.get_default_path() + 'toxygen.json'
+        with open(p) as fl:
+            data = fl.read()
+        data = json.loads(data)
+        if 'path' in data:
+            del data['path']
+            del data['name']
+        with open(p, 'w') as fl:
+            fl.write(json.dumps(data))
+
+    @staticmethod
+    def is_active_profile(path, name):
+        path = path + name + '.tox'
+        settings = Settings.get_default_path() + 'toxygen.json'
+        if os.path.isfile(settings):
+            with open(settings) as fl:
+                data = fl.read()
+            data = json.loads(data)
+            if 'active_profile' in data:
+                return path in data['active_profile']
+        return False
+
+    @staticmethod
+    def get_default_settings():
+        """
+        Default profile settings
+        """
+        return {
+            'theme': 'default',
+            'ipv6_enabled': True,
+            'udp_enabled': True,
+            'proxy_type': 0,
+            'proxy_host': '127.0.0.1',
+            'proxy_port': 9050,
+            'start_port': 0,
+            'end_port': 0,
+            'tcp_port': 0,
+            'notifications': True,
+            'sound_notifications': False,
+            'language': 'English',
+            'save_history': False,
+            'allow_inline': True,
+            'allow_auto_accept': True,
+            'auto_accept_path': None,
+            'show_online_friends': False,
+            'auto_accept_from_friends': [],
+            'friends_aliases': [],
+            'typing_notifications': False,
+            'calls_sound': True,
+            'blocked': [],
+            'plugins': [],
+            'notes': {},
+            'smileys': True,
+            'smiley_pack': 'default',
+            'mirror_mode': False,
+            'width': 920,
+            'height': 500,
+            'x': 400,
+            'y': 400,
+            'message_font_size': 14,
+            'unread_color': 'red',
+            'save_unsent_only': False,
+            'compact_mode': False,
+            'show_welcome_screen': True,
+            'notify_all_gc': False
+        }
+
+    @staticmethod
+    def supported_languages():
+        return {
+            'English': 'en_EN',
+            'Russian': 'ru_RU',
+            'French': 'fr_FR'
+        }
+
+    def upgrade(self):
+        default = Settings.get_default_settings()
+        for key in default:
+            if key not in self:
+                print(key)
+                self[key] = default[key]
+        self.save()
+
+    def save(self):
+        text = json.dumps(self)
+        inst = ToxEncryptSave.get_instance()
+        if inst.has_password():
+            text = bytes(inst.pass_encrypt(bytes(text, 'utf-8')))
+        else:
+            text = bytes(text, 'utf-8')
+        with open(self.path, 'wb') as fl:
+            fl.write(text)
+
+    def close(self):
+        path = Settings.get_default_path() + 'toxygen.json'
+        if os.path.isfile(path):
+            with open(path) as fl:
+                data = fl.read()
+            app_settings = json.loads(data)
+            try:
+                app_settings['active_profile'].remove(str(ProfileHelper.get_path() + self.name + '.tox'))
+            except:
+                pass
+            data = json.dumps(app_settings)
+            with open(path, 'w') as fl:
+                fl.write(data)
+
+    def set_active_profile(self):
+        """
+        Mark current profile as active
+        """
+        path = Settings.get_default_path() + 'toxygen.json'
+        if os.path.isfile(path):
+            with open(path) as fl:
+                data = fl.read()
+            app_settings = json.loads(data)
+        else:
+            app_settings = {}
+        if 'active_profile' not in app_settings:
+            app_settings['active_profile'] = []
+        profilepath = ProfileHelper.get_path()
+        app_settings['active_profile'].append(str(profilepath + str(self.name) + '.tox'))
+        data = json.dumps(app_settings)
+        with open(path, 'w') as fl:
+            fl.write(data)
+
+    def export(self, path):
+        text = json.dumps(self)
+        with open(path + str(self.name) + '.json', 'w') as fl:
+            fl.write(text)
+
+    @staticmethod
+    def get_default_path():
+        if system() == 'Linux':
+            return os.getenv('HOME') + '/.config/tox/'
+        elif system() == 'Windows':
+            return os.getenv('APPDATA') + '/Tox/'
+
+
+class ProfileHelper(Singleton):
+    """
+    Class with methods for search, load and save profiles
+    """
+    def __init__(self, path, name):
+        Singleton.__init__(self)
+        self._path = path + name + '.tox'
+        self._directory = path
+        # create /avatars if not exists:
+        directory = path + 'avatars'
+        if not os.path.exists(directory):
+            os.makedirs(directory)
+
+    def open_profile(self):
+        with open(self._path, 'rb') as fl:
+            data = fl.read()
+        if data:
+            return data
+        else:
+            raise IOError('Save file has zero size!')
+
+    def get_dir(self):
+        return self._directory
+
+    def save_profile(self, data):
+        inst = ToxEncryptSave.get_instance()
+        if inst.has_password():
+            data = inst.pass_encrypt(data)
+        with open(self._path, 'wb') as fl:
+            fl.write(data)
+        print('Profile saved successfully')
+
+    def export_profile(self, new_path):
+        new_path += os.path.basename(self._path)
+        with open(self._path, 'rb') as fin:
+            data = fin.read()
+        with open(new_path, 'wb') as fout:
+            fout.write(data)
+        print('Profile exported successfully')
+
+    @staticmethod
+    def find_profiles():
+        """
+        Find available tox profiles
+        """
+        path = Settings.get_default_path()
+        result = []
+        # check default path
+        if not os.path.exists(path):
+            os.makedirs(path)
+        for fl in os.listdir(path):
+            if fl.endswith('.tox'):
+                name = fl[:-4]
+                result.append((path, name))
+        path = curr_directory()
+        # check current directory
+        for fl in os.listdir(path):
+            if fl.endswith('.tox'):
+                name = fl[:-4]
+                result.append((path + '/', name))
+        return result
+
+    @staticmethod
+    def get_path():
+        return ProfileHelper.get_instance().get_dir()
diff --git a/toxygen/smileys/smileys.py b/toxygen/smileys.py
similarity index 70%
rename from toxygen/smileys/smileys.py
rename to toxygen/smileys.py
index 604e681..9143a0b 100644
--- a/toxygen/smileys/smileys.py
+++ b/toxygen/smileys.py
@@ -1,20 +1,14 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
+import util
 import json
-import logging
 import os
 from collections import OrderedDict
+try:
+    from PySide import QtCore
+except ImportError:
+    from PyQt4 import QtCore
 
-from  qtpy import QtCore
 
-from utils import util
-
-# LOG=util.log
-global LOG
-LOG = logging.getLogger('app.'+__name__)
-log = lambda x: LOG.info(x)
-
-class SmileyLoader:
+class SmileyLoader(util.Singleton):
     """
     Class which loads smileys packs and insert smileys into messages
     """
@@ -34,16 +28,16 @@ class SmileyLoader:
         pack_name = self._settings['smiley_pack']
         if self._settings['smileys'] and self._curr_pack != pack_name:
             self._curr_pack = pack_name
-            path = util.join_path(self.get_smileys_path(), 'config.json')
+            path = self.get_smileys_path() + 'config.json'
             try:
                 with open(path, encoding='utf8') as fl:
                     self._smileys = json.loads(fl.read())
                     fl.seek(0)
                     tmp = json.loads(fl.read(), object_pairs_hook=OrderedDict)
-                LOG.info('Smiley pack {} loaded'.format(pack_name))
+                print('Smiley pack {} loaded'.format(pack_name))
                 keys, values, self._list = [], [], []
                 for key, value in tmp.items():
-                    value = util.join_path(self.get_smileys_path(), value)
+                    value = self.get_smileys_path() + value
                     if value not in values:
                         keys.append(key)
                         values.append(value)
@@ -51,14 +45,13 @@ class SmileyLoader:
             except Exception as ex:
                 self._smileys = {}
                 self._list = []
-                LOG.error('Smiley pack {} was not loaded. Error: {}'.format(pack_name, str(ex)))
+                print('Smiley pack {} was not loaded. Error: {}'.format(pack_name, ex))
 
     def get_smileys_path(self):
-        return util.join_path(util.get_smileys_directory(), self._curr_pack) if self._curr_pack is not None else None
+        return util.curr_directory() + '/smileys/' + self._curr_pack + '/'
 
-    @staticmethod
-    def get_packs_list():
-        d = util.get_smileys_directory()
+    def get_packs_list(self):
+        d = util.curr_directory() + '/smileys/'
         return [x[1] for x in os.walk(d)][0]
 
     def get_smileys(self):
@@ -81,3 +74,18 @@ class SmileyLoader:
                 if file_name.endswith('.gif'):  # animated smiley
                     edit.addAnimation(QtCore.QUrl(file_name), self.get_smileys_path() + file_name)
         return ' '.join(arr)
+
+
+def sticker_loader():
+    """
+    :return list of stickers
+    """
+    result = []
+    d = util.curr_directory() + '/stickers/'
+    keys = [x[1] for x in os.walk(d)][0]
+    for key in keys:
+        path = d + key + '/'
+        files = filter(lambda f: f.endswith('.png'), os.listdir(path))
+        files = map(lambda f: str(path + f), files)
+        result.extend(files)
+    return result
diff --git a/toxygen/smileys/__init__.py b/toxygen/smileys/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/smileys/default/003020E3.png b/toxygen/smileys/default/003020E3.png
index e64ea3a..a196fa1 100644
Binary files a/toxygen/smileys/default/003020E3.png and b/toxygen/smileys/default/003020E3.png differ
diff --git a/toxygen/smileys/default/003120E3.png b/toxygen/smileys/default/003120E3.png
index 9501bdf..26d6754 100644
Binary files a/toxygen/smileys/default/003120E3.png and b/toxygen/smileys/default/003120E3.png differ
diff --git a/toxygen/smileys/default/003220E3.png b/toxygen/smileys/default/003220E3.png
index 8c44746..645c904 100644
Binary files a/toxygen/smileys/default/003220E3.png and b/toxygen/smileys/default/003220E3.png differ
diff --git a/toxygen/smileys/default/003320E3.png b/toxygen/smileys/default/003320E3.png
index ab5b4bc..1674b69 100644
Binary files a/toxygen/smileys/default/003320E3.png and b/toxygen/smileys/default/003320E3.png differ
diff --git a/toxygen/smileys/default/003420E3.png b/toxygen/smileys/default/003420E3.png
index 4ecbce7..ef64830 100644
Binary files a/toxygen/smileys/default/003420E3.png and b/toxygen/smileys/default/003420E3.png differ
diff --git a/toxygen/smileys/default/003520E3.png b/toxygen/smileys/default/003520E3.png
index c3d3077..782ee47 100644
Binary files a/toxygen/smileys/default/003520E3.png and b/toxygen/smileys/default/003520E3.png differ
diff --git a/toxygen/smileys/default/003620E3.png b/toxygen/smileys/default/003620E3.png
index 617d2ca..07f549a 100644
Binary files a/toxygen/smileys/default/003620E3.png and b/toxygen/smileys/default/003620E3.png differ
diff --git a/toxygen/smileys/default/003720E3.png b/toxygen/smileys/default/003720E3.png
index 7fce639..5093629 100644
Binary files a/toxygen/smileys/default/003720E3.png and b/toxygen/smileys/default/003720E3.png differ
diff --git a/toxygen/smileys/default/003820E3.png b/toxygen/smileys/default/003820E3.png
index 3ecb8fc..aea2c90 100644
Binary files a/toxygen/smileys/default/003820E3.png and b/toxygen/smileys/default/003820E3.png differ
diff --git a/toxygen/smileys/default/003920E3.png b/toxygen/smileys/default/003920E3.png
index f1d6641..5a19d1b 100644
Binary files a/toxygen/smileys/default/003920E3.png and b/toxygen/smileys/default/003920E3.png differ
diff --git a/toxygen/smileys/default/00A9.png b/toxygen/smileys/default/00A9.png
index 57666e9..5f52426 100644
Binary files a/toxygen/smileys/default/00A9.png and b/toxygen/smileys/default/00A9.png differ
diff --git a/toxygen/smileys/default/00AE.png b/toxygen/smileys/default/00AE.png
index 98fb62a..ebc7dd9 100644
Binary files a/toxygen/smileys/default/00AE.png and b/toxygen/smileys/default/00AE.png differ
diff --git a/toxygen/smileys/default/203C.png b/toxygen/smileys/default/203C.png
index 36d8dcf..e1c3057 100644
Binary files a/toxygen/smileys/default/203C.png and b/toxygen/smileys/default/203C.png differ
diff --git a/toxygen/smileys/default/2049.png b/toxygen/smileys/default/2049.png
index feb5368..0bacbe9 100644
Binary files a/toxygen/smileys/default/2049.png and b/toxygen/smileys/default/2049.png differ
diff --git a/toxygen/smileys/default/2122.png b/toxygen/smileys/default/2122.png
index 5119eae..8b5e91a 100644
Binary files a/toxygen/smileys/default/2122.png and b/toxygen/smileys/default/2122.png differ
diff --git a/toxygen/smileys/default/2139.png b/toxygen/smileys/default/2139.png
index 4393f8a..89e6eb4 100644
Binary files a/toxygen/smileys/default/2139.png and b/toxygen/smileys/default/2139.png differ
diff --git a/toxygen/smileys/default/2194.png b/toxygen/smileys/default/2194.png
index c8f4d49..87aa873 100644
Binary files a/toxygen/smileys/default/2194.png and b/toxygen/smileys/default/2194.png differ
diff --git a/toxygen/smileys/default/2195.png b/toxygen/smileys/default/2195.png
index 7d49587..beb8b2c 100644
Binary files a/toxygen/smileys/default/2195.png and b/toxygen/smileys/default/2195.png differ
diff --git a/toxygen/smileys/default/2196.png b/toxygen/smileys/default/2196.png
index 210315d..a1769d4 100644
Binary files a/toxygen/smileys/default/2196.png and b/toxygen/smileys/default/2196.png differ
diff --git a/toxygen/smileys/default/2197.png b/toxygen/smileys/default/2197.png
index b7f91c4..2b637fe 100644
Binary files a/toxygen/smileys/default/2197.png and b/toxygen/smileys/default/2197.png differ
diff --git a/toxygen/smileys/default/2198.png b/toxygen/smileys/default/2198.png
index e128d70..d868dd7 100644
Binary files a/toxygen/smileys/default/2198.png and b/toxygen/smileys/default/2198.png differ
diff --git a/toxygen/smileys/default/2199.png b/toxygen/smileys/default/2199.png
index 34cbf64..3775673 100644
Binary files a/toxygen/smileys/default/2199.png and b/toxygen/smileys/default/2199.png differ
diff --git a/toxygen/smileys/default/21A9.png b/toxygen/smileys/default/21A9.png
index d8a6c0b..9f9af80 100644
Binary files a/toxygen/smileys/default/21A9.png and b/toxygen/smileys/default/21A9.png differ
diff --git a/toxygen/smileys/default/21AA.png b/toxygen/smileys/default/21AA.png
index 588acf5..c13226b 100644
Binary files a/toxygen/smileys/default/21AA.png and b/toxygen/smileys/default/21AA.png differ
diff --git a/toxygen/smileys/default/231A.png b/toxygen/smileys/default/231A.png
index dbe2607..699dddd 100644
Binary files a/toxygen/smileys/default/231A.png and b/toxygen/smileys/default/231A.png differ
diff --git a/toxygen/smileys/default/231B.png b/toxygen/smileys/default/231B.png
index 060cf43..b69f1ed 100644
Binary files a/toxygen/smileys/default/231B.png and b/toxygen/smileys/default/231B.png differ
diff --git a/toxygen/smileys/default/23E9.png b/toxygen/smileys/default/23E9.png
index 4fc83f7..f4b575a 100644
Binary files a/toxygen/smileys/default/23E9.png and b/toxygen/smileys/default/23E9.png differ
diff --git a/toxygen/smileys/default/23EA.png b/toxygen/smileys/default/23EA.png
index 4909b06..557b09f 100644
Binary files a/toxygen/smileys/default/23EA.png and b/toxygen/smileys/default/23EA.png differ
diff --git a/toxygen/smileys/default/23EB.png b/toxygen/smileys/default/23EB.png
index 3240476..80b209b 100644
Binary files a/toxygen/smileys/default/23EB.png and b/toxygen/smileys/default/23EB.png differ
diff --git a/toxygen/smileys/default/23EC.png b/toxygen/smileys/default/23EC.png
index 9996c1a..36688b2 100644
Binary files a/toxygen/smileys/default/23EC.png and b/toxygen/smileys/default/23EC.png differ
diff --git a/toxygen/smileys/default/23F0.png b/toxygen/smileys/default/23F0.png
index 63485f6..c8ec471 100644
Binary files a/toxygen/smileys/default/23F0.png and b/toxygen/smileys/default/23F0.png differ
diff --git a/toxygen/smileys/default/23F3.png b/toxygen/smileys/default/23F3.png
index 0958429..eadb18c 100644
Binary files a/toxygen/smileys/default/23F3.png and b/toxygen/smileys/default/23F3.png differ
diff --git a/toxygen/smileys/default/24C2.png b/toxygen/smileys/default/24C2.png
index bccac5e..8af2206 100644
Binary files a/toxygen/smileys/default/24C2.png and b/toxygen/smileys/default/24C2.png differ
diff --git a/toxygen/smileys/default/25AA.png b/toxygen/smileys/default/25AA.png
index 54992ea..baed686 100644
Binary files a/toxygen/smileys/default/25AA.png and b/toxygen/smileys/default/25AA.png differ
diff --git a/toxygen/smileys/default/25AB.png b/toxygen/smileys/default/25AB.png
index a957fca..34a504f 100644
Binary files a/toxygen/smileys/default/25AB.png and b/toxygen/smileys/default/25AB.png differ
diff --git a/toxygen/smileys/default/25B6.png b/toxygen/smileys/default/25B6.png
index 9c291f6..7ffe84e 100644
Binary files a/toxygen/smileys/default/25B6.png and b/toxygen/smileys/default/25B6.png differ
diff --git a/toxygen/smileys/default/25C0.png b/toxygen/smileys/default/25C0.png
index 0ab4d16..ea2a965 100644
Binary files a/toxygen/smileys/default/25C0.png and b/toxygen/smileys/default/25C0.png differ
diff --git a/toxygen/smileys/default/25FB.png b/toxygen/smileys/default/25FB.png
index 02b39c8..1a9b1e4 100644
Binary files a/toxygen/smileys/default/25FB.png and b/toxygen/smileys/default/25FB.png differ
diff --git a/toxygen/smileys/default/25FC.png b/toxygen/smileys/default/25FC.png
index c1ba9c6..8ae60bf 100644
Binary files a/toxygen/smileys/default/25FC.png and b/toxygen/smileys/default/25FC.png differ
diff --git a/toxygen/smileys/default/25FD.png b/toxygen/smileys/default/25FD.png
index 0aab847..66144a8 100644
Binary files a/toxygen/smileys/default/25FD.png and b/toxygen/smileys/default/25FD.png differ
diff --git a/toxygen/smileys/default/25FE.png b/toxygen/smileys/default/25FE.png
index 1de985b..300b92d 100644
Binary files a/toxygen/smileys/default/25FE.png and b/toxygen/smileys/default/25FE.png differ
diff --git a/toxygen/smileys/default/2600.png b/toxygen/smileys/default/2600.png
index fcbfe56..ad91b05 100644
Binary files a/toxygen/smileys/default/2600.png and b/toxygen/smileys/default/2600.png differ
diff --git a/toxygen/smileys/default/2601.png b/toxygen/smileys/default/2601.png
index d1f979d..14ee8fd 100644
Binary files a/toxygen/smileys/default/2601.png and b/toxygen/smileys/default/2601.png differ
diff --git a/toxygen/smileys/default/260E.png b/toxygen/smileys/default/260E.png
index ef1b3c5..ae88c82 100644
Binary files a/toxygen/smileys/default/260E.png and b/toxygen/smileys/default/260E.png differ
diff --git a/toxygen/smileys/default/2611.png b/toxygen/smileys/default/2611.png
index c4b49d6..21f462c 100644
Binary files a/toxygen/smileys/default/2611.png and b/toxygen/smileys/default/2611.png differ
diff --git a/toxygen/smileys/default/2614.png b/toxygen/smileys/default/2614.png
index 2dad11e..154540c 100644
Binary files a/toxygen/smileys/default/2614.png and b/toxygen/smileys/default/2614.png differ
diff --git a/toxygen/smileys/default/2615.png b/toxygen/smileys/default/2615.png
index 944af22..f3ac6c9 100644
Binary files a/toxygen/smileys/default/2615.png and b/toxygen/smileys/default/2615.png differ
diff --git a/toxygen/smileys/default/261D.png b/toxygen/smileys/default/261D.png
index 8458b0e..caf5e7f 100644
Binary files a/toxygen/smileys/default/261D.png and b/toxygen/smileys/default/261D.png differ
diff --git a/toxygen/smileys/default/263A.png b/toxygen/smileys/default/263A.png
index 5db95a5..8a3409d 100644
Binary files a/toxygen/smileys/default/263A.png and b/toxygen/smileys/default/263A.png differ
diff --git a/toxygen/smileys/default/2648.png b/toxygen/smileys/default/2648.png
index 9d529e5..36ca321 100644
Binary files a/toxygen/smileys/default/2648.png and b/toxygen/smileys/default/2648.png differ
diff --git a/toxygen/smileys/default/2649.png b/toxygen/smileys/default/2649.png
index d67cb84..4e0687e 100644
Binary files a/toxygen/smileys/default/2649.png and b/toxygen/smileys/default/2649.png differ
diff --git a/toxygen/smileys/default/264A.png b/toxygen/smileys/default/264A.png
index 92fa0e7..1e131e0 100644
Binary files a/toxygen/smileys/default/264A.png and b/toxygen/smileys/default/264A.png differ
diff --git a/toxygen/smileys/default/264B.png b/toxygen/smileys/default/264B.png
index 0753593..b02cca6 100644
Binary files a/toxygen/smileys/default/264B.png and b/toxygen/smileys/default/264B.png differ
diff --git a/toxygen/smileys/default/264C.png b/toxygen/smileys/default/264C.png
index 7a44286..6354aa4 100644
Binary files a/toxygen/smileys/default/264C.png and b/toxygen/smileys/default/264C.png differ
diff --git a/toxygen/smileys/default/264D.png b/toxygen/smileys/default/264D.png
index f45f5f0..19cd5dc 100644
Binary files a/toxygen/smileys/default/264D.png and b/toxygen/smileys/default/264D.png differ
diff --git a/toxygen/smileys/default/264E.png b/toxygen/smileys/default/264E.png
index 80fa2eb..e000b39 100644
Binary files a/toxygen/smileys/default/264E.png and b/toxygen/smileys/default/264E.png differ
diff --git a/toxygen/smileys/default/264F.png b/toxygen/smileys/default/264F.png
index ad41780..82eb8eb 100644
Binary files a/toxygen/smileys/default/264F.png and b/toxygen/smileys/default/264F.png differ
diff --git a/toxygen/smileys/default/2650.png b/toxygen/smileys/default/2650.png
index a4cf9d8..2b4fa50 100644
Binary files a/toxygen/smileys/default/2650.png and b/toxygen/smileys/default/2650.png differ
diff --git a/toxygen/smileys/default/2651.png b/toxygen/smileys/default/2651.png
index 7e40bc3..ce713d8 100644
Binary files a/toxygen/smileys/default/2651.png and b/toxygen/smileys/default/2651.png differ
diff --git a/toxygen/smileys/default/2652.png b/toxygen/smileys/default/2652.png
index 09f6d3b..0032211 100644
Binary files a/toxygen/smileys/default/2652.png and b/toxygen/smileys/default/2652.png differ
diff --git a/toxygen/smileys/default/2653.png b/toxygen/smileys/default/2653.png
index a4da181..85c701a 100644
Binary files a/toxygen/smileys/default/2653.png and b/toxygen/smileys/default/2653.png differ
diff --git a/toxygen/smileys/default/2660.png b/toxygen/smileys/default/2660.png
index e2a9757..3ed0373 100644
Binary files a/toxygen/smileys/default/2660.png and b/toxygen/smileys/default/2660.png differ
diff --git a/toxygen/smileys/default/2663.png b/toxygen/smileys/default/2663.png
index 43b0f13..4dd8e0b 100644
Binary files a/toxygen/smileys/default/2663.png and b/toxygen/smileys/default/2663.png differ
diff --git a/toxygen/smileys/default/2665.png b/toxygen/smileys/default/2665.png
index 7fd68db..1088ec5 100644
Binary files a/toxygen/smileys/default/2665.png and b/toxygen/smileys/default/2665.png differ
diff --git a/toxygen/smileys/default/2666.png b/toxygen/smileys/default/2666.png
index e123db3..0fa97e6 100644
Binary files a/toxygen/smileys/default/2666.png and b/toxygen/smileys/default/2666.png differ
diff --git a/toxygen/smileys/default/2668.png b/toxygen/smileys/default/2668.png
index 6e148ea..244e954 100644
Binary files a/toxygen/smileys/default/2668.png and b/toxygen/smileys/default/2668.png differ
diff --git a/toxygen/smileys/default/267B.png b/toxygen/smileys/default/267B.png
index 4c7b51d..39e485d 100644
Binary files a/toxygen/smileys/default/267B.png and b/toxygen/smileys/default/267B.png differ
diff --git a/toxygen/smileys/default/267F.png b/toxygen/smileys/default/267F.png
index 1a33390..8e0341d 100644
Binary files a/toxygen/smileys/default/267F.png and b/toxygen/smileys/default/267F.png differ
diff --git a/toxygen/smileys/default/2693.png b/toxygen/smileys/default/2693.png
index f87253d..0a20950 100644
Binary files a/toxygen/smileys/default/2693.png and b/toxygen/smileys/default/2693.png differ
diff --git a/toxygen/smileys/default/26A0.png b/toxygen/smileys/default/26A0.png
index ec5d59f..da04fd6 100644
Binary files a/toxygen/smileys/default/26A0.png and b/toxygen/smileys/default/26A0.png differ
diff --git a/toxygen/smileys/default/26A1.png b/toxygen/smileys/default/26A1.png
index a753ef4..aa730a7 100644
Binary files a/toxygen/smileys/default/26A1.png and b/toxygen/smileys/default/26A1.png differ
diff --git a/toxygen/smileys/default/26AA.png b/toxygen/smileys/default/26AA.png
index d6dc51e..5a7d5c3 100644
Binary files a/toxygen/smileys/default/26AA.png and b/toxygen/smileys/default/26AA.png differ
diff --git a/toxygen/smileys/default/26AB.png b/toxygen/smileys/default/26AB.png
index d603714..4cf2098 100644
Binary files a/toxygen/smileys/default/26AB.png and b/toxygen/smileys/default/26AB.png differ
diff --git a/toxygen/smileys/default/26BD.png b/toxygen/smileys/default/26BD.png
index 27ff3f8..7bfd040 100644
Binary files a/toxygen/smileys/default/26BD.png and b/toxygen/smileys/default/26BD.png differ
diff --git a/toxygen/smileys/default/26BE.png b/toxygen/smileys/default/26BE.png
index aa929b6..ef49d55 100644
Binary files a/toxygen/smileys/default/26BE.png and b/toxygen/smileys/default/26BE.png differ
diff --git a/toxygen/smileys/default/26C4.png b/toxygen/smileys/default/26C4.png
index f7e509b..93bef58 100644
Binary files a/toxygen/smileys/default/26C4.png and b/toxygen/smileys/default/26C4.png differ
diff --git a/toxygen/smileys/default/26C5.png b/toxygen/smileys/default/26C5.png
index 8677043..eb04e5d 100644
Binary files a/toxygen/smileys/default/26C5.png and b/toxygen/smileys/default/26C5.png differ
diff --git a/toxygen/smileys/default/26CE.png b/toxygen/smileys/default/26CE.png
index 35c5af5..0ad227f 100644
Binary files a/toxygen/smileys/default/26CE.png and b/toxygen/smileys/default/26CE.png differ
diff --git a/toxygen/smileys/default/26D4.png b/toxygen/smileys/default/26D4.png
index dcfb49e..e28fada 100644
Binary files a/toxygen/smileys/default/26D4.png and b/toxygen/smileys/default/26D4.png differ
diff --git a/toxygen/smileys/default/26EA.png b/toxygen/smileys/default/26EA.png
index d606692..17727e0 100644
Binary files a/toxygen/smileys/default/26EA.png and b/toxygen/smileys/default/26EA.png differ
diff --git a/toxygen/smileys/default/26F2.png b/toxygen/smileys/default/26F2.png
index 2a86834..720ad23 100644
Binary files a/toxygen/smileys/default/26F2.png and b/toxygen/smileys/default/26F2.png differ
diff --git a/toxygen/smileys/default/26F3.png b/toxygen/smileys/default/26F3.png
index c51400b..50e4a27 100644
Binary files a/toxygen/smileys/default/26F3.png and b/toxygen/smileys/default/26F3.png differ
diff --git a/toxygen/smileys/default/26F5.png b/toxygen/smileys/default/26F5.png
index 5fd1cfb..4a5c029 100644
Binary files a/toxygen/smileys/default/26F5.png and b/toxygen/smileys/default/26F5.png differ
diff --git a/toxygen/smileys/default/26FA.png b/toxygen/smileys/default/26FA.png
index 053a098..516ad10 100644
Binary files a/toxygen/smileys/default/26FA.png and b/toxygen/smileys/default/26FA.png differ
diff --git a/toxygen/smileys/default/26FD.png b/toxygen/smileys/default/26FD.png
index fcf14e6..dbd528b 100644
Binary files a/toxygen/smileys/default/26FD.png and b/toxygen/smileys/default/26FD.png differ
diff --git a/toxygen/smileys/default/2702.png b/toxygen/smileys/default/2702.png
index 15ea13b..0822488 100644
Binary files a/toxygen/smileys/default/2702.png and b/toxygen/smileys/default/2702.png differ
diff --git a/toxygen/smileys/default/2705.png b/toxygen/smileys/default/2705.png
index 9c849f7..0f82df9 100644
Binary files a/toxygen/smileys/default/2705.png and b/toxygen/smileys/default/2705.png differ
diff --git a/toxygen/smileys/default/2708.png b/toxygen/smileys/default/2708.png
index c053b6a..509fa7b 100644
Binary files a/toxygen/smileys/default/2708.png and b/toxygen/smileys/default/2708.png differ
diff --git a/toxygen/smileys/default/2709.png b/toxygen/smileys/default/2709.png
index dfeac0a..4035754 100644
Binary files a/toxygen/smileys/default/2709.png and b/toxygen/smileys/default/2709.png differ
diff --git a/toxygen/smileys/default/270A.png b/toxygen/smileys/default/270A.png
index 51eefbb..43d7ca8 100644
Binary files a/toxygen/smileys/default/270A.png and b/toxygen/smileys/default/270A.png differ
diff --git a/toxygen/smileys/default/270B.png b/toxygen/smileys/default/270B.png
index 3f03100..984f829 100644
Binary files a/toxygen/smileys/default/270B.png and b/toxygen/smileys/default/270B.png differ
diff --git a/toxygen/smileys/default/270C.png b/toxygen/smileys/default/270C.png
index 437fc21..7fe482f 100644
Binary files a/toxygen/smileys/default/270C.png and b/toxygen/smileys/default/270C.png differ
diff --git a/toxygen/smileys/default/270F.png b/toxygen/smileys/default/270F.png
index 2aa3ee0..a86cf25 100644
Binary files a/toxygen/smileys/default/270F.png and b/toxygen/smileys/default/270F.png differ
diff --git a/toxygen/smileys/default/2712.png b/toxygen/smileys/default/2712.png
index 97c1072..cc6c6ab 100644
Binary files a/toxygen/smileys/default/2712.png and b/toxygen/smileys/default/2712.png differ
diff --git a/toxygen/smileys/default/2714.png b/toxygen/smileys/default/2714.png
index df3540f..b675396 100644
Binary files a/toxygen/smileys/default/2714.png and b/toxygen/smileys/default/2714.png differ
diff --git a/toxygen/smileys/default/2716.png b/toxygen/smileys/default/2716.png
index 041e6c2..7fac672 100644
Binary files a/toxygen/smileys/default/2716.png and b/toxygen/smileys/default/2716.png differ
diff --git a/toxygen/smileys/default/2728.png b/toxygen/smileys/default/2728.png
index 5e7c381..82aad35 100644
Binary files a/toxygen/smileys/default/2728.png and b/toxygen/smileys/default/2728.png differ
diff --git a/toxygen/smileys/default/2733.png b/toxygen/smileys/default/2733.png
index 334a066..d9b1f08 100644
Binary files a/toxygen/smileys/default/2733.png and b/toxygen/smileys/default/2733.png differ
diff --git a/toxygen/smileys/default/2734.png b/toxygen/smileys/default/2734.png
index 93a995f..f95730e 100644
Binary files a/toxygen/smileys/default/2734.png and b/toxygen/smileys/default/2734.png differ
diff --git a/toxygen/smileys/default/2744.png b/toxygen/smileys/default/2744.png
index bf4d09e..f88a35d 100644
Binary files a/toxygen/smileys/default/2744.png and b/toxygen/smileys/default/2744.png differ
diff --git a/toxygen/smileys/default/2747.png b/toxygen/smileys/default/2747.png
index 1eaf5d2..6179ee0 100644
Binary files a/toxygen/smileys/default/2747.png and b/toxygen/smileys/default/2747.png differ
diff --git a/toxygen/smileys/default/274C.png b/toxygen/smileys/default/274C.png
index 2bf2526..64036e1 100644
Binary files a/toxygen/smileys/default/274C.png and b/toxygen/smileys/default/274C.png differ
diff --git a/toxygen/smileys/default/274E.png b/toxygen/smileys/default/274E.png
index 8bbf629..9a337af 100644
Binary files a/toxygen/smileys/default/274E.png and b/toxygen/smileys/default/274E.png differ
diff --git a/toxygen/smileys/default/2753.png b/toxygen/smileys/default/2753.png
index fd4a4f8..303b2f5 100644
Binary files a/toxygen/smileys/default/2753.png and b/toxygen/smileys/default/2753.png differ
diff --git a/toxygen/smileys/default/2754.png b/toxygen/smileys/default/2754.png
index c1afcb2..ce83bb6 100644
Binary files a/toxygen/smileys/default/2754.png and b/toxygen/smileys/default/2754.png differ
diff --git a/toxygen/smileys/default/2755.png b/toxygen/smileys/default/2755.png
index 80cc60e..74b34e0 100644
Binary files a/toxygen/smileys/default/2755.png and b/toxygen/smileys/default/2755.png differ
diff --git a/toxygen/smileys/default/2757.png b/toxygen/smileys/default/2757.png
index 5ce95cb..0932319 100644
Binary files a/toxygen/smileys/default/2757.png and b/toxygen/smileys/default/2757.png differ
diff --git a/toxygen/smileys/default/2764.png b/toxygen/smileys/default/2764.png
index 20b145d..db9de9e 100644
Binary files a/toxygen/smileys/default/2764.png and b/toxygen/smileys/default/2764.png differ
diff --git a/toxygen/smileys/default/2795.png b/toxygen/smileys/default/2795.png
index fedda3d..33bb432 100644
Binary files a/toxygen/smileys/default/2795.png and b/toxygen/smileys/default/2795.png differ
diff --git a/toxygen/smileys/default/2796.png b/toxygen/smileys/default/2796.png
index 0341ac4..ca89edf 100644
Binary files a/toxygen/smileys/default/2796.png and b/toxygen/smileys/default/2796.png differ
diff --git a/toxygen/smileys/default/2797.png b/toxygen/smileys/default/2797.png
index d820089..04ca489 100644
Binary files a/toxygen/smileys/default/2797.png and b/toxygen/smileys/default/2797.png differ
diff --git a/toxygen/smileys/default/27A1.png b/toxygen/smileys/default/27A1.png
index f0dd357..36fad95 100644
Binary files a/toxygen/smileys/default/27A1.png and b/toxygen/smileys/default/27A1.png differ
diff --git a/toxygen/smileys/default/27B0.png b/toxygen/smileys/default/27B0.png
index 3ce1cc0..8460f2e 100644
Binary files a/toxygen/smileys/default/27B0.png and b/toxygen/smileys/default/27B0.png differ
diff --git a/toxygen/smileys/default/27BF.png b/toxygen/smileys/default/27BF.png
index 798387c..1245f99 100644
Binary files a/toxygen/smileys/default/27BF.png and b/toxygen/smileys/default/27BF.png differ
diff --git a/toxygen/smileys/default/2934.png b/toxygen/smileys/default/2934.png
index 3517e59..7b52ecd 100644
Binary files a/toxygen/smileys/default/2934.png and b/toxygen/smileys/default/2934.png differ
diff --git a/toxygen/smileys/default/2935.png b/toxygen/smileys/default/2935.png
index 857caf4..0aba0d0 100644
Binary files a/toxygen/smileys/default/2935.png and b/toxygen/smileys/default/2935.png differ
diff --git a/toxygen/smileys/default/2B05.png b/toxygen/smileys/default/2B05.png
index 368e2fa..8bacdda 100644
Binary files a/toxygen/smileys/default/2B05.png and b/toxygen/smileys/default/2B05.png differ
diff --git a/toxygen/smileys/default/2B06.png b/toxygen/smileys/default/2B06.png
index 56bb954..b394430 100644
Binary files a/toxygen/smileys/default/2B06.png and b/toxygen/smileys/default/2B06.png differ
diff --git a/toxygen/smileys/default/2B07.png b/toxygen/smileys/default/2B07.png
index ed86d82..bc9532a 100644
Binary files a/toxygen/smileys/default/2B07.png and b/toxygen/smileys/default/2B07.png differ
diff --git a/toxygen/smileys/default/2B1B.png b/toxygen/smileys/default/2B1B.png
index 9f51c6b..6a833f5 100644
Binary files a/toxygen/smileys/default/2B1B.png and b/toxygen/smileys/default/2B1B.png differ
diff --git a/toxygen/smileys/default/2B1C.png b/toxygen/smileys/default/2B1C.png
index 25ce49a..94275fd 100644
Binary files a/toxygen/smileys/default/2B1C.png and b/toxygen/smileys/default/2B1C.png differ
diff --git a/toxygen/smileys/default/2B50.png b/toxygen/smileys/default/2B50.png
index d08be34..358da2b 100644
Binary files a/toxygen/smileys/default/2B50.png and b/toxygen/smileys/default/2B50.png differ
diff --git a/toxygen/smileys/default/2B55.png b/toxygen/smileys/default/2B55.png
index bb71bcc..ff62f1b 100644
Binary files a/toxygen/smileys/default/2B55.png and b/toxygen/smileys/default/2B55.png differ
diff --git a/toxygen/smileys/default/3030.png b/toxygen/smileys/default/3030.png
index 9a8d53a..aeb952e 100644
Binary files a/toxygen/smileys/default/3030.png and b/toxygen/smileys/default/3030.png differ
diff --git a/toxygen/smileys/default/303D.png b/toxygen/smileys/default/303D.png
index 09639ea..6701f76 100644
Binary files a/toxygen/smileys/default/303D.png and b/toxygen/smileys/default/303D.png differ
diff --git a/toxygen/smileys/default/D83CDC04.png b/toxygen/smileys/default/D83CDC04.png
index fb1f1f6..6521d64 100644
Binary files a/toxygen/smileys/default/D83CDC04.png and b/toxygen/smileys/default/D83CDC04.png differ
diff --git a/toxygen/smileys/default/D83CDCCF.png b/toxygen/smileys/default/D83CDCCF.png
index 3ea5b82..754d3c2 100644
Binary files a/toxygen/smileys/default/D83CDCCF.png and b/toxygen/smileys/default/D83CDCCF.png differ
diff --git a/toxygen/smileys/default/D83CDD70.png b/toxygen/smileys/default/D83CDD70.png
index 75ea41b..dd82624 100644
Binary files a/toxygen/smileys/default/D83CDD70.png and b/toxygen/smileys/default/D83CDD70.png differ
diff --git a/toxygen/smileys/default/D83CDD71.png b/toxygen/smileys/default/D83CDD71.png
index 13c53fc..84f20f3 100644
Binary files a/toxygen/smileys/default/D83CDD71.png and b/toxygen/smileys/default/D83CDD71.png differ
diff --git a/toxygen/smileys/default/D83CDD7E.png b/toxygen/smileys/default/D83CDD7E.png
index eb5ebf8..9a56329 100644
Binary files a/toxygen/smileys/default/D83CDD7E.png and b/toxygen/smileys/default/D83CDD7E.png differ
diff --git a/toxygen/smileys/default/D83CDD7F.png b/toxygen/smileys/default/D83CDD7F.png
index c8f6432..aa5dca1 100644
Binary files a/toxygen/smileys/default/D83CDD7F.png and b/toxygen/smileys/default/D83CDD7F.png differ
diff --git a/toxygen/smileys/default/D83CDD8E.png b/toxygen/smileys/default/D83CDD8E.png
index f615013..3e3a43e 100644
Binary files a/toxygen/smileys/default/D83CDD8E.png and b/toxygen/smileys/default/D83CDD8E.png differ
diff --git a/toxygen/smileys/default/D83CDD91.png b/toxygen/smileys/default/D83CDD91.png
index 09b02e0..2f37aac 100644
Binary files a/toxygen/smileys/default/D83CDD91.png and b/toxygen/smileys/default/D83CDD91.png differ
diff --git a/toxygen/smileys/default/D83CDD92.png b/toxygen/smileys/default/D83CDD92.png
index fefcd76..6727be3 100644
Binary files a/toxygen/smileys/default/D83CDD92.png and b/toxygen/smileys/default/D83CDD92.png differ
diff --git a/toxygen/smileys/default/D83CDD93.png b/toxygen/smileys/default/D83CDD93.png
index 2294126..47a754e 100644
Binary files a/toxygen/smileys/default/D83CDD93.png and b/toxygen/smileys/default/D83CDD93.png differ
diff --git a/toxygen/smileys/default/D83CDD94.png b/toxygen/smileys/default/D83CDD94.png
index 5681f7a..0b710e1 100644
Binary files a/toxygen/smileys/default/D83CDD94.png and b/toxygen/smileys/default/D83CDD94.png differ
diff --git a/toxygen/smileys/default/D83CDD95.png b/toxygen/smileys/default/D83CDD95.png
index 4b2176b..25149a7 100644
Binary files a/toxygen/smileys/default/D83CDD95.png and b/toxygen/smileys/default/D83CDD95.png differ
diff --git a/toxygen/smileys/default/D83CDD96.png b/toxygen/smileys/default/D83CDD96.png
index 1835eec..14d0d3f 100644
Binary files a/toxygen/smileys/default/D83CDD96.png and b/toxygen/smileys/default/D83CDD96.png differ
diff --git a/toxygen/smileys/default/D83CDD97.png b/toxygen/smileys/default/D83CDD97.png
index 4c5e3dc..8ffef12 100644
Binary files a/toxygen/smileys/default/D83CDD97.png and b/toxygen/smileys/default/D83CDD97.png differ
diff --git a/toxygen/smileys/default/D83CDD98.png b/toxygen/smileys/default/D83CDD98.png
index 4cd5f0c..7288cbb 100644
Binary files a/toxygen/smileys/default/D83CDD98.png and b/toxygen/smileys/default/D83CDD98.png differ
diff --git a/toxygen/smileys/default/D83CDD99.png b/toxygen/smileys/default/D83CDD99.png
index ab809ad..6d7180d 100644
Binary files a/toxygen/smileys/default/D83CDD99.png and b/toxygen/smileys/default/D83CDD99.png differ
diff --git a/toxygen/smileys/default/D83CDD9A.png b/toxygen/smileys/default/D83CDD9A.png
index 91e0db3..7c34f12 100644
Binary files a/toxygen/smileys/default/D83CDD9A.png and b/toxygen/smileys/default/D83CDD9A.png differ
diff --git a/toxygen/smileys/default/D83CDE01.png b/toxygen/smileys/default/D83CDE01.png
index f3aed09..93c9689 100644
Binary files a/toxygen/smileys/default/D83CDE01.png and b/toxygen/smileys/default/D83CDE01.png differ
diff --git a/toxygen/smileys/default/D83CDF00.png b/toxygen/smileys/default/D83CDF00.png
index f920a25..bec5f14 100644
Binary files a/toxygen/smileys/default/D83CDF00.png and b/toxygen/smileys/default/D83CDF00.png differ
diff --git a/toxygen/smileys/default/D83CDF01.png b/toxygen/smileys/default/D83CDF01.png
index a834fd3..ac39b56 100644
Binary files a/toxygen/smileys/default/D83CDF01.png and b/toxygen/smileys/default/D83CDF01.png differ
diff --git a/toxygen/smileys/default/D83CDF02.png b/toxygen/smileys/default/D83CDF02.png
index c668d0d..c4c1867 100644
Binary files a/toxygen/smileys/default/D83CDF02.png and b/toxygen/smileys/default/D83CDF02.png differ
diff --git a/toxygen/smileys/default/D83CDF03.png b/toxygen/smileys/default/D83CDF03.png
index 67e776a..cfbe0c0 100644
Binary files a/toxygen/smileys/default/D83CDF03.png and b/toxygen/smileys/default/D83CDF03.png differ
diff --git a/toxygen/smileys/default/D83CDF04.png b/toxygen/smileys/default/D83CDF04.png
index 8b5e8fe..fdc05fe 100644
Binary files a/toxygen/smileys/default/D83CDF04.png and b/toxygen/smileys/default/D83CDF04.png differ
diff --git a/toxygen/smileys/default/D83CDF05.png b/toxygen/smileys/default/D83CDF05.png
index 8a9b125..4ee1bf4 100644
Binary files a/toxygen/smileys/default/D83CDF05.png and b/toxygen/smileys/default/D83CDF05.png differ
diff --git a/toxygen/smileys/default/D83CDF06.png b/toxygen/smileys/default/D83CDF06.png
index f456a1d..47856c7 100644
Binary files a/toxygen/smileys/default/D83CDF06.png and b/toxygen/smileys/default/D83CDF06.png differ
diff --git a/toxygen/smileys/default/D83CDF07.png b/toxygen/smileys/default/D83CDF07.png
index 0627f7d..235c6bb 100644
Binary files a/toxygen/smileys/default/D83CDF07.png and b/toxygen/smileys/default/D83CDF07.png differ
diff --git a/toxygen/smileys/default/D83CDF08.png b/toxygen/smileys/default/D83CDF08.png
index 0f9f281..428c842 100644
Binary files a/toxygen/smileys/default/D83CDF08.png and b/toxygen/smileys/default/D83CDF08.png differ
diff --git a/toxygen/smileys/default/D83CDF09.png b/toxygen/smileys/default/D83CDF09.png
index 3075763..ebe76dc 100644
Binary files a/toxygen/smileys/default/D83CDF09.png and b/toxygen/smileys/default/D83CDF09.png differ
diff --git a/toxygen/smileys/default/D83CDF0A.png b/toxygen/smileys/default/D83CDF0A.png
index fb65924..f844fde 100644
Binary files a/toxygen/smileys/default/D83CDF0A.png and b/toxygen/smileys/default/D83CDF0A.png differ
diff --git a/toxygen/smileys/default/D83CDF0B.png b/toxygen/smileys/default/D83CDF0B.png
index 362aea0..76fecab 100644
Binary files a/toxygen/smileys/default/D83CDF0B.png and b/toxygen/smileys/default/D83CDF0B.png differ
diff --git a/toxygen/smileys/default/D83CDF0C.png b/toxygen/smileys/default/D83CDF0C.png
index 222ef58..8f50380 100644
Binary files a/toxygen/smileys/default/D83CDF0C.png and b/toxygen/smileys/default/D83CDF0C.png differ
diff --git a/toxygen/smileys/default/D83CDF0D.png b/toxygen/smileys/default/D83CDF0D.png
index b76776d..ab306db 100644
Binary files a/toxygen/smileys/default/D83CDF0D.png and b/toxygen/smileys/default/D83CDF0D.png differ
diff --git a/toxygen/smileys/default/D83CDF0E.png b/toxygen/smileys/default/D83CDF0E.png
index 8a21855..3ccaf4f 100644
Binary files a/toxygen/smileys/default/D83CDF0E.png and b/toxygen/smileys/default/D83CDF0E.png differ
diff --git a/toxygen/smileys/default/D83CDF0F.png b/toxygen/smileys/default/D83CDF0F.png
index 3cb44be..5d3be08 100644
Binary files a/toxygen/smileys/default/D83CDF0F.png and b/toxygen/smileys/default/D83CDF0F.png differ
diff --git a/toxygen/smileys/default/D83CDF10.png b/toxygen/smileys/default/D83CDF10.png
index 1b50e85..b5f35fb 100644
Binary files a/toxygen/smileys/default/D83CDF10.png and b/toxygen/smileys/default/D83CDF10.png differ
diff --git a/toxygen/smileys/default/D83CDF11.png b/toxygen/smileys/default/D83CDF11.png
index 031d83f..078260d 100644
Binary files a/toxygen/smileys/default/D83CDF11.png and b/toxygen/smileys/default/D83CDF11.png differ
diff --git a/toxygen/smileys/default/D83CDF12.png b/toxygen/smileys/default/D83CDF12.png
index 1833939..d0a0f72 100644
Binary files a/toxygen/smileys/default/D83CDF12.png and b/toxygen/smileys/default/D83CDF12.png differ
diff --git a/toxygen/smileys/default/D83CDF13.png b/toxygen/smileys/default/D83CDF13.png
index 37c2f24..2c72896 100644
Binary files a/toxygen/smileys/default/D83CDF13.png and b/toxygen/smileys/default/D83CDF13.png differ
diff --git a/toxygen/smileys/default/D83CDF14.png b/toxygen/smileys/default/D83CDF14.png
index 68e1e8a..66696d8 100644
Binary files a/toxygen/smileys/default/D83CDF14.png and b/toxygen/smileys/default/D83CDF14.png differ
diff --git a/toxygen/smileys/default/D83CDF15.png b/toxygen/smileys/default/D83CDF15.png
index 8a91553..ff5c8e0 100644
Binary files a/toxygen/smileys/default/D83CDF15.png and b/toxygen/smileys/default/D83CDF15.png differ
diff --git a/toxygen/smileys/default/D83CDF16.png b/toxygen/smileys/default/D83CDF16.png
index efcf233..63734dd 100644
Binary files a/toxygen/smileys/default/D83CDF16.png and b/toxygen/smileys/default/D83CDF16.png differ
diff --git a/toxygen/smileys/default/D83CDF17.png b/toxygen/smileys/default/D83CDF17.png
index 18e5714..97e3de6 100644
Binary files a/toxygen/smileys/default/D83CDF17.png and b/toxygen/smileys/default/D83CDF17.png differ
diff --git a/toxygen/smileys/default/D83CDF18.png b/toxygen/smileys/default/D83CDF18.png
index eb66d26..13f8d9c 100644
Binary files a/toxygen/smileys/default/D83CDF18.png and b/toxygen/smileys/default/D83CDF18.png differ
diff --git a/toxygen/smileys/default/D83CDF19.png b/toxygen/smileys/default/D83CDF19.png
index 6092dfa..4443ab3 100644
Binary files a/toxygen/smileys/default/D83CDF19.png and b/toxygen/smileys/default/D83CDF19.png differ
diff --git a/toxygen/smileys/default/D83CDF1A.png b/toxygen/smileys/default/D83CDF1A.png
index edfbed2..48cf54e 100644
Binary files a/toxygen/smileys/default/D83CDF1A.png and b/toxygen/smileys/default/D83CDF1A.png differ
diff --git a/toxygen/smileys/default/D83CDF1B.png b/toxygen/smileys/default/D83CDF1B.png
index 42516ba..3f93634 100644
Binary files a/toxygen/smileys/default/D83CDF1B.png and b/toxygen/smileys/default/D83CDF1B.png differ
diff --git a/toxygen/smileys/default/D83CDF1C.png b/toxygen/smileys/default/D83CDF1C.png
index 048e306..a57bf54 100644
Binary files a/toxygen/smileys/default/D83CDF1C.png and b/toxygen/smileys/default/D83CDF1C.png differ
diff --git a/toxygen/smileys/default/D83CDF1D.png b/toxygen/smileys/default/D83CDF1D.png
index 3c2f76a..c982949 100644
Binary files a/toxygen/smileys/default/D83CDF1D.png and b/toxygen/smileys/default/D83CDF1D.png differ
diff --git a/toxygen/smileys/default/D83CDF1E.png b/toxygen/smileys/default/D83CDF1E.png
index 888b5c9..a78f6b1 100644
Binary files a/toxygen/smileys/default/D83CDF1E.png and b/toxygen/smileys/default/D83CDF1E.png differ
diff --git a/toxygen/smileys/default/D83CDF1F.png b/toxygen/smileys/default/D83CDF1F.png
index 1350976..a5aa959 100644
Binary files a/toxygen/smileys/default/D83CDF1F.png and b/toxygen/smileys/default/D83CDF1F.png differ
diff --git a/toxygen/smileys/default/D83CDF20.png b/toxygen/smileys/default/D83CDF20.png
index ea8ff38..502a017 100644
Binary files a/toxygen/smileys/default/D83CDF20.png and b/toxygen/smileys/default/D83CDF20.png differ
diff --git a/toxygen/smileys/default/D83CDF30.png b/toxygen/smileys/default/D83CDF30.png
index 37e573e..ca0e78a 100644
Binary files a/toxygen/smileys/default/D83CDF30.png and b/toxygen/smileys/default/D83CDF30.png differ
diff --git a/toxygen/smileys/default/D83CDF31.png b/toxygen/smileys/default/D83CDF31.png
index 036d056..e2a1224 100644
Binary files a/toxygen/smileys/default/D83CDF31.png and b/toxygen/smileys/default/D83CDF31.png differ
diff --git a/toxygen/smileys/default/D83CDF32.png b/toxygen/smileys/default/D83CDF32.png
index d0658c0..ea51b2b 100644
Binary files a/toxygen/smileys/default/D83CDF32.png and b/toxygen/smileys/default/D83CDF32.png differ
diff --git a/toxygen/smileys/default/D83CDF33.png b/toxygen/smileys/default/D83CDF33.png
index d9130ec..25ad311 100644
Binary files a/toxygen/smileys/default/D83CDF33.png and b/toxygen/smileys/default/D83CDF33.png differ
diff --git a/toxygen/smileys/default/D83CDF34.png b/toxygen/smileys/default/D83CDF34.png
index 60a8055..5d1fc87 100644
Binary files a/toxygen/smileys/default/D83CDF34.png and b/toxygen/smileys/default/D83CDF34.png differ
diff --git a/toxygen/smileys/default/D83CDF35.png b/toxygen/smileys/default/D83CDF35.png
index d72047e..d9f1ebe 100644
Binary files a/toxygen/smileys/default/D83CDF35.png and b/toxygen/smileys/default/D83CDF35.png differ
diff --git a/toxygen/smileys/default/D83CDF37.png b/toxygen/smileys/default/D83CDF37.png
index 59a7b43..58f4f4c 100644
Binary files a/toxygen/smileys/default/D83CDF37.png and b/toxygen/smileys/default/D83CDF37.png differ
diff --git a/toxygen/smileys/default/D83CDF38.png b/toxygen/smileys/default/D83CDF38.png
index ab096d3..3e76c62 100644
Binary files a/toxygen/smileys/default/D83CDF38.png and b/toxygen/smileys/default/D83CDF38.png differ
diff --git a/toxygen/smileys/default/D83CDF39.png b/toxygen/smileys/default/D83CDF39.png
index 5c285fa..aec58de 100644
Binary files a/toxygen/smileys/default/D83CDF39.png and b/toxygen/smileys/default/D83CDF39.png differ
diff --git a/toxygen/smileys/default/D83CDF3A.png b/toxygen/smileys/default/D83CDF3A.png
index 6058a37..1b83115 100644
Binary files a/toxygen/smileys/default/D83CDF3A.png and b/toxygen/smileys/default/D83CDF3A.png differ
diff --git a/toxygen/smileys/default/D83CDF3B.png b/toxygen/smileys/default/D83CDF3B.png
index 6ff1d5d..97da792 100644
Binary files a/toxygen/smileys/default/D83CDF3B.png and b/toxygen/smileys/default/D83CDF3B.png differ
diff --git a/toxygen/smileys/default/D83CDF3C.png b/toxygen/smileys/default/D83CDF3C.png
index 18e7026..cc737e7 100644
Binary files a/toxygen/smileys/default/D83CDF3C.png and b/toxygen/smileys/default/D83CDF3C.png differ
diff --git a/toxygen/smileys/default/D83CDF3D.png b/toxygen/smileys/default/D83CDF3D.png
index 853a69f..648a283 100644
Binary files a/toxygen/smileys/default/D83CDF3D.png and b/toxygen/smileys/default/D83CDF3D.png differ
diff --git a/toxygen/smileys/default/D83CDF3E.png b/toxygen/smileys/default/D83CDF3E.png
index e63cc58..ecbf4cd 100644
Binary files a/toxygen/smileys/default/D83CDF3E.png and b/toxygen/smileys/default/D83CDF3E.png differ
diff --git a/toxygen/smileys/default/D83CDF3F.png b/toxygen/smileys/default/D83CDF3F.png
index 789498b..dd5399e 100644
Binary files a/toxygen/smileys/default/D83CDF3F.png and b/toxygen/smileys/default/D83CDF3F.png differ
diff --git a/toxygen/smileys/default/D83CDF40.png b/toxygen/smileys/default/D83CDF40.png
index 9699a95..86ac7ed 100644
Binary files a/toxygen/smileys/default/D83CDF40.png and b/toxygen/smileys/default/D83CDF40.png differ
diff --git a/toxygen/smileys/default/D83CDF41.png b/toxygen/smileys/default/D83CDF41.png
index a2876b5..e2a9cdd 100644
Binary files a/toxygen/smileys/default/D83CDF41.png and b/toxygen/smileys/default/D83CDF41.png differ
diff --git a/toxygen/smileys/default/D83CDF42.png b/toxygen/smileys/default/D83CDF42.png
index d2b2b31..640daa0 100644
Binary files a/toxygen/smileys/default/D83CDF42.png and b/toxygen/smileys/default/D83CDF42.png differ
diff --git a/toxygen/smileys/default/D83CDF43.png b/toxygen/smileys/default/D83CDF43.png
index 0be4af5..94773f8 100644
Binary files a/toxygen/smileys/default/D83CDF43.png and b/toxygen/smileys/default/D83CDF43.png differ
diff --git a/toxygen/smileys/default/D83CDF44.png b/toxygen/smileys/default/D83CDF44.png
index 73218b9..f1114e7 100644
Binary files a/toxygen/smileys/default/D83CDF44.png and b/toxygen/smileys/default/D83CDF44.png differ
diff --git a/toxygen/smileys/default/D83CDF45.png b/toxygen/smileys/default/D83CDF45.png
index 4b3fae0..d11e096 100644
Binary files a/toxygen/smileys/default/D83CDF45.png and b/toxygen/smileys/default/D83CDF45.png differ
diff --git a/toxygen/smileys/default/D83CDF46.png b/toxygen/smileys/default/D83CDF46.png
index cce4962..a0ea6fc 100644
Binary files a/toxygen/smileys/default/D83CDF46.png and b/toxygen/smileys/default/D83CDF46.png differ
diff --git a/toxygen/smileys/default/D83CDF47.png b/toxygen/smileys/default/D83CDF47.png
index 05ba907..ffe08fe 100644
Binary files a/toxygen/smileys/default/D83CDF47.png and b/toxygen/smileys/default/D83CDF47.png differ
diff --git a/toxygen/smileys/default/D83CDF48.png b/toxygen/smileys/default/D83CDF48.png
index 3e50ffc..dd86e85 100644
Binary files a/toxygen/smileys/default/D83CDF48.png and b/toxygen/smileys/default/D83CDF48.png differ
diff --git a/toxygen/smileys/default/D83CDF49.png b/toxygen/smileys/default/D83CDF49.png
index 1110ede..45f804c 100644
Binary files a/toxygen/smileys/default/D83CDF49.png and b/toxygen/smileys/default/D83CDF49.png differ
diff --git a/toxygen/smileys/default/D83CDF4A.png b/toxygen/smileys/default/D83CDF4A.png
index 9747153..7b3689a 100644
Binary files a/toxygen/smileys/default/D83CDF4A.png and b/toxygen/smileys/default/D83CDF4A.png differ
diff --git a/toxygen/smileys/default/D83CDF4B.png b/toxygen/smileys/default/D83CDF4B.png
index f88a92e..3fa9c85 100644
Binary files a/toxygen/smileys/default/D83CDF4B.png and b/toxygen/smileys/default/D83CDF4B.png differ
diff --git a/toxygen/smileys/default/D83CDF4C.png b/toxygen/smileys/default/D83CDF4C.png
index 8843766..700ff44 100644
Binary files a/toxygen/smileys/default/D83CDF4C.png and b/toxygen/smileys/default/D83CDF4C.png differ
diff --git a/toxygen/smileys/default/D83CDF4D.png b/toxygen/smileys/default/D83CDF4D.png
index 7e96d5f..9f1070e 100644
Binary files a/toxygen/smileys/default/D83CDF4D.png and b/toxygen/smileys/default/D83CDF4D.png differ
diff --git a/toxygen/smileys/default/D83CDF4E.png b/toxygen/smileys/default/D83CDF4E.png
index 73a3174..e360df0 100644
Binary files a/toxygen/smileys/default/D83CDF4E.png and b/toxygen/smileys/default/D83CDF4E.png differ
diff --git a/toxygen/smileys/default/D83CDF4F.png b/toxygen/smileys/default/D83CDF4F.png
index 9dec886..4f42927 100644
Binary files a/toxygen/smileys/default/D83CDF4F.png and b/toxygen/smileys/default/D83CDF4F.png differ
diff --git a/toxygen/smileys/default/D83CDF50.png b/toxygen/smileys/default/D83CDF50.png
index b56380f..436b580 100644
Binary files a/toxygen/smileys/default/D83CDF50.png and b/toxygen/smileys/default/D83CDF50.png differ
diff --git a/toxygen/smileys/default/D83CDF51.png b/toxygen/smileys/default/D83CDF51.png
index df81f72..677749f 100644
Binary files a/toxygen/smileys/default/D83CDF51.png and b/toxygen/smileys/default/D83CDF51.png differ
diff --git a/toxygen/smileys/default/D83CDF52.png b/toxygen/smileys/default/D83CDF52.png
index 262cd7a..3069b83 100644
Binary files a/toxygen/smileys/default/D83CDF52.png and b/toxygen/smileys/default/D83CDF52.png differ
diff --git a/toxygen/smileys/default/D83CDF53.png b/toxygen/smileys/default/D83CDF53.png
index 5438131..eeb27c8 100644
Binary files a/toxygen/smileys/default/D83CDF53.png and b/toxygen/smileys/default/D83CDF53.png differ
diff --git a/toxygen/smileys/default/D83CDF54.png b/toxygen/smileys/default/D83CDF54.png
index f5dc18f..8065a3e 100644
Binary files a/toxygen/smileys/default/D83CDF54.png and b/toxygen/smileys/default/D83CDF54.png differ
diff --git a/toxygen/smileys/default/D83CDF55.png b/toxygen/smileys/default/D83CDF55.png
index d3a43de..5cb9566 100644
Binary files a/toxygen/smileys/default/D83CDF55.png and b/toxygen/smileys/default/D83CDF55.png differ
diff --git a/toxygen/smileys/default/D83CDF56.png b/toxygen/smileys/default/D83CDF56.png
index 3cae88e..2c9d393 100644
Binary files a/toxygen/smileys/default/D83CDF56.png and b/toxygen/smileys/default/D83CDF56.png differ
diff --git a/toxygen/smileys/default/D83CDF57.png b/toxygen/smileys/default/D83CDF57.png
index fafc625..d21ea0d 100644
Binary files a/toxygen/smileys/default/D83CDF57.png and b/toxygen/smileys/default/D83CDF57.png differ
diff --git a/toxygen/smileys/default/D83CDF58.png b/toxygen/smileys/default/D83CDF58.png
index 5e40d1b..948a08d 100644
Binary files a/toxygen/smileys/default/D83CDF58.png and b/toxygen/smileys/default/D83CDF58.png differ
diff --git a/toxygen/smileys/default/D83CDF59.png b/toxygen/smileys/default/D83CDF59.png
index 9c72e3c..61ab47a 100644
Binary files a/toxygen/smileys/default/D83CDF59.png and b/toxygen/smileys/default/D83CDF59.png differ
diff --git a/toxygen/smileys/default/D83CDF5A.png b/toxygen/smileys/default/D83CDF5A.png
index e9e4f2e..6cb3253 100644
Binary files a/toxygen/smileys/default/D83CDF5A.png and b/toxygen/smileys/default/D83CDF5A.png differ
diff --git a/toxygen/smileys/default/D83CDF5B.png b/toxygen/smileys/default/D83CDF5B.png
index a6808e1..0a79679 100644
Binary files a/toxygen/smileys/default/D83CDF5B.png and b/toxygen/smileys/default/D83CDF5B.png differ
diff --git a/toxygen/smileys/default/D83CDF5C.png b/toxygen/smileys/default/D83CDF5C.png
index 3d94196..12fa5e9 100644
Binary files a/toxygen/smileys/default/D83CDF5C.png and b/toxygen/smileys/default/D83CDF5C.png differ
diff --git a/toxygen/smileys/default/D83CDF5D.png b/toxygen/smileys/default/D83CDF5D.png
index 7b67c2f..f76f82a 100644
Binary files a/toxygen/smileys/default/D83CDF5D.png and b/toxygen/smileys/default/D83CDF5D.png differ
diff --git a/toxygen/smileys/default/D83CDF5E.png b/toxygen/smileys/default/D83CDF5E.png
index 9a99501..281ddda 100644
Binary files a/toxygen/smileys/default/D83CDF5E.png and b/toxygen/smileys/default/D83CDF5E.png differ
diff --git a/toxygen/smileys/default/D83CDF5F.png b/toxygen/smileys/default/D83CDF5F.png
index 6b0d1cf..0b4ca04 100644
Binary files a/toxygen/smileys/default/D83CDF5F.png and b/toxygen/smileys/default/D83CDF5F.png differ
diff --git a/toxygen/smileys/default/D83CDF60.png b/toxygen/smileys/default/D83CDF60.png
index 27ab69e..d25bedc 100644
Binary files a/toxygen/smileys/default/D83CDF60.png and b/toxygen/smileys/default/D83CDF60.png differ
diff --git a/toxygen/smileys/default/D83CDF61.png b/toxygen/smileys/default/D83CDF61.png
index edb01f7..f8a2280 100644
Binary files a/toxygen/smileys/default/D83CDF61.png and b/toxygen/smileys/default/D83CDF61.png differ
diff --git a/toxygen/smileys/default/D83CDF62.png b/toxygen/smileys/default/D83CDF62.png
index ce1b492..62a24bc 100644
Binary files a/toxygen/smileys/default/D83CDF62.png and b/toxygen/smileys/default/D83CDF62.png differ
diff --git a/toxygen/smileys/default/D83CDF63.png b/toxygen/smileys/default/D83CDF63.png
index 9cb9907..361eb81 100644
Binary files a/toxygen/smileys/default/D83CDF63.png and b/toxygen/smileys/default/D83CDF63.png differ
diff --git a/toxygen/smileys/default/D83CDF64.png b/toxygen/smileys/default/D83CDF64.png
index 3aa20b7..5bd6768 100644
Binary files a/toxygen/smileys/default/D83CDF64.png and b/toxygen/smileys/default/D83CDF64.png differ
diff --git a/toxygen/smileys/default/D83CDF65.png b/toxygen/smileys/default/D83CDF65.png
index a8d746f..a480926 100644
Binary files a/toxygen/smileys/default/D83CDF65.png and b/toxygen/smileys/default/D83CDF65.png differ
diff --git a/toxygen/smileys/default/D83CDF66.png b/toxygen/smileys/default/D83CDF66.png
index 7e6e8ab..7d67e8e 100644
Binary files a/toxygen/smileys/default/D83CDF66.png and b/toxygen/smileys/default/D83CDF66.png differ
diff --git a/toxygen/smileys/default/D83CDF67.png b/toxygen/smileys/default/D83CDF67.png
index e64baba..b88025d 100644
Binary files a/toxygen/smileys/default/D83CDF67.png and b/toxygen/smileys/default/D83CDF67.png differ
diff --git a/toxygen/smileys/default/D83CDF68.png b/toxygen/smileys/default/D83CDF68.png
index 0e23805..429f44e 100644
Binary files a/toxygen/smileys/default/D83CDF68.png and b/toxygen/smileys/default/D83CDF68.png differ
diff --git a/toxygen/smileys/default/D83CDF69.png b/toxygen/smileys/default/D83CDF69.png
index de6d759..54efe4f 100644
Binary files a/toxygen/smileys/default/D83CDF69.png and b/toxygen/smileys/default/D83CDF69.png differ
diff --git a/toxygen/smileys/default/D83CDF6A.png b/toxygen/smileys/default/D83CDF6A.png
index cd6c10a..a739508 100644
Binary files a/toxygen/smileys/default/D83CDF6A.png and b/toxygen/smileys/default/D83CDF6A.png differ
diff --git a/toxygen/smileys/default/D83CDF6B.png b/toxygen/smileys/default/D83CDF6B.png
index 73ad91c..b16a6e0 100644
Binary files a/toxygen/smileys/default/D83CDF6B.png and b/toxygen/smileys/default/D83CDF6B.png differ
diff --git a/toxygen/smileys/default/D83CDF6C.png b/toxygen/smileys/default/D83CDF6C.png
index fbc30fd..b796b6a 100644
Binary files a/toxygen/smileys/default/D83CDF6C.png and b/toxygen/smileys/default/D83CDF6C.png differ
diff --git a/toxygen/smileys/default/D83CDF6D.png b/toxygen/smileys/default/D83CDF6D.png
index 90a201a..622f296 100644
Binary files a/toxygen/smileys/default/D83CDF6D.png and b/toxygen/smileys/default/D83CDF6D.png differ
diff --git a/toxygen/smileys/default/D83CDF6E.png b/toxygen/smileys/default/D83CDF6E.png
index f3a454c..c534a4b 100644
Binary files a/toxygen/smileys/default/D83CDF6E.png and b/toxygen/smileys/default/D83CDF6E.png differ
diff --git a/toxygen/smileys/default/D83CDF6F.png b/toxygen/smileys/default/D83CDF6F.png
index f64f24e..3f03181 100644
Binary files a/toxygen/smileys/default/D83CDF6F.png and b/toxygen/smileys/default/D83CDF6F.png differ
diff --git a/toxygen/smileys/default/D83CDF70.png b/toxygen/smileys/default/D83CDF70.png
index 4101f40..f930ce7 100644
Binary files a/toxygen/smileys/default/D83CDF70.png and b/toxygen/smileys/default/D83CDF70.png differ
diff --git a/toxygen/smileys/default/D83CDF71.png b/toxygen/smileys/default/D83CDF71.png
index dce9338..0db1d71 100644
Binary files a/toxygen/smileys/default/D83CDF71.png and b/toxygen/smileys/default/D83CDF71.png differ
diff --git a/toxygen/smileys/default/D83CDF72.png b/toxygen/smileys/default/D83CDF72.png
index 3f26b49..0ae27b1 100644
Binary files a/toxygen/smileys/default/D83CDF72.png and b/toxygen/smileys/default/D83CDF72.png differ
diff --git a/toxygen/smileys/default/D83CDF73.png b/toxygen/smileys/default/D83CDF73.png
index 4b8c2ef..5b7dcfa 100644
Binary files a/toxygen/smileys/default/D83CDF73.png and b/toxygen/smileys/default/D83CDF73.png differ
diff --git a/toxygen/smileys/default/D83CDF74.png b/toxygen/smileys/default/D83CDF74.png
index 368e073..a15bf59 100644
Binary files a/toxygen/smileys/default/D83CDF74.png and b/toxygen/smileys/default/D83CDF74.png differ
diff --git a/toxygen/smileys/default/D83CDF75.png b/toxygen/smileys/default/D83CDF75.png
index e1fd614..cc30ad5 100644
Binary files a/toxygen/smileys/default/D83CDF75.png and b/toxygen/smileys/default/D83CDF75.png differ
diff --git a/toxygen/smileys/default/D83CDF76.png b/toxygen/smileys/default/D83CDF76.png
index 84afa14..449d352 100644
Binary files a/toxygen/smileys/default/D83CDF76.png and b/toxygen/smileys/default/D83CDF76.png differ
diff --git a/toxygen/smileys/default/D83CDF77.png b/toxygen/smileys/default/D83CDF77.png
index 0ac8434..12098c5 100644
Binary files a/toxygen/smileys/default/D83CDF77.png and b/toxygen/smileys/default/D83CDF77.png differ
diff --git a/toxygen/smileys/default/D83CDF78.png b/toxygen/smileys/default/D83CDF78.png
index ea85994..f4ed4ea 100644
Binary files a/toxygen/smileys/default/D83CDF78.png and b/toxygen/smileys/default/D83CDF78.png differ
diff --git a/toxygen/smileys/default/D83CDF79.png b/toxygen/smileys/default/D83CDF79.png
index 5b94fda..ce34a5f 100644
Binary files a/toxygen/smileys/default/D83CDF79.png and b/toxygen/smileys/default/D83CDF79.png differ
diff --git a/toxygen/smileys/default/D83CDF7A.png b/toxygen/smileys/default/D83CDF7A.png
index 7f8a1f2..e5efdae 100644
Binary files a/toxygen/smileys/default/D83CDF7A.png and b/toxygen/smileys/default/D83CDF7A.png differ
diff --git a/toxygen/smileys/default/D83CDF7B.png b/toxygen/smileys/default/D83CDF7B.png
index 5fac44f..f690c80 100644
Binary files a/toxygen/smileys/default/D83CDF7B.png and b/toxygen/smileys/default/D83CDF7B.png differ
diff --git a/toxygen/smileys/default/D83CDF7C.png b/toxygen/smileys/default/D83CDF7C.png
index 765efa2..81e6102 100644
Binary files a/toxygen/smileys/default/D83CDF7C.png and b/toxygen/smileys/default/D83CDF7C.png differ
diff --git a/toxygen/smileys/default/D83CDF80.png b/toxygen/smileys/default/D83CDF80.png
index 3e00c99..18bfbb4 100644
Binary files a/toxygen/smileys/default/D83CDF80.png and b/toxygen/smileys/default/D83CDF80.png differ
diff --git a/toxygen/smileys/default/D83CDF81.png b/toxygen/smileys/default/D83CDF81.png
index 6fa89b5..aa28c36 100644
Binary files a/toxygen/smileys/default/D83CDF81.png and b/toxygen/smileys/default/D83CDF81.png differ
diff --git a/toxygen/smileys/default/D83CDF82.png b/toxygen/smileys/default/D83CDF82.png
index 5de5f4e..a9c0f5b 100644
Binary files a/toxygen/smileys/default/D83CDF82.png and b/toxygen/smileys/default/D83CDF82.png differ
diff --git a/toxygen/smileys/default/D83CDF83.png b/toxygen/smileys/default/D83CDF83.png
index 29f2d56..d446cf6 100644
Binary files a/toxygen/smileys/default/D83CDF83.png and b/toxygen/smileys/default/D83CDF83.png differ
diff --git a/toxygen/smileys/default/D83CDF84.png b/toxygen/smileys/default/D83CDF84.png
index d219ab8..c86aa0f 100644
Binary files a/toxygen/smileys/default/D83CDF84.png and b/toxygen/smileys/default/D83CDF84.png differ
diff --git a/toxygen/smileys/default/D83CDF85.png b/toxygen/smileys/default/D83CDF85.png
index 2b96030..d9a4273 100644
Binary files a/toxygen/smileys/default/D83CDF85.png and b/toxygen/smileys/default/D83CDF85.png differ
diff --git a/toxygen/smileys/default/D83CDF86.png b/toxygen/smileys/default/D83CDF86.png
index bbe1a2f..3c07faf 100644
Binary files a/toxygen/smileys/default/D83CDF86.png and b/toxygen/smileys/default/D83CDF86.png differ
diff --git a/toxygen/smileys/default/D83CDF87.png b/toxygen/smileys/default/D83CDF87.png
index 1af4546..6fb75ec 100644
Binary files a/toxygen/smileys/default/D83CDF87.png and b/toxygen/smileys/default/D83CDF87.png differ
diff --git a/toxygen/smileys/default/D83CDF88.png b/toxygen/smileys/default/D83CDF88.png
index f208b55..ad51677 100644
Binary files a/toxygen/smileys/default/D83CDF88.png and b/toxygen/smileys/default/D83CDF88.png differ
diff --git a/toxygen/smileys/default/D83CDF89.png b/toxygen/smileys/default/D83CDF89.png
index aaf4071..5c4d559 100644
Binary files a/toxygen/smileys/default/D83CDF89.png and b/toxygen/smileys/default/D83CDF89.png differ
diff --git a/toxygen/smileys/default/D83CDF8A.png b/toxygen/smileys/default/D83CDF8A.png
index ace4f9e..7d5afa9 100644
Binary files a/toxygen/smileys/default/D83CDF8A.png and b/toxygen/smileys/default/D83CDF8A.png differ
diff --git a/toxygen/smileys/default/D83CDF8B.png b/toxygen/smileys/default/D83CDF8B.png
index fedb653..51c96fe 100644
Binary files a/toxygen/smileys/default/D83CDF8B.png and b/toxygen/smileys/default/D83CDF8B.png differ
diff --git a/toxygen/smileys/default/D83CDF8C.png b/toxygen/smileys/default/D83CDF8C.png
index 3b62bba..f2f460b 100644
Binary files a/toxygen/smileys/default/D83CDF8C.png and b/toxygen/smileys/default/D83CDF8C.png differ
diff --git a/toxygen/smileys/default/D83CDF8D.png b/toxygen/smileys/default/D83CDF8D.png
index f73d236..b83bebb 100644
Binary files a/toxygen/smileys/default/D83CDF8D.png and b/toxygen/smileys/default/D83CDF8D.png differ
diff --git a/toxygen/smileys/default/D83CDF8E.png b/toxygen/smileys/default/D83CDF8E.png
index a3dcad2..734e849 100644
Binary files a/toxygen/smileys/default/D83CDF8E.png and b/toxygen/smileys/default/D83CDF8E.png differ
diff --git a/toxygen/smileys/default/D83CDF8F.png b/toxygen/smileys/default/D83CDF8F.png
index ef3b5fe..a23ab7e 100644
Binary files a/toxygen/smileys/default/D83CDF8F.png and b/toxygen/smileys/default/D83CDF8F.png differ
diff --git a/toxygen/smileys/default/D83CDF90.png b/toxygen/smileys/default/D83CDF90.png
index 17e008f..7a282a3 100644
Binary files a/toxygen/smileys/default/D83CDF90.png and b/toxygen/smileys/default/D83CDF90.png differ
diff --git a/toxygen/smileys/default/D83CDF91.png b/toxygen/smileys/default/D83CDF91.png
index a306b33..2c748d0 100644
Binary files a/toxygen/smileys/default/D83CDF91.png and b/toxygen/smileys/default/D83CDF91.png differ
diff --git a/toxygen/smileys/default/D83CDF92.png b/toxygen/smileys/default/D83CDF92.png
index 557fcf4..485bd18 100644
Binary files a/toxygen/smileys/default/D83CDF92.png and b/toxygen/smileys/default/D83CDF92.png differ
diff --git a/toxygen/smileys/default/D83CDF93.png b/toxygen/smileys/default/D83CDF93.png
index 8d6ae23..5a601fe 100644
Binary files a/toxygen/smileys/default/D83CDF93.png and b/toxygen/smileys/default/D83CDF93.png differ
diff --git a/toxygen/smileys/default/D83CDFA0.png b/toxygen/smileys/default/D83CDFA0.png
index 7e28985..0ba4267 100644
Binary files a/toxygen/smileys/default/D83CDFA0.png and b/toxygen/smileys/default/D83CDFA0.png differ
diff --git a/toxygen/smileys/default/D83CDFA1.png b/toxygen/smileys/default/D83CDFA1.png
index 0413512..d59c5e5 100644
Binary files a/toxygen/smileys/default/D83CDFA1.png and b/toxygen/smileys/default/D83CDFA1.png differ
diff --git a/toxygen/smileys/default/D83CDFA2.png b/toxygen/smileys/default/D83CDFA2.png
index eb1699c..3e8437b 100644
Binary files a/toxygen/smileys/default/D83CDFA2.png and b/toxygen/smileys/default/D83CDFA2.png differ
diff --git a/toxygen/smileys/default/D83CDFA3.png b/toxygen/smileys/default/D83CDFA3.png
index 215a5a4..493f9f4 100644
Binary files a/toxygen/smileys/default/D83CDFA3.png and b/toxygen/smileys/default/D83CDFA3.png differ
diff --git a/toxygen/smileys/default/D83CDFA4.png b/toxygen/smileys/default/D83CDFA4.png
index 8f12411..8ad0988 100644
Binary files a/toxygen/smileys/default/D83CDFA4.png and b/toxygen/smileys/default/D83CDFA4.png differ
diff --git a/toxygen/smileys/default/D83CDFA5.png b/toxygen/smileys/default/D83CDFA5.png
index 7d73059..d21fd0e 100644
Binary files a/toxygen/smileys/default/D83CDFA5.png and b/toxygen/smileys/default/D83CDFA5.png differ
diff --git a/toxygen/smileys/default/D83CDFA6.png b/toxygen/smileys/default/D83CDFA6.png
index 8a0dceb..e3a45c1 100644
Binary files a/toxygen/smileys/default/D83CDFA6.png and b/toxygen/smileys/default/D83CDFA6.png differ
diff --git a/toxygen/smileys/default/D83CDFA7.png b/toxygen/smileys/default/D83CDFA7.png
index 3b36443..e351eff 100644
Binary files a/toxygen/smileys/default/D83CDFA7.png and b/toxygen/smileys/default/D83CDFA7.png differ
diff --git a/toxygen/smileys/default/D83CDFA8.png b/toxygen/smileys/default/D83CDFA8.png
index 73bba44..ef35ada 100644
Binary files a/toxygen/smileys/default/D83CDFA8.png and b/toxygen/smileys/default/D83CDFA8.png differ
diff --git a/toxygen/smileys/default/D83CDFA9.png b/toxygen/smileys/default/D83CDFA9.png
index 1337f64..27f6c29 100644
Binary files a/toxygen/smileys/default/D83CDFA9.png and b/toxygen/smileys/default/D83CDFA9.png differ
diff --git a/toxygen/smileys/default/D83CDFAA.png b/toxygen/smileys/default/D83CDFAA.png
index 81fd66e..ccb34e0 100644
Binary files a/toxygen/smileys/default/D83CDFAA.png and b/toxygen/smileys/default/D83CDFAA.png differ
diff --git a/toxygen/smileys/default/D83CDFAB.png b/toxygen/smileys/default/D83CDFAB.png
index a0ca7dc..38e00dd 100644
Binary files a/toxygen/smileys/default/D83CDFAB.png and b/toxygen/smileys/default/D83CDFAB.png differ
diff --git a/toxygen/smileys/default/D83CDFAC.png b/toxygen/smileys/default/D83CDFAC.png
index 3effe3a..6ddf1db 100644
Binary files a/toxygen/smileys/default/D83CDFAC.png and b/toxygen/smileys/default/D83CDFAC.png differ
diff --git a/toxygen/smileys/default/D83CDFAD.png b/toxygen/smileys/default/D83CDFAD.png
index 97b9917..ec99842 100644
Binary files a/toxygen/smileys/default/D83CDFAD.png and b/toxygen/smileys/default/D83CDFAD.png differ
diff --git a/toxygen/smileys/default/D83CDFAE.png b/toxygen/smileys/default/D83CDFAE.png
index c125606..a94e3a6 100644
Binary files a/toxygen/smileys/default/D83CDFAE.png and b/toxygen/smileys/default/D83CDFAE.png differ
diff --git a/toxygen/smileys/default/D83CDFAF.png b/toxygen/smileys/default/D83CDFAF.png
index 3ba2c9c..b8aa1e1 100644
Binary files a/toxygen/smileys/default/D83CDFAF.png and b/toxygen/smileys/default/D83CDFAF.png differ
diff --git a/toxygen/smileys/default/D83CDFB0.png b/toxygen/smileys/default/D83CDFB0.png
index f6ac7a2..a3c36cf 100644
Binary files a/toxygen/smileys/default/D83CDFB0.png and b/toxygen/smileys/default/D83CDFB0.png differ
diff --git a/toxygen/smileys/default/D83CDFB1.png b/toxygen/smileys/default/D83CDFB1.png
index 5d28833..efb17ad 100644
Binary files a/toxygen/smileys/default/D83CDFB1.png and b/toxygen/smileys/default/D83CDFB1.png differ
diff --git a/toxygen/smileys/default/D83CDFB2.png b/toxygen/smileys/default/D83CDFB2.png
index dc90beb..8fc6c03 100644
Binary files a/toxygen/smileys/default/D83CDFB2.png and b/toxygen/smileys/default/D83CDFB2.png differ
diff --git a/toxygen/smileys/default/D83CDFB3.png b/toxygen/smileys/default/D83CDFB3.png
index 953c7d9..c7459fa 100644
Binary files a/toxygen/smileys/default/D83CDFB3.png and b/toxygen/smileys/default/D83CDFB3.png differ
diff --git a/toxygen/smileys/default/D83CDFB4.png b/toxygen/smileys/default/D83CDFB4.png
index 6f383e7..2090a8d 100644
Binary files a/toxygen/smileys/default/D83CDFB4.png and b/toxygen/smileys/default/D83CDFB4.png differ
diff --git a/toxygen/smileys/default/D83CDFB5.png b/toxygen/smileys/default/D83CDFB5.png
index 4024eeb..e9c0683 100644
Binary files a/toxygen/smileys/default/D83CDFB5.png and b/toxygen/smileys/default/D83CDFB5.png differ
diff --git a/toxygen/smileys/default/D83CDFB6.png b/toxygen/smileys/default/D83CDFB6.png
index 685a06f..956bc4d 100644
Binary files a/toxygen/smileys/default/D83CDFB6.png and b/toxygen/smileys/default/D83CDFB6.png differ
diff --git a/toxygen/smileys/default/D83CDFB7.png b/toxygen/smileys/default/D83CDFB7.png
index 421da92..4fde005 100644
Binary files a/toxygen/smileys/default/D83CDFB7.png and b/toxygen/smileys/default/D83CDFB7.png differ
diff --git a/toxygen/smileys/default/D83CDFB8.png b/toxygen/smileys/default/D83CDFB8.png
index 649899d..584ba69 100644
Binary files a/toxygen/smileys/default/D83CDFB8.png and b/toxygen/smileys/default/D83CDFB8.png differ
diff --git a/toxygen/smileys/default/D83CDFB9.png b/toxygen/smileys/default/D83CDFB9.png
index ecb2a80..748a587 100644
Binary files a/toxygen/smileys/default/D83CDFB9.png and b/toxygen/smileys/default/D83CDFB9.png differ
diff --git a/toxygen/smileys/default/D83CDFBA.png b/toxygen/smileys/default/D83CDFBA.png
index e19dd82..77ca90e 100644
Binary files a/toxygen/smileys/default/D83CDFBA.png and b/toxygen/smileys/default/D83CDFBA.png differ
diff --git a/toxygen/smileys/default/D83CDFBB.png b/toxygen/smileys/default/D83CDFBB.png
index 5b59ae9..0f1b9a7 100644
Binary files a/toxygen/smileys/default/D83CDFBB.png and b/toxygen/smileys/default/D83CDFBB.png differ
diff --git a/toxygen/smileys/default/D83CDFBC.png b/toxygen/smileys/default/D83CDFBC.png
index 12bbebd..0441b72 100644
Binary files a/toxygen/smileys/default/D83CDFBC.png and b/toxygen/smileys/default/D83CDFBC.png differ
diff --git a/toxygen/smileys/default/D83CDFBD.png b/toxygen/smileys/default/D83CDFBD.png
index 724799d..5d2beac 100644
Binary files a/toxygen/smileys/default/D83CDFBD.png and b/toxygen/smileys/default/D83CDFBD.png differ
diff --git a/toxygen/smileys/default/D83CDFBE.png b/toxygen/smileys/default/D83CDFBE.png
index 24ae90a..96e8605 100644
Binary files a/toxygen/smileys/default/D83CDFBE.png and b/toxygen/smileys/default/D83CDFBE.png differ
diff --git a/toxygen/smileys/default/D83CDFBF.png b/toxygen/smileys/default/D83CDFBF.png
index 8891637..79f2da3 100644
Binary files a/toxygen/smileys/default/D83CDFBF.png and b/toxygen/smileys/default/D83CDFBF.png differ
diff --git a/toxygen/smileys/default/D83CDFC0.png b/toxygen/smileys/default/D83CDFC0.png
index a877641..8bf69e2 100644
Binary files a/toxygen/smileys/default/D83CDFC0.png and b/toxygen/smileys/default/D83CDFC0.png differ
diff --git a/toxygen/smileys/default/D83CDFC1.png b/toxygen/smileys/default/D83CDFC1.png
index f0f5e29..be1a59b 100644
Binary files a/toxygen/smileys/default/D83CDFC1.png and b/toxygen/smileys/default/D83CDFC1.png differ
diff --git a/toxygen/smileys/default/D83CDFC2.png b/toxygen/smileys/default/D83CDFC2.png
index 9e35a6e..9bdd6b4 100644
Binary files a/toxygen/smileys/default/D83CDFC2.png and b/toxygen/smileys/default/D83CDFC2.png differ
diff --git a/toxygen/smileys/default/D83CDFC3.png b/toxygen/smileys/default/D83CDFC3.png
index f4721f1..d8c9cf3 100644
Binary files a/toxygen/smileys/default/D83CDFC3.png and b/toxygen/smileys/default/D83CDFC3.png differ
diff --git a/toxygen/smileys/default/D83CDFC4.png b/toxygen/smileys/default/D83CDFC4.png
index 19b88bb..dbe988e 100644
Binary files a/toxygen/smileys/default/D83CDFC4.png and b/toxygen/smileys/default/D83CDFC4.png differ
diff --git a/toxygen/smileys/default/D83CDFC6.png b/toxygen/smileys/default/D83CDFC6.png
index 7ede172..ea79487 100644
Binary files a/toxygen/smileys/default/D83CDFC6.png and b/toxygen/smileys/default/D83CDFC6.png differ
diff --git a/toxygen/smileys/default/D83CDFC7.png b/toxygen/smileys/default/D83CDFC7.png
index 4579ae2..1309322 100644
Binary files a/toxygen/smileys/default/D83CDFC7.png and b/toxygen/smileys/default/D83CDFC7.png differ
diff --git a/toxygen/smileys/default/D83CDFC8.png b/toxygen/smileys/default/D83CDFC8.png
index d804bf5..4540605 100644
Binary files a/toxygen/smileys/default/D83CDFC8.png and b/toxygen/smileys/default/D83CDFC8.png differ
diff --git a/toxygen/smileys/default/D83CDFC9.png b/toxygen/smileys/default/D83CDFC9.png
index fca8fbf..f4048b8 100644
Binary files a/toxygen/smileys/default/D83CDFC9.png and b/toxygen/smileys/default/D83CDFC9.png differ
diff --git a/toxygen/smileys/default/D83CDFCA.png b/toxygen/smileys/default/D83CDFCA.png
index 4fdb629..45aa9fb 100644
Binary files a/toxygen/smileys/default/D83CDFCA.png and b/toxygen/smileys/default/D83CDFCA.png differ
diff --git a/toxygen/smileys/default/D83CDFE0.png b/toxygen/smileys/default/D83CDFE0.png
index 2324908..2fcd7b3 100644
Binary files a/toxygen/smileys/default/D83CDFE0.png and b/toxygen/smileys/default/D83CDFE0.png differ
diff --git a/toxygen/smileys/default/D83CDFE1.png b/toxygen/smileys/default/D83CDFE1.png
index 197c598..18ae9e6 100644
Binary files a/toxygen/smileys/default/D83CDFE1.png and b/toxygen/smileys/default/D83CDFE1.png differ
diff --git a/toxygen/smileys/default/D83CDFE2.png b/toxygen/smileys/default/D83CDFE2.png
index 43f5c16..4c73dcb 100644
Binary files a/toxygen/smileys/default/D83CDFE2.png and b/toxygen/smileys/default/D83CDFE2.png differ
diff --git a/toxygen/smileys/default/D83CDFE3.png b/toxygen/smileys/default/D83CDFE3.png
index 8214077..dafcbb0 100644
Binary files a/toxygen/smileys/default/D83CDFE3.png and b/toxygen/smileys/default/D83CDFE3.png differ
diff --git a/toxygen/smileys/default/D83CDFE4.png b/toxygen/smileys/default/D83CDFE4.png
index 28cec25..32ec6cc 100644
Binary files a/toxygen/smileys/default/D83CDFE4.png and b/toxygen/smileys/default/D83CDFE4.png differ
diff --git a/toxygen/smileys/default/D83CDFE5.png b/toxygen/smileys/default/D83CDFE5.png
index a048c9c..05da811 100644
Binary files a/toxygen/smileys/default/D83CDFE5.png and b/toxygen/smileys/default/D83CDFE5.png differ
diff --git a/toxygen/smileys/default/D83CDFE7.png b/toxygen/smileys/default/D83CDFE7.png
index 612e467..4527782 100644
Binary files a/toxygen/smileys/default/D83CDFE7.png and b/toxygen/smileys/default/D83CDFE7.png differ
diff --git a/toxygen/smileys/default/D83CDFE8.png b/toxygen/smileys/default/D83CDFE8.png
index d6ef1f5..a8586ee 100644
Binary files a/toxygen/smileys/default/D83CDFE8.png and b/toxygen/smileys/default/D83CDFE8.png differ
diff --git a/toxygen/smileys/default/D83CDFE9.png b/toxygen/smileys/default/D83CDFE9.png
index 55f1ea2..54bc6d1 100644
Binary files a/toxygen/smileys/default/D83CDFE9.png and b/toxygen/smileys/default/D83CDFE9.png differ
diff --git a/toxygen/smileys/default/D83CDFEA.png b/toxygen/smileys/default/D83CDFEA.png
index dceb8d4..4060f51 100644
Binary files a/toxygen/smileys/default/D83CDFEA.png and b/toxygen/smileys/default/D83CDFEA.png differ
diff --git a/toxygen/smileys/default/D83CDFEB.png b/toxygen/smileys/default/D83CDFEB.png
index e20e33f..0b2ec51 100644
Binary files a/toxygen/smileys/default/D83CDFEB.png and b/toxygen/smileys/default/D83CDFEB.png differ
diff --git a/toxygen/smileys/default/D83CDFEC.png b/toxygen/smileys/default/D83CDFEC.png
index a2e4eb8..7b0d510 100644
Binary files a/toxygen/smileys/default/D83CDFEC.png and b/toxygen/smileys/default/D83CDFEC.png differ
diff --git a/toxygen/smileys/default/D83CDFED.png b/toxygen/smileys/default/D83CDFED.png
index 8172224..0ae5367 100644
Binary files a/toxygen/smileys/default/D83CDFED.png and b/toxygen/smileys/default/D83CDFED.png differ
diff --git a/toxygen/smileys/default/D83CDFEE.png b/toxygen/smileys/default/D83CDFEE.png
index 5885b27..55982b6 100644
Binary files a/toxygen/smileys/default/D83CDFEE.png and b/toxygen/smileys/default/D83CDFEE.png differ
diff --git a/toxygen/smileys/default/D83CDFEF.png b/toxygen/smileys/default/D83CDFEF.png
index 22f4662..1e446c8 100644
Binary files a/toxygen/smileys/default/D83CDFEF.png and b/toxygen/smileys/default/D83CDFEF.png differ
diff --git a/toxygen/smileys/default/D83CDFF0.png b/toxygen/smileys/default/D83CDFF0.png
index 9eccbe5..0db16f2 100644
Binary files a/toxygen/smileys/default/D83CDFF0.png and b/toxygen/smileys/default/D83CDFF0.png differ
diff --git a/toxygen/smileys/default/D83DDC00.png b/toxygen/smileys/default/D83DDC00.png
index 7ced002..f7982a4 100644
Binary files a/toxygen/smileys/default/D83DDC00.png and b/toxygen/smileys/default/D83DDC00.png differ
diff --git a/toxygen/smileys/default/D83DDC01.png b/toxygen/smileys/default/D83DDC01.png
index 0a276c7..6d16b88 100644
Binary files a/toxygen/smileys/default/D83DDC01.png and b/toxygen/smileys/default/D83DDC01.png differ
diff --git a/toxygen/smileys/default/D83DDC02.png b/toxygen/smileys/default/D83DDC02.png
index c59ce81..4ec1cce 100644
Binary files a/toxygen/smileys/default/D83DDC02.png and b/toxygen/smileys/default/D83DDC02.png differ
diff --git a/toxygen/smileys/default/D83DDC03.png b/toxygen/smileys/default/D83DDC03.png
index e9eb0d1..df9e284 100644
Binary files a/toxygen/smileys/default/D83DDC03.png and b/toxygen/smileys/default/D83DDC03.png differ
diff --git a/toxygen/smileys/default/D83DDC04.png b/toxygen/smileys/default/D83DDC04.png
index 8f72f4f..2d50aa0 100644
Binary files a/toxygen/smileys/default/D83DDC04.png and b/toxygen/smileys/default/D83DDC04.png differ
diff --git a/toxygen/smileys/default/D83DDC05.png b/toxygen/smileys/default/D83DDC05.png
index 5fcfd9e..94cb5f0 100644
Binary files a/toxygen/smileys/default/D83DDC05.png and b/toxygen/smileys/default/D83DDC05.png differ
diff --git a/toxygen/smileys/default/D83DDC06.png b/toxygen/smileys/default/D83DDC06.png
index df6bdd1..bb771a6 100644
Binary files a/toxygen/smileys/default/D83DDC06.png and b/toxygen/smileys/default/D83DDC06.png differ
diff --git a/toxygen/smileys/default/D83DDC07.png b/toxygen/smileys/default/D83DDC07.png
index 69a3c74..53b5530 100644
Binary files a/toxygen/smileys/default/D83DDC07.png and b/toxygen/smileys/default/D83DDC07.png differ
diff --git a/toxygen/smileys/default/D83DDC08.png b/toxygen/smileys/default/D83DDC08.png
index 2c239f2..17991b3 100644
Binary files a/toxygen/smileys/default/D83DDC08.png and b/toxygen/smileys/default/D83DDC08.png differ
diff --git a/toxygen/smileys/default/D83DDC09.png b/toxygen/smileys/default/D83DDC09.png
index 77e3895..6ce569d 100644
Binary files a/toxygen/smileys/default/D83DDC09.png and b/toxygen/smileys/default/D83DDC09.png differ
diff --git a/toxygen/smileys/default/D83DDC0A.png b/toxygen/smileys/default/D83DDC0A.png
index bb83653..a8e76cb 100644
Binary files a/toxygen/smileys/default/D83DDC0A.png and b/toxygen/smileys/default/D83DDC0A.png differ
diff --git a/toxygen/smileys/default/D83DDC0B.png b/toxygen/smileys/default/D83DDC0B.png
index 878e117..9cc5171 100644
Binary files a/toxygen/smileys/default/D83DDC0B.png and b/toxygen/smileys/default/D83DDC0B.png differ
diff --git a/toxygen/smileys/default/D83DDC0C.png b/toxygen/smileys/default/D83DDC0C.png
index 700a0dc..0d36155 100644
Binary files a/toxygen/smileys/default/D83DDC0C.png and b/toxygen/smileys/default/D83DDC0C.png differ
diff --git a/toxygen/smileys/default/D83DDC0D.png b/toxygen/smileys/default/D83DDC0D.png
index 411c781..6b38170 100644
Binary files a/toxygen/smileys/default/D83DDC0D.png and b/toxygen/smileys/default/D83DDC0D.png differ
diff --git a/toxygen/smileys/default/D83DDC0E.png b/toxygen/smileys/default/D83DDC0E.png
index b5774b4..9080dd0 100644
Binary files a/toxygen/smileys/default/D83DDC0E.png and b/toxygen/smileys/default/D83DDC0E.png differ
diff --git a/toxygen/smileys/default/D83DDC0F.png b/toxygen/smileys/default/D83DDC0F.png
index 69c405b..e74447a 100644
Binary files a/toxygen/smileys/default/D83DDC0F.png and b/toxygen/smileys/default/D83DDC0F.png differ
diff --git a/toxygen/smileys/default/D83DDC10.png b/toxygen/smileys/default/D83DDC10.png
index 871bcad..070c460 100644
Binary files a/toxygen/smileys/default/D83DDC10.png and b/toxygen/smileys/default/D83DDC10.png differ
diff --git a/toxygen/smileys/default/D83DDC11.png b/toxygen/smileys/default/D83DDC11.png
index 7f92df6..6f143a6 100644
Binary files a/toxygen/smileys/default/D83DDC11.png and b/toxygen/smileys/default/D83DDC11.png differ
diff --git a/toxygen/smileys/default/D83DDC12.png b/toxygen/smileys/default/D83DDC12.png
index 30c9e4f..a584b4a 100644
Binary files a/toxygen/smileys/default/D83DDC12.png and b/toxygen/smileys/default/D83DDC12.png differ
diff --git a/toxygen/smileys/default/D83DDC13.png b/toxygen/smileys/default/D83DDC13.png
index db6531a..ed3c077 100644
Binary files a/toxygen/smileys/default/D83DDC13.png and b/toxygen/smileys/default/D83DDC13.png differ
diff --git a/toxygen/smileys/default/D83DDC14.png b/toxygen/smileys/default/D83DDC14.png
index 7af6dd1..2e92ba2 100644
Binary files a/toxygen/smileys/default/D83DDC14.png and b/toxygen/smileys/default/D83DDC14.png differ
diff --git a/toxygen/smileys/default/D83DDC15.png b/toxygen/smileys/default/D83DDC15.png
index ee2f83f..d9fc622 100644
Binary files a/toxygen/smileys/default/D83DDC15.png and b/toxygen/smileys/default/D83DDC15.png differ
diff --git a/toxygen/smileys/default/D83DDC16.png b/toxygen/smileys/default/D83DDC16.png
index a1c3d5d..c321277 100644
Binary files a/toxygen/smileys/default/D83DDC16.png and b/toxygen/smileys/default/D83DDC16.png differ
diff --git a/toxygen/smileys/default/D83DDC17.png b/toxygen/smileys/default/D83DDC17.png
index 42f14de..0043f3c 100644
Binary files a/toxygen/smileys/default/D83DDC17.png and b/toxygen/smileys/default/D83DDC17.png differ
diff --git a/toxygen/smileys/default/D83DDC18.png b/toxygen/smileys/default/D83DDC18.png
index 27f35b6..8a93ce9 100644
Binary files a/toxygen/smileys/default/D83DDC18.png and b/toxygen/smileys/default/D83DDC18.png differ
diff --git a/toxygen/smileys/default/D83DDC19.png b/toxygen/smileys/default/D83DDC19.png
index da588a4..ac19c2d 100644
Binary files a/toxygen/smileys/default/D83DDC19.png and b/toxygen/smileys/default/D83DDC19.png differ
diff --git a/toxygen/smileys/default/D83DDC1A.png b/toxygen/smileys/default/D83DDC1A.png
index ae8a07b..635ccfa 100644
Binary files a/toxygen/smileys/default/D83DDC1A.png and b/toxygen/smileys/default/D83DDC1A.png differ
diff --git a/toxygen/smileys/default/D83DDC1B.png b/toxygen/smileys/default/D83DDC1B.png
index 412d5fe..dccb76e 100644
Binary files a/toxygen/smileys/default/D83DDC1B.png and b/toxygen/smileys/default/D83DDC1B.png differ
diff --git a/toxygen/smileys/default/D83DDC1C.png b/toxygen/smileys/default/D83DDC1C.png
index fd285ed..73d740e 100644
Binary files a/toxygen/smileys/default/D83DDC1C.png and b/toxygen/smileys/default/D83DDC1C.png differ
diff --git a/toxygen/smileys/default/D83DDC1D.png b/toxygen/smileys/default/D83DDC1D.png
index c74c7a7..1b49267 100644
Binary files a/toxygen/smileys/default/D83DDC1D.png and b/toxygen/smileys/default/D83DDC1D.png differ
diff --git a/toxygen/smileys/default/D83DDC1E.png b/toxygen/smileys/default/D83DDC1E.png
index 4a47598..d66de86 100644
Binary files a/toxygen/smileys/default/D83DDC1E.png and b/toxygen/smileys/default/D83DDC1E.png differ
diff --git a/toxygen/smileys/default/D83DDC1F.png b/toxygen/smileys/default/D83DDC1F.png
index ca88daf..52f30a8 100644
Binary files a/toxygen/smileys/default/D83DDC1F.png and b/toxygen/smileys/default/D83DDC1F.png differ
diff --git a/toxygen/smileys/default/D83DDC20.png b/toxygen/smileys/default/D83DDC20.png
index 465e11a..2b1e644 100644
Binary files a/toxygen/smileys/default/D83DDC20.png and b/toxygen/smileys/default/D83DDC20.png differ
diff --git a/toxygen/smileys/default/D83DDC21.png b/toxygen/smileys/default/D83DDC21.png
index 475564a..279dc2e 100644
Binary files a/toxygen/smileys/default/D83DDC21.png and b/toxygen/smileys/default/D83DDC21.png differ
diff --git a/toxygen/smileys/default/D83DDC22.png b/toxygen/smileys/default/D83DDC22.png
index 9db8e98..2314d9f 100644
Binary files a/toxygen/smileys/default/D83DDC22.png and b/toxygen/smileys/default/D83DDC22.png differ
diff --git a/toxygen/smileys/default/D83DDC23.png b/toxygen/smileys/default/D83DDC23.png
index 12b00cd..7a6f8d5 100644
Binary files a/toxygen/smileys/default/D83DDC23.png and b/toxygen/smileys/default/D83DDC23.png differ
diff --git a/toxygen/smileys/default/D83DDC24.png b/toxygen/smileys/default/D83DDC24.png
index 37ff954..480fcf1 100644
Binary files a/toxygen/smileys/default/D83DDC24.png and b/toxygen/smileys/default/D83DDC24.png differ
diff --git a/toxygen/smileys/default/D83DDC25.png b/toxygen/smileys/default/D83DDC25.png
index f72eb69..6e05fed 100644
Binary files a/toxygen/smileys/default/D83DDC25.png and b/toxygen/smileys/default/D83DDC25.png differ
diff --git a/toxygen/smileys/default/D83DDC26.png b/toxygen/smileys/default/D83DDC26.png
index 033be84..e53f643 100644
Binary files a/toxygen/smileys/default/D83DDC26.png and b/toxygen/smileys/default/D83DDC26.png differ
diff --git a/toxygen/smileys/default/D83DDC27.png b/toxygen/smileys/default/D83DDC27.png
index f48cc2f..779766c 100644
Binary files a/toxygen/smileys/default/D83DDC27.png and b/toxygen/smileys/default/D83DDC27.png differ
diff --git a/toxygen/smileys/default/D83DDC28.png b/toxygen/smileys/default/D83DDC28.png
index 4a113e2..cb6821c 100644
Binary files a/toxygen/smileys/default/D83DDC28.png and b/toxygen/smileys/default/D83DDC28.png differ
diff --git a/toxygen/smileys/default/D83DDC2A.png b/toxygen/smileys/default/D83DDC2A.png
index c3c5aef..a44d2e1 100644
Binary files a/toxygen/smileys/default/D83DDC2A.png and b/toxygen/smileys/default/D83DDC2A.png differ
diff --git a/toxygen/smileys/default/D83DDC2B.png b/toxygen/smileys/default/D83DDC2B.png
index 0f40d31..f09e1a3 100644
Binary files a/toxygen/smileys/default/D83DDC2B.png and b/toxygen/smileys/default/D83DDC2B.png differ
diff --git a/toxygen/smileys/default/D83DDC2C.png b/toxygen/smileys/default/D83DDC2C.png
index d107ff6..2c855eb 100644
Binary files a/toxygen/smileys/default/D83DDC2C.png and b/toxygen/smileys/default/D83DDC2C.png differ
diff --git a/toxygen/smileys/default/D83DDC2D.png b/toxygen/smileys/default/D83DDC2D.png
index d8b4f90..ff2e49a 100644
Binary files a/toxygen/smileys/default/D83DDC2D.png and b/toxygen/smileys/default/D83DDC2D.png differ
diff --git a/toxygen/smileys/default/D83DDC2E.png b/toxygen/smileys/default/D83DDC2E.png
index cd05541..f95c3b9 100644
Binary files a/toxygen/smileys/default/D83DDC2E.png and b/toxygen/smileys/default/D83DDC2E.png differ
diff --git a/toxygen/smileys/default/D83DDC2F.png b/toxygen/smileys/default/D83DDC2F.png
index 086a5b6..3598329 100644
Binary files a/toxygen/smileys/default/D83DDC2F.png and b/toxygen/smileys/default/D83DDC2F.png differ
diff --git a/toxygen/smileys/default/D83DDC30.png b/toxygen/smileys/default/D83DDC30.png
index e926a23..3249366 100644
Binary files a/toxygen/smileys/default/D83DDC30.png and b/toxygen/smileys/default/D83DDC30.png differ
diff --git a/toxygen/smileys/default/D83DDC31.png b/toxygen/smileys/default/D83DDC31.png
index c250baf..5a410e3 100644
Binary files a/toxygen/smileys/default/D83DDC31.png and b/toxygen/smileys/default/D83DDC31.png differ
diff --git a/toxygen/smileys/default/D83DDC32.png b/toxygen/smileys/default/D83DDC32.png
index a2f991b..0857137 100644
Binary files a/toxygen/smileys/default/D83DDC32.png and b/toxygen/smileys/default/D83DDC32.png differ
diff --git a/toxygen/smileys/default/D83DDC33.png b/toxygen/smileys/default/D83DDC33.png
index aacba60..6f025da 100644
Binary files a/toxygen/smileys/default/D83DDC33.png and b/toxygen/smileys/default/D83DDC33.png differ
diff --git a/toxygen/smileys/default/D83DDC34.png b/toxygen/smileys/default/D83DDC34.png
index a0b1b67..0be777d 100644
Binary files a/toxygen/smileys/default/D83DDC34.png and b/toxygen/smileys/default/D83DDC34.png differ
diff --git a/toxygen/smileys/default/D83DDC35.png b/toxygen/smileys/default/D83DDC35.png
index 4873b38..5ccdc02 100644
Binary files a/toxygen/smileys/default/D83DDC35.png and b/toxygen/smileys/default/D83DDC35.png differ
diff --git a/toxygen/smileys/default/D83DDC36.png b/toxygen/smileys/default/D83DDC36.png
index e4bb3d7..50ff6c0 100644
Binary files a/toxygen/smileys/default/D83DDC36.png and b/toxygen/smileys/default/D83DDC36.png differ
diff --git a/toxygen/smileys/default/D83DDC37.png b/toxygen/smileys/default/D83DDC37.png
index 4d6fad6..78afd2c 100644
Binary files a/toxygen/smileys/default/D83DDC37.png and b/toxygen/smileys/default/D83DDC37.png differ
diff --git a/toxygen/smileys/default/D83DDC38.png b/toxygen/smileys/default/D83DDC38.png
index d646948..2141d1b 100644
Binary files a/toxygen/smileys/default/D83DDC38.png and b/toxygen/smileys/default/D83DDC38.png differ
diff --git a/toxygen/smileys/default/D83DDC39.png b/toxygen/smileys/default/D83DDC39.png
index cd2027c..775d857 100644
Binary files a/toxygen/smileys/default/D83DDC39.png and b/toxygen/smileys/default/D83DDC39.png differ
diff --git a/toxygen/smileys/default/D83DDC3A.png b/toxygen/smileys/default/D83DDC3A.png
index 7dcd9f7..a2bbc5b 100644
Binary files a/toxygen/smileys/default/D83DDC3A.png and b/toxygen/smileys/default/D83DDC3A.png differ
diff --git a/toxygen/smileys/default/D83DDC3B.png b/toxygen/smileys/default/D83DDC3B.png
index f9d2a6b..6a91df8 100644
Binary files a/toxygen/smileys/default/D83DDC3B.png and b/toxygen/smileys/default/D83DDC3B.png differ
diff --git a/toxygen/smileys/default/D83DDC3C.png b/toxygen/smileys/default/D83DDC3C.png
index c18d728..58ecebc 100644
Binary files a/toxygen/smileys/default/D83DDC3C.png and b/toxygen/smileys/default/D83DDC3C.png differ
diff --git a/toxygen/smileys/default/D83DDC3D.png b/toxygen/smileys/default/D83DDC3D.png
index 0044223..3863e97 100644
Binary files a/toxygen/smileys/default/D83DDC3D.png and b/toxygen/smileys/default/D83DDC3D.png differ
diff --git a/toxygen/smileys/default/D83DDC3E.png b/toxygen/smileys/default/D83DDC3E.png
index 5f8023e..288939d 100644
Binary files a/toxygen/smileys/default/D83DDC3E.png and b/toxygen/smileys/default/D83DDC3E.png differ
diff --git a/toxygen/smileys/default/D83DDC40.png b/toxygen/smileys/default/D83DDC40.png
index 4c4ede3..3a43419 100644
Binary files a/toxygen/smileys/default/D83DDC40.png and b/toxygen/smileys/default/D83DDC40.png differ
diff --git a/toxygen/smileys/default/D83DDC42.png b/toxygen/smileys/default/D83DDC42.png
index 990bff9..baeb7b1 100644
Binary files a/toxygen/smileys/default/D83DDC42.png and b/toxygen/smileys/default/D83DDC42.png differ
diff --git a/toxygen/smileys/default/D83DDC43.png b/toxygen/smileys/default/D83DDC43.png
index 72b0103..71af1e2 100644
Binary files a/toxygen/smileys/default/D83DDC43.png and b/toxygen/smileys/default/D83DDC43.png differ
diff --git a/toxygen/smileys/default/D83DDC44.png b/toxygen/smileys/default/D83DDC44.png
index 627f204..fd7a6e2 100644
Binary files a/toxygen/smileys/default/D83DDC44.png and b/toxygen/smileys/default/D83DDC44.png differ
diff --git a/toxygen/smileys/default/D83DDC45.png b/toxygen/smileys/default/D83DDC45.png
index 63ec09e..75aec77 100644
Binary files a/toxygen/smileys/default/D83DDC45.png and b/toxygen/smileys/default/D83DDC45.png differ
diff --git a/toxygen/smileys/default/D83DDC46.png b/toxygen/smileys/default/D83DDC46.png
index ff52801..d881fdb 100644
Binary files a/toxygen/smileys/default/D83DDC46.png and b/toxygen/smileys/default/D83DDC46.png differ
diff --git a/toxygen/smileys/default/D83DDC47.png b/toxygen/smileys/default/D83DDC47.png
index 3fc0730..029274e 100644
Binary files a/toxygen/smileys/default/D83DDC47.png and b/toxygen/smileys/default/D83DDC47.png differ
diff --git a/toxygen/smileys/default/D83DDC48.png b/toxygen/smileys/default/D83DDC48.png
index c961655..7a68d8c 100644
Binary files a/toxygen/smileys/default/D83DDC48.png and b/toxygen/smileys/default/D83DDC48.png differ
diff --git a/toxygen/smileys/default/D83DDC49.png b/toxygen/smileys/default/D83DDC49.png
index 06ce893..db52a0d 100644
Binary files a/toxygen/smileys/default/D83DDC49.png and b/toxygen/smileys/default/D83DDC49.png differ
diff --git a/toxygen/smileys/default/D83DDC4A.png b/toxygen/smileys/default/D83DDC4A.png
index a4f5a83..0026ab1 100644
Binary files a/toxygen/smileys/default/D83DDC4A.png and b/toxygen/smileys/default/D83DDC4A.png differ
diff --git a/toxygen/smileys/default/D83DDC4B.png b/toxygen/smileys/default/D83DDC4B.png
index 66590dd..87d5bfe 100644
Binary files a/toxygen/smileys/default/D83DDC4B.png and b/toxygen/smileys/default/D83DDC4B.png differ
diff --git a/toxygen/smileys/default/D83DDC4C.png b/toxygen/smileys/default/D83DDC4C.png
index 5445e2f..60b7abc 100644
Binary files a/toxygen/smileys/default/D83DDC4C.png and b/toxygen/smileys/default/D83DDC4C.png differ
diff --git a/toxygen/smileys/default/D83DDC4D.png b/toxygen/smileys/default/D83DDC4D.png
index 3a8e512..2f816aa 100644
Binary files a/toxygen/smileys/default/D83DDC4D.png and b/toxygen/smileys/default/D83DDC4D.png differ
diff --git a/toxygen/smileys/default/D83DDC4E.png b/toxygen/smileys/default/D83DDC4E.png
index 79dec71..7773282 100644
Binary files a/toxygen/smileys/default/D83DDC4E.png and b/toxygen/smileys/default/D83DDC4E.png differ
diff --git a/toxygen/smileys/default/D83DDC4F.png b/toxygen/smileys/default/D83DDC4F.png
index f511857..45a633a 100644
Binary files a/toxygen/smileys/default/D83DDC4F.png and b/toxygen/smileys/default/D83DDC4F.png differ
diff --git a/toxygen/smileys/default/D83DDC50.png b/toxygen/smileys/default/D83DDC50.png
index 8809893..da64391 100644
Binary files a/toxygen/smileys/default/D83DDC50.png and b/toxygen/smileys/default/D83DDC50.png differ
diff --git a/toxygen/smileys/default/D83DDC51.png b/toxygen/smileys/default/D83DDC51.png
index d05d576..0eeaeec 100644
Binary files a/toxygen/smileys/default/D83DDC51.png and b/toxygen/smileys/default/D83DDC51.png differ
diff --git a/toxygen/smileys/default/D83DDC52.png b/toxygen/smileys/default/D83DDC52.png
index 4f4cc0f..897a330 100644
Binary files a/toxygen/smileys/default/D83DDC52.png and b/toxygen/smileys/default/D83DDC52.png differ
diff --git a/toxygen/smileys/default/D83DDC53.png b/toxygen/smileys/default/D83DDC53.png
index 3a691f0..5b9b401 100644
Binary files a/toxygen/smileys/default/D83DDC53.png and b/toxygen/smileys/default/D83DDC53.png differ
diff --git a/toxygen/smileys/default/D83DDC54.png b/toxygen/smileys/default/D83DDC54.png
index b095f9c..fa76d90 100644
Binary files a/toxygen/smileys/default/D83DDC54.png and b/toxygen/smileys/default/D83DDC54.png differ
diff --git a/toxygen/smileys/default/D83DDC55.png b/toxygen/smileys/default/D83DDC55.png
index 84a5d62..23d1ebc 100644
Binary files a/toxygen/smileys/default/D83DDC55.png and b/toxygen/smileys/default/D83DDC55.png differ
diff --git a/toxygen/smileys/default/D83DDC56.png b/toxygen/smileys/default/D83DDC56.png
index 6e6cdf4..3d3656b 100644
Binary files a/toxygen/smileys/default/D83DDC56.png and b/toxygen/smileys/default/D83DDC56.png differ
diff --git a/toxygen/smileys/default/D83DDC57.png b/toxygen/smileys/default/D83DDC57.png
index a795c98..14a9774 100644
Binary files a/toxygen/smileys/default/D83DDC57.png and b/toxygen/smileys/default/D83DDC57.png differ
diff --git a/toxygen/smileys/default/D83DDC58.png b/toxygen/smileys/default/D83DDC58.png
index 9f02a38..553cc6e 100644
Binary files a/toxygen/smileys/default/D83DDC58.png and b/toxygen/smileys/default/D83DDC58.png differ
diff --git a/toxygen/smileys/default/D83DDC59.png b/toxygen/smileys/default/D83DDC59.png
index 8ebca9a..4d2cfde 100644
Binary files a/toxygen/smileys/default/D83DDC59.png and b/toxygen/smileys/default/D83DDC59.png differ
diff --git a/toxygen/smileys/default/D83DDC5A.png b/toxygen/smileys/default/D83DDC5A.png
index b065c3b..f72b865 100644
Binary files a/toxygen/smileys/default/D83DDC5A.png and b/toxygen/smileys/default/D83DDC5A.png differ
diff --git a/toxygen/smileys/default/D83DDC5B.png b/toxygen/smileys/default/D83DDC5B.png
index 4fb1977..c5ea2dd 100644
Binary files a/toxygen/smileys/default/D83DDC5B.png and b/toxygen/smileys/default/D83DDC5B.png differ
diff --git a/toxygen/smileys/default/D83DDC5C.png b/toxygen/smileys/default/D83DDC5C.png
index 2dc62a9..4fad011 100644
Binary files a/toxygen/smileys/default/D83DDC5C.png and b/toxygen/smileys/default/D83DDC5C.png differ
diff --git a/toxygen/smileys/default/D83DDC5D.png b/toxygen/smileys/default/D83DDC5D.png
index ad2c7e2..ab72e00 100644
Binary files a/toxygen/smileys/default/D83DDC5D.png and b/toxygen/smileys/default/D83DDC5D.png differ
diff --git a/toxygen/smileys/default/D83DDC5E.png b/toxygen/smileys/default/D83DDC5E.png
index 260ffaf..a3cf22a 100644
Binary files a/toxygen/smileys/default/D83DDC5E.png and b/toxygen/smileys/default/D83DDC5E.png differ
diff --git a/toxygen/smileys/default/D83DDC5F.png b/toxygen/smileys/default/D83DDC5F.png
index 246033a..36f592b 100644
Binary files a/toxygen/smileys/default/D83DDC5F.png and b/toxygen/smileys/default/D83DDC5F.png differ
diff --git a/toxygen/smileys/default/D83DDC60.png b/toxygen/smileys/default/D83DDC60.png
index 069b0ba..03325c8 100644
Binary files a/toxygen/smileys/default/D83DDC60.png and b/toxygen/smileys/default/D83DDC60.png differ
diff --git a/toxygen/smileys/default/D83DDC61.png b/toxygen/smileys/default/D83DDC61.png
index c55056b..e565a42 100644
Binary files a/toxygen/smileys/default/D83DDC61.png and b/toxygen/smileys/default/D83DDC61.png differ
diff --git a/toxygen/smileys/default/D83DDC62.png b/toxygen/smileys/default/D83DDC62.png
index c024df0..445320f 100644
Binary files a/toxygen/smileys/default/D83DDC62.png and b/toxygen/smileys/default/D83DDC62.png differ
diff --git a/toxygen/smileys/default/D83DDC63.png b/toxygen/smileys/default/D83DDC63.png
index b9a69c7..171c4c6 100644
Binary files a/toxygen/smileys/default/D83DDC63.png and b/toxygen/smileys/default/D83DDC63.png differ
diff --git a/toxygen/smileys/default/D83DDC64.png b/toxygen/smileys/default/D83DDC64.png
index 8661c68..ebd2d98 100644
Binary files a/toxygen/smileys/default/D83DDC64.png and b/toxygen/smileys/default/D83DDC64.png differ
diff --git a/toxygen/smileys/default/D83DDC65.png b/toxygen/smileys/default/D83DDC65.png
index 90bc937..67d500e 100644
Binary files a/toxygen/smileys/default/D83DDC65.png and b/toxygen/smileys/default/D83DDC65.png differ
diff --git a/toxygen/smileys/default/D83DDC66.png b/toxygen/smileys/default/D83DDC66.png
index ae329b3..00b77bc 100644
Binary files a/toxygen/smileys/default/D83DDC66.png and b/toxygen/smileys/default/D83DDC66.png differ
diff --git a/toxygen/smileys/default/D83DDC67.png b/toxygen/smileys/default/D83DDC67.png
index 8d73e2a..162941f 100644
Binary files a/toxygen/smileys/default/D83DDC67.png and b/toxygen/smileys/default/D83DDC67.png differ
diff --git a/toxygen/smileys/default/D83DDC68.png b/toxygen/smileys/default/D83DDC68.png
index 1ae9332..37dfd2a 100644
Binary files a/toxygen/smileys/default/D83DDC68.png and b/toxygen/smileys/default/D83DDC68.png differ
diff --git a/toxygen/smileys/default/D83DDC69.png b/toxygen/smileys/default/D83DDC69.png
index 983e540..176ad8f 100644
Binary files a/toxygen/smileys/default/D83DDC69.png and b/toxygen/smileys/default/D83DDC69.png differ
diff --git a/toxygen/smileys/default/D83DDC6A.png b/toxygen/smileys/default/D83DDC6A.png
index 711c23f..1009ac8 100644
Binary files a/toxygen/smileys/default/D83DDC6A.png and b/toxygen/smileys/default/D83DDC6A.png differ
diff --git a/toxygen/smileys/default/D83DDC6B.png b/toxygen/smileys/default/D83DDC6B.png
index 11e9b49..be243b4 100644
Binary files a/toxygen/smileys/default/D83DDC6B.png and b/toxygen/smileys/default/D83DDC6B.png differ
diff --git a/toxygen/smileys/default/D83DDC6C.png b/toxygen/smileys/default/D83DDC6C.png
index 4fa7870..9a262f1 100644
Binary files a/toxygen/smileys/default/D83DDC6C.png and b/toxygen/smileys/default/D83DDC6C.png differ
diff --git a/toxygen/smileys/default/D83DDC6D.png b/toxygen/smileys/default/D83DDC6D.png
index caf627a..217da23 100644
Binary files a/toxygen/smileys/default/D83DDC6D.png and b/toxygen/smileys/default/D83DDC6D.png differ
diff --git a/toxygen/smileys/default/D83DDC6E.png b/toxygen/smileys/default/D83DDC6E.png
index b321f21..f389f63 100644
Binary files a/toxygen/smileys/default/D83DDC6E.png and b/toxygen/smileys/default/D83DDC6E.png differ
diff --git a/toxygen/smileys/default/D83DDC6F.png b/toxygen/smileys/default/D83DDC6F.png
index 9575084..6d1645b 100644
Binary files a/toxygen/smileys/default/D83DDC6F.png and b/toxygen/smileys/default/D83DDC6F.png differ
diff --git a/toxygen/smileys/default/D83DDC70.png b/toxygen/smileys/default/D83DDC70.png
index 2125032..1311170 100644
Binary files a/toxygen/smileys/default/D83DDC70.png and b/toxygen/smileys/default/D83DDC70.png differ
diff --git a/toxygen/smileys/default/D83DDC71.png b/toxygen/smileys/default/D83DDC71.png
index 79394f1..ca207b0 100644
Binary files a/toxygen/smileys/default/D83DDC71.png and b/toxygen/smileys/default/D83DDC71.png differ
diff --git a/toxygen/smileys/default/D83DDC72.png b/toxygen/smileys/default/D83DDC72.png
index 23686f7..86dc325 100644
Binary files a/toxygen/smileys/default/D83DDC72.png and b/toxygen/smileys/default/D83DDC72.png differ
diff --git a/toxygen/smileys/default/D83DDC73.png b/toxygen/smileys/default/D83DDC73.png
index 8d81068..c5aada5 100644
Binary files a/toxygen/smileys/default/D83DDC73.png and b/toxygen/smileys/default/D83DDC73.png differ
diff --git a/toxygen/smileys/default/D83DDC74.png b/toxygen/smileys/default/D83DDC74.png
index b3de20e..e007082 100644
Binary files a/toxygen/smileys/default/D83DDC74.png and b/toxygen/smileys/default/D83DDC74.png differ
diff --git a/toxygen/smileys/default/D83DDC75.png b/toxygen/smileys/default/D83DDC75.png
index 78898d7..1c70b19 100644
Binary files a/toxygen/smileys/default/D83DDC75.png and b/toxygen/smileys/default/D83DDC75.png differ
diff --git a/toxygen/smileys/default/D83DDC76.png b/toxygen/smileys/default/D83DDC76.png
index 72f543c..3b23af4 100644
Binary files a/toxygen/smileys/default/D83DDC76.png and b/toxygen/smileys/default/D83DDC76.png differ
diff --git a/toxygen/smileys/default/D83DDC77.png b/toxygen/smileys/default/D83DDC77.png
index e1b062e..65e3966 100644
Binary files a/toxygen/smileys/default/D83DDC77.png and b/toxygen/smileys/default/D83DDC77.png differ
diff --git a/toxygen/smileys/default/D83DDC78.png b/toxygen/smileys/default/D83DDC78.png
index 622e525..ccd0a43 100644
Binary files a/toxygen/smileys/default/D83DDC78.png and b/toxygen/smileys/default/D83DDC78.png differ
diff --git a/toxygen/smileys/default/D83DDC79.png b/toxygen/smileys/default/D83DDC79.png
index b5b0ea2..5b3e009 100644
Binary files a/toxygen/smileys/default/D83DDC79.png and b/toxygen/smileys/default/D83DDC79.png differ
diff --git a/toxygen/smileys/default/D83DDC7A.png b/toxygen/smileys/default/D83DDC7A.png
index ac66041..e649501 100644
Binary files a/toxygen/smileys/default/D83DDC7A.png and b/toxygen/smileys/default/D83DDC7A.png differ
diff --git a/toxygen/smileys/default/D83DDC7B.png b/toxygen/smileys/default/D83DDC7B.png
index f1f17be..abc5fe2 100644
Binary files a/toxygen/smileys/default/D83DDC7B.png and b/toxygen/smileys/default/D83DDC7B.png differ
diff --git a/toxygen/smileys/default/D83DDC7C.png b/toxygen/smileys/default/D83DDC7C.png
index 71683c9..4dec37d 100644
Binary files a/toxygen/smileys/default/D83DDC7C.png and b/toxygen/smileys/default/D83DDC7C.png differ
diff --git a/toxygen/smileys/default/D83DDC7D.png b/toxygen/smileys/default/D83DDC7D.png
index 6c4630f..57db9bb 100644
Binary files a/toxygen/smileys/default/D83DDC7D.png and b/toxygen/smileys/default/D83DDC7D.png differ
diff --git a/toxygen/smileys/default/D83DDC7E.png b/toxygen/smileys/default/D83DDC7E.png
index 8151fb6..854cae3 100644
Binary files a/toxygen/smileys/default/D83DDC7E.png and b/toxygen/smileys/default/D83DDC7E.png differ
diff --git a/toxygen/smileys/default/D83DDC7F.png b/toxygen/smileys/default/D83DDC7F.png
index 47ae002..6283942 100644
Binary files a/toxygen/smileys/default/D83DDC7F.png and b/toxygen/smileys/default/D83DDC7F.png differ
diff --git a/toxygen/smileys/default/D83DDC80.png b/toxygen/smileys/default/D83DDC80.png
index 5dbecd7..73f61d9 100644
Binary files a/toxygen/smileys/default/D83DDC80.png and b/toxygen/smileys/default/D83DDC80.png differ
diff --git a/toxygen/smileys/default/D83DDC81.png b/toxygen/smileys/default/D83DDC81.png
index f8a8ea5..ec18497 100644
Binary files a/toxygen/smileys/default/D83DDC81.png and b/toxygen/smileys/default/D83DDC81.png differ
diff --git a/toxygen/smileys/default/D83DDC82.png b/toxygen/smileys/default/D83DDC82.png
index 94dcdec..4591862 100644
Binary files a/toxygen/smileys/default/D83DDC82.png and b/toxygen/smileys/default/D83DDC82.png differ
diff --git a/toxygen/smileys/default/D83DDC83.png b/toxygen/smileys/default/D83DDC83.png
index 4294502..cae7c04 100644
Binary files a/toxygen/smileys/default/D83DDC83.png and b/toxygen/smileys/default/D83DDC83.png differ
diff --git a/toxygen/smileys/default/D83DDC84.png b/toxygen/smileys/default/D83DDC84.png
index a4c6036..514f9b0 100644
Binary files a/toxygen/smileys/default/D83DDC84.png and b/toxygen/smileys/default/D83DDC84.png differ
diff --git a/toxygen/smileys/default/D83DDC85.png b/toxygen/smileys/default/D83DDC85.png
index 504a06e..9d85f43 100644
Binary files a/toxygen/smileys/default/D83DDC85.png and b/toxygen/smileys/default/D83DDC85.png differ
diff --git a/toxygen/smileys/default/D83DDC86.png b/toxygen/smileys/default/D83DDC86.png
index ebdd6ab..45b22d1 100644
Binary files a/toxygen/smileys/default/D83DDC86.png and b/toxygen/smileys/default/D83DDC86.png differ
diff --git a/toxygen/smileys/default/D83DDC87.png b/toxygen/smileys/default/D83DDC87.png
index 2b05cff..aa8ac45 100644
Binary files a/toxygen/smileys/default/D83DDC87.png and b/toxygen/smileys/default/D83DDC87.png differ
diff --git a/toxygen/smileys/default/D83DDC88.png b/toxygen/smileys/default/D83DDC88.png
index cf3e845..0491543 100644
Binary files a/toxygen/smileys/default/D83DDC88.png and b/toxygen/smileys/default/D83DDC88.png differ
diff --git a/toxygen/smileys/default/D83DDC89.png b/toxygen/smileys/default/D83DDC89.png
index ba2b624..c2151a2 100644
Binary files a/toxygen/smileys/default/D83DDC89.png and b/toxygen/smileys/default/D83DDC89.png differ
diff --git a/toxygen/smileys/default/D83DDC8A.png b/toxygen/smileys/default/D83DDC8A.png
index 950a9fb..1ee7330 100644
Binary files a/toxygen/smileys/default/D83DDC8A.png and b/toxygen/smileys/default/D83DDC8A.png differ
diff --git a/toxygen/smileys/default/D83DDC8B.png b/toxygen/smileys/default/D83DDC8B.png
index 620a4e5..c2ae15e 100644
Binary files a/toxygen/smileys/default/D83DDC8B.png and b/toxygen/smileys/default/D83DDC8B.png differ
diff --git a/toxygen/smileys/default/D83DDC8C.png b/toxygen/smileys/default/D83DDC8C.png
index ed94152..9a0a3eb 100644
Binary files a/toxygen/smileys/default/D83DDC8C.png and b/toxygen/smileys/default/D83DDC8C.png differ
diff --git a/toxygen/smileys/default/D83DDC8D.png b/toxygen/smileys/default/D83DDC8D.png
index 1c90ba0..cde47d9 100644
Binary files a/toxygen/smileys/default/D83DDC8D.png and b/toxygen/smileys/default/D83DDC8D.png differ
diff --git a/toxygen/smileys/default/D83DDC8E.png b/toxygen/smileys/default/D83DDC8E.png
index 379a76d..d17d19c 100644
Binary files a/toxygen/smileys/default/D83DDC8E.png and b/toxygen/smileys/default/D83DDC8E.png differ
diff --git a/toxygen/smileys/default/D83DDC8F.png b/toxygen/smileys/default/D83DDC8F.png
index 8837f68..af81a7f 100644
Binary files a/toxygen/smileys/default/D83DDC8F.png and b/toxygen/smileys/default/D83DDC8F.png differ
diff --git a/toxygen/smileys/default/D83DDC90.png b/toxygen/smileys/default/D83DDC90.png
index 2ee1054..41d16a3 100644
Binary files a/toxygen/smileys/default/D83DDC90.png and b/toxygen/smileys/default/D83DDC90.png differ
diff --git a/toxygen/smileys/default/D83DDC91.png b/toxygen/smileys/default/D83DDC91.png
index e8638cc..2654b92 100644
Binary files a/toxygen/smileys/default/D83DDC91.png and b/toxygen/smileys/default/D83DDC91.png differ
diff --git a/toxygen/smileys/default/D83DDC92.png b/toxygen/smileys/default/D83DDC92.png
index 621b28b..9146473 100644
Binary files a/toxygen/smileys/default/D83DDC92.png and b/toxygen/smileys/default/D83DDC92.png differ
diff --git a/toxygen/smileys/default/D83DDC93.png b/toxygen/smileys/default/D83DDC93.png
index b6443f8..cf1b001 100644
Binary files a/toxygen/smileys/default/D83DDC93.png and b/toxygen/smileys/default/D83DDC93.png differ
diff --git a/toxygen/smileys/default/D83DDC94.png b/toxygen/smileys/default/D83DDC94.png
index b2c521a..17a5bd9 100644
Binary files a/toxygen/smileys/default/D83DDC94.png and b/toxygen/smileys/default/D83DDC94.png differ
diff --git a/toxygen/smileys/default/D83DDC95.png b/toxygen/smileys/default/D83DDC95.png
index bef3d7c..8757fb1 100644
Binary files a/toxygen/smileys/default/D83DDC95.png and b/toxygen/smileys/default/D83DDC95.png differ
diff --git a/toxygen/smileys/default/D83DDC96.png b/toxygen/smileys/default/D83DDC96.png
index 161fd16..1dda2ee 100644
Binary files a/toxygen/smileys/default/D83DDC96.png and b/toxygen/smileys/default/D83DDC96.png differ
diff --git a/toxygen/smileys/default/D83DDC97.png b/toxygen/smileys/default/D83DDC97.png
index aaa9839..7baa800 100644
Binary files a/toxygen/smileys/default/D83DDC97.png and b/toxygen/smileys/default/D83DDC97.png differ
diff --git a/toxygen/smileys/default/D83DDC98.png b/toxygen/smileys/default/D83DDC98.png
index 1459cdd..2cee5aa 100644
Binary files a/toxygen/smileys/default/D83DDC98.png and b/toxygen/smileys/default/D83DDC98.png differ
diff --git a/toxygen/smileys/default/D83DDC99.png b/toxygen/smileys/default/D83DDC99.png
index dc7c449..e9ab2c1 100644
Binary files a/toxygen/smileys/default/D83DDC99.png and b/toxygen/smileys/default/D83DDC99.png differ
diff --git a/toxygen/smileys/default/D83DDC9A.png b/toxygen/smileys/default/D83DDC9A.png
index 1100ab0..9f94d53 100644
Binary files a/toxygen/smileys/default/D83DDC9A.png and b/toxygen/smileys/default/D83DDC9A.png differ
diff --git a/toxygen/smileys/default/D83DDC9B.png b/toxygen/smileys/default/D83DDC9B.png
index ce1b877..77174a5 100644
Binary files a/toxygen/smileys/default/D83DDC9B.png and b/toxygen/smileys/default/D83DDC9B.png differ
diff --git a/toxygen/smileys/default/D83DDC9C.png b/toxygen/smileys/default/D83DDC9C.png
index 0d9d147..207d7d3 100644
Binary files a/toxygen/smileys/default/D83DDC9C.png and b/toxygen/smileys/default/D83DDC9C.png differ
diff --git a/toxygen/smileys/default/D83DDC9D.png b/toxygen/smileys/default/D83DDC9D.png
index 148421e..908575c 100644
Binary files a/toxygen/smileys/default/D83DDC9D.png and b/toxygen/smileys/default/D83DDC9D.png differ
diff --git a/toxygen/smileys/default/D83DDC9E.png b/toxygen/smileys/default/D83DDC9E.png
index 030ceb5..d0d1292 100644
Binary files a/toxygen/smileys/default/D83DDC9E.png and b/toxygen/smileys/default/D83DDC9E.png differ
diff --git a/toxygen/smileys/default/D83DDC9F.png b/toxygen/smileys/default/D83DDC9F.png
index 11d897a..c4d1c4e 100644
Binary files a/toxygen/smileys/default/D83DDC9F.png and b/toxygen/smileys/default/D83DDC9F.png differ
diff --git a/toxygen/smileys/default/D83DDCA0.png b/toxygen/smileys/default/D83DDCA0.png
index f596e31..fc2c29f 100644
Binary files a/toxygen/smileys/default/D83DDCA0.png and b/toxygen/smileys/default/D83DDCA0.png differ
diff --git a/toxygen/smileys/default/D83DDCA1.png b/toxygen/smileys/default/D83DDCA1.png
index d2cb0f2..57a5d7f 100644
Binary files a/toxygen/smileys/default/D83DDCA1.png and b/toxygen/smileys/default/D83DDCA1.png differ
diff --git a/toxygen/smileys/default/D83DDCA2.png b/toxygen/smileys/default/D83DDCA2.png
index e232809..cff291f 100644
Binary files a/toxygen/smileys/default/D83DDCA2.png and b/toxygen/smileys/default/D83DDCA2.png differ
diff --git a/toxygen/smileys/default/D83DDCA3.png b/toxygen/smileys/default/D83DDCA3.png
index 2480754..2b943e9 100644
Binary files a/toxygen/smileys/default/D83DDCA3.png and b/toxygen/smileys/default/D83DDCA3.png differ
diff --git a/toxygen/smileys/default/D83DDCA4.png b/toxygen/smileys/default/D83DDCA4.png
index 04fa05f..d25ffff 100644
Binary files a/toxygen/smileys/default/D83DDCA4.png and b/toxygen/smileys/default/D83DDCA4.png differ
diff --git a/toxygen/smileys/default/D83DDCA5.png b/toxygen/smileys/default/D83DDCA5.png
index 7fbed7d..4db5a0e 100644
Binary files a/toxygen/smileys/default/D83DDCA5.png and b/toxygen/smileys/default/D83DDCA5.png differ
diff --git a/toxygen/smileys/default/D83DDCA6.png b/toxygen/smileys/default/D83DDCA6.png
index d4b4dde..758ce6d 100644
Binary files a/toxygen/smileys/default/D83DDCA6.png and b/toxygen/smileys/default/D83DDCA6.png differ
diff --git a/toxygen/smileys/default/D83DDCA7.png b/toxygen/smileys/default/D83DDCA7.png
index 1602702..74c1d2b 100644
Binary files a/toxygen/smileys/default/D83DDCA7.png and b/toxygen/smileys/default/D83DDCA7.png differ
diff --git a/toxygen/smileys/default/D83DDCA8.png b/toxygen/smileys/default/D83DDCA8.png
index c1d6de3..f8039e1 100644
Binary files a/toxygen/smileys/default/D83DDCA8.png and b/toxygen/smileys/default/D83DDCA8.png differ
diff --git a/toxygen/smileys/default/D83DDCA9.png b/toxygen/smileys/default/D83DDCA9.png
index e1a4e26..a86877f 100644
Binary files a/toxygen/smileys/default/D83DDCA9.png and b/toxygen/smileys/default/D83DDCA9.png differ
diff --git a/toxygen/smileys/default/D83DDCAA.png b/toxygen/smileys/default/D83DDCAA.png
index 50a329b..5a1e68d 100644
Binary files a/toxygen/smileys/default/D83DDCAA.png and b/toxygen/smileys/default/D83DDCAA.png differ
diff --git a/toxygen/smileys/default/D83DDCAB.png b/toxygen/smileys/default/D83DDCAB.png
index ab3e93e..999a667 100644
Binary files a/toxygen/smileys/default/D83DDCAB.png and b/toxygen/smileys/default/D83DDCAB.png differ
diff --git a/toxygen/smileys/default/D83DDCAC.png b/toxygen/smileys/default/D83DDCAC.png
index d9d38ca..effcbbe 100644
Binary files a/toxygen/smileys/default/D83DDCAC.png and b/toxygen/smileys/default/D83DDCAC.png differ
diff --git a/toxygen/smileys/default/D83DDCAD.png b/toxygen/smileys/default/D83DDCAD.png
index 9f2dbd6..f23fd2b 100644
Binary files a/toxygen/smileys/default/D83DDCAD.png and b/toxygen/smileys/default/D83DDCAD.png differ
diff --git a/toxygen/smileys/default/D83DDCAE.png b/toxygen/smileys/default/D83DDCAE.png
index f28bd12..b9af846 100644
Binary files a/toxygen/smileys/default/D83DDCAE.png and b/toxygen/smileys/default/D83DDCAE.png differ
diff --git a/toxygen/smileys/default/D83DDCAF.png b/toxygen/smileys/default/D83DDCAF.png
index 2a48074..5fb8824 100644
Binary files a/toxygen/smileys/default/D83DDCAF.png and b/toxygen/smileys/default/D83DDCAF.png differ
diff --git a/toxygen/smileys/default/D83DDCB0.png b/toxygen/smileys/default/D83DDCB0.png
index 6fe6259..4b7d9ad 100644
Binary files a/toxygen/smileys/default/D83DDCB0.png and b/toxygen/smileys/default/D83DDCB0.png differ
diff --git a/toxygen/smileys/default/D83DDCB1.png b/toxygen/smileys/default/D83DDCB1.png
index ae95bc2..fea9346 100644
Binary files a/toxygen/smileys/default/D83DDCB1.png and b/toxygen/smileys/default/D83DDCB1.png differ
diff --git a/toxygen/smileys/default/D83DDCB2.png b/toxygen/smileys/default/D83DDCB2.png
index 6b2c85a..4e83e77 100644
Binary files a/toxygen/smileys/default/D83DDCB2.png and b/toxygen/smileys/default/D83DDCB2.png differ
diff --git a/toxygen/smileys/default/D83DDCB3.png b/toxygen/smileys/default/D83DDCB3.png
index 6976b53..6141cec 100644
Binary files a/toxygen/smileys/default/D83DDCB3.png and b/toxygen/smileys/default/D83DDCB3.png differ
diff --git a/toxygen/smileys/default/D83DDCB4.png b/toxygen/smileys/default/D83DDCB4.png
index 7d94640..9f6bda2 100644
Binary files a/toxygen/smileys/default/D83DDCB4.png and b/toxygen/smileys/default/D83DDCB4.png differ
diff --git a/toxygen/smileys/default/D83DDCB5.png b/toxygen/smileys/default/D83DDCB5.png
index 92f8caf..d27fb53 100644
Binary files a/toxygen/smileys/default/D83DDCB5.png and b/toxygen/smileys/default/D83DDCB5.png differ
diff --git a/toxygen/smileys/default/D83DDCB6.png b/toxygen/smileys/default/D83DDCB6.png
index d47427b..b4d6405 100644
Binary files a/toxygen/smileys/default/D83DDCB6.png and b/toxygen/smileys/default/D83DDCB6.png differ
diff --git a/toxygen/smileys/default/D83DDCB7.png b/toxygen/smileys/default/D83DDCB7.png
index 1e7679c..e1f5526 100644
Binary files a/toxygen/smileys/default/D83DDCB7.png and b/toxygen/smileys/default/D83DDCB7.png differ
diff --git a/toxygen/smileys/default/D83DDCB8.png b/toxygen/smileys/default/D83DDCB8.png
index 10b4518..20240f8 100644
Binary files a/toxygen/smileys/default/D83DDCB8.png and b/toxygen/smileys/default/D83DDCB8.png differ
diff --git a/toxygen/smileys/default/D83DDCB9.png b/toxygen/smileys/default/D83DDCB9.png
index 63ea27d..ba319c9 100644
Binary files a/toxygen/smileys/default/D83DDCB9.png and b/toxygen/smileys/default/D83DDCB9.png differ
diff --git a/toxygen/smileys/default/D83DDCBA.png b/toxygen/smileys/default/D83DDCBA.png
index a3ba77d..4a9e280 100644
Binary files a/toxygen/smileys/default/D83DDCBA.png and b/toxygen/smileys/default/D83DDCBA.png differ
diff --git a/toxygen/smileys/default/D83DDCBB.png b/toxygen/smileys/default/D83DDCBB.png
index feb6e40..d4f6546 100644
Binary files a/toxygen/smileys/default/D83DDCBB.png and b/toxygen/smileys/default/D83DDCBB.png differ
diff --git a/toxygen/smileys/default/D83DDCBC.png b/toxygen/smileys/default/D83DDCBC.png
index ec6ce62..4f7011c 100644
Binary files a/toxygen/smileys/default/D83DDCBC.png and b/toxygen/smileys/default/D83DDCBC.png differ
diff --git a/toxygen/smileys/default/D83DDCBD.png b/toxygen/smileys/default/D83DDCBD.png
index 11ba64a..d2e416e 100644
Binary files a/toxygen/smileys/default/D83DDCBD.png and b/toxygen/smileys/default/D83DDCBD.png differ
diff --git a/toxygen/smileys/default/D83DDCBE.png b/toxygen/smileys/default/D83DDCBE.png
index 6137dff..de1a1c0 100644
Binary files a/toxygen/smileys/default/D83DDCBE.png and b/toxygen/smileys/default/D83DDCBE.png differ
diff --git a/toxygen/smileys/default/D83DDCBF.png b/toxygen/smileys/default/D83DDCBF.png
index d50b58b..38c906b 100644
Binary files a/toxygen/smileys/default/D83DDCBF.png and b/toxygen/smileys/default/D83DDCBF.png differ
diff --git a/toxygen/smileys/default/D83DDCC0.png b/toxygen/smileys/default/D83DDCC0.png
index 5a76a4c..da3cd5d 100644
Binary files a/toxygen/smileys/default/D83DDCC0.png and b/toxygen/smileys/default/D83DDCC0.png differ
diff --git a/toxygen/smileys/default/D83DDCC1.png b/toxygen/smileys/default/D83DDCC1.png
index 29f32f3..f37868d 100644
Binary files a/toxygen/smileys/default/D83DDCC1.png and b/toxygen/smileys/default/D83DDCC1.png differ
diff --git a/toxygen/smileys/default/D83DDCC2.png b/toxygen/smileys/default/D83DDCC2.png
index aae823b..4b727dd 100644
Binary files a/toxygen/smileys/default/D83DDCC2.png and b/toxygen/smileys/default/D83DDCC2.png differ
diff --git a/toxygen/smileys/default/D83DDCC3.png b/toxygen/smileys/default/D83DDCC3.png
index 902cf6b..08f5dc1 100644
Binary files a/toxygen/smileys/default/D83DDCC3.png and b/toxygen/smileys/default/D83DDCC3.png differ
diff --git a/toxygen/smileys/default/D83DDCC4.png b/toxygen/smileys/default/D83DDCC4.png
index ef8e394..33665a1 100644
Binary files a/toxygen/smileys/default/D83DDCC4.png and b/toxygen/smileys/default/D83DDCC4.png differ
diff --git a/toxygen/smileys/default/D83DDCC5.png b/toxygen/smileys/default/D83DDCC5.png
index 63cca6c..b4c0e8c 100644
Binary files a/toxygen/smileys/default/D83DDCC5.png and b/toxygen/smileys/default/D83DDCC5.png differ
diff --git a/toxygen/smileys/default/D83DDCC6.png b/toxygen/smileys/default/D83DDCC6.png
index 56da2d6..698aabb 100644
Binary files a/toxygen/smileys/default/D83DDCC6.png and b/toxygen/smileys/default/D83DDCC6.png differ
diff --git a/toxygen/smileys/default/D83DDCC7.png b/toxygen/smileys/default/D83DDCC7.png
index f9519fd..e1b35a1 100644
Binary files a/toxygen/smileys/default/D83DDCC7.png and b/toxygen/smileys/default/D83DDCC7.png differ
diff --git a/toxygen/smileys/default/D83DDCC8.png b/toxygen/smileys/default/D83DDCC8.png
index 22100cb..ddaa706 100644
Binary files a/toxygen/smileys/default/D83DDCC8.png and b/toxygen/smileys/default/D83DDCC8.png differ
diff --git a/toxygen/smileys/default/D83DDCC9.png b/toxygen/smileys/default/D83DDCC9.png
index ff5eca4..7b956c6 100644
Binary files a/toxygen/smileys/default/D83DDCC9.png and b/toxygen/smileys/default/D83DDCC9.png differ
diff --git a/toxygen/smileys/default/D83DDCCA.png b/toxygen/smileys/default/D83DDCCA.png
index d67cb31..4778f38 100644
Binary files a/toxygen/smileys/default/D83DDCCA.png and b/toxygen/smileys/default/D83DDCCA.png differ
diff --git a/toxygen/smileys/default/D83DDCCB.png b/toxygen/smileys/default/D83DDCCB.png
index ee94954..2d0720d 100644
Binary files a/toxygen/smileys/default/D83DDCCB.png and b/toxygen/smileys/default/D83DDCCB.png differ
diff --git a/toxygen/smileys/default/D83DDCCC.png b/toxygen/smileys/default/D83DDCCC.png
index c880d4b..9735eca 100644
Binary files a/toxygen/smileys/default/D83DDCCC.png and b/toxygen/smileys/default/D83DDCCC.png differ
diff --git a/toxygen/smileys/default/D83DDCCD.png b/toxygen/smileys/default/D83DDCCD.png
index 918bf6a..f50854a 100644
Binary files a/toxygen/smileys/default/D83DDCCD.png and b/toxygen/smileys/default/D83DDCCD.png differ
diff --git a/toxygen/smileys/default/D83DDCCE.png b/toxygen/smileys/default/D83DDCCE.png
index 7fd4ef8..ce86e8b 100644
Binary files a/toxygen/smileys/default/D83DDCCE.png and b/toxygen/smileys/default/D83DDCCE.png differ
diff --git a/toxygen/smileys/default/D83DDCCF.png b/toxygen/smileys/default/D83DDCCF.png
index 62159a8..8aa5e8f 100644
Binary files a/toxygen/smileys/default/D83DDCCF.png and b/toxygen/smileys/default/D83DDCCF.png differ
diff --git a/toxygen/smileys/default/D83DDCD0.png b/toxygen/smileys/default/D83DDCD0.png
index 21d5db7..f637998 100644
Binary files a/toxygen/smileys/default/D83DDCD0.png and b/toxygen/smileys/default/D83DDCD0.png differ
diff --git a/toxygen/smileys/default/D83DDCD1.png b/toxygen/smileys/default/D83DDCD1.png
index 5b4e246..c0a4b77 100644
Binary files a/toxygen/smileys/default/D83DDCD1.png and b/toxygen/smileys/default/D83DDCD1.png differ
diff --git a/toxygen/smileys/default/D83DDCD2.png b/toxygen/smileys/default/D83DDCD2.png
index 9f5585b..400cf7b 100644
Binary files a/toxygen/smileys/default/D83DDCD2.png and b/toxygen/smileys/default/D83DDCD2.png differ
diff --git a/toxygen/smileys/default/D83DDCD3.png b/toxygen/smileys/default/D83DDCD3.png
index d045646..930e01f 100644
Binary files a/toxygen/smileys/default/D83DDCD3.png and b/toxygen/smileys/default/D83DDCD3.png differ
diff --git a/toxygen/smileys/default/D83DDCD4.png b/toxygen/smileys/default/D83DDCD4.png
index 3f988be..b26265e 100644
Binary files a/toxygen/smileys/default/D83DDCD4.png and b/toxygen/smileys/default/D83DDCD4.png differ
diff --git a/toxygen/smileys/default/D83DDCD5.png b/toxygen/smileys/default/D83DDCD5.png
index 5da1fd4..06d3364 100644
Binary files a/toxygen/smileys/default/D83DDCD5.png and b/toxygen/smileys/default/D83DDCD5.png differ
diff --git a/toxygen/smileys/default/D83DDCD6.png b/toxygen/smileys/default/D83DDCD6.png
index 9d187a4..be0ef9c 100644
Binary files a/toxygen/smileys/default/D83DDCD6.png and b/toxygen/smileys/default/D83DDCD6.png differ
diff --git a/toxygen/smileys/default/D83DDCD7.png b/toxygen/smileys/default/D83DDCD7.png
index 7546b7c..1b3f7b7 100644
Binary files a/toxygen/smileys/default/D83DDCD7.png and b/toxygen/smileys/default/D83DDCD7.png differ
diff --git a/toxygen/smileys/default/D83DDCD8.png b/toxygen/smileys/default/D83DDCD8.png
index bec3da5..7cb1ac9 100644
Binary files a/toxygen/smileys/default/D83DDCD8.png and b/toxygen/smileys/default/D83DDCD8.png differ
diff --git a/toxygen/smileys/default/D83DDCD9.png b/toxygen/smileys/default/D83DDCD9.png
index 7004cc8..ecf7d46 100644
Binary files a/toxygen/smileys/default/D83DDCD9.png and b/toxygen/smileys/default/D83DDCD9.png differ
diff --git a/toxygen/smileys/default/D83DDCDA.png b/toxygen/smileys/default/D83DDCDA.png
index 66f8c39..2ebfaf0 100644
Binary files a/toxygen/smileys/default/D83DDCDA.png and b/toxygen/smileys/default/D83DDCDA.png differ
diff --git a/toxygen/smileys/default/D83DDCDB.png b/toxygen/smileys/default/D83DDCDB.png
index 4445168..36a9b0f 100644
Binary files a/toxygen/smileys/default/D83DDCDB.png and b/toxygen/smileys/default/D83DDCDB.png differ
diff --git a/toxygen/smileys/default/D83DDCDC.png b/toxygen/smileys/default/D83DDCDC.png
index 64d1bfb..056647b 100644
Binary files a/toxygen/smileys/default/D83DDCDC.png and b/toxygen/smileys/default/D83DDCDC.png differ
diff --git a/toxygen/smileys/default/D83DDCDD.png b/toxygen/smileys/default/D83DDCDD.png
index 418fd8c..35e9942 100644
Binary files a/toxygen/smileys/default/D83DDCDD.png and b/toxygen/smileys/default/D83DDCDD.png differ
diff --git a/toxygen/smileys/default/D83DDCDE.png b/toxygen/smileys/default/D83DDCDE.png
index a38c396..20ba9ba 100644
Binary files a/toxygen/smileys/default/D83DDCDE.png and b/toxygen/smileys/default/D83DDCDE.png differ
diff --git a/toxygen/smileys/default/D83DDCDF.png b/toxygen/smileys/default/D83DDCDF.png
index 4d557ff..8d932d2 100644
Binary files a/toxygen/smileys/default/D83DDCDF.png and b/toxygen/smileys/default/D83DDCDF.png differ
diff --git a/toxygen/smileys/default/D83DDCE0.png b/toxygen/smileys/default/D83DDCE0.png
index f3cfa40..781669e 100644
Binary files a/toxygen/smileys/default/D83DDCE0.png and b/toxygen/smileys/default/D83DDCE0.png differ
diff --git a/toxygen/smileys/default/D83DDCE1.png b/toxygen/smileys/default/D83DDCE1.png
index b690973..c2a3bc9 100644
Binary files a/toxygen/smileys/default/D83DDCE1.png and b/toxygen/smileys/default/D83DDCE1.png differ
diff --git a/toxygen/smileys/default/D83DDCE2.png b/toxygen/smileys/default/D83DDCE2.png
index 0ff4ad0..4c3be3e 100644
Binary files a/toxygen/smileys/default/D83DDCE2.png and b/toxygen/smileys/default/D83DDCE2.png differ
diff --git a/toxygen/smileys/default/D83DDCE3.png b/toxygen/smileys/default/D83DDCE3.png
index aa3537c..5847867 100644
Binary files a/toxygen/smileys/default/D83DDCE3.png and b/toxygen/smileys/default/D83DDCE3.png differ
diff --git a/toxygen/smileys/default/D83DDCE4.png b/toxygen/smileys/default/D83DDCE4.png
index b54ab57..0e6254d 100644
Binary files a/toxygen/smileys/default/D83DDCE4.png and b/toxygen/smileys/default/D83DDCE4.png differ
diff --git a/toxygen/smileys/default/D83DDCE5.png b/toxygen/smileys/default/D83DDCE5.png
index 3e3c172..6a731d1 100644
Binary files a/toxygen/smileys/default/D83DDCE5.png and b/toxygen/smileys/default/D83DDCE5.png differ
diff --git a/toxygen/smileys/default/D83DDCE6.png b/toxygen/smileys/default/D83DDCE6.png
index f087231..4d3f701 100644
Binary files a/toxygen/smileys/default/D83DDCE6.png and b/toxygen/smileys/default/D83DDCE6.png differ
diff --git a/toxygen/smileys/default/D83DDCE7.png b/toxygen/smileys/default/D83DDCE7.png
index 6855487..5bd2454 100644
Binary files a/toxygen/smileys/default/D83DDCE7.png and b/toxygen/smileys/default/D83DDCE7.png differ
diff --git a/toxygen/smileys/default/D83DDCE8.png b/toxygen/smileys/default/D83DDCE8.png
index 8b185e7..446ff97 100644
Binary files a/toxygen/smileys/default/D83DDCE8.png and b/toxygen/smileys/default/D83DDCE8.png differ
diff --git a/toxygen/smileys/default/D83DDCE9.png b/toxygen/smileys/default/D83DDCE9.png
index 329d08e..b7b83f5 100644
Binary files a/toxygen/smileys/default/D83DDCE9.png and b/toxygen/smileys/default/D83DDCE9.png differ
diff --git a/toxygen/smileys/default/D83DDCEA.png b/toxygen/smileys/default/D83DDCEA.png
index 1855bf2..ec474be 100644
Binary files a/toxygen/smileys/default/D83DDCEA.png and b/toxygen/smileys/default/D83DDCEA.png differ
diff --git a/toxygen/smileys/default/D83DDCEB.png b/toxygen/smileys/default/D83DDCEB.png
index 831daf7..4239a5a 100644
Binary files a/toxygen/smileys/default/D83DDCEB.png and b/toxygen/smileys/default/D83DDCEB.png differ
diff --git a/toxygen/smileys/default/D83DDCEC.png b/toxygen/smileys/default/D83DDCEC.png
index 8b04b2d..4289c26 100644
Binary files a/toxygen/smileys/default/D83DDCEC.png and b/toxygen/smileys/default/D83DDCEC.png differ
diff --git a/toxygen/smileys/default/D83DDCED.png b/toxygen/smileys/default/D83DDCED.png
index 0e50de2..2084740 100644
Binary files a/toxygen/smileys/default/D83DDCED.png and b/toxygen/smileys/default/D83DDCED.png differ
diff --git a/toxygen/smileys/default/D83DDCEE.png b/toxygen/smileys/default/D83DDCEE.png
index 7213a4e..e50f686 100644
Binary files a/toxygen/smileys/default/D83DDCEE.png and b/toxygen/smileys/default/D83DDCEE.png differ
diff --git a/toxygen/smileys/default/D83DDCEF.png b/toxygen/smileys/default/D83DDCEF.png
index 370aef4..2e33772 100644
Binary files a/toxygen/smileys/default/D83DDCEF.png and b/toxygen/smileys/default/D83DDCEF.png differ
diff --git a/toxygen/smileys/default/D83DDCF0.png b/toxygen/smileys/default/D83DDCF0.png
index 8d12ebe..016fa96 100644
Binary files a/toxygen/smileys/default/D83DDCF0.png and b/toxygen/smileys/default/D83DDCF0.png differ
diff --git a/toxygen/smileys/default/D83DDCF1.png b/toxygen/smileys/default/D83DDCF1.png
index 3571e61..cc722ad 100644
Binary files a/toxygen/smileys/default/D83DDCF1.png and b/toxygen/smileys/default/D83DDCF1.png differ
diff --git a/toxygen/smileys/default/D83DDCF2.png b/toxygen/smileys/default/D83DDCF2.png
index ab7fc38..c954661 100644
Binary files a/toxygen/smileys/default/D83DDCF2.png and b/toxygen/smileys/default/D83DDCF2.png differ
diff --git a/toxygen/smileys/default/D83DDCF3.png b/toxygen/smileys/default/D83DDCF3.png
index 2fd96ff..687897b 100644
Binary files a/toxygen/smileys/default/D83DDCF3.png and b/toxygen/smileys/default/D83DDCF3.png differ
diff --git a/toxygen/smileys/default/D83DDCF4.png b/toxygen/smileys/default/D83DDCF4.png
index 41aa227..0547aba 100644
Binary files a/toxygen/smileys/default/D83DDCF4.png and b/toxygen/smileys/default/D83DDCF4.png differ
diff --git a/toxygen/smileys/default/D83DDCF5.png b/toxygen/smileys/default/D83DDCF5.png
index 30fd19c..136b78a 100644
Binary files a/toxygen/smileys/default/D83DDCF5.png and b/toxygen/smileys/default/D83DDCF5.png differ
diff --git a/toxygen/smileys/default/D83DDCF6.png b/toxygen/smileys/default/D83DDCF6.png
index c0be3ad..68a63e0 100644
Binary files a/toxygen/smileys/default/D83DDCF6.png and b/toxygen/smileys/default/D83DDCF6.png differ
diff --git a/toxygen/smileys/default/D83DDCF7.png b/toxygen/smileys/default/D83DDCF7.png
index b02f891..d38227e 100644
Binary files a/toxygen/smileys/default/D83DDCF7.png and b/toxygen/smileys/default/D83DDCF7.png differ
diff --git a/toxygen/smileys/default/D83DDCF9.png b/toxygen/smileys/default/D83DDCF9.png
index 3ce2305..6cb3b36 100644
Binary files a/toxygen/smileys/default/D83DDCF9.png and b/toxygen/smileys/default/D83DDCF9.png differ
diff --git a/toxygen/smileys/default/D83DDCFA.png b/toxygen/smileys/default/D83DDCFA.png
index c81b1f6..c282230 100644
Binary files a/toxygen/smileys/default/D83DDCFA.png and b/toxygen/smileys/default/D83DDCFA.png differ
diff --git a/toxygen/smileys/default/D83DDCFB.png b/toxygen/smileys/default/D83DDCFB.png
index 0c1e441..173b13c 100644
Binary files a/toxygen/smileys/default/D83DDCFB.png and b/toxygen/smileys/default/D83DDCFB.png differ
diff --git a/toxygen/smileys/default/D83DDCFC.png b/toxygen/smileys/default/D83DDCFC.png
index d7fcb26..7c71a81 100644
Binary files a/toxygen/smileys/default/D83DDCFC.png and b/toxygen/smileys/default/D83DDCFC.png differ
diff --git a/toxygen/smileys/default/D83DDD00.png b/toxygen/smileys/default/D83DDD00.png
index 08fd865..03465b5 100644
Binary files a/toxygen/smileys/default/D83DDD00.png and b/toxygen/smileys/default/D83DDD00.png differ
diff --git a/toxygen/smileys/default/D83DDD01.png b/toxygen/smileys/default/D83DDD01.png
index 3f8c7bf..bc521ef 100644
Binary files a/toxygen/smileys/default/D83DDD01.png and b/toxygen/smileys/default/D83DDD01.png differ
diff --git a/toxygen/smileys/default/D83DDD02.png b/toxygen/smileys/default/D83DDD02.png
index 373200a..41ac492 100644
Binary files a/toxygen/smileys/default/D83DDD02.png and b/toxygen/smileys/default/D83DDD02.png differ
diff --git a/toxygen/smileys/default/D83DDD03.png b/toxygen/smileys/default/D83DDD03.png
index fc4963b..6f24b20 100644
Binary files a/toxygen/smileys/default/D83DDD03.png and b/toxygen/smileys/default/D83DDD03.png differ
diff --git a/toxygen/smileys/default/D83DDD04.png b/toxygen/smileys/default/D83DDD04.png
index ba2b21f..6255482 100644
Binary files a/toxygen/smileys/default/D83DDD04.png and b/toxygen/smileys/default/D83DDD04.png differ
diff --git a/toxygen/smileys/default/D83DDD05.png b/toxygen/smileys/default/D83DDD05.png
index e6d2462..0fd3e11 100644
Binary files a/toxygen/smileys/default/D83DDD05.png and b/toxygen/smileys/default/D83DDD05.png differ
diff --git a/toxygen/smileys/default/D83DDD06.png b/toxygen/smileys/default/D83DDD06.png
index 771f42a..7df3172 100644
Binary files a/toxygen/smileys/default/D83DDD06.png and b/toxygen/smileys/default/D83DDD06.png differ
diff --git a/toxygen/smileys/default/D83DDD07.png b/toxygen/smileys/default/D83DDD07.png
index cc4fc65..ff3769c 100644
Binary files a/toxygen/smileys/default/D83DDD07.png and b/toxygen/smileys/default/D83DDD07.png differ
diff --git a/toxygen/smileys/default/D83DDD09.png b/toxygen/smileys/default/D83DDD09.png
index 435a7b9..a51efc8 100644
Binary files a/toxygen/smileys/default/D83DDD09.png and b/toxygen/smileys/default/D83DDD09.png differ
diff --git a/toxygen/smileys/default/D83DDD0B.png b/toxygen/smileys/default/D83DDD0B.png
index c637732..fd3e3d2 100644
Binary files a/toxygen/smileys/default/D83DDD0B.png and b/toxygen/smileys/default/D83DDD0B.png differ
diff --git a/toxygen/smileys/default/D83DDD0C.png b/toxygen/smileys/default/D83DDD0C.png
index b0fa88c..0317018 100644
Binary files a/toxygen/smileys/default/D83DDD0C.png and b/toxygen/smileys/default/D83DDD0C.png differ
diff --git a/toxygen/smileys/default/D83DDD0D.png b/toxygen/smileys/default/D83DDD0D.png
index 4f7cb05..2bdf40b 100644
Binary files a/toxygen/smileys/default/D83DDD0D.png and b/toxygen/smileys/default/D83DDD0D.png differ
diff --git a/toxygen/smileys/default/D83DDD0E.png b/toxygen/smileys/default/D83DDD0E.png
index fe0ace1..d9a8b8a 100644
Binary files a/toxygen/smileys/default/D83DDD0E.png and b/toxygen/smileys/default/D83DDD0E.png differ
diff --git a/toxygen/smileys/default/D83DDD0F.png b/toxygen/smileys/default/D83DDD0F.png
index 0b76fe1..3dc1ea0 100644
Binary files a/toxygen/smileys/default/D83DDD0F.png and b/toxygen/smileys/default/D83DDD0F.png differ
diff --git a/toxygen/smileys/default/D83DDD10.png b/toxygen/smileys/default/D83DDD10.png
index e94d395..4210428 100644
Binary files a/toxygen/smileys/default/D83DDD10.png and b/toxygen/smileys/default/D83DDD10.png differ
diff --git a/toxygen/smileys/default/D83DDD11.png b/toxygen/smileys/default/D83DDD11.png
index 37ac8b5..c60bcad 100644
Binary files a/toxygen/smileys/default/D83DDD11.png and b/toxygen/smileys/default/D83DDD11.png differ
diff --git a/toxygen/smileys/default/D83DDD12.png b/toxygen/smileys/default/D83DDD12.png
index 74fb17e..8a680cf 100644
Binary files a/toxygen/smileys/default/D83DDD12.png and b/toxygen/smileys/default/D83DDD12.png differ
diff --git a/toxygen/smileys/default/D83DDD13.png b/toxygen/smileys/default/D83DDD13.png
index a0f8311..bfc3b9b 100644
Binary files a/toxygen/smileys/default/D83DDD13.png and b/toxygen/smileys/default/D83DDD13.png differ
diff --git a/toxygen/smileys/default/D83DDD14.png b/toxygen/smileys/default/D83DDD14.png
index fefc17b..937d445 100644
Binary files a/toxygen/smileys/default/D83DDD14.png and b/toxygen/smileys/default/D83DDD14.png differ
diff --git a/toxygen/smileys/default/D83DDD15.png b/toxygen/smileys/default/D83DDD15.png
index 061cebb..135191f 100644
Binary files a/toxygen/smileys/default/D83DDD15.png and b/toxygen/smileys/default/D83DDD15.png differ
diff --git a/toxygen/smileys/default/D83DDD16.png b/toxygen/smileys/default/D83DDD16.png
index 42f2d33..8081be2 100644
Binary files a/toxygen/smileys/default/D83DDD16.png and b/toxygen/smileys/default/D83DDD16.png differ
diff --git a/toxygen/smileys/default/D83DDD17.png b/toxygen/smileys/default/D83DDD17.png
index d7df173..fbab54d 100644
Binary files a/toxygen/smileys/default/D83DDD17.png and b/toxygen/smileys/default/D83DDD17.png differ
diff --git a/toxygen/smileys/default/D83DDD18.png b/toxygen/smileys/default/D83DDD18.png
index da9708d..9787965 100644
Binary files a/toxygen/smileys/default/D83DDD18.png and b/toxygen/smileys/default/D83DDD18.png differ
diff --git a/toxygen/smileys/default/D83DDD19.png b/toxygen/smileys/default/D83DDD19.png
index 54da6d2..ed66b43 100644
Binary files a/toxygen/smileys/default/D83DDD19.png and b/toxygen/smileys/default/D83DDD19.png differ
diff --git a/toxygen/smileys/default/D83DDD1A.png b/toxygen/smileys/default/D83DDD1A.png
index b77ca43..adcdb79 100644
Binary files a/toxygen/smileys/default/D83DDD1A.png and b/toxygen/smileys/default/D83DDD1A.png differ
diff --git a/toxygen/smileys/default/D83DDD1B.png b/toxygen/smileys/default/D83DDD1B.png
index 9ebeaac..956d7d7 100644
Binary files a/toxygen/smileys/default/D83DDD1B.png and b/toxygen/smileys/default/D83DDD1B.png differ
diff --git a/toxygen/smileys/default/D83DDD1C.png b/toxygen/smileys/default/D83DDD1C.png
index bb52bb6..72d88f5 100644
Binary files a/toxygen/smileys/default/D83DDD1C.png and b/toxygen/smileys/default/D83DDD1C.png differ
diff --git a/toxygen/smileys/default/D83DDD1D.png b/toxygen/smileys/default/D83DDD1D.png
index 75acdb0..940a84d 100644
Binary files a/toxygen/smileys/default/D83DDD1D.png and b/toxygen/smileys/default/D83DDD1D.png differ
diff --git a/toxygen/smileys/default/D83DDD1E.png b/toxygen/smileys/default/D83DDD1E.png
index 81a8f84..4577ba8 100644
Binary files a/toxygen/smileys/default/D83DDD1E.png and b/toxygen/smileys/default/D83DDD1E.png differ
diff --git a/toxygen/smileys/default/D83DDD1F.png b/toxygen/smileys/default/D83DDD1F.png
index c709d36..9533fa0 100644
Binary files a/toxygen/smileys/default/D83DDD1F.png and b/toxygen/smileys/default/D83DDD1F.png differ
diff --git a/toxygen/smileys/default/D83DDD20.png b/toxygen/smileys/default/D83DDD20.png
index 0fdbe29..74e29fa 100644
Binary files a/toxygen/smileys/default/D83DDD20.png and b/toxygen/smileys/default/D83DDD20.png differ
diff --git a/toxygen/smileys/default/D83DDD21.png b/toxygen/smileys/default/D83DDD21.png
index 4cc424d..c77b49a 100644
Binary files a/toxygen/smileys/default/D83DDD21.png and b/toxygen/smileys/default/D83DDD21.png differ
diff --git a/toxygen/smileys/default/D83DDD22.png b/toxygen/smileys/default/D83DDD22.png
index d86193a..841012e 100644
Binary files a/toxygen/smileys/default/D83DDD22.png and b/toxygen/smileys/default/D83DDD22.png differ
diff --git a/toxygen/smileys/default/D83DDD23.png b/toxygen/smileys/default/D83DDD23.png
index 5684e65..8320fa3 100644
Binary files a/toxygen/smileys/default/D83DDD23.png and b/toxygen/smileys/default/D83DDD23.png differ
diff --git a/toxygen/smileys/default/D83DDD24.png b/toxygen/smileys/default/D83DDD24.png
index 37b4083..eeb0666 100644
Binary files a/toxygen/smileys/default/D83DDD24.png and b/toxygen/smileys/default/D83DDD24.png differ
diff --git a/toxygen/smileys/default/D83DDD25.png b/toxygen/smileys/default/D83DDD25.png
index 3625517..f4db0d9 100644
Binary files a/toxygen/smileys/default/D83DDD25.png and b/toxygen/smileys/default/D83DDD25.png differ
diff --git a/toxygen/smileys/default/D83DDD26.png b/toxygen/smileys/default/D83DDD26.png
index ec4ca85..78acca9 100644
Binary files a/toxygen/smileys/default/D83DDD26.png and b/toxygen/smileys/default/D83DDD26.png differ
diff --git a/toxygen/smileys/default/D83DDD27.png b/toxygen/smileys/default/D83DDD27.png
index c29a3d3..9a424b4 100644
Binary files a/toxygen/smileys/default/D83DDD27.png and b/toxygen/smileys/default/D83DDD27.png differ
diff --git a/toxygen/smileys/default/D83DDD28.png b/toxygen/smileys/default/D83DDD28.png
index d63385a..0193fc1 100644
Binary files a/toxygen/smileys/default/D83DDD28.png and b/toxygen/smileys/default/D83DDD28.png differ
diff --git a/toxygen/smileys/default/D83DDD29.png b/toxygen/smileys/default/D83DDD29.png
index 59c3282..7eaa1d6 100644
Binary files a/toxygen/smileys/default/D83DDD29.png and b/toxygen/smileys/default/D83DDD29.png differ
diff --git a/toxygen/smileys/default/D83DDD2A.png b/toxygen/smileys/default/D83DDD2A.png
index 3070ae7..02c024a 100644
Binary files a/toxygen/smileys/default/D83DDD2A.png and b/toxygen/smileys/default/D83DDD2A.png differ
diff --git a/toxygen/smileys/default/D83DDD2B.png b/toxygen/smileys/default/D83DDD2B.png
index d54b05b..40cd7f9 100644
Binary files a/toxygen/smileys/default/D83DDD2B.png and b/toxygen/smileys/default/D83DDD2B.png differ
diff --git a/toxygen/smileys/default/D83DDD2C.png b/toxygen/smileys/default/D83DDD2C.png
index abf56d7..0147271 100644
Binary files a/toxygen/smileys/default/D83DDD2C.png and b/toxygen/smileys/default/D83DDD2C.png differ
diff --git a/toxygen/smileys/default/D83DDD2D.png b/toxygen/smileys/default/D83DDD2D.png
index be1709a..450c039 100644
Binary files a/toxygen/smileys/default/D83DDD2D.png and b/toxygen/smileys/default/D83DDD2D.png differ
diff --git a/toxygen/smileys/default/D83DDD2E.png b/toxygen/smileys/default/D83DDD2E.png
index b6d25a8..2c056bf 100644
Binary files a/toxygen/smileys/default/D83DDD2E.png and b/toxygen/smileys/default/D83DDD2E.png differ
diff --git a/toxygen/smileys/default/D83DDD31.png b/toxygen/smileys/default/D83DDD31.png
index 35dc231..d115de5 100644
Binary files a/toxygen/smileys/default/D83DDD31.png and b/toxygen/smileys/default/D83DDD31.png differ
diff --git a/toxygen/smileys/default/D83DDD32.png b/toxygen/smileys/default/D83DDD32.png
index 04e60df..f0c6e28 100644
Binary files a/toxygen/smileys/default/D83DDD32.png and b/toxygen/smileys/default/D83DDD32.png differ
diff --git a/toxygen/smileys/default/D83DDD33.png b/toxygen/smileys/default/D83DDD33.png
index 2d7cd76..9b0a44d 100644
Binary files a/toxygen/smileys/default/D83DDD33.png and b/toxygen/smileys/default/D83DDD33.png differ
diff --git a/toxygen/smileys/default/D83DDD34.png b/toxygen/smileys/default/D83DDD34.png
index 7b308fb..a1b4491 100644
Binary files a/toxygen/smileys/default/D83DDD34.png and b/toxygen/smileys/default/D83DDD34.png differ
diff --git a/toxygen/smileys/default/D83DDD35.png b/toxygen/smileys/default/D83DDD35.png
index 67cf643..e746012 100644
Binary files a/toxygen/smileys/default/D83DDD35.png and b/toxygen/smileys/default/D83DDD35.png differ
diff --git a/toxygen/smileys/default/D83DDD36.png b/toxygen/smileys/default/D83DDD36.png
index 54bdf32..2d3c57d 100644
Binary files a/toxygen/smileys/default/D83DDD36.png and b/toxygen/smileys/default/D83DDD36.png differ
diff --git a/toxygen/smileys/default/D83DDD37.png b/toxygen/smileys/default/D83DDD37.png
index 32336fe..7fea98d 100644
Binary files a/toxygen/smileys/default/D83DDD37.png and b/toxygen/smileys/default/D83DDD37.png differ
diff --git a/toxygen/smileys/default/D83DDD38.png b/toxygen/smileys/default/D83DDD38.png
index dc39083..136df51 100644
Binary files a/toxygen/smileys/default/D83DDD38.png and b/toxygen/smileys/default/D83DDD38.png differ
diff --git a/toxygen/smileys/default/D83DDD39.png b/toxygen/smileys/default/D83DDD39.png
index e6bce51..00a0c43 100644
Binary files a/toxygen/smileys/default/D83DDD39.png and b/toxygen/smileys/default/D83DDD39.png differ
diff --git a/toxygen/smileys/default/D83DDD3A.png b/toxygen/smileys/default/D83DDD3A.png
index d0902a9..8f7b1d3 100644
Binary files a/toxygen/smileys/default/D83DDD3A.png and b/toxygen/smileys/default/D83DDD3A.png differ
diff --git a/toxygen/smileys/default/D83DDD3B.png b/toxygen/smileys/default/D83DDD3B.png
index de5a4d5..e980342 100644
Binary files a/toxygen/smileys/default/D83DDD3B.png and b/toxygen/smileys/default/D83DDD3B.png differ
diff --git a/toxygen/smileys/default/D83DDD3C.png b/toxygen/smileys/default/D83DDD3C.png
index 5e58b6f..c5f37cb 100644
Binary files a/toxygen/smileys/default/D83DDD3C.png and b/toxygen/smileys/default/D83DDD3C.png differ
diff --git a/toxygen/smileys/default/D83DDD3D.png b/toxygen/smileys/default/D83DDD3D.png
index 8c6b23f..d887596 100644
Binary files a/toxygen/smileys/default/D83DDD3D.png and b/toxygen/smileys/default/D83DDD3D.png differ
diff --git a/toxygen/smileys/default/D83DDDFB.png b/toxygen/smileys/default/D83DDDFB.png
index 588ac40..cdecd76 100644
Binary files a/toxygen/smileys/default/D83DDDFB.png and b/toxygen/smileys/default/D83DDDFB.png differ
diff --git a/toxygen/smileys/default/D83DDDFC.png b/toxygen/smileys/default/D83DDDFC.png
index 77970e7..ea72808 100644
Binary files a/toxygen/smileys/default/D83DDDFC.png and b/toxygen/smileys/default/D83DDDFC.png differ
diff --git a/toxygen/smileys/default/D83DDDFD.png b/toxygen/smileys/default/D83DDDFD.png
index e189cd1..899fe6e 100644
Binary files a/toxygen/smileys/default/D83DDDFD.png and b/toxygen/smileys/default/D83DDDFD.png differ
diff --git a/toxygen/smileys/default/D83DDDFE.png b/toxygen/smileys/default/D83DDDFE.png
index d8fbe06..bd3ca85 100644
Binary files a/toxygen/smileys/default/D83DDDFE.png and b/toxygen/smileys/default/D83DDDFE.png differ
diff --git a/toxygen/smileys/default/D83DDDFF.png b/toxygen/smileys/default/D83DDDFF.png
index 89148f1..bb6cad6 100644
Binary files a/toxygen/smileys/default/D83DDDFF.png and b/toxygen/smileys/default/D83DDDFF.png differ
diff --git a/toxygen/smileys/default/D83DDE00.png b/toxygen/smileys/default/D83DDE00.png
index 0cd39d2..e7cbe1d 100644
Binary files a/toxygen/smileys/default/D83DDE00.png and b/toxygen/smileys/default/D83DDE00.png differ
diff --git a/toxygen/smileys/default/D83DDE01.png b/toxygen/smileys/default/D83DDE01.png
index acf0f88..deee5ea 100644
Binary files a/toxygen/smileys/default/D83DDE01.png and b/toxygen/smileys/default/D83DDE01.png differ
diff --git a/toxygen/smileys/default/D83DDE02.png b/toxygen/smileys/default/D83DDE02.png
index ba6136b..89190fa 100644
Binary files a/toxygen/smileys/default/D83DDE02.png and b/toxygen/smileys/default/D83DDE02.png differ
diff --git a/toxygen/smileys/default/D83DDE03.png b/toxygen/smileys/default/D83DDE03.png
index 1f17728..be04f6c 100644
Binary files a/toxygen/smileys/default/D83DDE03.png and b/toxygen/smileys/default/D83DDE03.png differ
diff --git a/toxygen/smileys/default/D83DDE04.png b/toxygen/smileys/default/D83DDE04.png
index eaddfd3..435b0ca 100644
Binary files a/toxygen/smileys/default/D83DDE04.png and b/toxygen/smileys/default/D83DDE04.png differ
diff --git a/toxygen/smileys/default/D83DDE05.png b/toxygen/smileys/default/D83DDE05.png
index 0ffdcd3..2aaf1b7 100644
Binary files a/toxygen/smileys/default/D83DDE05.png and b/toxygen/smileys/default/D83DDE05.png differ
diff --git a/toxygen/smileys/default/D83DDE06.png b/toxygen/smileys/default/D83DDE06.png
index 99739e2..f3f1c7e 100644
Binary files a/toxygen/smileys/default/D83DDE06.png and b/toxygen/smileys/default/D83DDE06.png differ
diff --git a/toxygen/smileys/default/D83DDE07.png b/toxygen/smileys/default/D83DDE07.png
index 12dee1b..00ddb6e 100644
Binary files a/toxygen/smileys/default/D83DDE07.png and b/toxygen/smileys/default/D83DDE07.png differ
diff --git a/toxygen/smileys/default/D83DDE08.png b/toxygen/smileys/default/D83DDE08.png
index aa09cf9..b775c51 100644
Binary files a/toxygen/smileys/default/D83DDE08.png and b/toxygen/smileys/default/D83DDE08.png differ
diff --git a/toxygen/smileys/default/D83DDE09.png b/toxygen/smileys/default/D83DDE09.png
index 510fd1e..5eccad2 100644
Binary files a/toxygen/smileys/default/D83DDE09.png and b/toxygen/smileys/default/D83DDE09.png differ
diff --git a/toxygen/smileys/default/D83DDE0A.png b/toxygen/smileys/default/D83DDE0A.png
index c24689c..2885494 100644
Binary files a/toxygen/smileys/default/D83DDE0A.png and b/toxygen/smileys/default/D83DDE0A.png differ
diff --git a/toxygen/smileys/default/D83DDE0B.png b/toxygen/smileys/default/D83DDE0B.png
index cdac089..83c0e83 100644
Binary files a/toxygen/smileys/default/D83DDE0B.png and b/toxygen/smileys/default/D83DDE0B.png differ
diff --git a/toxygen/smileys/default/D83DDE0C.png b/toxygen/smileys/default/D83DDE0C.png
index a1a9721..b8a367a 100644
Binary files a/toxygen/smileys/default/D83DDE0C.png and b/toxygen/smileys/default/D83DDE0C.png differ
diff --git a/toxygen/smileys/default/D83DDE0D.png b/toxygen/smileys/default/D83DDE0D.png
index 0ec4145..6fdf5c6 100644
Binary files a/toxygen/smileys/default/D83DDE0D.png and b/toxygen/smileys/default/D83DDE0D.png differ
diff --git a/toxygen/smileys/default/D83DDE0E.png b/toxygen/smileys/default/D83DDE0E.png
index 4393ef6..8c1b63f 100644
Binary files a/toxygen/smileys/default/D83DDE0E.png and b/toxygen/smileys/default/D83DDE0E.png differ
diff --git a/toxygen/smileys/default/D83DDE0F.png b/toxygen/smileys/default/D83DDE0F.png
index a14dc21..d8e00d0 100644
Binary files a/toxygen/smileys/default/D83DDE0F.png and b/toxygen/smileys/default/D83DDE0F.png differ
diff --git a/toxygen/smileys/default/D83DDE10.png b/toxygen/smileys/default/D83DDE10.png
index 21b43ea..f5b9b12 100644
Binary files a/toxygen/smileys/default/D83DDE10.png and b/toxygen/smileys/default/D83DDE10.png differ
diff --git a/toxygen/smileys/default/D83DDE11.png b/toxygen/smileys/default/D83DDE11.png
index e6946b0..c050655 100644
Binary files a/toxygen/smileys/default/D83DDE11.png and b/toxygen/smileys/default/D83DDE11.png differ
diff --git a/toxygen/smileys/default/D83DDE12.png b/toxygen/smileys/default/D83DDE12.png
index bd3e0a2..bfd07f9 100644
Binary files a/toxygen/smileys/default/D83DDE12.png and b/toxygen/smileys/default/D83DDE12.png differ
diff --git a/toxygen/smileys/default/D83DDE13.png b/toxygen/smileys/default/D83DDE13.png
index d9be4e9..9812eea 100644
Binary files a/toxygen/smileys/default/D83DDE13.png and b/toxygen/smileys/default/D83DDE13.png differ
diff --git a/toxygen/smileys/default/D83DDE14.png b/toxygen/smileys/default/D83DDE14.png
index c73602b..e2ff195 100644
Binary files a/toxygen/smileys/default/D83DDE14.png and b/toxygen/smileys/default/D83DDE14.png differ
diff --git a/toxygen/smileys/default/D83DDE15.png b/toxygen/smileys/default/D83DDE15.png
index 6d16ea3..c1dcf86 100644
Binary files a/toxygen/smileys/default/D83DDE15.png and b/toxygen/smileys/default/D83DDE15.png differ
diff --git a/toxygen/smileys/default/D83DDE16.png b/toxygen/smileys/default/D83DDE16.png
index a0ae46a..e61bc89 100644
Binary files a/toxygen/smileys/default/D83DDE16.png and b/toxygen/smileys/default/D83DDE16.png differ
diff --git a/toxygen/smileys/default/D83DDE17.png b/toxygen/smileys/default/D83DDE17.png
index d110e6a..b583473 100644
Binary files a/toxygen/smileys/default/D83DDE17.png and b/toxygen/smileys/default/D83DDE17.png differ
diff --git a/toxygen/smileys/default/D83DDE18.png b/toxygen/smileys/default/D83DDE18.png
index 04f349e..b4b985e 100644
Binary files a/toxygen/smileys/default/D83DDE18.png and b/toxygen/smileys/default/D83DDE18.png differ
diff --git a/toxygen/smileys/default/D83DDE19.png b/toxygen/smileys/default/D83DDE19.png
index be3d55c..6981b2b 100644
Binary files a/toxygen/smileys/default/D83DDE19.png and b/toxygen/smileys/default/D83DDE19.png differ
diff --git a/toxygen/smileys/default/D83DDE1A.png b/toxygen/smileys/default/D83DDE1A.png
index 0be83c9..5d72bc9 100644
Binary files a/toxygen/smileys/default/D83DDE1A.png and b/toxygen/smileys/default/D83DDE1A.png differ
diff --git a/toxygen/smileys/default/D83DDE1B.png b/toxygen/smileys/default/D83DDE1B.png
index 0a52738..5466a03 100644
Binary files a/toxygen/smileys/default/D83DDE1B.png and b/toxygen/smileys/default/D83DDE1B.png differ
diff --git a/toxygen/smileys/default/D83DDE1C.png b/toxygen/smileys/default/D83DDE1C.png
index 628cea5..6796924 100644
Binary files a/toxygen/smileys/default/D83DDE1C.png and b/toxygen/smileys/default/D83DDE1C.png differ
diff --git a/toxygen/smileys/default/D83DDE1D.png b/toxygen/smileys/default/D83DDE1D.png
index a646c97..aa3d784 100644
Binary files a/toxygen/smileys/default/D83DDE1D.png and b/toxygen/smileys/default/D83DDE1D.png differ
diff --git a/toxygen/smileys/default/D83DDE1E.png b/toxygen/smileys/default/D83DDE1E.png
index 908d406..f2845ef 100644
Binary files a/toxygen/smileys/default/D83DDE1E.png and b/toxygen/smileys/default/D83DDE1E.png differ
diff --git a/toxygen/smileys/default/D83DDE1F.png b/toxygen/smileys/default/D83DDE1F.png
index 486b21c..b21d08f 100644
Binary files a/toxygen/smileys/default/D83DDE1F.png and b/toxygen/smileys/default/D83DDE1F.png differ
diff --git a/toxygen/smileys/default/D83DDE20.png b/toxygen/smileys/default/D83DDE20.png
index 7bcfc84..5b4a0cd 100644
Binary files a/toxygen/smileys/default/D83DDE20.png and b/toxygen/smileys/default/D83DDE20.png differ
diff --git a/toxygen/smileys/default/D83DDE21.png b/toxygen/smileys/default/D83DDE21.png
index 4e176b4..4d891fa 100644
Binary files a/toxygen/smileys/default/D83DDE21.png and b/toxygen/smileys/default/D83DDE21.png differ
diff --git a/toxygen/smileys/default/D83DDE22.png b/toxygen/smileys/default/D83DDE22.png
index 11063d8..2cc2c82 100644
Binary files a/toxygen/smileys/default/D83DDE22.png and b/toxygen/smileys/default/D83DDE22.png differ
diff --git a/toxygen/smileys/default/D83DDE23.png b/toxygen/smileys/default/D83DDE23.png
index bab140c..3cd5062 100644
Binary files a/toxygen/smileys/default/D83DDE23.png and b/toxygen/smileys/default/D83DDE23.png differ
diff --git a/toxygen/smileys/default/D83DDE24.png b/toxygen/smileys/default/D83DDE24.png
index 900d0dc..7a98d95 100644
Binary files a/toxygen/smileys/default/D83DDE24.png and b/toxygen/smileys/default/D83DDE24.png differ
diff --git a/toxygen/smileys/default/D83DDE25.png b/toxygen/smileys/default/D83DDE25.png
index 271da83..e244fe7 100644
Binary files a/toxygen/smileys/default/D83DDE25.png and b/toxygen/smileys/default/D83DDE25.png differ
diff --git a/toxygen/smileys/default/D83DDE26.png b/toxygen/smileys/default/D83DDE26.png
index bd494b6..48641e6 100644
Binary files a/toxygen/smileys/default/D83DDE26.png and b/toxygen/smileys/default/D83DDE26.png differ
diff --git a/toxygen/smileys/default/D83DDE27.png b/toxygen/smileys/default/D83DDE27.png
index b18443d..a2e655a 100644
Binary files a/toxygen/smileys/default/D83DDE27.png and b/toxygen/smileys/default/D83DDE27.png differ
diff --git a/toxygen/smileys/default/D83DDE28.png b/toxygen/smileys/default/D83DDE28.png
index 75eafd2..76ccea9 100644
Binary files a/toxygen/smileys/default/D83DDE28.png and b/toxygen/smileys/default/D83DDE28.png differ
diff --git a/toxygen/smileys/default/D83DDE29.png b/toxygen/smileys/default/D83DDE29.png
index 28252f8..4430882 100644
Binary files a/toxygen/smileys/default/D83DDE29.png and b/toxygen/smileys/default/D83DDE29.png differ
diff --git a/toxygen/smileys/default/D83DDE2A.png b/toxygen/smileys/default/D83DDE2A.png
index c98fe7f..0bb276b 100644
Binary files a/toxygen/smileys/default/D83DDE2A.png and b/toxygen/smileys/default/D83DDE2A.png differ
diff --git a/toxygen/smileys/default/D83DDE2B.png b/toxygen/smileys/default/D83DDE2B.png
index fc972fb..0b459a2 100644
Binary files a/toxygen/smileys/default/D83DDE2B.png and b/toxygen/smileys/default/D83DDE2B.png differ
diff --git a/toxygen/smileys/default/D83DDE2C.png b/toxygen/smileys/default/D83DDE2C.png
index e4bc449..b945eff 100644
Binary files a/toxygen/smileys/default/D83DDE2C.png and b/toxygen/smileys/default/D83DDE2C.png differ
diff --git a/toxygen/smileys/default/D83DDE2D.png b/toxygen/smileys/default/D83DDE2D.png
index 2c7f81d..aac29ca 100644
Binary files a/toxygen/smileys/default/D83DDE2D.png and b/toxygen/smileys/default/D83DDE2D.png differ
diff --git a/toxygen/smileys/default/D83DDE2E.png b/toxygen/smileys/default/D83DDE2E.png
index f9d5570..f6df656 100644
Binary files a/toxygen/smileys/default/D83DDE2E.png and b/toxygen/smileys/default/D83DDE2E.png differ
diff --git a/toxygen/smileys/default/D83DDE2F.png b/toxygen/smileys/default/D83DDE2F.png
index 79a6935..98c4308 100644
Binary files a/toxygen/smileys/default/D83DDE2F.png and b/toxygen/smileys/default/D83DDE2F.png differ
diff --git a/toxygen/smileys/default/D83DDE30.png b/toxygen/smileys/default/D83DDE30.png
index b86ff4b..8f50d8d 100644
Binary files a/toxygen/smileys/default/D83DDE30.png and b/toxygen/smileys/default/D83DDE30.png differ
diff --git a/toxygen/smileys/default/D83DDE31.png b/toxygen/smileys/default/D83DDE31.png
index fbd5527..a54a8a0 100644
Binary files a/toxygen/smileys/default/D83DDE31.png and b/toxygen/smileys/default/D83DDE31.png differ
diff --git a/toxygen/smileys/default/D83DDE32.png b/toxygen/smileys/default/D83DDE32.png
index f401d0e..48c87ea 100644
Binary files a/toxygen/smileys/default/D83DDE32.png and b/toxygen/smileys/default/D83DDE32.png differ
diff --git a/toxygen/smileys/default/D83DDE33.png b/toxygen/smileys/default/D83DDE33.png
index cecc347..cf50892 100644
Binary files a/toxygen/smileys/default/D83DDE33.png and b/toxygen/smileys/default/D83DDE33.png differ
diff --git a/toxygen/smileys/default/D83DDE34.png b/toxygen/smileys/default/D83DDE34.png
index b5fb2d7..3f4f1d6 100644
Binary files a/toxygen/smileys/default/D83DDE34.png and b/toxygen/smileys/default/D83DDE34.png differ
diff --git a/toxygen/smileys/default/D83DDE35.png b/toxygen/smileys/default/D83DDE35.png
index 41cacc7..24391db 100644
Binary files a/toxygen/smileys/default/D83DDE35.png and b/toxygen/smileys/default/D83DDE35.png differ
diff --git a/toxygen/smileys/default/D83DDE36.png b/toxygen/smileys/default/D83DDE36.png
index a81be46..46b30af 100644
Binary files a/toxygen/smileys/default/D83DDE36.png and b/toxygen/smileys/default/D83DDE36.png differ
diff --git a/toxygen/smileys/default/D83DDE37.png b/toxygen/smileys/default/D83DDE37.png
index c8177d6..1dea4b5 100644
Binary files a/toxygen/smileys/default/D83DDE37.png and b/toxygen/smileys/default/D83DDE37.png differ
diff --git a/toxygen/smileys/default/D83DDE38.png b/toxygen/smileys/default/D83DDE38.png
index ffad9c5..882d0ac 100644
Binary files a/toxygen/smileys/default/D83DDE38.png and b/toxygen/smileys/default/D83DDE38.png differ
diff --git a/toxygen/smileys/default/D83DDE39.png b/toxygen/smileys/default/D83DDE39.png
index 828b832..c311744 100644
Binary files a/toxygen/smileys/default/D83DDE39.png and b/toxygen/smileys/default/D83DDE39.png differ
diff --git a/toxygen/smileys/default/D83DDE3A.png b/toxygen/smileys/default/D83DDE3A.png
index 8022c4d..a18fa7d 100644
Binary files a/toxygen/smileys/default/D83DDE3A.png and b/toxygen/smileys/default/D83DDE3A.png differ
diff --git a/toxygen/smileys/default/D83DDE3B.png b/toxygen/smileys/default/D83DDE3B.png
index c9405b7..ed35e28 100644
Binary files a/toxygen/smileys/default/D83DDE3B.png and b/toxygen/smileys/default/D83DDE3B.png differ
diff --git a/toxygen/smileys/default/D83DDE3C.png b/toxygen/smileys/default/D83DDE3C.png
index cb088d1..f924c45 100644
Binary files a/toxygen/smileys/default/D83DDE3C.png and b/toxygen/smileys/default/D83DDE3C.png differ
diff --git a/toxygen/smileys/default/D83DDE3D.png b/toxygen/smileys/default/D83DDE3D.png
index ca2a4cc..bf8c962 100644
Binary files a/toxygen/smileys/default/D83DDE3D.png and b/toxygen/smileys/default/D83DDE3D.png differ
diff --git a/toxygen/smileys/default/D83DDE3E.png b/toxygen/smileys/default/D83DDE3E.png
index 840ced0..e02931c 100644
Binary files a/toxygen/smileys/default/D83DDE3E.png and b/toxygen/smileys/default/D83DDE3E.png differ
diff --git a/toxygen/smileys/default/D83DDE3F.png b/toxygen/smileys/default/D83DDE3F.png
index 8d0375b..a7cd4e1 100644
Binary files a/toxygen/smileys/default/D83DDE3F.png and b/toxygen/smileys/default/D83DDE3F.png differ
diff --git a/toxygen/smileys/default/D83DDE40.png b/toxygen/smileys/default/D83DDE40.png
index 01df29d..9a72002 100644
Binary files a/toxygen/smileys/default/D83DDE40.png and b/toxygen/smileys/default/D83DDE40.png differ
diff --git a/toxygen/smileys/default/D83DDE45.png b/toxygen/smileys/default/D83DDE45.png
index f9da41a..503fd32 100644
Binary files a/toxygen/smileys/default/D83DDE45.png and b/toxygen/smileys/default/D83DDE45.png differ
diff --git a/toxygen/smileys/default/D83DDE46.png b/toxygen/smileys/default/D83DDE46.png
index cd8c707..f965125 100644
Binary files a/toxygen/smileys/default/D83DDE46.png and b/toxygen/smileys/default/D83DDE46.png differ
diff --git a/toxygen/smileys/default/D83DDE47.png b/toxygen/smileys/default/D83DDE47.png
index a491820..355c9d2 100644
Binary files a/toxygen/smileys/default/D83DDE47.png and b/toxygen/smileys/default/D83DDE47.png differ
diff --git a/toxygen/smileys/default/D83DDE48.png b/toxygen/smileys/default/D83DDE48.png
index 937cb83..098c7f5 100644
Binary files a/toxygen/smileys/default/D83DDE48.png and b/toxygen/smileys/default/D83DDE48.png differ
diff --git a/toxygen/smileys/default/D83DDE49.png b/toxygen/smileys/default/D83DDE49.png
index e63475f..320c7fa 100644
Binary files a/toxygen/smileys/default/D83DDE49.png and b/toxygen/smileys/default/D83DDE49.png differ
diff --git a/toxygen/smileys/default/D83DDE4A.png b/toxygen/smileys/default/D83DDE4A.png
index e73108d..2a36454 100644
Binary files a/toxygen/smileys/default/D83DDE4A.png and b/toxygen/smileys/default/D83DDE4A.png differ
diff --git a/toxygen/smileys/default/D83DDE4B.png b/toxygen/smileys/default/D83DDE4B.png
index 90e1e1c..e9f8655 100644
Binary files a/toxygen/smileys/default/D83DDE4B.png and b/toxygen/smileys/default/D83DDE4B.png differ
diff --git a/toxygen/smileys/default/D83DDE4C.png b/toxygen/smileys/default/D83DDE4C.png
index a6e7081..ccb621a 100644
Binary files a/toxygen/smileys/default/D83DDE4C.png and b/toxygen/smileys/default/D83DDE4C.png differ
diff --git a/toxygen/smileys/default/D83DDE4D.png b/toxygen/smileys/default/D83DDE4D.png
index 2954a6e..fc9fb7e 100644
Binary files a/toxygen/smileys/default/D83DDE4D.png and b/toxygen/smileys/default/D83DDE4D.png differ
diff --git a/toxygen/smileys/default/D83DDE4E.png b/toxygen/smileys/default/D83DDE4E.png
index 8032b6f..2fd7c71 100644
Binary files a/toxygen/smileys/default/D83DDE4E.png and b/toxygen/smileys/default/D83DDE4E.png differ
diff --git a/toxygen/smileys/default/D83DDE4F.png b/toxygen/smileys/default/D83DDE4F.png
index d597f2c..204545d 100644
Binary files a/toxygen/smileys/default/D83DDE4F.png and b/toxygen/smileys/default/D83DDE4F.png differ
diff --git a/toxygen/smileys/default/D83DDE80.png b/toxygen/smileys/default/D83DDE80.png
index 6ddfda4..7e9241d 100644
Binary files a/toxygen/smileys/default/D83DDE80.png and b/toxygen/smileys/default/D83DDE80.png differ
diff --git a/toxygen/smileys/default/D83DDE81.png b/toxygen/smileys/default/D83DDE81.png
index 3f6eb56..ade03ae 100644
Binary files a/toxygen/smileys/default/D83DDE81.png and b/toxygen/smileys/default/D83DDE81.png differ
diff --git a/toxygen/smileys/default/D83DDE82.png b/toxygen/smileys/default/D83DDE82.png
index 335e87f..ad242a2 100644
Binary files a/toxygen/smileys/default/D83DDE82.png and b/toxygen/smileys/default/D83DDE82.png differ
diff --git a/toxygen/smileys/default/D83DDE83.png b/toxygen/smileys/default/D83DDE83.png
index 25ae099..0c6dc18 100644
Binary files a/toxygen/smileys/default/D83DDE83.png and b/toxygen/smileys/default/D83DDE83.png differ
diff --git a/toxygen/smileys/default/D83DDE84.png b/toxygen/smileys/default/D83DDE84.png
index 025cf05..829cb73 100644
Binary files a/toxygen/smileys/default/D83DDE84.png and b/toxygen/smileys/default/D83DDE84.png differ
diff --git a/toxygen/smileys/default/D83DDE85.png b/toxygen/smileys/default/D83DDE85.png
index 4afbff7..07eb96a 100644
Binary files a/toxygen/smileys/default/D83DDE85.png and b/toxygen/smileys/default/D83DDE85.png differ
diff --git a/toxygen/smileys/default/D83DDE86.png b/toxygen/smileys/default/D83DDE86.png
index 8fc9521..d757d24 100644
Binary files a/toxygen/smileys/default/D83DDE86.png and b/toxygen/smileys/default/D83DDE86.png differ
diff --git a/toxygen/smileys/default/D83DDE87.png b/toxygen/smileys/default/D83DDE87.png
index b518e93..c50d0df 100644
Binary files a/toxygen/smileys/default/D83DDE87.png and b/toxygen/smileys/default/D83DDE87.png differ
diff --git a/toxygen/smileys/default/D83DDE88.png b/toxygen/smileys/default/D83DDE88.png
index 1120b8b..2be47b2 100644
Binary files a/toxygen/smileys/default/D83DDE88.png and b/toxygen/smileys/default/D83DDE88.png differ
diff --git a/toxygen/smileys/default/D83DDE89.png b/toxygen/smileys/default/D83DDE89.png
index fff5d09..0de6bd4 100644
Binary files a/toxygen/smileys/default/D83DDE89.png and b/toxygen/smileys/default/D83DDE89.png differ
diff --git a/toxygen/smileys/default/D83DDE8A.png b/toxygen/smileys/default/D83DDE8A.png
index 84a5df9..46637f4 100644
Binary files a/toxygen/smileys/default/D83DDE8A.png and b/toxygen/smileys/default/D83DDE8A.png differ
diff --git a/toxygen/smileys/default/D83DDE8B.png b/toxygen/smileys/default/D83DDE8B.png
index 53c3359..c25512b 100644
Binary files a/toxygen/smileys/default/D83DDE8B.png and b/toxygen/smileys/default/D83DDE8B.png differ
diff --git a/toxygen/smileys/default/D83DDE8C.png b/toxygen/smileys/default/D83DDE8C.png
index 2630cd1..95e047a 100644
Binary files a/toxygen/smileys/default/D83DDE8C.png and b/toxygen/smileys/default/D83DDE8C.png differ
diff --git a/toxygen/smileys/default/D83DDE8D.png b/toxygen/smileys/default/D83DDE8D.png
index a923db8..eeffc28 100644
Binary files a/toxygen/smileys/default/D83DDE8D.png and b/toxygen/smileys/default/D83DDE8D.png differ
diff --git a/toxygen/smileys/default/D83DDE8E.png b/toxygen/smileys/default/D83DDE8E.png
index d75ab66..cce5a5b 100644
Binary files a/toxygen/smileys/default/D83DDE8E.png and b/toxygen/smileys/default/D83DDE8E.png differ
diff --git a/toxygen/smileys/default/D83DDE8F.png b/toxygen/smileys/default/D83DDE8F.png
index df2c9ca..be13deb 100644
Binary files a/toxygen/smileys/default/D83DDE8F.png and b/toxygen/smileys/default/D83DDE8F.png differ
diff --git a/toxygen/smileys/default/D83DDE90.png b/toxygen/smileys/default/D83DDE90.png
index 44dc513..8407d41 100644
Binary files a/toxygen/smileys/default/D83DDE90.png and b/toxygen/smileys/default/D83DDE90.png differ
diff --git a/toxygen/smileys/default/D83DDE91.png b/toxygen/smileys/default/D83DDE91.png
index fd48b03..d9b1421 100644
Binary files a/toxygen/smileys/default/D83DDE91.png and b/toxygen/smileys/default/D83DDE91.png differ
diff --git a/toxygen/smileys/default/D83DDE92.png b/toxygen/smileys/default/D83DDE92.png
index 8d5c3e9..57981c7 100644
Binary files a/toxygen/smileys/default/D83DDE92.png and b/toxygen/smileys/default/D83DDE92.png differ
diff --git a/toxygen/smileys/default/D83DDE93.png b/toxygen/smileys/default/D83DDE93.png
index beb4e21..b2c6847 100644
Binary files a/toxygen/smileys/default/D83DDE93.png and b/toxygen/smileys/default/D83DDE93.png differ
diff --git a/toxygen/smileys/default/D83DDE94.png b/toxygen/smileys/default/D83DDE94.png
index b6efd4a..3cacb88 100644
Binary files a/toxygen/smileys/default/D83DDE94.png and b/toxygen/smileys/default/D83DDE94.png differ
diff --git a/toxygen/smileys/default/D83DDE95.png b/toxygen/smileys/default/D83DDE95.png
index d2e3f98..7facea0 100644
Binary files a/toxygen/smileys/default/D83DDE95.png and b/toxygen/smileys/default/D83DDE95.png differ
diff --git a/toxygen/smileys/default/D83DDE96.png b/toxygen/smileys/default/D83DDE96.png
index cdda0b7..8fe25d5 100644
Binary files a/toxygen/smileys/default/D83DDE96.png and b/toxygen/smileys/default/D83DDE96.png differ
diff --git a/toxygen/smileys/default/D83DDE97.png b/toxygen/smileys/default/D83DDE97.png
index 15ab93c..26e2b61 100644
Binary files a/toxygen/smileys/default/D83DDE97.png and b/toxygen/smileys/default/D83DDE97.png differ
diff --git a/toxygen/smileys/default/D83DDE98.png b/toxygen/smileys/default/D83DDE98.png
index 7c80a77..1324bd2 100644
Binary files a/toxygen/smileys/default/D83DDE98.png and b/toxygen/smileys/default/D83DDE98.png differ
diff --git a/toxygen/smileys/default/D83DDE99.png b/toxygen/smileys/default/D83DDE99.png
index 6a965bb..9b0d95c 100644
Binary files a/toxygen/smileys/default/D83DDE99.png and b/toxygen/smileys/default/D83DDE99.png differ
diff --git a/toxygen/smileys/default/D83DDE9A.png b/toxygen/smileys/default/D83DDE9A.png
index 93a7987..ef64e61 100644
Binary files a/toxygen/smileys/default/D83DDE9A.png and b/toxygen/smileys/default/D83DDE9A.png differ
diff --git a/toxygen/smileys/default/D83DDE9B.png b/toxygen/smileys/default/D83DDE9B.png
index 5edebd5..59af9fe 100644
Binary files a/toxygen/smileys/default/D83DDE9B.png and b/toxygen/smileys/default/D83DDE9B.png differ
diff --git a/toxygen/smileys/default/D83DDE9C.png b/toxygen/smileys/default/D83DDE9C.png
index bbc00b8..3fa3330 100644
Binary files a/toxygen/smileys/default/D83DDE9C.png and b/toxygen/smileys/default/D83DDE9C.png differ
diff --git a/toxygen/smileys/default/D83DDE9D.png b/toxygen/smileys/default/D83DDE9D.png
index 255da82..7c4a412 100644
Binary files a/toxygen/smileys/default/D83DDE9D.png and b/toxygen/smileys/default/D83DDE9D.png differ
diff --git a/toxygen/smileys/default/D83DDE9E.png b/toxygen/smileys/default/D83DDE9E.png
index be587b1..19b0411 100644
Binary files a/toxygen/smileys/default/D83DDE9E.png and b/toxygen/smileys/default/D83DDE9E.png differ
diff --git a/toxygen/smileys/default/D83DDE9F.png b/toxygen/smileys/default/D83DDE9F.png
index af0cff5..f391cdf 100644
Binary files a/toxygen/smileys/default/D83DDE9F.png and b/toxygen/smileys/default/D83DDE9F.png differ
diff --git a/toxygen/smileys/default/D83DDEA0.png b/toxygen/smileys/default/D83DDEA0.png
index 82936e9..6987fd5 100644
Binary files a/toxygen/smileys/default/D83DDEA0.png and b/toxygen/smileys/default/D83DDEA0.png differ
diff --git a/toxygen/smileys/default/D83DDEA1.png b/toxygen/smileys/default/D83DDEA1.png
index 9149416..e6abeb1 100644
Binary files a/toxygen/smileys/default/D83DDEA1.png and b/toxygen/smileys/default/D83DDEA1.png differ
diff --git a/toxygen/smileys/default/D83DDEA2.png b/toxygen/smileys/default/D83DDEA2.png
index 023eb77..ec8cd9e 100644
Binary files a/toxygen/smileys/default/D83DDEA2.png and b/toxygen/smileys/default/D83DDEA2.png differ
diff --git a/toxygen/smileys/default/D83DDEA3.png b/toxygen/smileys/default/D83DDEA3.png
index b91a9fb..4f29601 100644
Binary files a/toxygen/smileys/default/D83DDEA3.png and b/toxygen/smileys/default/D83DDEA3.png differ
diff --git a/toxygen/smileys/default/D83DDEA4.png b/toxygen/smileys/default/D83DDEA4.png
index 1f18619..e4ab4aa 100644
Binary files a/toxygen/smileys/default/D83DDEA4.png and b/toxygen/smileys/default/D83DDEA4.png differ
diff --git a/toxygen/smileys/default/D83DDEA5.png b/toxygen/smileys/default/D83DDEA5.png
index f6dab29..d3d6899 100644
Binary files a/toxygen/smileys/default/D83DDEA5.png and b/toxygen/smileys/default/D83DDEA5.png differ
diff --git a/toxygen/smileys/default/D83DDEA6.png b/toxygen/smileys/default/D83DDEA6.png
index a5c108f..4f32bd1 100644
Binary files a/toxygen/smileys/default/D83DDEA6.png and b/toxygen/smileys/default/D83DDEA6.png differ
diff --git a/toxygen/smileys/default/D83DDEA7.png b/toxygen/smileys/default/D83DDEA7.png
index dab4659..041e7ba 100644
Binary files a/toxygen/smileys/default/D83DDEA7.png and b/toxygen/smileys/default/D83DDEA7.png differ
diff --git a/toxygen/smileys/default/D83DDEA8.png b/toxygen/smileys/default/D83DDEA8.png
index 3e7d2ac..b67f810 100644
Binary files a/toxygen/smileys/default/D83DDEA8.png and b/toxygen/smileys/default/D83DDEA8.png differ
diff --git a/toxygen/smileys/default/D83DDEA9.png b/toxygen/smileys/default/D83DDEA9.png
index 08f1c5d..a0127db 100644
Binary files a/toxygen/smileys/default/D83DDEA9.png and b/toxygen/smileys/default/D83DDEA9.png differ
diff --git a/toxygen/smileys/default/D83DDEAA.png b/toxygen/smileys/default/D83DDEAA.png
index d186b37..808f73c 100644
Binary files a/toxygen/smileys/default/D83DDEAA.png and b/toxygen/smileys/default/D83DDEAA.png differ
diff --git a/toxygen/smileys/default/D83DDEAB.png b/toxygen/smileys/default/D83DDEAB.png
index 8d02055..092f7f7 100644
Binary files a/toxygen/smileys/default/D83DDEAB.png and b/toxygen/smileys/default/D83DDEAB.png differ
diff --git a/toxygen/smileys/default/D83DDEAC.png b/toxygen/smileys/default/D83DDEAC.png
index 0e9d890..2295dd1 100644
Binary files a/toxygen/smileys/default/D83DDEAC.png and b/toxygen/smileys/default/D83DDEAC.png differ
diff --git a/toxygen/smileys/default/D83DDEAD.png b/toxygen/smileys/default/D83DDEAD.png
index e8eb437..947ecea 100644
Binary files a/toxygen/smileys/default/D83DDEAD.png and b/toxygen/smileys/default/D83DDEAD.png differ
diff --git a/toxygen/smileys/default/D83DDEAE.png b/toxygen/smileys/default/D83DDEAE.png
index 89e1dbf..7c37d2e 100644
Binary files a/toxygen/smileys/default/D83DDEAE.png and b/toxygen/smileys/default/D83DDEAE.png differ
diff --git a/toxygen/smileys/default/D83DDEAF.png b/toxygen/smileys/default/D83DDEAF.png
index c1febe4..cc94e68 100644
Binary files a/toxygen/smileys/default/D83DDEAF.png and b/toxygen/smileys/default/D83DDEAF.png differ
diff --git a/toxygen/smileys/default/D83DDEB0.png b/toxygen/smileys/default/D83DDEB0.png
index 5a814ce..3790c67 100644
Binary files a/toxygen/smileys/default/D83DDEB0.png and b/toxygen/smileys/default/D83DDEB0.png differ
diff --git a/toxygen/smileys/default/D83DDEB1.png b/toxygen/smileys/default/D83DDEB1.png
index b94e904..6b01824 100644
Binary files a/toxygen/smileys/default/D83DDEB1.png and b/toxygen/smileys/default/D83DDEB1.png differ
diff --git a/toxygen/smileys/default/D83DDEB2.png b/toxygen/smileys/default/D83DDEB2.png
index 204b1ff..ff22425 100644
Binary files a/toxygen/smileys/default/D83DDEB2.png and b/toxygen/smileys/default/D83DDEB2.png differ
diff --git a/toxygen/smileys/default/D83DDEB3.png b/toxygen/smileys/default/D83DDEB3.png
index 94b6121..3aa33ae 100644
Binary files a/toxygen/smileys/default/D83DDEB3.png and b/toxygen/smileys/default/D83DDEB3.png differ
diff --git a/toxygen/smileys/default/D83DDEB4.png b/toxygen/smileys/default/D83DDEB4.png
index 2b63d91..5b8e424 100644
Binary files a/toxygen/smileys/default/D83DDEB4.png and b/toxygen/smileys/default/D83DDEB4.png differ
diff --git a/toxygen/smileys/default/D83DDEB5.png b/toxygen/smileys/default/D83DDEB5.png
index 9f9fb96..a53016c 100644
Binary files a/toxygen/smileys/default/D83DDEB5.png and b/toxygen/smileys/default/D83DDEB5.png differ
diff --git a/toxygen/smileys/default/D83DDEB6.png b/toxygen/smileys/default/D83DDEB6.png
index 2662d36..3cae405 100644
Binary files a/toxygen/smileys/default/D83DDEB6.png and b/toxygen/smileys/default/D83DDEB6.png differ
diff --git a/toxygen/smileys/default/D83DDEB7.png b/toxygen/smileys/default/D83DDEB7.png
index a3c0a9b..464b925 100644
Binary files a/toxygen/smileys/default/D83DDEB7.png and b/toxygen/smileys/default/D83DDEB7.png differ
diff --git a/toxygen/smileys/default/D83DDEB8.png b/toxygen/smileys/default/D83DDEB8.png
index 479c4ee..ff26204 100644
Binary files a/toxygen/smileys/default/D83DDEB8.png and b/toxygen/smileys/default/D83DDEB8.png differ
diff --git a/toxygen/smileys/default/D83DDEB9.png b/toxygen/smileys/default/D83DDEB9.png
index ed294c0..34f6afe 100644
Binary files a/toxygen/smileys/default/D83DDEB9.png and b/toxygen/smileys/default/D83DDEB9.png differ
diff --git a/toxygen/smileys/default/D83DDEBA.png b/toxygen/smileys/default/D83DDEBA.png
index 2ec1130..42f346a 100644
Binary files a/toxygen/smileys/default/D83DDEBA.png and b/toxygen/smileys/default/D83DDEBA.png differ
diff --git a/toxygen/smileys/default/D83DDEBB.png b/toxygen/smileys/default/D83DDEBB.png
index d213051..730020e 100644
Binary files a/toxygen/smileys/default/D83DDEBB.png and b/toxygen/smileys/default/D83DDEBB.png differ
diff --git a/toxygen/smileys/default/D83DDEBC.png b/toxygen/smileys/default/D83DDEBC.png
index b473c5f..2e16d90 100644
Binary files a/toxygen/smileys/default/D83DDEBC.png and b/toxygen/smileys/default/D83DDEBC.png differ
diff --git a/toxygen/smileys/default/D83DDEBD.png b/toxygen/smileys/default/D83DDEBD.png
index 911fac4..4bae582 100644
Binary files a/toxygen/smileys/default/D83DDEBD.png and b/toxygen/smileys/default/D83DDEBD.png differ
diff --git a/toxygen/smileys/default/D83DDEBE.png b/toxygen/smileys/default/D83DDEBE.png
index e2bfc5d..0ad1c76 100644
Binary files a/toxygen/smileys/default/D83DDEBE.png and b/toxygen/smileys/default/D83DDEBE.png differ
diff --git a/toxygen/smileys/default/D83DDEBF.png b/toxygen/smileys/default/D83DDEBF.png
index 093ca87..90e601a 100644
Binary files a/toxygen/smileys/default/D83DDEBF.png and b/toxygen/smileys/default/D83DDEBF.png differ
diff --git a/toxygen/smileys/default/D83DDEC0.png b/toxygen/smileys/default/D83DDEC0.png
index 907b1da..048ec2c 100644
Binary files a/toxygen/smileys/default/D83DDEC0.png and b/toxygen/smileys/default/D83DDEC0.png differ
diff --git a/toxygen/smileys/default/D83DDEC1.png b/toxygen/smileys/default/D83DDEC1.png
index 9ac0098..84976f7 100644
Binary files a/toxygen/smileys/default/D83DDEC1.png and b/toxygen/smileys/default/D83DDEC1.png differ
diff --git a/toxygen/smileys/default/D83DDEC2.png b/toxygen/smileys/default/D83DDEC2.png
index 2e8a9b7..9149a02 100644
Binary files a/toxygen/smileys/default/D83DDEC2.png and b/toxygen/smileys/default/D83DDEC2.png differ
diff --git a/toxygen/smileys/default/D83DDEC3.png b/toxygen/smileys/default/D83DDEC3.png
index d76d3e2..affea8e 100644
Binary files a/toxygen/smileys/default/D83DDEC3.png and b/toxygen/smileys/default/D83DDEC3.png differ
diff --git a/toxygen/smileys/default/D83DDEC4.png b/toxygen/smileys/default/D83DDEC4.png
index 2bb6658..1ba4191 100644
Binary files a/toxygen/smileys/default/D83DDEC4.png and b/toxygen/smileys/default/D83DDEC4.png differ
diff --git a/toxygen/smileys/default/D83DDEC5.png b/toxygen/smileys/default/D83DDEC5.png
index 19f966a..cd438cb 100644
Binary files a/toxygen/smileys/default/D83DDEC5.png and b/toxygen/smileys/default/D83DDEC5.png differ
diff --git a/toxygen/smileys/default/ad.png b/toxygen/smileys/default/ad.png
old mode 100644
new mode 100755
index 552e976..625ca84
Binary files a/toxygen/smileys/default/ad.png and b/toxygen/smileys/default/ad.png differ
diff --git a/toxygen/smileys/default/ae.png b/toxygen/smileys/default/ae.png
old mode 100644
new mode 100755
index 670f615..ef3a1ec
Binary files a/toxygen/smileys/default/ae.png and b/toxygen/smileys/default/ae.png differ
diff --git a/toxygen/smileys/default/af.png b/toxygen/smileys/default/af.png
old mode 100644
new mode 100755
index cb6e23b..a4742e2
Binary files a/toxygen/smileys/default/af.png and b/toxygen/smileys/default/af.png differ
diff --git a/toxygen/smileys/default/ag.png b/toxygen/smileys/default/ag.png
old mode 100644
new mode 100755
index 421ae03..556d550
Binary files a/toxygen/smileys/default/ag.png and b/toxygen/smileys/default/ag.png differ
diff --git a/toxygen/smileys/default/ai.png b/toxygen/smileys/default/ai.png
old mode 100644
new mode 100755
index d4683cb..74ed29d
Binary files a/toxygen/smileys/default/ai.png and b/toxygen/smileys/default/ai.png differ
diff --git a/toxygen/smileys/default/al.png b/toxygen/smileys/default/al.png
old mode 100644
new mode 100755
index 1fe5c25..92354cb
Binary files a/toxygen/smileys/default/al.png and b/toxygen/smileys/default/al.png differ
diff --git a/toxygen/smileys/default/am.png b/toxygen/smileys/default/am.png
old mode 100644
new mode 100755
index 0bc24a7..344a2a8
Binary files a/toxygen/smileys/default/am.png and b/toxygen/smileys/default/am.png differ
diff --git a/toxygen/smileys/default/an.png b/toxygen/smileys/default/an.png
old mode 100644
new mode 100755
index 796943f..633e4b8
Binary files a/toxygen/smileys/default/an.png and b/toxygen/smileys/default/an.png differ
diff --git a/toxygen/smileys/default/ao.png b/toxygen/smileys/default/ao.png
index 5efe456..bcbd1d6 100644
Binary files a/toxygen/smileys/default/ao.png and b/toxygen/smileys/default/ao.png differ
diff --git a/toxygen/smileys/default/ar.png b/toxygen/smileys/default/ar.png
old mode 100644
new mode 100755
index 8cc53f5..e5ef8f1
Binary files a/toxygen/smileys/default/ar.png and b/toxygen/smileys/default/ar.png differ
diff --git a/toxygen/smileys/default/as.png b/toxygen/smileys/default/as.png
old mode 100644
new mode 100755
index a13e8c8..32f30e4
Binary files a/toxygen/smileys/default/as.png and b/toxygen/smileys/default/as.png differ
diff --git a/toxygen/smileys/default/at.png b/toxygen/smileys/default/at.png
old mode 100644
new mode 100755
index a420c86..0f15f34
Binary files a/toxygen/smileys/default/at.png and b/toxygen/smileys/default/at.png differ
diff --git a/toxygen/smileys/default/au.png b/toxygen/smileys/default/au.png
old mode 100644
new mode 100755
index f847827..a01389a
Binary files a/toxygen/smileys/default/au.png and b/toxygen/smileys/default/au.png differ
diff --git a/toxygen/smileys/default/aw.png b/toxygen/smileys/default/aw.png
old mode 100644
new mode 100755
index 3804086..a3579c2
Binary files a/toxygen/smileys/default/aw.png and b/toxygen/smileys/default/aw.png differ
diff --git a/toxygen/smileys/default/ax.png b/toxygen/smileys/default/ax.png
old mode 100644
new mode 100755
index d8075a5..1eea80a
Binary files a/toxygen/smileys/default/ax.png and b/toxygen/smileys/default/ax.png differ
diff --git a/toxygen/smileys/default/az.png b/toxygen/smileys/default/az.png
old mode 100644
new mode 100755
index 0eaf6b2..4ee9fe5
Binary files a/toxygen/smileys/default/az.png and b/toxygen/smileys/default/az.png differ
diff --git a/toxygen/smileys/default/ba.png b/toxygen/smileys/default/ba.png
old mode 100644
new mode 100755
index 96619d2..c774992
Binary files a/toxygen/smileys/default/ba.png and b/toxygen/smileys/default/ba.png differ
diff --git a/toxygen/smileys/default/bb.png b/toxygen/smileys/default/bb.png
old mode 100644
new mode 100755
index bf17f85..0df19c7
Binary files a/toxygen/smileys/default/bb.png and b/toxygen/smileys/default/bb.png differ
diff --git a/toxygen/smileys/default/bd.png b/toxygen/smileys/default/bd.png
old mode 100644
new mode 100755
index 4f0390c..076a8bf
Binary files a/toxygen/smileys/default/bd.png and b/toxygen/smileys/default/bd.png differ
diff --git a/toxygen/smileys/default/be.png b/toxygen/smileys/default/be.png
old mode 100644
new mode 100755
index 4c2c9da..d86ebc8
Binary files a/toxygen/smileys/default/be.png and b/toxygen/smileys/default/be.png differ
diff --git a/toxygen/smileys/default/bf.png b/toxygen/smileys/default/bf.png
old mode 100644
new mode 100755
index b7de459..ab5ce8f
Binary files a/toxygen/smileys/default/bf.png and b/toxygen/smileys/default/bf.png differ
diff --git a/toxygen/smileys/default/bg.png b/toxygen/smileys/default/bg.png
old mode 100644
new mode 100755
index c3c2e2c..0469f06
Binary files a/toxygen/smileys/default/bg.png and b/toxygen/smileys/default/bg.png differ
diff --git a/toxygen/smileys/default/bh.png b/toxygen/smileys/default/bh.png
old mode 100644
new mode 100755
index f3a88fd..ea8ce68
Binary files a/toxygen/smileys/default/bh.png and b/toxygen/smileys/default/bh.png differ
diff --git a/toxygen/smileys/default/bi.png b/toxygen/smileys/default/bi.png
old mode 100644
new mode 100755
index 18d51e2..5cc2e30
Binary files a/toxygen/smileys/default/bi.png and b/toxygen/smileys/default/bi.png differ
diff --git a/toxygen/smileys/default/bj.png b/toxygen/smileys/default/bj.png
old mode 100644
new mode 100755
index 32cf542..1cc8b45
Binary files a/toxygen/smileys/default/bj.png and b/toxygen/smileys/default/bj.png differ
diff --git a/toxygen/smileys/default/bm.png b/toxygen/smileys/default/bm.png
old mode 100644
new mode 100755
index 007e4d8..c0c7aea
Binary files a/toxygen/smileys/default/bm.png and b/toxygen/smileys/default/bm.png differ
diff --git a/toxygen/smileys/default/bn.png b/toxygen/smileys/default/bn.png
old mode 100644
new mode 100755
index a9e83e9..8fb0984
Binary files a/toxygen/smileys/default/bn.png and b/toxygen/smileys/default/bn.png differ
diff --git a/toxygen/smileys/default/bo.png b/toxygen/smileys/default/bo.png
old mode 100644
new mode 100755
index 06eb7a7..ce7ba52
Binary files a/toxygen/smileys/default/bo.png and b/toxygen/smileys/default/bo.png differ
diff --git a/toxygen/smileys/default/br.png b/toxygen/smileys/default/br.png
old mode 100644
new mode 100755
index f9da237..9b1a553
Binary files a/toxygen/smileys/default/br.png and b/toxygen/smileys/default/br.png differ
diff --git a/toxygen/smileys/default/bs.png b/toxygen/smileys/default/bs.png
old mode 100644
new mode 100755
index 09c3c6d..639fa6c
Binary files a/toxygen/smileys/default/bs.png and b/toxygen/smileys/default/bs.png differ
diff --git a/toxygen/smileys/default/bt.png b/toxygen/smileys/default/bt.png
old mode 100644
new mode 100755
index 5f07fd9..1d512df
Binary files a/toxygen/smileys/default/bt.png and b/toxygen/smileys/default/bt.png differ
diff --git a/toxygen/smileys/default/bv.png b/toxygen/smileys/default/bv.png
old mode 100644
new mode 100755
index 00997d1..160b6b5
Binary files a/toxygen/smileys/default/bv.png and b/toxygen/smileys/default/bv.png differ
diff --git a/toxygen/smileys/default/bw.png b/toxygen/smileys/default/bw.png
old mode 100644
new mode 100755
index 51d5ec4..fcb1039
Binary files a/toxygen/smileys/default/bw.png and b/toxygen/smileys/default/bw.png differ
diff --git a/toxygen/smileys/default/by.png b/toxygen/smileys/default/by.png
old mode 100644
new mode 100755
index b26d9a7..504774e
Binary files a/toxygen/smileys/default/by.png and b/toxygen/smileys/default/by.png differ
diff --git a/toxygen/smileys/default/bz.png b/toxygen/smileys/default/bz.png
old mode 100644
new mode 100755
index 3de1ee3..be63ee1
Binary files a/toxygen/smileys/default/bz.png and b/toxygen/smileys/default/bz.png differ
diff --git a/toxygen/smileys/default/ca.png b/toxygen/smileys/default/ca.png
old mode 100644
new mode 100755
index d11daef..1f20419
Binary files a/toxygen/smileys/default/ca.png and b/toxygen/smileys/default/ca.png differ
diff --git a/toxygen/smileys/default/catalonia.png b/toxygen/smileys/default/catalonia.png
index 0ae1406..5041e30 100644
Binary files a/toxygen/smileys/default/catalonia.png and b/toxygen/smileys/default/catalonia.png differ
diff --git a/toxygen/smileys/default/cc.png b/toxygen/smileys/default/cc.png
old mode 100644
new mode 100755
index 6b71349..aed3d3b
Binary files a/toxygen/smileys/default/cc.png and b/toxygen/smileys/default/cc.png differ
diff --git a/toxygen/smileys/default/cd.png b/toxygen/smileys/default/cd.png
index d04d285..5e48942 100644
Binary files a/toxygen/smileys/default/cd.png and b/toxygen/smileys/default/cd.png differ
diff --git a/toxygen/smileys/default/cf.png b/toxygen/smileys/default/cf.png
old mode 100644
new mode 100755
index e08fe16..da687bd
Binary files a/toxygen/smileys/default/cf.png and b/toxygen/smileys/default/cf.png differ
diff --git a/toxygen/smileys/default/cg.png b/toxygen/smileys/default/cg.png
old mode 100644
new mode 100755
index 5ff3986..a859792
Binary files a/toxygen/smileys/default/cg.png and b/toxygen/smileys/default/cg.png differ
diff --git a/toxygen/smileys/default/ch.png b/toxygen/smileys/default/ch.png
old mode 100644
new mode 100755
index da989f3..242ec01
Binary files a/toxygen/smileys/default/ch.png and b/toxygen/smileys/default/ch.png differ
diff --git a/toxygen/smileys/default/ci.png b/toxygen/smileys/default/ci.png
old mode 100644
new mode 100755
index 631d1fb..3f2c62e
Binary files a/toxygen/smileys/default/ci.png and b/toxygen/smileys/default/ci.png differ
diff --git a/toxygen/smileys/default/ck.png b/toxygen/smileys/default/ck.png
old mode 100644
new mode 100755
index 6f8d893..746d3d6
Binary files a/toxygen/smileys/default/ck.png and b/toxygen/smileys/default/ck.png differ
diff --git a/toxygen/smileys/default/cl.png b/toxygen/smileys/default/cl.png
old mode 100644
new mode 100755
index 3fe300c..29c6d61
Binary files a/toxygen/smileys/default/cl.png and b/toxygen/smileys/default/cl.png differ
diff --git a/toxygen/smileys/default/cm.png b/toxygen/smileys/default/cm.png
old mode 100644
new mode 100755
index 6a13412..f65c5bd
Binary files a/toxygen/smileys/default/cm.png and b/toxygen/smileys/default/cm.png differ
diff --git a/toxygen/smileys/default/cn.png b/toxygen/smileys/default/cn.png
old mode 100644
new mode 100755
index a73f73b..8914414
Binary files a/toxygen/smileys/default/cn.png and b/toxygen/smileys/default/cn.png differ
diff --git a/toxygen/smileys/default/co.png b/toxygen/smileys/default/co.png
old mode 100644
new mode 100755
index 075ff39..a118ff4
Binary files a/toxygen/smileys/default/co.png and b/toxygen/smileys/default/co.png differ
diff --git a/toxygen/smileys/default/config.json b/toxygen/smileys/default/config.json
index 1b69dbc..020a529 100644
--- a/toxygen/smileys/default/config.json
+++ b/toxygen/smileys/default/config.json
@@ -1 +1 @@
-{ ":523:": "D83DDE12.png", ":)": "D83DDE0A.png", ":-*": "D83DDE1A.png", ":office:": "D83CDFE2.png", ":post_office:": "D83CDFE3.png", ":276:": "D83CDFE0.png", ":o": "D83DDE28.png", ":atm:": "D83CDFE7.png", ":european_post_office:": "D83CDFE4.png", ":hospital:": "D83CDFE5.png", ":convenience_store:": "D83CDFEA.png", ":school:": "D83CDFEB.png", ":hotel:": "D83CDFE8.png", ":love_hotel:": "D83CDFE9.png", ":izakaya_lantern:": "D83CDFEE.png", ":japanese_castle:": "D83CDFEF.png", ":department_store:": "D83CDFEC.png", ":factory:": "D83CDFED.png", ":|": "D83DDE10.png", ":yum:": "D83DDE0B.png", ":snowboarder:": "D83CDFC2.png", ":running:": "D83CDFC3.png", ":basketball:": "D83CDFC0.png", ":checkered_flag:": "D83CDFC1.png", ":trophy:": "D83CDFC6.png", ":horse_racing:": "D83CDFC7.png", ":surfer:": "D83CDFC4.png", ":D": "D83DDE03.png", ":football:": "D83CDFC8.png", ":rugby:": "D83CDFC9.png", ":]": "D83DDE0F.png", ":x": "D83DDE37.png", "⬜": "2B1C.png", ":vhs:": "D83DDCFC.png", ":233:": "D83DDCFA.png", ":212:": "D83DDCFB.png", "⬛": "2B1B.png", ":video_camera:": "D83DDCF9.png", ":signal_strength:": "D83DDCF6.png", ":camera:": "D83DDCF7.png", "➡": "27A1.png", ":no_mobile_phones:": "D83DDCF5.png", ":413:": "D83DDCF2.png", ":649:": "D83DDCF3.png", ":387:": "D83DDCF0.png", ":370:": "D83DDCF1.png", "📮": "D83DDCEE.png", ":110:": "D83DDCEF.png", ":mailbox:": "D83DDCEC.png", ":mailbox_closed:": "D83DDCED.png", ":mailbox_2:": "D83DDCEA.png", ":253:": "D83DDCEB.png", ":incoming_envelope:": "D83DDCE8.png", ":email:": "D83DDCE9.png", ":package:": "D83DDCE6.png", ":e-mail:": "D83DDCE7.png", ":445:": "D83DDCE4.png", ":inbox_tray:": "D83DDCE5.png", ":210:": "D83DDCE2.png", ":295:": "D83DDCE3.png", ":446:": "D83DDCE0.png", ":117:": "D83DDCBC.png", ":82:": "D83DDCDE.png", ":pager:": "D83DDCDF.png", ":scroll:": "D83DDCDC.png", ":memo:": "D83DDCDD.png", ":books:": "D83DDCDA.png", ":name_badge:": "D83DDCDB.png", ":blue_book:": "D83DDCD8.png", ":orange_book:": "D83DDCD9.png", ":book:": "D83DDCD6.png", ":green_book:": "D83DDCD7.png", ":notebook2:": "D83DDCD4.png", ":red_book:": "D83DDCD5.png", ":ledger:": "D83DDCD2.png", ":notebook:": "D83DDCD3.png", ":triangular_ruler:": "D83DDCD0.png", ":bookmark_tabs:": "D83DDCD1.png", ":paperclip:": "D83DDCCE.png", ":straight_ruler:": "D83DDCCF.png", ":pushpin:": "D83DDCCC.png", ":38:": "D83DDCCD.png", ":bar_chart:": "D83DDCCA.png", ":clipboard:": "D83DDCCB.png", ":chart_with_upwards_trend:": "D83DDCC8.png", ":chart_with_downwards_trend:": "D83DDCC9.png", ":calendar:": "D83DDCC6.png", ":card_index:": "D83DDCC7.png", ":page_facing_up:": "D83DDCC4.png", ":date:": "D83DDCC5.png", ":open_file_folder:": "D83DDCC2.png", ":page_with_curl:": "D83DDCC3.png", ":608:": "D83DDCC0.png", ":file_folder:": "D83DDCC1.png", ":265:": "D83DDEA1.png", ":527:": "D83DDEA0.png", ":rowboat:": "D83DDEA3.png", ":ship:": "D83DDEA2.png", ":traffic_light:": "D83DDEA5.png", ":speedboat:": "D83DDEA4.png", ":construction:": "D83DDEA7.png", ":v_traffic_light:": "D83DDEA6.png", ":triangular_flag_on_post:": "D83DDEA9.png", ":rotating_light:": "D83DDEA8.png", ":no_entry_sign:": "D83DDEAB.png", ":door:": "D83DDEAA.png", ":no_smoking:": "D83DDEAD.png", ":smoking:": "D83DDEAC.png", ":408:": "D83DDEAF.png", ":133:": "D83DDEAE.png", "◀": "25C0.png", ":potable_water:": "D83DDEB0.png", ":no_bicycles:": "D83DDEB3.png", ":bike:": "D83DDEB2.png", ":mountain_bicyclist:": "D83DDEB5.png", ":bicyclist:": "D83DDEB4.png", ":no_pedestrians:": "D83DDEB7.png", ":311:": "D83DDEB6.png", ":mens:": "D83DDEB9.png", ":children_crossing:": "D83DDEB8.png", ":restroom:": "D83DDEBB.png", ":womens:": "D83DDEBA.png", ":toilet:": "D83DDEBD.png", ":baby_symbol:": "D83DDEBC.png", ":shower:": "D83DDEBF.png", ":wc:": "D83DDEBE.png", ":helicopter:": "D83DDE81.png", ":rocket:": "D83DDE80.png", ":340:": "D83DDE83.png", ":steam_locomotive:": "D83DDE82.png", ":bullettrain_side:": "D83DDE85.png", ":391:": "D83DDE84.png", ":bullettrain_front:": "D83DDE87.png", ":train2:": "D83DDE86.png", ":station:": "D83DDE89.png", ":light_rail:": "D83DDE88.png", ":railway_car:": "D83DDE8B.png", ":tram:": "D83DDE8A.png", ":oncoming_bus:": "D83DDE8D.png", ":bus:": "D83DDE8C.png", ":busstop:": "D83DDE8F.png", ":trolleybus:": "D83DDE8E.png", ":ambulance:": "D83DDE91.png", ":minibus:": "D83DDE90.png", ":police_car:": "D83DDE93.png", ":pensive:": "D83DDE14.png", ":taxi:": "D83DDE95.png", ":oncoming_police_car:": "D83DDE94.png", ":car:": "D83DDE97.png", ":oncoming_taxi:": "D83DDE96.png", ":blue_car:": "D83DDE99.png", ":oncoming_automobile:": "D83DDE98.png", ":articulated_lorry:": "D83DDE9B.png", ":truck:": "D83DDE9A.png", ":monorail:": "D83DDE9D.png", ":tractor:": "D83DDE9C.png", ":suspension_railway:": "D83DDE9F.png", ":mountain_railway:": "D83DDE9E.png", ":538:": "D83DDCAF.png", ":571:": "D83DDE48.png", ":543:": "D83DDE49.png", ":168:": "D83DDE4A.png", ":raising_hand:": "D83DDE4B.png", ":raised_hands:": "D83DDE4C.png", ":639:": "D83DDE4D.png", ":357:": "D83DDE4E.png", ":pray:": "D83DDE4F.png", ":scream_cat:": "D83DDE40.png", ":no_good:": "D83DDE45.png", ":ok_woman:": "D83DDE46.png", ":bow:": "D83DDE47.png", ":non-potable_water:": "D83DDEB1.png", "⁉": "2049.png", ":mahjong:": "D83CDC04.png", "↪": "21AA.png", "↩": "21A9.png", "⌚": "231A.png", ":purple_heart:": "D83DDC9C.png", "↗": "2197.png", "↖": "2196.png", "↕": "2195.png", "↔": "2194.png", "↙": "2199.png", "↘": "2198.png", "⚾": "26BE.png", "⚽": "26BD.png", ":house_with_garden:": "D83CDFE1.png", "⚡": "26A1.png", "⚠": "26A0.png", "⚫": "26AB.png", ":rage:": "D83DDE21.png", "⚓": "2693.png", "0⃣": "003020E3.png", "1⃣": "003120E3.png", "2⃣": "003220E3.png", "3⃣": "003320E3.png", "4⃣": "003420E3.png", "5⃣": "003520E3.png", "6⃣": "003620E3.png", "7⃣": "003720E3.png", "8⃣": "003820E3.png", "9⃣": "003920E3.png", ":10:": "D83DDD1F.png", "⭐": "2B50.png", "⚪": "26AA.png", "⭕": "2B55.png", ":1234:": "D83DDD22.png", "Ⓜ": "24C2.png", ":european_castle:": "D83CDFF0.png", "⌛": "231B.png", "➗": "2797.png", ":((": "D83DDE29.png", ":high_heel:": "D83DDC60.png", ":swimmer:": "D83CDFCA.png", ":busts_in_silhouette:": "D83DDC65.png", "8-)": "D83DDE0D.png", "➕": "2795.png", "♈": "2648.png", ":two_women_holding_hands:": "D83DDC6D.png", ":424:": "D83DDCB9.png", ":money_with_wings:": "D83DDCB8.png", ":computer:": "D83DDCBB.png", ":348:": "D83DDCBA.png", ":minidisc:": "D83DDCBD.png", "8o": "D83DDE32.png", ":dvd:": "D83DDCBF.png", ":floppy_disc:": "D83DDCBE.png", ":59:": "D83DDCB1.png", ":moneybag:": "D83DDCB0.png", ":credit_card:": "D83DDCB3.png", ":$:": "D83DDCB2.png", ":dollar:": "D83DDCB5.png", "💴": "D83DDCB4.png", ":pound:": "D83DDCB7.png", ":yen:": "D83DDCB6.png", ":shit:": "D83DDCA9.png", ":dash:": "D83DDCA8.png", ":dizzy:": "D83DDCAB.png", ":muscle:": "D83DDCAA.png", ":thought_balloon:": "D83DDCAD.png", ":speech_balloon:": "D83DDCAC.png", "8|": "D83DDE33.png", ":28:": "D83DDCAE.png", ":bulb:": "D83DDCA1.png", ":534:": "D83DDCA0.png", ":bomb:": "D83DDCA3.png", ":297:": "D83DDCA2.png", ":boom:": "D83DDCA5.png", ":zzz:": "D83DDCA4.png", ":661:": "D83DDCA7.png", ":sweat_drops:": "D83DDCA6.png", ":blue_heart:": "D83DDC99.png", ":cupid:": "D83DDC98.png", ":yellow_heart:": "D83DDC9B.png", ":green_heart:": "D83DDC9A.png", ":56:": "D83DDC9D.png", ":32:": "D83DDC9F.png", ":revolving_hearts:": "D83DDC9E.png", ":couple_with_heart:": "D83DDC91.png", ":590:": "D83DDC90.png", ":336:": "D83DDC93.png", ":wedding:": "D83DDC92.png", ":247:": "D83DDC95.png", ":broken_heart:": "D83DDC94.png", ":heartpulse:": "D83DDC97.png", ":sparkling_heart:": "D83DDC96.png", ":syringe:": "D83DDC89.png", ":420:": "D83DDC88.png", ":kiss:": "D83DDC8B.png", ":pill:": "D83DDC8A.png", ":ring:": "D83DDC8D.png", ":383:": "D83DDC8C.png", "💏": "D83DDC8F.png", ":gem:": "D83DDC8E.png", ":information_desk_person:": "D83DDC81.png", ":skull:": "D83DDC80.png", ":dancer:": "D83DDC83.png", ":guardsman:": "D83DDC82.png", ":nail_care:": "D83DDC85.png", ":lipstick:": "D83DDC84.png", ":haircut:": "D83DDC87.png", ":massage:": "D83DDC86.png", ":bride_with_veil:": "D83DDC70.png", ":person_with_blond_hair:": "D83DDC71.png", ":man_with_gua_pi_mao:": "D83DDC72.png", ":man_with_turban:": "D83DDC73.png", ":older_man:": "D83DDC74.png", ":older_woman:": "D83DDC75.png", ":baby:": "D83DDC76.png", ":construction_worker:": "D83DDC77.png", ":princess:": "D83DDC78.png", ":japanese_ogre:": "D83DDC79.png", ":japanese_goblin:": "D83DDC7A.png", ":ghost:": "D83DDC7B.png", ":angel:": "D83DDC7C.png", ":alien:": "D83DDC7D.png", ":249:": "D83DDC7E.png", ":imp:": "D83DDC7F.png", "=(": "D83DDE20.png", ":sandal:": "D83DDC61.png", ":boot:": "D83DDC62.png", ":feet:": "D83DDC63.png", ":bust_in_silhouette:": "D83DDC64.png", ":mans_shoe:": "D83DDC5E.png", "👦": "D83DDC66.png", ":332:": "D83DDC67.png", ":man:": "D83DDC68.png", ":woman:": "D83DDC69.png", ":family:": "D83DDC6A.png", ":couple:": "D83DDC6B.png", ":two_men_holding_hands:": "D83DDC6C.png", ":point_up:": "261D.png", ":cop:": "D83DDC6E.png", ":290:": "D83DDC6F.png", ":open_hands:": "D83DDC50.png", ":crown:": "D83DDC51.png", ":womans_hat:": "D83DDC52.png", ":eyeglasses:": "D83DDC53.png", ":necktie:": "D83DDC54.png", ":shirt:": "D83DDC55.png", ":jeans:": "D83DDC56.png", ":dress:": "D83DDC57.png", ":kimono:": "D83DDC58.png", ":bikini:": "D83DDC59.png", ":womans_clothes:": "D83DDC5A.png", ":purse:": "D83DDC5B.png", ":handbag:": "D83DDC5C.png", ":pouch:": "D83DDC5D.png", "xD": "D83DDE06.png", ":shoe:": "D83DDC5F.png", ":eyes:": "D83DDC40.png", ":ear:": "D83DDC42.png", ":nose:": "D83DDC43.png", ":lips:": "D83DDC44.png", ":tongue:": "D83DDC45.png", ":point_up_2:": "D83DDC46.png", ":point_down:": "D83DDC47.png", ":point_left:": "D83DDC48.png", ":point_right:": "D83DDC49.png", ":facepunch:": "D83DDC4A.png", ":wave:": "D83DDC4B.png", ":ok:": "D83DDC4C.png", ":like:": "D83DDC4D.png", ":dislike:": "D83DDC4E.png", ":+1:": "D83DDC4D.png", ":-1:": "D83DDC4E.png", ":clap:": "D83DDC4F.png", ":^D": "D83DDE1B.png", ":3": "D83DDE19.png", ":kissing_heart:": "D83DDE18.png", ":((": "D83DDE1F.png", ":(": "D83DDE1E.png", ":stuck_out_tongue_closed_eyes:": "D83DDE1D.png", ":stuck_out_tongue_winking_eye:": "D83DDE1C.png", ":sweat:": "D83DDE13.png", ":593:": "D83DDE11.png", ":^)": "D83DDE17.png", ":confounded:": "D83DDE16.png", ":\\": "D83DDE15.png", ";-)": "D83DDE09.png", ":smiling_imp:": "D83DDE08.png", "B)": "D83DDE0E.png", "3-)": "D83DDE0C.png", ":*(": "D83DDE02.png", "=)": "D83DDE01.png", ":smile:": "D83DDE00.png", ":610:": "D83DDE07.png", ":sweat_smile:": "D83DDE05.png", ":smile2:": "D83DDE04.png", ":heart_eyes_cat:": "D83DDE3B.png", ":smile_cat:": "D83DDE3A.png", ":joy_cat:": "D83DDE39.png", ":smiley_cat:": "D83DDE38.png", ":crying_cat_face:": "D83DDE3F.png", ":pouting_cat:": "D83DDE3E.png", ":kissing_cat:": "D83DDE3D.png", ":smirk_cat:": "D83DDE3C.png", ":scream:": "D83DDE31.png", ";o": "D83DDE30.png", ":no_mouth:": "D83DDE36.png", ":dizzy_face:": "D83DDE35.png", ":sleeping:": "D83DDE34.png", ":tired_face:": "D83DDE2B.png", ":389:": "D83DDE2A.png", ":0": "D83DDE2F.png", ":O": "D83DDE2E.png", ":sob:": "D83DDE2D.png", ":p": "D83DDE2C.png", ":58:": "D83DDE23.png", ":'(": "D83DDE22.png", ":206:": "D83DDE27.png", ":622:": "D83DDE26.png", ":461:": "D83DDE25.png", ":134:": "D83DDE24.png", ":25:": "D83CDFAD.png", ":244:": "D83CDFAC.png", ":darts:": "D83CDFAF.png", ":149:": "D83CDFAE.png", ":tophat:": "D83CDFA9.png", ":art:": "D83CDFA8.png", ":113:": "D83CDFAB.png", ":circus_tent:": "D83CDFAA.png", ":movie_camera:": "D83CDFA5.png", ":microphone:": "D83CDFA4.png", ":219:": "D83CDFA7.png", ":313:": "D83CDFA6.png", ":ferris_wheel:": "D83CDFA1.png", ":carousel_horse:": "D83CDFA0.png", ":637:": "D83CDFA3.png", ":roller_coaster:": "D83CDFA2.png", ":119:": "D83CDFBD.png", ":129:": "D83CDFBC.png", ":127:": "D83CDFBF.png", ":tennis:": "D83CDFBE.png", ":musical_keyboard:": "D83CDFB9.png", ":guitar:": "D83CDFB8.png", ":violin:": "D83CDFBB.png", ":trumpet:": "D83CDFBA.png", ":647:": "D83CDFB5.png", ":43:": "D83CDFB4.png", ":saxophone:": "D83CDFB7.png", ":85:": "D83CDFB6.png", ":8ball:": "D83CDFB1.png", ":565:": "D83CDFB0.png", ":bowling:": "D83CDFB3.png", ":game_die:": "D83CDFB2.png", ":245:": "D83CDF8D.png", ":crossed_flags:": "D83CDF8C.png", ":lags:": "D83CDF8F.png", ":dolls:": "D83CDF8E.png", ":tada:": "D83CDF89.png", ":balloon:": "D83CDF88.png", ":tanabata_tree:": "D83CDF8B.png", ":confetti_ball:": "D83CDF8A.png", ":santa:": "D83CDF85.png", ":christmas_tree:": "D83CDF84.png", ":sparkler:": "D83CDF87.png", ":fireworks:": "D83CDF86.png", ":gift:": "D83CDF81.png", ":42:": "D83CDF80.png", ":jack_o_lantern:": "D83CDF83.png", ":3:": "D83CDF82.png", ":444:": "D83DDCE1.png", ":61:": "D83CDF91.png", ":386:": "D83CDF90.png", ":440:": "D83CDF93.png", ":school_satchel:": "D83CDF92.png", "⛵": "26F5.png", "⛲": "26F2.png", "⛳": "26F3.png", "⛽": "26FD.png", "⛺": "26FA.png", "⛪": "26EA.png", "⛔": "26D4.png", "⛄": "26C4.png", "⛅": "26C5.png", "⛎": "26CE.png", "✨": "2728.png", "✴": "2734.png", "✳": "2733.png", "✌": "270C.png", "✏": "270F.png", "✉": "2709.png", "✈": "2708.png", "✋": "270B.png", "✊": "270A.png", "✅": "2705.png", "✂": "2702.png", "✔": "2714.png", "✖": "2716.png", "✒": "2712.png", ":crystal_ball:": "D83DDD2E.png", ":telescope:": "D83DDD2D.png", ":microscope:": "D83DDD2C.png", ":gun:": "D83DDD2B.png", ":hocho:": "D83DDD2A.png", ":63:": "D83DDD29.png", ":504:": "D83DDD28.png", ":301:": "D83DDD27.png", ":flashlight:": "D83DDD26.png", ":fire:": "D83DDD25.png", ":abc:": "D83DDD24.png", ":274:": "D83DDD23.png", ":abcd:": "D83DDD21.png", ":ABCD:": "D83DDD20.png", ":down:": "D83DDD3D.png", ":up:": "D83DDD3C.png", ":144:": "D83DDD3B.png", ":371:": "D83DDD3A.png", ":46:": "D83DDD39.png", ":240:": "D83DDD38.png", ":48:": "D83DDD37.png", ":298:": "D83DDD36.png", ":29:": "D83DDD35.png", ":250:": "D83DDD34.png", ":white_square_button:": "D83DDD33.png", ":black_square_button:": "D83DDD32.png", ":trident:": "D83DDD31.png", ":280:": "D83DDD0F.png", ":585:": "D83DDD0E.png", ":204:": "D83DDD0D.png", ":electric_plug:": "D83DDD0C.png", ":battery:": "D83DDD0B.png", ":speaker:": "D83DDD09.png", ":nosound:": "D83DDD07.png", ":low_brightness:": "D83DDD06.png", ":397:": "D83DDD05.png", ":268:": "D83DDD04.png", ":597:": "D83DDD03.png", ":584:": "D83DDD02.png", ":517:": "D83DDD01.png", ":374:": "D83DDD00.png", ":underage:": "D83DDD1E.png", ":173:": "D83DDD1D.png", ":soon:": "D83DDD1C.png", ":on:": "D83DDD1B.png", ":end:": "D83DDD1A.png", ":back:": "D83DDD19.png", ":583:": "D83DDD18.png", ":230:": "D83DDD17.png", ":629:": "D83DDD16.png", ":163:": "D83DDD15.png", ":426:": "D83DDD14.png", ":lock:": "D83DDD13.png", ":232:": "D83DDD12.png", ":key:": "D83DDD11.png", ":328:": "D83DDD10.png", ":black_joker:": "D83CDCCF.png", "©": "00A9.png", ":174:": "D83CDF64.png", ":235:": "D83CDF65.png", ":132:": "D83CDF66.png", ":417:": "D83CDF67.png", ":524:": "D83CDF60.png", ":105:": "D83CDF61.png", ":229:": "D83CDF62.png", ":50:": "D83CDF63.png", ":499:": "D83CDF6C.png", ":415:": "D83CDF6D.png", ":467:": "D83CDF6E.png", ":546:": "D83CDF6F.png", ":74:": "D83CDF68.png", ":306:": "D83CDF69.png", ":363:": "D83CDF6A.png", ":148:": "D83CDF6B.png", ":654:": "D83CDF74.png", ":251:": "D83CDF75.png", ":641:": "D83CDF76.png", ":439:": "D83CDF77.png", ":320:": "D83CDF70.png", ":44:": "D83CDF71.png", ":291:": "D83CDF72.png", ":495:": "D83CDF73.png", ":554:": "D83CDF7C.png", ":513:": "D83CDF78.png", ":384:": "D83CDF79.png", ":9:": "D83CDF7A.png", ":90:": "D83CDF7B.png", ":522:": "D83CDF44.png", ":498:": "D83CDF45.png", ":266:": "D83CDF46.png", ":16:": "D83CDF47.png", ":152:": "D83CDF40.png", ":560:": "D83CDF41.png", ":454:": "D83CDF42.png", ":302:": "D83CDF43.png", ":483:": "D83CDF4C.png", ":664:": "D83CDF4D.png", ":96:": "D83CDF4E.png", ":663:": "D83CDF4F.png", ":493:": "D83CDF48.png", ":539:": "D83CDF49.png", ":270:": "D83CDF4A.png", ":643:": "D83CDF4B.png", ":17:": "D83CDF54.png", ":343:": "D83CDF55.png", ":399:": "D83CDF56.png", ":450:": "D83CDF57.png", ":293:": "D83CDF50.png", ":184:": "D83CDF51.png", ":47:": "D83CDF52.png", ":403:": "D83CDF53.png", ":400:": "D83CDF5C.png", ":410:": "D83CDF5D.png", ":322:": "D83CDF5E.png", ":482:": "D83CDF5F.png", ":143:": "D83CDF58.png", ":542:": "D83CDF59.png", ":588:": "D83CDF5A.png", ":529:": "D83CDF5B.png", "☺": "263A.png", ":111:": "D83CDF08.png", "☑": "2611.png", "☕": "2615.png", ":22:": "D83CDF15.png", "☎": "260E.png", "☁": "2601.png", "☀": "2600.png", ":14:": "D83DDC33.png", ":409:": "D83DDC32.png", ":382:": "D83DDC31.png", ":460:": "D83DDC30.png", ":484:": "D83DDC37.png", ":288:": "D83DDC36.png", ":594:": "D83DDC35.png", ":591:": "D83DDC34.png", ":372:": "D83DDC3B.png", ":563:": "D83DDC3A.png", ":596:": "D83DDC39.png", ":595:": "D83DDC38.png", ":367:": "D83DDC3E.png", ":376:": "D83DDC3D.png", ":305:": "D83DDC3C.png", ":281:": "D83DDC23.png", ":452:": "D83DDC22.png", ":388:": "D83DDC21.png", ":39:": "D83DDC20.png", ":36:": "D83DDC27.png", ":189:": "D83DDC26.png", ":500:": "D83DDC25.png", ":277:": "D83DDC24.png", ":459:": "D83DDC2B.png", ":486:": "D83DDC2A.png", ":662:": "D83DDC28.png", ":509:": "D83DDC2F.png", ":365:": "D83DDC2E.png", ":34:": "D83DDC2D.png", ":319:": "D83DDC2C.png", ":118:": "D83DDC13.png", ":463:": "D83DDC12.png", ":220:": "D83DDC11.png", ":653:": "D83DDC10.png", ":599:": "D83DDC17.png", ":603:": "D83DDC16.png", ":489:": "D83DDC15.png", ":552:": "D83DDC14.png", ":181:": "D83DDC1B.png", ":208:": "D83DDC1A.png", ":19:": "D83DDC19.png", ":580:": "D83DDC18.png", ":398:": "D83DDC1F.png", ":310:": "D83DDC1E.png", ":4:": "D83DDC1D.png", ":564:": "D83DDC1C.png", ":135:": "D83DDC03.png", ":566:": "D83DDC02.png", ":211:": "D83DDC01.png", ":631:": "D83DDC00.png", ":496:": "D83DDC07.png", ":263:": "D83DDC06.png", ":447:": "D83DDC05.png", ":283:": "D83DDC04.png", ":607:": "D83DDC0B.png", ":23:": "D83DDC0A.png", ":634:": "D83DDC09.png", ":435:": "D83DDC08.png", ":349:": "D83DDC0F.png", ":53:": "D83DDC0E.png", ":101:": "D83DDC0D.png", ":567:": "D83DDC0C.png", "◻": "25FB.png", "❤": "2764.png", "◼": "25FC.png", "◽": "25FD.png", "◾": "25FE.png", "➿": "27BF.png", "⬅": "2B05.png", ":531:": "D83DDE92.png", ":116:": "D83CDD8E.png", ":466:": "D83CDD95.png", ":545:": "D83CDD94.png", ":202:": "D83CDD97.png", ":100:": "D83CDD96.png", ":CL:": "D83CDD91.png", ":free:": "D83CDD93.png", ":cool:": "D83CDD92.png", ":UP!:": "D83CDD99.png", ":SOS:": "D83CDD98.png", ":VS:": "D83CDD9A.png", ":525:": "D83CDF20.png", ":614:": "D83CDF37.png", ":13:": "D83CDF35.png", ":108:": "D83CDF34.png", ":289:": "D83CDF33.png", ":390:": "D83CDF32.png", ":222:": "D83CDF31.png", ":92:": "D83CDF30.png", ":205:": "D83CDF3F.png", ":7:": "D83CDF3E.png", ":142:": "D83CDF3D.png", ":536:": "D83CDF3C.png", ":153:": "D83CDF3B.png", ":307:": "D83CDF3A.png", ":304:": "D83CDF39.png", ":18:": "D83CDF38.png", ":558:": "D83CDF07.png", ":71:": "D83CDF06.png", ":421:": "D83CDF05.png", ":485:": "D83CDF04.png", ":223:": "D83CDF03.png", ":269:": "D83CDF02.png", ":574:": "D83CDF01.png", ":124:": "D83CDF00.png", ":65:": "D83CDF0F.png", ":15:": "D83CDF0E.png", ":411:": "D83CDF0D.png", ":272:": "D83CDF0C.png", ":335:": "D83CDF0B.png", ":379:": "D83CDF0A.png", ":192:": "D83CDF09.png", ":359:": "D83CDF17.png", ":436:": "D83CDF16.png", ":479:": "D83CDF14.png", ":231:": "D83CDF13.png", ":528:": "D83CDF12.png", ":341:": "D83CDF11.png", ":491:": "D83CDF10.png", ":520:": "D83CDF1F.png", ":185:": "D83CDF1E.png", ":568:": "D83CDF1D.png", ":40:": "D83CDF1C.png", ":393:": "D83CDF1B.png", ":275:": "D83CDF1A.png", ":72:": "D83CDF19.png", ":246:": "D83CDF18.png", "❓": "2753.png", "❗": "2757.png", "❔": "2754.png", "❕": "2755.png", "➰": "27B0.png", "❄": "2744.png", "❎": "274E.png", "❌": "274C.png", "⏰": "23F0.png", "⏳": "23F3.png", "⏩": "23E9.png", "⏪": "23EA.png", "⏫": "23EB.png", "⏬": "23EC.png", ":106:": "D83DDEC4.png", ":465:": "D83DDEC2.png", "▶": "25B6.png", ":236:": "D83DDEC0.png", ":271:": "D83DDEC1.png", "⤵": "2935.png", "⤴": "2934.png", "▫": "25AB.png", "▪": "25AA.png", "❇": "2747.png", ":510:": "D83CDD7E.png", ":480:": "D83CDD7F.png", "〽": "303D.png", "™": "2122.png", "〰": "3030.png", ":351:": "D83CDD70.png", ":237:": "D83CDD71.png", "ℹ": "2139.png", "☔": "2614.png", "⬇": "2B07.png", "➖": "2796.png", ":358:": "D83CDE01.png", "⬆": "2B06.png", "♿": "267F.png", "♻": "267B.png", "♨": "2668.png", "♦": "2666.png", "♥": "2665.png", "♣": "2663.png", "♠": "2660.png", "®": "00AE.png", "♒": "2652.png", "♓": "2653.png", "♐": "2650.png", "♑": "2651.png", "♎": "264E.png", "♏": "264F.png", "♌": "264C.png", "♍": "264D.png", "♊": "264A.png", "♋": "264B.png", ":592:": "D83DDCF4.png", "♉": "2649.png", ":472:": "D83DDDFB.png", ":84:": "D83DDDFE.png", ":254:": "D83DDDFF.png", ":316:": "D83DDDFC.png", ":226:": "D83DDDFD.png", "‼": "203C.png", ":621:": "D83DDEC5.png", ":140:": "D83DDEC3.png", ":tox:": "tox.png", ":wtox:": "wtox.png", ":td:": "td.png", ":cr:": "cr.png", ":gd:": "gd.png", ":si:": "si.png", ":ni:": "ni.png", ":gs:": "gs.png", ":er:": "er.png", ":pg:": "pg.png", ":jp:": "jp.png", ":hu:": "hu.png", ":is:": "is.png", ":ro:": "ro.png", ":europeanunion:": "europeanunion.png", ":bj:": "bj.png", ":mp:": "mp.png", ":tl:": "tl.png", ":gl:": "gl.png", ":sa:": "sa.png", ":na:": "na.png", ":as:": "as.png", ":lt:": "lt.png", ":za:": "za.png", ":bb:": "bb.png", ":mx:": "mx.png", ":np:": "np.png", ":ru:": "ru.png", ":vg:": "vg.png", ":sy:": "sy.png", ":fam:": "fam.png", ":bo:": "bo.png", ":um:": "um.png", ":fr:": "fr.png", ":sh:": "sh.png", ":gr:": "gr.png", ":nu:": "nu.png", ":mo:": "mo.png", ":ir:": "ir.png", ":de:": "de.png", ":tt:": "tt.png", ":lb:": "lb.png", ":ci:": "ci.png", ":tm:": "tm.png", ":pl:": "pl.png", ":vi:": "vi.png", ":hr:": "hr.png", ":ar:": "ar.png", ":bz:": "bz.png", ":ae:": "ae.png", ":lu:": "lu.png", ":cz:": "cz.png", ":dm:": "dm.png", ":ca:": "ca.png", ":yt:": "yt.png", ":va:": "va.png", ":ee:": "ee.png", ":pt:": "pt.png", ":az:": "az.png", ":br:": "br.png", ":dk:": "dk.png", ":am:": "am.png", ":tw:": "tw.png", ":gq:": "gq.png", ":et:": "et.png", ":pe:": "pe.png", ":pr:": "pr.png", ":kr:": "kr.png", ":st:": "st.png", ":ki:": "ki.png", ":bt:": "bt.png", ":mr:": "mr.png", ":nz:": "nz.png", ":lc:": "lc.png", ":mm:": "mm.png", ":ch:": "ch.png", ":gb:": "gb.png", ":so:": "so.png", ":nc:": "nc.png", ":gy:": "gy.png", ":scotland:": "scotland.png", ":pm:": "pm.png", ":ug:": "ug.png", ":sv:": "sv.png", ":gf:": "gf.png", ":kz:": "kz.png", ":lr:": "lr.png", ":cy:": "cy.png", ":ad:": "ad.png", ":mz:": "mz.png", ":fj:": "fj.png", ":sg:": "sg.png", ":au:": "au.png", ":bs:": "bs.png", ":england:": "england.png", ":al:": "al.png", ":bd:": "bd.png", ":fi:": "fi.png", ":md:": "md.png", ":rs:": "rs.png", ":ps:": "ps.png", ":uy:": "uy.png", ":tr:": "tr.png", ":kh:": "kh.png", ":zm:": "zm.png", ":ga:": "ga.png", ":ml:": "ml.png", ":hk:": "hk.png", ":ky:": "ky.png", ":it:": "it.png", ":cn:": "cn.png", ":ag:": "ag.png", ":ls:": "ls.png", ":tz:": "tz.png", ":wales:": "wales.png", ":bm:": "bm.png", ":co:": "co.png", ":tk:": "tk.png", ":gi:": "gi.png", ":eg:": "eg.png", ":ie:": "ie.png", ":at:": "at.png", ":mc:": "mc.png", ":by:": "by.png", ":ao:": "ao.png", ":lk:": "lk.png", ":me:": "me.png", ":be:": "be.png", ":cg:": "cg.png", ":sn:": "sn.png", ":kp:": "kp.png", ":mq:": "mq.png", ":kg:": "kg.png", ":bv:": "bv.png", ":mt:": "mt.png", ":qa:": "qa.png", ":la:": "la.png", ":th:": "th.png", ":cv:": "cv.png", ":id:": "id.png", ":sm:": "sm.png", ":ne:": "ne.png", ":il:": "il.png", ":bi:": "bi.png", ":jm:": "jm.png", ":af:": "af.png", ":bn:": "bn.png", ":ma:": "ma.png", ":gh:": "gh.png", ":se:": "se.png", ":pk:": "pk.png", ":ua:": "ua.png", ":to:": "to.png", ":aw:": "aw.png", ":gt:": "gt.png", ":mk:": "mk.png", ":an:": "an.png", ":bf:": "bf.png", ":tc:": "tc.png", ":nl:": "nl.png", ":cf:": "cf.png", ":gp:": "gp.png", ":ly:": "ly.png", ":mw:": "mw.png", ":bw:": "bw.png", ":cu:": "cu.png", ":mn:": "mn.png", ":nf:": "nf.png", ":sl:": "sl.png", ":io:": "io.png", ":cx:": "cx.png", ":kw:": "kw.png", ":py:": "py.png", ":us:": "us.png", ":pa:": "pa.png", ":kn:": "kn.png", ":zw:": "zw.png", ":cm:": "cm.png", ":fm:": "fm.png", ":sd:": "sd.png", ":ph:": "ph.png", ":fk:": "fk.png", ":tj:": "tj.png", ":ai:": "ai.png", ":li:": "li.png", ":mg:": "mg.png", ":bg:": "bg.png", ":ms:": "ms.png", ":gw:": "gw.png", ":vc:": "vc.png", ":hn:": "hn.png", ":ke:": "ke.png", ":ax:": "ax.png", ":mv:": "mv.png", ":tf:": "tf.png", ":vu:": "vu.png", ":sk:": "sk.png", ":ng:": "ng.png", ":vn:": "vn.png", ":in:": "in.png", ":sr:": "sr.png", ":iq:": "iq.png", ":hm:": "hm.png", ":pw:": "pw.png", ":ws:": "ws.png", ":jo:": "jo.png", ":km:": "km.png", ":bh:": "bh.png", ":tn:": "tn.png", ":cl:": "cl.png", ":gn:": "gn.png", ":sc:": "sc.png", ":eh:": "eh.png", ":ba:": "ba.png", ":re:": "re.png", ":lv:": "lv.png", ":cd:": "cd.png", ":ve:": "ve.png", ":om:": "om.png", ":cs:": "cs.png", ":ge:": "ge.png", ":tg:": "tg.png", ":es:": "es.png", ":sj:": "sj.png", ":pf:": "pf.png", ":ht:": "ht.png", ":dj:": "dj.png", ":ec:": "ec.png", ":tv:": "tv.png", ":wf:": "wf.png", ":catalonia:": "catalonia.png", ":ck:": "ck.png", ":fo:": "fo.png", ":gm:": "gm.png", ":mh:": "mh.png", ":ye:": "ye.png", ":sb:": "sb.png", ":pn:": "pn.png", ":mu:": "mu.png", ":do:": "do.png", ":my:": "my.png", ":nr:": "nr.png", ":cc:": "cc.png", ":no:": "no.png", ":gu:": "gu.png", ":rw:": "rw.png", ":dz:": "dz.png", ":sz:": "sz.png", ":uz:": "uz.png" }
+{ ":523:": "D83DDE12.png", ":)": "D83DDE0A.png", ":-*": "D83DDE1A.png", ":office:": "D83CDFE2.png", ":post_office:": "D83CDFE3.png", ":276:": "D83CDFE0.png", ":o": "D83DDE28.png", ":atm:": "D83CDFE7.png", ":european_post_office:": "D83CDFE4.png", ":hospital:": "D83CDFE5.png", ":convenience_store:": "D83CDFEA.png", ":school:": "D83CDFEB.png", ":hotel:": "D83CDFE8.png", ":love_hotel:": "D83CDFE9.png", ":izakaya_lantern:": "D83CDFEE.png", ":japanese_castle:": "D83CDFEF.png", ":department_store:": "D83CDFEC.png", ":factory:": "D83CDFED.png", ":|": "D83DDE10.png", ":yum:": "D83DDE0B.png", ":snowboarder:": "D83CDFC2.png", ":running:": "D83CDFC3.png", ":basketball:": "D83CDFC0.png", ":checkered_flag:": "D83CDFC1.png", ":trophy:": "D83CDFC6.png", ":horse_racing:": "D83CDFC7.png", ":surfer:": "D83CDFC4.png", ":D": "D83DDE03.png", ":football:": "D83CDFC8.png", ":rugby:": "D83CDFC9.png", ":]": "D83DDE0F.png", ":x": "D83DDE37.png", "⬜": "2B1C.png", ":vhs:": "D83DDCFC.png", ":233:": "D83DDCFA.png", ":212:": "D83DDCFB.png", "⬛": "2B1B.png", ":video_camera:": "D83DDCF9.png", ":signal_strength:": "D83DDCF6.png", ":camera:": "D83DDCF7.png", "➡": "27A1.png", ":no_mobile_phones:": "D83DDCF5.png", ":413:": "D83DDCF2.png", ":649:": "D83DDCF3.png", ":387:": "D83DDCF0.png", ":370:": "D83DDCF1.png", "📮": "D83DDCEE.png", ":110:": "D83DDCEF.png", ":mailbox:": "D83DDCEC.png", ":mailbox_closed:": "D83DDCED.png", ":mailbox_2:": "D83DDCEA.png", ":253:": "D83DDCEB.png", ":incoming_envelope:": "D83DDCE8.png", ":email:": "D83DDCE9.png", ":package:": "D83DDCE6.png", ":e-mail:": "D83DDCE7.png", ":445:": "D83DDCE4.png", ":inbox_tray:": "D83DDCE5.png", ":210:": "D83DDCE2.png", ":295:": "D83DDCE3.png", ":446:": "D83DDCE0.png", ":117:": "D83DDCBC.png", ":82:": "D83DDCDE.png", ":pager:": "D83DDCDF.png", ":scroll:": "D83DDCDC.png", ":memo:": "D83DDCDD.png", ":books:": "D83DDCDA.png", ":name_badge:": "D83DDCDB.png", ":blue_book:": "D83DDCD8.png", ":orange_book:": "D83DDCD9.png", ":book:": "D83DDCD6.png", ":green_book:": "D83DDCD7.png", ":notebook2:": "D83DDCD4.png", ":red_book:": "D83DDCD5.png", ":ledger:": "D83DDCD2.png", ":notebook:": "D83DDCD3.png", ":triangular_ruler:": "D83DDCD0.png", ":bookmark_tabs:": "D83DDCD1.png", ":paperclip:": "D83DDCCE.png", ":straight_ruler:": "D83DDCCF.png", ":pushpin:": "D83DDCCC.png", ":38:": "D83DDCCD.png", ":bar_chart:": "D83DDCCA.png", ":clipboard:": "D83DDCCB.png", ":chart_with_upwards_trend:": "D83DDCC8.png", ":chart_with_downwards_trend:": "D83DDCC9.png", ":calendar:": "D83DDCC6.png", ":card_index:": "D83DDCC7.png", ":page_facing_up:": "D83DDCC4.png", ":date:": "D83DDCC5.png", ":open_file_folder:": "D83DDCC2.png", ":page_with_curl:": "D83DDCC3.png", ":608:": "D83DDCC0.png", ":file_folder:": "D83DDCC1.png", ":265:": "D83DDEA1.png", ":527:": "D83DDEA0.png", ":rowboat:": "D83DDEA3.png", ":ship:": "D83DDEA2.png", ":traffic_light:": "D83DDEA5.png", ":speedboat:": "D83DDEA4.png", ":construction:": "D83DDEA7.png", ":v_traffic_light:": "D83DDEA6.png", ":triangular_flag_on_post:": "D83DDEA9.png", ":rotating_light:": "D83DDEA8.png", ":no_entry_sign:": "D83DDEAB.png", ":door:": "D83DDEAA.png", ":no_smoking:": "D83DDEAD.png", ":smoking:": "D83DDEAC.png", ":408:": "D83DDEAF.png", ":133:": "D83DDEAE.png", "◀": "25C0.png", ":potable_water:": "D83DDEB0.png", ":no_bicycles:": "D83DDEB3.png", ":bike:": "D83DDEB2.png", ":mountain_bicyclist:": "D83DDEB5.png", ":bicyclist:": "D83DDEB4.png", ":no_pedestrians:": "D83DDEB7.png", ":311:": "D83DDEB6.png", ":mens:": "D83DDEB9.png", ":children_crossing:": "D83DDEB8.png", ":restroom:": "D83DDEBB.png", ":womens:": "D83DDEBA.png", ":toilet:": "D83DDEBD.png", ":baby_symbol:": "D83DDEBC.png", ":shower:": "D83DDEBF.png", ":wc:": "D83DDEBE.png", ":helicopter:": "D83DDE81.png", ":rocket:": "D83DDE80.png", ":340:": "D83DDE83.png", ":steam_locomotive:": "D83DDE82.png", ":bullettrain_side:": "D83DDE85.png", ":391:": "D83DDE84.png", ":bullettrain_front:": "D83DDE87.png", ":train2:": "D83DDE86.png", ":station:": "D83DDE89.png", ":light_rail:": "D83DDE88.png", ":railway_car:": "D83DDE8B.png", ":tram:": "D83DDE8A.png", ":oncoming_bus:": "D83DDE8D.png", ":bus:": "D83DDE8C.png", ":busstop:": "D83DDE8F.png", ":trolleybus:": "D83DDE8E.png", ":ambulance:": "D83DDE91.png", ":minibus:": "D83DDE90.png", ":police_car:": "D83DDE93.png", ":pensive:": "D83DDE14.png", ":taxi:": "D83DDE95.png", ":oncoming_police_car:": "D83DDE94.png", ":car:": "D83DDE97.png", ":oncoming_taxi:": "D83DDE96.png", ":blue_car:": "D83DDE99.png", ":oncoming_automobile:": "D83DDE98.png", ":articulated_lorry:": "D83DDE9B.png", ":truck:": "D83DDE9A.png", ":monorail:": "D83DDE9D.png", ":tractor:": "D83DDE9C.png", ":suspension_railway:": "D83DDE9F.png", ":mountain_railway:": "D83DDE9E.png", ":538:": "D83DDCAF.png", ":571:": "D83DDE48.png", ":543:": "D83DDE49.png", ":168:": "D83DDE4A.png", ":raising_hand:": "D83DDE4B.png", ":raised_hands:": "D83DDE4C.png", ":639:": "D83DDE4D.png", ":357:": "D83DDE4E.png", ":pray:": "D83DDE4F.png", ":scream_cat:": "D83DDE40.png", ":no_good:": "D83DDE45.png", ":ok_woman:": "D83DDE46.png", ":bow:": "D83DDE47.png", ":non-potable_water:": "D83DDEB1.png", "⁉": "2049.png", ":mahjong:": "D83CDC04.png", "↪": "21AA.png", "↩": "21A9.png", "⌚": "231A.png", ":purple_heart:": "D83DDC9C.png", "↗": "2197.png", "↖": "2196.png", "↕": "2195.png", "↔": "2194.png", "↙": "2199.png", "↘": "2198.png", "⚾": "26BE.png", "⚽": "26BD.png", ":house_with_garden:": "D83CDFE1.png", "⚡": "26A1.png", "⚠": "26A0.png", "⚫": "26AB.png", ":rage:": "D83DDE21.png", "⚓": "2693.png", "0⃣": "003020E3.png", "1⃣": "003120E3.png", "2⃣": "003220E3.png", "3⃣": "003320E3.png", "4⃣": "003420E3.png", "5⃣": "003520E3.png", "6⃣": "003620E3.png", "7⃣": "003720E3.png", "8⃣": "003820E3.png", "9⃣": "003920E3.png", ":10:": "D83DDD1F.png", "⭐": "2B50.png", "⚪": "26AA.png", "⭕": "2B55.png", ":1234:": "D83DDD22.png", "Ⓜ": "24C2.png", ":european_castle:": "D83CDFF0.png", "⌛": "231B.png", "➗": "2797.png", ":((": "D83DDE29.png", ":high_heel:": "D83DDC60.png", ":swimmer:": "D83CDFCA.png", ":busts_in_silhouette:": "D83DDC65.png", "8-)": "D83DDE0D.png", "➕": "2795.png", "♈": "2648.png", ":two_women_holding_hands:": "D83DDC6D.png", ":424:": "D83DDCB9.png", ":money_with_wings:": "D83DDCB8.png", ":computer:": "D83DDCBB.png", ":348:": "D83DDCBA.png", ":minidisc:": "D83DDCBD.png", "8o": "D83DDE32.png", ":dvd:": "D83DDCBF.png", ":floppy_disc:": "D83DDCBE.png", ":59:": "D83DDCB1.png", ":moneybag:": "D83DDCB0.png", ":credit_card:": "D83DDCB3.png", ":$:": "D83DDCB2.png", ":dollar:": "D83DDCB5.png", "💴": "D83DDCB4.png", ":pound:": "D83DDCB7.png", ":yen:": "D83DDCB6.png", ":shit:": "D83DDCA9.png", ":dash:": "D83DDCA8.png", ":dizzy:": "D83DDCAB.png", ":muscle:": "D83DDCAA.png", ":thought_balloon:": "D83DDCAD.png", ":speech_balloon:": "D83DDCAC.png", "8|": "D83DDE33.png", ":28:": "D83DDCAE.png", ":bulb:": "D83DDCA1.png", ":534:": "D83DDCA0.png", ":bomb:": "D83DDCA3.png", ":297:": "D83DDCA2.png", ":boom:": "D83DDCA5.png", ":zzz:": "D83DDCA4.png", ":661:": "D83DDCA7.png", ":sweat_drops:": "D83DDCA6.png", ":blue_heart:": "D83DDC99.png", ":cupid:": "D83DDC98.png", ":yellow_heart:": "D83DDC9B.png", ":green_heart:": "D83DDC9A.png", ":56:": "D83DDC9D.png", ":32:": "D83DDC9F.png", ":revolving_hearts:": "D83DDC9E.png", ":couple_with_heart:": "D83DDC91.png", ":590:": "D83DDC90.png", ":336:": "D83DDC93.png", ":wedding:": "D83DDC92.png", ":247:": "D83DDC95.png", ":broken_heart:": "D83DDC94.png", ":heartpulse:": "D83DDC97.png", ":sparkling_heart:": "D83DDC96.png", ":syringe:": "D83DDC89.png", ":420:": "D83DDC88.png", ":kiss:": "D83DDC8B.png", ":pill:": "D83DDC8A.png", ":ring:": "D83DDC8D.png", ":383:": "D83DDC8C.png", "💏": "D83DDC8F.png", ":gem:": "D83DDC8E.png", ":information_desk_person:": "D83DDC81.png", ":skull:": "D83DDC80.png", ":dancer:": "D83DDC83.png", ":guardsman:": "D83DDC82.png", ":nail_care:": "D83DDC85.png", ":lipstick:": "D83DDC84.png", ":haircut:": "D83DDC87.png", ":massage:": "D83DDC86.png", ":bride_with_veil:": "D83DDC70.png", ":person_with_blond_hair:": "D83DDC71.png", ":man_with_gua_pi_mao:": "D83DDC72.png", ":man_with_turban:": "D83DDC73.png", ":older_man:": "D83DDC74.png", ":older_woman:": "D83DDC75.png", ":baby:": "D83DDC76.png", ":construction_worker:": "D83DDC77.png", ":princess:": "D83DDC78.png", ":japanese_ogre:": "D83DDC79.png", ":japanese_goblin:": "D83DDC7A.png", ":ghost:": "D83DDC7B.png", ":angel:": "D83DDC7C.png", ":alien:": "D83DDC7D.png", ":249:": "D83DDC7E.png", ":imp:": "D83DDC7F.png", "=(": "D83DDE20.png", ":sandal:": "D83DDC61.png", ":boot:": "D83DDC62.png", ":feet:": "D83DDC63.png", ":bust_in_silhouette:": "D83DDC64.png", ":mans_shoe:": "D83DDC5E.png", "👦": "D83DDC66.png", ":332:": "D83DDC67.png", ":man:": "D83DDC68.png", ":woman:": "D83DDC69.png", ":family:": "D83DDC6A.png", ":couple:": "D83DDC6B.png", ":two_men_holding_hands:": "D83DDC6C.png", ":point_up:": "261D.png", ":cop:": "D83DDC6E.png", ":290:": "D83DDC6F.png", ":open_hands:": "D83DDC50.png", ":crown:": "D83DDC51.png", ":womans_hat:": "D83DDC52.png", ":eyeglasses:": "D83DDC53.png", ":necktie:": "D83DDC54.png", ":shirt:": "D83DDC55.png", ":jeans:": "D83DDC56.png", ":dress:": "D83DDC57.png", ":kimono:": "D83DDC58.png", ":bikini:": "D83DDC59.png", ":womans_clothes:": "D83DDC5A.png", ":purse:": "D83DDC5B.png", ":handbag:": "D83DDC5C.png", ":pouch:": "D83DDC5D.png", "xD": "D83DDE06.png", ":shoe:": "D83DDC5F.png", ":eyes:": "D83DDC40.png", ":ear:": "D83DDC42.png", ":nose:": "D83DDC43.png", ":lips:": "D83DDC44.png", ":tongue:": "D83DDC45.png", ":point_up_2:": "D83DDC46.png", ":point_down:": "D83DDC47.png", ":point_left:": "D83DDC48.png", ":point_right:": "D83DDC49.png", ":facepunch:": "D83DDC4A.png", ":wave:": "D83DDC4B.png", ":ok:": "D83DDC4C.png", ":like:": "D83DDC4D.png", ":dislike:": "D83DDC4E.png", ":clap:": "D83DDC4F.png", ":^D": "D83DDE1B.png", ":3": "D83DDE19.png", ":kissing_heart:": "D83DDE18.png", ":((": "D83DDE1F.png", ":(": "D83DDE1E.png", ":stuck_out_tongue_closed_eyes:": "D83DDE1D.png", ":stuck_out_tongue_winking_eye:": "D83DDE1C.png", ":sweat:": "D83DDE13.png", ":593:": "D83DDE11.png", ":^)": "D83DDE17.png", ":confounded:": "D83DDE16.png", ":\\": "D83DDE15.png", ";-)": "D83DDE09.png", ":smiling_imp:": "D83DDE08.png", "B)": "D83DDE0E.png", "3-)": "D83DDE0C.png", ":*(": "D83DDE02.png", ":650:": "D83DDE01.png", ":smile:": "D83DDE00.png", ":610:": "D83DDE07.png", ":sweat_smile:": "D83DDE05.png", ":smile2:": "D83DDE04.png", ":heart_eyes_cat:": "D83DDE3B.png", ":smile_cat:": "D83DDE3A.png", ":joy_cat:": "D83DDE39.png", ":smiley_cat:": "D83DDE38.png", ":crying_cat_face:": "D83DDE3F.png", ":pouting_cat:": "D83DDE3E.png", ":kissing_cat:": "D83DDE3D.png", ":smirk_cat:": "D83DDE3C.png", ":scream:": "D83DDE31.png", ";o": "D83DDE30.png", ":no_mouth:": "D83DDE36.png", ":dizzy_face:": "D83DDE35.png", ":sleeping:": "D83DDE34.png", ":tired_face:": "D83DDE2B.png", ":389:": "D83DDE2A.png", ":0": "D83DDE2F.png", ":O": "D83DDE2E.png", ":sob:": "D83DDE2D.png", ":p": "D83DDE2C.png", ":58:": "D83DDE23.png", ":'(": "D83DDE22.png", ":206:": "D83DDE27.png", ":622:": "D83DDE26.png", ":461:": "D83DDE25.png", ":134:": "D83DDE24.png", ":25:": "D83CDFAD.png", ":244:": "D83CDFAC.png", ":darts:": "D83CDFAF.png", ":149:": "D83CDFAE.png", ":tophat:": "D83CDFA9.png", ":art:": "D83CDFA8.png", ":113:": "D83CDFAB.png", ":circus_tent:": "D83CDFAA.png", ":movie_camera:": "D83CDFA5.png", ":microphone:": "D83CDFA4.png", ":219:": "D83CDFA7.png", ":313:": "D83CDFA6.png", ":ferris_wheel:": "D83CDFA1.png", ":carousel_horse:": "D83CDFA0.png", ":637:": "D83CDFA3.png", ":roller_coaster:": "D83CDFA2.png", ":119:": "D83CDFBD.png", ":129:": "D83CDFBC.png", ":127:": "D83CDFBF.png", ":tennis:": "D83CDFBE.png", ":musical_keyboard:": "D83CDFB9.png", ":guitar:": "D83CDFB8.png", ":violin:": "D83CDFBB.png", ":trumpet:": "D83CDFBA.png", ":647:": "D83CDFB5.png", ":43:": "D83CDFB4.png", ":saxophone:": "D83CDFB7.png", ":85:": "D83CDFB6.png", ":8ball:": "D83CDFB1.png", ":565:": "D83CDFB0.png", ":bowling:": "D83CDFB3.png", ":game_die:": "D83CDFB2.png", ":245:": "D83CDF8D.png", ":crossed_flags:": "D83CDF8C.png", ":lags:": "D83CDF8F.png", ":dolls:": "D83CDF8E.png", ":tada:": "D83CDF89.png", ":balloon:": "D83CDF88.png", ":tanabata_tree:": "D83CDF8B.png", ":confetti_ball:": "D83CDF8A.png", ":santa:": "D83CDF85.png", ":christmas_tree:": "D83CDF84.png", ":sparkler:": "D83CDF87.png", ":fireworks:": "D83CDF86.png", ":gift:": "D83CDF81.png", ":42:": "D83CDF80.png", ":jack_o_lantern:": "D83CDF83.png", ":3:": "D83CDF82.png", ":444:": "D83DDCE1.png", ":61:": "D83CDF91.png", ":386:": "D83CDF90.png", ":440:": "D83CDF93.png", ":school_satchel:": "D83CDF92.png", "⛵": "26F5.png", "⛲": "26F2.png", "⛳": "26F3.png", "⛽": "26FD.png", "⛺": "26FA.png", "⛪": "26EA.png", "⛔": "26D4.png", "⛄": "26C4.png", "⛅": "26C5.png", "⛎": "26CE.png", "✨": "2728.png", "✴": "2734.png", "✳": "2733.png", "✌": "270C.png", "✏": "270F.png", "✉": "2709.png", "✈": "2708.png", "✋": "270B.png", "✊": "270A.png", "✅": "2705.png", "✂": "2702.png", "✔": "2714.png", "✖": "2716.png", "✒": "2712.png", ":crystal_ball:": "D83DDD2E.png", ":telescope:": "D83DDD2D.png", ":microscope:": "D83DDD2C.png", ":gun:": "D83DDD2B.png", ":hocho:": "D83DDD2A.png", ":63:": "D83DDD29.png", ":504:": "D83DDD28.png", ":301:": "D83DDD27.png", ":flashlight:": "D83DDD26.png", ":fire:": "D83DDD25.png", ":abc:": "D83DDD24.png", ":274:": "D83DDD23.png", ":abcd:": "D83DDD21.png", ":ABCD:": "D83DDD20.png", ":down:": "D83DDD3D.png", ":up:": "D83DDD3C.png", ":144:": "D83DDD3B.png", ":371:": "D83DDD3A.png", ":46:": "D83DDD39.png", ":240:": "D83DDD38.png", ":48:": "D83DDD37.png", ":298:": "D83DDD36.png", ":29:": "D83DDD35.png", ":250:": "D83DDD34.png", ":white_square_button:": "D83DDD33.png", ":black_square_button:": "D83DDD32.png", ":trident:": "D83DDD31.png", ":280:": "D83DDD0F.png", ":585:": "D83DDD0E.png", ":204:": "D83DDD0D.png", ":electric_plug:": "D83DDD0C.png", ":battery:": "D83DDD0B.png", ":speaker:": "D83DDD09.png", ":nosound:": "D83DDD07.png", ":low_brightness:": "D83DDD06.png", ":397:": "D83DDD05.png", ":268:": "D83DDD04.png", ":597:": "D83DDD03.png", ":584:": "D83DDD02.png", ":517:": "D83DDD01.png", ":374:": "D83DDD00.png", ":underage:": "D83DDD1E.png", ":173:": "D83DDD1D.png", ":soon:": "D83DDD1C.png", ":on:": "D83DDD1B.png", ":end:": "D83DDD1A.png", ":back:": "D83DDD19.png", ":583:": "D83DDD18.png", ":230:": "D83DDD17.png", ":629:": "D83DDD16.png", ":163:": "D83DDD15.png", ":426:": "D83DDD14.png", ":lock:": "D83DDD13.png", ":232:": "D83DDD12.png", ":key:": "D83DDD11.png", ":328:": "D83DDD10.png", ":black_joker:": "D83CDCCF.png", "©": "00A9.png", ":174:": "D83CDF64.png", ":235:": "D83CDF65.png", ":132:": "D83CDF66.png", ":417:": "D83CDF67.png", ":524:": "D83CDF60.png", ":105:": "D83CDF61.png", ":229:": "D83CDF62.png", ":50:": "D83CDF63.png", ":499:": "D83CDF6C.png", ":415:": "D83CDF6D.png", ":467:": "D83CDF6E.png", ":546:": "D83CDF6F.png", ":74:": "D83CDF68.png", ":306:": "D83CDF69.png", ":363:": "D83CDF6A.png", ":148:": "D83CDF6B.png", ":654:": "D83CDF74.png", ":251:": "D83CDF75.png", ":641:": "D83CDF76.png", ":439:": "D83CDF77.png", ":320:": "D83CDF70.png", ":44:": "D83CDF71.png", ":291:": "D83CDF72.png", ":495:": "D83CDF73.png", ":554:": "D83CDF7C.png", ":513:": "D83CDF78.png", ":384:": "D83CDF79.png", ":9:": "D83CDF7A.png", ":90:": "D83CDF7B.png", ":522:": "D83CDF44.png", ":498:": "D83CDF45.png", ":266:": "D83CDF46.png", ":16:": "D83CDF47.png", ":152:": "D83CDF40.png", ":560:": "D83CDF41.png", ":454:": "D83CDF42.png", ":302:": "D83CDF43.png", ":483:": "D83CDF4C.png", ":664:": "D83CDF4D.png", ":96:": "D83CDF4E.png", ":663:": "D83CDF4F.png", ":493:": "D83CDF48.png", ":539:": "D83CDF49.png", ":270:": "D83CDF4A.png", ":643:": "D83CDF4B.png", ":17:": "D83CDF54.png", ":343:": "D83CDF55.png", ":399:": "D83CDF56.png", ":450:": "D83CDF57.png", ":293:": "D83CDF50.png", ":184:": "D83CDF51.png", ":47:": "D83CDF52.png", ":403:": "D83CDF53.png", ":400:": "D83CDF5C.png", ":410:": "D83CDF5D.png", ":322:": "D83CDF5E.png", ":482:": "D83CDF5F.png", ":143:": "D83CDF58.png", ":542:": "D83CDF59.png", ":588:": "D83CDF5A.png", ":529:": "D83CDF5B.png", "☺": "263A.png", ":111:": "D83CDF08.png", "☑": "2611.png", "☕": "2615.png", ":22:": "D83CDF15.png", "☎": "260E.png", "☁": "2601.png", "☀": "2600.png", ":14:": "D83DDC33.png", ":409:": "D83DDC32.png", ":382:": "D83DDC31.png", ":460:": "D83DDC30.png", ":484:": "D83DDC37.png", ":288:": "D83DDC36.png", ":594:": "D83DDC35.png", ":591:": "D83DDC34.png", ":372:": "D83DDC3B.png", ":563:": "D83DDC3A.png", ":596:": "D83DDC39.png", ":595:": "D83DDC38.png", ":367:": "D83DDC3E.png", ":376:": "D83DDC3D.png", ":305:": "D83DDC3C.png", ":281:": "D83DDC23.png", ":452:": "D83DDC22.png", ":388:": "D83DDC21.png", ":39:": "D83DDC20.png", ":36:": "D83DDC27.png", ":189:": "D83DDC26.png", ":500:": "D83DDC25.png", ":277:": "D83DDC24.png", ":459:": "D83DDC2B.png", ":486:": "D83DDC2A.png", ":662:": "D83DDC28.png", ":509:": "D83DDC2F.png", ":365:": "D83DDC2E.png", ":34:": "D83DDC2D.png", ":319:": "D83DDC2C.png", ":118:": "D83DDC13.png", ":463:": "D83DDC12.png", ":220:": "D83DDC11.png", ":653:": "D83DDC10.png", ":599:": "D83DDC17.png", ":603:": "D83DDC16.png", ":489:": "D83DDC15.png", ":552:": "D83DDC14.png", ":181:": "D83DDC1B.png", ":208:": "D83DDC1A.png", ":19:": "D83DDC19.png", ":580:": "D83DDC18.png", ":398:": "D83DDC1F.png", ":310:": "D83DDC1E.png", ":4:": "D83DDC1D.png", ":564:": "D83DDC1C.png", ":135:": "D83DDC03.png", ":566:": "D83DDC02.png", ":211:": "D83DDC01.png", ":631:": "D83DDC00.png", ":496:": "D83DDC07.png", ":263:": "D83DDC06.png", ":447:": "D83DDC05.png", ":283:": "D83DDC04.png", ":607:": "D83DDC0B.png", ":23:": "D83DDC0A.png", ":634:": "D83DDC09.png", ":435:": "D83DDC08.png", ":349:": "D83DDC0F.png", ":53:": "D83DDC0E.png", ":101:": "D83DDC0D.png", ":567:": "D83DDC0C.png", "◻": "25FB.png", "❤": "2764.png", "◼": "25FC.png", "◽": "25FD.png", "◾": "25FE.png", "➿": "27BF.png", "⬅": "2B05.png", ":531:": "D83DDE92.png", ":116:": "D83CDD8E.png", ":466:": "D83CDD95.png", ":545:": "D83CDD94.png", ":202:": "D83CDD97.png", ":100:": "D83CDD96.png", ":CL:": "D83CDD91.png", ":free:": "D83CDD93.png", ":cool:": "D83CDD92.png", ":UP!:": "D83CDD99.png", ":SOS:": "D83CDD98.png", ":VS:": "D83CDD9A.png", ":525:": "D83CDF20.png", ":614:": "D83CDF37.png", ":13:": "D83CDF35.png", ":108:": "D83CDF34.png", ":289:": "D83CDF33.png", ":390:": "D83CDF32.png", ":222:": "D83CDF31.png", ":92:": "D83CDF30.png", ":205:": "D83CDF3F.png", ":7:": "D83CDF3E.png", ":142:": "D83CDF3D.png", ":536:": "D83CDF3C.png", ":153:": "D83CDF3B.png", ":307:": "D83CDF3A.png", ":304:": "D83CDF39.png", ":18:": "D83CDF38.png", ":558:": "D83CDF07.png", ":71:": "D83CDF06.png", ":421:": "D83CDF05.png", ":485:": "D83CDF04.png", ":223:": "D83CDF03.png", ":269:": "D83CDF02.png", ":574:": "D83CDF01.png", ":124:": "D83CDF00.png", ":65:": "D83CDF0F.png", ":15:": "D83CDF0E.png", ":411:": "D83CDF0D.png", ":272:": "D83CDF0C.png", ":335:": "D83CDF0B.png", ":379:": "D83CDF0A.png", ":192:": "D83CDF09.png", ":359:": "D83CDF17.png", ":436:": "D83CDF16.png", ":479:": "D83CDF14.png", ":231:": "D83CDF13.png", ":528:": "D83CDF12.png", ":341:": "D83CDF11.png", ":491:": "D83CDF10.png", ":520:": "D83CDF1F.png", ":185:": "D83CDF1E.png", ":568:": "D83CDF1D.png", ":40:": "D83CDF1C.png", ":393:": "D83CDF1B.png", ":275:": "D83CDF1A.png", ":72:": "D83CDF19.png", ":246:": "D83CDF18.png", "❓": "2753.png", "❗": "2757.png", "❔": "2754.png", "❕": "2755.png", "➰": "27B0.png", "❄": "2744.png", "❎": "274E.png", "❌": "274C.png", "⏰": "23F0.png", "⏳": "23F3.png", "⏩": "23E9.png", "⏪": "23EA.png", "⏫": "23EB.png", "⏬": "23EC.png", ":106:": "D83DDEC4.png", ":465:": "D83DDEC2.png", "▶": "25B6.png", ":236:": "D83DDEC0.png", ":271:": "D83DDEC1.png", "⤵": "2935.png", "⤴": "2934.png", "▫": "25AB.png", "▪": "25AA.png", "❇": "2747.png", ":510:": "D83CDD7E.png", ":480:": "D83CDD7F.png", "〽": "303D.png", "™": "2122.png", "〰": "3030.png", ":351:": "D83CDD70.png", ":237:": "D83CDD71.png", "ℹ": "2139.png", "☔": "2614.png", "⬇": "2B07.png", "➖": "2796.png", ":358:": "D83CDE01.png", "⬆": "2B06.png", "♿": "267F.png", "♻": "267B.png", "♨": "2668.png", "♦": "2666.png", "♥": "2665.png", "♣": "2663.png", "♠": "2660.png", "®": "00AE.png", "♒": "2652.png", "♓": "2653.png", "♐": "2650.png", "♑": "2651.png", "♎": "264E.png", "♏": "264F.png", "♌": "264C.png", "♍": "264D.png", "♊": "264A.png", "♋": "264B.png", ":592:": "D83DDCF4.png", "♉": "2649.png", ":472:": "D83DDDFB.png", ":84:": "D83DDDFE.png", ":254:": "D83DDDFF.png", ":316:": "D83DDDFC.png", ":226:": "D83DDDFD.png", "‼": "203C.png", ":621:": "D83DDEC5.png", ":140:": "D83DDEC3.png", ":tox:": "tox.png", ":wtox:": "wtox.png", ":td:": "td.png", ":cr:": "cr.png", ":gd:": "gd.png", ":si:": "si.png", ":ni:": "ni.png", ":gs:": "gs.png", ":er:": "er.png", ":pg:": "pg.png", ":jp:": "jp.png", ":hu:": "hu.png", ":is:": "is.png", ":ro:": "ro.png", ":europeanunion:": "europeanunion.png", ":bj:": "bj.png", ":mp:": "mp.png", ":tl:": "tl.png", ":gl:": "gl.png", ":sa:": "sa.png", ":na:": "na.png", ":as:": "as.png", ":lt:": "lt.png", ":za:": "za.png", ":bb:": "bb.png", ":mx:": "mx.png", ":np:": "np.png", ":ru:": "ru.png", ":vg:": "vg.png", ":sy:": "sy.png", ":fam:": "fam.png", ":bo:": "bo.png", ":um:": "um.png", ":fr:": "fr.png", ":sh:": "sh.png", ":gr:": "gr.png", ":nu:": "nu.png", ":mo:": "mo.png", ":ir:": "ir.png", ":de:": "de.png", ":tt:": "tt.png", ":lb:": "lb.png", ":ci:": "ci.png", ":tm:": "tm.png", ":pl:": "pl.png", ":vi:": "vi.png", ":hr:": "hr.png", ":ar:": "ar.png", ":bz:": "bz.png", ":ae:": "ae.png", ":lu:": "lu.png", ":cz:": "cz.png", ":dm:": "dm.png", ":ca:": "ca.png", ":yt:": "yt.png", ":va:": "va.png", ":ee:": "ee.png", ":pt:": "pt.png", ":az:": "az.png", ":br:": "br.png", ":dk:": "dk.png", ":am:": "am.png", ":tw:": "tw.png", ":gq:": "gq.png", ":et:": "et.png", ":pe:": "pe.png", ":pr:": "pr.png", ":kr:": "kr.png", ":st:": "st.png", ":ki:": "ki.png", ":bt:": "bt.png", ":mr:": "mr.png", ":nz:": "nz.png", ":lc:": "lc.png", ":mm:": "mm.png", ":ch:": "ch.png", ":gb:": "gb.png", ":so:": "so.png", ":nc:": "nc.png", ":gy:": "gy.png", ":scotland:": "scotland.png", ":pm:": "pm.png", ":ug:": "ug.png", ":sv:": "sv.png", ":gf:": "gf.png", ":kz:": "kz.png", ":lr:": "lr.png", ":cy:": "cy.png", ":ad:": "ad.png", ":mz:": "mz.png", ":fj:": "fj.png", ":sg:": "sg.png", ":au:": "au.png", ":bs:": "bs.png", ":england:": "england.png", ":al:": "al.png", ":bd:": "bd.png", ":fi:": "fi.png", ":md:": "md.png", ":rs:": "rs.png", ":ps:": "ps.png", ":uy:": "uy.png", ":tr:": "tr.png", ":kh:": "kh.png", ":zm:": "zm.png", ":ga:": "ga.png", ":ml:": "ml.png", ":hk:": "hk.png", ":ky:": "ky.png", ":it:": "it.png", ":cn:": "cn.png", ":ag:": "ag.png", ":ls:": "ls.png", ":tz:": "tz.png", ":wales:": "wales.png", ":bm:": "bm.png", ":co:": "co.png", ":tk:": "tk.png", ":gi:": "gi.png", ":eg:": "eg.png", ":ie:": "ie.png", ":at:": "at.png", ":mc:": "mc.png", ":by:": "by.png", ":ao:": "ao.png", ":lk:": "lk.png", ":me:": "me.png", ":be:": "be.png", ":cg:": "cg.png", ":sn:": "sn.png", ":kp:": "kp.png", ":mq:": "mq.png", ":kg:": "kg.png", ":bv:": "bv.png", ":mt:": "mt.png", ":qa:": "qa.png", ":la:": "la.png", ":th:": "th.png", ":cv:": "cv.png", ":id:": "id.png", ":sm:": "sm.png", ":ne:": "ne.png", ":il:": "il.png", ":bi:": "bi.png", ":jm:": "jm.png", ":af:": "af.png", ":bn:": "bn.png", ":ma:": "ma.png", ":gh:": "gh.png", ":se:": "se.png", ":pk:": "pk.png", ":ua:": "ua.png", ":to:": "to.png", ":aw:": "aw.png", ":gt:": "gt.png", ":mk:": "mk.png", ":an:": "an.png", ":bf:": "bf.png", ":tc:": "tc.png", ":nl:": "nl.png", ":cf:": "cf.png", ":gp:": "gp.png", ":ly:": "ly.png", ":mw:": "mw.png", ":bw:": "bw.png", ":cu:": "cu.png", ":mn:": "mn.png", ":nf:": "nf.png", ":sl:": "sl.png", ":io:": "io.png", ":cx:": "cx.png", ":kw:": "kw.png", ":py:": "py.png", ":us:": "us.png", ":pa:": "pa.png", ":kn:": "kn.png", ":zw:": "zw.png", ":cm:": "cm.png", ":fm:": "fm.png", ":sd:": "sd.png", ":ph:": "ph.png", ":fk:": "fk.png", ":tj:": "tj.png", ":ai:": "ai.png", ":li:": "li.png", ":mg:": "mg.png", ":bg:": "bg.png", ":ms:": "ms.png", ":gw:": "gw.png", ":vc:": "vc.png", ":hn:": "hn.png", ":ke:": "ke.png", ":ax:": "ax.png", ":mv:": "mv.png", ":tf:": "tf.png", ":vu:": "vu.png", ":sk:": "sk.png", ":ng:": "ng.png", ":vn:": "vn.png", ":in:": "in.png", ":sr:": "sr.png", ":iq:": "iq.png", ":hm:": "hm.png", ":pw:": "pw.png", ":ws:": "ws.png", ":jo:": "jo.png", ":km:": "km.png", ":bh:": "bh.png", ":tn:": "tn.png", ":cl:": "cl.png", ":gn:": "gn.png", ":sc:": "sc.png", ":eh:": "eh.png", ":ba:": "ba.png", ":re:": "re.png", ":lv:": "lv.png", ":cd:": "cd.png", ":ve:": "ve.png", ":om:": "om.png", ":cs:": "cs.png", ":ge:": "ge.png", ":tg:": "tg.png", ":es:": "es.png", ":sj:": "sj.png", ":pf:": "pf.png", ":ht:": "ht.png", ":dj:": "dj.png", ":ec:": "ec.png", ":tv:": "tv.png", ":wf:": "wf.png", ":catalonia:": "catalonia.png", ":ck:": "ck.png", ":fo:": "fo.png", ":gm:": "gm.png", ":mh:": "mh.png", ":ye:": "ye.png", ":sb:": "sb.png", ":pn:": "pn.png", ":mu:": "mu.png", ":do:": "do.png", ":my:": "my.png", ":nr:": "nr.png", ":cc:": "cc.png", ":no:": "no.png", ":gu:": "gu.png", ":rw:": "rw.png", ":dz:": "dz.png", ":sz:": "sz.png", ":uz:": "uz.png" }
diff --git a/toxygen/smileys/default/cr.png b/toxygen/smileys/default/cr.png
old mode 100644
new mode 100755
index a90450c..c7a3731
Binary files a/toxygen/smileys/default/cr.png and b/toxygen/smileys/default/cr.png differ
diff --git a/toxygen/smileys/default/cs.png b/toxygen/smileys/default/cs.png
old mode 100644
new mode 100755
index 45b4710..8254790
Binary files a/toxygen/smileys/default/cs.png and b/toxygen/smileys/default/cs.png differ
diff --git a/toxygen/smileys/default/cu.png b/toxygen/smileys/default/cu.png
old mode 100644
new mode 100755
index eef7f8a..083f1d6
Binary files a/toxygen/smileys/default/cu.png and b/toxygen/smileys/default/cu.png differ
diff --git a/toxygen/smileys/default/cv.png b/toxygen/smileys/default/cv.png
old mode 100644
new mode 100755
index 4ac3d24..a63f7ea
Binary files a/toxygen/smileys/default/cv.png and b/toxygen/smileys/default/cv.png differ
diff --git a/toxygen/smileys/default/cx.png b/toxygen/smileys/default/cx.png
old mode 100644
new mode 100755
index 1c57fbf..48e31ad
Binary files a/toxygen/smileys/default/cx.png and b/toxygen/smileys/default/cx.png differ
diff --git a/toxygen/smileys/default/cy.png b/toxygen/smileys/default/cy.png
old mode 100644
new mode 100755
index 6e234cc..5b1ad6c
Binary files a/toxygen/smileys/default/cy.png and b/toxygen/smileys/default/cy.png differ
diff --git a/toxygen/smileys/default/cz.png b/toxygen/smileys/default/cz.png
old mode 100644
new mode 100755
index 526d990..c8403dd
Binary files a/toxygen/smileys/default/cz.png and b/toxygen/smileys/default/cz.png differ
diff --git a/toxygen/smileys/default/de.png b/toxygen/smileys/default/de.png
old mode 100644
new mode 100755
index 4e202a6..ac4a977
Binary files a/toxygen/smileys/default/de.png and b/toxygen/smileys/default/de.png differ
diff --git a/toxygen/smileys/default/dj.png b/toxygen/smileys/default/dj.png
old mode 100644
new mode 100755
index 9b3da9c..582af36
Binary files a/toxygen/smileys/default/dj.png and b/toxygen/smileys/default/dj.png differ
diff --git a/toxygen/smileys/default/dk.png b/toxygen/smileys/default/dk.png
old mode 100644
new mode 100755
index 72af9e3..e2993d3
Binary files a/toxygen/smileys/default/dk.png and b/toxygen/smileys/default/dk.png differ
diff --git a/toxygen/smileys/default/dm.png b/toxygen/smileys/default/dm.png
old mode 100644
new mode 100755
index d10d036..5fbffcb
Binary files a/toxygen/smileys/default/dm.png and b/toxygen/smileys/default/dm.png differ
diff --git a/toxygen/smileys/default/do.png b/toxygen/smileys/default/do.png
old mode 100644
new mode 100755
index 2134259..5a04932
Binary files a/toxygen/smileys/default/do.png and b/toxygen/smileys/default/do.png differ
diff --git a/toxygen/smileys/default/dz.png b/toxygen/smileys/default/dz.png
old mode 100644
new mode 100755
index f49fb58..335c239
Binary files a/toxygen/smileys/default/dz.png and b/toxygen/smileys/default/dz.png differ
diff --git a/toxygen/smileys/default/ec.png b/toxygen/smileys/default/ec.png
old mode 100644
new mode 100755
index d6b42d6..0caa0b1
Binary files a/toxygen/smileys/default/ec.png and b/toxygen/smileys/default/ec.png differ
diff --git a/toxygen/smileys/default/ee.png b/toxygen/smileys/default/ee.png
old mode 100644
new mode 100755
index 5ffe80e..0c82efb
Binary files a/toxygen/smileys/default/ee.png and b/toxygen/smileys/default/ee.png differ
diff --git a/toxygen/smileys/default/eg.png b/toxygen/smileys/default/eg.png
old mode 100644
new mode 100755
index 50e4c7e..8a3f7a1
Binary files a/toxygen/smileys/default/eg.png and b/toxygen/smileys/default/eg.png differ
diff --git a/toxygen/smileys/default/eh.png b/toxygen/smileys/default/eh.png
old mode 100644
new mode 100755
index b4f35cd..90a1195
Binary files a/toxygen/smileys/default/eh.png and b/toxygen/smileys/default/eh.png differ
diff --git a/toxygen/smileys/default/england.png b/toxygen/smileys/default/england.png
old mode 100644
new mode 100755
index 0cd6e96..3a7311d
Binary files a/toxygen/smileys/default/england.png and b/toxygen/smileys/default/england.png differ
diff --git a/toxygen/smileys/default/er.png b/toxygen/smileys/default/er.png
old mode 100644
new mode 100755
index 4d302a6..13065ae
Binary files a/toxygen/smileys/default/er.png and b/toxygen/smileys/default/er.png differ
diff --git a/toxygen/smileys/default/es.png b/toxygen/smileys/default/es.png
old mode 100644
new mode 100755
index c804049..c2de2d7
Binary files a/toxygen/smileys/default/es.png and b/toxygen/smileys/default/es.png differ
diff --git a/toxygen/smileys/default/et.png b/toxygen/smileys/default/et.png
old mode 100644
new mode 100755
index ebc5f34..2e893fa
Binary files a/toxygen/smileys/default/et.png and b/toxygen/smileys/default/et.png differ
diff --git a/toxygen/smileys/default/europeanunion.png b/toxygen/smileys/default/europeanunion.png
index 50815ae..d6d8711 100644
Binary files a/toxygen/smileys/default/europeanunion.png and b/toxygen/smileys/default/europeanunion.png differ
diff --git a/toxygen/smileys/default/fam.png b/toxygen/smileys/default/fam.png
old mode 100644
new mode 100755
index e2cdcb7..cf50c75
Binary files a/toxygen/smileys/default/fam.png and b/toxygen/smileys/default/fam.png differ
diff --git a/toxygen/smileys/default/fi.png b/toxygen/smileys/default/fi.png
old mode 100644
new mode 100755
index 0c0af94..14ec091
Binary files a/toxygen/smileys/default/fi.png and b/toxygen/smileys/default/fi.png differ
diff --git a/toxygen/smileys/default/fj.png b/toxygen/smileys/default/fj.png
old mode 100644
new mode 100755
index 14a9d76..cee9988
Binary files a/toxygen/smileys/default/fj.png and b/toxygen/smileys/default/fj.png differ
diff --git a/toxygen/smileys/default/fk.png b/toxygen/smileys/default/fk.png
old mode 100644
new mode 100755
index 0b2c8e1..ceaeb27
Binary files a/toxygen/smileys/default/fk.png and b/toxygen/smileys/default/fk.png differ
diff --git a/toxygen/smileys/default/fm.png b/toxygen/smileys/default/fm.png
old mode 100644
new mode 100755
index c3fbeed..066bb24
Binary files a/toxygen/smileys/default/fm.png and b/toxygen/smileys/default/fm.png differ
diff --git a/toxygen/smileys/default/fo.png b/toxygen/smileys/default/fo.png
old mode 100644
new mode 100755
index b48a3f9..cbceb80
Binary files a/toxygen/smileys/default/fo.png and b/toxygen/smileys/default/fo.png differ
diff --git a/toxygen/smileys/default/fr.png b/toxygen/smileys/default/fr.png
old mode 100644
new mode 100755
index eaec4f3..8332c4e
Binary files a/toxygen/smileys/default/fr.png and b/toxygen/smileys/default/fr.png differ
diff --git a/toxygen/smileys/default/ga.png b/toxygen/smileys/default/ga.png
old mode 100644
new mode 100755
index 14df032..0e0d434
Binary files a/toxygen/smileys/default/ga.png and b/toxygen/smileys/default/ga.png differ
diff --git a/toxygen/smileys/default/gb.png b/toxygen/smileys/default/gb.png
index 032b04d..ff701e1 100644
Binary files a/toxygen/smileys/default/gb.png and b/toxygen/smileys/default/gb.png differ
diff --git a/toxygen/smileys/default/gd.png b/toxygen/smileys/default/gd.png
old mode 100644
new mode 100755
index 96ddfd9..9ab57f5
Binary files a/toxygen/smileys/default/gd.png and b/toxygen/smileys/default/gd.png differ
diff --git a/toxygen/smileys/default/ge.png b/toxygen/smileys/default/ge.png
old mode 100644
new mode 100755
index 2f5475e..728d970
Binary files a/toxygen/smileys/default/ge.png and b/toxygen/smileys/default/ge.png differ
diff --git a/toxygen/smileys/default/gf.png b/toxygen/smileys/default/gf.png
old mode 100644
new mode 100755
index fddf8f6..8332c4e
Binary files a/toxygen/smileys/default/gf.png and b/toxygen/smileys/default/gf.png differ
diff --git a/toxygen/smileys/default/gh.png b/toxygen/smileys/default/gh.png
old mode 100644
new mode 100755
index 57561cf..4e2f896
Binary files a/toxygen/smileys/default/gh.png and b/toxygen/smileys/default/gh.png differ
diff --git a/toxygen/smileys/default/gi.png b/toxygen/smileys/default/gi.png
old mode 100644
new mode 100755
index 29a981a..e76797f
Binary files a/toxygen/smileys/default/gi.png and b/toxygen/smileys/default/gi.png differ
diff --git a/toxygen/smileys/default/gl.png b/toxygen/smileys/default/gl.png
old mode 100644
new mode 100755
index d0f4bca..ef12a73
Binary files a/toxygen/smileys/default/gl.png and b/toxygen/smileys/default/gl.png differ
diff --git a/toxygen/smileys/default/gm.png b/toxygen/smileys/default/gm.png
old mode 100644
new mode 100755
index abf8f8f..0720b66
Binary files a/toxygen/smileys/default/gm.png and b/toxygen/smileys/default/gm.png differ
diff --git a/toxygen/smileys/default/gn.png b/toxygen/smileys/default/gn.png
old mode 100644
new mode 100755
index ff76a52..ea660b0
Binary files a/toxygen/smileys/default/gn.png and b/toxygen/smileys/default/gn.png differ
diff --git a/toxygen/smileys/default/gp.png b/toxygen/smileys/default/gp.png
old mode 100644
new mode 100755
index 88d2995..dbb086d
Binary files a/toxygen/smileys/default/gp.png and b/toxygen/smileys/default/gp.png differ
diff --git a/toxygen/smileys/default/gq.png b/toxygen/smileys/default/gq.png
old mode 100644
new mode 100755
index 1051698..ebe20a2
Binary files a/toxygen/smileys/default/gq.png and b/toxygen/smileys/default/gq.png differ
diff --git a/toxygen/smileys/default/gr.png b/toxygen/smileys/default/gr.png
old mode 100644
new mode 100755
index 0c856e4..8651ade
Binary files a/toxygen/smileys/default/gr.png and b/toxygen/smileys/default/gr.png differ
diff --git a/toxygen/smileys/default/gs.png b/toxygen/smileys/default/gs.png
old mode 100644
new mode 100755
index a0d6575..7ef0bf5
Binary files a/toxygen/smileys/default/gs.png and b/toxygen/smileys/default/gs.png differ
diff --git a/toxygen/smileys/default/gt.png b/toxygen/smileys/default/gt.png
old mode 100644
new mode 100755
index cec6821..c43a70d
Binary files a/toxygen/smileys/default/gt.png and b/toxygen/smileys/default/gt.png differ
diff --git a/toxygen/smileys/default/gu.png b/toxygen/smileys/default/gu.png
old mode 100644
new mode 100755
index da5f65b..92f37c0
Binary files a/toxygen/smileys/default/gu.png and b/toxygen/smileys/default/gu.png differ
diff --git a/toxygen/smileys/default/gw.png b/toxygen/smileys/default/gw.png
old mode 100644
new mode 100755
index 9d3af7c..b37bcf0
Binary files a/toxygen/smileys/default/gw.png and b/toxygen/smileys/default/gw.png differ
diff --git a/toxygen/smileys/default/gy.png b/toxygen/smileys/default/gy.png
old mode 100644
new mode 100755
index eee94e9..22cbe2f
Binary files a/toxygen/smileys/default/gy.png and b/toxygen/smileys/default/gy.png differ
diff --git a/toxygen/smileys/default/hk.png b/toxygen/smileys/default/hk.png
old mode 100644
new mode 100755
index 4ca283f..d5c380c
Binary files a/toxygen/smileys/default/hk.png and b/toxygen/smileys/default/hk.png differ
diff --git a/toxygen/smileys/default/hm.png b/toxygen/smileys/default/hm.png
old mode 100644
new mode 100755
index 67c1149..a01389a
Binary files a/toxygen/smileys/default/hm.png and b/toxygen/smileys/default/hm.png differ
diff --git a/toxygen/smileys/default/hn.png b/toxygen/smileys/default/hn.png
old mode 100644
new mode 100755
index b1eb441..96f8388
Binary files a/toxygen/smileys/default/hn.png and b/toxygen/smileys/default/hn.png differ
diff --git a/toxygen/smileys/default/hr.png b/toxygen/smileys/default/hr.png
old mode 100644
new mode 100755
index 8cf6064..696b515
Binary files a/toxygen/smileys/default/hr.png and b/toxygen/smileys/default/hr.png differ
diff --git a/toxygen/smileys/default/ht.png b/toxygen/smileys/default/ht.png
old mode 100644
new mode 100755
index 9e447d6..416052a
Binary files a/toxygen/smileys/default/ht.png and b/toxygen/smileys/default/ht.png differ
diff --git a/toxygen/smileys/default/hu.png b/toxygen/smileys/default/hu.png
old mode 100644
new mode 100755
index 09361e4..7baafe4
Binary files a/toxygen/smileys/default/hu.png and b/toxygen/smileys/default/hu.png differ
diff --git a/toxygen/smileys/default/id.png b/toxygen/smileys/default/id.png
old mode 100644
new mode 100755
index 76e9fbd..c6bc0fa
Binary files a/toxygen/smileys/default/id.png and b/toxygen/smileys/default/id.png differ
diff --git a/toxygen/smileys/default/ie.png b/toxygen/smileys/default/ie.png
old mode 100644
new mode 100755
index fd87d4b..26baa31
Binary files a/toxygen/smileys/default/ie.png and b/toxygen/smileys/default/ie.png differ
diff --git a/toxygen/smileys/default/il.png b/toxygen/smileys/default/il.png
old mode 100644
new mode 100755
index b4d8f2d..2ca772d
Binary files a/toxygen/smileys/default/il.png and b/toxygen/smileys/default/il.png differ
diff --git a/toxygen/smileys/default/in.png b/toxygen/smileys/default/in.png
old mode 100644
new mode 100755
index f72030a..e4d7e81
Binary files a/toxygen/smileys/default/in.png and b/toxygen/smileys/default/in.png differ
diff --git a/toxygen/smileys/default/io.png b/toxygen/smileys/default/io.png
old mode 100644
new mode 100755
index 0f338e8..3e74b6a
Binary files a/toxygen/smileys/default/io.png and b/toxygen/smileys/default/io.png differ
diff --git a/toxygen/smileys/default/iq.png b/toxygen/smileys/default/iq.png
old mode 100644
new mode 100755
index 97219ae..878a351
Binary files a/toxygen/smileys/default/iq.png and b/toxygen/smileys/default/iq.png differ
diff --git a/toxygen/smileys/default/ir.png b/toxygen/smileys/default/ir.png
old mode 100644
new mode 100755
index f0b721c..c5fd136
Binary files a/toxygen/smileys/default/ir.png and b/toxygen/smileys/default/ir.png differ
diff --git a/toxygen/smileys/default/is.png b/toxygen/smileys/default/is.png
old mode 100644
new mode 100755
index 5236627..b8f6d0f
Binary files a/toxygen/smileys/default/is.png and b/toxygen/smileys/default/is.png differ
diff --git a/toxygen/smileys/default/it.png b/toxygen/smileys/default/it.png
old mode 100644
new mode 100755
index a2c0f02..89692f7
Binary files a/toxygen/smileys/default/it.png and b/toxygen/smileys/default/it.png differ
diff --git a/toxygen/smileys/default/jm.png b/toxygen/smileys/default/jm.png
old mode 100644
new mode 100755
index 37ae2ba..7be119e
Binary files a/toxygen/smileys/default/jm.png and b/toxygen/smileys/default/jm.png differ
diff --git a/toxygen/smileys/default/jo.png b/toxygen/smileys/default/jo.png
old mode 100644
new mode 100755
index 97c0f1a..11bd497
Binary files a/toxygen/smileys/default/jo.png and b/toxygen/smileys/default/jo.png differ
diff --git a/toxygen/smileys/default/jp.png b/toxygen/smileys/default/jp.png
old mode 100644
new mode 100755
index 7b5c019..325fbad
Binary files a/toxygen/smileys/default/jp.png and b/toxygen/smileys/default/jp.png differ
diff --git a/toxygen/smileys/default/ke.png b/toxygen/smileys/default/ke.png
old mode 100644
new mode 100755
index a6ae21e..51879ad
Binary files a/toxygen/smileys/default/ke.png and b/toxygen/smileys/default/ke.png differ
diff --git a/toxygen/smileys/default/kg.png b/toxygen/smileys/default/kg.png
old mode 100644
new mode 100755
index 0d09612..0a818f6
Binary files a/toxygen/smileys/default/kg.png and b/toxygen/smileys/default/kg.png differ
diff --git a/toxygen/smileys/default/kh.png b/toxygen/smileys/default/kh.png
old mode 100644
new mode 100755
index 1f272a5..30f6bb1
Binary files a/toxygen/smileys/default/kh.png and b/toxygen/smileys/default/kh.png differ
diff --git a/toxygen/smileys/default/ki.png b/toxygen/smileys/default/ki.png
old mode 100644
new mode 100755
index 83b15b8..2dcce4b
Binary files a/toxygen/smileys/default/ki.png and b/toxygen/smileys/default/ki.png differ
diff --git a/toxygen/smileys/default/km.png b/toxygen/smileys/default/km.png
old mode 100644
new mode 100755
index 5d8863a..812b2f5
Binary files a/toxygen/smileys/default/km.png and b/toxygen/smileys/default/km.png differ
diff --git a/toxygen/smileys/default/kn.png b/toxygen/smileys/default/kn.png
old mode 100644
new mode 100755
index 6d48d10..febd5b4
Binary files a/toxygen/smileys/default/kn.png and b/toxygen/smileys/default/kn.png differ
diff --git a/toxygen/smileys/default/kp.png b/toxygen/smileys/default/kp.png
old mode 100644
new mode 100755
index 50dfa1e..d3d509a
Binary files a/toxygen/smileys/default/kp.png and b/toxygen/smileys/default/kp.png differ
diff --git a/toxygen/smileys/default/kr.png b/toxygen/smileys/default/kr.png
old mode 100644
new mode 100755
index 33b8144..9c0a78e
Binary files a/toxygen/smileys/default/kr.png and b/toxygen/smileys/default/kr.png differ
diff --git a/toxygen/smileys/default/kw.png b/toxygen/smileys/default/kw.png
old mode 100644
new mode 100755
index 66ae3a4..96546da
Binary files a/toxygen/smileys/default/kw.png and b/toxygen/smileys/default/kw.png differ
diff --git a/toxygen/smileys/default/ky.png b/toxygen/smileys/default/ky.png
old mode 100644
new mode 100755
index 823b285..15c5f8e
Binary files a/toxygen/smileys/default/ky.png and b/toxygen/smileys/default/ky.png differ
diff --git a/toxygen/smileys/default/kz.png b/toxygen/smileys/default/kz.png
old mode 100644
new mode 100755
index aa8118a..45a8c88
Binary files a/toxygen/smileys/default/kz.png and b/toxygen/smileys/default/kz.png differ
diff --git a/toxygen/smileys/default/la.png b/toxygen/smileys/default/la.png
old mode 100644
new mode 100755
index 302427f..e28acd0
Binary files a/toxygen/smileys/default/la.png and b/toxygen/smileys/default/la.png differ
diff --git a/toxygen/smileys/default/lb.png b/toxygen/smileys/default/lb.png
old mode 100644
new mode 100755
index 55a5e5b..d0d452b
Binary files a/toxygen/smileys/default/lb.png and b/toxygen/smileys/default/lb.png differ
diff --git a/toxygen/smileys/default/lc.png b/toxygen/smileys/default/lc.png
index 291f1c5..a47d065 100644
Binary files a/toxygen/smileys/default/lc.png and b/toxygen/smileys/default/lc.png differ
diff --git a/toxygen/smileys/default/li.png b/toxygen/smileys/default/li.png
old mode 100644
new mode 100755
index 5c0ec41..6469909
Binary files a/toxygen/smileys/default/li.png and b/toxygen/smileys/default/li.png differ
diff --git a/toxygen/smileys/default/lk.png b/toxygen/smileys/default/lk.png
old mode 100644
new mode 100755
index d2bc667..088aad6
Binary files a/toxygen/smileys/default/lk.png and b/toxygen/smileys/default/lk.png differ
diff --git a/toxygen/smileys/default/lr.png b/toxygen/smileys/default/lr.png
old mode 100644
new mode 100755
index 24db5a9..89a5bc7
Binary files a/toxygen/smileys/default/lr.png and b/toxygen/smileys/default/lr.png differ
diff --git a/toxygen/smileys/default/ls.png b/toxygen/smileys/default/ls.png
old mode 100644
new mode 100755
index e4e7966..33fdef1
Binary files a/toxygen/smileys/default/ls.png and b/toxygen/smileys/default/ls.png differ
diff --git a/toxygen/smileys/default/lt.png b/toxygen/smileys/default/lt.png
old mode 100644
new mode 100755
index 7c2bdd6..c8ef0da
Binary files a/toxygen/smileys/default/lt.png and b/toxygen/smileys/default/lt.png differ
diff --git a/toxygen/smileys/default/lu.png b/toxygen/smileys/default/lu.png
old mode 100644
new mode 100755
index 37544b4..4cabba9
Binary files a/toxygen/smileys/default/lu.png and b/toxygen/smileys/default/lu.png differ
diff --git a/toxygen/smileys/default/lv.png b/toxygen/smileys/default/lv.png
old mode 100644
new mode 100755
index 6bb32b0..49b6998
Binary files a/toxygen/smileys/default/lv.png and b/toxygen/smileys/default/lv.png differ
diff --git a/toxygen/smileys/default/ly.png b/toxygen/smileys/default/ly.png
old mode 100644
new mode 100755
index 86c41fc..b163a9f
Binary files a/toxygen/smileys/default/ly.png and b/toxygen/smileys/default/ly.png differ
diff --git a/toxygen/smileys/default/ma.png b/toxygen/smileys/default/ma.png
old mode 100644
new mode 100755
index e720d87..f386770
Binary files a/toxygen/smileys/default/ma.png and b/toxygen/smileys/default/ma.png differ
diff --git a/toxygen/smileys/default/mc.png b/toxygen/smileys/default/mc.png
old mode 100644
new mode 100755
index 5666a75..1aa830f
Binary files a/toxygen/smileys/default/mc.png and b/toxygen/smileys/default/mc.png differ
diff --git a/toxygen/smileys/default/md.png b/toxygen/smileys/default/md.png
old mode 100644
new mode 100755
index 1bc8b47..4e92c18
Binary files a/toxygen/smileys/default/md.png and b/toxygen/smileys/default/md.png differ
diff --git a/toxygen/smileys/default/me.png b/toxygen/smileys/default/me.png
index 7449387..ac72535 100644
Binary files a/toxygen/smileys/default/me.png and b/toxygen/smileys/default/me.png differ
diff --git a/toxygen/smileys/default/mg.png b/toxygen/smileys/default/mg.png
old mode 100644
new mode 100755
index 65e7f27..d2715b3
Binary files a/toxygen/smileys/default/mg.png and b/toxygen/smileys/default/mg.png differ
diff --git a/toxygen/smileys/default/mh.png b/toxygen/smileys/default/mh.png
old mode 100644
new mode 100755
index 67cc066..fb523a8
Binary files a/toxygen/smileys/default/mh.png and b/toxygen/smileys/default/mh.png differ
diff --git a/toxygen/smileys/default/mk.png b/toxygen/smileys/default/mk.png
old mode 100644
new mode 100755
index 2e50b58..db173aa
Binary files a/toxygen/smileys/default/mk.png and b/toxygen/smileys/default/mk.png differ
diff --git a/toxygen/smileys/default/ml.png b/toxygen/smileys/default/ml.png
old mode 100644
new mode 100755
index 47844ad..2cec8ba
Binary files a/toxygen/smileys/default/ml.png and b/toxygen/smileys/default/ml.png differ
diff --git a/toxygen/smileys/default/mm.png b/toxygen/smileys/default/mm.png
old mode 100644
new mode 100755
index db89f01..f464f67
Binary files a/toxygen/smileys/default/mm.png and b/toxygen/smileys/default/mm.png differ
diff --git a/toxygen/smileys/default/mn.png b/toxygen/smileys/default/mn.png
old mode 100644
new mode 100755
index c976ecd..9396355
Binary files a/toxygen/smileys/default/mn.png and b/toxygen/smileys/default/mn.png differ
diff --git a/toxygen/smileys/default/mo.png b/toxygen/smileys/default/mo.png
old mode 100644
new mode 100755
index cf8113c..deb801d
Binary files a/toxygen/smileys/default/mo.png and b/toxygen/smileys/default/mo.png differ
diff --git a/toxygen/smileys/default/mp.png b/toxygen/smileys/default/mp.png
old mode 100644
new mode 100755
index 013e183..298d588
Binary files a/toxygen/smileys/default/mp.png and b/toxygen/smileys/default/mp.png differ
diff --git a/toxygen/smileys/default/mq.png b/toxygen/smileys/default/mq.png
old mode 100644
new mode 100755
index 1920168..010143b
Binary files a/toxygen/smileys/default/mq.png and b/toxygen/smileys/default/mq.png differ
diff --git a/toxygen/smileys/default/mr.png b/toxygen/smileys/default/mr.png
old mode 100644
new mode 100755
index 06984ac..319546b
Binary files a/toxygen/smileys/default/mr.png and b/toxygen/smileys/default/mr.png differ
diff --git a/toxygen/smileys/default/ms.png b/toxygen/smileys/default/ms.png
old mode 100644
new mode 100755
index ab6f7fb..d4cbb43
Binary files a/toxygen/smileys/default/ms.png and b/toxygen/smileys/default/ms.png differ
diff --git a/toxygen/smileys/default/mt.png b/toxygen/smileys/default/mt.png
old mode 100644
new mode 100755
index 0d1f30c..00af948
Binary files a/toxygen/smileys/default/mt.png and b/toxygen/smileys/default/mt.png differ
diff --git a/toxygen/smileys/default/mu.png b/toxygen/smileys/default/mu.png
old mode 100644
new mode 100755
index e0191f7..b7fdce1
Binary files a/toxygen/smileys/default/mu.png and b/toxygen/smileys/default/mu.png differ
diff --git a/toxygen/smileys/default/mv.png b/toxygen/smileys/default/mv.png
old mode 100644
new mode 100755
index 44c2b5f..5073d9e
Binary files a/toxygen/smileys/default/mv.png and b/toxygen/smileys/default/mv.png differ
diff --git a/toxygen/smileys/default/mw.png b/toxygen/smileys/default/mw.png
old mode 100644
new mode 100755
index 675d2c2..13886e9
Binary files a/toxygen/smileys/default/mw.png and b/toxygen/smileys/default/mw.png differ
diff --git a/toxygen/smileys/default/mx.png b/toxygen/smileys/default/mx.png
old mode 100644
new mode 100755
index 0c11c5a..5bc58ab
Binary files a/toxygen/smileys/default/mx.png and b/toxygen/smileys/default/mx.png differ
diff --git a/toxygen/smileys/default/my.png b/toxygen/smileys/default/my.png
old mode 100644
new mode 100755
index 2757cf3..9034cba
Binary files a/toxygen/smileys/default/my.png and b/toxygen/smileys/default/my.png differ
diff --git a/toxygen/smileys/default/mz.png b/toxygen/smileys/default/mz.png
old mode 100644
new mode 100755
index e4ff602..76405e0
Binary files a/toxygen/smileys/default/mz.png and b/toxygen/smileys/default/mz.png differ
diff --git a/toxygen/smileys/default/na.png b/toxygen/smileys/default/na.png
old mode 100644
new mode 100755
index 4bf47fd..63358c6
Binary files a/toxygen/smileys/default/na.png and b/toxygen/smileys/default/na.png differ
diff --git a/toxygen/smileys/default/nc.png b/toxygen/smileys/default/nc.png
old mode 100644
new mode 100755
index a4c6811..2cad283
Binary files a/toxygen/smileys/default/nc.png and b/toxygen/smileys/default/nc.png differ
diff --git a/toxygen/smileys/default/ne.png b/toxygen/smileys/default/ne.png
old mode 100644
new mode 100755
index bc088df..d85f424
Binary files a/toxygen/smileys/default/ne.png and b/toxygen/smileys/default/ne.png differ
diff --git a/toxygen/smileys/default/nf.png b/toxygen/smileys/default/nf.png
old mode 100644
new mode 100755
index a02fcb8..f9bcdda
Binary files a/toxygen/smileys/default/nf.png and b/toxygen/smileys/default/nf.png differ
diff --git a/toxygen/smileys/default/ng.png b/toxygen/smileys/default/ng.png
old mode 100644
new mode 100755
index cc46ceb..3eea2e0
Binary files a/toxygen/smileys/default/ng.png and b/toxygen/smileys/default/ng.png differ
diff --git a/toxygen/smileys/default/ni.png b/toxygen/smileys/default/ni.png
old mode 100644
new mode 100755
index 4171012..3969aaa
Binary files a/toxygen/smileys/default/ni.png and b/toxygen/smileys/default/ni.png differ
diff --git a/toxygen/smileys/default/nl.png b/toxygen/smileys/default/nl.png
old mode 100644
new mode 100755
index 00df165..fe44791
Binary files a/toxygen/smileys/default/nl.png and b/toxygen/smileys/default/nl.png differ
diff --git a/toxygen/smileys/default/no.png b/toxygen/smileys/default/no.png
old mode 100644
new mode 100755
index d76758b..160b6b5
Binary files a/toxygen/smileys/default/no.png and b/toxygen/smileys/default/no.png differ
diff --git a/toxygen/smileys/default/np.png b/toxygen/smileys/default/np.png
old mode 100644
new mode 100755
index 48b9d27..aeb058b
Binary files a/toxygen/smileys/default/np.png and b/toxygen/smileys/default/np.png differ
diff --git a/toxygen/smileys/default/nr.png b/toxygen/smileys/default/nr.png
old mode 100644
new mode 100755
index 10f1242..705fc33
Binary files a/toxygen/smileys/default/nr.png and b/toxygen/smileys/default/nr.png differ
diff --git a/toxygen/smileys/default/nu.png b/toxygen/smileys/default/nu.png
old mode 100644
new mode 100755
index abae7f1..c3ce4ae
Binary files a/toxygen/smileys/default/nu.png and b/toxygen/smileys/default/nu.png differ
diff --git a/toxygen/smileys/default/nz.png b/toxygen/smileys/default/nz.png
old mode 100644
new mode 100755
index c92a8a9..10d6306
Binary files a/toxygen/smileys/default/nz.png and b/toxygen/smileys/default/nz.png differ
diff --git a/toxygen/smileys/default/om.png b/toxygen/smileys/default/om.png
old mode 100644
new mode 100755
index 53b7a32..2ffba7e
Binary files a/toxygen/smileys/default/om.png and b/toxygen/smileys/default/om.png differ
diff --git a/toxygen/smileys/default/pa.png b/toxygen/smileys/default/pa.png
old mode 100644
new mode 100755
index 6b0c717..9b2ee9a
Binary files a/toxygen/smileys/default/pa.png and b/toxygen/smileys/default/pa.png differ
diff --git a/toxygen/smileys/default/pe.png b/toxygen/smileys/default/pe.png
old mode 100644
new mode 100755
index 2e9d19b..62a0497
Binary files a/toxygen/smileys/default/pe.png and b/toxygen/smileys/default/pe.png differ
diff --git a/toxygen/smileys/default/pf.png b/toxygen/smileys/default/pf.png
old mode 100644
new mode 100755
index cac6edd..771a0f6
Binary files a/toxygen/smileys/default/pf.png and b/toxygen/smileys/default/pf.png differ
diff --git a/toxygen/smileys/default/pg.png b/toxygen/smileys/default/pg.png
old mode 100644
new mode 100755
index 0ee77b3..10d6233
Binary files a/toxygen/smileys/default/pg.png and b/toxygen/smileys/default/pg.png differ
diff --git a/toxygen/smileys/default/ph.png b/toxygen/smileys/default/ph.png
old mode 100644
new mode 100755
index 464cb77..b89e159
Binary files a/toxygen/smileys/default/ph.png and b/toxygen/smileys/default/ph.png differ
diff --git a/toxygen/smileys/default/pk.png b/toxygen/smileys/default/pk.png
old mode 100644
new mode 100755
index de39a39..e9df70c
Binary files a/toxygen/smileys/default/pk.png and b/toxygen/smileys/default/pk.png differ
diff --git a/toxygen/smileys/default/pl.png b/toxygen/smileys/default/pl.png
old mode 100644
new mode 100755
index ed09b83..d413d01
Binary files a/toxygen/smileys/default/pl.png and b/toxygen/smileys/default/pl.png differ
diff --git a/toxygen/smileys/default/pm.png b/toxygen/smileys/default/pm.png
old mode 100644
new mode 100755
index 507dd9f..ba91d2c
Binary files a/toxygen/smileys/default/pm.png and b/toxygen/smileys/default/pm.png differ
diff --git a/toxygen/smileys/default/pn.png b/toxygen/smileys/default/pn.png
old mode 100644
new mode 100755
index fb14070..aa9344f
Binary files a/toxygen/smileys/default/pn.png and b/toxygen/smileys/default/pn.png differ
diff --git a/toxygen/smileys/default/pr.png b/toxygen/smileys/default/pr.png
old mode 100644
new mode 100755
index 452991e..82d9130
Binary files a/toxygen/smileys/default/pr.png and b/toxygen/smileys/default/pr.png differ
diff --git a/toxygen/smileys/default/ps.png b/toxygen/smileys/default/ps.png
old mode 100644
new mode 100755
index ead1ff3..f5f5477
Binary files a/toxygen/smileys/default/ps.png and b/toxygen/smileys/default/ps.png differ
diff --git a/toxygen/smileys/default/pt.png b/toxygen/smileys/default/pt.png
old mode 100644
new mode 100755
index 98dddc4..ece7980
Binary files a/toxygen/smileys/default/pt.png and b/toxygen/smileys/default/pt.png differ
diff --git a/toxygen/smileys/default/pw.png b/toxygen/smileys/default/pw.png
old mode 100644
new mode 100755
index e7b5f90..6178b25
Binary files a/toxygen/smileys/default/pw.png and b/toxygen/smileys/default/pw.png differ
diff --git a/toxygen/smileys/default/py.png b/toxygen/smileys/default/py.png
old mode 100644
new mode 100755
index ae83d82..cb8723c
Binary files a/toxygen/smileys/default/py.png and b/toxygen/smileys/default/py.png differ
diff --git a/toxygen/smileys/default/qa.png b/toxygen/smileys/default/qa.png
old mode 100644
new mode 100755
index edea054..ed4c621
Binary files a/toxygen/smileys/default/qa.png and b/toxygen/smileys/default/qa.png differ
diff --git a/toxygen/smileys/default/re.png b/toxygen/smileys/default/re.png
old mode 100644
new mode 100755
index 7a9a7fa..8332c4e
Binary files a/toxygen/smileys/default/re.png and b/toxygen/smileys/default/re.png differ
diff --git a/toxygen/smileys/default/ro.png b/toxygen/smileys/default/ro.png
old mode 100644
new mode 100755
index 6d38ac7..57e74a6
Binary files a/toxygen/smileys/default/ro.png and b/toxygen/smileys/default/ro.png differ
diff --git a/toxygen/smileys/default/rs.png b/toxygen/smileys/default/rs.png
index 178e8b4..9439a5b 100644
Binary files a/toxygen/smileys/default/rs.png and b/toxygen/smileys/default/rs.png differ
diff --git a/toxygen/smileys/default/ru.png b/toxygen/smileys/default/ru.png
old mode 100644
new mode 100755
index 6f73c01..47da421
Binary files a/toxygen/smileys/default/ru.png and b/toxygen/smileys/default/ru.png differ
diff --git a/toxygen/smileys/default/rw.png b/toxygen/smileys/default/rw.png
old mode 100644
new mode 100755
index 33f99b9..5356491
Binary files a/toxygen/smileys/default/rw.png and b/toxygen/smileys/default/rw.png differ
diff --git a/toxygen/smileys/default/sa.png b/toxygen/smileys/default/sa.png
old mode 100644
new mode 100755
index 2057140..b4641c7
Binary files a/toxygen/smileys/default/sa.png and b/toxygen/smileys/default/sa.png differ
diff --git a/toxygen/smileys/default/sb.png b/toxygen/smileys/default/sb.png
old mode 100644
new mode 100755
index 7b61cab..a9937cc
Binary files a/toxygen/smileys/default/sb.png and b/toxygen/smileys/default/sb.png differ
diff --git a/toxygen/smileys/default/sc.png b/toxygen/smileys/default/sc.png
old mode 100644
new mode 100755
index a222766..39ee371
Binary files a/toxygen/smileys/default/sc.png and b/toxygen/smileys/default/sc.png differ
diff --git a/toxygen/smileys/default/scotland.png b/toxygen/smileys/default/scotland.png
old mode 100644
new mode 100755
index 44ef46d..a0e57b4
Binary files a/toxygen/smileys/default/scotland.png and b/toxygen/smileys/default/scotland.png differ
diff --git a/toxygen/smileys/default/sd.png b/toxygen/smileys/default/sd.png
old mode 100644
new mode 100755
index d3a1f2b..eaab69e
Binary files a/toxygen/smileys/default/sd.png and b/toxygen/smileys/default/sd.png differ
diff --git a/toxygen/smileys/default/se.png b/toxygen/smileys/default/se.png
old mode 100644
new mode 100755
index 995f965..1994653
Binary files a/toxygen/smileys/default/se.png and b/toxygen/smileys/default/se.png differ
diff --git a/toxygen/smileys/default/sg.png b/toxygen/smileys/default/sg.png
old mode 100644
new mode 100755
index 35f8df7..dd34d61
Binary files a/toxygen/smileys/default/sg.png and b/toxygen/smileys/default/sg.png differ
diff --git a/toxygen/smileys/default/sh.png b/toxygen/smileys/default/sh.png
old mode 100644
new mode 100755
index 34f77a7..4b1d2a2
Binary files a/toxygen/smileys/default/sh.png and b/toxygen/smileys/default/sh.png differ
diff --git a/toxygen/smileys/default/si.png b/toxygen/smileys/default/si.png
old mode 100644
new mode 100755
index 0e218b6..bb1476f
Binary files a/toxygen/smileys/default/si.png and b/toxygen/smileys/default/si.png differ
diff --git a/toxygen/smileys/default/sj.png b/toxygen/smileys/default/sj.png
old mode 100644
new mode 100755
index eb91f75..160b6b5
Binary files a/toxygen/smileys/default/sj.png and b/toxygen/smileys/default/sj.png differ
diff --git a/toxygen/smileys/default/sk.png b/toxygen/smileys/default/sk.png
old mode 100644
new mode 100755
index 1d389f7..7ccbc82
Binary files a/toxygen/smileys/default/sk.png and b/toxygen/smileys/default/sk.png differ
diff --git a/toxygen/smileys/default/sl.png b/toxygen/smileys/default/sl.png
old mode 100644
new mode 100755
index 4e620b3..12d812d
Binary files a/toxygen/smileys/default/sl.png and b/toxygen/smileys/default/sl.png differ
diff --git a/toxygen/smileys/default/sm.png b/toxygen/smileys/default/sm.png
old mode 100644
new mode 100755
index 9b02225..3df2fdc
Binary files a/toxygen/smileys/default/sm.png and b/toxygen/smileys/default/sm.png differ
diff --git a/toxygen/smileys/default/sn.png b/toxygen/smileys/default/sn.png
old mode 100644
new mode 100755
index 188e42a..eabb71d
Binary files a/toxygen/smileys/default/sn.png and b/toxygen/smileys/default/sn.png differ
diff --git a/toxygen/smileys/default/so.png b/toxygen/smileys/default/so.png
old mode 100644
new mode 100755
index f1a1dfc..4a1ea4b
Binary files a/toxygen/smileys/default/so.png and b/toxygen/smileys/default/so.png differ
diff --git a/toxygen/smileys/default/sr.png b/toxygen/smileys/default/sr.png
old mode 100644
new mode 100755
index d6be029..5eff927
Binary files a/toxygen/smileys/default/sr.png and b/toxygen/smileys/default/sr.png differ
diff --git a/toxygen/smileys/default/st.png b/toxygen/smileys/default/st.png
old mode 100644
new mode 100755
index 0786db0..2978557
Binary files a/toxygen/smileys/default/st.png and b/toxygen/smileys/default/st.png differ
diff --git a/toxygen/smileys/default/sv.png b/toxygen/smileys/default/sv.png
old mode 100644
new mode 100755
index 7b533d1..2498799
Binary files a/toxygen/smileys/default/sv.png and b/toxygen/smileys/default/sv.png differ
diff --git a/toxygen/smileys/default/sy.png b/toxygen/smileys/default/sy.png
old mode 100644
new mode 100755
index dfecd39..f5ce30d
Binary files a/toxygen/smileys/default/sy.png and b/toxygen/smileys/default/sy.png differ
diff --git a/toxygen/smileys/default/sz.png b/toxygen/smileys/default/sz.png
old mode 100644
new mode 100755
index 4d4fb90..914ee86
Binary files a/toxygen/smileys/default/sz.png and b/toxygen/smileys/default/sz.png differ
diff --git a/toxygen/smileys/default/tc.png b/toxygen/smileys/default/tc.png
old mode 100644
new mode 100755
index eaec510..8fc1156
Binary files a/toxygen/smileys/default/tc.png and b/toxygen/smileys/default/tc.png differ
diff --git a/toxygen/smileys/default/td.png b/toxygen/smileys/default/td.png
old mode 100644
new mode 100755
index 6236dfa..667f21f
Binary files a/toxygen/smileys/default/td.png and b/toxygen/smileys/default/td.png differ
diff --git a/toxygen/smileys/default/tf.png b/toxygen/smileys/default/tf.png
old mode 100644
new mode 100755
index 8534274..80529a4
Binary files a/toxygen/smileys/default/tf.png and b/toxygen/smileys/default/tf.png differ
diff --git a/toxygen/smileys/default/tg.png b/toxygen/smileys/default/tg.png
old mode 100644
new mode 100755
index ad50b11..3aa00ad
Binary files a/toxygen/smileys/default/tg.png and b/toxygen/smileys/default/tg.png differ
diff --git a/toxygen/smileys/default/th.png b/toxygen/smileys/default/th.png
old mode 100644
new mode 100755
index bb00577..dd8ba91
Binary files a/toxygen/smileys/default/th.png and b/toxygen/smileys/default/th.png differ
diff --git a/toxygen/smileys/default/tj.png b/toxygen/smileys/default/tj.png
old mode 100644
new mode 100755
index 060d647..617bf64
Binary files a/toxygen/smileys/default/tj.png and b/toxygen/smileys/default/tj.png differ
diff --git a/toxygen/smileys/default/tk.png b/toxygen/smileys/default/tk.png
old mode 100644
new mode 100755
index 050fd63..67b8c8c
Binary files a/toxygen/smileys/default/tk.png and b/toxygen/smileys/default/tk.png differ
diff --git a/toxygen/smileys/default/tl.png b/toxygen/smileys/default/tl.png
old mode 100644
new mode 100755
index a4fc566..77da181
Binary files a/toxygen/smileys/default/tl.png and b/toxygen/smileys/default/tl.png differ
diff --git a/toxygen/smileys/default/tm.png b/toxygen/smileys/default/tm.png
old mode 100644
new mode 100755
index 2981188..828020e
Binary files a/toxygen/smileys/default/tm.png and b/toxygen/smileys/default/tm.png differ
diff --git a/toxygen/smileys/default/tn.png b/toxygen/smileys/default/tn.png
old mode 100644
new mode 100755
index 202faea..183cdd3
Binary files a/toxygen/smileys/default/tn.png and b/toxygen/smileys/default/tn.png differ
diff --git a/toxygen/smileys/default/to.png b/toxygen/smileys/default/to.png
old mode 100644
new mode 100755
index 63949b1..f89b8ba
Binary files a/toxygen/smileys/default/to.png and b/toxygen/smileys/default/to.png differ
diff --git a/toxygen/smileys/default/tox.png b/toxygen/smileys/default/tox.png
old mode 100644
new mode 100755
index ad5e1d5..1c551f7
Binary files a/toxygen/smileys/default/tox.png and b/toxygen/smileys/default/tox.png differ
diff --git a/toxygen/smileys/default/tr.png b/toxygen/smileys/default/tr.png
old mode 100644
new mode 100755
index 58ee839..be32f77
Binary files a/toxygen/smileys/default/tr.png and b/toxygen/smileys/default/tr.png differ
diff --git a/toxygen/smileys/default/tt.png b/toxygen/smileys/default/tt.png
old mode 100644
new mode 100755
index e7d7502..2a11c1e
Binary files a/toxygen/smileys/default/tt.png and b/toxygen/smileys/default/tt.png differ
diff --git a/toxygen/smileys/default/tv.png b/toxygen/smileys/default/tv.png
old mode 100644
new mode 100755
index 83720a3..28274c5
Binary files a/toxygen/smileys/default/tv.png and b/toxygen/smileys/default/tv.png differ
diff --git a/toxygen/smileys/default/tw.png b/toxygen/smileys/default/tw.png
old mode 100644
new mode 100755
index 3e751fd..f31c654
Binary files a/toxygen/smileys/default/tw.png and b/toxygen/smileys/default/tw.png differ
diff --git a/toxygen/smileys/default/tz.png b/toxygen/smileys/default/tz.png
old mode 100644
new mode 100755
index e1cde1b..c00ff79
Binary files a/toxygen/smileys/default/tz.png and b/toxygen/smileys/default/tz.png differ
diff --git a/toxygen/smileys/default/ua.png b/toxygen/smileys/default/ua.png
old mode 100644
new mode 100755
index 100319b..09563a2
Binary files a/toxygen/smileys/default/ua.png and b/toxygen/smileys/default/ua.png differ
diff --git a/toxygen/smileys/default/ug.png b/toxygen/smileys/default/ug.png
old mode 100644
new mode 100755
index 659f629..33f4aff
Binary files a/toxygen/smileys/default/ug.png and b/toxygen/smileys/default/ug.png differ
diff --git a/toxygen/smileys/default/um.png b/toxygen/smileys/default/um.png
old mode 100644
new mode 100755
index 2f425ad..c1dd965
Binary files a/toxygen/smileys/default/um.png and b/toxygen/smileys/default/um.png differ
diff --git a/toxygen/smileys/default/us.png b/toxygen/smileys/default/us.png
old mode 100644
new mode 100755
index fae49a0..10f451f
Binary files a/toxygen/smileys/default/us.png and b/toxygen/smileys/default/us.png differ
diff --git a/toxygen/smileys/default/uy.png b/toxygen/smileys/default/uy.png
old mode 100644
new mode 100755
index dc42cd1..31d948a
Binary files a/toxygen/smileys/default/uy.png and b/toxygen/smileys/default/uy.png differ
diff --git a/toxygen/smileys/default/uz.png b/toxygen/smileys/default/uz.png
old mode 100644
new mode 100755
index e2a6331..fef5dc1
Binary files a/toxygen/smileys/default/uz.png and b/toxygen/smileys/default/uz.png differ
diff --git a/toxygen/smileys/default/va.png b/toxygen/smileys/default/va.png
old mode 100644
new mode 100755
index f6ac0a5..b31eaf2
Binary files a/toxygen/smileys/default/va.png and b/toxygen/smileys/default/va.png differ
diff --git a/toxygen/smileys/default/vc.png b/toxygen/smileys/default/vc.png
old mode 100644
new mode 100755
index d737c4b..8fa17b0
Binary files a/toxygen/smileys/default/vc.png and b/toxygen/smileys/default/vc.png differ
diff --git a/toxygen/smileys/default/ve.png b/toxygen/smileys/default/ve.png
old mode 100644
new mode 100755
index 629fe46..00c90f9
Binary files a/toxygen/smileys/default/ve.png and b/toxygen/smileys/default/ve.png differ
diff --git a/toxygen/smileys/default/vg.png b/toxygen/smileys/default/vg.png
old mode 100644
new mode 100755
index b250b1f..4156907
Binary files a/toxygen/smileys/default/vg.png and b/toxygen/smileys/default/vg.png differ
diff --git a/toxygen/smileys/default/vi.png b/toxygen/smileys/default/vi.png
old mode 100644
new mode 100755
index 22623b0..ed26915
Binary files a/toxygen/smileys/default/vi.png and b/toxygen/smileys/default/vi.png differ
diff --git a/toxygen/smileys/default/vn.png b/toxygen/smileys/default/vn.png
old mode 100644
new mode 100755
index 76c3aa7..ec7cd48
Binary files a/toxygen/smileys/default/vn.png and b/toxygen/smileys/default/vn.png differ
diff --git a/toxygen/smileys/default/vu.png b/toxygen/smileys/default/vu.png
old mode 100644
new mode 100755
index c92506e..b3397bc
Binary files a/toxygen/smileys/default/vu.png and b/toxygen/smileys/default/vu.png differ
diff --git a/toxygen/smileys/default/wales.png b/toxygen/smileys/default/wales.png
old mode 100644
new mode 100755
index bc0200b..e0d7cee
Binary files a/toxygen/smileys/default/wales.png and b/toxygen/smileys/default/wales.png differ
diff --git a/toxygen/smileys/default/wf.png b/toxygen/smileys/default/wf.png
old mode 100644
new mode 100755
index 879d578..9f95587
Binary files a/toxygen/smileys/default/wf.png and b/toxygen/smileys/default/wf.png differ
diff --git a/toxygen/smileys/default/ws.png b/toxygen/smileys/default/ws.png
old mode 100644
new mode 100755
index 3f3e7d7..c169508
Binary files a/toxygen/smileys/default/ws.png and b/toxygen/smileys/default/ws.png differ
diff --git a/toxygen/smileys/default/wtox.png b/toxygen/smileys/default/wtox.png
old mode 100644
new mode 100755
index a5c49a7..d95f396
Binary files a/toxygen/smileys/default/wtox.png and b/toxygen/smileys/default/wtox.png differ
diff --git a/toxygen/smileys/default/ye.png b/toxygen/smileys/default/ye.png
old mode 100644
new mode 100755
index 9dcf729..468dfad
Binary files a/toxygen/smileys/default/ye.png and b/toxygen/smileys/default/ye.png differ
diff --git a/toxygen/smileys/default/yt.png b/toxygen/smileys/default/yt.png
old mode 100644
new mode 100755
index 6170745..c298f37
Binary files a/toxygen/smileys/default/yt.png and b/toxygen/smileys/default/yt.png differ
diff --git a/toxygen/smileys/default/za.png b/toxygen/smileys/default/za.png
old mode 100644
new mode 100755
index ad4d0eb..57c58e2
Binary files a/toxygen/smileys/default/za.png and b/toxygen/smileys/default/za.png differ
diff --git a/toxygen/smileys/default/zm.png b/toxygen/smileys/default/zm.png
old mode 100644
new mode 100755
index 38d8a3c..c25b07b
Binary files a/toxygen/smileys/default/zm.png and b/toxygen/smileys/default/zm.png differ
diff --git a/toxygen/smileys/default/zw.png b/toxygen/smileys/default/zw.png
old mode 100644
new mode 100755
index e8e51b7..53c9725
Binary files a/toxygen/smileys/default/zw.png and b/toxygen/smileys/default/zw.png differ
diff --git a/toxygen/smileys/ksk/angry.png b/toxygen/smileys/ksk/angry.png
deleted file mode 100644
index 2659bf2..0000000
Binary files a/toxygen/smileys/ksk/angry.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/angry2.png b/toxygen/smileys/ksk/angry2.png
deleted file mode 100644
index 6ecbb1e..0000000
Binary files a/toxygen/smileys/ksk/angry2.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/angry3.png b/toxygen/smileys/ksk/angry3.png
deleted file mode 100644
index 9b9ebc0..0000000
Binary files a/toxygen/smileys/ksk/angry3.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/blink.png b/toxygen/smileys/ksk/blink.png
deleted file mode 100644
index b7fe238..0000000
Binary files a/toxygen/smileys/ksk/blink.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/bluestar.png b/toxygen/smileys/ksk/bluestar.png
deleted file mode 100644
index 21f37ca..0000000
Binary files a/toxygen/smileys/ksk/bluestar.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/calm.png b/toxygen/smileys/ksk/calm.png
deleted file mode 100644
index da19990..0000000
Binary files a/toxygen/smileys/ksk/calm.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/config.json b/toxygen/smileys/ksk/config.json
deleted file mode 100644
index 0cc910e..0000000
--- a/toxygen/smileys/ksk/config.json
+++ /dev/null
@@ -1 +0,0 @@
-{"BD": "cool2.png", "v_v": "calm.png", ":/": "getlost.png", ":(": "sad.png", ":)": "smile.png", ":*": "kiss.png", ":animal:": "pawn.png", "=|": "none.png", "=*": "kiss.png", ":heart:": "heart.png", "B]": "cool.png", "=o": "shocked.png", ":0": "shocked.png", "=S": "none2.png", "=]": "smile2.png", "=\\": "getlost.png", "B-)": "cool.png", ":pawn:": "pawn.png", "=O": "shocked.png", ">:\\": "angry2.png", ":redstar:": "redstar.png", ":o": "shocked.png", "=0": "shocked.png", "B-D": "cool2.png", ":|": "none.png", ":''(": "cry.png", "=/": "getlost.png", "=)": "smile.png", "=(": "sad.png", "B-]": "cool.png", ":O": "shocked.png", ":D": "grin.png", "B)": "cool.png", ":'(": "cry.png", ":]": "smile2.png", ":music:": "notes.png", ":P": "tongue.png", ":S": "none2.png", ":evil:": "evil.png", ":-O": "shocked.png", ":zzzzz:": "zzz.png", ">:[]": "angry.png", ";|": "none.png", ":-\\": "getlost.png", ":-]": "smile2.png", ":-S": "none2.png", ":-P": "tongue.png", ";o": "shocked.png", ";S": "none2.png", ":\\": "getlost.png", ";P": "tongue.png", ":pet:": "pawn.png", ":-o": "shocked.png", ";]": "blink.png", ";\\": "getlost.png", ":oops:": "oops.png", ":-|": "none.png", ";D": "grin.png", ";O": "shocked.png", "@->-": "flower.png", ";0": "shocked.png", ":zzz:": "zzz.png", ":cool2:": "cool2.png", "^_^": "pleased.png", ":)))": "grin.png", ";)": "blink.png", ";/": "getlost.png", ":-*": "kiss.png", ":-(": "sad.png", ":-)": "smile.png", "8-[]": "scared.png", ":cool:": "cool.png", ":kiss:": "kiss.png", ":notes:": "notes.png", ":calm:": "calm.png", ":-0": "shocked.png", ":greenstar:": "greenstar.png", ">:][": "angry.png", ">:]]": "evil2.png", "B))": "cool2.png", ">:)": "evil.png", ">:(": "angry3.png", ">:/": "angry2.png", ":lol:": "lol.png", ":scared:": "scared.png", ">:>": "evil.png", ">:<": "angry3.png", ">:D": "evil2.png", "B]]": "cool2.png", ">:((": "angry3.png", ">:[": "angry3.png", ":sick:": "unwell.png", ":-/": "getlost.png", ":cry:": "cry.png", "<3": "heart.png", ":leaf:": "leaf.png", ">:))": "evil2.png", ":bluestar:": "bluestar.png", ";-0": "shocked.png", ":weed:": "leaf.png", ":zzzz:": "zzz.png", ":sing:": "notes.png", ":yellowstar:": "yellowstar.png", ";-/": "getlost.png", ";-)": "blink.png", ":dead:": "dead.png", ";-S": "none2.png", "^^": "pleased.png", ";-P": "tongue.png", ";-]": "blink.png", ";-\\": "getlost.png", ":flower:": "flower.png", ":puke:": "unwell.png", ";-O": "shocked.png", ":love:": "heart.png", ";-o": "shocked.png", ":))))": "grin.png", ":))": "grin.png"}
diff --git a/toxygen/smileys/ksk/cool.png b/toxygen/smileys/ksk/cool.png
deleted file mode 100644
index 891ed33..0000000
Binary files a/toxygen/smileys/ksk/cool.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/cool2.png b/toxygen/smileys/ksk/cool2.png
deleted file mode 100644
index 3dea030..0000000
Binary files a/toxygen/smileys/ksk/cool2.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/cry.png b/toxygen/smileys/ksk/cry.png
deleted file mode 100644
index fea2481..0000000
Binary files a/toxygen/smileys/ksk/cry.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/dead.png b/toxygen/smileys/ksk/dead.png
deleted file mode 100644
index 7b22495..0000000
Binary files a/toxygen/smileys/ksk/dead.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/evil.png b/toxygen/smileys/ksk/evil.png
deleted file mode 100644
index 140a259..0000000
Binary files a/toxygen/smileys/ksk/evil.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/evil2.png b/toxygen/smileys/ksk/evil2.png
deleted file mode 100644
index c01efdd..0000000
Binary files a/toxygen/smileys/ksk/evil2.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/flower.png b/toxygen/smileys/ksk/flower.png
deleted file mode 100644
index 5463fda..0000000
Binary files a/toxygen/smileys/ksk/flower.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/getlost.png b/toxygen/smileys/ksk/getlost.png
deleted file mode 100644
index 2c75727..0000000
Binary files a/toxygen/smileys/ksk/getlost.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/greenstar.png b/toxygen/smileys/ksk/greenstar.png
deleted file mode 100644
index b557c50..0000000
Binary files a/toxygen/smileys/ksk/greenstar.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/grin.png b/toxygen/smileys/ksk/grin.png
deleted file mode 100644
index b35bf24..0000000
Binary files a/toxygen/smileys/ksk/grin.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/heart.png b/toxygen/smileys/ksk/heart.png
deleted file mode 100644
index 25d3d7f..0000000
Binary files a/toxygen/smileys/ksk/heart.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/kiss.png b/toxygen/smileys/ksk/kiss.png
deleted file mode 100644
index 4764d69..0000000
Binary files a/toxygen/smileys/ksk/kiss.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/leaf.png b/toxygen/smileys/ksk/leaf.png
deleted file mode 100644
index 7598896..0000000
Binary files a/toxygen/smileys/ksk/leaf.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/lol.png b/toxygen/smileys/ksk/lol.png
deleted file mode 100644
index 9d42add..0000000
Binary files a/toxygen/smileys/ksk/lol.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/none.png b/toxygen/smileys/ksk/none.png
deleted file mode 100644
index 03d421f..0000000
Binary files a/toxygen/smileys/ksk/none.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/none2.png b/toxygen/smileys/ksk/none2.png
deleted file mode 100644
index 0fc9cf1..0000000
Binary files a/toxygen/smileys/ksk/none2.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/notes.png b/toxygen/smileys/ksk/notes.png
deleted file mode 100644
index 6c07260..0000000
Binary files a/toxygen/smileys/ksk/notes.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/oops.png b/toxygen/smileys/ksk/oops.png
deleted file mode 100644
index 744a2a0..0000000
Binary files a/toxygen/smileys/ksk/oops.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/pawn.png b/toxygen/smileys/ksk/pawn.png
deleted file mode 100644
index cce0cad..0000000
Binary files a/toxygen/smileys/ksk/pawn.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/pleased.png b/toxygen/smileys/ksk/pleased.png
deleted file mode 100644
index 2c7e60d..0000000
Binary files a/toxygen/smileys/ksk/pleased.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/redstar.png b/toxygen/smileys/ksk/redstar.png
deleted file mode 100644
index 33bcdf1..0000000
Binary files a/toxygen/smileys/ksk/redstar.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/sad.png b/toxygen/smileys/ksk/sad.png
deleted file mode 100644
index 0a33174..0000000
Binary files a/toxygen/smileys/ksk/sad.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/scared.png b/toxygen/smileys/ksk/scared.png
deleted file mode 100644
index 1b5c55c..0000000
Binary files a/toxygen/smileys/ksk/scared.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/shocked.png b/toxygen/smileys/ksk/shocked.png
deleted file mode 100644
index 83e0850..0000000
Binary files a/toxygen/smileys/ksk/shocked.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/smile.png b/toxygen/smileys/ksk/smile.png
deleted file mode 100644
index a431ca7..0000000
Binary files a/toxygen/smileys/ksk/smile.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/smile2.png b/toxygen/smileys/ksk/smile2.png
deleted file mode 100644
index 4d003ae..0000000
Binary files a/toxygen/smileys/ksk/smile2.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/tongue.png b/toxygen/smileys/ksk/tongue.png
deleted file mode 100644
index bf6c37e..0000000
Binary files a/toxygen/smileys/ksk/tongue.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/unwell.png b/toxygen/smileys/ksk/unwell.png
deleted file mode 100644
index 5bca721..0000000
Binary files a/toxygen/smileys/ksk/unwell.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/yellowstar.png b/toxygen/smileys/ksk/yellowstar.png
deleted file mode 100644
index 5e00805..0000000
Binary files a/toxygen/smileys/ksk/yellowstar.png and /dev/null differ
diff --git a/toxygen/smileys/ksk/zzz.png b/toxygen/smileys/ksk/zzz.png
deleted file mode 100644
index 0d17073..0000000
Binary files a/toxygen/smileys/ksk/zzz.png and /dev/null differ
diff --git a/toxygen/smileys/starwars/ackbar.png b/toxygen/smileys/starwars/ackbar.png
index 1f8a4d5..0a0a482 100644
Binary files a/toxygen/smileys/starwars/ackbar.png and b/toxygen/smileys/starwars/ackbar.png differ
diff --git a/toxygen/smileys/starwars/boba.png b/toxygen/smileys/starwars/boba.png
index 1c234c5..88789dc 100644
Binary files a/toxygen/smileys/starwars/boba.png and b/toxygen/smileys/starwars/boba.png differ
diff --git a/toxygen/smileys/starwars/c3p0.png b/toxygen/smileys/starwars/c3p0.png
index be5adea..a37df94 100644
Binary files a/toxygen/smileys/starwars/c3p0.png and b/toxygen/smileys/starwars/c3p0.png differ
diff --git a/toxygen/smileys/starwars/chewie.png b/toxygen/smileys/starwars/chewie.png
index 8f5a5f6..669dd36 100644
Binary files a/toxygen/smileys/starwars/chewie.png and b/toxygen/smileys/starwars/chewie.png differ
diff --git a/toxygen/smileys/starwars/confused.png b/toxygen/smileys/starwars/confused.png
index 5cc2fd1..54afd60 100644
Binary files a/toxygen/smileys/starwars/confused.png and b/toxygen/smileys/starwars/confused.png differ
diff --git a/toxygen/smileys/starwars/darthmaul.png b/toxygen/smileys/starwars/darthmaul.png
index 57e8ca1..e536a7e 100644
Binary files a/toxygen/smileys/starwars/darthmaul.png and b/toxygen/smileys/starwars/darthmaul.png differ
diff --git a/toxygen/smileys/starwars/darthsidious.png b/toxygen/smileys/starwars/darthsidious.png
index de36348..6c787c1 100644
Binary files a/toxygen/smileys/starwars/darthsidious.png and b/toxygen/smileys/starwars/darthsidious.png differ
diff --git a/toxygen/smileys/starwars/darthvader.png b/toxygen/smileys/starwars/darthvader.png
index 66c1409..a0b01e4 100644
Binary files a/toxygen/smileys/starwars/darthvader.png and b/toxygen/smileys/starwars/darthvader.png differ
diff --git a/toxygen/smileys/starwars/deathstar.png b/toxygen/smileys/starwars/deathstar.png
index 3ee623c..383e730 100644
Binary files a/toxygen/smileys/starwars/deathstar.png and b/toxygen/smileys/starwars/deathstar.png differ
diff --git a/toxygen/smileys/starwars/dualsith.png b/toxygen/smileys/starwars/dualsith.png
index 732d6a3..39143ec 100644
Binary files a/toxygen/smileys/starwars/dualsith.png and b/toxygen/smileys/starwars/dualsith.png differ
diff --git a/toxygen/smileys/starwars/grin.png b/toxygen/smileys/starwars/grin.png
index 226f31d..8ee5ff0 100644
Binary files a/toxygen/smileys/starwars/grin.png and b/toxygen/smileys/starwars/grin.png differ
diff --git a/toxygen/smileys/starwars/happy.png b/toxygen/smileys/starwars/happy.png
index d1f08ed..68f8717 100644
Binary files a/toxygen/smileys/starwars/happy.png and b/toxygen/smileys/starwars/happy.png differ
diff --git a/toxygen/smileys/starwars/jango.png b/toxygen/smileys/starwars/jango.png
index dc731be..5275e7d 100644
Binary files a/toxygen/smileys/starwars/jango.png and b/toxygen/smileys/starwars/jango.png differ
diff --git a/toxygen/smileys/starwars/jarjarbinks.png b/toxygen/smileys/starwars/jarjarbinks.png
index b83ebbe..802c83a 100644
Binary files a/toxygen/smileys/starwars/jarjarbinks.png and b/toxygen/smileys/starwars/jarjarbinks.png differ
diff --git a/toxygen/smileys/starwars/jedi.png b/toxygen/smileys/starwars/jedi.png
index 6bc3173..f8ff638 100644
Binary files a/toxygen/smileys/starwars/jedi.png and b/toxygen/smileys/starwars/jedi.png differ
diff --git a/toxygen/smileys/starwars/jedi2.png b/toxygen/smileys/starwars/jedi2.png
index 3f3a39d..3550eb8 100644
Binary files a/toxygen/smileys/starwars/jedi2.png and b/toxygen/smileys/starwars/jedi2.png differ
diff --git a/toxygen/smileys/starwars/leia.png b/toxygen/smileys/starwars/leia.png
index 885087d..2e40729 100644
Binary files a/toxygen/smileys/starwars/leia.png and b/toxygen/smileys/starwars/leia.png differ
diff --git a/toxygen/smileys/starwars/mad.png b/toxygen/smileys/starwars/mad.png
index 0380c2e..6109783 100644
Binary files a/toxygen/smileys/starwars/mad.png and b/toxygen/smileys/starwars/mad.png differ
diff --git a/toxygen/smileys/starwars/masteryoda.png b/toxygen/smileys/starwars/masteryoda.png
index 080cc02..9d5f86f 100644
Binary files a/toxygen/smileys/starwars/masteryoda.png and b/toxygen/smileys/starwars/masteryoda.png differ
diff --git a/toxygen/smileys/starwars/r2d2.png b/toxygen/smileys/starwars/r2d2.png
index f0f0f9d..e114b3f 100644
Binary files a/toxygen/smileys/starwars/r2d2.png and b/toxygen/smileys/starwars/r2d2.png differ
diff --git a/toxygen/smileys/starwars/sad.png b/toxygen/smileys/starwars/sad.png
index c556767..484d18f 100644
Binary files a/toxygen/smileys/starwars/sad.png and b/toxygen/smileys/starwars/sad.png differ
diff --git a/toxygen/smileys/starwars/samjackson.png b/toxygen/smileys/starwars/samjackson.png
index 3c109d9..d6f8024 100644
Binary files a/toxygen/smileys/starwars/samjackson.png and b/toxygen/smileys/starwars/samjackson.png differ
diff --git a/toxygen/smileys/starwars/shocked.png b/toxygen/smileys/starwars/shocked.png
index 2770ab3..fa3747d 100644
Binary files a/toxygen/smileys/starwars/shocked.png and b/toxygen/smileys/starwars/shocked.png differ
diff --git a/toxygen/smileys/starwars/sith.png b/toxygen/smileys/starwars/sith.png
index 38223e9..e04a07c 100644
Binary files a/toxygen/smileys/starwars/sith.png and b/toxygen/smileys/starwars/sith.png differ
diff --git a/toxygen/smileys/starwars/smile.png b/toxygen/smileys/starwars/smile.png
index 79beb89..dcc34cb 100644
Binary files a/toxygen/smileys/starwars/smile.png and b/toxygen/smileys/starwars/smile.png differ
diff --git a/toxygen/smileys/starwars/stormtrooper.png b/toxygen/smileys/starwars/stormtrooper.png
index a4a4cfe..5014c1e 100644
Binary files a/toxygen/smileys/starwars/stormtrooper.png and b/toxygen/smileys/starwars/stormtrooper.png differ
diff --git a/toxygen/smileys/starwars/tape.png b/toxygen/smileys/starwars/tape.png
index 04cefdb..866ac05 100644
Binary files a/toxygen/smileys/starwars/tape.png and b/toxygen/smileys/starwars/tape.png differ
diff --git a/toxygen/smileys/starwars/tongue.png b/toxygen/smileys/starwars/tongue.png
index 8c22f1e..b10b914 100644
Binary files a/toxygen/smileys/starwars/tongue.png and b/toxygen/smileys/starwars/tongue.png differ
diff --git a/toxygen/smileys/starwars/wink.png b/toxygen/smileys/starwars/wink.png
index 50d0edc..dd5a292 100644
Binary files a/toxygen/smileys/starwars/wink.png and b/toxygen/smileys/starwars/wink.png differ
diff --git a/toxygen/smileys/starwars/x-wing.png b/toxygen/smileys/starwars/x-wing.png
index e48036b..e2a633a 100644
Binary files a/toxygen/smileys/starwars/x-wing.png and b/toxygen/smileys/starwars/x-wing.png differ
diff --git a/toxygen/smileys/starwars/y-wing.png b/toxygen/smileys/starwars/y-wing.png
index f750184..59d0b52 100644
Binary files a/toxygen/smileys/starwars/y-wing.png and b/toxygen/smileys/starwars/y-wing.png differ
diff --git a/toxygen/stickers/__init__.py b/toxygen/stickers/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/stickers/stickers.py b/toxygen/stickers/stickers.py
deleted file mode 100644
index 56a0a29..0000000
--- a/toxygen/stickers/stickers.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import os
-import utils.util as util
-
-def load_stickers():
-    """
-    :return list of stickers
-    """
-    result = []
-    d = util.get_stickers_directory()
-    keys = [x[1] for x in os.walk(d)][0]
-    for key in keys:
-        path = util.join_path(d, key)
-        files = filter(lambda f: f.endswith('.png'), os.listdir(path))
-        files = map(lambda f: util.join_path(path, f), files)
-        result.extend(files)
-
-    return result
diff --git a/toxygen/stickers/tox/black.png b/toxygen/stickers/tox/black.png
old mode 100644
new mode 100755
index 3a38a70..5d1e0eb
Binary files a/toxygen/stickers/tox/black.png and b/toxygen/stickers/tox/black.png differ
diff --git a/toxygen/stickers/tox/red.png b/toxygen/stickers/tox/red.png
old mode 100644
new mode 100755
index cf7fb77..3185319
Binary files a/toxygen/stickers/tox/red.png and b/toxygen/stickers/tox/red.png differ
diff --git a/toxygen/stickers/tox/tox_logo.png b/toxygen/stickers/tox/tox_logo.png
deleted file mode 100644
index afb2d2d..0000000
Binary files a/toxygen/stickers/tox/tox_logo.png and /dev/null differ
diff --git a/toxygen/stickers/tox/tox_logo_1.png b/toxygen/stickers/tox/tox_logo_1.png
deleted file mode 100644
index 038d833..0000000
Binary files a/toxygen/stickers/tox/tox_logo_1.png and /dev/null differ
diff --git a/toxygen/stickers/tox/white.png b/toxygen/stickers/tox/white.png
old mode 100644
new mode 100755
index bee4a90..745b597
Binary files a/toxygen/stickers/tox/white.png and b/toxygen/stickers/tox/white.png differ
diff --git a/toxygen/styles/dark_style.qss b/toxygen/styles/dark_style.qss
deleted file mode 100644
index ece5ec3..0000000
--- a/toxygen/styles/dark_style.qss
+++ /dev/null
@@ -1,1335 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) <2013-2014> <Colin Duquesnoy>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
-
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
-
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-QToolTip
-{
-    border: 1px solid #3A3939;
-    background-color: rgb(90, 102, 117);;
-    color: white;
-    padding: 1px;
-    opacity: 200;
-}
-
-QWidget
-{
-    color: silver;
-    background-color: #302F2F;
-    selection-background-color: #A9A9A9;
-    selection-color: black;
-    background-clip: border;
-    border-image: none;
-    outline: 0;
-}
-
-QWidget:item:hover
-{
-    background-color: #78879b;
-    color: black;
-}
-
-QWidget:item:selected
-{
-    background-color: #A9A9A9;
-}
-
-QProgressBar:horizontal {
-    border: 1px solid #3A3939;
-    text-align: center;
-    padding: 1px;
-    background: #201F1F;
-}
-QProgressBar::chunk:horizontal {
-    background-color: qlineargradient(spread:reflect, x1:1, y1:0.545, x2:1, y2:0, stop:0 rgba(28, 66, 111, 255), stop:1 rgba(37, 87, 146, 255));
-}
-
-QCheckBox:disabled
-{
-    color: #777777;
-}
-QCheckBox::indicator,
-QGroupBox::indicator
-{
-    width: 18px;
-    height: 18px;
-}
-QGroupBox::indicator
-{
-    margin-left: 2px;
-}
-
-QCheckBox::indicator:unchecked,
-QCheckBox::indicator:unchecked:hover,
-QGroupBox::indicator:unchecked,
-QGroupBox::indicator:unchecked:hover
-{
-    image: url(:/qss_icons/rc/checkbox_unchecked.png);
-}
-
-QCheckBox::indicator:unchecked:focus,
-QCheckBox::indicator:unchecked:pressed,
-QGroupBox::indicator:unchecked:focus,
-QGroupBox::indicator:unchecked:pressed
-{
-  border: none;
-    image: url(:/qss_icons/rc/checkbox_unchecked_focus.png);
-}
-
-QCheckBox::indicator:checked,
-QCheckBox::indicator:checked:hover,
-QGroupBox::indicator:checked,
-QGroupBox::indicator:checked:hover
-{
-    image: url(:/qss_icons/rc/checkbox_checked.png);
-}
-
-QCheckBox::indicator:checked:focus,
-QCheckBox::indicator:checked:pressed,
-QGroupBox::indicator:checked:focus,
-QGroupBox::indicator:checked:pressed
-{
-  border: none;
-    image: url(:/qss_icons/rc/checkbox_checked_focus.png);
-}
-
-QCheckBox::indicator:indeterminate,
-QCheckBox::indicator:indeterminate:hover,
-QCheckBox::indicator:indeterminate:pressed
-QGroupBox::indicator:indeterminate,
-QGroupBox::indicator:indeterminate:hover,
-QGroupBox::indicator:indeterminate:pressed
-{
-    image: url(:/qss_icons/rc/checkbox_indeterminate.png);
-}
-
-QCheckBox::indicator:indeterminate:focus,
-QGroupBox::indicator:indeterminate:focus
-{
-    image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png);
-}
-
-QCheckBox::indicator:checked:disabled,
-QGroupBox::indicator:checked:disabled
-{
-    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);
-}
-
-QCheckBox::indicator:unchecked:disabled,
-QGroupBox::indicator:unchecked:disabled
-{
-    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);
-}
-
-QRadioButton
-{
-    spacing: 5px;
-    outline: none;
-    color: #bbb;
-    margin-bottom: 2px;
-}
-
-QRadioButton:disabled
-{
-    color: #777777;
-}
-QRadioButton::indicator
-{
-    width: 21px;
-    height: 21px;
-}
-
-QRadioButton::indicator:unchecked,
-QRadioButton::indicator:unchecked:hover
-{
-    image: url(:/qss_icons/rc/radio_unchecked.png);
-}
-
-QRadioButton::indicator:unchecked:focus,
-QRadioButton::indicator:unchecked:pressed
-{
-  border: none;
-  outline: none;
-    image: url(:/qss_icons/rc/radio_unchecked_focus.png);
-}
-
-QRadioButton::indicator:checked,
-QRadioButton::indicator:checked:hover
-{
-  border: none;
-  outline: none;
-    image: url(:/qss_icons/rc/radio_checked.png);
-}
-
-QRadioButton::indicator:checked:focus,
-QRadioButton::indicato::menu-arrowr:checked:pressed
-{
-  border: none;
-  outline: none;
-    image: url(:/qss_icons/rc/radio_checked_focus.png);
-}
-
-QRadioButton::indicator:indeterminate,
-QRadioButton::indicator:indeterminate:hover,
-QRadioButton::indicator:indeterminate:pressed
-{
-        image: url(:/qss_icons/rc/radio_indeterminate.png);
-}
-
-QRadioButton::indicator:checked:disabled
-{
-  outline: none;
-  image: url(:/qss_icons/rc/radio_checked_disabled.png);
-}
-
-QRadioButton::indicator:unchecked:disabled
-{
-    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);
-}
-
-
-QMenuBar
-{
-    background-color: #302F2F;
-    color: silver;
-}
-
-QMenuBar::item
-{
-    background: transparent;
-}
-
-QMenuBar::item:selected
-{
-    background: transparent;
-    border: 1px solid #A9A9A9;
-}
-
-QMenuBar::item:pressed
-{
-    border: 1px solid #3A3939;
-    background-color: #A9A9A9;
-    color: black;
-    margin-bottom:-1px;
-    padding-bottom:1px;
-}
-
-QMenu
-{
-    border: 1px solid #3A3939;
-    color: silver;
-    margin: 2px;
-}
-
-QMenu::icon
-{
-    margin: 5px;
-}
-
-QMenu::item
-{
-    padding: 5px 30px 5px 30px;
-    margin-left: 5px;
-    border: 1px solid transparent; /* reserve space for selection border */
-}
-
-QMenu::item:selected
-{
-    color: black;
-}
-
-QMenu::separator {
-    height: 2px;
-    background: lightblue;
-    margin-left: 10px;
-    margin-right: 5px;
-}
-
-QMenu::indicator {
-    width: 18px;
-    height: 18px;
-}
-
-/* non-exclusive indicator = check box style indicator
-   (see QActionGroup::setExclusive) */
-QMenu::indicator:non-exclusive:unchecked {
-    image: url(:/qss_icons/rc/checkbox_unchecked.png);
-}
-
-QMenu::indicator:non-exclusive:unchecked:selected {
-    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);
-}
-
-QMenu::indicator:non-exclusive:checked {
-    image: url(:/qss_icons/rc/checkbox_checked.png);
-}
-
-QMenu::indicator:non-exclusive:checked:selected {
-    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);
-}
-
-/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */
-QMenu::indicator:exclusive:unchecked {
-    image: url(:/qss_icons/rc/radio_unchecked.png);
-}
-
-QMenu::indicator:exclusive:unchecked:selected {
-    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);
-}
-
-QMenu::indicator:exclusive:checked {
-    image: url(:/qss_icons/rc/radio_checked.png);
-}
-
-QMenu::indicator:exclusive:checked:selected {
-    image: url(:/qss_icons/rc/radio_checked_disabled.png);
-}
-
-QMenu::right-arrow {
-    margin: 5px;
-    image: url(:/qss_icons/rc/right_arrow.png)
-}
-
-
-QWidget:disabled
-{
-    color: #404040;
-    background-color: #302F2F;
-}
-
-QAbstractItemView
-{
-    alternate-background-color: #3A3939;
-    color: silver;
-    border: 1px solid 3A3939;
-    border-radius: 2px;
-    padding: 1px;
-}
-
-QWidget:focus, QMenuBar:focus
-{
-    border: 1px solid #78879b;
-}
-
-QTabWidget:focus, QCheckBox:focus, QRadioButton:focus, QSlider:focus
-{
-    border: none;
-}
-
-QLineEdit
-{
-    background-color: #201F1F;
-    padding: 2px;
-    border-style: solid;
-    border: 1px solid #3A3939;
-    border-radius: 2px;
-    color: silver;
-}
-
-QGroupBox {
-    border:1px solid #3A3939;
-    border-radius: 2px;
-    margin-top: 20px;
-}
-
-QGroupBox::title {
-    subcontrol-origin: margin;
-    subcontrol-position: top center;
-    padding-left: 10px;
-    padding-right: 10px;
-    padding-top: 10px;
-}
-
-QAbstractScrollArea
-{
-    border-radius: 2px;
-    border: 1px solid #3A3939;
-    background-color: transparent;
-}
-
-QScrollBar:horizontal
-{
-    height: 15px;
-    margin: 3px 15px 3px 15px;
-    border: 1px transparent #2A2929;
-    border-radius: 4px;
-    background-color: #2A2929;
-}
-
-QScrollBar::handle:horizontal
-{
-    background-color: #605F5F;
-    min-width: 5px;
-    border-radius: 4px;
-}
-
-QScrollBar::add-line:horizontal
-{
-    margin: 0px 3px 0px 3px;
-    border-image: url(:/qss_icons/rc/right_arrow_disabled.png);
-    width: 10px;
-    height: 10px;
-    subcontrol-position: right;
-    subcontrol-origin: margin;
-}
-
-QScrollBar::sub-line:horizontal
-{
-    margin: 0px 3px 0px 3px;
-    border-image: url(:/qss_icons/rc/left_arrow_disabled.png);
-    height: 10px;
-    width: 10px;
-    subcontrol-position: left;
-    subcontrol-origin: margin;
-}
-
-QScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on
-{
-    border-image: url(:/qss_icons/rc/right_arrow.png);
-    height: 10px;
-    width: 10px;
-    subcontrol-position: right;
-    subcontrol-origin: margin;
-}
-
-
-QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on
-{
-    border-image: url(:/qss_icons/rc/left_arrow.png);
-    height: 10px;
-    width: 10px;
-    subcontrol-position: left;
-    subcontrol-origin: margin;
-}
-
-QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal
-{
-    background: none;
-}
-
-
-QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal
-{
-    background: none;
-}
-
-QScrollBar:vertical
-{
-    background-color: #2A2929;
-    width: 15px;
-    margin: 15px 3px 15px 3px;
-    border: 1px transparent #2A2929;
-    border-radius: 4px;
-}
-
-QScrollBar::handle:vertical
-{
-    background-color: #605F5F;
-    min-height: 5px;
-    border-radius: 4px;
-}
-
-QScrollBar::sub-line:vertical
-{
-    margin: 3px 0px 3px 0px;
-    border-image: url(:/qss_icons/rc/up_arrow_disabled.png);
-    height: 10px;
-    width: 10px;
-    subcontrol-position: top;
-    subcontrol-origin: margin;
-}
-
-QScrollBar::add-line:vertical
-{
-    margin: 3px 0px 3px 0px;
-    border-image: url(:/qss_icons/rc/down_arrow_disabled.png);
-    height: 10px;
-    width: 10px;
-    subcontrol-position: bottom;
-    subcontrol-origin: margin;
-}
-
-QScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on
-{
-
-    border-image: url(:/qss_icons/rc/up_arrow.png);
-    height: 10px;
-    width: 10px;
-    subcontrol-position: top;
-    subcontrol-origin: margin;
-}
-
-
-QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on
-{
-    border-image: url(:/qss_icons/rc/down_arrow.png);
-    height: 10px;
-    width: 10px;
-    subcontrol-position: bottom;
-    subcontrol-origin: margin;
-}
-
-QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical
-{
-    background: none;
-}
-
-
-QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
-{
-    background: none;
-}
-
-QTextEdit
-{
-    background-color: #201F1F;
-    color: silver;
-    border: 1px solid #3A3939;
-}
-
-QPlainTextEdit
-{
-    background-color: #201F1F;;
-    color: silver;
-    border-radius: 2px;
-    border: 1px solid #3A3939;
-}
-
-QHeaderView::section
-{
-    background-color: #3A3939;
-    color: silver;
-    padding-left: 4px;
-    border: 1px solid #6c6c6c;
-}
-
-QSizeGrip {
-    image: url(:/qss_icons/rc/sizegrip.png);
-    width: 12px;
-    height: 12px;
-}
-
-
-QMainWindow::separator
-{
-    background-color: #302F2F;
-    color: white;
-    padding-left: 4px;
-    spacing: 2px;
-    border: 1px dashed #3A3939;
-}
-
-QMainWindow::separator:hover
-{
-
-    background-color: #787876;
-    color: white;
-    padding-left: 4px;
-    border: 1px solid #3A3939;
-    spacing: 2px;
-}
-
-
-QMenu::separator
-{
-    height: 1px;
-    background-color: #3A3939;
-    color: white;
-    padding-left: 4px;
-    margin-left: 10px;
-    margin-right: 5px;
-}
-
-
-QFrame
-{
-    border-radius: 2px;
-    border: 1px solid #444;
-}
-
-QFrame[frameShape="0"]
-{
-    border-radius: 2px;
-    border: 1px transparent #444;
-}
-
-QStackedWidget
-{
-    border: 1px transparent black;
-}
-
-QToolBar {
-    border: 1px transparent #393838;
-    background: 1px solid #302F2F;
-    font-weight: bold;
-}
-
-QToolBar::handle:horizontal {
-    image: url(:/qss_icons/rc/Hmovetoolbar.png);
-}
-QToolBar::handle:vertical {
-    image: url(:/qss_icons/rc/Vmovetoolbar.png);
-}
-QToolBar::separator:horizontal {
-    image: url(:/qss_icons/rc/Hsepartoolbar.png);
-}
-QToolBar::separator:vertical {
-    image: url(:/qss_icons/rc/Vsepartoolbars.png);
-}
-
-QPushButton
-{
-    color: silver;
-    background-color: #302F2F;
-    border-width: 1px;
-    border-color: #4A4949;
-    border-style: solid;
-    padding-top: 5px;
-    padding-bottom: 5px;
-    padding-left: 5px;
-    padding-right: 5px;
-    border-radius: 2px;
-    outline: none;
-}
-
-QPushButton:focus
-{
-    border-width: 1px;
-    border-color: #4A4949;
-    border-style: solid;
-}
-
-QPushButton:disabled
-{
-    background-color: #302F2F;
-    border-width: 1px;
-    border-color: #3A3939;
-    border-style: solid;
-    padding-top: 5px;
-    padding-bottom: 5px;
-    padding-left: 10px;
-    padding-right: 10px;
-    /*border-radius: 2px;*/
-    color: #454545;
-}
-
-QComboBox
-{
-    selection-background-color: #A9A9A9;
-    background-color: #201F1F;
-    border-style: solid;
-    border: 1px solid #3A3939;
-    border-radius: 2px;
-    padding: 2px;
-    min-width: 75px;
-}
-
-QPushButton:hover
-{
-    background-color: #3d8ec9;
-    color: white;
-}
-
-QComboBox:hover,QAbstractSpinBox:hover,QLineEdit:hover,QTextEdit:hover,QPlainTextEdit:hover,QAbstractView:hover,QTreeView:hover
-{
-    border: 1px solid #78879b;
-    color: silver;
-}
-
-QComboBox:on
-{
-    background-color: #626873;
-    padding-top: 3px;
-    padding-left: 4px;
-    selection-background-color: #4a4a4a;
-}
-
-QComboBox QAbstractItemView
-{
-    background-color: #201F1F;
-    border-radius: 2px;
-    border: 1px solid #444;
-    selection-background-color: #A9A9A9;
-}
-
-QComboBox::drop-down
-{
-    subcontrol-origin: padding;
-    subcontrol-position: top right;
-    width: 15px;
-
-    border-left-width: 0px;
-    border-left-color: darkgray;
-    border-left-style: solid;
-    border-top-right-radius: 3px;
-    border-bottom-right-radius: 3px;
-}
-
-QComboBox::down-arrow
-{
-    image: url(:/qss_icons/rc/down_arrow_disabled.png);
-}
-
-QComboBox::down-arrow:on, QComboBox::down-arrow:hover,
-QComboBox::down-arrow:focus
-{
-    image: url(:/qss_icons/rc/down_arrow.png);
-}
-
-QAbstractSpinBox {
-    padding-top: 2px;
-    padding-bottom: 2px;
-    border: 1px solid #3A3939;
-    background-color: #201F1F;
-    color: silver;
-    border-radius: 2px;
-    min-width: 75px;
-}
-
-QAbstractSpinBox:up-button
-{
-    background-color: transparent;
-    subcontrol-origin: border;
-    subcontrol-position: center right;
-}
-
-QAbstractSpinBox:down-button
-{
-    background-color: transparent;
-    subcontrol-origin: border;
-    subcontrol-position: center left;
-}
-
-QAbstractSpinBox::up-arrow,QAbstractSpinBox::up-arrow:disabled,QAbstractSpinBox::up-arrow:off {
-    image: url(:/qss_icons/rc/up_arrow_disabled.png);
-    width: 10px;
-    height: 10px;
-}
-QAbstractSpinBox::up-arrow:hover
-{
-    image: url(:/qss_icons/rc/up_arrow.png);
-}
-
-
-QAbstractSpinBox::down-arrow,QAbstractSpinBox::down-arrow:disabled,QAbstractSpinBox::down-arrow:off
-{
-    image: url(:/qss_icons/rc/down_arrow_disabled.png);
-    width: 10px;
-    height: 10px;
-}
-QAbstractSpinBox::down-arrow:hover
-{
-    image: url(:/qss_icons/rc/down_arrow.png);
-}
-
-
-QLabel
-{
-    border: 0px solid black;
-    background-color: transparent;
-}
-
-QTabWidget{
-    border: 1px transparent black;
-}
-
-QTabWidget::pane {
-    border: 1px solid #444;
-    border-radius: 3px;
-    padding: 3px;
-}
-
-QTabBar
-{
-    qproperty-drawBase: 0;
-    left: 5px; /* move to the right by 5px */
-}
-
-QTabBar:focus
-{
-    border: 0px transparent black;
-}
-
-QTabBar::close-button  {
-    image: url(:/qss_icons/rc/close.png);
-    background: transparent;
-}
-
-QTabBar::close-button:hover
-{
-    image: url(:/qss_icons/rc/close-hover.png);
-    background: transparent;
-}
-
-QTabBar::close-button:pressed {
-    image: url(:/qss_icons/rc/close-pressed.png);
-    background: transparent;
-}
-
-/* TOP TABS */
-QTabBar::tab:top {
-    color: #b1b1b1;
-    border: 1px solid #4A4949;
-    border-bottom: 1px transparent black;
-    background-color: #302F2F;
-    padding: 5px;
-    border-top-left-radius: 2px;
-    border-top-right-radius: 2px;
-}
-
-QTabBar::tab:top:!selected
-{
-    color: #b1b1b1;
-    background-color: #201F1F;
-    border: 1px transparent #4A4949;
-    border-bottom: 1px transparent #4A4949;
-    border-top-left-radius: 0px;
-    border-top-right-radius: 0px;
-}
-
-QTabBar::tab:top:!selected:hover {
-    background-color: #48576b;
-}
-
-/* BOTTOM TABS */
-QTabBar::tab:bottom {
-    color: #b1b1b1;
-    border: 1px solid #4A4949;
-    border-top: 1px transparent black;
-    background-color: #302F2F;
-    padding: 5px;
-    border-bottom-left-radius: 2px;
-    border-bottom-right-radius: 2px;
-}
-
-QTabBar::tab:bottom:!selected
-{
-    color: #b1b1b1;
-    background-color: #201F1F;
-    border: 1px transparent #4A4949;
-    border-top: 1px transparent #4A4949;
-    border-bottom-left-radius: 0px;
-    border-bottom-right-radius: 0px;
-}
-
-QTabBar::tab:bottom:!selected:hover {
-    background-color: #78879b;
-}
-
-/* LEFT TABS */
-QTabBar::tab:left {
-    color: #b1b1b1;
-    border: 1px solid #4A4949;
-    border-left: 1px transparent black;
-    background-color: #302F2F;
-    padding: 5px;
-    border-top-right-radius: 2px;
-    border-bottom-right-radius: 2px;
-}
-
-QTabBar::tab:left:!selected
-{
-    color: #b1b1b1;
-    background-color: #201F1F;
-    border: 1px transparent #4A4949;
-    border-right: 1px transparent #4A4949;
-    border-top-right-radius: 0px;
-    border-bottom-right-radius: 0px;
-}
-
-QTabBar::tab:left:!selected:hover {
-    background-color: #48576b;
-}
-
-
-/* RIGHT TABS */
-QTabBar::tab:right {
-    color: #b1b1b1;
-    border: 1px solid #4A4949;
-    border-right: 1px transparent black;
-    background-color: #302F2F;
-    padding: 5px;
-    border-top-left-radius: 2px;
-    border-bottom-left-radius: 2px;
-}
-
-QTabBar::tab:right:!selected
-{
-    color: #b1b1b1;
-    background-color: #201F1F;
-    border: 1px transparent #4A4949;
-    border-right: 1px transparent #4A4949;
-    border-top-left-radius: 0px;
-    border-bottom-left-radius: 0px;
-}
-
-QTabBar::tab:right:!selected:hover {
-    background-color: #48576b;
-}
-
-QTabBar QToolButton::right-arrow:enabled {
-     image: url(:/qss_icons/rc/right_arrow.png);
- }
-
- QTabBar QToolButton::left-arrow:enabled {
-     image: url(:/qss_icons/rc/left_arrow.png);
- }
-
-QTabBar QToolButton::right-arrow:disabled {
-     image: url(:/qss_icons/rc/right_arrow_disabled.png);
- }
-
- QTabBar QToolButton::left-arrow:disabled {
-     image: url(:/qss_icons/rc/left_arrow_disabled.png);
- }
-
-
-QDockWidget {
-    border: 1px solid #403F3F;
-    titlebar-close-icon: url(:/qss_icons/rc/close.png);
-    titlebar-normal-icon: url(:/qss_icons/rc/undock.png);
-}
-
-QDockWidget::close-button, QDockWidget::float-button {
-    border: 1px solid transparent;
-    border-radius: 2px;
-    background: transparent;
-}
-
-QDockWidget::close-button:hover, QDockWidget::float-button:hover {
-    background: rgba(255, 255, 255, 10);
-}
-
-QDockWidget::close-button:pressed, QDockWidget::float-button:pressed {
-    padding: 1px -1px -1px 1px;
-    background: rgba(255, 255, 255, 10);
-}
-
-QTreeView, QListView
-{
-    border: 1px solid #444;
-    background-color: #201F1F;
-}
-
-QTreeView:branch:selected, QTreeView:branch:hover
-{
-    background: url(:/qss_icons/rc/transparent.png);
-}
-
-QTreeView::branch:has-siblings:!adjoins-item {
-    border-image: url(:/qss_icons/rc/transparent.png);
-}
-
-QTreeView::branch:has-siblings:adjoins-item {
-    border-image: url(:/qss_icons/rc/transparent.png);
-}
-
-QTreeView::branch:!has-children:!has-siblings:adjoins-item {
-    border-image: url(:/qss_icons/rc/transparent.png);
-}
-
-QTreeView::branch:has-children:!has-siblings:closed,
-QTreeView::branch:closed:has-children:has-siblings {
-    image: url(:/qss_icons/rc/branch_closed.png);
-}
-
-QTreeView::branch:open:has-children:!has-siblings,
-QTreeView::branch:open:has-children:has-siblings  {
-    image: url(:/qss_icons/rc/branch_open.png);
-}
-
-QTreeView::branch:has-children:!has-siblings:closed:hover,
-QTreeView::branch:closed:has-children:has-siblings:hover {
-    image: url(:/qss_icons/rc/branch_closed-on.png);
-    }
-
-QTreeView::branch:open:has-children:!has-siblings:hover,
-QTreeView::branch:open:has-children:has-siblings:hover  {
-    image: url(:/qss_icons/rc/branch_open-on.png);
-    }
-
-QListView::item:!selected:hover, QListView::item:!selected:hover, QTreeView::item:!selected:hover  {
-    background: rgba(0, 0, 0, 0);
-    outline: 0;
-    color: #FFFFFF
-}
-
-QListView::item:selected:hover, QListView::item:selected:hover, QTreeView::item:selected:hover  {
-    background: #3d8ec9;
-    color: #FFFFFF;
-}
-
-QSlider::groove:horizontal {
-    border: 1px solid #3A3939;
-    height: 8px;
-    background: #201F1F;
-    margin: 2px 0;
-    border-radius: 2px;
-}
-
-QSlider::handle:horizontal {
-    background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,
-      stop: 0.0 silver, stop: 0.2 #a8a8a8, stop: 1 #727272);
-    border: 1px solid #3A3939;
-    width: 14px;
-    height: 14px;
-    margin: -4px 0;
-    border-radius: 2px;
-}
-
-QSlider::groove:vertical {
-    border: 1px solid #3A3939;
-    width: 8px;
-    background: #201F1F;
-    margin: 0 0px;
-    border-radius: 2px;
-}
-
-QSlider::handle:vertical {
-    background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 silver,
-      stop: 0.2 #a8a8a8, stop: 1 #727272);
-    border: 1px solid #3A3939;
-    width: 14px;
-    height: 14px;
-    margin: 0 -4px;
-    border-radius: 2px;
-}
-
-QToolButton {
-    background-color: transparent;
-    border: 1px transparent #4A4949;
-    border-radius: 2px;
-    margin: 3px;
-    padding: 3px;
-}
-
-QToolButton[popupMode="1"] { /* only for MenuButtonPopup */
- padding-right: 20px; /* make way for the popup button */
- border: 1px transparent #4A4949;
- border-radius: 5px;
-}
-
-QToolButton[popupMode="2"] { /* only for InstantPopup */
- padding-right: 10px; /* make way for the popup button */
- border: 1px transparent #4A4949;
-}
-
-
-QToolButton:hover, QToolButton::menu-button:hover {
-    background-color: transparent;
-    border: 1px solid #78879b;
-}
-
-QToolButton:checked, QToolButton:pressed,
-        QToolButton::menu-button:pressed {
-    background-color: #4A4949;
-    border: 1px solid #78879b;
-}
-
-/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */
-QToolButton::menu-indicator {
-    image: url(:/qss_icons/rc/down_arrow.png);
-    top: -7px; left: -2px; /* shift it a bit */
-}
-
-/* the subcontrols below are used only in the MenuButtonPopup mode */
-QToolButton::menu-button {
-    border: 1px transparent #4A4949;
-    border-top-right-radius: 6px;
-    border-bottom-right-radius: 6px;
-    /* 16px width + 4px for border = 20px allocated above */
-    width: 16px;
-    outline: none;
-}
-
-QToolButton::menu-arrow {
-    image: url(:/qss_icons/rc/down_arrow.png);
-}
-
-QToolButton::menu-arrow:open {
-    top: 1px; left: 1px; /* shift it a bit */
-    border: 1px solid #3A3939;
-}
-
-QTableView
-{
-    border: 1px solid #444;
-    gridline-color: #6c6c6c;
-    background-color: #201F1F;
-}
-
-
-QTableView, QHeaderView
-{
-    border-radius: 0px;
-}
-
-QTableView::item:pressed, QListView::item:pressed, QTreeView::item:pressed  {
-    background: #78879b;
-    color: #FFFFFF;
-}
-
-QTableView::item:selected:active, QTreeView::item:selected:active, QListView::item:selected:active  {
-    background: #3d8ec9;
-    color: #FFFFFF;
-}
-
-
-QHeaderView
-{
-    border: 1px transparent;
-    border-radius: 2px;
-    margin: 0px;
-    padding: 0px;
-}
-
-QHeaderView::section  {
-    background-color: #3A3939;
-    color: silver;
-    padding: 4px;
-    border: 1px solid #6c6c6c;
-    border-radius: 0px;
-    text-align: center;
-}
-
-QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one
-{
-    border-top: 1px solid #6c6c6c;
-}
-
-QHeaderView::section::vertical
-{
-    border-top: transparent;
-}
-
-QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one
-{
-    border-left: 1px solid #6c6c6c;
-}
-
-QHeaderView::section::horizontal
-{
-    border-left: transparent;
-}
-
-
-QHeaderView::section:checked
- {
-    color: white;
-    background-color: #5A5959;
- }
-
- /* style the sort indicator */
-QHeaderView::down-arrow {
-    image: url(:/qss_icons/rc/down_arrow.png);
-}
-
-QHeaderView::up-arrow {
-    image: url(:/qss_icons/rc/up_arrow.png);
-}
-
-
-QTableCornerButton::section {
-    background-color: #3A3939;
-    border: 1px solid #3A3939;
-    border-radius: 2px;
-}
-
-QToolBox  {
-    padding: 3px;
-    border: 1px transparent black;
-}
-
-QToolBox::tab {
-    color: #b1b1b1;
-    background-color: #302F2F;
-    border: 1px solid #4A4949;
-    border-bottom: 1px transparent #302F2F;
-    border-top-left-radius: 5px;
-    border-top-right-radius: 5px;
-}
-
- QToolBox::tab:selected { /* italicize selected tabs */
-    font: italic;
-    background-color: #302F2F;
-    border-color: #3d8ec9;
- }
-
-QStatusBar::item {
-    border: 1px solid #3A3939;
-    border-radius: 2px;
- }
-
-
-QFrame[height="3"], QFrame[width="3"] {
-    background-color: #444;
-}
-
-
-QSplitter::handle {
-    border: 1px dashed #3A3939;
-}
-
-QSplitter::handle:hover {
-    background-color: #787876;
-    border: 1px solid #3A3939;
-}
-
-QSplitter::handle:horizontal {
-    width: 1px;
-}
-
-QSplitter::handle:vertical {
-    height: 1px;
-}
-
-MessageItem
-{
-    border: none;
-}
-
-MessageBrowser
-{
-    border: none;
-}
-
-MessageBrowser::focus
-{
-    border: none;
-}
-
-MessageItem::focus
-{
-    border: none;
-}
-
-MessageBrowser:hover
-{
-    border: none;
-}
-
-QListWidget QPushButton 
-{
-    background-color: transparent;
-    border: none;
-}
-
-QPushButton:hover 
-{
-    background-color: #4A4949;
-}
-
-#messages:item:selected
-{
-    background-color: #1E90FF;
-}
-
-MessageBrowser
-{
-    background-color: transparent;
-}
-
-#messages:item:selected QListWidgetItem
-{
-    background-color: #1E90FF;
-}
-
-#friendsListWidget:item:selected
-{
-    background-color: #333333;
-}
-
-#toxygen
-{
-    color: #A9A9A9;
-}
-
-QCheckBox
-{
-    spacing: 5px;
-    outline: none;
-    color: #bbb;
-    margin-bottom: 2px;
-    text-align: center;
-}
-
-QListWidget > QLabel 
-{
-    color: #A9A9A9;
-}
-
-#searchLineEdit
-{
-    padding-left: 22px;
-}
-
-#mainmenubutton
-{
-    border: 1px solid #3A3939;
-    color: silver;
-    margin: 0px;
-    text-align: center;
-}
-
-#mainmenubutton:hover
-{
-    background: transparent;
-    border: 1px solid #A9A9A9;
-    background-color: #302F2F;
-}
-
-#mainmenubutton:pressed
-{
-    background: transparent;
-    border: 1px solid #A9A9A9;
-    background-color: #302F2F;
-}
-
-#mainmenubutton::menu-indicator
-{
-    image: none;
-    width: 0px;
-    height: 0px;
-}
-
-ClickableLabel:focus
-{
-    border-width: 1px;
-    border-color: #4A4949;
-    border-style: solid;
-}
-
-ClickableLabel:hover
-{
-    background-color: #4A4949;
-}
-
-#warningLabel
-{
-    color: #BC1C1C;
-}
-
-#groupInvitesPushButton
-{
-    background-color: #009c00;
-}
-
diff --git a/toxygen/styles/rc/Hmovetoolbar.png b/toxygen/styles/rc/Hmovetoolbar.png
old mode 100644
new mode 100755
index 4b55192..cead99e
Binary files a/toxygen/styles/rc/Hmovetoolbar.png and b/toxygen/styles/rc/Hmovetoolbar.png differ
diff --git a/toxygen/styles/rc/Hsepartoolbar.png b/toxygen/styles/rc/Hsepartoolbar.png
old mode 100644
new mode 100755
index 58840be..7f183c8
Binary files a/toxygen/styles/rc/Hsepartoolbar.png and b/toxygen/styles/rc/Hsepartoolbar.png differ
diff --git a/toxygen/styles/rc/Vmovetoolbar.png b/toxygen/styles/rc/Vmovetoolbar.png
old mode 100644
new mode 100755
index c3b4762..512edce
Binary files a/toxygen/styles/rc/Vmovetoolbar.png and b/toxygen/styles/rc/Vmovetoolbar.png differ
diff --git a/toxygen/styles/rc/Vsepartoolbar.png b/toxygen/styles/rc/Vsepartoolbar.png
old mode 100644
new mode 100755
index 5de9a34..d9dc156
Binary files a/toxygen/styles/rc/Vsepartoolbar.png and b/toxygen/styles/rc/Vsepartoolbar.png differ
diff --git a/toxygen/styles/rc/branch_closed-on.png b/toxygen/styles/rc/branch_closed-on.png
old mode 100644
new mode 100755
index 9020fe7..d081e9b
Binary files a/toxygen/styles/rc/branch_closed-on.png and b/toxygen/styles/rc/branch_closed-on.png differ
diff --git a/toxygen/styles/rc/branch_closed.png b/toxygen/styles/rc/branch_closed.png
old mode 100644
new mode 100755
index 7c20500..d652159
Binary files a/toxygen/styles/rc/branch_closed.png and b/toxygen/styles/rc/branch_closed.png differ
diff --git a/toxygen/styles/rc/branch_open-on.png b/toxygen/styles/rc/branch_open-on.png
old mode 100644
new mode 100755
index f41f80c..ec372b2
Binary files a/toxygen/styles/rc/branch_open-on.png and b/toxygen/styles/rc/branch_open-on.png differ
diff --git a/toxygen/styles/rc/branch_open.png b/toxygen/styles/rc/branch_open.png
old mode 100644
new mode 100755
index efb6068..66f8e1a
Binary files a/toxygen/styles/rc/branch_open.png and b/toxygen/styles/rc/branch_open.png differ
diff --git a/toxygen/styles/rc/checkbox_checked.png b/toxygen/styles/rc/checkbox_checked.png
old mode 100644
new mode 100755
index 1539bc9..e09ce02
Binary files a/toxygen/styles/rc/checkbox_checked.png and b/toxygen/styles/rc/checkbox_checked.png differ
diff --git a/toxygen/styles/rc/checkbox_checked_disabled.png b/toxygen/styles/rc/checkbox_checked_disabled.png
old mode 100644
new mode 100755
index 1539bc9..e09ce02
Binary files a/toxygen/styles/rc/checkbox_checked_disabled.png and b/toxygen/styles/rc/checkbox_checked_disabled.png differ
diff --git a/toxygen/styles/rc/checkbox_checked_focus.png b/toxygen/styles/rc/checkbox_checked_focus.png
old mode 100644
new mode 100755
index 1539bc9..e09ce02
Binary files a/toxygen/styles/rc/checkbox_checked_focus.png and b/toxygen/styles/rc/checkbox_checked_focus.png differ
diff --git a/toxygen/styles/rc/checkbox_indeterminate.png b/toxygen/styles/rc/checkbox_indeterminate.png
old mode 100644
new mode 100755
index 15e221b..41024f7
Binary files a/toxygen/styles/rc/checkbox_indeterminate.png and b/toxygen/styles/rc/checkbox_indeterminate.png differ
diff --git a/toxygen/styles/rc/checkbox_indeterminate_disabled.png b/toxygen/styles/rc/checkbox_indeterminate_disabled.png
old mode 100644
new mode 100755
index bc26933..abdc01d
Binary files a/toxygen/styles/rc/checkbox_indeterminate_disabled.png and b/toxygen/styles/rc/checkbox_indeterminate_disabled.png differ
diff --git a/toxygen/styles/rc/checkbox_indeterminate_focus.png b/toxygen/styles/rc/checkbox_indeterminate_focus.png
old mode 100644
new mode 100755
index 7c00620..a9a16f7
Binary files a/toxygen/styles/rc/checkbox_indeterminate_focus.png and b/toxygen/styles/rc/checkbox_indeterminate_focus.png differ
diff --git a/toxygen/styles/rc/checkbox_unchecked.png b/toxygen/styles/rc/checkbox_unchecked.png
old mode 100644
new mode 100755
index 30631ba..30deeb5
Binary files a/toxygen/styles/rc/checkbox_unchecked.png and b/toxygen/styles/rc/checkbox_unchecked.png differ
diff --git a/toxygen/styles/rc/checkbox_unchecked_disabled.png b/toxygen/styles/rc/checkbox_unchecked_disabled.png
old mode 100644
new mode 100755
index 30631ba..30deeb5
Binary files a/toxygen/styles/rc/checkbox_unchecked_disabled.png and b/toxygen/styles/rc/checkbox_unchecked_disabled.png differ
diff --git a/toxygen/styles/rc/checkbox_unchecked_focus.png b/toxygen/styles/rc/checkbox_unchecked_focus.png
old mode 100644
new mode 100755
index 30631ba..30deeb5
Binary files a/toxygen/styles/rc/checkbox_unchecked_focus.png and b/toxygen/styles/rc/checkbox_unchecked_focus.png differ
diff --git a/toxygen/styles/rc/close-hover.png b/toxygen/styles/rc/close-hover.png
old mode 100644
new mode 100755
index f8fbb31..657943a
Binary files a/toxygen/styles/rc/close-hover.png and b/toxygen/styles/rc/close-hover.png differ
diff --git a/toxygen/styles/rc/close-pressed.png b/toxygen/styles/rc/close-pressed.png
old mode 100644
new mode 100755
index 7c644b6..937d005
Binary files a/toxygen/styles/rc/close-pressed.png and b/toxygen/styles/rc/close-pressed.png differ
diff --git a/toxygen/styles/rc/close.png b/toxygen/styles/rc/close.png
old mode 100644
new mode 100755
index b3e51a0..bc0f576
Binary files a/toxygen/styles/rc/close.png and b/toxygen/styles/rc/close.png differ
diff --git a/toxygen/styles/rc/down_arrow.png b/toxygen/styles/rc/down_arrow.png
old mode 100644
new mode 100755
index ff4a62b..e271f7f
Binary files a/toxygen/styles/rc/down_arrow.png and b/toxygen/styles/rc/down_arrow.png differ
diff --git a/toxygen/styles/rc/down_arrow_disabled.png b/toxygen/styles/rc/down_arrow_disabled.png
old mode 100644
new mode 100755
index 388339c..5805d98
Binary files a/toxygen/styles/rc/down_arrow_disabled.png and b/toxygen/styles/rc/down_arrow_disabled.png differ
diff --git a/toxygen/styles/rc/left_arrow.png b/toxygen/styles/rc/left_arrow.png
old mode 100644
new mode 100755
index f0c00ea..f808d2d
Binary files a/toxygen/styles/rc/left_arrow.png and b/toxygen/styles/rc/left_arrow.png differ
diff --git a/toxygen/styles/rc/left_arrow_disabled.png b/toxygen/styles/rc/left_arrow_disabled.png
old mode 100644
new mode 100755
index 570a940..f5b9af8
Binary files a/toxygen/styles/rc/left_arrow_disabled.png and b/toxygen/styles/rc/left_arrow_disabled.png differ
diff --git a/toxygen/styles/rc/radio_checked.png b/toxygen/styles/rc/radio_checked.png
old mode 100644
new mode 100755
index c6aacda..14b1cb1
Binary files a/toxygen/styles/rc/radio_checked.png and b/toxygen/styles/rc/radio_checked.png differ
diff --git a/toxygen/styles/rc/radio_checked_disabled.png b/toxygen/styles/rc/radio_checked_disabled.png
old mode 100644
new mode 100755
index c6aacda..14b1cb1
Binary files a/toxygen/styles/rc/radio_checked_disabled.png and b/toxygen/styles/rc/radio_checked_disabled.png differ
diff --git a/toxygen/styles/rc/radio_checked_focus.png b/toxygen/styles/rc/radio_checked_focus.png
old mode 100644
new mode 100755
index c6aacda..14b1cb1
Binary files a/toxygen/styles/rc/radio_checked_focus.png and b/toxygen/styles/rc/radio_checked_focus.png differ
diff --git a/toxygen/styles/rc/radio_unchecked.png b/toxygen/styles/rc/radio_unchecked.png
old mode 100644
new mode 100755
index c0565b5..27af811
Binary files a/toxygen/styles/rc/radio_unchecked.png and b/toxygen/styles/rc/radio_unchecked.png differ
diff --git a/toxygen/styles/rc/radio_unchecked_disabled.png b/toxygen/styles/rc/radio_unchecked_disabled.png
old mode 100644
new mode 100755
index c0565b5..27af811
Binary files a/toxygen/styles/rc/radio_unchecked_disabled.png and b/toxygen/styles/rc/radio_unchecked_disabled.png differ
diff --git a/toxygen/styles/rc/radio_unchecked_focus.png b/toxygen/styles/rc/radio_unchecked_focus.png
old mode 100644
new mode 100755
index c0565b5..27af811
Binary files a/toxygen/styles/rc/radio_unchecked_focus.png and b/toxygen/styles/rc/radio_unchecked_focus.png differ
diff --git a/toxygen/styles/rc/right_arrow.png b/toxygen/styles/rc/right_arrow.png
old mode 100644
new mode 100755
index 75e5b5a..9b0a4e6
Binary files a/toxygen/styles/rc/right_arrow.png and b/toxygen/styles/rc/right_arrow.png differ
diff --git a/toxygen/styles/rc/right_arrow_disabled.png b/toxygen/styles/rc/right_arrow_disabled.png
old mode 100644
new mode 100755
index 31f4831..5c0bee4
Binary files a/toxygen/styles/rc/right_arrow_disabled.png and b/toxygen/styles/rc/right_arrow_disabled.png differ
diff --git a/toxygen/styles/rc/sizegrip.png b/toxygen/styles/rc/sizegrip.png
old mode 100644
new mode 100755
index 09473be..350583a
Binary files a/toxygen/styles/rc/sizegrip.png and b/toxygen/styles/rc/sizegrip.png differ
diff --git a/toxygen/styles/rc/stylesheet-branch-end.png b/toxygen/styles/rc/stylesheet-branch-end.png
old mode 100644
new mode 100755
index 5569ee6..cb5d3b5
Binary files a/toxygen/styles/rc/stylesheet-branch-end.png and b/toxygen/styles/rc/stylesheet-branch-end.png differ
diff --git a/toxygen/styles/rc/stylesheet-branch-more.png b/toxygen/styles/rc/stylesheet-branch-more.png
old mode 100644
new mode 100755
index 57fe30d..6271140
Binary files a/toxygen/styles/rc/stylesheet-branch-more.png and b/toxygen/styles/rc/stylesheet-branch-more.png differ
diff --git a/toxygen/styles/rc/stylesheet-vline.png b/toxygen/styles/rc/stylesheet-vline.png
old mode 100644
new mode 100755
index 253cacb..87536cc
Binary files a/toxygen/styles/rc/stylesheet-vline.png and b/toxygen/styles/rc/stylesheet-vline.png differ
diff --git a/toxygen/styles/rc/transparent.png b/toxygen/styles/rc/transparent.png
old mode 100644
new mode 100755
index cf1c4f6..483df25
Binary files a/toxygen/styles/rc/transparent.png and b/toxygen/styles/rc/transparent.png differ
diff --git a/toxygen/styles/rc/undock.png b/toxygen/styles/rc/undock.png
old mode 100644
new mode 100755
index 4a7b0c8..88691d7
Binary files a/toxygen/styles/rc/undock.png and b/toxygen/styles/rc/undock.png differ
diff --git a/toxygen/styles/rc/up_arrow.png b/toxygen/styles/rc/up_arrow.png
old mode 100644
new mode 100755
index 0cc7d6d..abcc724
Binary files a/toxygen/styles/rc/up_arrow.png and b/toxygen/styles/rc/up_arrow.png differ
diff --git a/toxygen/styles/rc/up_arrow_disabled.png b/toxygen/styles/rc/up_arrow_disabled.png
old mode 100644
new mode 100755
index 99c6b67..b9c8e3b
Binary files a/toxygen/styles/rc/up_arrow_disabled.png and b/toxygen/styles/rc/up_arrow_disabled.png differ
diff --git a/toxygen/styles/style.py b/toxygen/styles/style.py
index dca54d8..50ddfee 100644
--- a/toxygen/styles/style.py
+++ b/toxygen/styles/style.py
@@ -1,14 +1,21 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
+# -*- coding: utf-8 -*-
 
-from  qtpy import QtCore
+# Resource object code
+#
+# Created: вт июн 21 13:50:04 2016
+#      by: The Resource Compiler for PySide (Qt v4.8.4)
+#
+# WARNING! All changes made in this file will be lost!
+
+from PySide import QtCore
 
 qt_resource_data = b"\x00\x00d\xdc/*\x0a * The MIT License (MIT)\x0a *\x0a * Copyright (c) <2013-2014> <Colin Duquesnoy>\x0a *\x0a * Permission is hereby granted, free of charge, to any person obtaining a copy\x0a * of this software and associated documentation files (the \x22Software\x22), to deal\x0a * in the Software without restriction, including without limitation the rights\x0a * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\x0a * copies of the Software, and to permit persons to whom the Software is\x0a * furnished to do so, subject to the following conditions:\x0a\x0a * The above copyright notice and this permission notice shall be included in\x0a * all copies or substantial portions of the Software.\x0a\x0a * THE SOFTWARE IS PROVIDED \x22AS IS\x22, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\x0a * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\x0a * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\x0a * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\x0a * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\x0a * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\x0a * THE SOFTWARE.\x0a */\x0a\x0aQToolTip\x0a{\x0a    border: 1px solid #3A3939;\x0a    background-color: rgb(90, 102, 117);;\x0a    color: white;\x0a    padding: 1px;\x0a    opacity: 200;\x0a}\x0a\x0aQWidget\x0a{\x0a    color: silver;\x0a    background-color: #302F2F;\x0a    selection-background-color: #A9A9A9;\x0a    selection-color: black;\x0a    background-clip: border;\x0a    border-image: none;\x0a    outline: 0;\x0a}\x0a\x0aQWidget:item:hover\x0a{\x0a    background-color: #78879b;\x0a    color: black;\x0a}\x0a\x0aQWidget:item:selected\x0a{\x0a    background-color: #A9A9A9;\x0a}\x0a\x0aQProgressBar:horizontal {\x0a    border: 1px solid #3A3939;\x0a    text-align: center;\x0a    padding: 1px;\x0a    background: #201F1F;\x0a}\x0aQProgressBar::chunk:horizontal {\x0a    background-color: qlineargradient(spread:reflect, x1:1, y1:0.545, x2:1, y2:0, stop:0 rgba(28, 66, 111, 255), stop:1 rgba(37, 87, 146, 255));\x0a}\x0a\x0aQCheckBox:disabled\x0a{\x0a    color: #777777;\x0a}\x0aQCheckBox::indicator,\x0aQGroupBox::indicator\x0a{\x0a    width: 18px;\x0a    height: 18px;\x0a}\x0aQGroupBox::indicator\x0a{\x0a    margin-left: 2px;\x0a}\x0a\x0aQCheckBox::indicator:unchecked,\x0aQCheckBox::indicator:unchecked:hover,\x0aQGroupBox::indicator:unchecked,\x0aQGroupBox::indicator:unchecked:hover\x0a{\x0a    image: url(:/qss_icons/rc/checkbox_unchecked.png);\x0a}\x0a\x0aQCheckBox::indicator:unchecked:focus,\x0aQCheckBox::indicator:unchecked:pressed,\x0aQGroupBox::indicator:unchecked:focus,\x0aQGroupBox::indicator:unchecked:pressed\x0a{\x0a  border: none;\x0a    image: url(:/qss_icons/rc/checkbox_unchecked_focus.png);\x0a}\x0a\x0aQCheckBox::indicator:checked,\x0aQCheckBox::indicator:checked:hover,\x0aQGroupBox::indicator:checked,\x0aQGroupBox::indicator:checked:hover\x0a{\x0a    image: url(:/qss_icons/rc/checkbox_checked.png);\x0a}\x0a\x0aQCheckBox::indicator:checked:focus,\x0aQCheckBox::indicator:checked:pressed,\x0aQGroupBox::indicator:checked:focus,\x0aQGroupBox::indicator:checked:pressed\x0a{\x0a  border: none;\x0a    image: url(:/qss_icons/rc/checkbox_checked_focus.png);\x0a}\x0a\x0aQCheckBox::indicator:indeterminate,\x0aQCheckBox::indicator:indeterminate:hover,\x0aQCheckBox::indicator:indeterminate:pressed\x0aQGroupBox::indicator:indeterminate,\x0aQGroupBox::indicator:indeterminate:hover,\x0aQGroupBox::indicator:indeterminate:pressed\x0a{\x0a    image: url(:/qss_icons/rc/checkbox_indeterminate.png);\x0a}\x0a\x0aQCheckBox::indicator:indeterminate:focus,\x0aQGroupBox::indicator:indeterminate:focus\x0a{\x0a    image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png);\x0a}\x0a\x0aQCheckBox::indicator:checked:disabled,\x0aQGroupBox::indicator:checked:disabled\x0a{\x0a    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);\x0a}\x0a\x0aQCheckBox::indicator:unchecked:disabled,\x0aQGroupBox::indicator:unchecked:disabled\x0a{\x0a    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);\x0a}\x0a\x0aQRadioButton\x0a{\x0a    spacing: 5px;\x0a    outline: none;\x0a    color: #bbb;\x0a    margin-bottom: 2px;\x0a}\x0a\x0aQRadioButton:disabled\x0a{\x0a    color: #777777;\x0a}\x0aQRadioButton::indicator\x0a{\x0a    width: 21px;\x0a    height: 21px;\x0a}\x0a\x0aQRadioButton::indicator:unchecked,\x0aQRadioButton::indicator:unchecked:hover\x0a{\x0a    image: url(:/qss_icons/rc/radio_unchecked.png);\x0a}\x0a\x0aQRadioButton::indicator:unchecked:focus,\x0aQRadioButton::indicator:unchecked:pressed\x0a{\x0a  border: none;\x0a  outline: none;\x0a    image: url(:/qss_icons/rc/radio_unchecked_focus.png);\x0a}\x0a\x0aQRadioButton::indicator:checked,\x0aQRadioButton::indicator:checked:hover\x0a{\x0a  border: none;\x0a  outline: none;\x0a    image: url(:/qss_icons/rc/radio_checked.png);\x0a}\x0a\x0aQRadioButton::indicator:checked:focus,\x0aQRadioButton::indicato::menu-arrowr:checked:pressed\x0a{\x0a  border: none;\x0a  outline: none;\x0a    image: url(:/qss_icons/rc/radio_checked_focus.png);\x0a}\x0a\x0aQRadioButton::indicator:indeterminate,\x0aQRadioButton::indicator:indeterminate:hover,\x0aQRadioButton::indicator:indeterminate:pressed\x0a{\x0a        image: url(:/qss_icons/rc/radio_indeterminate.png);\x0a}\x0a\x0aQRadioButton::indicator:checked:disabled\x0a{\x0a  outline: none;\x0a  image: url(:/qss_icons/rc/radio_checked_disabled.png);\x0a}\x0a\x0aQRadioButton::indicator:unchecked:disabled\x0a{\x0a    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);\x0a}\x0a\x0a\x0aQMenuBar\x0a{\x0a    background-color: #302F2F;\x0a    color: silver;\x0a}\x0a\x0aQMenuBar::item\x0a{\x0a    background: transparent;\x0a}\x0a\x0aQMenuBar::item:selected\x0a{\x0a    background: transparent;\x0a    border: 1px solid #A9A9A9;\x0a}\x0a\x0aQMenuBar::item:pressed\x0a{\x0a    border: 1px solid #3A3939;\x0a    background-color: #A9A9A9;\x0a    color: black;\x0a    margin-bottom:-1px;\x0a    padding-bottom:1px;\x0a}\x0a\x0aQMenu\x0a{\x0a    border: 1px solid #3A3939;\x0a    color: silver;\x0a    margin: 2px;\x0a}\x0a\x0aQMenu::icon\x0a{\x0a    margin: 5px;\x0a}\x0a\x0aQMenu::item\x0a{\x0a    padding: 5px 30px 5px 30px;\x0a    margin-left: 5px;\x0a    border: 1px solid transparent; /* reserve space for selection border */\x0a}\x0a\x0aQMenu::item:selected\x0a{\x0a    color: black;\x0a}\x0a\x0aQMenu::separator {\x0a    height: 2px;\x0a    background: lightblue;\x0a    margin-left: 10px;\x0a    margin-right: 5px;\x0a}\x0a\x0aQMenu::indicator {\x0a    width: 18px;\x0a    height: 18px;\x0a}\x0a\x0a/* non-exclusive indicator = check box style indicator\x0a   (see QActionGroup::setExclusive) */\x0aQMenu::indicator:non-exclusive:unchecked {\x0a    image: url(:/qss_icons/rc/checkbox_unchecked.png);\x0a}\x0a\x0aQMenu::indicator:non-exclusive:unchecked:selected {\x0a    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);\x0a}\x0a\x0aQMenu::indicator:non-exclusive:checked {\x0a    image: url(:/qss_icons/rc/checkbox_checked.png);\x0a}\x0a\x0aQMenu::indicator:non-exclusive:checked:selected {\x0a    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);\x0a}\x0a\x0a/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */\x0aQMenu::indicator:exclusive:unchecked {\x0a    image: url(:/qss_icons/rc/radio_unchecked.png);\x0a}\x0a\x0aQMenu::indicator:exclusive:unchecked:selected {\x0a    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);\x0a}\x0a\x0aQMenu::indicator:exclusive:checked {\x0a    image: url(:/qss_icons/rc/radio_checked.png);\x0a}\x0a\x0aQMenu::indicator:exclusive:checked:selected {\x0a    image: url(:/qss_icons/rc/radio_checked_disabled.png);\x0a}\x0a\x0aQMenu::right-arrow {\x0a    margin: 5px;\x0a    image: url(:/qss_icons/rc/right_arrow.png)\x0a}\x0a\x0a\x0aQWidget:disabled\x0a{\x0a    color: #404040;\x0a    background-color: #302F2F;\x0a}\x0a\x0aQAbstractItemView\x0a{\x0a    alternate-background-color: #3A3939;\x0a    color: silver;\x0a    border: 1px solid 3A3939;\x0a    border-radius: 2px;\x0a    padding: 1px;\x0a}\x0a\x0aQWidget:focus, QMenuBar:focus\x0a{\x0a    border: 1px solid #78879b;\x0a}\x0a\x0aQTabWidget:focus, QCheckBox:focus, QRadioButton:focus, QSlider:focus\x0a{\x0a    border: none;\x0a}\x0a\x0aQLineEdit\x0a{\x0a    background-color: #201F1F;\x0a    padding: 2px;\x0a    border-style: solid;\x0a    border: 1px solid #3A3939;\x0a    border-radius: 2px;\x0a    color: silver;\x0a}\x0a\x0aQGroupBox {\x0a    border:1px solid #3A3939;\x0a    border-radius: 2px;\x0a    margin-top: 20px;\x0a}\x0a\x0aQGroupBox::title {\x0a    subcontrol-origin: margin;\x0a    subcontrol-position: top center;\x0a    padding-left: 10px;\x0a    padding-right: 10px;\x0a    padding-top: 10px;\x0a}\x0a\x0aQAbstractScrollArea\x0a{\x0a    border-radius: 2px;\x0a    border: 1px solid #3A3939;\x0a    background-color: transparent;\x0a}\x0a\x0aQScrollBar:horizontal\x0a{\x0a    height: 15px;\x0a    margin: 3px 15px 3px 15px;\x0a    border: 1px transparent #2A2929;\x0a    border-radius: 4px;\x0a    background-color: #2A2929;\x0a}\x0a\x0aQScrollBar::handle:horizontal\x0a{\x0a    background-color: #605F5F;\x0a    min-width: 5px;\x0a    border-radius: 4px;\x0a}\x0a\x0aQScrollBar::add-line:horizontal\x0a{\x0a    margin: 0px 3px 0px 3px;\x0a    border-image: url(:/qss_icons/rc/right_arrow_disabled.png);\x0a    width: 10px;\x0a    height: 10px;\x0a    subcontrol-position: right;\x0a    subcontrol-origin: margin;\x0a}\x0a\x0aQScrollBar::sub-line:horizontal\x0a{\x0a    margin: 0px 3px 0px 3px;\x0a    border-image: url(:/qss_icons/rc/left_arrow_disabled.png);\x0a    height: 10px;\x0a    width: 10px;\x0a    subcontrol-position: left;\x0a    subcontrol-origin: margin;\x0a}\x0a\x0aQScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on\x0a{\x0a    border-image: url(:/qss_icons/rc/right_arrow.png);\x0a    height: 10px;\x0a    width: 10px;\x0a    subcontrol-position: right;\x0a    subcontrol-origin: margin;\x0a}\x0a\x0a\x0aQScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on\x0a{\x0a    border-image: url(:/qss_icons/rc/left_arrow.png);\x0a    height: 10px;\x0a    width: 10px;\x0a    subcontrol-position: left;\x0a    subcontrol-origin: margin;\x0a}\x0a\x0aQScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal\x0a{\x0a    background: none;\x0a}\x0a\x0a\x0aQScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal\x0a{\x0a    background: none;\x0a}\x0a\x0aQScrollBar:vertical\x0a{\x0a    background-color: #2A2929;\x0a    width: 15px;\x0a    margin: 15px 3px 15px 3px;\x0a    border: 1px transparent #2A2929;\x0a    border-radius: 4px;\x0a}\x0a\x0aQScrollBar::handle:vertical\x0a{\x0a    background-color: #605F5F;\x0a    min-height: 5px;\x0a    border-radius: 4px;\x0a}\x0a\x0aQScrollBar::sub-line:vertical\x0a{\x0a    margin: 3px 0px 3px 0px;\x0a    border-image: url(:/qss_icons/rc/up_arrow_disabled.png);\x0a    height: 10px;\x0a    width: 10px;\x0a    subcontrol-position: top;\x0a    subcontrol-origin: margin;\x0a}\x0a\x0aQScrollBar::add-line:vertical\x0a{\x0a    margin: 3px 0px 3px 0px;\x0a    border-image: url(:/qss_icons/rc/down_arrow_disabled.png);\x0a    height: 10px;\x0a    width: 10px;\x0a    subcontrol-position: bottom;\x0a    subcontrol-origin: margin;\x0a}\x0a\x0aQScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on\x0a{\x0a\x0a    border-image: url(:/qss_icons/rc/up_arrow.png);\x0a    height: 10px;\x0a    width: 10px;\x0a    subcontrol-position: top;\x0a    subcontrol-origin: margin;\x0a}\x0a\x0a\x0aQScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on\x0a{\x0a    border-image: url(:/qss_icons/rc/down_arrow.png);\x0a    height: 10px;\x0a    width: 10px;\x0a    subcontrol-position: bottom;\x0a    subcontrol-origin: margin;\x0a}\x0a\x0aQScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical\x0a{\x0a    background: none;\x0a}\x0a\x0a\x0aQScrollBar::add-page:vertical, QScrollBar::sub-page:vertical\x0a{\x0a    background: none;\x0a}\x0a\x0aQTextEdit\x0a{\x0a    background-color: #201F1F;\x0a    color: silver;\x0a    border: 1px solid #3A3939;\x0a}\x0a\x0aQPlainTextEdit\x0a{\x0a    background-color: #201F1F;;\x0a    color: silver;\x0a    border-radius: 2px;\x0a    border: 1px solid #3A3939;\x0a}\x0a\x0aQHeaderView::section\x0a{\x0a    background-color: #3A3939;\x0a    color: silver;\x0a    padding-left: 4px;\x0a    border: 1px solid #6c6c6c;\x0a}\x0a\x0aQSizeGrip {\x0a    image: url(:/qss_icons/rc/sizegrip.png);\x0a    width: 12px;\x0a    height: 12px;\x0a}\x0a\x0a\x0aQMainWindow::separator\x0a{\x0a    background-color: #302F2F;\x0a    color: white;\x0a    padding-left: 4px;\x0a    spacing: 2px;\x0a    border: 1px dashed #3A3939;\x0a}\x0a\x0aQMainWindow::separator:hover\x0a{\x0a\x0a    background-color: #787876;\x0a    color: white;\x0a    padding-left: 4px;\x0a    border: 1px solid #3A3939;\x0a    spacing: 2px;\x0a}\x0a\x0a\x0aQMenu::separator\x0a{\x0a    height: 1px;\x0a    background-color: #3A3939;\x0a    color: white;\x0a    padding-left: 4px;\x0a    margin-left: 10px;\x0a    margin-right: 5px;\x0a}\x0a\x0a\x0aQFrame\x0a{\x0a    border-radius: 2px;\x0a    border: 1px solid #444;\x0a}\x0a\x0aQFrame[frameShape=\x220\x22]\x0a{\x0a    border-radius: 2px;\x0a    border: 1px transparent #444;\x0a}\x0a\x0aQStackedWidget\x0a{\x0a    border: 1px transparent black;\x0a}\x0a\x0aQToolBar {\x0a    border: 1px transparent #393838;\x0a    background: 1px solid #302F2F;\x0a    font-weight: bold;\x0a}\x0a\x0aQToolBar::handle:horizontal {\x0a    image: url(:/qss_icons/rc/Hmovetoolbar.png);\x0a}\x0aQToolBar::handle:vertical {\x0a    image: url(:/qss_icons/rc/Vmovetoolbar.png);\x0a}\x0aQToolBar::separator:horizontal {\x0a    image: url(:/qss_icons/rc/Hsepartoolbar.png);\x0a}\x0aQToolBar::separator:vertical {\x0a    image: url(:/qss_icons/rc/Vsepartoolbars.png);\x0a}\x0a\x0aQPushButton\x0a{\x0a    color: silver;\x0a    background-color: #302F2F;\x0a    border-width: 1px;\x0a    border-color: #4A4949;\x0a    border-style: solid;\x0a    padding-top: 5px;\x0a    padding-bottom: 5px;\x0a    padding-left: 5px;\x0a    padding-right: 5px;\x0a    border-radius: 2px;\x0a    outline: none;\x0a}\x0a\x0aQPushButton:focus\x0a{\x0a    border-width: 1px;\x0a    border-color: #4A4949;\x0a    border-style: solid;\x0a}\x0a\x0aQPushButton:disabled\x0a{\x0a    background-color: #302F2F;\x0a    border-width: 1px;\x0a    border-color: #3A3939;\x0a    border-style: solid;\x0a    padding-top: 5px;\x0a    padding-bottom: 5px;\x0a    padding-left: 10px;\x0a    padding-right: 10px;\x0a    /*border-radius: 2px;*/\x0a    color: #454545;\x0a}\x0a\x0aQComboBox\x0a{\x0a    selection-background-color: #A9A9A9;\x0a    background-color: #201F1F;\x0a    border-style: solid;\x0a    border: 1px solid #3A3939;\x0a    border-radius: 2px;\x0a    padding: 2px;\x0a    min-width: 75px;\x0a}\x0a\x0aQPushButton:hover\x0a{\x0a    background-color: #3d8ec9;\x0a    color: white;\x0a}\x0a\x0aQComboBox:hover,QAbstractSpinBox:hover,QLineEdit:hover,QTextEdit:hover,QPlainTextEdit:hover,QAbstractView:hover,QTreeView:hover\x0a{\x0a    border: 1px solid #78879b;\x0a    color: silver;\x0a}\x0a\x0aQComboBox:on\x0a{\x0a    background-color: #626873;\x0a    padding-top: 3px;\x0a    padding-left: 4px;\x0a    selection-background-color: #4a4a4a;\x0a}\x0a\x0aQComboBox QAbstractItemView\x0a{\x0a    background-color: #201F1F;\x0a    border-radius: 2px;\x0a    border: 1px solid #444;\x0a    selection-background-color: #A9A9A9;\x0a}\x0a\x0aQComboBox::drop-down\x0a{\x0a    subcontrol-origin: padding;\x0a    subcontrol-position: top right;\x0a    width: 15px;\x0a\x0a    border-left-width: 0px;\x0a    border-left-color: darkgray;\x0a    border-left-style: solid;\x0a    border-top-right-radius: 3px;\x0a    border-bottom-right-radius: 3px;\x0a}\x0a\x0aQComboBox::down-arrow\x0a{\x0a    image: url(:/qss_icons/rc/down_arrow_disabled.png);\x0a}\x0a\x0aQComboBox::down-arrow:on, QComboBox::down-arrow:hover,\x0aQComboBox::down-arrow:focus\x0a{\x0a    image: url(:/qss_icons/rc/down_arrow.png);\x0a}\x0a\x0aQAbstractSpinBox {\x0a    padding-top: 2px;\x0a    padding-bottom: 2px;\x0a    border: 1px solid #3A3939;\x0a    background-color: #201F1F;\x0a    color: silver;\x0a    border-radius: 2px;\x0a    min-width: 75px;\x0a}\x0a\x0aQAbstractSpinBox:up-button\x0a{\x0a    background-color: transparent;\x0a    subcontrol-origin: border;\x0a    subcontrol-position: center right;\x0a}\x0a\x0aQAbstractSpinBox:down-button\x0a{\x0a    background-color: transparent;\x0a    subcontrol-origin: border;\x0a    subcontrol-position: center left;\x0a}\x0a\x0aQAbstractSpinBox::up-arrow,QAbstractSpinBox::up-arrow:disabled,QAbstractSpinBox::up-arrow:off {\x0a    image: url(:/qss_icons/rc/up_arrow_disabled.png);\x0a    width: 10px;\x0a    height: 10px;\x0a}\x0aQAbstractSpinBox::up-arrow:hover\x0a{\x0a    image: url(:/qss_icons/rc/up_arrow.png);\x0a}\x0a\x0a\x0aQAbstractSpinBox::down-arrow,QAbstractSpinBox::down-arrow:disabled,QAbstractSpinBox::down-arrow:off\x0a{\x0a    image: url(:/qss_icons/rc/down_arrow_disabled.png);\x0a    width: 10px;\x0a    height: 10px;\x0a}\x0aQAbstractSpinBox::down-arrow:hover\x0a{\x0a    image: url(:/qss_icons/rc/down_arrow.png);\x0a}\x0a\x0a\x0aQLabel\x0a{\x0a    border: 0px solid black;\x0a    background-color: transparent;\x0a}\x0a\x0aQTabWidget{\x0a    border: 1px transparent black;\x0a}\x0a\x0aQTabWidget::pane {\x0a    border: 1px solid #444;\x0a    border-radius: 3px;\x0a    padding: 3px;\x0a}\x0a\x0aQTabBar\x0a{\x0a    qproperty-drawBase: 0;\x0a    left: 5px; /* move to the right by 5px */\x0a}\x0a\x0aQTabBar:focus\x0a{\x0a    border: 0px transparent black;\x0a}\x0a\x0aQTabBar::close-button  {\x0a    image: url(:/qss_icons/rc/close.png);\x0a    background: transparent;\x0a}\x0a\x0aQTabBar::close-button:hover\x0a{\x0a    image: url(:/qss_icons/rc/close-hover.png);\x0a    background: transparent;\x0a}\x0a\x0aQTabBar::close-button:pressed {\x0a    image: url(:/qss_icons/rc/close-pressed.png);\x0a    background: transparent;\x0a}\x0a\x0a/* TOP TABS */\x0aQTabBar::tab:top {\x0a    color: #b1b1b1;\x0a    border: 1px solid #4A4949;\x0a    border-bottom: 1px transparent black;\x0a    background-color: #302F2F;\x0a    padding: 5px;\x0a    border-top-left-radius: 2px;\x0a    border-top-right-radius: 2px;\x0a}\x0a\x0aQTabBar::tab:top:!selected\x0a{\x0a    color: #b1b1b1;\x0a    background-color: #201F1F;\x0a    border: 1px transparent #4A4949;\x0a    border-bottom: 1px transparent #4A4949;\x0a    border-top-left-radius: 0px;\x0a    border-top-right-radius: 0px;\x0a}\x0a\x0aQTabBar::tab:top:!selected:hover {\x0a    background-color: #48576b;\x0a}\x0a\x0a/* BOTTOM TABS */\x0aQTabBar::tab:bottom {\x0a    color: #b1b1b1;\x0a    border: 1px solid #4A4949;\x0a    border-top: 1px transparent black;\x0a    background-color: #302F2F;\x0a    padding: 5px;\x0a    border-bottom-left-radius: 2px;\x0a    border-bottom-right-radius: 2px;\x0a}\x0a\x0aQTabBar::tab:bottom:!selected\x0a{\x0a    color: #b1b1b1;\x0a    background-color: #201F1F;\x0a    border: 1px transparent #4A4949;\x0a    border-top: 1px transparent #4A4949;\x0a    border-bottom-left-radius: 0px;\x0a    border-bottom-right-radius: 0px;\x0a}\x0a\x0aQTabBar::tab:bottom:!selected:hover {\x0a    background-color: #78879b;\x0a}\x0a\x0a/* LEFT TABS */\x0aQTabBar::tab:left {\x0a    color: #b1b1b1;\x0a    border: 1px solid #4A4949;\x0a    border-left: 1px transparent black;\x0a    background-color: #302F2F;\x0a    padding: 5px;\x0a    border-top-right-radius: 2px;\x0a    border-bottom-right-radius: 2px;\x0a}\x0a\x0aQTabBar::tab:left:!selected\x0a{\x0a    color: #b1b1b1;\x0a    background-color: #201F1F;\x0a    border: 1px transparent #4A4949;\x0a    border-right: 1px transparent #4A4949;\x0a    border-top-right-radius: 0px;\x0a    border-bottom-right-radius: 0px;\x0a}\x0a\x0aQTabBar::tab:left:!selected:hover {\x0a    background-color: #48576b;\x0a}\x0a\x0a\x0a/* RIGHT TABS */\x0aQTabBar::tab:right {\x0a    color: #b1b1b1;\x0a    border: 1px solid #4A4949;\x0a    border-right: 1px transparent black;\x0a    background-color: #302F2F;\x0a    padding: 5px;\x0a    border-top-left-radius: 2px;\x0a    border-bottom-left-radius: 2px;\x0a}\x0a\x0aQTabBar::tab:right:!selected\x0a{\x0a    color: #b1b1b1;\x0a    background-color: #201F1F;\x0a    border: 1px transparent #4A4949;\x0a    border-right: 1px transparent #4A4949;\x0a    border-top-left-radius: 0px;\x0a    border-bottom-left-radius: 0px;\x0a}\x0a\x0aQTabBar::tab:right:!selected:hover {\x0a    background-color: #48576b;\x0a}\x0a\x0aQTabBar QToolButton::right-arrow:enabled {\x0a     image: url(:/qss_icons/rc/right_arrow.png);\x0a }\x0a\x0a QTabBar QToolButton::left-arrow:enabled {\x0a     image: url(:/qss_icons/rc/left_arrow.png);\x0a }\x0a\x0aQTabBar QToolButton::right-arrow:disabled {\x0a     image: url(:/qss_icons/rc/right_arrow_disabled.png);\x0a }\x0a\x0a QTabBar QToolButton::left-arrow:disabled {\x0a     image: url(:/qss_icons/rc/left_arrow_disabled.png);\x0a }\x0a\x0a\x0aQDockWidget {\x0a    border: 1px solid #403F3F;\x0a    titlebar-close-icon: url(:/qss_icons/rc/close.png);\x0a    titlebar-normal-icon: url(:/qss_icons/rc/undock.png);\x0a}\x0a\x0aQDockWidget::close-button, QDockWidget::float-button {\x0a    border: 1px solid transparent;\x0a    border-radius: 2px;\x0a    background: transparent;\x0a}\x0a\x0aQDockWidget::close-button:hover, QDockWidget::float-button:hover {\x0a    background: rgba(255, 255, 255, 10);\x0a}\x0a\x0aQDockWidget::close-button:pressed, QDockWidget::float-button:pressed {\x0a    padding: 1px -1px -1px 1px;\x0a    background: rgba(255, 255, 255, 10);\x0a}\x0a\x0aQTreeView, QListView\x0a{\x0a    border: 1px solid #444;\x0a    background-color: #201F1F;\x0a}\x0a\x0aQTreeView:branch:selected, QTreeView:branch:hover\x0a{\x0a    background: url(:/qss_icons/rc/transparent.png);\x0a}\x0a\x0aQTreeView::branch:has-siblings:!adjoins-item {\x0a    border-image: url(:/qss_icons/rc/transparent.png);\x0a}\x0a\x0aQTreeView::branch:has-siblings:adjoins-item {\x0a    border-image: url(:/qss_icons/rc/transparent.png);\x0a}\x0a\x0aQTreeView::branch:!has-children:!has-siblings:adjoins-item {\x0a    border-image: url(:/qss_icons/rc/transparent.png);\x0a}\x0a\x0aQTreeView::branch:has-children:!has-siblings:closed,\x0aQTreeView::branch:closed:has-children:has-siblings {\x0a    image: url(:/qss_icons/rc/branch_closed.png);\x0a}\x0a\x0aQTreeView::branch:open:has-children:!has-siblings,\x0aQTreeView::branch:open:has-children:has-siblings  {\x0a    image: url(:/qss_icons/rc/branch_open.png);\x0a}\x0a\x0aQTreeView::branch:has-children:!has-siblings:closed:hover,\x0aQTreeView::branch:closed:has-children:has-siblings:hover {\x0a    image: url(:/qss_icons/rc/branch_closed-on.png);\x0a    }\x0a\x0aQTreeView::branch:open:has-children:!has-siblings:hover,\x0aQTreeView::branch:open:has-children:has-siblings:hover  {\x0a    image: url(:/qss_icons/rc/branch_open-on.png);\x0a    }\x0a\x0aQListView::item:!selected:hover, QListView::item:!selected:hover, QTreeView::item:!selected:hover  {\x0a    background: rgba(0, 0, 0, 0);\x0a    outline: 0;\x0a    color: #FFFFFF\x0a}\x0a\x0aQListView::item:selected:hover, QListView::item:selected:hover, QTreeView::item:selected:hover  {\x0a    background: #3d8ec9;\x0a    color: #FFFFFF;\x0a}\x0a\x0aQSlider::groove:horizontal {\x0a    border: 1px solid #3A3939;\x0a    height: 8px;\x0a    background: #201F1F;\x0a    margin: 2px 0;\x0a    border-radius: 2px;\x0a}\x0a\x0aQSlider::handle:horizontal {\x0a    background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,\x0a      stop: 0.0 silver, stop: 0.2 #a8a8a8, stop: 1 #727272);\x0a    border: 1px solid #3A3939;\x0a    width: 14px;\x0a    height: 14px;\x0a    margin: -4px 0;\x0a    border-radius: 2px;\x0a}\x0a\x0aQSlider::groove:vertical {\x0a    border: 1px solid #3A3939;\x0a    width: 8px;\x0a    background: #201F1F;\x0a    margin: 0 0px;\x0a    border-radius: 2px;\x0a}\x0a\x0aQSlider::handle:vertical {\x0a    background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 silver,\x0a      stop: 0.2 #a8a8a8, stop: 1 #727272);\x0a    border: 1px solid #3A3939;\x0a    width: 14px;\x0a    height: 14px;\x0a    margin: 0 -4px;\x0a    border-radius: 2px;\x0a}\x0a\x0aQToolButton {\x0a    background-color: transparent;\x0a    border: 1px transparent #4A4949;\x0a    border-radius: 2px;\x0a    margin: 3px;\x0a    padding: 3px;\x0a}\x0a\x0aQToolButton[popupMode=\x221\x22] { /* only for MenuButtonPopup */\x0a padding-right: 20px; /* make way for the popup button */\x0a border: 1px transparent #4A4949;\x0a border-radius: 5px;\x0a}\x0a\x0aQToolButton[popupMode=\x222\x22] { /* only for InstantPopup */\x0a padding-right: 10px; /* make way for the popup button */\x0a border: 1px transparent #4A4949;\x0a}\x0a\x0a\x0aQToolButton:hover, QToolButton::menu-button:hover {\x0a    background-color: transparent;\x0a    border: 1px solid #78879b;\x0a}\x0a\x0aQToolButton:checked, QToolButton:pressed,\x0a        QToolButton::menu-button:pressed {\x0a    background-color: #4A4949;\x0a    border: 1px solid #78879b;\x0a}\x0a\x0a/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */\x0aQToolButton::menu-indicator {\x0a    image: url(:/qss_icons/rc/down_arrow.png);\x0a    top: -7px; left: -2px; /* shift it a bit */\x0a}\x0a\x0a/* the subcontrols below are used only in the MenuButtonPopup mode */\x0aQToolButton::menu-button {\x0a    border: 1px transparent #4A4949;\x0a    border-top-right-radius: 6px;\x0a    border-bottom-right-radius: 6px;\x0a    /* 16px width + 4px for border = 20px allocated above */\x0a    width: 16px;\x0a    outline: none;\x0a}\x0a\x0aQToolButton::menu-arrow {\x0a    image: url(:/qss_icons/rc/down_arrow.png);\x0a}\x0a\x0aQToolButton::menu-arrow:open {\x0a    top: 1px; left: 1px; /* shift it a bit */\x0a    border: 1px solid #3A3939;\x0a}\x0a\x0aQPushButton::menu-indicator  {\x0a    subcontrol-origin: padding;\x0a    subcontrol-position: bottom right;\x0a    left: 8px;\x0a}\x0a\x0aQTableView\x0a{\x0a    border: 1px solid #444;\x0a    gridline-color: #6c6c6c;\x0a    background-color: #201F1F;\x0a}\x0a\x0a\x0aQTableView, QHeaderView\x0a{\x0a    border-radius: 0px;\x0a}\x0a\x0aQTableView::item:pressed, QListView::item:pressed, QTreeView::item:pressed  {\x0a    background: #78879b;\x0a    color: #FFFFFF;\x0a}\x0a\x0aQTableView::item:selected:active, QTreeView::item:selected:active, QListView::item:selected:active  {\x0a    background: #3d8ec9;\x0a    color: #FFFFFF;\x0a}\x0a\x0a\x0aQHeaderView\x0a{\x0a    border: 1px transparent;\x0a    border-radius: 2px;\x0a    margin: 0px;\x0a    padding: 0px;\x0a}\x0a\x0aQHeaderView::section  {\x0a    background-color: #3A3939;\x0a    color: silver;\x0a    padding: 4px;\x0a    border: 1px solid #6c6c6c;\x0a    border-radius: 0px;\x0a    text-align: center;\x0a}\x0a\x0aQHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one\x0a{\x0a    border-top: 1px solid #6c6c6c;\x0a}\x0a\x0aQHeaderView::section::vertical\x0a{\x0a    border-top: transparent;\x0a}\x0a\x0aQHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one\x0a{\x0a    border-left: 1px solid #6c6c6c;\x0a}\x0a\x0aQHeaderView::section::horizontal\x0a{\x0a    border-left: transparent;\x0a}\x0a\x0a\x0aQHeaderView::section:checked\x0a {\x0a    color: white;\x0a    background-color: #5A5959;\x0a }\x0a\x0a /* style the sort indicator */\x0aQHeaderView::down-arrow {\x0a    image: url(:/qss_icons/rc/down_arrow.png);\x0a}\x0a\x0aQHeaderView::up-arrow {\x0a    image: url(:/qss_icons/rc/up_arrow.png);\x0a}\x0a\x0a\x0aQTableCornerButton::section {\x0a    background-color: #3A3939;\x0a    border: 1px solid #3A3939;\x0a    border-radius: 2px;\x0a}\x0a\x0aQToolBox  {\x0a    padding: 3px;\x0a    border: 1px transparent black;\x0a}\x0a\x0aQToolBox::tab {\x0a    color: #b1b1b1;\x0a    background-color: #302F2F;\x0a    border: 1px solid #4A4949;\x0a    border-bottom: 1px transparent #302F2F;\x0a    border-top-left-radius: 5px;\x0a    border-top-right-radius: 5px;\x0a}\x0a\x0a QToolBox::tab:selected { /* italicize selected tabs */\x0a    font: italic;\x0a    background-color: #302F2F;\x0a    border-color: #3d8ec9;\x0a }\x0a\x0aQStatusBar::item {\x0a    border: 1px solid #3A3939;\x0a    border-radius: 2px;\x0a }\x0a\x0a\x0aQFrame[height=\x223\x22], QFrame[width=\x223\x22] {\x0a    background-color: #444;\x0a}\x0a\x0a\x0aQSplitter::handle {\x0a    border: 1px dashed #3A3939;\x0a}\x0a\x0aQSplitter::handle:hover {\x0a    background-color: #787876;\x0a    border: 1px solid #3A3939;\x0a}\x0a\x0aQSplitter::handle:horizontal {\x0a    width: 1px;\x0a}\x0a\x0aQSplitter::handle:vertical {\x0a    height: 1px;\x0a}\x0a\x0aMessageItem\x0a{\x0a    border: none;\x0a}\x0a\x0aMessageEdit\x0a{\x0a    border: none;\x0a}\x0a\x0aMessageEdit::focus\x0a{\x0a    border: none;\x0a}\x0a\x0aMessageItem::focus\x0a{\x0a    border: none;\x0a}\x0a\x0aMessageEdit:hover\x0a{\x0a    border: none;\x0a}\x0a\x0aQListWidget QPushButton \x0a{\x0a    background-color: transparent;\x0a    border: none;\x0a}\x0a\x0aQPushButton:hover \x0a{\x0a    background-color: #4A4949;\x0a}\x0a\x0a#messages:item:selected\x0a{\x0a    background-color: transparent;\x0a}\x0a\x0a#friends_list:item:selected\x0a{\x0a    background-color: #333333;\x0a}\x0a\x0a#toxygen\x0a{\x0a    color: #A9A9A9;\x0a}\x0a\x0aQCheckBox\x0a{\x0a    spacing: 5px;\x0a    outline: none;\x0a    color: #bbb;\x0a    margin-bottom: 2px;\x0a    text-align: center;\x0a}\x0a\x0aQListWidget > QLabel \x0a{\x0a    color: #A9A9A9;\x0a}\x0a\x0a#contact_name\x0a{\x0a    padding-left: 22px;\x0a}\x00\x00\x03\xa5\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x03\x22IDATX\x85\xed\x96MlTU\x14\xc7\x7f\xe7\x0d\xa9\x09\xcc\x90Pv\xb6\xc6``\xe3\xa3\x864\xf4\xc3\xc6g\xa4\x1b\xa2\x98@\x13]\xc9\x1a6\xda\x84~Y\x5c\xcd\xce:\xa43\x09\xcb\xaee\x83\x89\x19L\x04\xc3\xc6:\x98\xb4o\x22bK'\xc64\xac\x9c\x067\x94t\x98\x92P:\xef\xef\xe2M\xa75\x99\xe9\xccCv\xf4\xbf\xba\xe7\xbds\xef\xf9\xdds\xee\x17\xeciO\xaf\xba,\x8a\xb3\x9b,\xb4\x1dN\xac\x0f\xc98\x07\xea\x06:\xaa\xbf\x8a\x88\xdf\xcd,\xfb\xa8t [H\xba\x1b/\x1d\xc0\xcb\xcc\x7f\x82,\x05\x1c\x01\xbb\x8f4\x8bC\x11\xc0\xa4\x0e\xe1\x9c\x02ua<0l\x22w\xa9\xf7\xfb\x97\x02\xf0\xe9\xf5\xeb\xb1\x7fV\xdeL!F\x80\x9f$&\x7f\x1d\xed[\xa8\xe7;\x90\xc9\x9f\x88\x05\x9a\xc28\x0d\x5c\xb9S\xea\x9d$iA\xab\x93\xac+/\xe3O{i\xbf\xf2~f~\xac\xe5>i\x7f\xdcK\xfb\x15/\xed\xa7\x9a\xf9\xee\x9a\x81j\xda\xbf3l,7\xd2;\x0d\xf0\xe1\xd5\xe5\xd7\x9e<\x7f|\xd1\xe03Y\xd0\x15\x0eb\x8b\x18\xd7\xe2\xb1\xf6\x99[\xc3\xc7\x9eU\xc1'\x10\xdf`\x0c\xdd\xb9\xd4\x97\x8d\x0c\xe0&\x0bm\xed\x07\xcb\x7f\x1a\xfa+7\xd2\xff\x11\xc0\x07W\xe7;+\x9b\xceMP\x17X\x00r\xaa\xc3\x84mc1\x16\xd3\x99\xd9\xe1\xfe\x22\xc0{\x99\xfcm\x93\x8e\xac\x96\xe2n\xa3\x85\xe94\x028\x9cX\x1f\x02\xde\x0ad\x97\xb7f^\xd9tnb:\x1ezhG\xdfZ\xbb\xab\xb2\xc9\x8fn\xb2\xd0\x06\xe0\x04\xf6%p\xf4P\xa2|\xb6Q\x9c\x86\x00\xe1Vcak\xc1\x95+\xab\x17@]h\x97\xb2\x09\x03{\xa7\xfd`\xf9\x02@n\xb4\xe7\x9e\xc4\x92At\x00P\xb7\xa1_jf`\xe7\xc3T\xef.A\x00\x9c\xdf\xb2\x0d~\xc68\xf9\x02\x00\xbc.\xacX\xb3L\xee\x7f\xd3^_\x06\x0e\xc8\xdd\x01\xb4\xc2\xf6\x81\x15\x09\x00,\xdaIY7\x80\x99\x11f%2\xc0C\x02:k\x96\xac\xd0j\x09$\x96\xb6mu\x00\x0f\xa3\x03\x88\xdf\x04\xa7\xb6=\xf5m\xab%0\xb3k;>\x0d\x02\xf9\xc8\x00f\x965\xe3\xf8@&\x7f\x02 \x1ek\x9f\xc1X\xc4\xd0.\xd1%\xe3\x8f\xd5R|\x06\xc0\xcb\xccu\x03oc\xfa!2\xc0\xa3\xd2\x81,\xc6\x83X\xa0)\x80[\xc3\xc7\x9e\xc5b:\x03\xdc\xafF\xab\x95\xa3\xba\xf2\x11,TT\xf9\xb8\x90t7\x90\x0c9)`\xf9\xe9\xfe}7\x22\x03\x14\x92\xee\x86\xc48\xc6i/\xed\x8f\x03\xcc\x0e\xf7\x17W\xd7\xe2=\xc0\x17R\x90\x07\xd6\x81u\xa4\xbc\x99>\x7f\xbc\x16\xef\x9b\x1b\x19X\x01\xf0\xd2\xfe$0h\x0a\xc6\xee^<\xf9\xbcQ\x9c\xa6\xf2\xd2~\xaaz\xb1\x8c\xb7\xd4A2oz\xferx\x81\xf9S\xcd\xdc\x9bo\xb3\xa4\x1c/\x91\xff\x1ac\x02\xb8mr&s\xa3=\xf7\xea\xc2f\xe6\xba\xabi\x1f4#\x95[\xeb\xfd\xaa\xd9u\x1c\xe1A\xe2\x9fC\x5c\x01\x8eJ,\x991\x8b\xf17\x00\xe2\x0d\xc2\x1d\xe3\x02\xcb\xa6`,7\xfan\xc3\x85\xf7B\x00\x10\xde\x90\x87\x12\xe5\xb3T\x9fd\x86u\x86\xf1U4\xd9]\x1ce\x9f\xee\xdfw\xe3\x7f\xd5|O{z\xe5\xf4/\x95?G\xacm\xe50s\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02J\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq\xde\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdf\x04\x19\x10\x14\x1a8\xc77\xd0\x00\x00\x00\x1diTXtComment\x00\x00\x00\x00\x00Created with GIMPd.e\x07\x00\x00\x01\xaeIDATx\xda\xed\x9bI\x92\xc3 \x0cE#]\xdc\xf6\xc9\xd3\xbb\xaeT\x06&\xe9\x7f\x09\x8c\xd6]2\xef!h \xf0x\xec\xd8\xb1\xe3\xce!\xcc\x8f\x9d\xe7\xf9l\xfc;YB@+p\xa4\x10\xc9\x0a\xcd\x92!\xb3\x80\xa3D\xc8\x8c\xf0\x9e\x12dFpO\x112;\xbcU\x82\xcc\x0en\x15!+\xc1\x8fH\x90\xd5\xe0{%\xe8^\x0a/\xd8\xfb=U V\xf8\xe38\xfes\x5c\xd7E\x11\xf5\xfa\xcd\xdawk\x12\xd4\xbba\xef\x8dC\xc3[C\x11\xa5\x8f\x920\x92\xb7\xc6\xa0\xa8q\xef-\xc1\x92\xaf\xc4b\x1e\x02\xa5\xf1\xe7%\xa1\x94\xc7:\xef\x88W\xef\xa3\x1a\xe9\x99\xf7\xdb\x84\xe86\x09\x22*\x01\xd9\xf3\x90\xff\x02\x9e\x12\x18\xf0_\x87\x80\xc7\xa2\xc7\xdax$\xfc\xfb0\x80,\x85-\x95\xc0\xeay\xf8^`D\x02\x1b\x1e\xbe\x19\xea\x91\x10\x01\xff1\x07\xa06=586\xfc\xeb<@\xd9\x0e\x8f\xce\x09\x8c\xcd\x15\xed<\xa0\x17\x86\xb5\xb3\xa4\x1e\x88\xb4B\xb1\xe0\xe9\x02Z\xe0\x98\xf0!\x02,\xeb\x80\xe9\x05\xb4\xc21%h6x\xb6\x04\x8d\x86g\x9c'\x84\x0ah\x81\x8f\x94\x00\xd9\x0d\x8e\xf6<cQD\xd9\x0d\x8e\xc2DT\x82f\x1a\xf3\x11\x124\x13|\x84\x04\xb7CQ\xc4\x18\xf6\xce\x07=\x14EL`\x8cJ\xd0\xac\xf0,\x09R(\x97g4\xbc\xe7w~\xfdH\x1ar&\x98!_U\x80\xe5\xe6\x15\xaa\xb1\xa3yK,\x9a\xbd\xe7\xd1\xf9\xcd\x17$\xb2G\xad\x92\xf7\x15\x99\x8ed\xfb\x96\xd8\x8a\xb1/J\x0e$\xbf\xefU\xd9\xcc\x22h\x97\xa53J\x08\xb9.\x9fE\x82\xf5\xd1\xc4~2\x03h\xd8=\x1fM!eL\xf5l\xceC\x08\xf3\xe1\xe4\x8e\xbb\xc7\x1f\xfe\x88Z\xe2\xcd\xef\x1cI\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xac\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x07\x00\x00\x00?\x08\x06\x00\x00\x00,{\xd2\x13\x00\x00\x00\x06bKGD\x00\xb3\x00y\x00y\xdc\xddS\xfc\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdf\x04\x19\x10.\x14\xfa\xd6\xc4\xae\x00\x00\x009IDAT8\xcbc` \x06\xc4\xc7\xc73\xc4\xc7\xc7\xa3\x881aS\x84S\x12\xaf\xce\x91(\xc9\x82\xc4\xfe\x8f\xc4f\x1c\x0d\xa1Q\xc9Q\xc9QI|\x05\x06\xe3h\x08\x91*I>\x00\x00\x88K\x04\xd39.\x90?\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xb6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x18\x00\x00\x00\x11\x08\x06\x00\x00\x00\xc7xl0\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b,\x0d\x1fC\xaa\xe1\x00\x00\x006IDAT8\xcbc` \x01,Z\xb4\xe8\xff\xa2E\x8b\xfe\x93\xa2\x87\x89\x81\xc6`\xd4\x82\x11`\x01#\xa9\xc9t\xd0\xf9\x80\x85\x1cMqqq\x8c\xa3\xa9h\xd4\x82ad\x01\x001\xb5\x09\xec\x1fK\xb4\x15\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02B\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq\xde\x00\x00\x00\x06bKGD\x00\xb3\x00y\x00y\xdc\xddS\xfc\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdf\x04\x19\x10\x17;_\x83tM\x00\x00\x00\x1diTXtComment\x00\x00\x00\x00\x00Created with GIMPd.e\x07\x00\x00\x01\xa6IDATx\xda\xed\x9b\xdb\x0e\xc3 \x0cC\x9bh\xff\xdd\xf6\xcb\xb7\xb7i\x9avIK\xec\x98B^7Q|p(\x85\xb0,3f\xcc\x189\x8c\xf9\xb0m\xdb\xee\xc1\xff\xd9%\x00D\x05W\x021U\xd1,\x18\xd6\x8bp\x14\x08\xebQ|&\x04\xebQx&\x08\xeb]|+\x04\xeb]x+\x08\xbb\x92\xf83\x10\xecj\xe2\x8fB\xb8Uvr]\xd7g'\xf7}/\x01lU\xa3\xff*\x1e\x05!\xe2\x02S\x11_\x05\xc1+m\x7f\xe6wj\x0ad\x8f\xfe\x11q\x99N\xf8\xe5\x02S\x14\xcf\x84\xe0\xd5\xb6\xff%\x92\x91\x0e\x86\x1e\xfd\xa8x\xc6\xc4\xf8\xc9\x05\xae2\xf2UNp%\xdbW@0\x84\xfd[\xed\x8cL\x87\xf74p\x85\x91\xaft\x82\xab\x89gCpE\xf1L\x08\x96\x91\xff\xe8WXv\xfb\xaf\xf3\x80+\x8e<\xd3\x09\xae.\x1e\x0d\xc1{\x10\x8f\x84\xe0\xccN*\xb6O]\x07(\xb6\xefj9\xc9N;W\xcbI\xf6\x9c\xe3\xc8\x9c\xcc\x82\x80\x9cpS\xe6\x00$\x04\xf4\xdb&\xf5k0\xbb\xb3\x08\xf1\xd0\xaf\xc1L'\xb0\xd6\x19\xd4u@\x14\x02s\x91\x05\xd9\x11j\x81\xc0^aB7E\x8f\x8aA\x8b\xa7o\x8a\x1eqB\xc5\xb7\x05\x1c@\x14B\x95\xf8\xaf)\x90\x99\x06-\xeb\x81\xcb\x9c\x0c\x9d\x11\xc3\xaa\x17\xa0\x1e\x8eF\x9d\xc0<\x22\xa7\x1f\x8f\xff\x13\xc7\xae\x14))\x90\xf8\xe6\x04\x84\xf8\x7f\x05\x12e%2\xef\x10*\xc4\x87\x01 !\xa0\x22Z%\xe6\xcb\xe01\x0b%O4>n\xa9\xac2\x08Z\xb1\xb4\x22\x84\x92ry\x15\x08\xad\x97&\xe6\x95\x19@\xc7\xc6\xbc4\x85\x84\xd1\xd5\xb5\xb9\x0c \xcc\x8b\x933F\x8f\x07S!r\xe7\x176+c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xd4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x02QIDATX\x85\xed\x96AKTQ\x14\xc7\x7f\xe7\x8d\xb8\xd0&0wi\x84\xe1\xaa)\x90A\xc7\x92^\xa0\x1b\xa1\x8d\x0a\xf5\x19Z;3\xda\xd8j\x16A6\x83\xf3\xbe\x87A\x8d\xad\xc2M\xf6\x14\xf4\x0d\x99H\x0e\x11\xe2\xaa\x11\xdb\x184\xa8\x0b\xc3wZ\xccH\x10\xf3t\xee\xe8\xae\xf9o\xef9\xfc\x7f\xf7\xdc{\xcf=\xd0TS\xff\xbb\xc4$8\x92.\xb6v\x86\x0f'T\x18\x07\x8d\x02]\xd5\xa5\x12\xcag\x11\xc9\xef\x97\xdb\xf3\xc5t\xe4\xf8\xd2\x01lg\xed1*\x19\xa0\x07\xe4\x0b\xaaKX\x94\x00D\xb5K\xb1\x86A\xef\x22\xec\x082\xedN\xc6\xde\x5c\x0a\xc0\x93\xf9\xf9\xd0\x8f\xdd\x9b\x19\x948\xf0^\x95\xd4Jbp\xb3V\xec\x90S\xe8\x0b\xf9:\x8b0\x0ad\x97\xcb\xb1\x14i\xf1\xeb\xdddM\xd9\x8e7g\xe7\xbc\x93\x87\xceZ\xb2\xee\x9c\x9c7e\xe7\xbc\x13;\xe7e\xce\x8b=\xb3\x02\xd5\xb2\xbf\x16$\xe9\xc6cs\xf5\x02Tr\xbdi\x94W\x08\x13\xcb\x93\x83yc\x80H\xba\xd8z\xed\xea\xc1WA\xbf\xb9\xf1{\x8fL\xccO\xf5\xc0),\x8aj\xcf\xcf\xf2\x95H\xd0\xc5\xb4\x82\x92;\xc3\x87\x13\xc0-_e\xa6\x11s\x00\xcb\x97g@oG\xf8`,0&h\xa1\xf2\xd4\xd8\x0c\xbap\xf5\xc8M\x0cl\xa8\xb2%`\x0e\x00\x1a\x15\xf4c\xa3\xe6\xa7\x12\xf8\x80\xd0\xdf\x00\x00\xd7\x15)]\x14@a\x97\xbf\x0d\xcb\x08\x00\xc4\xacS\xd64\x10\x11 \xb0\x17\x9c\x05\xb0\x87O\xf7E\x01\x14\xed\x02\xf6\xcc\x01\x94O\x0a\xc3\x17\x05\x00F\x80\x821\x80\x88\xe4E\xb83\xe4\x14\xfa\x1au\xb6\x9d\xd5(p\x1b\xd1w\xc6\x00\xfb\xe5\xf6<\xc2N\xc8\xd7\xd9\x86\xdcU\x05\xb52\xc0\xf6Q[\xcb\x821@1\x1d9Ve\x0aa\xd4\xceyS\xa6\xfev\xceK\x01#\xa2~r\xfdi\xffoc\x00\x80\x95\xf8\xe0[ \x0b\xcc\xd6\x0d\xa1*\xf6\xdc\xda\x0c\x22/D\xc8\xb8\x89\xfb\x81\xe5\x87z\xe6\x81\xb4Zv\xb8\xf0\x12a\x1aX\x14\xb5Rnb`\xa3V\xa8\xed\xacF\xabe\x1f\x11!\xe3\xfe\x8a=?\xef;6\x18H\xbcq\x94,\xd0\xab\xca\x96\x08K\x08\xdf\x01PnPy1\x11`[\xd4O\x9e\xb7sc\x00\xa8\xfc\x90\x1d\xe1\x831\xaa#\x99 \xdd\x15\x7f-\x89\xca:\x96\xe6\x8f\xdaZ\x16\xce:\xf3\xa6\x9aj\xea_\xfd\x01\xd3\x1c\xd9\x7f^\xb93\xcd\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\x9f\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x14\x1f\xf9#\xd9\x0b\x00\x00\x00#IDAT\x08\xd7c`\xc0\x0d\xe6|\x80\xb1\x18\x91\x05R\x04\xe0B\x08\x15)\x02\x0c\x0c\x8c\xc8\x02\x08\x95h\x00\x00\xac\xac\x07\x90Ne4\xac\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0bN\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x0aOiCCPPhotoshop ICC profile\x00\x00x\xda\x9dSgTS\xe9\x16=\xf7\xde\xf4BK\x88\x80\x94KoR\x15\x08 RB\x8b\x80\x14\x91&*!\x09\x10J\x88!\xa1\xd9\x15Q\xc1\x11EE\x04\x1b\xc8\xa0\x88\x03\x8e\x8e\x80\x8c\x15Q,\x0c\x8a\x0a\xd8\x07\xe4!\xa2\x8e\x83\xa3\x88\x8a\xca\xfb\xe1{\xa3k\xd6\xbc\xf7\xe6\xcd\xfe\xb5\xd7>\xe7\xac\xf3\x9d\xb3\xcf\x07\xc0\x08\x0c\x96H3Q5\x80\x0c\xa9B\x1e\x11\xe0\x83\xc7\xc4\xc6\xe1\xe4.@\x81\x0a$p\x00\x10\x08\xb3d!s\xfd#\x01\x00\xf8~<<+\x22\xc0\x07\xbe\x00\x01x\xd3\x0b\x08\x00\xc0M\x9b\xc00\x1c\x87\xff\x0f\xeaB\x99\x5c\x01\x80\x84\x01\xc0t\x918K\x08\x80\x14\x00@z\x8eB\xa6\x00@F\x01\x80\x9d\x98&S\x00\xa0\x04\x00`\xcbcb\xe3\x00P-\x00`'\x7f\xe6\xd3\x00\x80\x9d\xf8\x99{\x01\x00[\x94!\x15\x01\xa0\x91\x00 \x13e\x88D\x00h;\x00\xac\xcfV\x8aE\x00X0\x00\x14fK\xc49\x00\xd8-\x000IWfH\x00\xb0\xb7\x00\xc0\xce\x10\x0b\xb2\x00\x08\x0c\x000Q\x88\x85)\x00\x04{\x00`\xc8##x\x00\x84\x99\x00\x14F\xf2W<\xf1+\xae\x10\xe7*\x00\x00x\x99\xb2<\xb9$9E\x81[\x08-q\x07WW.\x1e(\xceI\x17+\x146a\x02a\x9a@.\xc2y\x99\x192\x814\x0f\xe0\xf3\xcc\x00\x00\xa0\x91\x15\x11\xe0\x83\xf3\xfdx\xce\x0e\xae\xce\xce6\x8e\xb6\x0e_-\xea\xbf\x06\xff\x22bb\xe3\xfe\xe5\xcf\xabp@\x00\x00\xe1t~\xd1\xfe,/\xb3\x1a\x80;\x06\x80m\xfe\xa2%\xee\x04h^\x0b\xa0u\xf7\x8bf\xb2\x0f@\xb5\x00\xa0\xe9\xdaW\xf3p\xf8~<<E\xa1\x90\xb9\xd9\xd9\xe5\xe4\xe4\xd8J\xc4B[a\xcaW}\xfeg\xc2_\xc0W\xfdl\xf9~<\xfc\xf7\xf5\xe0\xbe\xe2$\x812]\x81G\x04\xf8\xe0\xc2\xcc\xf4L\xa5\x1c\xcf\x92\x09\x84b\xdc\xe6\x8fG\xfc\xb7\x0b\xff\xfc\x1d\xd3\x22\xc4Ib\xb9X*\x14\xe3Q\x12q\x8eD\x9a\x8c\xf32\xa5\x22\x89B\x92)\xc5%\xd2\xffd\xe2\xdf,\xfb\x03>\xdf5\x00\xb0j>\x01{\x91-\xa8]c\x03\xf6K'\x10Xt\xc0\xe2\xf7\x00\x00\xf2\xbbo\xc1\xd4(\x08\x03\x80h\x83\xe1\xcfw\xff\xef?\xfdG\xa0%\x00\x80fI\x92q\x00\x00^D$.T\xca\xb3?\xc7\x08\x00\x00D\xa0\x81*\xb0A\x1b\xf4\xc1\x18,\xc0\x06\x1c\xc1\x05\xdc\xc1\x0b\xfc`6\x84B$\xc4\xc2B\x10B\x0ad\x80\x1cr`)\xac\x82B(\x86\xcd\xb0\x1d*`/\xd4@\x1d4\xc0Qh\x86\x93p\x0e.\xc2U\xb8\x0e=p\x0f\xfaa\x08\x9e\xc1(\xbc\x81\x09\x04A\xc8\x08\x13a!\xda\x88\x01b\x8aX#\x8e\x08\x17\x99\x85\xf8!\xc1H\x04\x12\x8b$ \xc9\x88\x14Q\x22K\x915H1R\x8aT UH\x1d\xf2=r\x029\x87\x5cF\xba\x91;\xc8\x002\x82\xfc\x86\xbcG1\x94\x81\xb2Q=\xd4\x0c\xb5C\xb9\xa87\x1a\x84F\xa2\x0b\xd0dt1\x9a\x8f\x16\xa0\x9b\xd0r\xb4\x1a=\x8c6\xa1\xe7\xd0\xabh\x0f\xda\x8f>C\xc70\xc0\xe8\x18\x073\xc4l0.\xc6\xc3B\xb18,\x09\x93c\xcb\xb1\x22\xac\x0c\xab\xc6\x1a\xb0V\xac\x03\xbb\x89\xf5c\xcf\xb1w\x04\x12\x81E\xc0\x096\x04wB a\x1eAHXLXN\xd8H\xa8 \x1c$4\x11\xda\x097\x09\x03\x84Q\xc2'\x22\x93\xa8K\xb4&\xba\x11\xf9\xc4\x18b21\x87XH,#\xd6\x12\x8f\x13/\x10{\x88C\xc47$\x12\x89C2'\xb9\x90\x02I\xb1\xa4T\xd2\x12\xd2F\xd2nR#\xe9,\xa9\x9b4H\x1a#\x93\xc9\xdadk\xb2\x079\x94, +\xc8\x85\xe4\x9d\xe4\xc3\xe43\xe4\x1b\xe4!\xf2[\x0a\x9db@q\xa4\xf8S\xe2(R\xcajJ\x19\xe5\x10\xe54\xe5\x06e\x982AU\xa3\x9aR\xdd\xa8\xa1T\x115\x8fZB\xad\xa1\xb6R\xafQ\x87\xa8\x134u\x9a9\xcd\x83\x16IK\xa5\xad\xa2\x95\xd3\x1ah\x17h\xf7i\xaf\xe8t\xba\x11\xdd\x95\x1eN\x97\xd0W\xd2\xcb\xe9G\xe8\x97\xe8\x03\xf4w\x0c\x0d\x86\x15\x83\xc7\x88g(\x19\x9b\x18\x07\x18g\x19w\x18\xaf\x98L\xa6\x19\xd3\x8b\x19\xc7T071\xeb\x98\xe7\x99\x0f\x99oUX*\xb6*|\x15\x91\xca\x0a\x95J\x95&\x95\x1b*/T\xa9\xaa\xa6\xaa\xde\xaa\x0bU\xf3U\xcbT\x8f\xa9^S}\xaeFU3S\xe3\xa9\x09\xd4\x96\xabU\xaa\x9dP\xebS\x1bSg\xa9;\xa8\x87\xaag\xa8oT?\xa4~Y\xfd\x89\x06Y\xc3L\xc3OC\xa4Q\xa0\xb1_\xe3\xbc\xc6 \x0bc\x19\xb3x,!k\x0d\xab\x86u\x815\xc4&\xb1\xcd\xd9|v*\xbb\x98\xfd\x1d\xbb\x8b=\xaa\xa9\xa19C3J3W\xb3R\xf3\x94f?\x07\xe3\x98q\xf8\x9ctN\x09\xe7(\xa7\x97\xf3~\x8a\xde\x14\xef)\xe2)\x1b\xa64L\xb91e\x5ck\xaa\x96\x97\x96X\xabH\xabQ\xabG\xeb\xbd6\xae\xed\xa7\x9d\xa6\xbdE\xbbY\xfb\x81\x0eA\xc7J'\x5c'Gg\x8f\xce\x05\x9d\xe7S\xd9S\xdd\xa7\x0a\xa7\x16M=:\xf5\xae.\xaak\xa5\x1b\xa1\xbbDw\xbfn\xa7\xee\x98\x9e\xbe^\x80\x9eLo\xa7\xdey\xbd\xe7\xfa\x1c}/\xfdT\xfdm\xfa\xa7\xf5G\x0cX\x06\xb3\x0c$\x06\xdb\x0c\xce\x18<\xc55qo<\x1d/\xc7\xdb\xf1QC]\xc3@C\xa5a\x95a\x97\xe1\x84\x91\xb9\xd1<\xa3\xd5F\x8dF\x0f\x8ci\xc6\x5c\xe3$\xe3m\xc6m\xc6\xa3&\x06&!&KM\xeaM\xee\x9aRM\xb9\xa6)\xa6;L;L\xc7\xcd\xcc\xcd\xa2\xcd\xd6\x995\x9b=1\xd72\xe7\x9b\xe7\x9b\xd7\x9b\xdf\xb7`ZxZ,\xb6\xa8\xb6\xb8eI\xb2\xe4Z\xa6Y\xee\xb6\xbcn\x85Z9Y\xa5XUZ]\xb3F\xad\x9d\xad%\xd6\xbb\xad\xbb\xa7\x11\xa7\xb9N\x93N\xab\x9e\xd6g\xc3\xb0\xf1\xb6\xc9\xb6\xa9\xb7\x19\xb0\xe5\xd8\x06\xdb\xae\xb6m\xb6}agb\x17g\xb7\xc5\xae\xc3\xee\x93\xbd\x93}\xba}\x8d\xfd=\x07\x0d\x87\xd9\x0e\xab\x1dZ\x1d~s\xb4r\x14:V:\xde\x9a\xce\x9c\xee?}\xc5\xf4\x96\xe9/gX\xcf\x10\xcf\xd83\xe3\xb6\x13\xcb)\xc4i\x9dS\x9b\xd3Gg\x17g\xb9s\x83\xf3\x88\x8b\x89K\x82\xcb.\x97>.\x9b\x1b\xc6\xdd\xc8\xbd\xe4Jt\xf5q]\xe1z\xd2\xf5\x9d\x9b\xb3\x9b\xc2\xed\xa8\xdb\xaf\xee6\xeei\xee\x87\xdc\x9f\xcc4\x9f)\x9eY3s\xd0\xc3\xc8C\xe0Q\xe5\xd1?\x0b\x9f\x950k\xdf\xac~OCO\x81g\xb5\xe7#/c/\x91W\xad\xd7\xb0\xb7\xa5w\xaa\xf7a\xef\x17>\xf6>r\x9f\xe3>\xe3<7\xde2\xdeY_\xcc7\xc0\xb7\xc8\xb7\xcbO\xc3o\x9e_\x85\xdfC\x7f#\xffd\xffz\xff\xd1\x00\xa7\x80%\x01g\x03\x89\x81A\x81[\x02\xfb\xf8z|!\xbf\x8e?:\xdbe\xf6\xb2\xd9\xedA\x8c\xa0\xb9A\x15A\x8f\x82\xad\x82\xe5\xc1\xad!h\xc8\xec\x90\xad!\xf7\xe7\x98\xce\x91\xcei\x0e\x85P~\xe8\xd6\xd0\x07a\xe6a\x8b\xc3~\x0c'\x85\x87\x85W\x86?\x8ep\x88X\x1a\xd11\x975w\xd1\xdcCs\xdfD\xfaD\x96D\xde\x9bg1O9\xaf-J5*>\xaa.j<\xda7\xba4\xba?\xc6.fY\xcc\xd5X\x9dXIlK\x1c9.*\xae6nl\xbe\xdf\xfc\xed\xf3\x87\xe2\x9d\xe2\x0b\xe3{\x17\x98/\xc8]py\xa1\xce\xc2\xf4\x85\xa7\x16\xa9.\x12,:\x96@L\x88N8\x94\xf0A\x10*\xa8\x16\x8c%\xf2\x13w%\x8e\x0ay\xc2\x1d\xc2g\x22/\xd16\xd1\x88\xd8C\x5c*\x1eN\xf2H*Mz\x92\xec\x91\xbc5y$\xc53\xa5,\xe5\xb9\x84'\xa9\x90\xbcL\x0dL\xdd\x9b:\x9e\x16\x9av m2=:\xbd1\x83\x92\x91\x90qB\xaa!M\x93\xb6g\xeag\xe6fv\xcb\xace\x85\xb2\xfe\xc5n\x8b\xb7/\x1e\x95\x07\xc9k\xb3\x90\xac\x05Y-\x0a\xb6B\xa6\xe8TZ(\xd7*\x07\xb2geWf\xbf\xcd\x89\xca9\x96\xab\x9e+\xcd\xed\xcc\xb3\xca\xdb\x907\x9c\xef\x9f\xff\xed\x12\xc2\x12\xe1\x92\xb6\xa5\x86KW-\x1dX\xe6\xbd\xacj9\xb2<qy\xdb\x0a\xe3\x15\x05+\x86V\x06\xac<\xb8\x8a\xb6*m\xd5O\xab\xedW\x97\xae~\xbd&zMk\x81^\xc1\xca\x82\xc1\xb5\x01k\xeb\x0bU\x0a\xe5\x85}\xeb\xdc\xd7\xed]OX/Y\xdf\xb5a\xfa\x86\x9d\x1b>\x15\x89\x8a\xae\x14\xdb\x17\x97\x15\x7f\xd8(\xdcx\xe5\x1b\x87o\xca\xbf\x99\xdc\x94\xb4\xa9\xab\xc4\xb9d\xcff\xd2f\xe9\xe6\xde-\x9e[\x0e\x96\xaa\x97\xe6\x97\x0en\x0d\xd9\xda\xb4\x0d\xdfV\xb4\xed\xf5\xf6E\xdb/\x97\xcd(\xdb\xbb\x83\xb6C\xb9\xa3\xbf<\xb8\xbce\xa7\xc9\xce\xcd;?T\xa4T\xf4T\xfaT6\xee\xd2\xdd\xb5a\xd7\xf8n\xd1\xee\x1b{\xbc\xf64\xec\xd5\xdb[\xbc\xf7\xfd>\xc9\xbe\xdbU\x01UM\xd5f\xd5e\xfbI\xfb\xb3\xf7?\xae\x89\xaa\xe9\xf8\x96\xfbm]\xadNmq\xed\xc7\x03\xd2\x03\xfd\x07#\x0e\xb6\xd7\xb9\xd4\xd5\x1d\xd2=TR\x8f\xd6+\xebG\x0e\xc7\x1f\xbe\xfe\x9d\xefw-\x0d6\x0dU\x8d\x9c\xc6\xe2#pDy\xe4\xe9\xf7\x09\xdf\xf7\x1e\x0d:\xdav\x8c{\xac\xe1\x07\xd3\x1fv\x1dg\x1d/jB\x9a\xf2\x9aF\x9bS\x9a\xfb[b[\xbaO\xcc>\xd1\xd6\xea\xdez\xfcG\xdb\x1f\x0f\x9c4<YyJ\xf3T\xc9i\xda\xe9\x82\xd3\x93g\xf2\xcf\x8c\x9d\x95\x9d}~.\xf9\xdc`\xdb\xa2\xb6{\xe7c\xce\xdfj\x0fo\xef\xba\x10t\xe1\xd2E\xff\x8b\xe7;\xbc;\xce\x5c\xf2\xb8t\xf2\xb2\xdb\xe5\x13W\xb8W\x9a\xaf:_m\xeat\xea<\xfe\x93\xd3O\xc7\xbb\x9c\xbb\x9a\xae\xb9\x5ck\xb9\xeez\xbd\xb5{f\xf7\xe9\x1b\x9e7\xce\xdd\xf4\xbdy\xf1\x16\xff\xd6\xd5\x9e9=\xdd\xbd\xf3zo\xf7\xc5\xf7\xf5\xdf\x16\xdd~r'\xfd\xce\xcb\xbb\xd9w'\xee\xad\xbcO\xbc_\xf4@\xedA\xd9C\xdd\x87\xd5?[\xfe\xdc\xd8\xef\xdc\x7fj\xc0w\xa0\xf3\xd1\xdcG\xf7\x06\x85\x83\xcf\xfe\x91\xf5\x8f\x0fC\x05\x8f\x99\x8f\xcb\x86\x0d\x86\xeb\x9e8>99\xe2?r\xfd\xe9\xfc\xa7C\xcfd\xcf&\x9e\x17\xfe\xa2\xfe\xcb\xae\x17\x16/~\xf8\xd5\xeb\xd7\xce\xd1\x98\xd1\xa1\x97\xf2\x97\x93\xbfm|\xa5\xfd\xea\xc0\xeb\x19\xaf\xdb\xc6\xc2\xc6\x1e\xbe\xc9x31^\xf4V\xfb\xed\xc1w\xdcw\x1d\xef\xa3\xdf\x0fO\xe4| \x7f(\xffh\xf9\xb1\xf5S\xd0\xa7\xfb\x93\x19\x93\x93\xff\x04\x03\x98\xf3\xfcc3-\xdb\x00\x00\x00 cHRM\x00\x00z%\x00\x00\x80\x83\x00\x00\xf9\xff\x00\x00\x80\xe9\x00\x00u0\x00\x00\xea`\x00\x00:\x98\x00\x00\x17o\x92_\xc5F\x00\x00\x00yIDATx\xda\xec\x971\x0a\xc00\x0c\x03%\x93_\xf5\xfd}\x97\xb3\xb4\x10h\x07gPR\xa8<y1>$\x07\x14f&vV`s\xb5\xbb9I\x00X%\x07\x8fK\xf9Q\x81\x95^\xe4C\x817J\xd5\xd2\xca\x0dP!{\x15\x80J\xef?\xf7\x0a\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\x10\xd5\xf4\xaaJ\xc61\x13\xa1\x15\xb1\xbc\xcd\x0e(-\xe0\x22\xdb9\xee\xe2\xef\x7f\xc7\x1d\x00\x00\xff\xff\x03\x00>H\x12?\xd7\xafML\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xc3\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq\xde\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x0b\x07\x09.7\xffD\xe8\xf0\x00\x00\x00\x1diTXtComment\x00\x00\x00\x00\x00Created with GIMPd.e\x07\x00\x00\x00'IDATx\xda\xed\xc1\x01\x0d\x00\x00\x00\xc2\xa0\xf7Om\x0e7\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80w\x03@@\x00\x01\xafz\x0e\xe8\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0bN\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x0aOiCCPPhotoshop ICC profile\x00\x00x\xda\x9dSgTS\xe9\x16=\xf7\xde\xf4BK\x88\x80\x94KoR\x15\x08 RB\x8b\x80\x14\x91&*!\x09\x10J\x88!\xa1\xd9\x15Q\xc1\x11EE\x04\x1b\xc8\xa0\x88\x03\x8e\x8e\x80\x8c\x15Q,\x0c\x8a\x0a\xd8\x07\xe4!\xa2\x8e\x83\xa3\x88\x8a\xca\xfb\xe1{\xa3k\xd6\xbc\xf7\xe6\xcd\xfe\xb5\xd7>\xe7\xac\xf3\x9d\xb3\xcf\x07\xc0\x08\x0c\x96H3Q5\x80\x0c\xa9B\x1e\x11\xe0\x83\xc7\xc4\xc6\xe1\xe4.@\x81\x0a$p\x00\x10\x08\xb3d!s\xfd#\x01\x00\xf8~<<+\x22\xc0\x07\xbe\x00\x01x\xd3\x0b\x08\x00\xc0M\x9b\xc00\x1c\x87\xff\x0f\xeaB\x99\x5c\x01\x80\x84\x01\xc0t\x918K\x08\x80\x14\x00@z\x8eB\xa6\x00@F\x01\x80\x9d\x98&S\x00\xa0\x04\x00`\xcbcb\xe3\x00P-\x00`'\x7f\xe6\xd3\x00\x80\x9d\xf8\x99{\x01\x00[\x94!\x15\x01\xa0\x91\x00 \x13e\x88D\x00h;\x00\xac\xcfV\x8aE\x00X0\x00\x14fK\xc49\x00\xd8-\x000IWfH\x00\xb0\xb7\x00\xc0\xce\x10\x0b\xb2\x00\x08\x0c\x000Q\x88\x85)\x00\x04{\x00`\xc8##x\x00\x84\x99\x00\x14F\xf2W<\xf1+\xae\x10\xe7*\x00\x00x\x99\xb2<\xb9$9E\x81[\x08-q\x07WW.\x1e(\xceI\x17+\x146a\x02a\x9a@.\xc2y\x99\x192\x814\x0f\xe0\xf3\xcc\x00\x00\xa0\x91\x15\x11\xe0\x83\xf3\xfdx\xce\x0e\xae\xce\xce6\x8e\xb6\x0e_-\xea\xbf\x06\xff\x22bb\xe3\xfe\xe5\xcf\xabp@\x00\x00\xe1t~\xd1\xfe,/\xb3\x1a\x80;\x06\x80m\xfe\xa2%\xee\x04h^\x0b\xa0u\xf7\x8bf\xb2\x0f@\xb5\x00\xa0\xe9\xdaW\xf3p\xf8~<<E\xa1\x90\xb9\xd9\xd9\xe5\xe4\xe4\xd8J\xc4B[a\xcaW}\xfeg\xc2_\xc0W\xfdl\xf9~<\xfc\xf7\xf5\xe0\xbe\xe2$\x812]\x81G\x04\xf8\xe0\xc2\xcc\xf4L\xa5\x1c\xcf\x92\x09\x84b\xdc\xe6\x8fG\xfc\xb7\x0b\xff\xfc\x1d\xd3\x22\xc4Ib\xb9X*\x14\xe3Q\x12q\x8eD\x9a\x8c\xf32\xa5\x22\x89B\x92)\xc5%\xd2\xffd\xe2\xdf,\xfb\x03>\xdf5\x00\xb0j>\x01{\x91-\xa8]c\x03\xf6K'\x10Xt\xc0\xe2\xf7\x00\x00\xf2\xbbo\xc1\xd4(\x08\x03\x80h\x83\xe1\xcfw\xff\xef?\xfdG\xa0%\x00\x80fI\x92q\x00\x00^D$.T\xca\xb3?\xc7\x08\x00\x00D\xa0\x81*\xb0A\x1b\xf4\xc1\x18,\xc0\x06\x1c\xc1\x05\xdc\xc1\x0b\xfc`6\x84B$\xc4\xc2B\x10B\x0ad\x80\x1cr`)\xac\x82B(\x86\xcd\xb0\x1d*`/\xd4@\x1d4\xc0Qh\x86\x93p\x0e.\xc2U\xb8\x0e=p\x0f\xfaa\x08\x9e\xc1(\xbc\x81\x09\x04A\xc8\x08\x13a!\xda\x88\x01b\x8aX#\x8e\x08\x17\x99\x85\xf8!\xc1H\x04\x12\x8b$ \xc9\x88\x14Q\x22K\x915H1R\x8aT UH\x1d\xf2=r\x029\x87\x5cF\xba\x91;\xc8\x002\x82\xfc\x86\xbcG1\x94\x81\xb2Q=\xd4\x0c\xb5C\xb9\xa87\x1a\x84F\xa2\x0b\xd0dt1\x9a\x8f\x16\xa0\x9b\xd0r\xb4\x1a=\x8c6\xa1\xe7\xd0\xabh\x0f\xda\x8f>C\xc70\xc0\xe8\x18\x073\xc4l0.\xc6\xc3B\xb18,\x09\x93c\xcb\xb1\x22\xac\x0c\xab\xc6\x1a\xb0V\xac\x03\xbb\x89\xf5c\xcf\xb1w\x04\x12\x81E\xc0\x096\x04wB a\x1eAHXLXN\xd8H\xa8 \x1c$4\x11\xda\x097\x09\x03\x84Q\xc2'\x22\x93\xa8K\xb4&\xba\x11\xf9\xc4\x18b21\x87XH,#\xd6\x12\x8f\x13/\x10{\x88C\xc47$\x12\x89C2'\xb9\x90\x02I\xb1\xa4T\xd2\x12\xd2F\xd2nR#\xe9,\xa9\x9b4H\x1a#\x93\xc9\xdadk\xb2\x079\x94, +\xc8\x85\xe4\x9d\xe4\xc3\xe43\xe4\x1b\xe4!\xf2[\x0a\x9db@q\xa4\xf8S\xe2(R\xcajJ\x19\xe5\x10\xe54\xe5\x06e\x982AU\xa3\x9aR\xdd\xa8\xa1T\x115\x8fZB\xad\xa1\xb6R\xafQ\x87\xa8\x134u\x9a9\xcd\x83\x16IK\xa5\xad\xa2\x95\xd3\x1ah\x17h\xf7i\xaf\xe8t\xba\x11\xdd\x95\x1eN\x97\xd0W\xd2\xcb\xe9G\xe8\x97\xe8\x03\xf4w\x0c\x0d\x86\x15\x83\xc7\x88g(\x19\x9b\x18\x07\x18g\x19w\x18\xaf\x98L\xa6\x19\xd3\x8b\x19\xc7T071\xeb\x98\xe7\x99\x0f\x99oUX*\xb6*|\x15\x91\xca\x0a\x95J\x95&\x95\x1b*/T\xa9\xaa\xa6\xaa\xde\xaa\x0bU\xf3U\xcbT\x8f\xa9^S}\xaeFU3S\xe3\xa9\x09\xd4\x96\xabU\xaa\x9dP\xebS\x1bSg\xa9;\xa8\x87\xaag\xa8oT?\xa4~Y\xfd\x89\x06Y\xc3L\xc3OC\xa4Q\xa0\xb1_\xe3\xbc\xc6 \x0bc\x19\xb3x,!k\x0d\xab\x86u\x815\xc4&\xb1\xcd\xd9|v*\xbb\x98\xfd\x1d\xbb\x8b=\xaa\xa9\xa19C3J3W\xb3R\xf3\x94f?\x07\xe3\x98q\xf8\x9ctN\x09\xe7(\xa7\x97\xf3~\x8a\xde\x14\xef)\xe2)\x1b\xa64L\xb91e\x5ck\xaa\x96\x97\x96X\xabH\xabQ\xabG\xeb\xbd6\xae\xed\xa7\x9d\xa6\xbdE\xbbY\xfb\x81\x0eA\xc7J'\x5c'Gg\x8f\xce\x05\x9d\xe7S\xd9S\xdd\xa7\x0a\xa7\x16M=:\xf5\xae.\xaak\xa5\x1b\xa1\xbbDw\xbfn\xa7\xee\x98\x9e\xbe^\x80\x9eLo\xa7\xdey\xbd\xe7\xfa\x1c}/\xfdT\xfdm\xfa\xa7\xf5G\x0cX\x06\xb3\x0c$\x06\xdb\x0c\xce\x18<\xc55qo<\x1d/\xc7\xdb\xf1QC]\xc3@C\xa5a\x95a\x97\xe1\x84\x91\xb9\xd1<\xa3\xd5F\x8dF\x0f\x8ci\xc6\x5c\xe3$\xe3m\xc6m\xc6\xa3&\x06&!&KM\xeaM\xee\x9aRM\xb9\xa6)\xa6;L;L\xc7\xcd\xcc\xcd\xa2\xcd\xd6\x995\x9b=1\xd72\xe7\x9b\xe7\x9b\xd7\x9b\xdf\xb7`ZxZ,\xb6\xa8\xb6\xb8eI\xb2\xe4Z\xa6Y\xee\xb6\xbcn\x85Z9Y\xa5XUZ]\xb3F\xad\x9d\xad%\xd6\xbb\xad\xbb\xa7\x11\xa7\xb9N\x93N\xab\x9e\xd6g\xc3\xb0\xf1\xb6\xc9\xb6\xa9\xb7\x19\xb0\xe5\xd8\x06\xdb\xae\xb6m\xb6}agb\x17g\xb7\xc5\xae\xc3\xee\x93\xbd\x93}\xba}\x8d\xfd=\x07\x0d\x87\xd9\x0e\xab\x1dZ\x1d~s\xb4r\x14:V:\xde\x9a\xce\x9c\xee?}\xc5\xf4\x96\xe9/gX\xcf\x10\xcf\xd83\xe3\xb6\x13\xcb)\xc4i\x9dS\x9b\xd3Gg\x17g\xb9s\x83\xf3\x88\x8b\x89K\x82\xcb.\x97>.\x9b\x1b\xc6\xdd\xc8\xbd\xe4Jt\xf5q]\xe1z\xd2\xf5\x9d\x9b\xb3\x9b\xc2\xed\xa8\xdb\xaf\xee6\xeei\xee\x87\xdc\x9f\xcc4\x9f)\x9eY3s\xd0\xc3\xc8C\xe0Q\xe5\xd1?\x0b\x9f\x950k\xdf\xac~OCO\x81g\xb5\xe7#/c/\x91W\xad\xd7\xb0\xb7\xa5w\xaa\xf7a\xef\x17>\xf6>r\x9f\xe3>\xe3<7\xde2\xdeY_\xcc7\xc0\xb7\xc8\xb7\xcbO\xc3o\x9e_\x85\xdfC\x7f#\xffd\xffz\xff\xd1\x00\xa7\x80%\x01g\x03\x89\x81A\x81[\x02\xfb\xf8z|!\xbf\x8e?:\xdbe\xf6\xb2\xd9\xedA\x8c\xa0\xb9A\x15A\x8f\x82\xad\x82\xe5\xc1\xad!h\xc8\xec\x90\xad!\xf7\xe7\x98\xce\x91\xcei\x0e\x85P~\xe8\xd6\xd0\x07a\xe6a\x8b\xc3~\x0c'\x85\x87\x85W\x86?\x8ep\x88X\x1a\xd11\x975w\xd1\xdcCs\xdfD\xfaD\x96D\xde\x9bg1O9\xaf-J5*>\xaa.j<\xda7\xba4\xba?\xc6.fY\xcc\xd5X\x9dXIlK\x1c9.*\xae6nl\xbe\xdf\xfc\xed\xf3\x87\xe2\x9d\xe2\x0b\xe3{\x17\x98/\xc8]py\xa1\xce\xc2\xf4\x85\xa7\x16\xa9.\x12,:\x96@L\x88N8\x94\xf0A\x10*\xa8\x16\x8c%\xf2\x13w%\x8e\x0ay\xc2\x1d\xc2g\x22/\xd16\xd1\x88\xd8C\x5c*\x1eN\xf2H*Mz\x92\xec\x91\xbc5y$\xc53\xa5,\xe5\xb9\x84'\xa9\x90\xbcL\x0dL\xdd\x9b:\x9e\x16\x9av m2=:\xbd1\x83\x92\x91\x90qB\xaa!M\x93\xb6g\xeag\xe6fv\xcb\xace\x85\xb2\xfe\xc5n\x8b\xb7/\x1e\x95\x07\xc9k\xb3\x90\xac\x05Y-\x0a\xb6B\xa6\xe8TZ(\xd7*\x07\xb2geWf\xbf\xcd\x89\xca9\x96\xab\x9e+\xcd\xed\xcc\xb3\xca\xdb\x907\x9c\xef\x9f\xff\xed\x12\xc2\x12\xe1\x92\xb6\xa5\x86KW-\x1dX\xe6\xbd\xacj9\xb2<qy\xdb\x0a\xe3\x15\x05+\x86V\x06\xac<\xb8\x8a\xb6*m\xd5O\xab\xedW\x97\xae~\xbd&zMk\x81^\xc1\xca\x82\xc1\xb5\x01k\xeb\x0bU\x0a\xe5\x85}\xeb\xdc\xd7\xed]OX/Y\xdf\xb5a\xfa\x86\x9d\x1b>\x15\x89\x8a\xae\x14\xdb\x17\x97\x15\x7f\xd8(\xdcx\xe5\x1b\x87o\xca\xbf\x99\xdc\x94\xb4\xa9\xab\xc4\xb9d\xcff\xd2f\xe9\xe6\xde-\x9e[\x0e\x96\xaa\x97\xe6\x97\x0en\x0d\xd9\xda\xb4\x0d\xdfV\xb4\xed\xf5\xf6E\xdb/\x97\xcd(\xdb\xbb\x83\xb6C\xb9\xa3\xbf<\xb8\xbce\xa7\xc9\xce\xcd;?T\xa4T\xf4T\xfaT6\xee\xd2\xdd\xb5a\xd7\xf8n\xd1\xee\x1b{\xbc\xf64\xec\xd5\xdb[\xbc\xf7\xfd>\xc9\xbe\xdbU\x01UM\xd5f\xd5e\xfbI\xfb\xb3\xf7?\xae\x89\xaa\xe9\xf8\x96\xfbm]\xadNmq\xed\xc7\x03\xd2\x03\xfd\x07#\x0e\xb6\xd7\xb9\xd4\xd5\x1d\xd2=TR\x8f\xd6+\xebG\x0e\xc7\x1f\xbe\xfe\x9d\xefw-\x0d6\x0dU\x8d\x9c\xc6\xe2#pDy\xe4\xe9\xf7\x09\xdf\xf7\x1e\x0d:\xdav\x8c{\xac\xe1\x07\xd3\x1fv\x1dg\x1d/jB\x9a\xf2\x9aF\x9bS\x9a\xfb[b[\xbaO\xcc>\xd1\xd6\xea\xdez\xfcG\xdb\x1f\x0f\x9c4<YyJ\xf3T\xc9i\xda\xe9\x82\xd3\x93g\xf2\xcf\x8c\x9d\x95\x9d}~.\xf9\xdc`\xdb\xa2\xb6{\xe7c\xce\xdfj\x0fo\xef\xba\x10t\xe1\xd2E\xff\x8b\xe7;\xbc;\xce\x5c\xf2\xb8t\xf2\xb2\xdb\xe5\x13W\xb8W\x9a\xaf:_m\xeat\xea<\xfe\x93\xd3O\xc7\xbb\x9c\xbb\x9a\xae\xb9\x5ck\xb9\xeez\xbd\xb5{f\xf7\xe9\x1b\x9e7\xce\xdd\xf4\xbdy\xf1\x16\xff\xd6\xd5\x9e9=\xdd\xbd\xf3zo\xf7\xc5\xf7\xf5\xdf\x16\xdd~r'\xfd\xce\xcb\xbb\xd9w'\xee\xad\xbcO\xbc_\xf4@\xedA\xd9C\xdd\x87\xd5?[\xfe\xdc\xd8\xef\xdc\x7fj\xc0w\xa0\xf3\xd1\xdcG\xf7\x06\x85\x83\xcf\xfe\x91\xf5\x8f\x0fC\x05\x8f\x99\x8f\xcb\x86\x0d\x86\xeb\x9e8>99\xe2?r\xfd\xe9\xfc\xa7C\xcfd\xcf&\x9e\x17\xfe\xa2\xfe\xcb\xae\x17\x16/~\xf8\xd5\xeb\xd7\xce\xd1\x98\xd1\xa1\x97\xf2\x97\x93\xbfm|\xa5\xfd\xea\xc0\xeb\x19\xaf\xdb\xc6\xc2\xc6\x1e\xbe\xc9x31^\xf4V\xfb\xed\xc1w\xdcw\x1d\xef\xa3\xdf\x0fO\xe4| \x7f(\xffh\xf9\xb1\xf5S\xd0\xa7\xfb\x93\x19\x93\x93\xff\x04\x03\x98\xf3\xfcc3-\xdb\x00\x00\x00 cHRM\x00\x00z%\x00\x00\x80\x83\x00\x00\xf9\xff\x00\x00\x80\xe9\x00\x00u0\x00\x00\xea`\x00\x00:\x98\x00\x00\x17o\x92_\xc5F\x00\x00\x00yIDATx\xda\xec\x971\x0a\xc00\x0c\x03%\x93_\xf5\xfd}\x97\xb3\xb4\x10h\x07gPR\xa8<y1>$\x07\x14f&vV`s\xb5\xbb9I\x00X%\x07\x8fK\xf9Q\x81\x95^\xe4C\x817J\xd5\xd2\xca\x0dP!{\x15\x80J\xef?\xf7\x0a\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\x10\xd5\xf4\xaaJ\xc61\x13\xa1\x15\xb1\xbc\xcd\x0e(-\xe0\x22\xdb9\xee\xe2\xef\x7f\xc7\x1d\x00\x00\xff\xff\x03\x00>H\x12?\xd7\xafML\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xef\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00Q\x00\x00\x00:\x08\x06\x00\x00\x00\xc8\xbc\xb5\xaf\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b*2\xff\x7f Z\x00\x00\x00oIDATx\xda\xed\xd0\xb1\x0d\x000\x08\x03A\xc8\xa0\x0c\xc7\xa2I\xcf\x04(\xba/]Y\x97\xb1\xb4\xee\xbes\xab\xaa\xdc\xf8\xf5\x84 B\x84(\x88\x10!B\x14D\x88\x10!\x0a\x22D\x88\x10\x05\x11\x22D\x88\x82\x08\x11\x22DA\x84\x08Q\x10!B\x84(\x88\x10!B\x14D\x88\x10!\x0a\x22D\x88\x10\x05\x11\x22D\x88\x82\x08\x11\x22DA\x84\x08Q\x10!B\xfc\xaa\x07\x12U\x04tV\x9e\x9eT\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02V\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq\xde\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdf\x04\x19\x10\x14-\x80z\x92\xdf\x00\x00\x00\x1diTXtComment\x00\x00\x00\x00\x00Created with GIMPd.e\x07\x00\x00\x01\xbaIDATx\xda\xed\x9b[\x92\x02!\x0cEM\x16\xa6\x1b\xd0\xd5\x8e\x1b\xd0\x8d\xe9\x9fe9\xda<\x92{\x13h\xf2=\x95\xe6\x1c\x1eC\x10\x0e\x87\x15+V\xec9\x84\xf9\xb1\xbf\xe3\xf1Q\xf3w\x97\xfb]\xa6\x10P\x0b\x1c)D\xb2B\xb3d\xc8(\xe0(\x112\x22\xbc\xa7\x04\x19\x11\xdcS\x84\x8c\x0eo\x95 \xa3\x83[E\xc8L\xf0=\x12d6\xf8V\x09\xba\xb6\xc2\x13\xf6~\xcb(\x10+\xfc\xf9v{\xe5\xb8\x9eN\x14Q\xef\xdf,}\xb7$A\xbd\x1b\xf6\xd984\xbc5\x141\xf4Q\x12z\xf2\x96\x18\x145\xef\xbd%X\xf2m\xb1\x98\xa7\xc0\xd6\xfc\xf3\x92\xb0\x95\xc7\xba\xee\x88W\xef\xa3\x1a\xe9\x99\xf7\xdb\x82\xe8\xb6\x08\x22F\x02\xb2\xe7!\xff\x05<%0\xe0\xbfN\x01\x8fM\x8f\xb5\xf1H\xf8\xcfi\x00\xd9\x0a[F\x02\xab\xe7\xe1\xb5@\x8f\x046<\xbc\x18j\x91\x10\x01\xffo\x0d@\x15=%86\xfc\xfb:@)\x87{\xd7\x04FqE;\x0fh\x85aU\x96\xd4\x03\x91Z(\x16<]@\x0d\x1c\x13>D\x80e\x1f0\xbc\x80Z8\xa6\x04\xcd\x06\xcf\x96\xa0\xd1\xf0\x8c\xf3\x84P\x015\xf0\x91\x12 \xd5`o\xcf36E\x94j\xb0\x17&b$h\xa69\x1f!A3\xc1GHp;\x14E\xcca\xef|\xd0CQ\xc4\x02\xc6\x18\x09\x9a\x15\x9e%\xe1g\x82\xdai\xc0\xaa\xe7\xad\xdf\xf9\xf5#i\xc8\x99`\x86|E\x01\x96\x9bW\xa8\xc6\xf6\xe6\xddb\xd1\xec=\x8f\xceo\xbe \x91=J#y]\x91\xa9M\xb6n\x89M\x1a\xeb\xa2dk\xf2]_\x95\xcd,\x82vY:\xa3\x84\x90\xeb\xf2Y$X\x1fM\xac'3\xde\x0d\xdb\xed\xa3)\xa4\x8c\xa1\x9e\xcdy\x08a>\x9c\x5c\xb1\xf7x\x02Q\xa0Z\x91w\xd2\x02#\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0b\x95\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x0aOiCCPPhotoshop ICC profile\x00\x00x\xda\x9dSgTS\xe9\x16=\xf7\xde\xf4BK\x88\x80\x94KoR\x15\x08 RB\x8b\x80\x14\x91&*!\x09\x10J\x88!\xa1\xd9\x15Q\xc1\x11EE\x04\x1b\xc8\xa0\x88\x03\x8e\x8e\x80\x8c\x15Q,\x0c\x8a\x0a\xd8\x07\xe4!\xa2\x8e\x83\xa3\x88\x8a\xca\xfb\xe1{\xa3k\xd6\xbc\xf7\xe6\xcd\xfe\xb5\xd7>\xe7\xac\xf3\x9d\xb3\xcf\x07\xc0\x08\x0c\x96H3Q5\x80\x0c\xa9B\x1e\x11\xe0\x83\xc7\xc4\xc6\xe1\xe4.@\x81\x0a$p\x00\x10\x08\xb3d!s\xfd#\x01\x00\xf8~<<+\x22\xc0\x07\xbe\x00\x01x\xd3\x0b\x08\x00\xc0M\x9b\xc00\x1c\x87\xff\x0f\xeaB\x99\x5c\x01\x80\x84\x01\xc0t\x918K\x08\x80\x14\x00@z\x8eB\xa6\x00@F\x01\x80\x9d\x98&S\x00\xa0\x04\x00`\xcbcb\xe3\x00P-\x00`'\x7f\xe6\xd3\x00\x80\x9d\xf8\x99{\x01\x00[\x94!\x15\x01\xa0\x91\x00 \x13e\x88D\x00h;\x00\xac\xcfV\x8aE\x00X0\x00\x14fK\xc49\x00\xd8-\x000IWfH\x00\xb0\xb7\x00\xc0\xce\x10\x0b\xb2\x00\x08\x0c\x000Q\x88\x85)\x00\x04{\x00`\xc8##x\x00\x84\x99\x00\x14F\xf2W<\xf1+\xae\x10\xe7*\x00\x00x\x99\xb2<\xb9$9E\x81[\x08-q\x07WW.\x1e(\xceI\x17+\x146a\x02a\x9a@.\xc2y\x99\x192\x814\x0f\xe0\xf3\xcc\x00\x00\xa0\x91\x15\x11\xe0\x83\xf3\xfdx\xce\x0e\xae\xce\xce6\x8e\xb6\x0e_-\xea\xbf\x06\xff\x22bb\xe3\xfe\xe5\xcf\xabp@\x00\x00\xe1t~\xd1\xfe,/\xb3\x1a\x80;\x06\x80m\xfe\xa2%\xee\x04h^\x0b\xa0u\xf7\x8bf\xb2\x0f@\xb5\x00\xa0\xe9\xdaW\xf3p\xf8~<<E\xa1\x90\xb9\xd9\xd9\xe5\xe4\xe4\xd8J\xc4B[a\xcaW}\xfeg\xc2_\xc0W\xfdl\xf9~<\xfc\xf7\xf5\xe0\xbe\xe2$\x812]\x81G\x04\xf8\xe0\xc2\xcc\xf4L\xa5\x1c\xcf\x92\x09\x84b\xdc\xe6\x8fG\xfc\xb7\x0b\xff\xfc\x1d\xd3\x22\xc4Ib\xb9X*\x14\xe3Q\x12q\x8eD\x9a\x8c\xf32\xa5\x22\x89B\x92)\xc5%\xd2\xffd\xe2\xdf,\xfb\x03>\xdf5\x00\xb0j>\x01{\x91-\xa8]c\x03\xf6K'\x10Xt\xc0\xe2\xf7\x00\x00\xf2\xbbo\xc1\xd4(\x08\x03\x80h\x83\xe1\xcfw\xff\xef?\xfdG\xa0%\x00\x80fI\x92q\x00\x00^D$.T\xca\xb3?\xc7\x08\x00\x00D\xa0\x81*\xb0A\x1b\xf4\xc1\x18,\xc0\x06\x1c\xc1\x05\xdc\xc1\x0b\xfc`6\x84B$\xc4\xc2B\x10B\x0ad\x80\x1cr`)\xac\x82B(\x86\xcd\xb0\x1d*`/\xd4@\x1d4\xc0Qh\x86\x93p\x0e.\xc2U\xb8\x0e=p\x0f\xfaa\x08\x9e\xc1(\xbc\x81\x09\x04A\xc8\x08\x13a!\xda\x88\x01b\x8aX#\x8e\x08\x17\x99\x85\xf8!\xc1H\x04\x12\x8b$ \xc9\x88\x14Q\x22K\x915H1R\x8aT UH\x1d\xf2=r\x029\x87\x5cF\xba\x91;\xc8\x002\x82\xfc\x86\xbcG1\x94\x81\xb2Q=\xd4\x0c\xb5C\xb9\xa87\x1a\x84F\xa2\x0b\xd0dt1\x9a\x8f\x16\xa0\x9b\xd0r\xb4\x1a=\x8c6\xa1\xe7\xd0\xabh\x0f\xda\x8f>C\xc70\xc0\xe8\x18\x073\xc4l0.\xc6\xc3B\xb18,\x09\x93c\xcb\xb1\x22\xac\x0c\xab\xc6\x1a\xb0V\xac\x03\xbb\x89\xf5c\xcf\xb1w\x04\x12\x81E\xc0\x096\x04wB a\x1eAHXLXN\xd8H\xa8 \x1c$4\x11\xda\x097\x09\x03\x84Q\xc2'\x22\x93\xa8K\xb4&\xba\x11\xf9\xc4\x18b21\x87XH,#\xd6\x12\x8f\x13/\x10{\x88C\xc47$\x12\x89C2'\xb9\x90\x02I\xb1\xa4T\xd2\x12\xd2F\xd2nR#\xe9,\xa9\x9b4H\x1a#\x93\xc9\xdadk\xb2\x079\x94, +\xc8\x85\xe4\x9d\xe4\xc3\xe43\xe4\x1b\xe4!\xf2[\x0a\x9db@q\xa4\xf8S\xe2(R\xcajJ\x19\xe5\x10\xe54\xe5\x06e\x982AU\xa3\x9aR\xdd\xa8\xa1T\x115\x8fZB\xad\xa1\xb6R\xafQ\x87\xa8\x134u\x9a9\xcd\x83\x16IK\xa5\xad\xa2\x95\xd3\x1ah\x17h\xf7i\xaf\xe8t\xba\x11\xdd\x95\x1eN\x97\xd0W\xd2\xcb\xe9G\xe8\x97\xe8\x03\xf4w\x0c\x0d\x86\x15\x83\xc7\x88g(\x19\x9b\x18\x07\x18g\x19w\x18\xaf\x98L\xa6\x19\xd3\x8b\x19\xc7T071\xeb\x98\xe7\x99\x0f\x99oUX*\xb6*|\x15\x91\xca\x0a\x95J\x95&\x95\x1b*/T\xa9\xaa\xa6\xaa\xde\xaa\x0bU\xf3U\xcbT\x8f\xa9^S}\xaeFU3S\xe3\xa9\x09\xd4\x96\xabU\xaa\x9dP\xebS\x1bSg\xa9;\xa8\x87\xaag\xa8oT?\xa4~Y\xfd\x89\x06Y\xc3L\xc3OC\xa4Q\xa0\xb1_\xe3\xbc\xc6 \x0bc\x19\xb3x,!k\x0d\xab\x86u\x815\xc4&\xb1\xcd\xd9|v*\xbb\x98\xfd\x1d\xbb\x8b=\xaa\xa9\xa19C3J3W\xb3R\xf3\x94f?\x07\xe3\x98q\xf8\x9ctN\x09\xe7(\xa7\x97\xf3~\x8a\xde\x14\xef)\xe2)\x1b\xa64L\xb91e\x5ck\xaa\x96\x97\x96X\xabH\xabQ\xabG\xeb\xbd6\xae\xed\xa7\x9d\xa6\xbdE\xbbY\xfb\x81\x0eA\xc7J'\x5c'Gg\x8f\xce\x05\x9d\xe7S\xd9S\xdd\xa7\x0a\xa7\x16M=:\xf5\xae.\xaak\xa5\x1b\xa1\xbbDw\xbfn\xa7\xee\x98\x9e\xbe^\x80\x9eLo\xa7\xdey\xbd\xe7\xfa\x1c}/\xfdT\xfdm\xfa\xa7\xf5G\x0cX\x06\xb3\x0c$\x06\xdb\x0c\xce\x18<\xc55qo<\x1d/\xc7\xdb\xf1QC]\xc3@C\xa5a\x95a\x97\xe1\x84\x91\xb9\xd1<\xa3\xd5F\x8dF\x0f\x8ci\xc6\x5c\xe3$\xe3m\xc6m\xc6\xa3&\x06&!&KM\xeaM\xee\x9aRM\xb9\xa6)\xa6;L;L\xc7\xcd\xcc\xcd\xa2\xcd\xd6\x995\x9b=1\xd72\xe7\x9b\xe7\x9b\xd7\x9b\xdf\xb7`ZxZ,\xb6\xa8\xb6\xb8eI\xb2\xe4Z\xa6Y\xee\xb6\xbcn\x85Z9Y\xa5XUZ]\xb3F\xad\x9d\xad%\xd6\xbb\xad\xbb\xa7\x11\xa7\xb9N\x93N\xab\x9e\xd6g\xc3\xb0\xf1\xb6\xc9\xb6\xa9\xb7\x19\xb0\xe5\xd8\x06\xdb\xae\xb6m\xb6}agb\x17g\xb7\xc5\xae\xc3\xee\x93\xbd\x93}\xba}\x8d\xfd=\x07\x0d\x87\xd9\x0e\xab\x1dZ\x1d~s\xb4r\x14:V:\xde\x9a\xce\x9c\xee?}\xc5\xf4\x96\xe9/gX\xcf\x10\xcf\xd83\xe3\xb6\x13\xcb)\xc4i\x9dS\x9b\xd3Gg\x17g\xb9s\x83\xf3\x88\x8b\x89K\x82\xcb.\x97>.\x9b\x1b\xc6\xdd\xc8\xbd\xe4Jt\xf5q]\xe1z\xd2\xf5\x9d\x9b\xb3\x9b\xc2\xed\xa8\xdb\xaf\xee6\xeei\xee\x87\xdc\x9f\xcc4\x9f)\x9eY3s\xd0\xc3\xc8C\xe0Q\xe5\xd1?\x0b\x9f\x950k\xdf\xac~OCO\x81g\xb5\xe7#/c/\x91W\xad\xd7\xb0\xb7\xa5w\xaa\xf7a\xef\x17>\xf6>r\x9f\xe3>\xe3<7\xde2\xdeY_\xcc7\xc0\xb7\xc8\xb7\xcbO\xc3o\x9e_\x85\xdfC\x7f#\xffd\xffz\xff\xd1\x00\xa7\x80%\x01g\x03\x89\x81A\x81[\x02\xfb\xf8z|!\xbf\x8e?:\xdbe\xf6\xb2\xd9\xedA\x8c\xa0\xb9A\x15A\x8f\x82\xad\x82\xe5\xc1\xad!h\xc8\xec\x90\xad!\xf7\xe7\x98\xce\x91\xcei\x0e\x85P~\xe8\xd6\xd0\x07a\xe6a\x8b\xc3~\x0c'\x85\x87\x85W\x86?\x8ep\x88X\x1a\xd11\x975w\xd1\xdcCs\xdfD\xfaD\x96D\xde\x9bg1O9\xaf-J5*>\xaa.j<\xda7\xba4\xba?\xc6.fY\xcc\xd5X\x9dXIlK\x1c9.*\xae6nl\xbe\xdf\xfc\xed\xf3\x87\xe2\x9d\xe2\x0b\xe3{\x17\x98/\xc8]py\xa1\xce\xc2\xf4\x85\xa7\x16\xa9.\x12,:\x96@L\x88N8\x94\xf0A\x10*\xa8\x16\x8c%\xf2\x13w%\x8e\x0ay\xc2\x1d\xc2g\x22/\xd16\xd1\x88\xd8C\x5c*\x1eN\xf2H*Mz\x92\xec\x91\xbc5y$\xc53\xa5,\xe5\xb9\x84'\xa9\x90\xbcL\x0dL\xdd\x9b:\x9e\x16\x9av m2=:\xbd1\x83\x92\x91\x90qB\xaa!M\x93\xb6g\xeag\xe6fv\xcb\xace\x85\xb2\xfe\xc5n\x8b\xb7/\x1e\x95\x07\xc9k\xb3\x90\xac\x05Y-\x0a\xb6B\xa6\xe8TZ(\xd7*\x07\xb2geWf\xbf\xcd\x89\xca9\x96\xab\x9e+\xcd\xed\xcc\xb3\xca\xdb\x907\x9c\xef\x9f\xff\xed\x12\xc2\x12\xe1\x92\xb6\xa5\x86KW-\x1dX\xe6\xbd\xacj9\xb2<qy\xdb\x0a\xe3\x15\x05+\x86V\x06\xac<\xb8\x8a\xb6*m\xd5O\xab\xedW\x97\xae~\xbd&zMk\x81^\xc1\xca\x82\xc1\xb5\x01k\xeb\x0bU\x0a\xe5\x85}\xeb\xdc\xd7\xed]OX/Y\xdf\xb5a\xfa\x86\x9d\x1b>\x15\x89\x8a\xae\x14\xdb\x17\x97\x15\x7f\xd8(\xdcx\xe5\x1b\x87o\xca\xbf\x99\xdc\x94\xb4\xa9\xab\xc4\xb9d\xcff\xd2f\xe9\xe6\xde-\x9e[\x0e\x96\xaa\x97\xe6\x97\x0en\x0d\xd9\xda\xb4\x0d\xdfV\xb4\xed\xf5\xf6E\xdb/\x97\xcd(\xdb\xbb\x83\xb6C\xb9\xa3\xbf<\xb8\xbce\xa7\xc9\xce\xcd;?T\xa4T\xf4T\xfaT6\xee\xd2\xdd\xb5a\xd7\xf8n\xd1\xee\x1b{\xbc\xf64\xec\xd5\xdb[\xbc\xf7\xfd>\xc9\xbe\xdbU\x01UM\xd5f\xd5e\xfbI\xfb\xb3\xf7?\xae\x89\xaa\xe9\xf8\x96\xfbm]\xadNmq\xed\xc7\x03\xd2\x03\xfd\x07#\x0e\xb6\xd7\xb9\xd4\xd5\x1d\xd2=TR\x8f\xd6+\xebG\x0e\xc7\x1f\xbe\xfe\x9d\xefw-\x0d6\x0dU\x8d\x9c\xc6\xe2#pDy\xe4\xe9\xf7\x09\xdf\xf7\x1e\x0d:\xdav\x8c{\xac\xe1\x07\xd3\x1fv\x1dg\x1d/jB\x9a\xf2\x9aF\x9bS\x9a\xfb[b[\xbaO\xcc>\xd1\xd6\xea\xdez\xfcG\xdb\x1f\x0f\x9c4<YyJ\xf3T\xc9i\xda\xe9\x82\xd3\x93g\xf2\xcf\x8c\x9d\x95\x9d}~.\xf9\xdc`\xdb\xa2\xb6{\xe7c\xce\xdfj\x0fo\xef\xba\x10t\xe1\xd2E\xff\x8b\xe7;\xbc;\xce\x5c\xf2\xb8t\xf2\xb2\xdb\xe5\x13W\xb8W\x9a\xaf:_m\xeat\xea<\xfe\x93\xd3O\xc7\xbb\x9c\xbb\x9a\xae\xb9\x5ck\xb9\xeez\xbd\xb5{f\xf7\xe9\x1b\x9e7\xce\xdd\xf4\xbdy\xf1\x16\xff\xd6\xd5\x9e9=\xdd\xbd\xf3zo\xf7\xc5\xf7\xf5\xdf\x16\xdd~r'\xfd\xce\xcb\xbb\xd9w'\xee\xad\xbcO\xbc_\xf4@\xedA\xd9C\xdd\x87\xd5?[\xfe\xdc\xd8\xef\xdc\x7fj\xc0w\xa0\xf3\xd1\xdcG\xf7\x06\x85\x83\xcf\xfe\x91\xf5\x8f\x0fC\x05\x8f\x99\x8f\xcb\x86\x0d\x86\xeb\x9e8>99\xe2?r\xfd\xe9\xfc\xa7C\xcfd\xcf&\x9e\x17\xfe\xa2\xfe\xcb\xae\x17\x16/~\xf8\xd5\xeb\xd7\xce\xd1\x98\xd1\xa1\x97\xf2\x97\x93\xbfm|\xa5\xfd\xea\xc0\xeb\x19\xaf\xdb\xc6\xc2\xc6\x1e\xbe\xc9x31^\xf4V\xfb\xed\xc1w\xdcw\x1d\xef\xa3\xdf\x0fO\xe4| \x7f(\xffh\xf9\xb1\xf5S\xd0\xa7\xfb\x93\x19\x93\x93\xff\x04\x03\x98\xf3\xfcc3-\xdb\x00\x00\x00 cHRM\x00\x00z%\x00\x00\x80\x83\x00\x00\xf9\xff\x00\x00\x80\xe9\x00\x00u0\x00\x00\xea`\x00\x00:\x98\x00\x00\x17o\x92_\xc5F\x00\x00\x00\xc0IDATx\xda\xdcVA\x0e\x830\x0c\xab\xa3\xfe\xff=\xfb\x9dw\x021ThJ\xe2 -'\xd4\x03vl'-\xda\xa7\x1d\x8b\xad\xa6\xb0}\xf4b\xe0s\xa3\xb0\x17\xc0\x7f\x88\xf4\x99D\xa2\xce\xf7\xb2B\xf0\xe1\xbfM\x0c\xceA\xd7\x98)\xa0\x90\xfb2gV\xe5\xf5\x85\x1aR\x05\x5ceE\xdd\xbbCX\x0a\xfew\x16,w\x9fI\xe0\x11x\x16\x01*-`\x10\x00\x11\x02\x9eM\xc6\x08\xf8\x1d\x01:\xce\xa8\x9a\x02&\xf8\x8d\x08\x01\x04s\x81\x8c\x10*\xdf\x04\xee\x10B\x91\xfa\xd51\x84\x12\xdc\xbb\x88\xf0\x96\x05+$\xa0&p\x07\x82\x0a\x05dv\xf4\xc1\x8cCL\x82\x91M\x98~sv\xc5\x15\xbb\x9a\x81\xb2\xad7\xb2\xd3\xaaW\xef9K\xdf\x01\x00h\x95#\xfe/d\x9d\xea\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xa6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1d\x00\xb0\xd55\xa3\x00\x00\x00*IDAT\x08\xd7c`\xc0\x06\xfe\x9fg``B0\xa1\x1c\x08\x93\x81\x81\x09\xc1d``b``4D\xe2 s\x19\x90\x8d@\x02\x00d@\x09u\x86\xb3\xad\x9c\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\x96\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\x00\x00\x00\x02bKGD\x00\xd3\xb5W\xa0\x5c\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x0b\x07\x0c\x0d\x1bu\xfe1\x99\x00\x00\x00'IDAT\x08\xd7e\x8c\xb1\x0d\x00\x00\x08\x83\xe0\xff\xa3up\xb1\xca\xd4\x90Px\x08U!\x14\xb6Tp\xe6H\x8d\x87\xcc\x0f\x0d\xe0\xf0\x08\x024\xe2+\xa7\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xa0\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1c\x1f$\xc6\x09\x17\x00\x00\x00$IDAT\x08\xd7c`@\x05\xff\xcf\xc3XL\xc8\x5c&dY&d\xc5p\x0e\xa3!\x9c\xc3h\x88a\x1a\x0a\x00\x00m\x84\x09u7\x9e\xd9#\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xa5\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xbb\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00?\x00\x00\x00\x07\x08\x06\x00\x00\x00\xbfv\x95\x1f\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x095+U\xcaRj\x00\x00\x00;IDAT8\xcbc`\x18\x05#\x130\x12\xa3\xa8\xbe}*%v\xfc\xa7\x97;\xd1\xc1\xaa\xa5s\x18\xae_9\x8fS\x9ei4\xe6\x09\x00M\x1d\xc3!\x19\xf3\x0c\x0c\x0cxc~\x14\x8cT\x00\x00id\x0b\x05\xfdkX\xca\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xe4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x006\x00\x00\x00\x0a\x08\x06\x00\x00\x00\xff\xfd\xad\x0b\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x06bKGD\x00\x7f\x00\x87\x00\x95\xe6\xde\xa6\xaf\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x09*+\x98\x90\x5c\xf4\x00\x00\x00dIDATH\xc7c\xfc\xcf0<\x01\x0b\xa5\x064\xb4O\x85\x87\xcd\xaa\xa5s\x18\xae]9\xcfH+5\x14y\xcc\xd8\xc8\x88$\x03|\x89\xd0O-5\x84\xc0\xd9s\xe7\xe0l&\x86\x91\x92\x14\x91}MTR\x0cM&\xa8\x9fZjF\x93\xe2hR\x1c\x82I\x91\x91\xd2zLK\xc7\x10\xc5\x08l\xc54\xb5\xd4\xd0\xd5c\x83\x15\x00\x00z0J\x09q\xea-n\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xe0\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00Q\x00\x00\x00:\x08\x06\x00\x00\x00\xc8\xbc\xb5\xaf\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b)\x1c\x08\x84~V\x00\x00\x00`IDATx\xda\xed\xd9\xb1\x0d\x00 \x08\x00AqP\x86cQ\xed\x8d\x85%\x89w\xa5\x15\xf9HE\x8c\xa6\xaaj\x9do\x99\x19\x1dg\x9d\x03\x11E\x14\x11\x11E\x14QDD\x14QD\x11\x11QD\x11EDD\x11E\x14\x11\x11E\x14\xf1[\xd1u\xb0\xdb\xdd\xd9O\xb4\xce\x88(\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcf6\xcei\x07\x1e\xe99U@\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xd4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x02QIDATX\x85\xed\x96AKTQ\x14\xc7\x7f\xe7\x8d\xb8\xd0&0wi\x84\xe1\xaa)\x90A\xc7\x92^\xa0\x1b\xa1\x8d\x0a\xf5\x19Z;3\xda\xd8j\x16A6\x83\xf3\xbe\x87A\x8d\xad\xc2M\xf6\x14\xf4\x0d\x99H\x0e\x11\xe2\xaa\x11\xdb\x184\xa8\x0b\xc3wZ\xccH\x10\xf3t\xee\xe8\xae\xf9o\xef9\xfc\x7f\xf7\xdc{\xcf=\xd0TS\xff\xbb\xc4$8\x92.\xb6v\x86\x0f'T\x18\x07\x8d\x02]\xd5\xa5\x12\xcag\x11\xc9\xef\x97\xdb\xf3\xc5t\xe4\xf8\xd2\x01lg\xed1*\x19\xa0\x07\xe4\x0b\xaaKX\x94\x00D\xb5K\xb1\x86A\xef\x22\xec\x082\xedN\xc6\xde\x5c\x0a\xc0\x93\xf9\xf9\xd0\x8f\xdd\x9b\x19\x948\xf0^\x95\xd4Jbp\xb3V\xec\x90S\xe8\x0b\xf9:\x8b0\x0ad\x97\xcb\xb1\x14i\xf1\xeb\xdddM\xd9\x8e7g\xe7\xbc\x93\x87\xceZ\xb2\xee\x9c\x9c7e\xe7\xbc\x13;\xe7e\xce\x8b=\xb3\x02\xd5\xb2\xbf\x16$\xe9\xc6cs\xf5\x02Tr\xbdi\x94W\x08\x13\xcb\x93\x83yc\x80H\xba\xd8z\xed\xea\xc1WA\xbf\xb9\xf1{\x8fL\xccO\xf5\xc0),\x8aj\xcf\xcf\xf2\x95H\xd0\xc5\xb4\x82\x92;\xc3\x87\x13\xc0-_e\xa6\x11s\x00\xcb\x97g@oG\xf8`,0&h\xa1\xf2\xd4\xd8\x0c\xbap\xf5\xc8M\x0cl\xa8\xb2%`\x0e\x00\x1a\x15\xf4c\xa3\xe6\xa7\x12\xf8\x80\xd0\xdf\x00\x00\xd7\x15)]\x14@a\x97\xbf\x0d\xcb\x08\x00\xc4\xacS\xd64\x10\x11 \xb0\x17\x9c\x05\xb0\x87O\xf7E\x01\x14\xed\x02\xf6\xcc\x01\x94O\x0a\xc3\x17\x05\x00F\x80\x821\x80\x88\xe4E\xb83\xe4\x14\xfa\x1au\xb6\x9d\xd5(p\x1b\xd1w\xc6\x00\xfb\xe5\xf6<\xc2N\xc8\xd7\xd9\x86\xdcU\x05\xb52\xc0\xf6Q[\xcb\x821@1\x1d9Ve\x0aa\xd4\xceyS\xa6\xfev\xceK\x01#\xa2~r\xfdi\xffoc\x00\x80\x95\xf8\xe0[ \x0b\xcc\xd6\x0d\xa1*\xf6\xdc\xda\x0c\x22/D\xc8\xb8\x89\xfb\x81\xe5\x87z\xe6\x81\xb4Zv\xb8\xf0\x12a\x1aX\x14\xb5Rnb`\xa3V\xa8\xed\xacF\xabe\x1f\x11!\xe3\xfe\x8a=?\xef;6\x18H\xbcq\x94,\xd0\xab\xca\x96\x08K\x08\xdf\x01PnPy1\x11`[\xd4O\x9e\xb7sc\x00\xa8\xfc\x90\x1d\xe1\x831\xaa#\x99 \xdd\x15\x7f-\x89\xca:\x96\xe6\x8f\xdaZ\x16\xce:\xf3\xa6\x9aj\xea_\xfd\x01\xd3\x1c\xd9\x7f^\xb93\xcd\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\x93\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\x00\x00\x00\x02bKGD\x00\xd3\xb5W\xa0\x5c\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x0b\x07\x0c\x0c+J<0t\x00\x00\x00$IDAT\x08\xd7c`@\x05\xff\xff\xc3XL\xc8\x5c&dY&d\xc5p\x0e##\x9c\xc3\xc8\x88a\x1a\x0a\x00\x00\x9e\x14\x0a\x05+\xca\xe5u\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xa6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x1b\x0e\x16M[o\x00\x00\x00*IDAT\x08\xd7c`\xc0\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\x81\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x01\x03\x00\x00\x00%=m\x22\x00\x00\x00\x06PLTE\x00\x00\x00\xae\xae\xaewk\xd6-\x00\x00\x00\x01tRNS\x00@\xe6\xd8f\x00\x00\x00)IDATx^\x05\xc0\xb1\x0d\x00 \x08\x04\xc0\xc3X\xd8\xfe\x0a\xcc\xc2p\x8cm(\x0e\x97Gh\x86Uq\xda\x1do%\xba\xcd\xd8\xfd5\x0a\x04\x1b\xd6\xd9\x1a\x92\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xdc\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x10\x00\x00\x00@\x08\x06\x00\x00\x00\x13}\xf7\x96\x00\x00\x00\x06bKGD\x00\xb3\x00y\x00y\xdc\xddS\xfc\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdf\x04\x19\x10-\x19\xafJ\xeb\xd0\x00\x00\x00\x1diTXtComment\x00\x00\x00\x00\x00Created with GIMPd.e\x07\x00\x00\x00@IDATX\xc3\xed\xce1\x0a\x00 \x0c\x03@\xf5\xa3}[_\xaaS\xc1\xc9\xc5E\xe42\x05\x1a\x8e\xb6v\x99^%\x22f\xf5\xcc\xec\xfb\xe8t\x1b\xb7\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf06\xf0A\x16\x0bB\x08x\x15WD\xa2\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0bN\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x0aOiCCPPhotoshop ICC profile\x00\x00x\xda\x9dSgTS\xe9\x16=\xf7\xde\xf4BK\x88\x80\x94KoR\x15\x08 RB\x8b\x80\x14\x91&*!\x09\x10J\x88!\xa1\xd9\x15Q\xc1\x11EE\x04\x1b\xc8\xa0\x88\x03\x8e\x8e\x80\x8c\x15Q,\x0c\x8a\x0a\xd8\x07\xe4!\xa2\x8e\x83\xa3\x88\x8a\xca\xfb\xe1{\xa3k\xd6\xbc\xf7\xe6\xcd\xfe\xb5\xd7>\xe7\xac\xf3\x9d\xb3\xcf\x07\xc0\x08\x0c\x96H3Q5\x80\x0c\xa9B\x1e\x11\xe0\x83\xc7\xc4\xc6\xe1\xe4.@\x81\x0a$p\x00\x10\x08\xb3d!s\xfd#\x01\x00\xf8~<<+\x22\xc0\x07\xbe\x00\x01x\xd3\x0b\x08\x00\xc0M\x9b\xc00\x1c\x87\xff\x0f\xeaB\x99\x5c\x01\x80\x84\x01\xc0t\x918K\x08\x80\x14\x00@z\x8eB\xa6\x00@F\x01\x80\x9d\x98&S\x00\xa0\x04\x00`\xcbcb\xe3\x00P-\x00`'\x7f\xe6\xd3\x00\x80\x9d\xf8\x99{\x01\x00[\x94!\x15\x01\xa0\x91\x00 \x13e\x88D\x00h;\x00\xac\xcfV\x8aE\x00X0\x00\x14fK\xc49\x00\xd8-\x000IWfH\x00\xb0\xb7\x00\xc0\xce\x10\x0b\xb2\x00\x08\x0c\x000Q\x88\x85)\x00\x04{\x00`\xc8##x\x00\x84\x99\x00\x14F\xf2W<\xf1+\xae\x10\xe7*\x00\x00x\x99\xb2<\xb9$9E\x81[\x08-q\x07WW.\x1e(\xceI\x17+\x146a\x02a\x9a@.\xc2y\x99\x192\x814\x0f\xe0\xf3\xcc\x00\x00\xa0\x91\x15\x11\xe0\x83\xf3\xfdx\xce\x0e\xae\xce\xce6\x8e\xb6\x0e_-\xea\xbf\x06\xff\x22bb\xe3\xfe\xe5\xcf\xabp@\x00\x00\xe1t~\xd1\xfe,/\xb3\x1a\x80;\x06\x80m\xfe\xa2%\xee\x04h^\x0b\xa0u\xf7\x8bf\xb2\x0f@\xb5\x00\xa0\xe9\xdaW\xf3p\xf8~<<E\xa1\x90\xb9\xd9\xd9\xe5\xe4\xe4\xd8J\xc4B[a\xcaW}\xfeg\xc2_\xc0W\xfdl\xf9~<\xfc\xf7\xf5\xe0\xbe\xe2$\x812]\x81G\x04\xf8\xe0\xc2\xcc\xf4L\xa5\x1c\xcf\x92\x09\x84b\xdc\xe6\x8fG\xfc\xb7\x0b\xff\xfc\x1d\xd3\x22\xc4Ib\xb9X*\x14\xe3Q\x12q\x8eD\x9a\x8c\xf32\xa5\x22\x89B\x92)\xc5%\xd2\xffd\xe2\xdf,\xfb\x03>\xdf5\x00\xb0j>\x01{\x91-\xa8]c\x03\xf6K'\x10Xt\xc0\xe2\xf7\x00\x00\xf2\xbbo\xc1\xd4(\x08\x03\x80h\x83\xe1\xcfw\xff\xef?\xfdG\xa0%\x00\x80fI\x92q\x00\x00^D$.T\xca\xb3?\xc7\x08\x00\x00D\xa0\x81*\xb0A\x1b\xf4\xc1\x18,\xc0\x06\x1c\xc1\x05\xdc\xc1\x0b\xfc`6\x84B$\xc4\xc2B\x10B\x0ad\x80\x1cr`)\xac\x82B(\x86\xcd\xb0\x1d*`/\xd4@\x1d4\xc0Qh\x86\x93p\x0e.\xc2U\xb8\x0e=p\x0f\xfaa\x08\x9e\xc1(\xbc\x81\x09\x04A\xc8\x08\x13a!\xda\x88\x01b\x8aX#\x8e\x08\x17\x99\x85\xf8!\xc1H\x04\x12\x8b$ \xc9\x88\x14Q\x22K\x915H1R\x8aT UH\x1d\xf2=r\x029\x87\x5cF\xba\x91;\xc8\x002\x82\xfc\x86\xbcG1\x94\x81\xb2Q=\xd4\x0c\xb5C\xb9\xa87\x1a\x84F\xa2\x0b\xd0dt1\x9a\x8f\x16\xa0\x9b\xd0r\xb4\x1a=\x8c6\xa1\xe7\xd0\xabh\x0f\xda\x8f>C\xc70\xc0\xe8\x18\x073\xc4l0.\xc6\xc3B\xb18,\x09\x93c\xcb\xb1\x22\xac\x0c\xab\xc6\x1a\xb0V\xac\x03\xbb\x89\xf5c\xcf\xb1w\x04\x12\x81E\xc0\x096\x04wB a\x1eAHXLXN\xd8H\xa8 \x1c$4\x11\xda\x097\x09\x03\x84Q\xc2'\x22\x93\xa8K\xb4&\xba\x11\xf9\xc4\x18b21\x87XH,#\xd6\x12\x8f\x13/\x10{\x88C\xc47$\x12\x89C2'\xb9\x90\x02I\xb1\xa4T\xd2\x12\xd2F\xd2nR#\xe9,\xa9\x9b4H\x1a#\x93\xc9\xdadk\xb2\x079\x94, +\xc8\x85\xe4\x9d\xe4\xc3\xe43\xe4\x1b\xe4!\xf2[\x0a\x9db@q\xa4\xf8S\xe2(R\xcajJ\x19\xe5\x10\xe54\xe5\x06e\x982AU\xa3\x9aR\xdd\xa8\xa1T\x115\x8fZB\xad\xa1\xb6R\xafQ\x87\xa8\x134u\x9a9\xcd\x83\x16IK\xa5\xad\xa2\x95\xd3\x1ah\x17h\xf7i\xaf\xe8t\xba\x11\xdd\x95\x1eN\x97\xd0W\xd2\xcb\xe9G\xe8\x97\xe8\x03\xf4w\x0c\x0d\x86\x15\x83\xc7\x88g(\x19\x9b\x18\x07\x18g\x19w\x18\xaf\x98L\xa6\x19\xd3\x8b\x19\xc7T071\xeb\x98\xe7\x99\x0f\x99oUX*\xb6*|\x15\x91\xca\x0a\x95J\x95&\x95\x1b*/T\xa9\xaa\xa6\xaa\xde\xaa\x0bU\xf3U\xcbT\x8f\xa9^S}\xaeFU3S\xe3\xa9\x09\xd4\x96\xabU\xaa\x9dP\xebS\x1bSg\xa9;\xa8\x87\xaag\xa8oT?\xa4~Y\xfd\x89\x06Y\xc3L\xc3OC\xa4Q\xa0\xb1_\xe3\xbc\xc6 \x0bc\x19\xb3x,!k\x0d\xab\x86u\x815\xc4&\xb1\xcd\xd9|v*\xbb\x98\xfd\x1d\xbb\x8b=\xaa\xa9\xa19C3J3W\xb3R\xf3\x94f?\x07\xe3\x98q\xf8\x9ctN\x09\xe7(\xa7\x97\xf3~\x8a\xde\x14\xef)\xe2)\x1b\xa64L\xb91e\x5ck\xaa\x96\x97\x96X\xabH\xabQ\xabG\xeb\xbd6\xae\xed\xa7\x9d\xa6\xbdE\xbbY\xfb\x81\x0eA\xc7J'\x5c'Gg\x8f\xce\x05\x9d\xe7S\xd9S\xdd\xa7\x0a\xa7\x16M=:\xf5\xae.\xaak\xa5\x1b\xa1\xbbDw\xbfn\xa7\xee\x98\x9e\xbe^\x80\x9eLo\xa7\xdey\xbd\xe7\xfa\x1c}/\xfdT\xfdm\xfa\xa7\xf5G\x0cX\x06\xb3\x0c$\x06\xdb\x0c\xce\x18<\xc55qo<\x1d/\xc7\xdb\xf1QC]\xc3@C\xa5a\x95a\x97\xe1\x84\x91\xb9\xd1<\xa3\xd5F\x8dF\x0f\x8ci\xc6\x5c\xe3$\xe3m\xc6m\xc6\xa3&\x06&!&KM\xeaM\xee\x9aRM\xb9\xa6)\xa6;L;L\xc7\xcd\xcc\xcd\xa2\xcd\xd6\x995\x9b=1\xd72\xe7\x9b\xe7\x9b\xd7\x9b\xdf\xb7`ZxZ,\xb6\xa8\xb6\xb8eI\xb2\xe4Z\xa6Y\xee\xb6\xbcn\x85Z9Y\xa5XUZ]\xb3F\xad\x9d\xad%\xd6\xbb\xad\xbb\xa7\x11\xa7\xb9N\x93N\xab\x9e\xd6g\xc3\xb0\xf1\xb6\xc9\xb6\xa9\xb7\x19\xb0\xe5\xd8\x06\xdb\xae\xb6m\xb6}agb\x17g\xb7\xc5\xae\xc3\xee\x93\xbd\x93}\xba}\x8d\xfd=\x07\x0d\x87\xd9\x0e\xab\x1dZ\x1d~s\xb4r\x14:V:\xde\x9a\xce\x9c\xee?}\xc5\xf4\x96\xe9/gX\xcf\x10\xcf\xd83\xe3\xb6\x13\xcb)\xc4i\x9dS\x9b\xd3Gg\x17g\xb9s\x83\xf3\x88\x8b\x89K\x82\xcb.\x97>.\x9b\x1b\xc6\xdd\xc8\xbd\xe4Jt\xf5q]\xe1z\xd2\xf5\x9d\x9b\xb3\x9b\xc2\xed\xa8\xdb\xaf\xee6\xeei\xee\x87\xdc\x9f\xcc4\x9f)\x9eY3s\xd0\xc3\xc8C\xe0Q\xe5\xd1?\x0b\x9f\x950k\xdf\xac~OCO\x81g\xb5\xe7#/c/\x91W\xad\xd7\xb0\xb7\xa5w\xaa\xf7a\xef\x17>\xf6>r\x9f\xe3>\xe3<7\xde2\xdeY_\xcc7\xc0\xb7\xc8\xb7\xcbO\xc3o\x9e_\x85\xdfC\x7f#\xffd\xffz\xff\xd1\x00\xa7\x80%\x01g\x03\x89\x81A\x81[\x02\xfb\xf8z|!\xbf\x8e?:\xdbe\xf6\xb2\xd9\xedA\x8c\xa0\xb9A\x15A\x8f\x82\xad\x82\xe5\xc1\xad!h\xc8\xec\x90\xad!\xf7\xe7\x98\xce\x91\xcei\x0e\x85P~\xe8\xd6\xd0\x07a\xe6a\x8b\xc3~\x0c'\x85\x87\x85W\x86?\x8ep\x88X\x1a\xd11\x975w\xd1\xdcCs\xdfD\xfaD\x96D\xde\x9bg1O9\xaf-J5*>\xaa.j<\xda7\xba4\xba?\xc6.fY\xcc\xd5X\x9dXIlK\x1c9.*\xae6nl\xbe\xdf\xfc\xed\xf3\x87\xe2\x9d\xe2\x0b\xe3{\x17\x98/\xc8]py\xa1\xce\xc2\xf4\x85\xa7\x16\xa9.\x12,:\x96@L\x88N8\x94\xf0A\x10*\xa8\x16\x8c%\xf2\x13w%\x8e\x0ay\xc2\x1d\xc2g\x22/\xd16\xd1\x88\xd8C\x5c*\x1eN\xf2H*Mz\x92\xec\x91\xbc5y$\xc53\xa5,\xe5\xb9\x84'\xa9\x90\xbcL\x0dL\xdd\x9b:\x9e\x16\x9av m2=:\xbd1\x83\x92\x91\x90qB\xaa!M\x93\xb6g\xeag\xe6fv\xcb\xace\x85\xb2\xfe\xc5n\x8b\xb7/\x1e\x95\x07\xc9k\xb3\x90\xac\x05Y-\x0a\xb6B\xa6\xe8TZ(\xd7*\x07\xb2geWf\xbf\xcd\x89\xca9\x96\xab\x9e+\xcd\xed\xcc\xb3\xca\xdb\x907\x9c\xef\x9f\xff\xed\x12\xc2\x12\xe1\x92\xb6\xa5\x86KW-\x1dX\xe6\xbd\xacj9\xb2<qy\xdb\x0a\xe3\x15\x05+\x86V\x06\xac<\xb8\x8a\xb6*m\xd5O\xab\xedW\x97\xae~\xbd&zMk\x81^\xc1\xca\x82\xc1\xb5\x01k\xeb\x0bU\x0a\xe5\x85}\xeb\xdc\xd7\xed]OX/Y\xdf\xb5a\xfa\x86\x9d\x1b>\x15\x89\x8a\xae\x14\xdb\x17\x97\x15\x7f\xd8(\xdcx\xe5\x1b\x87o\xca\xbf\x99\xdc\x94\xb4\xa9\xab\xc4\xb9d\xcff\xd2f\xe9\xe6\xde-\x9e[\x0e\x96\xaa\x97\xe6\x97\x0en\x0d\xd9\xda\xb4\x0d\xdfV\xb4\xed\xf5\xf6E\xdb/\x97\xcd(\xdb\xbb\x83\xb6C\xb9\xa3\xbf<\xb8\xbce\xa7\xc9\xce\xcd;?T\xa4T\xf4T\xfaT6\xee\xd2\xdd\xb5a\xd7\xf8n\xd1\xee\x1b{\xbc\xf64\xec\xd5\xdb[\xbc\xf7\xfd>\xc9\xbe\xdbU\x01UM\xd5f\xd5e\xfbI\xfb\xb3\xf7?\xae\x89\xaa\xe9\xf8\x96\xfbm]\xadNmq\xed\xc7\x03\xd2\x03\xfd\x07#\x0e\xb6\xd7\xb9\xd4\xd5\x1d\xd2=TR\x8f\xd6+\xebG\x0e\xc7\x1f\xbe\xfe\x9d\xefw-\x0d6\x0dU\x8d\x9c\xc6\xe2#pDy\xe4\xe9\xf7\x09\xdf\xf7\x1e\x0d:\xdav\x8c{\xac\xe1\x07\xd3\x1fv\x1dg\x1d/jB\x9a\xf2\x9aF\x9bS\x9a\xfb[b[\xbaO\xcc>\xd1\xd6\xea\xdez\xfcG\xdb\x1f\x0f\x9c4<YyJ\xf3T\xc9i\xda\xe9\x82\xd3\x93g\xf2\xcf\x8c\x9d\x95\x9d}~.\xf9\xdc`\xdb\xa2\xb6{\xe7c\xce\xdfj\x0fo\xef\xba\x10t\xe1\xd2E\xff\x8b\xe7;\xbc;\xce\x5c\xf2\xb8t\xf2\xb2\xdb\xe5\x13W\xb8W\x9a\xaf:_m\xeat\xea<\xfe\x93\xd3O\xc7\xbb\x9c\xbb\x9a\xae\xb9\x5ck\xb9\xeez\xbd\xb5{f\xf7\xe9\x1b\x9e7\xce\xdd\xf4\xbdy\xf1\x16\xff\xd6\xd5\x9e9=\xdd\xbd\xf3zo\xf7\xc5\xf7\xf5\xdf\x16\xdd~r'\xfd\xce\xcb\xbb\xd9w'\xee\xad\xbcO\xbc_\xf4@\xedA\xd9C\xdd\x87\xd5?[\xfe\xdc\xd8\xef\xdc\x7fj\xc0w\xa0\xf3\xd1\xdcG\xf7\x06\x85\x83\xcf\xfe\x91\xf5\x8f\x0fC\x05\x8f\x99\x8f\xcb\x86\x0d\x86\xeb\x9e8>99\xe2?r\xfd\xe9\xfc\xa7C\xcfd\xcf&\x9e\x17\xfe\xa2\xfe\xcb\xae\x17\x16/~\xf8\xd5\xeb\xd7\xce\xd1\x98\xd1\xa1\x97\xf2\x97\x93\xbfm|\xa5\xfd\xea\xc0\xeb\x19\xaf\xdb\xc6\xc2\xc6\x1e\xbe\xc9x31^\xf4V\xfb\xed\xc1w\xdcw\x1d\xef\xa3\xdf\x0fO\xe4| \x7f(\xffh\xf9\xb1\xf5S\xd0\xa7\xfb\x93\x19\x93\x93\xff\x04\x03\x98\xf3\xfcc3-\xdb\x00\x00\x00 cHRM\x00\x00z%\x00\x00\x80\x83\x00\x00\xf9\xff\x00\x00\x80\xe9\x00\x00u0\x00\x00\xea`\x00\x00:\x98\x00\x00\x17o\x92_\xc5F\x00\x00\x00yIDATx\xda\xec\x971\x0a\xc00\x0c\x03%\x93_\xf5\xfd}\x97\xb3\xb4\x10h\x07gPR\xa8<y1>$\x07\x14f&vV`s\xb5\xbb9I\x00X%\x07\x8fK\xf9Q\x81\x95^\xe4C\x817J\xd5\xd2\xca\x0dP!{\x15\x80J\xef?\xf7\x0a\x0c`\x00\x03\x18\xc0\x00\x060\x80\x01\x0c\x10\xd5\xf4\xaaJ\xc61\x13\xa1\x15\xb1\xbc\xcd\x0e(-\xe0\x22\xdb9\xee\xe2\xef\x7f\xc7\x1d\x00\x00\xff\xff\x03\x00>H\x12?\xd7\xafML\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02V\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00@\x00\x00\x00@\x08\x06\x00\x00\x00\xaaiq\xde\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdf\x04\x19\x10\x15\x00\xdc\xbe\xff\xeb\x00\x00\x00\x1diTXtComment\x00\x00\x00\x00\x00Created with GIMPd.e\x07\x00\x00\x01\xbaIDATx\xda\xed\x9b[\x92\x02!\x0cEM\xd67.H\x17\xa0\x0b\xd2\xfd\xe9\x9fe9\xda<\x92{\x13h\xf2=\x95\xe6\x1c\x1eC\x10\x0e\x87\x15+V\xec9\x84\xf9\xb1\xdb\xe9\xf4\xa8\xf9\xbb\xe3\xf5*S\x08\xa8\x05\x8e\x14\x22Y\xa1Y2d\x14p\x94\x08\x19\x11\xdeS\x82\x8c\x08\xee)BF\x87\xb7J\x90\xd1\xc1\xad\x22d&\xf8\x1e\x092\x1b|\xab\x04][\xe1\x09{\xbfe\x14\x88\x15\xfe\xefry\xe5\xb8\x9f\xcf\x14Q\xef\xdf,}\xb7$A\xbd\x1b\xf6\xd984\xbc5\x141\xf4Q\x12z\xf2\x96\x18\x145\xef\xbd%X\xf2m\xb1\x98\xa7\xc0\xd6\xfc\xf3\x92\xb0\x95\xc7\xba\xee\x88W\xef\xa3\x1a\xe9\x99\xf7\xdb\x82\xe8\xb6\x08\x22F\x02\xb2\xe7!\xff\x05<%0\xe0\xbfN\x01\x8fM\x8f\xb5\xf1H\xf8\xcfi\x00\xd9\x0a[F\x02\xab\xe7\xe1\xb5@\x8f\x046<\xbc\x18j\x91\x10\x01\xffo\x0d@\x15=%86\xfc\xfb:@)\x87{\xd7\x04FqE;\x0fh\x85aU\x96\xd4\x03\x91Z(\x16<]@\x0d\x1c\x13>D\x80e\x1f0\xbc\x80Z8\xa6\x04\xcd\x06\xcf\x96\xa0\xd1\xf0\x8c\xf3\x84P\x015\xf0\x91\x12 \xd5`o\xcf36E\x94j\xb0\x17&b$h\xa69\x1f!A3\xc1GHp;\x14E\xcca\xef|\xd0CQ\xc4\x02\xc6\x18\x09\x9a\x15\x9e%\xe1g\x82\xdai\xc0\xaa\xe7\xad\xdf\xf9\xf5#i\xc8\x99`\x86|E\x01\x96\x9bW\xa8\xc6\xf6\xe6\xddb\xd1\xec=\x8f\xceo\xbe \x91=J#y]\x91\xa9M\xb6n\x89M\x1a\xeb\xa2dk\xf2]_\x95\xcd,\x82vY:\xa3\x84\x90\xeb\xf2Y$X\x1fM\xac'3\xde\x0d\xdb\xed\xa3)\xa4\x8c\xa1\x9e\xcdy\x08a>\x9c\x5c\xb1\xf7x\x02G\xb0[\x07:D>\x01\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xa0\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1f\x0d\xfcR+\x9c\x00\x00\x00$IDAT\x08\xd7c`@\x05s>\xc0XL\xc8\x5c&dY&d\xc5pN\x8a\x00\x9c\x93\x22\x80a\x1a\x0a\x00\x00)\x95\x08\xaf\x88\xac\xba4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x03\xa5\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x03\x22IDATX\x85\xed\x96MlTU\x14\xc7\x7f\xe7\x0d\xa9\x09\xcc\x90Pv\xb6\xc6``\xe3\xa3\x864\xf4\xc3\xc6g\xa4\x1b\xa2\x98@\x13]\xc9\x1a6\xda\x84~Y\x5c\xcd\xce:\xa43\x09\xcb\xaee\x83\x89\x19L\x04\xc3\xc6:\x98\xb4o\x22bK'\xc64\xac\x9c\x067\x94t\x98\x92P:\xef\xef\xe2M\xa75\x99\xe9\xccCv\xf4\xbf\xba\xe7\xbds\xef\xf9\xdds\xee\x17\xeciO\xaf\xba,\x8a\xb3\x9b,\xb4\x1dN\xac\x0f\xc98\x07\xea\x06:\xaa\xbf\x8a\x88\xdf\xcd,\xfb\xa8t [H\xba\x1b/\x1d\xc0\xcb\xcc\x7f\x82,\x05\x1c\x01\xbb\x8f4\x8bC\x11\xc0\xa4\x0e\xe1\x9c\x02ua<0l\x22w\xa9\xf7\xfb\x97\x02\xf0\xe9\xf5\xeb\xb1\x7fV\xdeL!F\x80\x9f$&\x7f\x1d\xed[\xa8\xe7;\x90\xc9\x9f\x88\x05\x9a\xc28\x0d\x5c\xb9S\xea\x9d$iA\xab\x93\xac+/\xe3O{i\xbf\xf2~f~\xac\xe5>i\x7f\xdcK\xfb\x15/\xed\xa7\x9a\xf9\xee\x9a\x81j\xda\xbf3l,7\xd2;\x0d\xf0\xe1\xd5\xe5\xd7\x9e<\x7f|\xd1\xe03Y\xd0\x15\x0eb\x8b\x18\xd7\xe2\xb1\xf6\x99[\xc3\xc7\x9eU\xc1'\x10\xdf`\x0c\xdd\xb9\xd4\x97\x8d\x0c\xe0&\x0bm\xed\x07\xcb\x7f\x1a\xfa+7\xd2\xff\x11\xc0\x07W\xe7;+\x9b\xceMP\x17X\x00r\xaa\xc3\x84mc1\x16\xd3\x99\xd9\xe1\xfe\x22\xc0{\x99\xfcm\x93\x8e\xac\x96\xe2n\xa3\x85\xe94\x028\x9cX\x1f\x02\xde\x0ad\x97\xb7f^\xd9tnb:\x1ezhG\xdfZ\xbb\xab\xb2\xc9\x8fn\xb2\xd0\x06\xe0\x04\xf6%p\xf4P\xa2|\xb6Q\x9c\x86\x00\xe1Vcak\xc1\x95+\xab\x17@]h\x97\xb2\x09\x03{\xa7\xfd`\xf9\x02@n\xb4\xe7\x9e\xc4\x92At\x00P\xb7\xa1_jf`\xe7\xc3T\xef.A\x00\x9c\xdf\xb2\x0d~\xc68\xf9\x02\x00\xbc.\xacX\xb3L\xee\x7f\xd3^_\x06\x0e\xc8\xdd\x01\xb4\xc2\xf6\x81\x15\x09\x00,\xdaIY7\x80\x99\x11f%2\xc0C\x02:k\x96\xac\xd0j\x09$\x96\xb6mu\x00\x0f\xa3\x03\x88\xdf\x04\xa7\xb6=\xf5m\xab%0\xb3k;>\x0d\x02\xf9\xc8\x00f\x965\xe3\xf8@&\x7f\x02 \x1ek\x9f\xc1X\xc4\xd0.\xd1%\xe3\x8f\xd5R|\x06\xc0\xcb\xccu\x03oc\xfa!2\xc0\xa3\xd2\x81,\xc6\x83X\xa0)\x80[\xc3\xc7\x9e\xc5b:\x03\xdc\xafF\xab\x95\xa3\xba\xf2\x11,TT\xf9\xb8\x90t7\x90\x0c9)`\xf9\xe9\xfe}7\x22\x03\x14\x92\xee\x86\xc48\xc6i/\xed\x8f\x03\xcc\x0e\xf7\x17W\xd7\xe2=\xc0\x17R\x90\x07\xd6\x81u\xa4\xbc\x99>\x7f\xbc\x16\xef\x9b\x1b\x19X\x01\xf0\xd2\xfe$0h\x0a\xc6\xee^<\xf9\xbcQ\x9c\xa6\xf2\xd2~\xaaz\xb1\x8c\xb7\xd4A2oz\xferx\x81\xf9S\xcd\xdc\x9bo\xb3\xa4\x1c/\x91\xff\x1ac\x02\xb8mr&s\xa3=\xf7\xea\xc2f\xe6\xba\xabi\x1f4#\x95[\xeb\xfd\xaa\xd9u\x1c\xe1A\xe2\x9fC\x5c\x01\x8eJ,\x991\x8b\xf17\x00\xe2\x0d\xc2\x1d\xe3\x02\xcb\xa6`,7\xfan\xc3\x85\xf7B\x00\x10\xde\x90\x87\x12\xe5\xb3T\x9fd\x86u\x86\xf1U4\xd9]\x1ce\x9f\xee\xdfw\xe3\x7f\xd5|O{z\xe5\xf4/\x95?G\xacm\xe50s\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xa6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xa0\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x1b)\xb3G\xee\x04\x00\x00\x00$IDAT\x08\xd7c`@\x05s>\xc0XL\xc8\x5c&dY&d\xc5pN\x8a\x00\x9c\x93\x22\x80a\x1a\x0a\x00\x00)\x95\x08\xaf\x88\xac\xba4\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x01\xed\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x01jIDATX\x85\xed\x97\xcbN\xc2@\x14\x86\xbfC\x08x}\x00\xf4\x15\xd4\x84w\x91ei\x0bq\xa1\xef#\xae\x9aq\xa8K|\x077\xae\x09\xe1\x1d\xc4\xbd\x17\xe4\x92\x1e\x17\xa5\xa6\x06\xd8\x98!\x18\xed\xbf\x9av&\xfd\xbeN\xa6\xcd9\xf0\xdf#\xf9\x0bU\x15kLP\x12\xb9T8\x05v\x1cq>\x04\x86@\xc7\x0b\x02+\x22\xba$\xa0\xaa\x12\x1bs\xab\x22M`\x02\xf4\x11yu\x82W=\x00\xea@\x15\x11\xd3\xf4\xfdv&Q\xce\xd6Xc\x02I\xe1\x8f\xa5r\xb9\xe1y\xde\xc8\x09|\x918\x8ek\xc9|\xdeC5\xb4\xd6>\x00]\x80R\xb6\xa0$r\x09L\x128w\x0d\x07\xf0<o4O\x92\x060\x15\xd5\xab/n6P8\x01\xfaa\x18>\xbb\x86gi\xb7\xdbO@\x9f\xf4|}\x17\x00v\x81\xf7M\xc1sy\x03\xf6V\x09l%\x85\xc0\xd6\x05\xca\xeb&\xac1\xban\xee'\xf1\xc3PV\xdd\xdf\xfa\x0e\x14\x02\x85@!\xb0\xf6?\xb0\xee\xbbu\x9d\xad\xef@!\xf0\xab\x04\xc6\xe4*\x95\x0df\x7f\xc1Z\x12\x18\x02\xf58\x8ek\x9b\x22[k\x8fI\xcb\xf3\xc1\x92\x80\xc0\x0dPMf\xb3\xfb(\x8a\x8e6\x02O\x92\x1eP\x11\xe8\xe4\xb8iTU\xba\xd6F\xa8\x86\xc0\x94\xb41yqBW=$}\xf3\x8aB\xe4\x07\xc1E\xd6\x98,\xb7f\xd6z\x8b\xba\xfd\x8c\xb4Rv\x9110@\xf5\xdao\xb5\xee\x1c=\xf3\x8f\xe4\x13\xfb6zV\x11\xde\xcf\xd8\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\xa6\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1f \xb9\x8dw\xe9\x00\x00\x00*IDAT\x08\xd7c`\xc0\x06\xe6|```B0\xa1\x1c\x08\x93\x81\x81\x09\xc1d``b`H\x11@\xe2 s\x19\x90\x8d@\x02\x00#\xed\x08\xafd\x9f\x0f\x15\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\xd4\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x02QIDATX\x85\xed\x96AKTQ\x14\xc7\x7f\xe7\x8d\xb8\xd0&0wi\x84\xe1\xaa)\x90A\xc7\x92^\xa0\x1b\xa1\x8d\x0a\xf5\x19Z;3\xda\xd8j\x16A6\x83\xf3\xbe\x87A\x8d\xad\xc2M\xf6\x14\xf4\x0d\x99H\x0e\x11\xe2\xaa\x11\xdb\x184\xa8\x0b\xc3wZ\xccH\x10\xf3t\xee\xe8\xae\xf9o\xef9\xfc\x7f\xf7\xdc{\xcf=\xd0TS\xff\xbb\xc4$8\x92.\xb6v\x86\x0f'T\x18\x07\x8d\x02]\xd5\xa5\x12\xcag\x11\xc9\xef\x97\xdb\xf3\xc5t\xe4\xf8\xd2\x01lg\xed1*\x19\xa0\x07\xe4\x0b\xaaKX\x94\x00D\xb5K\xb1\x86A\xef\x22\xec\x082\xedN\xc6\xde\x5c\x0a\xc0\x93\xf9\xf9\xd0\x8f\xdd\x9b\x19\x948\xf0^\x95\xd4Jbp\xb3V\xec\x90S\xe8\x0b\xf9:\x8b0\x0ad\x97\xcb\xb1\x14i\xf1\xeb\xdddM\xd9\x8e7g\xe7\xbc\x93\x87\xceZ\xb2\xee\x9c\x9c7e\xe7\xbc\x13;\xe7e\xce\x8b=\xb3\x02\xd5\xb2\xbf\x16$\xe9\xc6cs\xf5\x02Tr\xbdi\x94W\x08\x13\xcb\x93\x83yc\x80H\xba\xd8z\xed\xea\xc1WA\xbf\xb9\xf1{\x8fL\xccO\xf5\xc0),\x8aj\xcf\xcf\xf2\x95H\xd0\xc5\xb4\x82\x92;\xc3\x87\x13\xc0-_e\xa6\x11s\x00\xcb\x97g@oG\xf8`,0&h\xa1\xf2\xd4\xd8\x0c\xbap\xf5\xc8M\x0cl\xa8\xb2%`\x0e\x00\x1a\x15\xf4c\xa3\xe6\xa7\x12\xf8\x80\xd0\xdf\x00\x00\xd7\x15)]\x14@a\x97\xbf\x0d\xcb\x08\x00\xc4\xacS\xd64\x10\x11 \xb0\x17\x9c\x05\xb0\x87O\xf7E\x01\x14\xed\x02\xf6\xcc\x01\x94O\x0a\xc3\x17\x05\x00F\x80\x821\x80\x88\xe4E\xb83\xe4\x14\xfa\x1au\xb6\x9d\xd5(p\x1b\xd1w\xc6\x00\xfb\xe5\xf6<\xc2N\xc8\xd7\xd9\x86\xdcU\x05\xb52\xc0\xf6Q[\xcb\x821@1\x1d9Ve\x0aa\xd4\xceyS\xa6\xfev\xceK\x01#\xa2~r\xfdi\xffoc\x00\x80\x95\xf8\xe0[ \x0b\xcc\xd6\x0d\xa1*\xf6\xdc\xda\x0c\x22/D\xc8\xb8\x89\xfb\x81\xe5\x87z\xe6\x81\xb4Zv\xb8\xf0\x12a\x1aX\x14\xb5Rnb`\xa3V\xa8\xed\xacF\xabe\x1f\x11!\xe3\xfe\x8a=?\xef;6\x18H\xbcq\x94,\xd0\xab\xca\x96\x08K\x08\xdf\x01PnPy1\x11`[\xd4O\x9e\xb7sc\x00\xa8\xfc\x90\x1d\xe1\x831\xaa#\x99 \xdd\x15\x7f-\x89\xca:\x96\xe6\x8f\xdaZ\x16\xce:\xf3\xa6\x9aj\xea_\xfd\x01\xd3\x1c\xd9\x7f^\xb93\xcd\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0b\x95\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x0aOiCCPPhotoshop ICC profile\x00\x00x\xda\x9dSgTS\xe9\x16=\xf7\xde\xf4BK\x88\x80\x94KoR\x15\x08 RB\x8b\x80\x14\x91&*!\x09\x10J\x88!\xa1\xd9\x15Q\xc1\x11EE\x04\x1b\xc8\xa0\x88\x03\x8e\x8e\x80\x8c\x15Q,\x0c\x8a\x0a\xd8\x07\xe4!\xa2\x8e\x83\xa3\x88\x8a\xca\xfb\xe1{\xa3k\xd6\xbc\xf7\xe6\xcd\xfe\xb5\xd7>\xe7\xac\xf3\x9d\xb3\xcf\x07\xc0\x08\x0c\x96H3Q5\x80\x0c\xa9B\x1e\x11\xe0\x83\xc7\xc4\xc6\xe1\xe4.@\x81\x0a$p\x00\x10\x08\xb3d!s\xfd#\x01\x00\xf8~<<+\x22\xc0\x07\xbe\x00\x01x\xd3\x0b\x08\x00\xc0M\x9b\xc00\x1c\x87\xff\x0f\xeaB\x99\x5c\x01\x80\x84\x01\xc0t\x918K\x08\x80\x14\x00@z\x8eB\xa6\x00@F\x01\x80\x9d\x98&S\x00\xa0\x04\x00`\xcbcb\xe3\x00P-\x00`'\x7f\xe6\xd3\x00\x80\x9d\xf8\x99{\x01\x00[\x94!\x15\x01\xa0\x91\x00 \x13e\x88D\x00h;\x00\xac\xcfV\x8aE\x00X0\x00\x14fK\xc49\x00\xd8-\x000IWfH\x00\xb0\xb7\x00\xc0\xce\x10\x0b\xb2\x00\x08\x0c\x000Q\x88\x85)\x00\x04{\x00`\xc8##x\x00\x84\x99\x00\x14F\xf2W<\xf1+\xae\x10\xe7*\x00\x00x\x99\xb2<\xb9$9E\x81[\x08-q\x07WW.\x1e(\xceI\x17+\x146a\x02a\x9a@.\xc2y\x99\x192\x814\x0f\xe0\xf3\xcc\x00\x00\xa0\x91\x15\x11\xe0\x83\xf3\xfdx\xce\x0e\xae\xce\xce6\x8e\xb6\x0e_-\xea\xbf\x06\xff\x22bb\xe3\xfe\xe5\xcf\xabp@\x00\x00\xe1t~\xd1\xfe,/\xb3\x1a\x80;\x06\x80m\xfe\xa2%\xee\x04h^\x0b\xa0u\xf7\x8bf\xb2\x0f@\xb5\x00\xa0\xe9\xdaW\xf3p\xf8~<<E\xa1\x90\xb9\xd9\xd9\xe5\xe4\xe4\xd8J\xc4B[a\xcaW}\xfeg\xc2_\xc0W\xfdl\xf9~<\xfc\xf7\xf5\xe0\xbe\xe2$\x812]\x81G\x04\xf8\xe0\xc2\xcc\xf4L\xa5\x1c\xcf\x92\x09\x84b\xdc\xe6\x8fG\xfc\xb7\x0b\xff\xfc\x1d\xd3\x22\xc4Ib\xb9X*\x14\xe3Q\x12q\x8eD\x9a\x8c\xf32\xa5\x22\x89B\x92)\xc5%\xd2\xffd\xe2\xdf,\xfb\x03>\xdf5\x00\xb0j>\x01{\x91-\xa8]c\x03\xf6K'\x10Xt\xc0\xe2\xf7\x00\x00\xf2\xbbo\xc1\xd4(\x08\x03\x80h\x83\xe1\xcfw\xff\xef?\xfdG\xa0%\x00\x80fI\x92q\x00\x00^D$.T\xca\xb3?\xc7\x08\x00\x00D\xa0\x81*\xb0A\x1b\xf4\xc1\x18,\xc0\x06\x1c\xc1\x05\xdc\xc1\x0b\xfc`6\x84B$\xc4\xc2B\x10B\x0ad\x80\x1cr`)\xac\x82B(\x86\xcd\xb0\x1d*`/\xd4@\x1d4\xc0Qh\x86\x93p\x0e.\xc2U\xb8\x0e=p\x0f\xfaa\x08\x9e\xc1(\xbc\x81\x09\x04A\xc8\x08\x13a!\xda\x88\x01b\x8aX#\x8e\x08\x17\x99\x85\xf8!\xc1H\x04\x12\x8b$ \xc9\x88\x14Q\x22K\x915H1R\x8aT UH\x1d\xf2=r\x029\x87\x5cF\xba\x91;\xc8\x002\x82\xfc\x86\xbcG1\x94\x81\xb2Q=\xd4\x0c\xb5C\xb9\xa87\x1a\x84F\xa2\x0b\xd0dt1\x9a\x8f\x16\xa0\x9b\xd0r\xb4\x1a=\x8c6\xa1\xe7\xd0\xabh\x0f\xda\x8f>C\xc70\xc0\xe8\x18\x073\xc4l0.\xc6\xc3B\xb18,\x09\x93c\xcb\xb1\x22\xac\x0c\xab\xc6\x1a\xb0V\xac\x03\xbb\x89\xf5c\xcf\xb1w\x04\x12\x81E\xc0\x096\x04wB a\x1eAHXLXN\xd8H\xa8 \x1c$4\x11\xda\x097\x09\x03\x84Q\xc2'\x22\x93\xa8K\xb4&\xba\x11\xf9\xc4\x18b21\x87XH,#\xd6\x12\x8f\x13/\x10{\x88C\xc47$\x12\x89C2'\xb9\x90\x02I\xb1\xa4T\xd2\x12\xd2F\xd2nR#\xe9,\xa9\x9b4H\x1a#\x93\xc9\xdadk\xb2\x079\x94, +\xc8\x85\xe4\x9d\xe4\xc3\xe43\xe4\x1b\xe4!\xf2[\x0a\x9db@q\xa4\xf8S\xe2(R\xcajJ\x19\xe5\x10\xe54\xe5\x06e\x982AU\xa3\x9aR\xdd\xa8\xa1T\x115\x8fZB\xad\xa1\xb6R\xafQ\x87\xa8\x134u\x9a9\xcd\x83\x16IK\xa5\xad\xa2\x95\xd3\x1ah\x17h\xf7i\xaf\xe8t\xba\x11\xdd\x95\x1eN\x97\xd0W\xd2\xcb\xe9G\xe8\x97\xe8\x03\xf4w\x0c\x0d\x86\x15\x83\xc7\x88g(\x19\x9b\x18\x07\x18g\x19w\x18\xaf\x98L\xa6\x19\xd3\x8b\x19\xc7T071\xeb\x98\xe7\x99\x0f\x99oUX*\xb6*|\x15\x91\xca\x0a\x95J\x95&\x95\x1b*/T\xa9\xaa\xa6\xaa\xde\xaa\x0bU\xf3U\xcbT\x8f\xa9^S}\xaeFU3S\xe3\xa9\x09\xd4\x96\xabU\xaa\x9dP\xebS\x1bSg\xa9;\xa8\x87\xaag\xa8oT?\xa4~Y\xfd\x89\x06Y\xc3L\xc3OC\xa4Q\xa0\xb1_\xe3\xbc\xc6 \x0bc\x19\xb3x,!k\x0d\xab\x86u\x815\xc4&\xb1\xcd\xd9|v*\xbb\x98\xfd\x1d\xbb\x8b=\xaa\xa9\xa19C3J3W\xb3R\xf3\x94f?\x07\xe3\x98q\xf8\x9ctN\x09\xe7(\xa7\x97\xf3~\x8a\xde\x14\xef)\xe2)\x1b\xa64L\xb91e\x5ck\xaa\x96\x97\x96X\xabH\xabQ\xabG\xeb\xbd6\xae\xed\xa7\x9d\xa6\xbdE\xbbY\xfb\x81\x0eA\xc7J'\x5c'Gg\x8f\xce\x05\x9d\xe7S\xd9S\xdd\xa7\x0a\xa7\x16M=:\xf5\xae.\xaak\xa5\x1b\xa1\xbbDw\xbfn\xa7\xee\x98\x9e\xbe^\x80\x9eLo\xa7\xdey\xbd\xe7\xfa\x1c}/\xfdT\xfdm\xfa\xa7\xf5G\x0cX\x06\xb3\x0c$\x06\xdb\x0c\xce\x18<\xc55qo<\x1d/\xc7\xdb\xf1QC]\xc3@C\xa5a\x95a\x97\xe1\x84\x91\xb9\xd1<\xa3\xd5F\x8dF\x0f\x8ci\xc6\x5c\xe3$\xe3m\xc6m\xc6\xa3&\x06&!&KM\xeaM\xee\x9aRM\xb9\xa6)\xa6;L;L\xc7\xcd\xcc\xcd\xa2\xcd\xd6\x995\x9b=1\xd72\xe7\x9b\xe7\x9b\xd7\x9b\xdf\xb7`ZxZ,\xb6\xa8\xb6\xb8eI\xb2\xe4Z\xa6Y\xee\xb6\xbcn\x85Z9Y\xa5XUZ]\xb3F\xad\x9d\xad%\xd6\xbb\xad\xbb\xa7\x11\xa7\xb9N\x93N\xab\x9e\xd6g\xc3\xb0\xf1\xb6\xc9\xb6\xa9\xb7\x19\xb0\xe5\xd8\x06\xdb\xae\xb6m\xb6}agb\x17g\xb7\xc5\xae\xc3\xee\x93\xbd\x93}\xba}\x8d\xfd=\x07\x0d\x87\xd9\x0e\xab\x1dZ\x1d~s\xb4r\x14:V:\xde\x9a\xce\x9c\xee?}\xc5\xf4\x96\xe9/gX\xcf\x10\xcf\xd83\xe3\xb6\x13\xcb)\xc4i\x9dS\x9b\xd3Gg\x17g\xb9s\x83\xf3\x88\x8b\x89K\x82\xcb.\x97>.\x9b\x1b\xc6\xdd\xc8\xbd\xe4Jt\xf5q]\xe1z\xd2\xf5\x9d\x9b\xb3\x9b\xc2\xed\xa8\xdb\xaf\xee6\xeei\xee\x87\xdc\x9f\xcc4\x9f)\x9eY3s\xd0\xc3\xc8C\xe0Q\xe5\xd1?\x0b\x9f\x950k\xdf\xac~OCO\x81g\xb5\xe7#/c/\x91W\xad\xd7\xb0\xb7\xa5w\xaa\xf7a\xef\x17>\xf6>r\x9f\xe3>\xe3<7\xde2\xdeY_\xcc7\xc0\xb7\xc8\xb7\xcbO\xc3o\x9e_\x85\xdfC\x7f#\xffd\xffz\xff\xd1\x00\xa7\x80%\x01g\x03\x89\x81A\x81[\x02\xfb\xf8z|!\xbf\x8e?:\xdbe\xf6\xb2\xd9\xedA\x8c\xa0\xb9A\x15A\x8f\x82\xad\x82\xe5\xc1\xad!h\xc8\xec\x90\xad!\xf7\xe7\x98\xce\x91\xcei\x0e\x85P~\xe8\xd6\xd0\x07a\xe6a\x8b\xc3~\x0c'\x85\x87\x85W\x86?\x8ep\x88X\x1a\xd11\x975w\xd1\xdcCs\xdfD\xfaD\x96D\xde\x9bg1O9\xaf-J5*>\xaa.j<\xda7\xba4\xba?\xc6.fY\xcc\xd5X\x9dXIlK\x1c9.*\xae6nl\xbe\xdf\xfc\xed\xf3\x87\xe2\x9d\xe2\x0b\xe3{\x17\x98/\xc8]py\xa1\xce\xc2\xf4\x85\xa7\x16\xa9.\x12,:\x96@L\x88N8\x94\xf0A\x10*\xa8\x16\x8c%\xf2\x13w%\x8e\x0ay\xc2\x1d\xc2g\x22/\xd16\xd1\x88\xd8C\x5c*\x1eN\xf2H*Mz\x92\xec\x91\xbc5y$\xc53\xa5,\xe5\xb9\x84'\xa9\x90\xbcL\x0dL\xdd\x9b:\x9e\x16\x9av m2=:\xbd1\x83\x92\x91\x90qB\xaa!M\x93\xb6g\xeag\xe6fv\xcb\xace\x85\xb2\xfe\xc5n\x8b\xb7/\x1e\x95\x07\xc9k\xb3\x90\xac\x05Y-\x0a\xb6B\xa6\xe8TZ(\xd7*\x07\xb2geWf\xbf\xcd\x89\xca9\x96\xab\x9e+\xcd\xed\xcc\xb3\xca\xdb\x907\x9c\xef\x9f\xff\xed\x12\xc2\x12\xe1\x92\xb6\xa5\x86KW-\x1dX\xe6\xbd\xacj9\xb2<qy\xdb\x0a\xe3\x15\x05+\x86V\x06\xac<\xb8\x8a\xb6*m\xd5O\xab\xedW\x97\xae~\xbd&zMk\x81^\xc1\xca\x82\xc1\xb5\x01k\xeb\x0bU\x0a\xe5\x85}\xeb\xdc\xd7\xed]OX/Y\xdf\xb5a\xfa\x86\x9d\x1b>\x15\x89\x8a\xae\x14\xdb\x17\x97\x15\x7f\xd8(\xdcx\xe5\x1b\x87o\xca\xbf\x99\xdc\x94\xb4\xa9\xab\xc4\xb9d\xcff\xd2f\xe9\xe6\xde-\x9e[\x0e\x96\xaa\x97\xe6\x97\x0en\x0d\xd9\xda\xb4\x0d\xdfV\xb4\xed\xf5\xf6E\xdb/\x97\xcd(\xdb\xbb\x83\xb6C\xb9\xa3\xbf<\xb8\xbce\xa7\xc9\xce\xcd;?T\xa4T\xf4T\xfaT6\xee\xd2\xdd\xb5a\xd7\xf8n\xd1\xee\x1b{\xbc\xf64\xec\xd5\xdb[\xbc\xf7\xfd>\xc9\xbe\xdbU\x01UM\xd5f\xd5e\xfbI\xfb\xb3\xf7?\xae\x89\xaa\xe9\xf8\x96\xfbm]\xadNmq\xed\xc7\x03\xd2\x03\xfd\x07#\x0e\xb6\xd7\xb9\xd4\xd5\x1d\xd2=TR\x8f\xd6+\xebG\x0e\xc7\x1f\xbe\xfe\x9d\xefw-\x0d6\x0dU\x8d\x9c\xc6\xe2#pDy\xe4\xe9\xf7\x09\xdf\xf7\x1e\x0d:\xdav\x8c{\xac\xe1\x07\xd3\x1fv\x1dg\x1d/jB\x9a\xf2\x9aF\x9bS\x9a\xfb[b[\xbaO\xcc>\xd1\xd6\xea\xdez\xfcG\xdb\x1f\x0f\x9c4<YyJ\xf3T\xc9i\xda\xe9\x82\xd3\x93g\xf2\xcf\x8c\x9d\x95\x9d}~.\xf9\xdc`\xdb\xa2\xb6{\xe7c\xce\xdfj\x0fo\xef\xba\x10t\xe1\xd2E\xff\x8b\xe7;\xbc;\xce\x5c\xf2\xb8t\xf2\xb2\xdb\xe5\x13W\xb8W\x9a\xaf:_m\xeat\xea<\xfe\x93\xd3O\xc7\xbb\x9c\xbb\x9a\xae\xb9\x5ck\xb9\xeez\xbd\xb5{f\xf7\xe9\x1b\x9e7\xce\xdd\xf4\xbdy\xf1\x16\xff\xd6\xd5\x9e9=\xdd\xbd\xf3zo\xf7\xc5\xf7\xf5\xdf\x16\xdd~r'\xfd\xce\xcb\xbb\xd9w'\xee\xad\xbcO\xbc_\xf4@\xedA\xd9C\xdd\x87\xd5?[\xfe\xdc\xd8\xef\xdc\x7fj\xc0w\xa0\xf3\xd1\xdcG\xf7\x06\x85\x83\xcf\xfe\x91\xf5\x8f\x0fC\x05\x8f\x99\x8f\xcb\x86\x0d\x86\xeb\x9e8>99\xe2?r\xfd\xe9\xfc\xa7C\xcfd\xcf&\x9e\x17\xfe\xa2\xfe\xcb\xae\x17\x16/~\xf8\xd5\xeb\xd7\xce\xd1\x98\xd1\xa1\x97\xf2\x97\x93\xbfm|\xa5\xfd\xea\xc0\xeb\x19\xaf\xdb\xc6\xc2\xc6\x1e\xbe\xc9x31^\xf4V\xfb\xed\xc1w\xdcw\x1d\xef\xa3\xdf\x0fO\xe4| \x7f(\xffh\xf9\xb1\xf5S\xd0\xa7\xfb\x93\x19\x93\x93\xff\x04\x03\x98\xf3\xfcc3-\xdb\x00\x00\x00 cHRM\x00\x00z%\x00\x00\x80\x83\x00\x00\xf9\xff\x00\x00\x80\xe9\x00\x00u0\x00\x00\xea`\x00\x00:\x98\x00\x00\x17o\x92_\xc5F\x00\x00\x00\xc0IDATx\xda\xdcVA\x0e\x830\x0c\xab\xa3\xfe\xff=\xfb\x9dw\x021ThJ\xe2 -'\xd4\x03vl'-\xda\xa7\x1d\x8b\xad\xa6\xb0}\xf4b\xe0s\xa3\xb0\x17\xc0\x7f\x88\xf4\x99D\xa2\xce\xf7\xb2B\xf0\xe1\xbfM\x0c\xceA\xd7\x98)\xa0\x90\xfb2gV\xe5\xf5\x85\x1aR\x05\x5ceE\xdd\xbbCX\x0a\xfew\x16,w\x9fI\xe0\x11x\x16\x01*-`\x10\x00\x11\x02\x9eM\xc6\x08\xf8\x1d\x01:\xce\xa8\x9a\x02&\xf8\x8d\x08\x01\x04s\x81\x8c\x10*\xdf\x04\xee\x10B\x91\xfa\xd51\x84\x12\xdc\xbb\x88\xf0\x96\x05+$\xa0&p\x07\x82\x0a\x05dv\xf4\xc1\x8cCL\x82\x91M\x98~sv\xc5\x15\xbb\x9a\x81\xb2\xad7\xb2\xd3\xaaW\xef9K\xdf\x01\x00h\x95#\xfe/d\x9d\xea\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x03\xa5\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x03\x22IDATX\x85\xed\x96MlTU\x14\xc7\x7f\xe7\x0d\xa9\x09\xcc\x90Pv\xb6\xc6``\xe3\xa3\x864\xf4\xc3\xc6g\xa4\x1b\xa2\x98@\x13]\xc9\x1a6\xda\x84~Y\x5c\xcd\xce:\xa43\x09\xcb\xaee\x83\x89\x19L\x04\xc3\xc6:\x98\xb4o\x22bK'\xc64\xac\x9c\x067\x94t\x98\x92P:\xef\xef\xe2M\xa75\x99\xe9\xccCv\xf4\xbf\xba\xe7\xbds\xef\xf9\xdds\xee\x17\xeciO\xaf\xba,\x8a\xb3\x9b,\xb4\x1dN\xac\x0f\xc98\x07\xea\x06:\xaa\xbf\x8a\x88\xdf\xcd,\xfb\xa8t [H\xba\x1b/\x1d\xc0\xcb\xcc\x7f\x82,\x05\x1c\x01\xbb\x8f4\x8bC\x11\xc0\xa4\x0e\xe1\x9c\x02ua<0l\x22w\xa9\xf7\xfb\x97\x02\xf0\xe9\xf5\xeb\xb1\x7fV\xdeL!F\x80\x9f$&\x7f\x1d\xed[\xa8\xe7;\x90\xc9\x9f\x88\x05\x9a\xc28\x0d\x5c\xb9S\xea\x9d$iA\xab\x93\xac+/\xe3O{i\xbf\xf2~f~\xac\xe5>i\x7f\xdcK\xfb\x15/\xed\xa7\x9a\xf9\xee\x9a\x81j\xda\xbf3l,7\xd2;\x0d\xf0\xe1\xd5\xe5\xd7\x9e<\x7f|\xd1\xe03Y\xd0\x15\x0eb\x8b\x18\xd7\xe2\xb1\xf6\x99[\xc3\xc7\x9eU\xc1'\x10\xdf`\x0c\xdd\xb9\xd4\x97\x8d\x0c\xe0&\x0bm\xed\x07\xcb\x7f\x1a\xfa+7\xd2\xff\x11\xc0\x07W\xe7;+\x9b\xceMP\x17X\x00r\xaa\xc3\x84mc1\x16\xd3\x99\xd9\xe1\xfe\x22\xc0{\x99\xfcm\x93\x8e\xac\x96\xe2n\xa3\x85\xe94\x028\x9cX\x1f\x02\xde\x0ad\x97\xb7f^\xd9tnb:\x1ezhG\xdfZ\xbb\xab\xb2\xc9\x8fn\xb2\xd0\x06\xe0\x04\xf6%p\xf4P\xa2|\xb6Q\x9c\x86\x00\xe1Vcak\xc1\x95+\xab\x17@]h\x97\xb2\x09\x03{\xa7\xfd`\xf9\x02@n\xb4\xe7\x9e\xc4\x92At\x00P\xb7\xa1_jf`\xe7\xc3T\xef.A\x00\x9c\xdf\xb2\x0d~\xc68\xf9\x02\x00\xbc.\xacX\xb3L\xee\x7f\xd3^_\x06\x0e\xc8\xdd\x01\xb4\xc2\xf6\x81\x15\x09\x00,\xdaIY7\x80\x99\x11f%2\xc0C\x02:k\x96\xac\xd0j\x09$\x96\xb6mu\x00\x0f\xa3\x03\x88\xdf\x04\xa7\xb6=\xf5m\xab%0\xb3k;>\x0d\x02\xf9\xc8\x00f\x965\xe3\xf8@&\x7f\x02 \x1ek\x9f\xc1X\xc4\xd0.\xd1%\xe3\x8f\xd5R|\x06\xc0\xcb\xccu\x03oc\xfa!2\xc0\xa3\xd2\x81,\xc6\x83X\xa0)\x80[\xc3\xc7\x9e\xc5b:\x03\xdc\xafF\xab\x95\xa3\xba\xf2\x11,TT\xf9\xb8\x90t7\x90\x0c9)`\xf9\xe9\xfe}7\x22\x03\x14\x92\xee\x86\xc48\xc6i/\xed\x8f\x03\xcc\x0e\xf7\x17W\xd7\xe2=\xc0\x17R\x90\x07\xd6\x81u\xa4\xbc\x99>\x7f\xbc\x16\xef\x9b\x1b\x19X\x01\xf0\xd2\xfe$0h\x0a\xc6\xee^<\xf9\xbcQ\x9c\xa6\xf2\xd2~\xaaz\xb1\x8c\xb7\xd4A2oz\xferx\x81\xf9S\xcd\xdc\x9bo\xb3\xa4\x1c/\x91\xff\x1ac\x02\xb8mr&s\xa3=\xf7\xea\xc2f\xe6\xba\xabi\x1f4#\x95[\xeb\xfd\xaa\xd9u\x1c\xe1A\xe2\x9fC\x5c\x01\x8eJ,\x991\x8b\xf17\x00\xe2\x0d\xc2\x1d\xe3\x02\xcb\xa6`,7\xfan\xc3\x85\xf7B\x00\x10\xde\x90\x87\x12\xe5\xb3T\x9fd\x86u\x86\xf1U4\xd9]\x1ce\x9f\xee\xdfw\xe3\x7f\xd5|O{z\xe5\xf4/\x95?G\xacm\xe50s\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x02\x02\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00\x09pHYs\x00\x00\x0d\xd7\x00\x00\x0d\xd7\x01B(\x9bx\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\x01\x7fIDATX\x85\xed\x97\xcbJBQ\x14\x86\xbfe\xa5\xd9\xe5\x01\xacW\xc8@(\xa3\xd2\x9e\x22\x87\xdd\x88\x0663\xa1\x9e\xa1\x896\xa9F]iX\xef\x10\x1c\x8d\xb4@\xa2w\xc8\xe6]\xac,W\x83:\xa2\x1c\xcf$\xb6\x18u\xfe\xd9^\x1b\xf6\xf7\xb1`o\xf6\x82\xff\x1eiZ\xa9J,[X\x14\x95$B\x18\xe85\xc4yA\xb9\x05\xd9\xb1\xd6\xc6\x8f\x10Q\xa7\x80\xaa\xccl\x15\x0fU\x99\x07^\x05J\x8a>\x9a\xa0\x0b2\xa0\x10\x01\x02 \x07Vj|\xd9\x96\xa8\x0b\xc42\x97K\x82\xec\x83\xe6\x91\xee\x84\x95\x1a+\x9b\x80\xdb\x89g\xafC\xe8\xc7)0\xa5\xcaB.=q\x0c\xe0\xab[\xaa$\x81\xd7\xaew\xdf\xaci8\x80\x95\x1a+\xd7\xaa\xd5\x04\xf0&\xc2\xaa]\xaf\x0b \x8c\x08\x94\xce\xd7\xa3\xf7\xa6\xe1v\xf2\x1b\xb1;\xa0\x04\x84\x9d\x02\x10Txn\x17\xbc!O@_+\x81\x8e\xc4\x13\xe8\xb8@\xb7\xdbF<SP\xb7\xbd\x9f\xc4Z\x9b\x90V\xf5\x8ew\xc0\x13\xf0\x04<\x01\xd7w\xc0\xed\xde\x9aN\xc7;\xe0\x09\xfc*\x81\x8a4\xfcT\xda\x98~\xa0\xd2B@o\x15\x22\xf1\xecu\xa8]\xe4\xc9\xcc\xc50\x10\x11\xb8q\x0a\xa8o\x17\x08\xa0\x1fg\xd3\x9b\xb9\xa1v\xc0{\xe8:\x05\xfc5\xd1\x1d\xbb\xde4\x98\xc4\xb3W{\xa0K\xc0\x1bP\x02}0\x83\x97A\xbe\x06\x13\xbf\xc2^.\x15]q\x0c&\xb6D,S\x9c\xfb\xfe\xb7\x8f\x02A3\x02T\x04nTe\xdbJGO\x0c\x9d\xf9G\xf2\x09\xb5\xbdu\x94\xee\x91\xe8\xbe\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x00\x9e\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15\x0f\xfd\x8f\xf8.\x00\x00\x00\x22IDAT\x08\xd7c`\xc0\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1BH*\x0c\x19\x18\x18\x91\x05\x10*\xd1\x00\x00\xca\xb5\x07\xd2v\xbb\xb2\xc5\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x0b\x95\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x0aOiCCPPhotoshop ICC profile\x00\x00x\xda\x9dSgTS\xe9\x16=\xf7\xde\xf4BK\x88\x80\x94KoR\x15\x08 RB\x8b\x80\x14\x91&*!\x09\x10J\x88!\xa1\xd9\x15Q\xc1\x11EE\x04\x1b\xc8\xa0\x88\x03\x8e\x8e\x80\x8c\x15Q,\x0c\x8a\x0a\xd8\x07\xe4!\xa2\x8e\x83\xa3\x88\x8a\xca\xfb\xe1{\xa3k\xd6\xbc\xf7\xe6\xcd\xfe\xb5\xd7>\xe7\xac\xf3\x9d\xb3\xcf\x07\xc0\x08\x0c\x96H3Q5\x80\x0c\xa9B\x1e\x11\xe0\x83\xc7\xc4\xc6\xe1\xe4.@\x81\x0a$p\x00\x10\x08\xb3d!s\xfd#\x01\x00\xf8~<<+\x22\xc0\x07\xbe\x00\x01x\xd3\x0b\x08\x00\xc0M\x9b\xc00\x1c\x87\xff\x0f\xeaB\x99\x5c\x01\x80\x84\x01\xc0t\x918K\x08\x80\x14\x00@z\x8eB\xa6\x00@F\x01\x80\x9d\x98&S\x00\xa0\x04\x00`\xcbcb\xe3\x00P-\x00`'\x7f\xe6\xd3\x00\x80\x9d\xf8\x99{\x01\x00[\x94!\x15\x01\xa0\x91\x00 \x13e\x88D\x00h;\x00\xac\xcfV\x8aE\x00X0\x00\x14fK\xc49\x00\xd8-\x000IWfH\x00\xb0\xb7\x00\xc0\xce\x10\x0b\xb2\x00\x08\x0c\x000Q\x88\x85)\x00\x04{\x00`\xc8##x\x00\x84\x99\x00\x14F\xf2W<\xf1+\xae\x10\xe7*\x00\x00x\x99\xb2<\xb9$9E\x81[\x08-q\x07WW.\x1e(\xceI\x17+\x146a\x02a\x9a@.\xc2y\x99\x192\x814\x0f\xe0\xf3\xcc\x00\x00\xa0\x91\x15\x11\xe0\x83\xf3\xfdx\xce\x0e\xae\xce\xce6\x8e\xb6\x0e_-\xea\xbf\x06\xff\x22bb\xe3\xfe\xe5\xcf\xabp@\x00\x00\xe1t~\xd1\xfe,/\xb3\x1a\x80;\x06\x80m\xfe\xa2%\xee\x04h^\x0b\xa0u\xf7\x8bf\xb2\x0f@\xb5\x00\xa0\xe9\xdaW\xf3p\xf8~<<E\xa1\x90\xb9\xd9\xd9\xe5\xe4\xe4\xd8J\xc4B[a\xcaW}\xfeg\xc2_\xc0W\xfdl\xf9~<\xfc\xf7\xf5\xe0\xbe\xe2$\x812]\x81G\x04\xf8\xe0\xc2\xcc\xf4L\xa5\x1c\xcf\x92\x09\x84b\xdc\xe6\x8fG\xfc\xb7\x0b\xff\xfc\x1d\xd3\x22\xc4Ib\xb9X*\x14\xe3Q\x12q\x8eD\x9a\x8c\xf32\xa5\x22\x89B\x92)\xc5%\xd2\xffd\xe2\xdf,\xfb\x03>\xdf5\x00\xb0j>\x01{\x91-\xa8]c\x03\xf6K'\x10Xt\xc0\xe2\xf7\x00\x00\xf2\xbbo\xc1\xd4(\x08\x03\x80h\x83\xe1\xcfw\xff\xef?\xfdG\xa0%\x00\x80fI\x92q\x00\x00^D$.T\xca\xb3?\xc7\x08\x00\x00D\xa0\x81*\xb0A\x1b\xf4\xc1\x18,\xc0\x06\x1c\xc1\x05\xdc\xc1\x0b\xfc`6\x84B$\xc4\xc2B\x10B\x0ad\x80\x1cr`)\xac\x82B(\x86\xcd\xb0\x1d*`/\xd4@\x1d4\xc0Qh\x86\x93p\x0e.\xc2U\xb8\x0e=p\x0f\xfaa\x08\x9e\xc1(\xbc\x81\x09\x04A\xc8\x08\x13a!\xda\x88\x01b\x8aX#\x8e\x08\x17\x99\x85\xf8!\xc1H\x04\x12\x8b$ \xc9\x88\x14Q\x22K\x915H1R\x8aT UH\x1d\xf2=r\x029\x87\x5cF\xba\x91;\xc8\x002\x82\xfc\x86\xbcG1\x94\x81\xb2Q=\xd4\x0c\xb5C\xb9\xa87\x1a\x84F\xa2\x0b\xd0dt1\x9a\x8f\x16\xa0\x9b\xd0r\xb4\x1a=\x8c6\xa1\xe7\xd0\xabh\x0f\xda\x8f>C\xc70\xc0\xe8\x18\x073\xc4l0.\xc6\xc3B\xb18,\x09\x93c\xcb\xb1\x22\xac\x0c\xab\xc6\x1a\xb0V\xac\x03\xbb\x89\xf5c\xcf\xb1w\x04\x12\x81E\xc0\x096\x04wB a\x1eAHXLXN\xd8H\xa8 \x1c$4\x11\xda\x097\x09\x03\x84Q\xc2'\x22\x93\xa8K\xb4&\xba\x11\xf9\xc4\x18b21\x87XH,#\xd6\x12\x8f\x13/\x10{\x88C\xc47$\x12\x89C2'\xb9\x90\x02I\xb1\xa4T\xd2\x12\xd2F\xd2nR#\xe9,\xa9\x9b4H\x1a#\x93\xc9\xdadk\xb2\x079\x94, +\xc8\x85\xe4\x9d\xe4\xc3\xe43\xe4\x1b\xe4!\xf2[\x0a\x9db@q\xa4\xf8S\xe2(R\xcajJ\x19\xe5\x10\xe54\xe5\x06e\x982AU\xa3\x9aR\xdd\xa8\xa1T\x115\x8fZB\xad\xa1\xb6R\xafQ\x87\xa8\x134u\x9a9\xcd\x83\x16IK\xa5\xad\xa2\x95\xd3\x1ah\x17h\xf7i\xaf\xe8t\xba\x11\xdd\x95\x1eN\x97\xd0W\xd2\xcb\xe9G\xe8\x97\xe8\x03\xf4w\x0c\x0d\x86\x15\x83\xc7\x88g(\x19\x9b\x18\x07\x18g\x19w\x18\xaf\x98L\xa6\x19\xd3\x8b\x19\xc7T071\xeb\x98\xe7\x99\x0f\x99oUX*\xb6*|\x15\x91\xca\x0a\x95J\x95&\x95\x1b*/T\xa9\xaa\xa6\xaa\xde\xaa\x0bU\xf3U\xcbT\x8f\xa9^S}\xaeFU3S\xe3\xa9\x09\xd4\x96\xabU\xaa\x9dP\xebS\x1bSg\xa9;\xa8\x87\xaag\xa8oT?\xa4~Y\xfd\x89\x06Y\xc3L\xc3OC\xa4Q\xa0\xb1_\xe3\xbc\xc6 \x0bc\x19\xb3x,!k\x0d\xab\x86u\x815\xc4&\xb1\xcd\xd9|v*\xbb\x98\xfd\x1d\xbb\x8b=\xaa\xa9\xa19C3J3W\xb3R\xf3\x94f?\x07\xe3\x98q\xf8\x9ctN\x09\xe7(\xa7\x97\xf3~\x8a\xde\x14\xef)\xe2)\x1b\xa64L\xb91e\x5ck\xaa\x96\x97\x96X\xabH\xabQ\xabG\xeb\xbd6\xae\xed\xa7\x9d\xa6\xbdE\xbbY\xfb\x81\x0eA\xc7J'\x5c'Gg\x8f\xce\x05\x9d\xe7S\xd9S\xdd\xa7\x0a\xa7\x16M=:\xf5\xae.\xaak\xa5\x1b\xa1\xbbDw\xbfn\xa7\xee\x98\x9e\xbe^\x80\x9eLo\xa7\xdey\xbd\xe7\xfa\x1c}/\xfdT\xfdm\xfa\xa7\xf5G\x0cX\x06\xb3\x0c$\x06\xdb\x0c\xce\x18<\xc55qo<\x1d/\xc7\xdb\xf1QC]\xc3@C\xa5a\x95a\x97\xe1\x84\x91\xb9\xd1<\xa3\xd5F\x8dF\x0f\x8ci\xc6\x5c\xe3$\xe3m\xc6m\xc6\xa3&\x06&!&KM\xeaM\xee\x9aRM\xb9\xa6)\xa6;L;L\xc7\xcd\xcc\xcd\xa2\xcd\xd6\x995\x9b=1\xd72\xe7\x9b\xe7\x9b\xd7\x9b\xdf\xb7`ZxZ,\xb6\xa8\xb6\xb8eI\xb2\xe4Z\xa6Y\xee\xb6\xbcn\x85Z9Y\xa5XUZ]\xb3F\xad\x9d\xad%\xd6\xbb\xad\xbb\xa7\x11\xa7\xb9N\x93N\xab\x9e\xd6g\xc3\xb0\xf1\xb6\xc9\xb6\xa9\xb7\x19\xb0\xe5\xd8\x06\xdb\xae\xb6m\xb6}agb\x17g\xb7\xc5\xae\xc3\xee\x93\xbd\x93}\xba}\x8d\xfd=\x07\x0d\x87\xd9\x0e\xab\x1dZ\x1d~s\xb4r\x14:V:\xde\x9a\xce\x9c\xee?}\xc5\xf4\x96\xe9/gX\xcf\x10\xcf\xd83\xe3\xb6\x13\xcb)\xc4i\x9dS\x9b\xd3Gg\x17g\xb9s\x83\xf3\x88\x8b\x89K\x82\xcb.\x97>.\x9b\x1b\xc6\xdd\xc8\xbd\xe4Jt\xf5q]\xe1z\xd2\xf5\x9d\x9b\xb3\x9b\xc2\xed\xa8\xdb\xaf\xee6\xeei\xee\x87\xdc\x9f\xcc4\x9f)\x9eY3s\xd0\xc3\xc8C\xe0Q\xe5\xd1?\x0b\x9f\x950k\xdf\xac~OCO\x81g\xb5\xe7#/c/\x91W\xad\xd7\xb0\xb7\xa5w\xaa\xf7a\xef\x17>\xf6>r\x9f\xe3>\xe3<7\xde2\xdeY_\xcc7\xc0\xb7\xc8\xb7\xcbO\xc3o\x9e_\x85\xdfC\x7f#\xffd\xffz\xff\xd1\x00\xa7\x80%\x01g\x03\x89\x81A\x81[\x02\xfb\xf8z|!\xbf\x8e?:\xdbe\xf6\xb2\xd9\xedA\x8c\xa0\xb9A\x15A\x8f\x82\xad\x82\xe5\xc1\xad!h\xc8\xec\x90\xad!\xf7\xe7\x98\xce\x91\xcei\x0e\x85P~\xe8\xd6\xd0\x07a\xe6a\x8b\xc3~\x0c'\x85\x87\x85W\x86?\x8ep\x88X\x1a\xd11\x975w\xd1\xdcCs\xdfD\xfaD\x96D\xde\x9bg1O9\xaf-J5*>\xaa.j<\xda7\xba4\xba?\xc6.fY\xcc\xd5X\x9dXIlK\x1c9.*\xae6nl\xbe\xdf\xfc\xed\xf3\x87\xe2\x9d\xe2\x0b\xe3{\x17\x98/\xc8]py\xa1\xce\xc2\xf4\x85\xa7\x16\xa9.\x12,:\x96@L\x88N8\x94\xf0A\x10*\xa8\x16\x8c%\xf2\x13w%\x8e\x0ay\xc2\x1d\xc2g\x22/\xd16\xd1\x88\xd8C\x5c*\x1eN\xf2H*Mz\x92\xec\x91\xbc5y$\xc53\xa5,\xe5\xb9\x84'\xa9\x90\xbcL\x0dL\xdd\x9b:\x9e\x16\x9av m2=:\xbd1\x83\x92\x91\x90qB\xaa!M\x93\xb6g\xeag\xe6fv\xcb\xace\x85\xb2\xfe\xc5n\x8b\xb7/\x1e\x95\x07\xc9k\xb3\x90\xac\x05Y-\x0a\xb6B\xa6\xe8TZ(\xd7*\x07\xb2geWf\xbf\xcd\x89\xca9\x96\xab\x9e+\xcd\xed\xcc\xb3\xca\xdb\x907\x9c\xef\x9f\xff\xed\x12\xc2\x12\xe1\x92\xb6\xa5\x86KW-\x1dX\xe6\xbd\xacj9\xb2<qy\xdb\x0a\xe3\x15\x05+\x86V\x06\xac<\xb8\x8a\xb6*m\xd5O\xab\xedW\x97\xae~\xbd&zMk\x81^\xc1\xca\x82\xc1\xb5\x01k\xeb\x0bU\x0a\xe5\x85}\xeb\xdc\xd7\xed]OX/Y\xdf\xb5a\xfa\x86\x9d\x1b>\x15\x89\x8a\xae\x14\xdb\x17\x97\x15\x7f\xd8(\xdcx\xe5\x1b\x87o\xca\xbf\x99\xdc\x94\xb4\xa9\xab\xc4\xb9d\xcff\xd2f\xe9\xe6\xde-\x9e[\x0e\x96\xaa\x97\xe6\x97\x0en\x0d\xd9\xda\xb4\x0d\xdfV\xb4\xed\xf5\xf6E\xdb/\x97\xcd(\xdb\xbb\x83\xb6C\xb9\xa3\xbf<\xb8\xbce\xa7\xc9\xce\xcd;?T\xa4T\xf4T\xfaT6\xee\xd2\xdd\xb5a\xd7\xf8n\xd1\xee\x1b{\xbc\xf64\xec\xd5\xdb[\xbc\xf7\xfd>\xc9\xbe\xdbU\x01UM\xd5f\xd5e\xfbI\xfb\xb3\xf7?\xae\x89\xaa\xe9\xf8\x96\xfbm]\xadNmq\xed\xc7\x03\xd2\x03\xfd\x07#\x0e\xb6\xd7\xb9\xd4\xd5\x1d\xd2=TR\x8f\xd6+\xebG\x0e\xc7\x1f\xbe\xfe\x9d\xefw-\x0d6\x0dU\x8d\x9c\xc6\xe2#pDy\xe4\xe9\xf7\x09\xdf\xf7\x1e\x0d:\xdav\x8c{\xac\xe1\x07\xd3\x1fv\x1dg\x1d/jB\x9a\xf2\x9aF\x9bS\x9a\xfb[b[\xbaO\xcc>\xd1\xd6\xea\xdez\xfcG\xdb\x1f\x0f\x9c4<YyJ\xf3T\xc9i\xda\xe9\x82\xd3\x93g\xf2\xcf\x8c\x9d\x95\x9d}~.\xf9\xdc`\xdb\xa2\xb6{\xe7c\xce\xdfj\x0fo\xef\xba\x10t\xe1\xd2E\xff\x8b\xe7;\xbc;\xce\x5c\xf2\xb8t\xf2\xb2\xdb\xe5\x13W\xb8W\x9a\xaf:_m\xeat\xea<\xfe\x93\xd3O\xc7\xbb\x9c\xbb\x9a\xae\xb9\x5ck\xb9\xeez\xbd\xb5{f\xf7\xe9\x1b\x9e7\xce\xdd\xf4\xbdy\xf1\x16\xff\xd6\xd5\x9e9=\xdd\xbd\xf3zo\xf7\xc5\xf7\xf5\xdf\x16\xdd~r'\xfd\xce\xcb\xbb\xd9w'\xee\xad\xbcO\xbc_\xf4@\xedA\xd9C\xdd\x87\xd5?[\xfe\xdc\xd8\xef\xdc\x7fj\xc0w\xa0\xf3\xd1\xdcG\xf7\x06\x85\x83\xcf\xfe\x91\xf5\x8f\x0fC\x05\x8f\x99\x8f\xcb\x86\x0d\x86\xeb\x9e8>99\xe2?r\xfd\xe9\xfc\xa7C\xcfd\xcf&\x9e\x17\xfe\xa2\xfe\xcb\xae\x17\x16/~\xf8\xd5\xeb\xd7\xce\xd1\x98\xd1\xa1\x97\xf2\x97\x93\xbfm|\xa5\xfd\xea\xc0\xeb\x19\xaf\xdb\xc6\xc2\xc6\x1e\xbe\xc9x31^\xf4V\xfb\xed\xc1w\xdcw\x1d\xef\xa3\xdf\x0fO\xe4| \x7f(\xffh\xf9\xb1\xf5S\xd0\xa7\xfb\x93\x19\x93\x93\xff\x04\x03\x98\xf3\xfcc3-\xdb\x00\x00\x00 cHRM\x00\x00z%\x00\x00\x80\x83\x00\x00\xf9\xff\x00\x00\x80\xe9\x00\x00u0\x00\x00\xea`\x00\x00:\x98\x00\x00\x17o\x92_\xc5F\x00\x00\x00\xc0IDATx\xda\xdcVA\x0e\x830\x0c\xab\xa3\xfe\xff=\xfb\x9dw\x021ThJ\xe2 -'\xd4\x03vl'-\xda\xa7\x1d\x8b\xad\xa6\xb0}\xf4b\xe0s\xa3\xb0\x17\xc0\x7f\x88\xf4\x99D\xa2\xce\xf7\xb2B\xf0\xe1\xbfM\x0c\xceA\xd7\x98)\xa0\x90\xfb2gV\xe5\xf5\x85\x1aR\x05\x5ceE\xdd\xbbCX\x0a\xfew\x16,w\x9fI\xe0\x11x\x16\x01*-`\x10\x00\x11\x02\x9eM\xc6\x08\xf8\x1d\x01:\xce\xa8\x9a\x02&\xf8\x8d\x08\x01\x04s\x81\x8c\x10*\xdf\x04\xee\x10B\x91\xfa\xd51\x84\x12\xdc\xbb\x88\xf0\x96\x05+$\xa0&p\x07\x82\x0a\x05dv\xf4\xc1\x8cCL\x82\x91M\x98~sv\xc5\x15\xbb\x9a\x81\xb2\xad7\xb2\xd3\xaaW\xef9K\xdf\x01\x00h\x95#\xfe/d\x9d\xea\x00\x00\x00\x00IEND\xaeB`\x82"
 qt_resource_name = b"\x00\x09\x09_\x97\x13\x00q\x00s\x00s\x00_\x00i\x00c\x00o\x00n\x00s\x00\x0a\x09$M%\x00q\x00d\x00a\x00r\x00k\x00s\x00t\x00y\x00l\x00e\x00\x09\x00(\xad#\x00s\x00t\x00y\x00l\x00e\x00.\x00q\x00s\x00s\x00\x02\x00\x00\x07\x83\x00r\x00c\x00\x11\x0a\xe5l\x07\x00r\x00a\x00d\x00i\x00o\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00.\x00p\x00n\x00g\x00\x09\x06\x98\x83'\x00c\x00l\x00o\x00s\x00e\x00.\x00p\x00n\x00g\x00\x11\x08\x8cj\xa7\x00H\x00s\x00e\x00p\x00a\x00r\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00.\x00p\x00n\x00g\x00\x1a\x01!\xebG\x00s\x00t\x00y\x00l\x00e\x00s\x00h\x00e\x00e\x00t\x00-\x00b\x00r\x00a\x00n\x00c\x00h\x00-\x00m\x00o\x00r\x00e\x00.\x00p\x00n\x00g\x00\x0a\x05\x95\xde'\x00u\x00n\x00d\x00o\x00c\x00k\x00.\x00p\x00n\x00g\x00\x13\x08\xc8\x96\xe7\x00r\x00a\x00d\x00i\x00o\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00.\x00p\x00n\x00g\x00\x15\x0f\xf3\xc0\x07\x00u\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\x00\x1f\x0a\xae'G\x00c\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\x00\x0f\x0c\xe2hg\x00t\x00r\x00a\x00n\x00s\x00p\x00a\x00r\x00e\x00n\x00t\x00.\x00p\x00n\x00g\x00\x16\x01u\xcc\x87\x00c\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00.\x00p\x00n\x00g\x00\x14\x0b\xc5\xd7\xc7\x00s\x00t\x00y\x00l\x00e\x00s\x00h\x00e\x00e\x00t\x00-\x00v\x00l\x00i\x00n\x00e\x00.\x00p\x00n\x00g\x00\x11\x08\x90\x94g\x00c\x00l\x00o\x00s\x00e\x00-\x00p\x00r\x00e\x00s\x00s\x00e\x00d\x00.\x00p\x00n\x00g\x00\x14\x07\xec\xd1\xc7\x00c\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00.\x00p\x00n\x00g\x00\x0e\x0e\xde\xfa\xc7\x00l\x00e\x00f\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\x00\x12\x07\x8f\x9d'\x00b\x00r\x00a\x00n\x00c\x00h\x00_\x00o\x00p\x00e\x00n\x00-\x00o\x00n\x00.\x00p\x00n\x00g\x00\x0f\x02\x9f\x05\x87\x00r\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\x00\x0e\x04\xa2\xfc\xa7\x00d\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\x00\x11\x08\xc4j\xa7\x00V\x00s\x00e\x00p\x00a\x00r\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00.\x00p\x00n\x00g\x00\x10\x01\x07J\xa7\x00V\x00m\x00o\x00v\x00e\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00.\x00p\x00n\x00g\x00\x19\x08>\xcc\x07\x00s\x00t\x00y\x00l\x00e\x00s\x00h\x00e\x00e\x00t\x00-\x00b\x00r\x00a\x00n\x00c\x00h\x00-\x00e\x00n\x00d\x00.\x00p\x00n\x00g\x00\x1c\x01\xe0J\x07\x00r\x00a\x00d\x00i\x00o\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\x00\x14\x06^,\x07\x00b\x00r\x00a\x00n\x00c\x00h\x00_\x00c\x00l\x00o\x00s\x00e\x00d\x00-\x00o\x00n\x00.\x00p\x00n\x00g\x00\x0f\x06S%\xa7\x00b\x00r\x00a\x00n\x00c\x00h\x00_\x00o\x00p\x00e\x00n\x00.\x00p\x00n\x00g\x00\x0c\x06A@\x87\x00s\x00i\x00z\x00e\x00g\x00r\x00i\x00p\x00.\x00p\x00n\x00g\x00\x10\x01\x00\xca\xa7\x00H\x00m\x00o\x00v\x00e\x00t\x00o\x00o\x00l\x00b\x00a\x00r\x00.\x00p\x00n\x00g\x00\x1c\x08?\xdag\x00c\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\x00f\x00o\x00c\x00u\x00s\x00.\x00p\x00n\x00g\x00\x0f\x01\xf4\x81G\x00c\x00l\x00o\x00s\x00e\x00-\x00h\x00o\x00v\x00e\x00r\x00.\x00p\x00n\x00g\x00\x18\x03\x8e\xdeg\x00r\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\x00\x1a\x0e\xbc\xc3g\x00r\x00a\x00d\x00i\x00o\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\x00\x17\x0c\xabQ\x07\x00d\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\x00\x11\x0b\xda0\xa7\x00b\x00r\x00a\x00n\x00c\x00h\x00_\x00c\x00l\x00o\x00s\x00e\x00d\x00.\x00p\x00n\x00g\x00\x1a\x01\x87\xaeg\x00c\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00i\x00n\x00d\x00e\x00t\x00e\x00r\x00m\x00i\x00n\x00a\x00t\x00e\x00.\x00p\x00n\x00g\x00\x17\x0ce\xce\x07\x00l\x00e\x00f\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\x00\x19\x0bYn\x87\x00r\x00a\x00d\x00i\x00o\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\x00f\x00o\x00c\x00u\x00s\x00.\x00p\x00n\x00g\x00\x1a\x05\x11\xe0\xe7\x00c\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\x00f\x00o\x00c\x00u\x00s\x00.\x00p\x00n\x00g\x00\x17\x0f\x1e\x9bG\x00r\x00a\x00d\x00i\x00o\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\x00f\x00o\x00c\x00u\x00s\x00.\x00p\x00n\x00g\x00 \x09\xd7\x1f\xa7\x00c\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00i\x00n\x00d\x00e\x00t\x00e\x00r\x00m\x00i\x00n\x00a\x00t\x00e\x00_\x00f\x00o\x00c\x00u\x00s\x00.\x00p\x00n\x00g\x00\x0c\x06\xe6\xe6g\x00u\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\x00\x1d\x09\x07\x81\x07\x00c\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g"
 qt_resource_struct = b"\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x18\x00\x02\x00\x00\x00\x01\x00\x00\x00+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00J\x00\x02\x00\x00\x00'\x00\x00\x00\x04\x00\x00\x04P\x00\x00\x00\x00\x00\x01\x00\x00\xa2\x0d\x00\x00\x03D\x00\x00\x00\x00\x00\x01\x00\x00\x9b\xa3\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x00k\x87\x00\x00\x01\xd4\x00\x00\x00\x00\x00\x01\x00\x00~\x1b\x00\x00\x05\xa4\x00\x00\x00\x00\x00\x01\x00\x00\xb64\x00\x00\x03\xa2\x00\x00\x00\x00\x00\x01\x00\x00\x9do\x00\x00\x04\xb4\x00\x00\x00\x00\x00\x01\x00\x00\xae?\x00\x00\x02\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x99\x97\x00\x00\x04\xd8\x00\x00\x00\x00\x00\x01\x00\x00\xb0\x99\x00\x00\x02\xfa\x00\x00\x00\x00\x00\x01\x00\x00\x9a;\x00\x00\x06J\x00\x00\x00\x00\x00\x01\x00\x00\xbb\xa7\x00\x00\x00\xf6\x00\x00\x00\x00\x00\x01\x00\x00lA\x00\x00\x042\x00\x00\x00\x00\x00\x01\x00\x00\xa1\x88\x00\x00\x04\x0e\x00\x00\x00\x00\x00\x01\x00\x00\xa0\xde\x00\x00\x03\xe0\x00\x00\x00\x00\x00\x01\x00\x00\xa0G\x00\x00\x00|\x00\x00\x00\x00\x00\x01\x00\x00h\x89\x00\x00\x06\xfe\x00\x00\x00\x00\x00\x01\x00\x00\xcc\xef\x00\x00\x02\xac\x00\x00\x00\x00\x00\x01\x00\x00\x98\xfd\x00\x00\x02\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x8c\xba\x00\x00\x03j\x00\x00\x00\x00\x00\x01\x00\x00\x9c\x8b\x00\x00\x04v\x00\x00\x00\x00\x00\x01\x00\x00\xa2\xed\x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x00j\xd7\x00\x00\x024\x00\x00\x00\x00\x00\x01\x00\x00\x8a`\x00\x00\x03\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x9a\xe4\x00\x00\x01\x10\x00\x00\x00\x00\x00\x01\x00\x00n\x87\x00\x00\x07\x1c\x00\x00\x00\x00\x00\x01\x00\x00\xcd\x91\x00\x00\x06\xb8\x00\x00\x00\x00\x00\x01\x00\x00\xca\xe9\x00\x00\x01l\x00\x00\x00\x00\x00\x01\x00\x00r\x02\x00\x00\x00T\x00\x00\x00\x00\x00\x01\x00\x00d\xe0\x00\x00\x06\x12\x00\x00\x00\x00\x00\x01\x00\x00\xb8\xcf\x00\x00\x02\x06\x00\x00\x00\x00\x00\x01\x00\x00\x89m\x00\x00\x05|\x00\x00\x00\x00\x00\x01\x00\x00\xb5\x90\x00\x00\x05\xde\x00\x00\x00\x00\x00\x01\x00\x00\xb8%\x00\x00\x05H\x00\x00\x00\x00\x00\x01\x00\x00\xb4\xe6\x00\x00\x01\xb0\x00\x00\x00\x00\x00\x01\x00\x00}T\x00\x00\x05\x0e\x00\x00\x00\x00\x00\x01\x00\x00\xb1=\x00\x00\x02\x8a\x00\x00\x00\x00\x00\x01\x00\x00\x98S\x00\x00\x06\x84\x00\x00\x00\x00\x00\x01\x00\x00\xc7@\x00\x00\x01<\x00\x00\x00\x00\x00\x01\x00\x00q_\x00\x00\x002\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00"
-def qInitResources() -> None:
+def qInitResources():
     QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
 
-def qCleanupResources() -> None:
+def qCleanupResources():
     QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
 
 qInitResources()
diff --git a/toxygen/styles/style.qrc b/toxygen/styles/style.qrc
index 7ceed90..ac14bc5 100644
--- a/toxygen/styles/style.qrc
+++ b/toxygen/styles/style.qrc
@@ -41,9 +41,6 @@
     <file>rc/radio_unchecked.png</file>
   </qresource>
   <qresource prefix="qdarkstyle">
-      <file>dark_style.qss</file>
-  </qresource>
-  <qresource prefix="defaultstyle">
       <file>style.qss</file>
   </qresource>
 </RCC>
diff --git a/toxygen/styles/style.qss b/toxygen/styles/style.qss
index ff9f614..0eddfde 100644
--- a/toxygen/styles/style.qss
+++ b/toxygen/styles/style.qss
@@ -1,6 +1,1216 @@
-#searchLineEdit
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) <2013-2014> <Colin Duquesnoy>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+QToolTip
 {
-    padding-left: 22px;
+    border: 1px solid #3A3939;
+    background-color: rgb(90, 102, 117);;
+    color: white;
+    padding: 1px;
+    opacity: 200;
+}
+
+QWidget
+{
+    color: silver;
+    background-color: #302F2F;
+    selection-background-color: #A9A9A9;
+    selection-color: black;
+    background-clip: border;
+    border-image: none;
+    outline: 0;
+}
+
+QWidget:item:hover
+{
+    background-color: #78879b;
+    color: black;
+}
+
+QWidget:item:selected
+{
+    background-color: #A9A9A9;
+}
+
+QProgressBar:horizontal {
+    border: 1px solid #3A3939;
+    text-align: center;
+    padding: 1px;
+    background: #201F1F;
+}
+QProgressBar::chunk:horizontal {
+    background-color: qlineargradient(spread:reflect, x1:1, y1:0.545, x2:1, y2:0, stop:0 rgba(28, 66, 111, 255), stop:1 rgba(37, 87, 146, 255));
+}
+
+QCheckBox:disabled
+{
+    color: #777777;
+}
+QCheckBox::indicator,
+QGroupBox::indicator
+{
+    width: 18px;
+    height: 18px;
+}
+QGroupBox::indicator
+{
+    margin-left: 2px;
+}
+
+QCheckBox::indicator:unchecked,
+QCheckBox::indicator:unchecked:hover,
+QGroupBox::indicator:unchecked,
+QGroupBox::indicator:unchecked:hover
+{
+    image: url(:/qss_icons/rc/checkbox_unchecked.png);
+}
+
+QCheckBox::indicator:unchecked:focus,
+QCheckBox::indicator:unchecked:pressed,
+QGroupBox::indicator:unchecked:focus,
+QGroupBox::indicator:unchecked:pressed
+{
+  border: none;
+    image: url(:/qss_icons/rc/checkbox_unchecked_focus.png);
+}
+
+QCheckBox::indicator:checked,
+QCheckBox::indicator:checked:hover,
+QGroupBox::indicator:checked,
+QGroupBox::indicator:checked:hover
+{
+    image: url(:/qss_icons/rc/checkbox_checked.png);
+}
+
+QCheckBox::indicator:checked:focus,
+QCheckBox::indicator:checked:pressed,
+QGroupBox::indicator:checked:focus,
+QGroupBox::indicator:checked:pressed
+{
+  border: none;
+    image: url(:/qss_icons/rc/checkbox_checked_focus.png);
+}
+
+QCheckBox::indicator:indeterminate,
+QCheckBox::indicator:indeterminate:hover,
+QCheckBox::indicator:indeterminate:pressed
+QGroupBox::indicator:indeterminate,
+QGroupBox::indicator:indeterminate:hover,
+QGroupBox::indicator:indeterminate:pressed
+{
+    image: url(:/qss_icons/rc/checkbox_indeterminate.png);
+}
+
+QCheckBox::indicator:indeterminate:focus,
+QGroupBox::indicator:indeterminate:focus
+{
+    image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png);
+}
+
+QCheckBox::indicator:checked:disabled,
+QGroupBox::indicator:checked:disabled
+{
+    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);
+}
+
+QCheckBox::indicator:unchecked:disabled,
+QGroupBox::indicator:unchecked:disabled
+{
+    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);
+}
+
+QRadioButton
+{
+    spacing: 5px;
+    outline: none;
+    color: #bbb;
+    margin-bottom: 2px;
+}
+
+QRadioButton:disabled
+{
+    color: #777777;
+}
+QRadioButton::indicator
+{
+    width: 21px;
+    height: 21px;
+}
+
+QRadioButton::indicator:unchecked,
+QRadioButton::indicator:unchecked:hover
+{
+    image: url(:/qss_icons/rc/radio_unchecked.png);
+}
+
+QRadioButton::indicator:unchecked:focus,
+QRadioButton::indicator:unchecked:pressed
+{
+  border: none;
+  outline: none;
+    image: url(:/qss_icons/rc/radio_unchecked_focus.png);
+}
+
+QRadioButton::indicator:checked,
+QRadioButton::indicator:checked:hover
+{
+  border: none;
+  outline: none;
+    image: url(:/qss_icons/rc/radio_checked.png);
+}
+
+QRadioButton::indicator:checked:focus,
+QRadioButton::indicato::menu-arrowr:checked:pressed
+{
+  border: none;
+  outline: none;
+    image: url(:/qss_icons/rc/radio_checked_focus.png);
+}
+
+QRadioButton::indicator:indeterminate,
+QRadioButton::indicator:indeterminate:hover,
+QRadioButton::indicator:indeterminate:pressed
+{
+        image: url(:/qss_icons/rc/radio_indeterminate.png);
+}
+
+QRadioButton::indicator:checked:disabled
+{
+  outline: none;
+  image: url(:/qss_icons/rc/radio_checked_disabled.png);
+}
+
+QRadioButton::indicator:unchecked:disabled
+{
+    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);
+}
+
+
+QMenuBar
+{
+    background-color: #302F2F;
+    color: silver;
+}
+
+QMenuBar::item
+{
+    background: transparent;
+}
+
+QMenuBar::item:selected
+{
+    background: transparent;
+    border: 1px solid #A9A9A9;
+}
+
+QMenuBar::item:pressed
+{
+    border: 1px solid #3A3939;
+    background-color: #A9A9A9;
+    color: black;
+    margin-bottom:-1px;
+    padding-bottom:1px;
+}
+
+QMenu
+{
+    border: 1px solid #3A3939;
+    color: silver;
+    margin: 2px;
+}
+
+QMenu::icon
+{
+    margin: 5px;
+}
+
+QMenu::item
+{
+    padding: 5px 30px 5px 30px;
+    margin-left: 5px;
+    border: 1px solid transparent; /* reserve space for selection border */
+}
+
+QMenu::item:selected
+{
+    color: black;
+}
+
+QMenu::separator {
+    height: 2px;
+    background: lightblue;
+    margin-left: 10px;
+    margin-right: 5px;
+}
+
+QMenu::indicator {
+    width: 18px;
+    height: 18px;
+}
+
+/* non-exclusive indicator = check box style indicator
+   (see QActionGroup::setExclusive) */
+QMenu::indicator:non-exclusive:unchecked {
+    image: url(:/qss_icons/rc/checkbox_unchecked.png);
+}
+
+QMenu::indicator:non-exclusive:unchecked:selected {
+    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);
+}
+
+QMenu::indicator:non-exclusive:checked {
+    image: url(:/qss_icons/rc/checkbox_checked.png);
+}
+
+QMenu::indicator:non-exclusive:checked:selected {
+    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);
+}
+
+/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */
+QMenu::indicator:exclusive:unchecked {
+    image: url(:/qss_icons/rc/radio_unchecked.png);
+}
+
+QMenu::indicator:exclusive:unchecked:selected {
+    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);
+}
+
+QMenu::indicator:exclusive:checked {
+    image: url(:/qss_icons/rc/radio_checked.png);
+}
+
+QMenu::indicator:exclusive:checked:selected {
+    image: url(:/qss_icons/rc/radio_checked_disabled.png);
+}
+
+QMenu::right-arrow {
+    margin: 5px;
+    image: url(:/qss_icons/rc/right_arrow.png)
+}
+
+
+QWidget:disabled
+{
+    color: #404040;
+    background-color: #302F2F;
+}
+
+QAbstractItemView
+{
+    alternate-background-color: #3A3939;
+    color: silver;
+    border: 1px solid 3A3939;
+    border-radius: 2px;
+    padding: 1px;
+}
+
+QWidget:focus, QMenuBar:focus
+{
+    border: 1px solid #78879b;
+}
+
+QTabWidget:focus, QCheckBox:focus, QRadioButton:focus, QSlider:focus
+{
+    border: none;
+}
+
+QLineEdit
+{
+    background-color: #201F1F;
+    padding: 2px;
+    border-style: solid;
+    border: 1px solid #3A3939;
+    border-radius: 2px;
+    color: silver;
+}
+
+QGroupBox {
+    border:1px solid #3A3939;
+    border-radius: 2px;
+    margin-top: 20px;
+}
+
+QGroupBox::title {
+    subcontrol-origin: margin;
+    subcontrol-position: top center;
+    padding-left: 10px;
+    padding-right: 10px;
+    padding-top: 10px;
+}
+
+QAbstractScrollArea
+{
+    border-radius: 2px;
+    border: 1px solid #3A3939;
+    background-color: transparent;
+}
+
+QScrollBar:horizontal
+{
+    height: 15px;
+    margin: 3px 15px 3px 15px;
+    border: 1px transparent #2A2929;
+    border-radius: 4px;
+    background-color: #2A2929;
+}
+
+QScrollBar::handle:horizontal
+{
+    background-color: #605F5F;
+    min-width: 5px;
+    border-radius: 4px;
+}
+
+QScrollBar::add-line:horizontal
+{
+    margin: 0px 3px 0px 3px;
+    border-image: url(:/qss_icons/rc/right_arrow_disabled.png);
+    width: 10px;
+    height: 10px;
+    subcontrol-position: right;
+    subcontrol-origin: margin;
+}
+
+QScrollBar::sub-line:horizontal
+{
+    margin: 0px 3px 0px 3px;
+    border-image: url(:/qss_icons/rc/left_arrow_disabled.png);
+    height: 10px;
+    width: 10px;
+    subcontrol-position: left;
+    subcontrol-origin: margin;
+}
+
+QScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on
+{
+    border-image: url(:/qss_icons/rc/right_arrow.png);
+    height: 10px;
+    width: 10px;
+    subcontrol-position: right;
+    subcontrol-origin: margin;
+}
+
+
+QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on
+{
+    border-image: url(:/qss_icons/rc/left_arrow.png);
+    height: 10px;
+    width: 10px;
+    subcontrol-position: left;
+    subcontrol-origin: margin;
+}
+
+QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal
+{
+    background: none;
+}
+
+
+QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal
+{
+    background: none;
+}
+
+QScrollBar:vertical
+{
+    background-color: #2A2929;
+    width: 15px;
+    margin: 15px 3px 15px 3px;
+    border: 1px transparent #2A2929;
+    border-radius: 4px;
+}
+
+QScrollBar::handle:vertical
+{
+    background-color: #605F5F;
+    min-height: 5px;
+    border-radius: 4px;
+}
+
+QScrollBar::sub-line:vertical
+{
+    margin: 3px 0px 3px 0px;
+    border-image: url(:/qss_icons/rc/up_arrow_disabled.png);
+    height: 10px;
+    width: 10px;
+    subcontrol-position: top;
+    subcontrol-origin: margin;
+}
+
+QScrollBar::add-line:vertical
+{
+    margin: 3px 0px 3px 0px;
+    border-image: url(:/qss_icons/rc/down_arrow_disabled.png);
+    height: 10px;
+    width: 10px;
+    subcontrol-position: bottom;
+    subcontrol-origin: margin;
+}
+
+QScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on
+{
+
+    border-image: url(:/qss_icons/rc/up_arrow.png);
+    height: 10px;
+    width: 10px;
+    subcontrol-position: top;
+    subcontrol-origin: margin;
+}
+
+
+QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on
+{
+    border-image: url(:/qss_icons/rc/down_arrow.png);
+    height: 10px;
+    width: 10px;
+    subcontrol-position: bottom;
+    subcontrol-origin: margin;
+}
+
+QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical
+{
+    background: none;
+}
+
+
+QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
+{
+    background: none;
+}
+
+QTextEdit
+{
+    background-color: #201F1F;
+    color: silver;
+    border: 1px solid #3A3939;
+}
+
+QPlainTextEdit
+{
+    background-color: #201F1F;;
+    color: silver;
+    border-radius: 2px;
+    border: 1px solid #3A3939;
+}
+
+QHeaderView::section
+{
+    background-color: #3A3939;
+    color: silver;
+    padding-left: 4px;
+    border: 1px solid #6c6c6c;
+}
+
+QSizeGrip {
+    image: url(:/qss_icons/rc/sizegrip.png);
+    width: 12px;
+    height: 12px;
+}
+
+
+QMainWindow::separator
+{
+    background-color: #302F2F;
+    color: white;
+    padding-left: 4px;
+    spacing: 2px;
+    border: 1px dashed #3A3939;
+}
+
+QMainWindow::separator:hover
+{
+
+    background-color: #787876;
+    color: white;
+    padding-left: 4px;
+    border: 1px solid #3A3939;
+    spacing: 2px;
+}
+
+
+QMenu::separator
+{
+    height: 1px;
+    background-color: #3A3939;
+    color: white;
+    padding-left: 4px;
+    margin-left: 10px;
+    margin-right: 5px;
+}
+
+
+QFrame
+{
+    border-radius: 2px;
+    border: 1px solid #444;
+}
+
+QFrame[frameShape="0"]
+{
+    border-radius: 2px;
+    border: 1px transparent #444;
+}
+
+QStackedWidget
+{
+    border: 1px transparent black;
+}
+
+QToolBar {
+    border: 1px transparent #393838;
+    background: 1px solid #302F2F;
+    font-weight: bold;
+}
+
+QToolBar::handle:horizontal {
+    image: url(:/qss_icons/rc/Hmovetoolbar.png);
+}
+QToolBar::handle:vertical {
+    image: url(:/qss_icons/rc/Vmovetoolbar.png);
+}
+QToolBar::separator:horizontal {
+    image: url(:/qss_icons/rc/Hsepartoolbar.png);
+}
+QToolBar::separator:vertical {
+    image: url(:/qss_icons/rc/Vsepartoolbars.png);
+}
+
+QPushButton
+{
+    color: silver;
+    background-color: #302F2F;
+    border-width: 1px;
+    border-color: #4A4949;
+    border-style: solid;
+    padding-top: 5px;
+    padding-bottom: 5px;
+    padding-left: 5px;
+    padding-right: 5px;
+    border-radius: 2px;
+    outline: none;
+}
+
+QPushButton:focus
+{
+    border-width: 1px;
+    border-color: #4A4949;
+    border-style: solid;
+}
+
+QPushButton:disabled
+{
+    background-color: #302F2F;
+    border-width: 1px;
+    border-color: #3A3939;
+    border-style: solid;
+    padding-top: 5px;
+    padding-bottom: 5px;
+    padding-left: 10px;
+    padding-right: 10px;
+    /*border-radius: 2px;*/
+    color: #454545;
+}
+
+QComboBox
+{
+    selection-background-color: #A9A9A9;
+    background-color: #201F1F;
+    border-style: solid;
+    border: 1px solid #3A3939;
+    border-radius: 2px;
+    padding: 2px;
+    min-width: 75px;
+}
+
+QPushButton:hover
+{
+    background-color: #3d8ec9;
+    color: white;
+}
+
+QComboBox:hover,QAbstractSpinBox:hover,QLineEdit:hover,QTextEdit:hover,QPlainTextEdit:hover,QAbstractView:hover,QTreeView:hover
+{
+    border: 1px solid #78879b;
+    color: silver;
+}
+
+QComboBox:on
+{
+    background-color: #626873;
+    padding-top: 3px;
+    padding-left: 4px;
+    selection-background-color: #4a4a4a;
+}
+
+QComboBox QAbstractItemView
+{
+    background-color: #201F1F;
+    border-radius: 2px;
+    border: 1px solid #444;
+    selection-background-color: #A9A9A9;
+}
+
+QComboBox::drop-down
+{
+    subcontrol-origin: padding;
+    subcontrol-position: top right;
+    width: 15px;
+
+    border-left-width: 0px;
+    border-left-color: darkgray;
+    border-left-style: solid;
+    border-top-right-radius: 3px;
+    border-bottom-right-radius: 3px;
+}
+
+QComboBox::down-arrow
+{
+    image: url(:/qss_icons/rc/down_arrow_disabled.png);
+}
+
+QComboBox::down-arrow:on, QComboBox::down-arrow:hover,
+QComboBox::down-arrow:focus
+{
+    image: url(:/qss_icons/rc/down_arrow.png);
+}
+
+QAbstractSpinBox {
+    padding-top: 2px;
+    padding-bottom: 2px;
+    border: 1px solid #3A3939;
+    background-color: #201F1F;
+    color: silver;
+    border-radius: 2px;
+    min-width: 75px;
+}
+
+QAbstractSpinBox:up-button
+{
+    background-color: transparent;
+    subcontrol-origin: border;
+    subcontrol-position: center right;
+}
+
+QAbstractSpinBox:down-button
+{
+    background-color: transparent;
+    subcontrol-origin: border;
+    subcontrol-position: center left;
+}
+
+QAbstractSpinBox::up-arrow,QAbstractSpinBox::up-arrow:disabled,QAbstractSpinBox::up-arrow:off {
+    image: url(:/qss_icons/rc/up_arrow_disabled.png);
+    width: 10px;
+    height: 10px;
+}
+QAbstractSpinBox::up-arrow:hover
+{
+    image: url(:/qss_icons/rc/up_arrow.png);
+}
+
+
+QAbstractSpinBox::down-arrow,QAbstractSpinBox::down-arrow:disabled,QAbstractSpinBox::down-arrow:off
+{
+    image: url(:/qss_icons/rc/down_arrow_disabled.png);
+    width: 10px;
+    height: 10px;
+}
+QAbstractSpinBox::down-arrow:hover
+{
+    image: url(:/qss_icons/rc/down_arrow.png);
+}
+
+
+QLabel
+{
+    border: 0px solid black;
+    background-color: transparent;
+}
+
+QTabWidget{
+    border: 1px transparent black;
+}
+
+QTabWidget::pane {
+    border: 1px solid #444;
+    border-radius: 3px;
+    padding: 3px;
+}
+
+QTabBar
+{
+    qproperty-drawBase: 0;
+    left: 5px; /* move to the right by 5px */
+}
+
+QTabBar:focus
+{
+    border: 0px transparent black;
+}
+
+QTabBar::close-button  {
+    image: url(:/qss_icons/rc/close.png);
+    background: transparent;
+}
+
+QTabBar::close-button:hover
+{
+    image: url(:/qss_icons/rc/close-hover.png);
+    background: transparent;
+}
+
+QTabBar::close-button:pressed {
+    image: url(:/qss_icons/rc/close-pressed.png);
+    background: transparent;
+}
+
+/* TOP TABS */
+QTabBar::tab:top {
+    color: #b1b1b1;
+    border: 1px solid #4A4949;
+    border-bottom: 1px transparent black;
+    background-color: #302F2F;
+    padding: 5px;
+    border-top-left-radius: 2px;
+    border-top-right-radius: 2px;
+}
+
+QTabBar::tab:top:!selected
+{
+    color: #b1b1b1;
+    background-color: #201F1F;
+    border: 1px transparent #4A4949;
+    border-bottom: 1px transparent #4A4949;
+    border-top-left-radius: 0px;
+    border-top-right-radius: 0px;
+}
+
+QTabBar::tab:top:!selected:hover {
+    background-color: #48576b;
+}
+
+/* BOTTOM TABS */
+QTabBar::tab:bottom {
+    color: #b1b1b1;
+    border: 1px solid #4A4949;
+    border-top: 1px transparent black;
+    background-color: #302F2F;
+    padding: 5px;
+    border-bottom-left-radius: 2px;
+    border-bottom-right-radius: 2px;
+}
+
+QTabBar::tab:bottom:!selected
+{
+    color: #b1b1b1;
+    background-color: #201F1F;
+    border: 1px transparent #4A4949;
+    border-top: 1px transparent #4A4949;
+    border-bottom-left-radius: 0px;
+    border-bottom-right-radius: 0px;
+}
+
+QTabBar::tab:bottom:!selected:hover {
+    background-color: #78879b;
+}
+
+/* LEFT TABS */
+QTabBar::tab:left {
+    color: #b1b1b1;
+    border: 1px solid #4A4949;
+    border-left: 1px transparent black;
+    background-color: #302F2F;
+    padding: 5px;
+    border-top-right-radius: 2px;
+    border-bottom-right-radius: 2px;
+}
+
+QTabBar::tab:left:!selected
+{
+    color: #b1b1b1;
+    background-color: #201F1F;
+    border: 1px transparent #4A4949;
+    border-right: 1px transparent #4A4949;
+    border-top-right-radius: 0px;
+    border-bottom-right-radius: 0px;
+}
+
+QTabBar::tab:left:!selected:hover {
+    background-color: #48576b;
+}
+
+
+/* RIGHT TABS */
+QTabBar::tab:right {
+    color: #b1b1b1;
+    border: 1px solid #4A4949;
+    border-right: 1px transparent black;
+    background-color: #302F2F;
+    padding: 5px;
+    border-top-left-radius: 2px;
+    border-bottom-left-radius: 2px;
+}
+
+QTabBar::tab:right:!selected
+{
+    color: #b1b1b1;
+    background-color: #201F1F;
+    border: 1px transparent #4A4949;
+    border-right: 1px transparent #4A4949;
+    border-top-left-radius: 0px;
+    border-bottom-left-radius: 0px;
+}
+
+QTabBar::tab:right:!selected:hover {
+    background-color: #48576b;
+}
+
+QTabBar QToolButton::right-arrow:enabled {
+     image: url(:/qss_icons/rc/right_arrow.png);
+ }
+
+ QTabBar QToolButton::left-arrow:enabled {
+     image: url(:/qss_icons/rc/left_arrow.png);
+ }
+
+QTabBar QToolButton::right-arrow:disabled {
+     image: url(:/qss_icons/rc/right_arrow_disabled.png);
+ }
+
+ QTabBar QToolButton::left-arrow:disabled {
+     image: url(:/qss_icons/rc/left_arrow_disabled.png);
+ }
+
+
+QDockWidget {
+    border: 1px solid #403F3F;
+    titlebar-close-icon: url(:/qss_icons/rc/close.png);
+    titlebar-normal-icon: url(:/qss_icons/rc/undock.png);
+}
+
+QDockWidget::close-button, QDockWidget::float-button {
+    border: 1px solid transparent;
+    border-radius: 2px;
+    background: transparent;
+}
+
+QDockWidget::close-button:hover, QDockWidget::float-button:hover {
+    background: rgba(255, 255, 255, 10);
+}
+
+QDockWidget::close-button:pressed, QDockWidget::float-button:pressed {
+    padding: 1px -1px -1px 1px;
+    background: rgba(255, 255, 255, 10);
+}
+
+QTreeView, QListView
+{
+    border: 1px solid #444;
+    background-color: #201F1F;
+}
+
+QTreeView:branch:selected, QTreeView:branch:hover
+{
+    background: url(:/qss_icons/rc/transparent.png);
+}
+
+QTreeView::branch:has-siblings:!adjoins-item {
+    border-image: url(:/qss_icons/rc/transparent.png);
+}
+
+QTreeView::branch:has-siblings:adjoins-item {
+    border-image: url(:/qss_icons/rc/transparent.png);
+}
+
+QTreeView::branch:!has-children:!has-siblings:adjoins-item {
+    border-image: url(:/qss_icons/rc/transparent.png);
+}
+
+QTreeView::branch:has-children:!has-siblings:closed,
+QTreeView::branch:closed:has-children:has-siblings {
+    image: url(:/qss_icons/rc/branch_closed.png);
+}
+
+QTreeView::branch:open:has-children:!has-siblings,
+QTreeView::branch:open:has-children:has-siblings  {
+    image: url(:/qss_icons/rc/branch_open.png);
+}
+
+QTreeView::branch:has-children:!has-siblings:closed:hover,
+QTreeView::branch:closed:has-children:has-siblings:hover {
+    image: url(:/qss_icons/rc/branch_closed-on.png);
+    }
+
+QTreeView::branch:open:has-children:!has-siblings:hover,
+QTreeView::branch:open:has-children:has-siblings:hover  {
+    image: url(:/qss_icons/rc/branch_open-on.png);
+    }
+
+QListView::item:!selected:hover, QListView::item:!selected:hover, QTreeView::item:!selected:hover  {
+    background: rgba(0, 0, 0, 0);
+    outline: 0;
+    color: #FFFFFF
+}
+
+QListView::item:selected:hover, QListView::item:selected:hover, QTreeView::item:selected:hover  {
+    background: #3d8ec9;
+    color: #FFFFFF;
+}
+
+QSlider::groove:horizontal {
+    border: 1px solid #3A3939;
+    height: 8px;
+    background: #201F1F;
+    margin: 2px 0;
+    border-radius: 2px;
+}
+
+QSlider::handle:horizontal {
+    background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,
+      stop: 0.0 silver, stop: 0.2 #a8a8a8, stop: 1 #727272);
+    border: 1px solid #3A3939;
+    width: 14px;
+    height: 14px;
+    margin: -4px 0;
+    border-radius: 2px;
+}
+
+QSlider::groove:vertical {
+    border: 1px solid #3A3939;
+    width: 8px;
+    background: #201F1F;
+    margin: 0 0px;
+    border-radius: 2px;
+}
+
+QSlider::handle:vertical {
+    background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 silver,
+      stop: 0.2 #a8a8a8, stop: 1 #727272);
+    border: 1px solid #3A3939;
+    width: 14px;
+    height: 14px;
+    margin: 0 -4px;
+    border-radius: 2px;
+}
+
+QToolButton {
+    background-color: transparent;
+    border: 1px transparent #4A4949;
+    border-radius: 2px;
+    margin: 3px;
+    padding: 3px;
+}
+
+QToolButton[popupMode="1"] { /* only for MenuButtonPopup */
+ padding-right: 20px; /* make way for the popup button */
+ border: 1px transparent #4A4949;
+ border-radius: 5px;
+}
+
+QToolButton[popupMode="2"] { /* only for InstantPopup */
+ padding-right: 10px; /* make way for the popup button */
+ border: 1px transparent #4A4949;
+}
+
+
+QToolButton:hover, QToolButton::menu-button:hover {
+    background-color: transparent;
+    border: 1px solid #78879b;
+}
+
+QToolButton:checked, QToolButton:pressed,
+        QToolButton::menu-button:pressed {
+    background-color: #4A4949;
+    border: 1px solid #78879b;
+}
+
+/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */
+QToolButton::menu-indicator {
+    image: url(:/qss_icons/rc/down_arrow.png);
+    top: -7px; left: -2px; /* shift it a bit */
+}
+
+/* the subcontrols below are used only in the MenuButtonPopup mode */
+QToolButton::menu-button {
+    border: 1px transparent #4A4949;
+    border-top-right-radius: 6px;
+    border-bottom-right-radius: 6px;
+    /* 16px width + 4px for border = 20px allocated above */
+    width: 16px;
+    outline: none;
+}
+
+QToolButton::menu-arrow {
+    image: url(:/qss_icons/rc/down_arrow.png);
+}
+
+QToolButton::menu-arrow:open {
+    top: 1px; left: 1px; /* shift it a bit */
+    border: 1px solid #3A3939;
+}
+
+QPushButton::menu-indicator  {
+    subcontrol-origin: padding;
+    subcontrol-position: bottom right;
+    left: 8px;
+}
+
+QTableView
+{
+    border: 1px solid #444;
+    gridline-color: #6c6c6c;
+    background-color: #201F1F;
+}
+
+
+QTableView, QHeaderView
+{
+    border-radius: 0px;
+}
+
+QTableView::item:pressed, QListView::item:pressed, QTreeView::item:pressed  {
+    background: #78879b;
+    color: #FFFFFF;
+}
+
+QTableView::item:selected:active, QTreeView::item:selected:active, QListView::item:selected:active  {
+    background: #3d8ec9;
+    color: #FFFFFF;
+}
+
+
+QHeaderView
+{
+    border: 1px transparent;
+    border-radius: 2px;
+    margin: 0px;
+    padding: 0px;
+}
+
+QHeaderView::section  {
+    background-color: #3A3939;
+    color: silver;
+    padding: 4px;
+    border: 1px solid #6c6c6c;
+    border-radius: 0px;
+    text-align: center;
+}
+
+QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one
+{
+    border-top: 1px solid #6c6c6c;
+}
+
+QHeaderView::section::vertical
+{
+    border-top: transparent;
+}
+
+QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one
+{
+    border-left: 1px solid #6c6c6c;
+}
+
+QHeaderView::section::horizontal
+{
+    border-left: transparent;
+}
+
+
+QHeaderView::section:checked
+ {
+    color: white;
+    background-color: #5A5959;
+ }
+
+ /* style the sort indicator */
+QHeaderView::down-arrow {
+    image: url(:/qss_icons/rc/down_arrow.png);
+}
+
+QHeaderView::up-arrow {
+    image: url(:/qss_icons/rc/up_arrow.png);
+}
+
+
+QTableCornerButton::section {
+    background-color: #3A3939;
+    border: 1px solid #3A3939;
+    border-radius: 2px;
+}
+
+QToolBox  {
+    padding: 3px;
+    border: 1px transparent black;
+}
+
+QToolBox::tab {
+    color: #b1b1b1;
+    background-color: #302F2F;
+    border: 1px solid #4A4949;
+    border-bottom: 1px transparent #302F2F;
+    border-top-left-radius: 5px;
+    border-top-right-radius: 5px;
+}
+
+ QToolBox::tab:selected { /* italicize selected tabs */
+    font: italic;
+    background-color: #302F2F;
+    border-color: #3d8ec9;
+ }
+
+QStatusBar::item {
+    border: 1px solid #3A3939;
+    border-radius: 2px;
+ }
+
+
+QFrame[height="3"], QFrame[width="3"] {
+    background-color: #444;
+}
+
+
+QSplitter::handle {
+    border: 1px dashed #3A3939;
+}
+
+QSplitter::handle:hover {
+    background-color: #787876;
+    border: 1px solid #3A3939;
+}
+
+QSplitter::handle:horizontal {
+    width: 1px;
+}
+
+QSplitter::handle:vertical {
+    height: 1px;
+}
+
+MessageItem
+{
+    border: none;
 }
 
 MessageEdit
@@ -23,18 +1233,47 @@ MessageEdit:hover
     border: none;
 }
 
-MessageEdit
+QListWidget QPushButton 
+{
+    background-color: transparent;
+    border: none;
+}
+
+QPushButton:hover 
+{
+    background-color: #4A4949;
+}
+
+#messages:item:selected
 {
     background-color: transparent;
 }
 
-#warningLabel
+#friends_list:item:selected
 {
-    color: #BC1C1C;
+    background-color: #333333;
 }
 
-#groupInvitesPushButton
+#toxygen
 {
-    background-color: #009c00;
+    color: #A9A9A9;
 }
 
+QCheckBox
+{
+    spacing: 5px;
+    outline: none;
+    color: #bbb;
+    margin-bottom: 2px;
+    text-align: center;
+}
+
+QListWidget > QLabel 
+{
+    color: #A9A9A9;
+}
+
+#contact_name
+{
+    padding-left: 22px;
+}
\ No newline at end of file
diff --git a/toxygen/tests/README.txt b/toxygen/tests/README.txt
deleted file mode 100644
index b2c475f..0000000
--- a/toxygen/tests/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-unused
diff --git a/toxygen/tests/__init__.py b/toxygen/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/tests/conference_tests.py.bak b/toxygen/tests/conference_tests.py.bak
deleted file mode 100644
index 8da5912..0000000
--- a/toxygen/tests/conference_tests.py.bak
+++ /dev/null
@@ -1,151 +0,0 @@
-if False:
-    @unittest.skip # to yet
-    def test_conference(self):
-        """
-        t:group_new
-        t:conference_delete
-        t:conference_get_chatlist_size
-        t:conference_get_chatlist
-        t:conference_send_message
-        """
-        bob_addr = self.bob.self_get_address()
-        alice_addr = self.alice.self_get_address()
-
-        self.abid = self.alice.friend_by_public_key(bob_addr)
-        self.baid = self.bob.friend_by_public_key(alice_addr)
-
-        assert self.bob_just_add_alice_as_friend()
-
-        #: Test group add
-        privacy_state = enums.TOX_GROUP_PRIVACY_STATE['PUBLIC']
-        group_name = 'test_group'
-        nick = 'test_nick'
-        status = None # dunno
-        self.group_id = self.bob.group_new(privacy_state, group_name, nick, status)
-        # :return group number on success, UINT32_MAX on failure.
-        assert self.group_id >= 0
-
-        self.loop(50)
-
-        BID = self.abid
-
-        def alices_on_conference_invite(self, fid, type_, data):
-            assert fid == BID
-            assert type_ == 0
-            gn = self.conference_join(fid, data)
-            assert type_ == self.conference_get_type(gn)
-            self.gi = True
-
-        def alices_on_conference_peer_list_changed(self, gid):
-            logging.debug("alices_on_conference_peer_list_changed")
-            assert gid == self.group_id
-            self.gn = True
-
-        try:
-            AliceTox.on_conference_invite = alices_on_conference_invite
-            AliceTox.on_conference_peer_list_changed = alices_on_conference_peer_list_changed
-
-            self.alice.gi = False
-            self.alice.gn = False
-
-            self.wait_ensure_exec(self.bob.conference_invite, (self.aid, self.group_id))
-
-            assert self.wait_callback_trues(self.alice, ['gi', 'gn'])
-        except AssertionError as e:
-            raise
-        finally:
-            AliceTox.on_conference_invite = Tox.on_conference_invite
-            AliceTox.on_conference_peer_list_change = Tox.on_conference_peer_list_changed
-
-        #: Test group number of peers
-        self.loop(50)
-        assert self.bob.conference_peer_count(self.group_id) == 2
-
-        #: Test group peername
-        self.alice.self_set_name('Alice')
-        self.bob.self_set_name('Bob')
-
-        def alices_on_conference_peer_list_changed(self, gid):
-            logging.debug("alices_on_conference_peer_list_changed")
-            self.gn = True
-        try:
-            AliceTox.on_conference_peer_list_changed = alices_on_conference_peer_list_changed
-            self.alice.gn = False
-
-            assert self.wait_callback_true(self.alice, 'gn')
-        except AssertionError as e:
-            raise
-        finally:
-            AliceTox.on_conference_peer_list_changed = Tox.on_conference_peer_list_changed
-
-        peernames = [self.bob.conference_peer_get_name(self.group_id, i) for i in
-                     range(self.bob.conference_peer_count(self.group_id))]
-        assert 'Alice' in peernames
-        assert 'Bob' in peernames
-
-        #: Test title change
-        self.bob.conference_set_title(self.group_id, 'My special title')
-        assert self.bob.conference_get_title(self.group_id) == 'My special title'
-
-        #: Test group message
-        AID = self.aid
-        BID = self.bid
-        MSG = 'Group message test'
-
-        def alices_on_conference_message(self, gid, fgid, msg_type, message):
-            logging.debug("alices_on_conference_message" +repr(message))
-            if fgid == AID:
-                assert gid == self.group_id
-                assert str(message, 'UTF-8') == MSG
-                self.alice.gm = True
-
-        try:
-            AliceTox.on_conference_message = alices_on_conference_message
-            self.alice.gm = False
-
-            self.wait_ensure_exec(self.bob.conference_send_message, (
-                self.group_id, TOX_MESSAGE_TYPE['NORMAL'], MSG))
-            assert self.wait_callback_true(self.alice, 'gm')
-        except AssertionError as e:
-            raise
-        finally:
-            AliceTox.on_conference_message = Tox.on_conference_message
-
-        #: Test group action
-        AID = self.aid
-        BID = self.bid
-        MSG = 'Group action test'
-
-        def on_conference_action(self, gid, fgid, msg_type, action):
-            if fgid == AID:
-                assert gid == self.group_id
-                assert msg_type == TOX_MESSAGE_TYPE['ACTION']
-                assert str(action, 'UTF-8') == MSG
-                self.ga = True
-
-        try:
-            AliceTox.on_conference_message = on_conference_action
-            self.alice.ga = False
-
-            self.wait_ensure_exec(self.bob.conference_send_message,
-                             (self.group_id, TOX_MESSAGE_TYPE['ACTION'], MSG))
-
-            assert self.wait_callback_true(self.alice, 'ga')
-
-            #: Test chatlist
-            assert len(self.bob.conference_get_chatlist()) == self.bob.conference_get_chatlist_size(), \
-              print(len(self.bob.conference_get_chatlist()), '!=', self.bob.conference_get_chatlist_size())
-            assert len(self.alice.conference_get_chatlist()) == self.bob.conference_get_chatlist_size(), \
-              print(len(self.alice.conference_get_chatlist()), '!=', self.bob.conference_get_chatlist_size())
-            assert self.bob.conference_get_chatlist_size() == 1, \
-              self.bob.conference_get_chatlist_size()
-            self.bob.conference_delete(self.group_id)
-            assert self.bob.conference_get_chatlist_size() == 0, \
-              self.bob.conference_get_chatlist_size()
-
-        except AssertionError as e:
-            raise
-        finally:
-            AliceTox.on_conference_message = Tox.on_conference_message
-
-
diff --git a/toxygen/tests/socks.py b/toxygen/tests/socks.py
deleted file mode 100644
index f9f730e..0000000
--- a/toxygen/tests/socks.py
+++ /dev/null
@@ -1,393 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-"""SocksiPy - Python SOCKS module.
-Version 1.00
-
-Copyright 2006 Dan-Haim. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-1. Redistributions of source code must retain the above copyright notice, this
-   list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-3. Neither the name of Dan Haim nor the names of his contributors may be used
-   to endorse or promote products derived from this software without specific
-   prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
-
-
-This module provides a standard socket-like interface for Python
-for tunneling connections through SOCKS proxies.
-
-"""
-
-"""
-
-Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
-for use in PyLoris (http://pyloris.sourceforge.net/)
-
-Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/)
-mainly to merge bug fixes found in Sourceforge
-
-Minor modifications made by Eugene Dementiev (http://www.dementiev.eu/)
-
-"""
-
-import socket
-import struct
-import sys
-
-PROXY_TYPE_SOCKS4 = 1
-PROXY_TYPE_SOCKS5 = 2
-PROXY_TYPE_HTTP = 3
-
-_defaultproxy = None
-_orgsocket = socket.socket
-
-class ProxyError(Exception): pass
-class GeneralProxyError(ProxyError): pass
-class Socks5AuthError(ProxyError): pass
-class Socks5Error(ProxyError): pass
-class Socks4Error(ProxyError): pass
-class HTTPError(ProxyError): pass
-
-_generalerrors = ("success",
-    "invalid data",
-    "not connected",
-    "not available",
-    "bad proxy type",
-    "bad input")
-
-_socks5errors = ("succeeded",
-    "general SOCKS server failure",
-    "connection not allowed by ruleset",
-    "Network unreachable",
-    "Host unreachable",
-    "Connection refused",
-    "TTL expired",
-    "Command not supported",
-    "Address type not supported",
-    "Unknown error")
-
-_socks5autherrors = ("succeeded",
-    "authentication is required",
-    "all offered authentication methods were rejected",
-    "unknown username or invalid password",
-    "unknown error")
-
-_socks4errors = ("request granted",
-    "request rejected or failed",
-    "request rejected because SOCKS server cannot connect to identd on the client",
-    "request rejected because the client program and identd report different user-ids",
-    "unknown error")
-
-def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
-    """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
-    Sets a default proxy which all further socksocket objects will use,
-    unless explicitly changed.
-    """
-    global _defaultproxy
-    _defaultproxy = (proxytype, addr, port, rdns, username, password)
-
-def wrapmodule(module):
-    """wrapmodule(module)
-    Attempts to replace a module's socket library with a SOCKS socket. Must set
-    a default proxy using setdefaultproxy(...) first.
-    This will only work on modules that import socket directly into the namespace;
-    most of the Python Standard Library falls into this category.
-    """
-    if _defaultproxy != None:
-        module.socket.socket = socksocket
-    else:
-        raise GeneralProxyError((4, "no proxy specified"))
-
-class socksocket(socket.socket):
-    """socksocket([family[, type[, proto]]]) -> socket object
-    Open a SOCKS enabled socket. The parameters are the same as
-    those of the standard socket init. In order for SOCKS to work,
-    you must specify family=AF_INET, type=SOCK_STREAM and proto=0.
-    """
-
-    def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None):
-        _orgsocket.__init__(self, family, type, proto, _sock)
-        if _defaultproxy != None:
-            self.__proxy = _defaultproxy
-        else:
-            self.__proxy = (None, None, None, None, None, None)
-        self.__proxysockname = None
-        self.__proxypeername = None
-
-    def __recvall(self, count):
-        """__recvall(count) -> data
-        Receive EXACTLY the number of bytes requested from the socket.
-        Blocks until the required number of bytes have been received.
-        """
-        data = self.recv(count)
-        while len(data) < count:
-            d = self.recv(count-len(data))
-            if not d: raise GeneralProxyError((0, "connection closed unexpectedly"))
-            data = data + d
-        return data
-
-    def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
-        """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
-        Sets the proxy to be used.
-        proxytype -    The type of the proxy to be used. Three types
-                are supported: PROXY_TYPE_SOCKS4 (including socks4a),
-                PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
-        addr -        The address of the server (IP or DNS).
-        port -        The port of the server. Defaults to 1080 for SOCKS
-                servers and 8080 for HTTP proxy servers.
-        rdns -        Should DNS queries be preformed on the remote side
-                (rather than the local side). The default is True.
-                Note: This has no effect with SOCKS4 servers.
-        username -    Username to authenticate with to the server.
-                The default is no authentication.
-        password -    Password to authenticate with to the server.
-                Only relevant when username is also provided.
-        """
-        self.__proxy = (proxytype, addr, port, rdns, username, password)
-
-    def __negotiatesocks5(self, destaddr, destport):
-        """__negotiatesocks5(self,destaddr,destport)
-        Negotiates a connection through a SOCKS5 server.
-        """
-        # First we'll send the authentication packages we support.
-        if (self.__proxy[4]!=None) and (self.__proxy[5]!=None):
-            # The username/password details were supplied to the
-            # setproxy method so we support the USERNAME/PASSWORD
-            # authentication (in addition to the standard none).
-            self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02))
-        else:
-            # No username/password were entered, therefore we
-            # only support connections with no authentication.
-            self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00))
-        # We'll receive the server's response to determine which
-        # method was selected
-        chosenauth = self.__recvall(2)
-        if chosenauth[0:1] != chr(0x05).encode():
-            self.close()
-            raise GeneralProxyError((1, _generalerrors[1]))
-        # Check the chosen authentication method
-        if chosenauth[1:2] == chr(0x00).encode():
-            # No authentication is required
-            pass
-        elif chosenauth[1:2] == chr(0x02).encode():
-            # Okay, we need to perform a basic username/password
-            # authentication.
-            self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5])
-            authstat = self.__recvall(2)
-            if authstat[0:1] != chr(0x01).encode():
-                # Bad response
-                self.close()
-                raise GeneralProxyError((1, _generalerrors[1]))
-            if authstat[1:2] != chr(0x00).encode():
-                # Authentication failed
-                self.close()
-                raise Socks5AuthError((3, _socks5autherrors[3]))
-            # Authentication succeeded
-        else:
-            # Reaching here is always bad
-            self.close()
-            if chosenauth[1] == chr(0xFF).encode():
-                raise Socks5AuthError((2, _socks5autherrors[2]))
-            else:
-                raise GeneralProxyError((1, _generalerrors[1]))
-        # Now we can request the actual connection
-        req = struct.pack('BBB', 0x05, 0x01, 0x00)
-        # If the given destination address is an IP address, we'll
-        # use the IPv4 address request even if remote resolving was specified.
-        try:
-            ipaddr = socket.inet_aton(destaddr)
-            req = req + chr(0x01).encode() + ipaddr
-        except socket.error:
-            # Well it's not an IP number,  so it's probably a DNS name.
-            if self.__proxy[3]:
-                # Resolve remotely
-                ipaddr = None
-                if type(destaddr) != type(b''): # python3
-                    destaddr_bytes = destaddr.encode(encoding='idna')
-                else:
-                    destaddr_bytes = destaddr
-                req = req + chr(0x03).encode() + chr(len(destaddr_bytes)).encode() + destaddr_bytes
-            else:
-                # Resolve locally
-                ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
-                req = req + chr(0x01).encode() + ipaddr
-        req = req + struct.pack(">H", destport)
-        self.sendall(req)
-        # Get the response
-        resp = self.__recvall(4)
-        if resp[0:1] != chr(0x05).encode():
-            self.close()
-            raise GeneralProxyError((1, _generalerrors[1]))
-        elif resp[1:2] != chr(0x00).encode():
-            # Connection failed
-            self.close()
-            if ord(resp[1:2])<=8:
-                raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
-            else:
-                raise Socks5Error((9, _socks5errors[9]))
-        # Get the bound address/port
-        elif resp[3:4] == chr(0x01).encode():
-            boundaddr = self.__recvall(4)
-        elif resp[3:4] == chr(0x03).encode():
-            resp = resp + self.recv(1)
-            boundaddr = self.__recvall(ord(resp[4:5]))
-        else:
-            self.close()
-            raise GeneralProxyError((1,_generalerrors[1]))
-        boundport = struct.unpack(">H", self.__recvall(2))[0]
-        self.__proxysockname = (boundaddr, boundport)
-        if ipaddr != None:
-            self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
-        else:
-            self.__proxypeername = (destaddr, destport)
-
-    def getproxysockname(self):
-        """getsockname() -> address info
-        Returns the bound IP address and port number at the proxy.
-        """
-        return self.__proxysockname
-
-    def getproxypeername(self):
-        """getproxypeername() -> address info
-        Returns the IP and port number of the proxy.
-        """
-        return _orgsocket.getpeername(self)
-
-    def getpeername(self):
-        """getpeername() -> address info
-        Returns the IP address and port number of the destination
-        machine (note: getproxypeername returns the proxy)
-        """
-        return self.__proxypeername
-
-    def __negotiatesocks4(self,destaddr,destport):
-        """__negotiatesocks4(self,destaddr,destport)
-        Negotiates a connection through a SOCKS4 server.
-        """
-        # Check if the destination address provided is an IP address
-        rmtrslv = False
-        try:
-            ipaddr = socket.inet_aton(destaddr)
-        except socket.error:
-            # It's a DNS name. Check where it should be resolved.
-            if self.__proxy[3]:
-                ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)
-                rmtrslv = True
-            else:
-                ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
-        # Construct the request packet
-        req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
-        # The username parameter is considered userid for SOCKS4
-        if self.__proxy[4] != None:
-            req = req + self.__proxy[4]
-        req = req + chr(0x00).encode()
-        # DNS name if remote resolving is required
-        # NOTE: This is actually an extension to the SOCKS4 protocol
-        # called SOCKS4A and may not be supported in all cases.
-        if rmtrslv:
-            req = req + destaddr + chr(0x00).encode()
-        self.sendall(req)
-        # Get the response from the server
-        resp = self.__recvall(8)
-        if resp[0:1] != chr(0x00).encode():
-            # Bad data
-            self.close()
-            raise GeneralProxyError((1,_generalerrors[1]))
-        if resp[1:2] != chr(0x5A).encode():
-            # Server returned an error
-            self.close()
-            if ord(resp[1:2]) in (91, 92, 93):
-                self.close()
-                raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90]))
-            else:
-                raise Socks4Error((94, _socks4errors[4]))
-        # Get the bound address/port
-        self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0])
-        if rmtrslv != None:
-            self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
-        else:
-            self.__proxypeername = (destaddr, destport)
-
-    def __negotiatehttp(self, destaddr, destport):
-        """__negotiatehttp(self,destaddr,destport)
-        Negotiates a connection through an HTTP server.
-        """
-        # If we need to resolve locally, we do this now
-        if not self.__proxy[3]:
-            addr = socket.gethostbyname(destaddr)
-        else:
-            addr = destaddr
-        self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode())
-        # We read the response until we get the string "\r\n\r\n"
-        resp = self.recv(1)
-        while resp.find("\r\n\r\n".encode()) == -1:
-            recv = self.recv(1)
-            if not recv:
-                raise GeneralProxyError((1, _generalerrors[1]))
-            resp = resp + recv
-        # We just need the first line to check if the connection
-        # was successful
-        statusline = resp.splitlines()[0].split(" ".encode(), 2)
-        if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()):
-            self.close()
-            raise GeneralProxyError((1, _generalerrors[1]))
-        try:
-            statuscode = int(statusline[1])
-        except ValueError:
-            self.close()
-            raise GeneralProxyError((1, _generalerrors[1]))
-        if statuscode != 200:
-            self.close()
-            raise HTTPError((statuscode, statusline[2]))
-        self.__proxysockname = ("0.0.0.0", 0)
-        self.__proxypeername = (addr, destport)
-
-    def connect(self, destpair):
-        """connect(self, despair)
-        Connects to the specified destination through a proxy.
-        destpar - A tuple of the IP/DNS address and the port number.
-        (identical to socket's connect).
-        To select the proxy server use setproxy().
-        """
-        # Do a minimal input check first
-        if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int):
-            raise GeneralProxyError((5, _generalerrors[5]))
-        if self.__proxy[0] == PROXY_TYPE_SOCKS5:
-            if self.__proxy[2] != None:
-                portnum = int(self.__proxy[2])
-            else:
-                portnum = 1080
-            _orgsocket.connect(self, (self.__proxy[1], portnum))
-            self.__negotiatesocks5(destpair[0], destpair[1])
-        elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
-            if self.__proxy[2] != None:
-                portnum = self.__proxy[2]
-            else:
-                portnum = 1080
-            _orgsocket.connect(self,(self.__proxy[1], portnum))
-            self.__negotiatesocks4(destpair[0], destpair[1])
-        elif self.__proxy[0] == PROXY_TYPE_HTTP:
-            if self.__proxy[2] != None:
-                portnum = self.__proxy[2]
-            else:
-                portnum = 8080
-            _orgsocket.connect(self,(self.__proxy[1], portnum))
-            self.__negotiatehttp(destpair[0], destpair[1])
-        elif self.__proxy[0] == None:
-            _orgsocket.connect(self, (destpair[0], destpair[1]))
-        else:
-            raise GeneralProxyError((4, _generalerrors[4]))
diff --git a/toxygen/tests/test_gdb.py b/toxygen/tests/test_gdb.py
deleted file mode 100644
index 584987a..0000000
--- a/toxygen/tests/test_gdb.py
+++ /dev/null
@@ -1,938 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-# Verify that gdb can pretty-print the various PyObject* types
-#
-# The code for testing gdb was adapted from similar work in Unladen Swallow's
-# Lib/test/test_jit_gdb.py
-
-import locale
-import os
-import re
-import subprocess
-import sys
-import sysconfig
-import textwrap
-import unittest
-
-# Is this Python configured to support threads?
-try:
-    import _thread
-except ImportError:
-    _thread = None
-
-from test import support
-from test.support import run_unittest, findfile, python_is_optimized
-
-def get_gdb_version():
-    try:
-        proc = subprocess.Popen(["gdb", "-nx", "--version"],
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE,
-                                universal_newlines=True)
-        with proc:
-            version = proc.communicate()[0]
-    except OSError:
-        # This is what "no gdb" looks like.  There may, however, be other
-        # errors that manifest this way too.
-        raise unittest.SkipTest("Couldn't find gdb on the path")
-
-    # Regex to parse:
-    # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7
-    # 'GNU gdb (GDB) Fedora 7.9.1-17.fc22\n' -> 7.9
-    # 'GNU gdb 6.1.1 [FreeBSD]\n' -> 6.1
-    # 'GNU gdb (GDB) Fedora (7.5.1-37.fc18)\n' -> 7.5
-    match = re.search(r"^GNU gdb.*?\b(\d+)\.(\d+)", version)
-    if match is None:
-        raise Exception("unable to parse GDB version: %r" % version)
-    return (version, int(match.group(1)), int(match.group(2)))
-
-gdb_version, gdb_major_version, gdb_minor_version = get_gdb_version()
-if gdb_major_version < 7:
-    raise unittest.SkipTest("gdb versions before 7.0 didn't support python "
-                            "embedding. Saw %s.%s:\n%s"
-                            % (gdb_major_version, gdb_minor_version,
-                               gdb_version))
-
-if not sysconfig.is_python_build():
-    raise unittest.SkipTest("test_gdb only works on source builds at the moment.")
-
-# Location of custom hooks file in a repository checkout.
-checkout_hook_path = os.path.join(os.path.dirname(sys.executable),
-                                  'python-gdb.py')
-
-PYTHONHASHSEED = '123'
-
-def run_gdb(*args, **env_vars):
-    """Runs gdb in --batch mode with the additional arguments given by *args.
-
-    Returns its (stdout, stderr) decoded from utf-8 using the replace handler.
-    """
-    if env_vars:
-        env = os.environ.copy()
-        env.update(env_vars)
-    else:
-        env = None
-    # -nx: Do not execute commands from any .gdbinit initialization files
-    #      (issue #22188)
-    base_cmd = ('gdb', '--batch', '-nx')
-    if (gdb_major_version, gdb_minor_version) >= (7, 4):
-        base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path)
-    proc = subprocess.Popen(base_cmd + args,
-                            # Redirect stdin to prevent GDB from messing with
-                            # the terminal settings
-                            stdin=subprocess.PIPE,
-                            stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE,
-                            env=env)
-    with proc:
-        out, err = proc.communicate()
-    return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace')
-
-# Verify that "gdb" was built with the embedded python support enabled:
-gdbpy_version, _ = run_gdb("--eval-command=python import sys; print(sys.version_info)")
-if not gdbpy_version:
-    raise unittest.SkipTest("gdb not built with embedded python support")
-
-# Verify that "gdb" can load our custom hooks, as OS security settings may
-# disallow this without a customized .gdbinit.
-_, gdbpy_errors = run_gdb('--args', sys.executable)
-if "auto-loading has been declined" in gdbpy_errors:
-    msg = "gdb security settings prevent use of custom hooks: "
-    raise unittest.SkipTest(msg + gdbpy_errors.rstrip())
-
-def gdb_has_frame_select():
-    # Does this build of gdb have gdb.Frame.select ?
-    stdout, _ = run_gdb("--eval-command=python print(dir(gdb.Frame))")
-    m = re.match(r'.*\[(.*)\].*', stdout)
-    if not m:
-        raise unittest.SkipTest("Unable to parse output from gdb.Frame.select test")
-    gdb_frame_dir = m.group(1).split(', ')
-    return "'select'" in gdb_frame_dir
-
-HAS_PYUP_PYDOWN = gdb_has_frame_select()
-
-BREAKPOINT_FN='builtin_id'
-
-@unittest.skipIf(support.PGO, "not useful for PGO")
-class DebuggerTests(unittest.TestCase):
-
-    """Test that the debugger can debug Python."""
-
-    def get_stack_trace(self, source=None, script=None,
-                        breakpoint=BREAKPOINT_FN,
-                        cmds_after_breakpoint=None,
-                        import_site=False):
-        '''
-        Run 'python -c SOURCE' under gdb with a breakpoint.
-
-        Support injecting commands after the breakpoint is reached
-
-        Returns the stdout from gdb
-
-        cmds_after_breakpoint: if provided, a list of strings: gdb commands
-        '''
-        # We use "set breakpoint pending yes" to avoid blocking with a:
-        #   Function "foo" not defined.
-        #   Make breakpoint pending on future shared library load? (y or [n])
-        # error, which typically happens python is dynamically linked (the
-        # breakpoints of interest are to be found in the shared library)
-        # When this happens, we still get:
-        #   Function "textiowrapper_write" not defined.
-        # emitted to stderr each time, alas.
-
-        # Initially I had "--eval-command=continue" here, but removed it to
-        # avoid repeated print breakpoints when traversing hierarchical data
-        # structures
-
-        # Generate a list of commands in gdb's language:
-        commands = ['set breakpoint pending yes',
-                    'break %s' % breakpoint,
-
-                    # The tests assume that the first frame of printed
-                    #  backtrace will not contain program counter,
-                    #  that is however not guaranteed by gdb
-                    #  therefore we need to use 'set print address off' to
-                    #  make sure the counter is not there. For example:
-                    # #0 in PyObject_Print ...
-                    #  is assumed, but sometimes this can be e.g.
-                    # #0 0x00003fffb7dd1798 in PyObject_Print ...
-                    'set print address off',
-
-                    'run']
-
-        # GDB as of 7.4 onwards can distinguish between the
-        # value of a variable at entry vs current value:
-        #   http://sourceware.org/gdb/onlinedocs/gdb/Variables.html
-        # which leads to the selftests failing with errors like this:
-        #   AssertionError: 'v@entry=()' != '()'
-        # Disable this:
-        if (gdb_major_version, gdb_minor_version) >= (7, 4):
-            commands += ['set print entry-values no']
-
-        if cmds_after_breakpoint:
-            commands += cmds_after_breakpoint
-        else:
-            commands += ['backtrace']
-
-        # print commands
-
-        # Use "commands" to generate the arguments with which to invoke "gdb":
-        args = ['--eval-command=%s' % cmd for cmd in commands]
-        args += ["--args",
-                 sys.executable]
-        args.extend(subprocess._args_from_interpreter_flags())
-
-        if not import_site:
-            # -S suppresses the default 'import site'
-            args += ["-S"]
-
-        if source:
-            args += ["-c", source]
-        elif script:
-            args += [script]
-
-        # print args
-        # print (' '.join(args))
-
-        # Use "args" to invoke gdb, capturing stdout, stderr:
-        out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED)
-
-        errlines = err.splitlines()
-        unexpected_errlines = []
-
-        # Ignore some benign messages on stderr.
-        ignore_patterns = (
-            'Function "%s" not defined.' % breakpoint,
-            'Do you need "set solib-search-path" or '
-            '"set sysroot"?',
-            # BFD: /usr/lib/debug/(...): unable to initialize decompress
-            # status for section .debug_aranges
-            'BFD: ',
-            # ignore all warnings
-            'warning: ',
-            )
-        for line in errlines:
-            if not line:
-                continue
-            if not line.startswith(ignore_patterns):
-                unexpected_errlines.append(line)
-
-        # Ensure no unexpected error messages:
-        self.assertEqual(unexpected_errlines, [])
-        return out
-
-    def get_gdb_repr(self, source,
-                     cmds_after_breakpoint=None,
-                     import_site=False):
-        # Given an input python source representation of data,
-        # run "python -c'id(DATA)'" under gdb with a breakpoint on
-        # builtin_id and scrape out gdb's representation of the "op"
-        # parameter, and verify that the gdb displays the same string
-        #
-        # Verify that the gdb displays the expected string
-        #
-        # For a nested structure, the first time we hit the breakpoint will
-        # give us the top-level structure
-
-        # NOTE: avoid decoding too much of the traceback as some
-        # undecodable characters may lurk there in optimized mode
-        # (issue #19743).
-        cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"]
-        gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN,
-                                          cmds_after_breakpoint=cmds_after_breakpoint,
-                                          import_site=import_site)
-        # gdb can insert additional '\n' and space characters in various places
-        # in its output, depending on the width of the terminal it's connected
-        # to (using its "wrap_here" function)
-        m = re.match(r'.*#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)\)\s+at\s+\S*Python/bltinmodule.c.*',
-                     gdb_output, re.DOTALL)
-        if not m:
-            self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output))
-        return m.group(1), gdb_output
-
-    def assertEndsWith(self, actual, exp_end):
-        '''Ensure that the given "actual" string ends with "exp_end"'''
-        self.assertTrue(actual.endswith(exp_end),
-                        msg='%r did not end with %r' % (actual, exp_end))
-
-    def assertMultilineMatches(self, actual, pattern):
-        m = re.match(pattern, actual, re.DOTALL)
-        if not m:
-            self.fail(msg='%r did not match %r' % (actual, pattern))
-
-    def get_sample_script(self):
-        return findfile('gdb_sample.py')
-
-class PrettyPrintTests(DebuggerTests):
-    def test_getting_backtrace(self):
-        gdb_output = self.get_stack_trace('id(42)')
-        self.assertTrue(BREAKPOINT_FN in gdb_output)
-
-    def assertGdbRepr(self, val, exp_repr=None):
-        # Ensure that gdb's rendering of the value in a debugged process
-        # matches repr(value) in this process:
-        gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')')
-        if not exp_repr:
-            exp_repr = repr(val)
-        self.assertEqual(gdb_repr, exp_repr,
-                         ('%r did not equal expected %r; full output was:\n%s'
-                          % (gdb_repr, exp_repr, gdb_output)))
-
-    def test_int(self):
-        'Verify the pretty-printing of various int values'
-        self.assertGdbRepr(42)
-        self.assertGdbRepr(0)
-        self.assertGdbRepr(-7)
-        self.assertGdbRepr(1000000000000)
-        self.assertGdbRepr(-1000000000000000)
-
-    def test_singletons(self):
-        'Verify the pretty-printing of True, False and None'
-        self.assertGdbRepr(True)
-        self.assertGdbRepr(False)
-        self.assertGdbRepr(None)
-
-    def test_dicts(self):
-        'Verify the pretty-printing of dictionaries'
-        self.assertGdbRepr({})
-        self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}")
-        # Python preserves insertion order since 3.6
-        self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'foo': 'bar', 'douglas': 42}")
-
-    def test_lists(self):
-        'Verify the pretty-printing of lists'
-        self.assertGdbRepr([])
-        self.assertGdbRepr(list(range(5)))
-
-    def test_bytes(self):
-        'Verify the pretty-printing of bytes'
-        self.assertGdbRepr(b'')
-        self.assertGdbRepr(b'And now for something hopefully the same')
-        self.assertGdbRepr(b'string with embedded NUL here \0 and then some more text')
-        self.assertGdbRepr(b'this is a tab:\t'
-                           b' this is a slash-N:\n'
-                           b' this is a slash-R:\r'
-                           )
-
-        self.assertGdbRepr(b'this is byte 255:\xff and byte 128:\x80')
-
-        self.assertGdbRepr(bytes([b for b in range(255)]))
-
-    def test_strings(self):
-        'Verify the pretty-printing of unicode strings'
-        encoding = locale.getpreferredencoding()
-        def check_repr(text):
-            try:
-                text.encode(encoding)
-                printable = True
-            except UnicodeEncodeError:
-                self.assertGdbRepr(text, ascii(text))
-            else:
-                self.assertGdbRepr(text)
-
-        self.assertGdbRepr('')
-        self.assertGdbRepr('And now for something hopefully the same')
-        self.assertGdbRepr('string with embedded NUL here \0 and then some more text')
-
-        # Test printing a single character:
-        #    U+2620 SKULL AND CROSSBONES
-        check_repr('\u2620')
-
-        # Test printing a Japanese unicode string
-        # (I believe this reads "mojibake", using 3 characters from the CJK
-        # Unified Ideographs area, followed by U+3051 HIRAGANA LETTER KE)
-        check_repr('\u6587\u5b57\u5316\u3051')
-
-        # Test a character outside the BMP:
-        #    U+1D121 MUSICAL SYMBOL C CLEF
-        # This is:
-        # UTF-8: 0xF0 0x9D 0x84 0xA1
-        # UTF-16: 0xD834 0xDD21
-        check_repr(chr(0x1D121))
-
-    def test_tuples(self):
-        'Verify the pretty-printing of tuples'
-        self.assertGdbRepr(tuple(), '()')
-        self.assertGdbRepr((1,), '(1,)')
-        self.assertGdbRepr(('foo', 'bar', 'baz'))
-
-    def test_sets(self):
-        'Verify the pretty-printing of sets'
-        if (gdb_major_version, gdb_minor_version) < (7, 3):
-            self.skipTest("pretty-printing of sets needs gdb 7.3 or later")
-        self.assertGdbRepr(set(), "set()")
-        self.assertGdbRepr(set(['a']), "{'a'}")
-        # PYTHONHASHSEED is need to get the exact frozenset item order
-        if not sys.flags.ignore_environment:
-            self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}")
-            self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}")
-
-        # Ensure that we handle sets containing the "dummy" key value,
-        # which happens on deletion:
-        gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b'])
-s.remove('a')
-id(s)''')
-        self.assertEqual(gdb_repr, "{'b'}")
-
-    def test_frozensets(self):
-        'Verify the pretty-printing of frozensets'
-        if (gdb_major_version, gdb_minor_version) < (7, 3):
-            self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later")
-        self.assertGdbRepr(frozenset(), "frozenset()")
-        self.assertGdbRepr(frozenset(['a']), "frozenset({'a'})")
-        # PYTHONHASHSEED is need to get the exact frozenset item order
-        if not sys.flags.ignore_environment:
-            self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})")
-            self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})")
-
-    def test_exceptions(self):
-        # Test a RuntimeError
-        gdb_repr, gdb_output = self.get_gdb_repr('''
-try:
-    raise RuntimeError("I am an error")
-except RuntimeError as e:
-    id(e)
-''')
-        self.assertEqual(gdb_repr,
-                         "RuntimeError('I am an error',)")
-
-
-        # Test division by zero:
-        gdb_repr, gdb_output = self.get_gdb_repr('''
-try:
-    a = 1 / 0
-except ZeroDivisionError as e:
-    id(e)
-''')
-        self.assertEqual(gdb_repr,
-                         "ZeroDivisionError('division by zero',)")
-
-    def test_modern_class(self):
-        'Verify the pretty-printing of new-style class instances'
-        gdb_repr, gdb_output = self.get_gdb_repr('''
-class Foo:
-    pass
-foo = Foo()
-foo.an_int = 42
-id(foo)''')
-        m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
-        self.assertTrue(m,
-                        msg='Unexpected new-style class rendering %r' % gdb_repr)
-
-    def test_subclassing_list(self):
-        'Verify the pretty-printing of an instance of a list subclass'
-        gdb_repr, gdb_output = self.get_gdb_repr('''
-class Foo(list):
-    pass
-foo = Foo()
-foo += [1, 2, 3]
-foo.an_int = 42
-id(foo)''')
-        m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
-
-        self.assertTrue(m,
-                        msg='Unexpected new-style class rendering %r' % gdb_repr)
-
-    def test_subclassing_tuple(self):
-        'Verify the pretty-printing of an instance of a tuple subclass'
-        # This should exercise the negative tp_dictoffset code in the
-        # new-style class support
-        gdb_repr, gdb_output = self.get_gdb_repr('''
-class Foo(tuple):
-    pass
-foo = Foo((1, 2, 3))
-foo.an_int = 42
-id(foo)''')
-        m = re.match(r'<Foo\(an_int=42\) at remote 0x-?[0-9a-f]+>', gdb_repr)
-
-        self.assertTrue(m,
-                        msg='Unexpected new-style class rendering %r' % gdb_repr)
-
-    def assertSane(self, source, corruption, exprepr=None):
-        '''Run Python under gdb, corrupting variables in the inferior process
-        immediately before taking a backtrace.
-
-        Verify that the variable's representation is the expected failsafe
-        representation'''
-        if corruption:
-            cmds_after_breakpoint=[corruption, 'backtrace']
-        else:
-            cmds_after_breakpoint=['backtrace']
-
-        gdb_repr, gdb_output = \
-            self.get_gdb_repr(source,
-                              cmds_after_breakpoint=cmds_after_breakpoint)
-        if exprepr:
-            if gdb_repr == exprepr:
-                # gdb managed to print the value in spite of the corruption;
-                # this is good (see http://bugs.python.org/issue8330)
-                return
-
-        # Match anything for the type name; 0xDEADBEEF could point to
-        # something arbitrary (see  http://bugs.python.org/issue8330)
-        pattern = '<.* at remote 0x-?[0-9a-f]+>'
-
-        m = re.match(pattern, gdb_repr)
-        if not m:
-            self.fail('Unexpected gdb representation: %r\n%s' % \
-                          (gdb_repr, gdb_output))
-
-    def test_NULL_ptr(self):
-        'Ensure that a NULL PyObject* is handled gracefully'
-        gdb_repr, gdb_output = (
-            self.get_gdb_repr('id(42)',
-                              cmds_after_breakpoint=['set variable v=0',
-                                                     'backtrace'])
-            )
-
-        self.assertEqual(gdb_repr, '0x0')
-
-    def test_NULL_ob_type(self):
-        'Ensure that a PyObject* with NULL ob_type is handled gracefully'
-        self.assertSane('id(42)',
-                        'set v->ob_type=0')
-
-    def test_corrupt_ob_type(self):
-        'Ensure that a PyObject* with a corrupt ob_type is handled gracefully'
-        self.assertSane('id(42)',
-                        'set v->ob_type=0xDEADBEEF',
-                        exprepr='42')
-
-    def test_corrupt_tp_flags(self):
-        'Ensure that a PyObject* with a type with corrupt tp_flags is handled'
-        self.assertSane('id(42)',
-                        'set v->ob_type->tp_flags=0x0',
-                        exprepr='42')
-
-    def test_corrupt_tp_name(self):
-        'Ensure that a PyObject* with a type with corrupt tp_name is handled'
-        self.assertSane('id(42)',
-                        'set v->ob_type->tp_name=0xDEADBEEF',
-                        exprepr='42')
-
-    def test_builtins_help(self):
-        'Ensure that the new-style class _Helper in site.py can be handled'
-
-        if sys.flags.no_site:
-            self.skipTest("need site module, but -S option was used")
-
-        # (this was the issue causing tracebacks in
-        #  http://bugs.python.org/issue8032#msg100537 )
-        gdb_repr, gdb_output = self.get_gdb_repr('id(__builtins__.help)', import_site=True)
-
-        m = re.match(r'<_Helper at remote 0x-?[0-9a-f]+>', gdb_repr)
-        self.assertTrue(m,
-                        msg='Unexpected rendering %r' % gdb_repr)
-
-    def test_selfreferential_list(self):
-        '''Ensure that a reference loop involving a list doesn't lead proxyval
-        into an infinite loop:'''
-        gdb_repr, gdb_output = \
-            self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; id(a)")
-        self.assertEqual(gdb_repr, '[3, 4, 5, [...]]')
-
-        gdb_repr, gdb_output = \
-            self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; id(a)")
-        self.assertEqual(gdb_repr, '[3, 4, 5, [[...]]]')
-
-    def test_selfreferential_dict(self):
-        '''Ensure that a reference loop involving a dict doesn't lead proxyval
-        into an infinite loop:'''
-        gdb_repr, gdb_output = \
-            self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; id(a)")
-
-        self.assertEqual(gdb_repr, "{'foo': {'bar': {...}}}")
-
-    def test_selfreferential_old_style_instance(self):
-        gdb_repr, gdb_output = \
-            self.get_gdb_repr('''
-class Foo:
-    pass
-foo = Foo()
-foo.an_attr = foo
-id(foo)''')
-        self.assertTrue(re.match(r'<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>',
-                                 gdb_repr),
-                        'Unexpected gdb representation: %r\n%s' % \
-                            (gdb_repr, gdb_output))
-
-    def test_selfreferential_new_style_instance(self):
-        gdb_repr, gdb_output = \
-            self.get_gdb_repr('''
-class Foo(object):
-    pass
-foo = Foo()
-foo.an_attr = foo
-id(foo)''')
-        self.assertTrue(re.match(r'<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>',
-                                 gdb_repr),
-                        'Unexpected gdb representation: %r\n%s' % \
-                            (gdb_repr, gdb_output))
-
-        gdb_repr, gdb_output = \
-            self.get_gdb_repr('''
-class Foo(object):
-    pass
-a = Foo()
-b = Foo()
-a.an_attr = b
-b.an_attr = a
-id(a)''')
-        self.assertTrue(re.match(r'<Foo\(an_attr=<Foo\(an_attr=<\.\.\.>\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>',
-                                 gdb_repr),
-                        'Unexpected gdb representation: %r\n%s' % \
-                            (gdb_repr, gdb_output))
-
-    def test_truncation(self):
-        'Verify that very long output is truncated'
-        gdb_repr, gdb_output = self.get_gdb_repr('id(list(range(1000)))')
-        self.assertEqual(gdb_repr,
-                         "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, "
-                         "14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, "
-                         "27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, "
-                         "40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, "
-                         "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, "
-                         "66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, "
-                         "79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, "
-                         "92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, "
-                         "104, 105, 106, 107, 108, 109, 110, 111, 112, 113, "
-                         "114, 115, 116, 117, 118, 119, 120, 121, 122, 123, "
-                         "124, 125, 126, 127, 128, 129, 130, 131, 132, 133, "
-                         "134, 135, 136, 137, 138, 139, 140, 141, 142, 143, "
-                         "144, 145, 146, 147, 148, 149, 150, 151, 152, 153, "
-                         "154, 155, 156, 157, 158, 159, 160, 161, 162, 163, "
-                         "164, 165, 166, 167, 168, 169, 170, 171, 172, 173, "
-                         "174, 175, 176, 177, 178, 179, 180, 181, 182, 183, "
-                         "184, 185, 186, 187, 188, 189, 190, 191, 192, 193, "
-                         "194, 195, 196, 197, 198, 199, 200, 201, 202, 203, "
-                         "204, 205, 206, 207, 208, 209, 210, 211, 212, 213, "
-                         "214, 215, 216, 217, 218, 219, 220, 221, 222, 223, "
-                         "224, 225, 226...(truncated)")
-        self.assertEqual(len(gdb_repr),
-                         1024 + len('...(truncated)'))
-
-    def test_builtin_method(self):
-        gdb_repr, gdb_output = self.get_gdb_repr('import sys; id(sys.stdout.readlines)')
-        self.assertTrue(re.match(r'<built-in method readlines of _io.TextIOWrapper object at remote 0x-?[0-9a-f]+>',
-                                 gdb_repr),
-                        'Unexpected gdb representation: %r\n%s' % \
-                            (gdb_repr, gdb_output))
-
-    def test_frames(self):
-        gdb_output = self.get_stack_trace('''
-def foo(a, b, c):
-    pass
-
-foo(3, 4, 5)
-id(foo.__code__)''',
-                                          breakpoint='builtin_id',
-                                          cmds_after_breakpoint=['print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)']
-                                          )
-        self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*',
-                                 gdb_output,
-                                 re.DOTALL),
-                        'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output))
-
-@unittest.skipIf(python_is_optimized(),
-                 "Python was compiled with optimizations")
-class PyListTests(DebuggerTests):
-    def assertListing(self, expected, actual):
-        self.assertEndsWith(actual, expected)
-
-    def test_basic_command(self):
-        'Verify that the "py-list" command works'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-list'])
-
-        self.assertListing('   5    \n'
-                           '   6    def bar(a, b, c):\n'
-                           '   7        baz(a, b, c)\n'
-                           '   8    \n'
-                           '   9    def baz(*args):\n'
-                           ' >10        id(42)\n'
-                           '  11    \n'
-                           '  12    foo(1, 2, 3)\n',
-                           bt)
-
-    def test_one_abs_arg(self):
-        'Verify the "py-list" command with one absolute argument'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-list 9'])
-
-        self.assertListing('   9    def baz(*args):\n'
-                           ' >10        id(42)\n'
-                           '  11    \n'
-                           '  12    foo(1, 2, 3)\n',
-                           bt)
-
-    def test_two_abs_args(self):
-        'Verify the "py-list" command with two absolute arguments'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-list 1,3'])
-
-        self.assertListing('   1    # Sample script for use by test_gdb.py\n'
-                           '   2    \n'
-                           '   3    def foo(a, b, c):\n',
-                           bt)
-
-class StackNavigationTests(DebuggerTests):
-    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_pyup_command(self):
-        'Verify that the "py-up" command works'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-up', 'py-up'])
-        self.assertMultilineMatches(bt,
-                                    r'''^.*
-#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
-    baz\(a, b, c\)
-$''')
-
-    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
-    def test_down_at_bottom(self):
-        'Verify handling of "py-down" at the bottom of the stack'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-down'])
-        self.assertEndsWith(bt,
-                            'Unable to find a newer python frame\n')
-
-    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
-    def test_up_at_top(self):
-        'Verify handling of "py-up" at the top of the stack'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-up'] * 5)
-        self.assertEndsWith(bt,
-                            'Unable to find an older python frame\n')
-
-    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_up_then_down(self):
-        'Verify "py-up" followed by "py-down"'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-up', 'py-up', 'py-down'])
-        self.assertMultilineMatches(bt,
-                                    r'''^.*
-#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
-    baz\(a, b, c\)
-#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 10, in baz \(args=\(1, 2, 3\)\)
-    id\(42\)
-$''')
-
-class PyBtTests(DebuggerTests):
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_bt(self):
-        'Verify that the "py-bt" command works'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-bt'])
-        self.assertMultilineMatches(bt,
-                                    r'''^.*
-Traceback \(most recent call first\):
-  <built-in method id of module object .*>
-  File ".*gdb_sample.py", line 10, in baz
-    id\(42\)
-  File ".*gdb_sample.py", line 7, in bar
-    baz\(a, b, c\)
-  File ".*gdb_sample.py", line 4, in foo
-    bar\(a, b, c\)
-  File ".*gdb_sample.py", line 12, in <module>
-    foo\(1, 2, 3\)
-''')
-
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_bt_full(self):
-        'Verify that the "py-bt-full" command works'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-bt-full'])
-        self.assertMultilineMatches(bt,
-                                    r'''^.*
-#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
-    baz\(a, b, c\)
-#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
-    bar\(a, b, c\)
-#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
-    foo\(1, 2, 3\)
-''')
-
-    @unittest.skipUnless(_thread,
-                         "Python was compiled without thread support")
-    def test_threads(self):
-        'Verify that "py-bt" indicates threads that are waiting for the GIL'
-        cmd = '''
-from threading import Thread
-
-class TestThread(Thread):
-    # These threads would run forever, but we'll interrupt things with the
-    # debugger
-    def run(self):
-        i = 0
-        while 1:
-             i += 1
-
-t = {}
-for i in range(4):
-   t[i] = TestThread()
-   t[i].start()
-
-# Trigger a breakpoint on the main thread
-id(42)
-
-'''
-        # Verify with "py-bt":
-        gdb_output = self.get_stack_trace(cmd,
-                                          cmds_after_breakpoint=['thread apply all py-bt'])
-        self.assertIn('Waiting for the GIL', gdb_output)
-
-        # Verify with "py-bt-full":
-        gdb_output = self.get_stack_trace(cmd,
-                                          cmds_after_breakpoint=['thread apply all py-bt-full'])
-        self.assertIn('Waiting for the GIL', gdb_output)
-
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    # Some older versions of gdb will fail with
-    #  "Cannot find new threads: generic error"
-    # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
-    @unittest.skipUnless(_thread,
-                         "Python was compiled without thread support")
-    def test_gc(self):
-        'Verify that "py-bt" indicates if a thread is garbage-collecting'
-        cmd = ('from gc import collect\n'
-               'id(42)\n'
-               'def foo():\n'
-               '    collect()\n'
-               'def bar():\n'
-               '    foo()\n'
-               'bar()\n')
-        # Verify with "py-bt":
-        gdb_output = self.get_stack_trace(cmd,
-                                          cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt'],
-                                          )
-        self.assertIn('Garbage-collecting', gdb_output)
-
-        # Verify with "py-bt-full":
-        gdb_output = self.get_stack_trace(cmd,
-                                          cmds_after_breakpoint=['break update_refs', 'continue', 'py-bt-full'],
-                                          )
-        self.assertIn('Garbage-collecting', gdb_output)
-
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    # Some older versions of gdb will fail with
-    #  "Cannot find new threads: generic error"
-    # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
-    @unittest.skipUnless(_thread,
-                         "Python was compiled without thread support")
-    def test_pycfunction(self):
-        'Verify that "py-bt" displays invocations of PyCFunction instances'
-        # Tested function must not be defined with METH_NOARGS or METH_O,
-        # otherwise call_function() doesn't call PyCFunction_Call()
-        cmd = ('from time import gmtime\n'
-               'def foo():\n'
-               '    gmtime(1)\n'
-               'def bar():\n'
-               '    foo()\n'
-               'bar()\n')
-        # Verify with "py-bt":
-        gdb_output = self.get_stack_trace(cmd,
-                                          breakpoint='time_gmtime',
-                                          cmds_after_breakpoint=['bt', 'py-bt'],
-                                          )
-        self.assertIn('<built-in method gmtime', gdb_output)
-
-        # Verify with "py-bt-full":
-        gdb_output = self.get_stack_trace(cmd,
-                                          breakpoint='time_gmtime',
-                                          cmds_after_breakpoint=['py-bt-full'],
-                                          )
-        self.assertIn('#2 <built-in method gmtime', gdb_output)
-
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_wrapper_call(self):
-        cmd = textwrap.dedent('''
-            class MyList(list):
-                def __init__(self):
-                    super().__init__()   # toxygen_wrapper_call()
-
-            id("first break point")
-            l = MyList()
-        ''')
-        # Verify with "py-bt":
-        gdb_output = self.get_stack_trace(cmd,
-                                          cmds_after_breakpoint=['break toxygen_wrapper_call', 'continue', 'py-bt'])
-        self.assertRegex(gdb_output,
-                         r"<method-wrapper u?'__init__' of MyList object at ")
-
-
-class PyPrintTests(DebuggerTests):
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_basic_command(self):
-        'Verify that the "py-print" command works'
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-up', 'py-print args'])
-        self.assertMultilineMatches(bt,
-                                    r".*\nlocal 'args' = \(1, 2, 3\)\n.*")
-
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
-    def test_print_after_up(self):
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-up', 'py-up', 'py-print c', 'py-print b', 'py-print a'])
-        self.assertMultilineMatches(bt,
-                                    r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*")
-
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_printing_global(self):
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-up', 'py-print __name__'])
-        self.assertMultilineMatches(bt,
-                                    r".*\nglobal '__name__' = '__main__'\n.*")
-
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_printing_builtin(self):
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-up', 'py-print len'])
-        self.assertMultilineMatches(bt,
-                                    r".*\nbuiltin 'len' = <built-in method len of module object at remote 0x-?[0-9a-f]+>\n.*")
-
-class PyLocalsTests(DebuggerTests):
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_basic_command(self):
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-up', 'py-locals'])
-        self.assertMultilineMatches(bt,
-                                    r".*\nargs = \(1, 2, 3\)\n.*")
-
-    @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands")
-    @unittest.skipIf(python_is_optimized(),
-                     "Python was compiled with optimizations")
-    def test_locals_after_up(self):
-        bt = self.get_stack_trace(script=self.get_sample_script(),
-                                  cmds_after_breakpoint=['py-up', 'py-up', 'py-locals'])
-        self.assertMultilineMatches(bt,
-                                    r".*\na = 1\nb = 2\nc = 3\n.*")
-
-def test_main():
-    if support.verbose:
-        print("GDB version %s.%s:" % (gdb_major_version, gdb_minor_version))
-        for line in gdb_version.splitlines():
-            print(" " * 4 + line)
-    run_unittest(PrettyPrintTests,
-                 PyListTests,
-                 StackNavigationTests,
-                 PyBtTests,
-                 PyPrintTests,
-                 PyLocalsTests
-                 )
-
-if __name__ == "__main__":
-    test_main()
diff --git a/toxygen/tests/test_gdb.urls b/toxygen/tests/test_gdb.urls
deleted file mode 100644
index 5f2cb10..0000000
--- a/toxygen/tests/test_gdb.urls
+++ /dev/null
@@ -1 +0,0 @@
-https://github.com/akheron/cpython/raw/master/Lib/test/test_gdb.py
diff --git a/toxygen/tests/tests_socks.py b/toxygen/tests/tests_socks.py
deleted file mode 100644
index 1551557..0000000
--- a/toxygen/tests/tests_socks.py
+++ /dev/null
@@ -1,1885 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-#
-# @file   tests.py
-# @author Wei-Ning Huang (AZ) <aitjcize@gmail.com>
-#
-# Copyright (C) 2013 - 2014 Wei-Ning Huang (AZ) <aitjcize@gmail.com>
-# All Rights reserved.
-#
-# 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, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-
-"""Originaly from https://github.com/oxij/PyTox c-toxcore-02 branch
-which itself was forked from https://github.com/aitjcize/PyTox/
-
-Modified to work with
-"""
-
-import ctypes
-import faulthandler
-import hashlib
-import logging
-import os
-import random
-import re
-import sys
-import threading
-import traceback
-import unittest
-from ctypes import *
-
-faulthandler.enable()
-
-import warnings
-
-warnings.filterwarnings('ignore')
-
-try:
-    from io import BytesIO
-
-    import certifi
-    import pycurl
-except ImportError:
-    pycurl = None
-
-try:
-    import coloredlogs
-    os.environ['COLOREDLOGS_LEVEL_STYLES'] = 'spam=22;debug=28;verbose=34;notice=220;warning=202;success=118,bold;error=124;critical=background=red'
-except ImportError as e:
-    logging.log(logging.DEBUG, f"coloredlogs not available:  {e}")
-    coloredlogs = None
-
-try:
-    import color_runner
-except ImportError as e:
-    logging.log(logging.DEBUG, f"color_runner not available:  {e}")
-    color_runner = None
-
-import toxygen_wrapper
-import toxygen_wrapper.toxcore_enums_and_consts as enums
-from toxygen_wrapper.tox import Tox
-from toxygen_wrapper.toxcore_enums_and_consts import (TOX_ADDRESS_SIZE, TOX_CONNECTION,
-                                              TOX_FILE_CONTROL,
-                                              TOX_MESSAGE_TYPE,
-                                              TOX_SECRET_KEY_SIZE,
-                                              TOX_USER_STATUS)
-
-try:
-    import support_testing as ts
-except ImportError:
-    import toxygen_wrapper.tests.support_testing as ts
-
-try:
-    from tests.toxygen_tests import test_sound_notification
-    bIS_NOT_TOXYGEN = False
-except ImportError:
-    bIS_NOT_TOXYGEN = True
-
-# from  qtpy import QtCore
-import time
-
-sleep = time.sleep
-
-global LOG
-LOG = logging.getLogger('TestS')
-# just print to stdout so there is no complications from logging.
-def LOG_ERROR(l): print('EROR+ '+l)
-def LOG_WARN(l): print('WARN+ '+l)
-def LOG_INFO(l): print('INFO+ '+l)
-def LOG_DEBUG(l): print('DEBUG+ '+l)
-def LOG_TRACE(l): pass # print('TRAC+ '+l)
-
-ADDR_SIZE = 38 * 2
-CLIENT_ID_SIZE = 32 * 2
-THRESHOLD = 25
-
-global     oTOX_OPTIONS
-oTOX_OPTIONS = {}
-
-bIS_LOCAL = 'new' in sys.argv or 'main' in sys.argv or 'newlocal' in sys.argv
-
-# Patch unittest for Python version <= 2.6
-if not hasattr(unittest, 'skip'):
-    def unittest_skip(reason):
-        def _wrap1(func):
-            def _wrap2(self, *args, **kwargs):
-                pass
-            return _wrap2
-        return _wrap1
-    unittest.skip = unittest_skip
-
-if not hasattr(unittest, 'expectedFailureIf'):
-    def unittest_expectedFailureIf(condition, reason):
-        def _wrap1(test_item):
-            def _wrap2(self, *args, **kwargs):
-                if condition:
-                    test_item.__unittest_expecting_failure__ = True
-                pass
-            return _wrap2
-        return _wrap1
-
-    unittest.expectedFailureIf = unittest_expectedFailureIf
-
-def expectedFailure(test_item):
-    test_item.__unittest_expecting_failure__ = True
-    return test_item
-
-class ToxOptions():
-    def __init__(self):
-        self.ipv6_enabled = True
-        self.udp_enabled = True
-        self.proxy_type = 0
-        self.proxy_host = ''
-        self.proxy_port = 0
-        self.start_port = 0
-        self.end_port = 0
-        self.tcp_port = 0
-        self.savedata_type = 0  # 1=toxsave, 2=secretkey
-        self.savedata_data = b''
-        self.savedata_length = 0
-        self.local_discovery_enabled = False
-        self.dht_announcements_enabled = True
-        self.hole_punching_enabled = False
-        self.experimental_thread_safety = False
-
-class App():
-    def __init__(self):
-        self.mode = 0
-oAPP = App()
-
-class AliceTox(Tox):
-
-    def __init__(self, opts, app=None):
-
-        super(AliceTox, self).__init__(opts, app=app)
-        self._address = self.self_get_address()
-        self.name = 'alice'
-        self._opts = opts
-        self._app = app
-
-class BobTox(Tox):
-
-    def __init__(self, opts, app=None):
-        super(BobTox, self).__init__(opts, app=app)
-        self._address = self.self_get_address()
-        self.name = 'bob'
-        self._opts = opts
-        self._app = app
-
-class BaseThread(threading.Thread):
-
-    def __init__(self, name=None, target=None):
-        if name:
-            super().__init__(name=name, target=target)
-        else:
-            super().__init__(target=target)
-        self._stop_thread = False
-        self.name = name
-
-    def stop_thread(self, timeout=-1):
-        self._stop_thread = True
-        if timeout < 0:
-            timeout = ts.iTHREAD_TIMEOUT
-        i = 0
-        while i < ts.iTHREAD_JOINS:
-            self.join(timeout)
-            if not self.is_alive(): break
-            i = i + 1
-        else:
-            LOG.warning(f"{self.name} BLOCKED")
-
-class ToxIterateThread(BaseThread):
-
-    def __init__(self, tox):
-        super().__init__(name='ToxIterateThread')
-        self._tox = tox
-
-    def run(self):
-        while not self._stop_thread:
-            self._tox.iterate()
-            sleep(self._tox.iteration_interval() / 1000)
-
-global bob, alice
-bob = alice = None
-
-def prepare(self):
-    global bob, alice
-    def bobs_on_self_connection_status(iTox, connection_state, *args):
-        status = connection_state
-        self.bob.dht_connected = status
-        self.bob.mycon_time = time.time()
-        try:
-            if status != TOX_CONNECTION['NONE']:
-                LOG_DEBUG(f"bobs_on_self_connection_status TRUE {status}" \
-                          +f" last={int(self.bob.mycon_time)}" )
-                self.bob.mycon_status = True
-            else:
-                LOG_DEBUG(f"bobs_on_self_connection_status FALSE {status}" \
-                          +f" last={int(self.bob.mycon_time)}" )
-                self.bob.mycon_status = False
-        except Exception as e:
-            LOG_ERROR(f"bobs_on_self_connection_status {e}")
-        else:
-            if self.bob.self_get_connection_status() != status:
-                LOG_WARN(f"bobs_on_self_connection_status DISAGREE {status}")
-
-    def alices_on_self_connection_status(iTox, connection_state, *args):
-        #FixMe connection_num
-        status = connection_state
-        self.alice.dht_connected = status
-        self.alice.mycon_time = time.time()
-        try:
-            if status != TOX_CONNECTION['NONE']:
-                LOG_DEBUG(f"alices_on_self_connection_status TRUE {status}" \
-                          +f" last={int(self.alice.mycon_time)}" )
-                self.alice.mycon_status = True
-            else:
-                LOG_WARN(f"alices_on_self_connection_status FALSE {status}" \
-                          +f" last={int(self.alice.mycon_time)}" )
-                self.alice.mycon_status = False
-        except Exception as e:
-            LOG_ERROR(f"alices_on_self_connection_status error={e}")
-        else:
-            if self.alice.self_get_connection_status() != status:
-                LOG_WARN(f"alices_on_self_connection_status != {status}")
-        self.alice.dht_connected = status
-
-    opts = oToxygenToxOptions(oTOX_OARGS)
-    alice = AliceTox(opts, app=oAPP)
-    alice.oArgs = opts
-    alice.dht_connected = -1
-    alice.mycon_status = False
-    alice.mycon_time = 1
-    alice.callback_self_connection_status(alices_on_self_connection_status)
-
-    bob = BobTox(opts, app=oAPP)
-    bob.oArgs = opts
-    bob.dht_connected = -1
-    bob.mycon_status = False
-    bob.mycon_time = 1
-    bob.callback_self_connection_status(bobs_on_self_connection_status)
-    if not bIS_LOCAL and not ts.bAreWeConnected():
-        LOG.warning(f"doOnce not local and NOT CONNECTED")
-    return [bob, alice]
-
-class ToxSuite(unittest.TestCase):
-    failureException = RuntimeError
-
-    @classmethod
-    def setUpClass(cls):
-        global oTOX_OARGS
-        assert oTOX_OPTIONS
-        assert oTOX_OARGS
-
-        if not hasattr(cls, 'alice') and not hasattr(cls, 'bob'):
-            l = prepare(cls)
-            assert l
-            cls.bob, cls.alice = l
-        if not hasattr(cls.bob, '_main_loop'):
-            cls.bob._main_loop = ToxIterateThread(cls.bob)
-            cls.bob._main_loop.start()
-            LOG.debug(f"cls.bob._main_loop: ") # {threading.enumerate()}
-        if not hasattr(cls.alice, '_main_loop'):
-            cls.alice._main_loop = ToxIterateThread(cls.alice)
-            cls.alice._main_loop.start()
-            LOG.debug(f"cls.alice._main_loop: ") # {threading.enumerate()}
-
-        cls.lUdp = ts.generate_nodes(
-            oArgs=oTOX_OARGS,
-            nodes_count=2*ts.iNODES,
-            ipv='ipv4',
-            udp_not_tcp=True)
-
-        cls.lTcp = ts.generate_nodes(
-            oArgs=oTOX_OARGS,
-            nodes_count=2*ts.iNODES,
-            ipv='ipv4',
-            udp_not_tcp=False)
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.bob._main_loop.stop_thread()
-        cls.alice._main_loop.stop_thread()
-        if False:
-            cls.alice.kill()
-            cls.bob.kill()
-            del         cls.bob
-            del         cls.alice
-
-    def setUp(self):
-        """
-        """
-        if hasattr(self, 'baid') and self.baid >= 0 and \
-          self.baid in self.bob.self_get_friend_list():
-            LOG.warn(f"setUp ALICE IS ALREADY IN BOBS FRIEND LIST")
-        elif  self.bob.self_get_friend_list_size() >= 1:
-            LOG.warn(f"setUp BOB STILL HAS A FRIEND LIST")
-
-        if hasattr(self, 'abid') and self.abid >= 0 and \
-          self.abid in self.alice.self_get_friend_list():
-            LOG.warn(f"setUp BOB IS ALREADY IN ALICES FRIEND LIST")
-        elif self.alice.self_get_friend_list_size() >= 1:
-            LOG.warn(f"setUp ALICE STILL HAS A FRIEND LIST")
-
-    def tearDown(self):
-        """
-        """
-        if hasattr(self, 'baid') and self.baid >= 0 and \
-          self.baid in self.bob.self_get_friend_list():
-            LOG.warn(f"tearDown ALICE IS STILL IN BOBS FRIEND LIST")
-        elif self.bob.self_get_friend_list_size() >= 1:
-            LOG.warn(f"tearDown BOBS STILL HAS A FRIEND LIST")
-
-        if hasattr(self, 'abid') and self.abid >= 0 and \
-          self.abid in self.alice.self_get_friend_list():
-            LOG.warn(f"tearDown BOB IS STILL IN ALICES FRIEND LIST")
-        elif self.bob.self_get_friend_list_size() >= 1:
-            LOG.warn(f"tearDown ALICE STILL HAS A FRIEND LIST")
-
-    def run(self, result=None):
-        """ Stop after first error """
-        if not result.errors:
-            super(ToxSuite, self).run(result)
-
-    def get_connection_status(self):
-        if self.bob.mycon_time == -1 or self.alice.mycon_time == -1:
-            pass
-            # drop through
-        elif self.bob.dht_connected == TOX_CONNECTION['NONE']:
-            return False
-        elif self.alice.dht_connected == TOX_CONNECTION['NONE']:
-            return False
-
-        # if not self.connected
-        if self.bob.self_get_connection_status() == TOX_CONNECTION['NONE']:
-            return False
-        if self.alice.self_get_connection_status() == TOX_CONNECTION['NONE']:
-            return False
-        return True
-
-    def loop(self, n):
-        """
-        t:iterate
-        t:iteration_interval
-        """
-        interval = self.bob.iteration_interval()
-        for i in range(n):
-            self.alice.iterate()
-            self.bob.iterate()
-            sleep(interval / 1000.0)
-
-    def call_bootstrap(self, num=None, lToxes=None, i=0):
-        if num == None: num=ts.iNODES
-#        LOG.debug(f"call_bootstrap network={oTOX_OARGS.network}")
-        if oTOX_OARGS.network in ['new', 'newlocal', 'localnew']:
-            ts.bootstrap_local(self.lUdp, [self.alice, self.bob])
-        elif not ts.bAreWeConnected():
-            LOG.warning('we are NOT CONNECTED')
-        else:
-            random.shuffle(self.lUdp)
-            if oTOX_OARGS.proxy_port > 0:
-                lElts = self.lUdp[:1]
-            else:
-                lElts = self.lUdp[:num+i]
-            LOG.debug(f"call_bootstrap ts.bootstrap_udp {len(lElts)}")
-            if lToxes is None: lToxes = [self.alice, self.bob]
-            ts.bootstrap_udp(lElts, lToxes)
-            random.shuffle(self.lTcp)
-            lElts = self.lTcp[:num+i]
-            LOG.debug(f"call_bootstrap ts.bootstrap_tcp {len(lElts)}")
-            ts.bootstrap_tcp(lElts, lToxes)
-
-    def loop_until_connected(self, num=None):
-        """
-        t:on_self_connection_status
-        t:self_get_connection_status
-        """
-        i = 0
-        bRet = None
-        while i <= THRESHOLD :
-            if (self.alice.mycon_status and self.bob.mycon_status):
-                bRet = True
-                break
-            if i % 5 == 0:
-                j = i//5
-                self.call_bootstrap(num, lToxes=None, i=j)
-                s = ''
-                if i == 0: s = '\n'
-                LOG.info(s+"loop_until_connected " \
-                         +" #" + str(i) \
-                         +" BOB=" +repr(self.bob.self_get_connection_status()) \
-                         +" ALICE=" +repr(self.alice.self_get_connection_status())
-                         +f" BOBS={self.bob.mycon_status}" \
-                         +f" ALICES={self.alice.mycon_status}" \
-                         +f" last={int(self.bob.mycon_time)}" )
-            if (self.alice.mycon_status and self.bob.mycon_status):
-                bRet = True
-                break
-            if (self.alice.self_get_connection_status() and
-                self.bob.self_get_connection_status()):
-                LOG_WARN(f"loop_until_connected disagree status() DISAGREE" \
-                         +f' self.bob.mycon_status={self.bob.mycon_status}' \
-                         +f' alice.mycon_status={self.alice.mycon_status}' \
-                         +f" last={int(self.bob.mycon_time)}" )
-                bRet = True
-                break
-            i += 1
-            self.loop(100)
-        else:
-            bRet = False
-
-        if bRet or \
-            ( self.bob.self_get_connection_status() != TOX_CONNECTION['NONE'] and \
-              self.alice.self_get_connection_status() != TOX_CONNECTION['NONE'] ):
-            LOG.info(f"loop_until_connected returning True {i}" \
-                     +f" BOB={self.bob.self_get_connection_status()}" \
-                     +f" ALICE={self.alice.self_get_connection_status()}" \
-                     +f" last={int(self.bob.mycon_time)}" )
-            return True
-        else:
-            LOG.warning(f"loop_until_connected returning False {i}" \
-                     +f" BOB={self.bob.self_get_connection_status()}" \
-                     +f" ALICE={self.alice.self_get_connection_status()}" \
-                     +f" last={int(self.bob.mycon_time)}" )
-            return False
-
-    def wait_obj_attr(self, obj, attr):
-        return wait_otox_attrs(self, obj, [attr])
-
-    def wait_objs_attr(self, objs, attr):
-        i = 0
-        while i <= THRESHOLD:
-            if i % 5 == 0:
-                num = None
-                j = i//5
-                self.call_bootstrap(num, objs, i=j)
-                LOG.debug("wait_objs_attr " +repr(objs) \
-                             +" for " +repr(attr) \
-                             +" " +str(i))
-            if all([getattr(obj, attr) for obj in objs]):
-                return True
-            self.loop(100)
-            i += 1
-        else:
-            LOG.error(f"wait_obj_attr i >=  {THRESHOLD}")
-
-        return all([getattr(obj, attr) for obj in objs])
-
-    def wait_otox_attrs(self, obj, attrs):
-        i = 0
-        while i <= THRESHOLD:
-            if i % 5 == 0:
-                num = None
-                j = 0
-                if obj.mycon_time == 1:
-                    num = 4
-                    j = i//5
-                self.call_bootstrap(num, [obj], i=j)
-                LOG.debug(f"wait_otox_attrs {obj.name} for {attrs} {i}" \
-                         +f" last={int(obj.mycon_time)}")
-            if all([getattr(obj, attr) is not None for attr in attrs]):
-                return True
-            self.loop(100)
-            i += 1
-        else:
-            LOG.warning(f"wait_otox_attrs i >= {THRESHOLD}")
-
-        return all([getattr(obj, attr) for attr in attrs])
-
-    def wait_ensure_exec(self, method, args):
-        i = 0
-        oRet = None
-        while i <= THRESHOLD:
-            if i % 5 == 0:
-                j = i//5
-                self.call_bootstrap(num=None, lToxes=None, i=j)
-                LOG.debug("wait_ensure_exec " \
-                             +" " +str(method)
-                             +" " +str(i))
-            try:
-                oRet = method(*args)
-                if oRet:
-                    LOG.info(f"wait_ensure_exec oRet {oRet}")
-                    return True
-            except ArgumentError as e:
-                #  ArgumentError('This client is currently NOT CONNECTED to the friend.')
-                # dunno
-                LOG.warning(f"wait_ensure_exec ArgumentError {e}")
-                return False
-            except Exception as e:
-                LOG.warning(f"wait_ensure_exec EXCEPTION  {e}")
-                return False
-            sleep(3)
-            i += 1
-        else:
-            LOG.error(f"wait_ensure_exec i >=  {1*THRESHOLD}")
-            return False
-
-        return oRet
-
-    def bob_add_alice_as_friend_norequest(self):
-        if hasattr(self, 'baid') and self.baid >= 0 and \
-          self.baid in self.bob.self_get_friend_list():
-            LOG.warn('Alice is already in bobs friend list')
-            return True
-        if self.bob.self_get_friend_list_size() >= 1:
-            LOG.warn(f'Bob has a friend list {self.bob.self_get_friend_list()}')
-            return True
-
-        MSG = 'Hi, this is Bob.'
-        iRet = self.bob.friend_add_norequest(self.alice._address)
-        self.baid = self.bob.friend_by_public_key(self.alice._address)
-        assert self.baid >= 0, self.baid
-        assert self.bob.friend_exists(self.baid), "bob.friend_exists"
-        assert not self.bob.friend_exists(self.baid + 1)
-        assert self.baid in self.bob.self_get_friend_list()
-        assert self.bob.self_get_friend_list_size() >= 1
-        return iRet >= 0
-
-    def alice_add_bob_as_friend_norequest(self):
-        if hasattr(self, 'abid') and self.abid >= 0 and \
-          self.abid in self.alice.self_get_friend_list():
-            LOG.warn('Alice is already in Bobs friend list')
-            return True
-        if self.alice.self_get_friend_list_size() >= 1:
-            LOG.warn(f'Alice has a friend list {self.alice.self_get_friend_list()}')
-
-        MSG = 'Hi Bob, this is Alice.'
-        iRet = self.alice.friend_add_norequest(self.bob._address)
-        self.abid = self.alice.friend_by_public_key(self.bob._address)
-        assert self.abid >= 0, self.abid
-        assert self.abid in self.alice.self_get_friend_list()
-        assert self.alice.friend_exists(self.abid), "alice.friend_exists"
-        assert not self.alice.friend_exists(self.abid + 1)
-        assert self.alice.self_get_friend_list_size() >= 1
-        return iRet >= 0
-
-    def both_add_as_friend_norequest(self):
-        assert self.bob_add_alice_as_friend_norequest()
-        if not hasattr(self, 'baid') or self.baid < 0:
-            raise AssertionError("both_add_as_friend_norequest bob, 'baid'")
-
-        assert self.alice_add_bob_as_friend_norequest()
-        if not hasattr(self, 'abid') or self.abid < 0:
-            raise AssertionError("both_add_as_friend_norequest alice, 'abid'")
-
-        #: Test last online
-        assert self.alice.friend_get_last_online(self.abid) is not None
-        assert self.bob.friend_get_last_online(self.baid) is not None
-        return True
-
-    def bob_add_alice_as_friend(self):
-        """
-        t:friend_add
-        t:on_friend_request
-        t:friend_by_public_key
-        """
-        MSG = 'Alice, this is Bob.'
-        sSlot = 'friend_request'
-
-        def alices_on_friend_request(iTox,
-                                     public_key,
-                                     message_data,
-                                     message_data_size,
-                                     *largs):
-            LOG_DEBUG(f"alices_on_friend_request: " +repr(message_data))
-            try:
-                assert str(message_data, 'UTF-8') == MSG
-                LOG_INFO(f"alices_on_friend_request: friend_added = True ")
-            except Exception as e:
-                LOG_WARN(f"alices_on_friend_request: Exception  {e}")
-                # return
-            setattr(self.bob, sSlot, True)
-
-        setattr(self.bob, sSlot, None)
-        inum = -1
-        self.alice.callback_friend_request(alices_on_friend_request)
-        try:
-            inum = self.bob.friend_add(self.alice._address, bytes(MSG, 'UTF-8'))
-            if not inum >= 0:
-                LOG.warning('bob.friend_add !>= 0 ' +repr(inum))
-            if not self.wait_otox_attrs(self.bob, [sSlot]):
-                return False
-        except Exception as e:
-            LOG.error(f"bob.friend_add EXCEPTION  {e}")
-            return False
-        finally:
-            self.bob.callback_friend_message(None)
-
-        self.baid = self.bob.friend_by_public_key(self.alice._address)
-        assert self.baid >= 0, self.baid
-        assert self.bob.friend_exists(self.baid)
-        assert not self.bob.friend_exists(self.baid + 1)
-        assert self.baid in self.bob.self_get_friend_list()
-        assert self.bob.self_get_friend_list_size() >= 1
-        return True
-
-    def alice_add_bob_as_friend(self):
-        """
-        t:friend_add
-        t:on_friend_request
-        t:friend_by_public_key
-        """
-        MSG = 'Bob, this is Alice.'
-        sSlot = 'friend_request'
-
-        def bobs_on_friend_request(iTox,
-                                     public_key,
-                                     message_data,
-                                     message_data_size,
-                                     *largs):
-            LOG_DEBUG(f"bobs_on_friend_request: " +repr(message_data))
-            try:
-                assert str(message_data, 'UTF-8') == MSG
-                LOG_INFO(f"bobs_on_friend_request: friend_added = True ")
-            except Exception as e:
-                LOG_WARN(f"bobs_on_friend_request: Exception {e}")
-                # return
-            else:
-                setattr(self.alice, sSlot, True)
-
-        setattr(self.alice, sSlot, None)
-        inum = -1
-        self.bob.callback_friend_request(bobs_on_friend_request)
-        try:
-            inum = self.alice.friend_add(self.bob._address, bytes(MSG, 'UTF-8'))
-            if not inum >= 0:
-                LOG.warning('alice.friend_add !>= 0 ' +repr(inum))
-            if not self.wait_obj_attr(self.alice, sSlot):
-                return False
-        except Exception as e:
-            LOG.error(f"alice.friend_add EXCEPTION {e}")
-            return False
-        finally:
-            self.bob.callback_friend_message(None)
-        self.abid = self.alice.friend_by_public_key(self.bob._address)
-        assert self.abid >= 0, self.abid
-        assert self.alice.friend_exists(self.abid)
-        assert not self.alice.friend_exists(self.abid + 1)
-        assert self.abid in self.alice.self_get_friend_list()
-        assert self.alice.self_get_friend_list_size() >= 1
-        return True
-
-    def both_add_as_friend(self):
-        assert self.bob_add_alice_as_friend()
-        assert self.alice_add_bob_as_friend()
-
-        #: Test last online
-        assert self.alice.friend_get_last_online(self.abid) is not None
-        assert self.bob.friend_get_last_online(self.baid) is not None
-
-    def bob_add_alice_as_friend_and_status(self):
-        if oTOX_OARGS.bIS_LOCAL:
-            assert self.bob_add_alice_as_friend_norequest()
-        else:
-            assert self.bob_add_alice_as_friend()
-
-        #: Wait until both are online
-        self.bob.friend_conn_status = False
-        def bobs_on_friend_connection_status(iTox, friend_id, iStatus, *largs):
-            LOG_INFO(f"bobs_on_friend_connection_status {friend_id} ?>=0" +repr(iStatus))
-            if iStatus > 0:
-                self.bob.friend_conn_status = True
-
-        self.bob.friend_status = None
-        def bobs_on_friend_status(iTox, friend_id, iStatus, *largs):
-            LOG_INFO(f"bobs_on_friend_status {friend_id} ?>=0" +repr(iStatus))
-            if iStatus > 0:
-                self.bob.friend_status = True
-
-        self.alice.friend_conn_status = None
-        def alices_on_friend_connection_status(iTox, friend_id, iStatus, *largs):
-            LOG_INFO(f"alices_on_friend_connection_status {friend_id} ?>=0 " +repr(iStatus))
-            if iStatus > 0:
-                self.alice.friend_conn_status = True
-
-        self.alice.friend_status = False
-        def alices_on_friend_status(iTox, friend_id, iStatus, *largs):
-            LOG_INFO(f"alices_on_friend_status {friend_id} ?>=0 " +repr(iStatus))
-            if iStatus > 0:
-                self.alice.friend_status = True
-
-        self.alice.callback_friend_connection_status(alices_on_friend_connection_status)
-        self.alice.callback_friend_status(alices_on_friend_status)
-        try:
-            LOG.info("bob_add_alice_as_friend_and_status waiting for alice connections")
-            if not self.wait_otox_attrs(self.alice,
-                                            ['friend_conn_status',
-                                             'friend_status']):
-                return False
-
-            self.bob.callback_friend_connection_status(bobs_on_friend_connection_status)
-            self.bob.callback_friend_status(bobs_on_friend_status)
-
-            LOG.info("bob_add_alice_as_friend_and_status waiting for bob connections")
-            if not self.wait_otox_attrs(self.bob,
-                                            ['friend_conn_status',
-                                             'friend_status']):
-                                return False
-        except Exception as e:
-            LOG.error(f"bob_add_alice_as_friend_and_status ERROR   {e}")
-            return False
-        finally:
-            self.alice.callback_friend_connection_status(None)
-            self.bob.callback_friend_connection_status(None)
-            self.alice.callback_friend_status(None)
-            self.bob.callback_friend_status(None)
-        return True
-
-    def friend_delete(self, fname, baid):
-        #: Test delete friend
-        assert getattr(self, fname).friend_exists(baid)
-        getattr(self, fname).friend_delete(baid)
-        self.loop(50)
-        assert not self.bob.friend_exists(baid)
-
-    def warn_if_no_cb(self, alice, sSlot):
-        if not hasattr(alice, sSlot+'_cb') or \
-          not getattr(alice, sSlot+'_cb'):
-            LOG.warning(f"self.bob.{sSlot}_cb NOT EXIST")
-
-    def warn_if_cb(self, alice, sSlot):
-        if hasattr(self.bob, sSlot+'_cb') and \
-          getattr(self.bob, sSlot+'_cb'):
-            LOG.warning(f"self.bob.{sSlot}_cb EXIST")
-
-    # tests are executed in order
-    def test_notice_log(self): # works
-        notice = '/var/lib/tor/.SelekTOR/3xx/cache/9050/notice.log'
-        if True or os.path.exists(notice):
-            iRet = os.system(f"sudo sed -e '1,/.notice. Bootstrapped 100%/d' {notice}" + \
-                              "| grep 'Tried for 120 seconds to get a connection to :0.'")
-            if iRet == 0:
-                raise SystemExit("seconds to get a connection to :0")
-            else:
-                LOG.debug(f"checked {notice}")
-
-    def test_tests_logging(self): # works
-        with self.assertLogs('foo', level='INFO') as cm:
-            logging.getLogger('foo').info('first message')
-            logging.getLogger('foo.bar').error('second message')
-            logging.getLogger('foo.bar.baz').debug('third message')
-            self.assertEqual(cm.output, ['INFO:foo:first message',
-                                         'ERROR:foo.bar:second message'])
-
-    def test_tests_start(self): # works
-        LOG.info("test_tests_start " )
-        port = ts.tox_bootstrapd_port()
-
-        assert len(self.bob._address) == 2*TOX_ADDRESS_SIZE, len(self.bob._address)
-        assert len(self.alice._address) == 2*TOX_ADDRESS_SIZE, \
-            len(self.alice._address)
-
-    def test_bootstrap_local_netstat(self): # works
-        """
-        t:bootstrap
-        """
-        if oTOX_OARGS.network not in ['new', 'newlocal', 'local']:
-            return
-
-        port = ts.tox_bootstrapd_port()
-        if not port:
-            return
-        iStatus = os.system(f"""netstat -nle4 | grep :{port}""")
-        if iStatus == 0:
-            LOG.info(f"bootstrap_local_netstat port {port} iStatus={iStatus}")
-        else:
-            LOG.warning(f"bootstrap_local_netstat NOT {port} iStatus={iStatus}")
-
-    @unittest.skipIf(not bIS_LOCAL, "local test")
-    def test_bootstrap_local(self): # works
-        """
-        t:bootstrap
-        """
-        # get port from /etc/tox-bootstrapd.conf 33445
-        self.call_bootstrap()
-        # ts.bootstrap_local(self, self.lUdp)
-        i = 0
-        iStatus = -1
-        while i < 10:
-            i = i + 1
-            iStatus = self.bob.self_get_connection_status()
-            if iStatus != TOX_CONNECTION['NONE']:
-                break
-            sleep(3)
-        else:
-            pass
-
-        o1 = self.alice.self_get_dht_id()
-        assert len(o1) == 64
-        o2 = self.bob.self_get_dht_id()
-        assert len(o2) == 64
-
-#        if o1 != o2:  LOG.warning(f"bootstrap_local DHT NOT same {o1} {o2} iStatus={iStatus}")
-
-        iStatus = self.bob.self_get_connection_status()
-        if iStatus != TOX_CONNECTION['NONE']:
-            LOG.info(f"bootstrap_local connected iStatus={iStatus}")
-            return True
-        iStatus = self.alice.self_get_connection_status()
-        if iStatus != TOX_CONNECTION['NONE']:
-            LOG.info(f"bootstrap_local connected iStatus={iStatus}")
-            return True
-        LOG.warning(f"bootstrap_local NOT CONNECTED iStatus={iStatus}")
-        return False
-
-    def test_bootstrap_iNmapInfo(self): # works
-        if os.environ['USER'] != 'root':
-            return
-        if oTOX_OARGS.network in ['new', 'newlocal', 'localnew']:
-            lElts = self.lUdp
-        elif oTOX_OARGS.proxy_port > 0:
-            lElts = self.lTcp
-        else:
-            lElts = self.lUdp
-        lRetval = []
-        random.shuffle(lElts)
-        # assert
-        ts.bootstrap_iNmapInfo(lElts, oTOX_OARGS, bIS_LOCAL, iNODES=8)
-
-    def test_self_get_secret_key(self): # works
-        """
-        t:self_get_secret_key
-        """
-        # test_self_get_secret_key
-        CRYPTO_SECRET_KEY_SIZE         = 32
-        secret_key = create_string_buffer(CRYPTO_SECRET_KEY_SIZE)
-        oRet0 =  self.alice.self_get_secret_key(secret_key)
-        assert oRet0, repr(oRet0)
-        LOG.info('test_self_get_secret_key ' +repr(oRet0))
-        assert len(str(oRet0))
-        del secret_key
-
-    def test_self_get_public_keys(self): # works
-        """
-        t:self_get_secret_key
-        t:self_get_public_key
-        """
-
-        LOG.info('test_self_get_public_keys self.alice.self_get_secret_key')
-        oRet0 = self.alice.self_get_secret_key()
-        assert len(oRet0)
-        LOG.info('test_self_get_public_keys ' +repr(oRet0))
-        oRet1 = self.alice.self_get_public_key()
-        assert len(oRet1)
-        LOG.info('test_self_get_public_keys ' +repr(oRet1))
-        assert oRet0 != oRet1, repr(oRet0) +' != ' +repr(oRet1)
-
-    def test_self_name(self): # works
-        """
-        t:self_set_name
-        t:self_get_name
-        t:self_get_name_size
-        """
-        self.alice.self_set_name('Alice')
-        assert self.alice.self_get_name() == 'Alice'
-        assert self.alice.self_get_name_size() == len('Alice')
-        self.bob.self_set_name('Bob')
-        assert self.bob.self_get_name() == 'Bob'
-        assert self.bob.self_get_name_size() == len('Bob')
-
-    @unittest.skip('loud')
-    @unittest.skipIf(bIS_NOT_TOXYGEN or oTOX_OARGS.mode == 0, 'not testing in toxygen')
-    def test_sound_notification(self): # works
-        """
-        Plays sound notification
-        :param  type of notification
-        """
-        from tests.toxygen_tests import test_sound_notification
-        test_sound_notification(self)
-
-    def test_address(self): # works
-        """
-        t:self_get_address
-        t:self_get_nospam
-        t:self_set_nospam
-        t:self_get_keys
-        """
-        assert len(self.alice.self_get_address()) == ADDR_SIZE
-        assert len(self.bob.self_get_address()) == ADDR_SIZE
-
-        self.alice.self_set_nospam(0x12345678)
-        assert self.alice.self_get_nospam() == 0x12345678
-        self.loop(50)
-
-        if hasattr(self.alice, 'self_get_keys'):
-            pk, sk = self.alice.self_get_keys()
-            assert pk == self.alice.self_get_address()[:CLIENT_ID_SIZE]
-
-    def test_status_message(self): # works
-        MSG = 'Happy'
-        self.alice.self_set_status_message(MSG)
-        self.loop(100)
-        assert self.alice.self_get_status_message() == MSG, \
-            self.alice.self_get_status_message() +' is not ' +MSG
-        assert self.alice.self_get_status_message_size() == len(MSG)
-
-    def test_loop_until_connected(self): # works
-        assert self.loop_until_connected()
-
-    def test_self_get_udp_port(self): # works
-        """
-        t:self_get_udp_port
-        """
-        if hasattr(oTOX_OPTIONS, 'udp_port') and oTOX_OPTIONS.udp_port:
-            o = self.alice.self_get_udp_port()
-            LOG.info('self_get_udp_port alice ' +repr(o))
-            assert o > 0
-            o = self.bob.self_get_udp_port()
-            LOG.info('self_get_udp_port bob ' +repr(o))
-            assert o > 0
-
-    def test_self_get_tcp_port(self): # works
-        """
-        t:self_get_tcp_port
-        """
-        if hasattr(oTOX_OPTIONS, 'tcp_port') and oTOX_OPTIONS.tcp_port:
-            # errors if tcp_port <= 0
-            o = self.alice.self_get_tcp_port()
-            LOG.info('self_get_tcp_port ' +repr(o))
-            o = self.bob.self_get_tcp_port()
-            LOG.info('self_get_tcp_port ' +repr(o))
-
-    def test_get_dht_id(self): # works
-        """
-        t:self_get_dht_id
-        """
-        o1 = self.alice.self_get_dht_id()
-        assert len(o1) == 64
-        o2 = self.bob.self_get_dht_id()
-        assert len(o2) == 64
-
-    def test_bob_assert_connection_status(self): # works
-        if self.bob.self_get_connection_status() == TOX_CONNECTION['NONE']:
-            RuntimeError("ERROR: NOT CONNECTED " \
-                         +repr(self.bob.self_get_connection_status()))
-
-    def test_alice_assert_connection_status(self): # works
-        if self.alice.self_get_connection_status() == TOX_CONNECTION['NONE']:
-            RuntimeError("ERROR: NOT CONNECTED " \
-                         +repr(self.alice.self_get_connection_status()))
-
-    def test_bob_assert_mycon_status(self): # works
-        if self.bob.mycon_status == False:
-            RuntimeError("ERROR: NOT CONNECTED " \
-                         +repr(self.bob.mycon_status))
-
-    def test_alice_assert_mycon_status(self): # works
-        if self.alice.mycon_status == False:
-            RuntimeError("ERROR: NOT CONNECTED " \
-                         +repr(self.alice.mycon_status))
-
-    def test_bob_add_alice_as_friend_norequest(self): # works
-        assert len(self.bob.self_get_friend_list()) == 0
-        assert self.bob_add_alice_as_friend_norequest()
-        #: Test last online
-        assert self.bob.friend_get_last_online(self.baid) is not None
-        self.bob.friend_delete(self.baid)
-
-    def test_alice_add_bob_as_friend_norequest(self): # works
-        assert len(self.alice.self_get_friend_list()) == 0
-        assert self.alice_add_bob_as_friend_norequest()
-        assert len(self.alice.self_get_friend_list()) != 0
-        #: Test last online
-        assert self.alice.friend_get_last_online(self.abid) is not None
-        self.alice.friend_delete(self.abid)
-
-    def test_both_add_as_friend_norequest(self): # works
-        assert len(self.bob.self_get_friend_list()) == 0
-        assert len(self.alice.self_get_friend_list()) == 0
-        self.both_add_as_friend_norequest()
-
-        self.bob.friend_delete(self.baid)
-        self.alice.friend_delete(self.abid)
-        assert len(self.bob.self_get_friend_list()) == 0
-        assert len(self.alice.self_get_friend_list()) == 0
-
-    def test_bob_add_alice_as_friend_and_status(self):
-        self.bob_add_alice_as_friend_and_status()
-        self.bob.friend_delete(self.baid)
-
-    @unittest.skip('malloc_consolidate(): invalid chunk size')
-#    @unittest.skipIf(bIS_LOCAL, "local test")
-#    @expectedFailure # (bIS_LOCAL, "local test")
-    def test_bob_add_alice_as_friend(self): # fails
-        assert len(self.bob.self_get_friend_list()) == 0
-        try:
-            assert self.bob_add_alice_as_friend()
-            #: Test last online
-            assert self.bob.friend_get_last_online(self.baid) is not None
-        except AssertionError as e:
-            #WTF?
-            self.bob.friend_delete(self.baid)
-            raise RuntimeError(f"Failed test {e}")
-        finally:
-            self.bob.friend_delete(self.baid)
-            assert len(self.bob.self_get_friend_list()) == 0
-
-    @unittest.skip('malloc_consolidate(): invalid chunk size')
-#    @unittest.skipIf(bIS_LOCAL, "local test")
-#    @expectedFailure
-    def test_alice_add_bob_as_friend(self): # fails
-        assert len(self.bob.self_get_friend_list()) == 0
-        try:
-            assert self.alice_add_bob_as_friend()
-            #: Test last online
-            assert self.alice.friend_get_last_online(self.abid) is not None
-        except AssertionError as e:
-            raise RuntimeError(f"Failed test {e}")
-        except Exception as e:
-            LOG.error(f"test_alice_add_bob_as_friend EXCEPTION  {e}")
-            raise
-        finally:
-            self.alice.friend_delete(self.abid)
-        assert len(self.alice.self_get_friend_list()) == 0
-
-#    @unittest.skipIf(bIS_LOCAL, "local test")
-    @expectedFailure
-    def test_both_add_as_friend(self): # works
-        try:
-            self.both_add_as_friend()
-        except AssertionError as e:
-            raise RuntimeError(f"Failed test {e}")
-        except Exception as e:
-            LOG.error(f"test_both_add_as_friend EXCEPTION  {e}")
-            raise
-        finally:
-            self.bob.friend_delete(self.baid)
-            self.alice.friend_delete(self.abid)
-            assert len(self.bob.self_get_friend_list()) == 0
-            assert len(self.alice.self_get_friend_list()) == 0
-
-    @unittest.skip('unfinished')
-    def test_bob_add_alice_as_friend_and_status(self):
-        assert self.bob_add_alice_as_friend_and_status()
-        self.bob.friend_delete(self.baid)
-
-#?    @unittest.skip('fails')
-    @expectedFailure
-    def test_on_friend_status_message(self): # fails
-        """
-        t:self_set_status_message
-        t:self_get_status_message
-        t:self_get_status_message_size
-        t:friend_set_status_message
-        t:friend_get_status_message
-        t:friend_get_status_message_size
-        t:on_friend_status_message
-        """
-        MSG = 'Happy'
-        sSlot = 'friend_status_message'
-
-        def bob_on_friend_status_message(iTox, friend_id, new_status_message, new_status_size, *largs):
-            try:
-                assert str(new_status_message, 'UTF-8') == MSG
-                assert friend_id == self.baid
-            except Exception as e:
-                LOG_ERROR(f"BOB_ON_friend_status_message EXCEPTION {e}")
-            else:
-                LOG_INFO(f"BOB_ON_friend_status_message {friend_id}" \
-                      +repr(new_status_message))
-            setattr(self.bob, sSlot, True)
-
-        setattr(self.bob, sSlot, None)
-        try:
-            if oTOX_OARGS.bIS_LOCAL:
-                assert self.bob_add_alice_as_friend_norequest()
-            else:
-                assert self.bob_add_alice_as_friend()
-
-            self.bob.callback_friend_status_message(bob_on_friend_status_message)
-            self.warn_if_no_cb(self.bob, sSlot)
-            self.alice.self_set_status_message(MSG)
-            assert self.wait_otox_attrs(self.bob, [sSlot])
-
-            assert self.bob.friend_get_status_message(self.baid) == MSG
-            assert self.bob.friend_get_status_message_size(self.baid) == len(MSG)
-
-        except AssertionError as e:
-            raise RuntimeError(f"Failed test {e}")
-        except Exception as e:
-            LOG.error(f"test_on_friend_status_message EXCEPTION  {e}")
-            raise
-        finally:
-            self.alice.callback_friend_status(None)
-            self.bob.friend_delete(self.baid)
-
-    @expectedFailure
-    def test_friend(self): # works
-        """
-        t:friend_delete
-        t:friend_exists
-        t:friend_get_public_key
-        t:self_get_friend_list
-        t:self_get_friend_list_size
-        t:self_set_name
-        t:friend_get_name
-        t:friend_get_name_size
-        t:on_friend_name
-         """
-
-        assert len(self.bob.self_get_friend_list()) == 0
-        assert len(self.alice.self_get_friend_list()) == 0
-        #: Test friend request
-        if oTOX_OARGS.bIS_LOCAL:
-            assert self.bob_add_alice_as_friend_norequest()
-            assert self.alice_add_bob_as_friend_norequest()
-        else:
-            # no not connected error
-            assert self.bob_add_alice_as_friend()
-            assert self.alice_add_bob_as_friend()
-        try:
-            assert self.bob.friend_get_public_key(self.baid) == \
-              self.alice.self_get_address()[:CLIENT_ID_SIZE]
-
-            #: Test friend_get_public_key
-            assert self.alice.friend_get_public_key(self.abid) == \
-                self.bob.self_get_address()[:CLIENT_ID_SIZE]
-        except AssertionError as e:
-            raise RuntimeError(f"Failed test {e}")
-        except Exception as e:
-            LOG.error(f"test_friend EXCEPTION  {e}")
-            raise
-        finally:
-            self.bob.friend_delete(self.baid)
-            self.alice.friend_delete(self.abid)
-
-#    @unittest.skip('fails')
-#    @unittest.skipIf(not bIS_LOCAL and not ts.bAreWeConnected(), 'NOT CONNECTED')
-    @expectedFailure
-    def test_user_status(self):
-        """
-        t:self_get_status
-        t:self_set_status
-        t:friend_get_status
-        t:friend_get_status
-        t:on_friend_status
-        """
-        sSlot = 'friend_status'
-        if oTOX_OARGS.bIS_LOCAL:
-            assert self.bob_add_alice_as_friend_norequest()
-        else:
-            assert self.bob_add_alice_as_friend()
-
-        sSTATUS = TOX_USER_STATUS['NONE']
-        setattr(self.bob, sSlot, None)
-        def bobs_on_friend_set_status(iTox, friend_id, new_status, *largs):
-            LOG_INFO(f"bobs_on_friend_set_status {friend_id} {new_status}")
-            try:
-                assert friend_id == self.baid
-                assert new_status in [TOX_USER_STATUS['BUSY'], TOX_USER_STATUS['AWAY']]
-            except Exception as e:
-                LOG_WARN(f"bobs_on_friend_set_status EXCEPTION {e}")
-            setattr(self.bob, sSlot, True)
-
-        try:
-            if not self.get_connection_status():
-                LOG.warning(f"test_user_status NOT CONNECTED self.get_connection_status")
-                self.loop_until_connected()
-
-            self.bob.callback_friend_status(bobs_on_friend_set_status)
-            self.warn_if_no_cb(self.bob, sSlot)
-            sSTATUS = TOX_USER_STATUS['BUSY']
-            self.alice.self_set_status(sSTATUS)
-            sSTATUS = TOX_USER_STATUS['AWAY']
-            self.alice.self_set_status(sSTATUS)
-            assert self.wait_otox_attrs(self.bob, [sSlot])
-            # wait_obj_attr count >= 15 for friend_status
-
-            self.alice.self_set_status(TOX_USER_STATUS['NONE'])
-            assert self.alice.self_get_status() == TOX_USER_STATUS['NONE']
-            assert self.bob.friend_get_status(self.baid) == TOX_USER_STATUS['NONE']
-
-        except AssertionError as e:
-            raise RuntimeError(f"Failed test {e}")
-
-        except Exception as e:
-            LOG.error(f"test_user_status EXCEPTION  {e}")
-            raise
-        finally:
-            self.bob.callback_friend_status(None)
-            self.warn_if_cb(self.bob, sSlot)
-            self.bob.friend_delete(self.baid)
-
-    @unittest.skip('crashes')
-    def test_connection_status(self):
-        """
-        t:friend_get_connection_status
-        t:on_friend_connection_status
-        """
-        LOG.info("test_connection_status ")
-        if oTOX_OARGS.bIS_LOCAL:
-            assert self.bob_add_alice_as_friend_norequest()
-        else:
-            assert self.bob_add_alice_as_friend()
-
-        sSlot = 'friend_connection_status'
-        setattr(self.bob, sSlot, None)
-        def bobs_on_friend_connection_status(iTox, friend_id, iStatus, *largs):
-            setattr(self.bob, sSlot, True)
-            LOG_INFO(f"bobs_on_friend_connection_status " +repr(iStatus))
-            try:
-                assert friend_id == self.baid
-            except Exception as e:
-                LOG.error(f"bobs_on_friend_connection_status ERROR  {e}")
-
-        opts = oToxygenToxOptions(oTOX_OARGS)
-        try:
-            setattr(self.bob, sSlot, True)
-            self.bob.callback_friend_connection_status(bobs_on_friend_connection_status)
-
-            LOG.info("test_connection_status killing alice")
-            self.alice.kill() #! bang
-            LOG.info("test_connection_status making alice")
-            self.alice = Tox(opts, app=oAPP)
-            LOG.info("test_connection_status maked alice")
-
-            assert self.wait_otox_attrs(self.bob, [sSlot])
-        except AssertionError as e:
-            raise
-        except Exception as e:
-            LOG.error(f"bobs_on_friend_connection_status  {e}")
-            raise
-        finally:
-            self.bob.callback_friend_connection_status(None)
-
-            #? assert self.bob.friend_get_connection_status(self.aid) is False
-            self.bob.friend_delete(self.baid)
-
-#?    @unittest.skip('fails')
-    def test_friend_name(self): # fails
-        """
-        t:self_set_name
-        t:friend_get_name
-        t:friend_get_name_size
-        t:on_friend_name
-        """
-
-        sSlot= 'friend_name'
-        #: Test friend request
-
-        LOG.info("test_friend_name")
-        if oTOX_OARGS.bIS_LOCAL:
-            assert self.bob_add_alice_as_friend_norequest()
-        else:
-            assert self.bob_add_alice_as_friend()
-
-        if not self.get_connection_status():
-            LOG.warning(f"test_friend_message NOT CONNECTED")
-            self.loop_until_connected()
-
-        #: Test friend name
-        NEWNAME = 'Jenny'
-
-        def bobs_on_friend_name(iTox, fid, newname, iNameSize, *largs):
-            LOG_INFO(f"bobs_on_friend_name {sSlot} {fid}")
-            try:
-                assert fid == self.baid
-                assert str(newname, 'UTF-8') == NEWNAME
-            except Exception as e:
-                LOG.error(f"bobs_on_friend_name EXCEPTION {e}")
-            setattr(self.bob, sSlot, True)
-
-        setattr(self.bob, sSlot, None)
-        self.bob.callback_friend_name(bobs_on_friend_name)
-        self.warn_if_no_cb(self.bob, sSlot)
-        try:
-            self.alice.self_set_name(NEWNAME)
-            assert self.wait_otox_attrs(self.bob, [sSlot])
-
-            assert self.bob.friend_get_name(self.baid) == NEWNAME
-            assert self.bob.friend_get_name_size(self.baid) == len(NEWNAME)
-
-        except AssertionError as e:
-             raise RuntimeError(f"test_friend Failed test {e}")
-
-        except Exception as e:
-           LOG.error(f"test_friend EXCEPTION  {e}")
-           raise
-
-        finally:
-            self.bob.callback_friend_name(None)
-            if hasattr(self.bob, sSlot + '_cb') and \
-               getattr(self.bob, sSlot + '_cb'):
-                LOG.warning(sSlot + ' EXISTS')
-
-            self.bob.friend_delete(self.baid)
-
-    # wait_ensure_exec ArgumentError This client is currently not connected to the friend.
-    def test_friend_message(self): # fails
-        """
-        t:on_friend_action
-        t:on_friend_message
-        t:friend_send_message
-        """
-
-       #: Test message
-        MSG = 'Hi, Bob!'
-        sSlot = 'friend_message'
-        if oTOX_OARGS.bIS_LOCAL:
-            assert self.both_add_as_friend_norequest()
-        else:
-            assert self.both_add_as_friend()
-
-        if not self.get_connection_status():
-            LOG.warning(f"test_friend_message NOT CONNECTED")
-            self.loop_until_connected()
-
-        iRet = self.bob.friend_get_connection_status(self.baid)
-        if iRet == TOX_CONNECTION['NONE']:
-            LOG.error("bob.friend_get_connection_status")
-            raise RuntimeError("bob.friend_get_connection_status")
-        iRet = self.alice.friend_get_connection_status(self.abid)
-        if iRet == TOX_CONNECTION['NONE']:
-            LOG.error("alice.friend_get_connection_status")
-            raise RuntimeError("alice.friend_get_connection_status")
-
-        def alices_on_friend_message(iTox, fid, msg_type, message, iSize, *largs):
-            LOG_DEBUG(f"alices_on_friend_message {fid} {message}")
-            try:
-                assert fid == self.alice.abid
-                assert msg_type == TOX_MESSAGE_TYPE['NORMAL']
-                assert str(message, 'UTF-8') == MSG
-            except Exception as e:
-                LOG_ERROR(f"alices_on_friend_message EXCEPTION {e}")
-            else:
-                LOG_INFO(f"alices_on_friend_message {message}")
-            setattr(self.alice, sSlot, True)
-
-        setattr(self.alice, sSlot, None)
-        try:
-            self.alice.callback_friend_message(alices_on_friend_message)
-            self.warn_if_no_cb(self.alice, sSlot)
-
-            # dunno - both This client is currently NOT CONNECTED to the friend.
-            if True:
-                iMesId = self.bob.friend_send_message(
-                    self.baid,
-                    TOX_MESSAGE_TYPE['NORMAL'],
-                    bytes(MSG, 'UTF-8'))
-                #  ArgumentError('This client is currently NOT CONNECTED to the friend.')
-            else:
-                iMesId = self.wait_ensure_exec(self.bob.friend_send_message,
-                                               [self.baid,
-                                                TOX_MESSAGE_TYPE['NORMAL'],
-                                                bytes(MSG, 'UTF-8')])
-            assert iMesId >= 0
-            assert self.wait_otox_attrs(self.alice, [sSlot])
-        except ArgumentError as e:
-            #  ArgumentError('This client is currently NOT CONNECTED to the friend.')
-            # dunno
-            LOG.error(f"test_friend_message  {e}")
-            raise
-        except AssertionError as e:
-            LOG.warning(f"test_friend_message  {e}")
-            raise RuntimeError(f"Failed test test_friend_message {e}")
-        except Exception as e:
-            LOG.error(f"test_friend_message  {e}")
-            raise
-        finally:
-            self.alice.callback_friend_message(None)
-            self.warn_if_cb(self.alice, sSlot)
-            self.bob.friend_delete(self.baid)
-            self.alice.friend_delete(self.abid)
-
-#?    @unittest.skip('fails')
-    def test_friend_action(self):
-        """
-        t:on_friend_action
-        t:on_friend_message
-        t:friend_send_message
-        """
-
-        if oTOX_OARGS.bIS_LOCAL:
-            assert self.both_add_as_friend_norequest()
-        else:
-            assert self.both_add_as_friend()
-
-        if not self.get_connection_status():
-            LOG.warning(f"test_friend_message NOT CONNECTED")
-            self.loop_until_connected()
-
-        iRet = self.bob.friend_get_connection_status(self.baid)
-        if iRet == TOX_CONNECTION['NONE']:
-            LOG.error("bob.friend_get_connection_status")
-            raise RuntimeError("bob.friend_get_connection_status")
-        iRet = self.alice.friend_get_connection_status(self.abid)
-        if iRet == TOX_CONNECTION['NONE']:
-            LOG.error("alice.friend_get_connection_status")
-            raise RuntimeError("alice.friend_get_connection_status")
-
-        BID = self.baid
-        #: Test action
-        ACTION = 'Kick'
-        sSlot = 'friend_read_action'
-        setattr(self.bob, sSlot, None)
-        sSlot = 'friend_read_receipt'
-        setattr(self.bob, sSlot, None)
-        def alices_on_friend_action(iTox, fid, msg_type, action, *largs):
-            sSlot = 'friend_read_action'
-            LOG_DEBUG(f"alices_on_friend_action")
-            try:
-                assert fid == self.bob.baid
-                assert msg_type == TOX_MESSAGE_TYPE['ACTION']
-                assert action == ACTION
-            except Exception as e:
-                LOG_ERROR(f"alices_on_friend_action EXCEPTION {e}")
-            else:
-                LOG_INFO(f"alices_on_friend_action {message}")
-            setattr(self.bob, sSlot, True)
-
-        sSlot = 'friend_read_action'
-        setattr(self.alice, sSlot, None)
-        sSlot = 'friend_read_receipt'
-        setattr(self.alice, sSlot, None)
-        def alices_on_read_reciept(iTox, fid, msg_id, *largs):
-            LOG_DEBUG(f"alices_on_read_reciept")
-            sSlot = 'friend_read_receipt'
-            try:
-                assert fid == BID
-            except Exception as e:
-                LOG_ERROR(f"alices_on_read_reciept {e}")
-            else:
-                LOG_INFO(f"alices_on_read_reciept {fid}")
-            setattr(self.alice, sSlot, True)
-
-        sSlot = 'friend_read_receipt'
-        try:
-            sSlot = 'friend_read_action'
-            setattr(self.bob, sSlot, False)
-            sSlot = 'friend_read_receipt'
-            setattr(self.alice, sSlot, False)
-
-            self.alice.callback_friend_read_receipt(alices_on_read_reciept) #was alices_on_friend_action
-            self.warn_if_no_cb(self.alice, sSlot)
-            assert self.wait_ensure_exec(self.bob.friend_send_message,
-                                         [self.baid,
-                                          TOX_MESSAGE_TYPE['ACTION'],
-                                          bytes(ACTION, 'UTF-8')])
-            assert self.wait_otox_attrs(self.alice, [sSlot])
-        except AssertionError as e:
-            raise RuntimeError(f"Failed test {e}")
-        except ArgumentError as e:
-            #  ArgumentError('This client is currently NOT CONNECTED to the friend.')
-            # dunno
-            LOG.warning(f"test_friend_action  {e}")
-        except Exception as e:
-            LOG.error(f"test_friend_action  {e}")
-            raise
-        finally:
-            self.alice.callback_friend_read_receipt(None)
-            self.bob.friend_delete(self.baid)
-            self.alice.friend_delete(self.abid)
-
-    @unittest.skip('fails')
-    def test_alice_typing_status(self):
-        """
-        t:on_friend_read_receipt
-        t:on_friend_typing
-        t:self_set_typing
-        t:friend_get_typing
-        t:friend_get_last_online
-        """
-
-        sSlot = 'friend_typing'
-        # works
-        LOG.info("test_typing_status bob adding alice")
-        if oTOX_OARGS.bIS_LOCAL:
-            assert self.both_add_as_friend_norequest()
-        else:
-            assert self.both_add_as_friend()
-
-        BID = self.baid
-
-        #: Test typing status
-        def bob_on_friend_typing(iTox, fid, is_typing, *largs):
-            try:
-                assert fid == BID
-                assert is_typing is True
-                assert self.bob.friend_get_typing(fid) is True
-            except Exception as e:
-                LOG.error(f"BOB_ON_friend_typing  {e}")
-                raise
-            else:
-                LOG_INFO(f"BOB_ON_friend_typing" + str(fid))
-            setattr(self.bob, sSlot, True)
-
-        setattr(self.bob, sSlot, None)
-        try:
-            if not self.get_connection_status():
-                LOG.warning(f"test_friend_message NOT CONNECTED")
-                self.loop_until_connected()
-
-            self.bob.callback_friend_typing(bob_on_friend_typing)
-            self.alice.self_set_typing(self.abid, True)
-            assert self.wait_otox_attrs(self.bob, [sSlot])
-            if not hasattr(self.bob, sSlot+'_cb') or \
-               not getattr(self.bob, sSlot+'_cb'):
-                LOG.warning(f"self.bob.{sSlot}_cb NOT EXIST")
-        except AssertionError as e:
-            raise RuntimeError(f"Failed test {e}")
-        except Exception as e:
-            LOG.error(f"test_alice_typing_status error={e}")
-            raise
-        finally:
-            self.bob.callback_friend_typing(None)
-            self.bob.friend_delete(self.baid)
-            self.alice.friend_delete(self.abid)
-
-    @unittest.skip('unfinished')
-    def test_file_transfer(self): # unfinished
-        """
-        t:file_send
-        t:file_send_chunk
-        t:file_control
-        t:file_seek
-        t:file_get_file_id
-        t:on_file_recv
-        t:on_file_recv_control
-        t:on_file_recv_chunk
-        t:on_file_chunk_request
-        """
-
-        if oTOX_OARGS.bIS_LOCAL:
-            assert self.bob_add_alice_as_friend_norequest()
-        else:
-            assert self.bob_add_alice_as_friend()
-
-        BID = self.baid
-
-        FRIEND_NUMBER = self.baid
-        FILE_NUMBER = 1
-        FILE = os.urandom(1024 * 1024)
-        FILE_NAME = b"/tmp/test.bin"
-        if not os.path.exists(FILE_NAME):
-            with open(FILE_NAME, 'wb') as oFd:
-                oFd.write(FILE)
-        FILE_SIZE = len(FILE)
-        OFFSET = 567
-
-        m = hashlib.md5()
-        m.update(FILE[OFFSET:])
-        FILE_DIGEST = m.hexdigest()
-
-        CONTEXT = { 'FILE': bytes(), 'RECEIVED': 0, 'START': False, 'SENT': 0 }
-
-        def alice_on_file_recv(iTox, fid, file_number, kind, size, filename):
-            LOG_DEBUG(f"ALICE_ON_file_recv fid={fid} {file_number}")
-            try:
-                assert size == FILE_SIZE
-                assert filename == FILE_NAME
-                retv = self.alice.file_seek(fid, file_number, OFFSET)
-                assert retv is True
-                self.alice.file_control(fid, file_number, TOX_FILE_CONTROL['RESUME'])
-            except Exception as e:
-                LOG_ERROR(f"ALICE_ON_file_recv  {e}")
-            else:
-                LOG_INFO(f"ALICE_ON_file_recv " + str(fid))
-
-        def alice_on_file_recv_control(iTox, fid, file_number, control, *largs):
-            # TOX_FILE_CONTROL = {    'RESUME': 0,    'PAUSE': 1,    'CANCEL': 2,}
-            LOG_DEBUG(f"ALICE_ON_file_recv_control fid={fid} {file_number} {control}")
-            try:
-                assert FILE_NUMBER == file_number
-                # FixMe _FINISHED?
-                if False and control == TOX_FILE_CONTROL['RESUME']:
-                    #         assert CONTEXT['RECEIVED'] == FILE_SIZE
-                    #         m = hashlib.md5()
-                    #         m.update(CONTEXT['FILE'])
-                    #         assert m.hexdigest() == FILE_DIGEST
-                    self.alice.completed = True
-            except Exception as e:
-                LOG_ERROR(f"ALICE_ON_file_recv  {e}")
-            else:
-                LOG_INFO(f"ALICE_ON_file_recv " + str(fid))
-
-        self.alice.completed = False
-        def alice_on_file_recv_chunk(iTox, fid, file_number, position, iNumBytes, *largs):
-            LOG_DEBUG(f"ALICE_ON_file_recv_chunk {fid} {file_number}")
-            # FixMe - use file_number and iNumBytes to get data?
-            data = ''
-            try:
-                if data is None:
-                    assert CONTEXT['RECEIVED'] == (FILE_SIZE - OFFSET)
-                    m = hashlib.md5()
-                    m.update(CONTEXT['FILE'])
-                    assert m.hexdigest() == FILE_DIGEST
-                    self.alice.completed = True
-                    self.alice.file_control(fid, file_number, TOX_FILE_CONTROL['CANCEL'])
-                    return
-
-                CONTEXT['FILE'] += data
-                CONTEXT['RECEIVED'] += len(data)
-                # if CONTEXT['RECEIVED'] < FILE_SIZE:
-                #    assert self.file_data_remaining(
-                #        fid, file_number, 1) == FILE_SIZE - CONTEXT['RECEIVED']
-            except Exception as e:
-                LOG_ERROR(f"ALICE_ON_file_recv_chunk {e}")
-            else:
-                LOG_INFO(f"ALICE_ON_file_recv_chunk {fid}")
-
-        # AliceTox.on_file_send_request = on_file_send_request
-        # AliceTox.on_file_control = on_file_control
-        # AliceTox.on_file_data = on_file_data
-
-        LOG.info(f"test_file_transfer: baid={self.baid}")
-        try:
-            self.alice.callback_file_recv(alice_on_file_recv)
-            self.alice.callback_file_recv_control(alice_on_file_recv_control)
-            self.alice.callback_file_recv_chunk(alice_on_file_recv_chunk)
-
-            self.bob.completed = False
-            def bob_on_file_recv_control2(iTox, fid, file_number, control):
-                LOG_DEBUG(f"BOB_ON_file_recv_control2 {fid} {file_number} control={control}")
-                if control == TOX_FILE_CONTROL['RESUME']:
-                    CONTEXT['START'] = True
-                elif control == TOX_FILE_CONTROL['CANCEL']:
-                    self.bob.completed = True
-                    pass
-
-            def bob_on_file_chunk_request(iTox, fid, file_number, position, length, *largs):
-                LOG_DEBUG(f"BOB_ON_file_chunk_request {fid} {file_number}")
-                if length == 0:
-                    return
-                data = FILE[position:(position + length)]
-                self.bob.file_send_chunk(fid, file_number, position, data)
-
-            sSlot = 'file_recv_control'
-            self.bob.callback_file_recv_control(bob_on_file_recv_control2)
-            self.bob.callback_file_chunk_request(bob_on_file_chunk_request)
-
-            # was FILE_ID = FILE_NAME
-            FILE_ID = 32*'1' #
-            FILE_NAME = b'test.in'
-
-            if not self.get_connection_status():
-                LOG.warning(f"test_file_transfer NOT CONNECTED")
-                self.loop_until_connected()
-
-            i = 0
-            iKind = 0
-            while i < 2:
-                i += 1
-                try:
-                    FN = self.bob.file_send(self.baid, iKind, FILE_SIZE, FILE_ID, FILE_NAME)
-                    LOG.info(f"test_file_transfer bob.file_send {FN}")
-                except ArgumentError as e:
-                    LOG.debug(f"test_file_transfer bob.file_send {e} {i}")
-                    # ctypes.ArgumentError: This client is currently not connected to the friend.
-                    raise
-                else:
-                    break
-                self.loop(100)
-                sleep(1)
-            else:
-                LOG.error(f"test_file_transfer bob.file_send 2")
-                raise RuntimeError(f"test_file_transfer bob.file_send {THRESHOLD // 2}")
-
-            # UINT32_MAX
-            FID = self.bob.file_get_file_id(self.baid, FN)
-            hexFID = "".join([hex(ord(c))[2:].zfill(2) for c in FILE_NAME])
-            assert FID.startswith(hexFID.upper())
-
-            if not self.wait_obj_attrs(self.bob, ['completed']):
-                LOG.warning(f"test_file_transfer Bob not completed")
-                return False
-            if not self.wait_obj_attrs(self.alice, ['completed']):
-                LOG.warning(f"test_file_transfer Alice not completed")
-                return False
-            return True
-
-        except (ArgumentError, ValueError,) as e:
-               # ValueError: non-hexadecimal number found in fromhex() arg at position 0
-               LOG_ERROR(f"test_file_transfer: {e}")
-               raise
-
-        except Exception as e:
-               LOG_ERROR(f"test_file_transfer:: {e}")
-               LOG_DEBUG('\n' + traceback.format_exc())
-               raise
-
-        finally:
-            self.bob.friend_delete(self.baid)
-            self.alice.callback_file_recv(None)
-            self.alice.callback_file_recv_control(None)
-            self.alice.callback_file_recv_chunk(None)
-            self.bob.callback_file_recv_control(None)
-            self.bob.callback_file_chunk_request(None)
-
-        LOG_INFO(f"test_file_transfer:: self.wait_objs_attr completed")
-
-    @unittest.skip('crashes')
-    def test_tox_savedata(self): # works sorta
-        # but "{addr} != {self.alice.self_get_address()}"
-        """
-        t:get_savedata_size
-        t:get_savedata
-        """
-        # Fatal Python error: Aborted
-        # "/var/local/src/toxygen_wrapper/wrapper/tox.py", line 180 in kill
-        return
-
-        assert self.alice.get_savedata_size() > 0
-        data = self.alice.get_savedata()
-        assert data is not None
-        addr = self.alice.self_get_address()
-        # self._address
-
-        try:
-            LOG.info("test_tox_savedata alice.kill")
-            # crashes
-            self.alice.kill()
-        except:
-            pass
-
-        oArgs = oTOX_OARGS
-        opts = oToxygenToxOptions(oArgs)
-        opts.savedata_data = data
-        opts.savedata_length = len(data)
-
-        self.alice = Tox(tox_options=opts)
-        if addr != self.alice.self_get_address():
-            LOG.warning("test_tox_savedata " +
-                      f"{addr} != {self.alice.self_get_address()}")
-        else:
-            LOG.info("passed test_tox_savedata")
-
-def vOargsToxPreamble(oArgs, Tox, ToxTest):
-
-    ts.vSetupLogging(oArgs)
-
-    methods = set([x for x in dir(Tox) if not x[0].isupper()
-                   and not x[0] == '_'])
-    docs = "".join([getattr(ToxTest, x).__doc__ for x in dir(ToxTest)
-                    if getattr(ToxTest, x).__doc__ is not None])
-
-    tested = set(re.findall(r't:(.*?)\n', docs))
-    not_tested = methods.difference(tested)
-
-    logging.info('Test Coverage: %.2f%%' % (len(tested) * 100.0 / len(methods)))
-    if len(not_tested):
-        logging.info('Not tested:\n    %s' % "\n    ".join(sorted(list(not_tested))))
-
-###
-
-def iMain(oArgs):
-    failfast=True
-
-    vOargsToxPreamble(oArgs, Tox, ToxSuite)
-    # https://stackoverflow.com/questions/35930811/how-to-sort-unittest-testcases-properly/35930812#35930812
-    cases = ts.suiteFactory(*ts.caseFactory([ToxSuite]))
-    if color_runner:
-        runner = color_runner.runner.TextTestRunner(verbosity=2, failfast=failfast)
-    else:
-        runner = unittest.TextTestRunner(verbosity=2, failfast=failfast, warnings='ignore')
-    runner.run(cases)
-
-def oToxygenToxOptions(oArgs):
-    data = None
-    tox_options = toxygen_wrapper.tox.Tox.options_new()
-    if oArgs.proxy_type:
-        tox_options.contents.proxy_type = int(oArgs.proxy_type)
-        tox_options.contents.proxy_host = bytes(oArgs.proxy_host, 'UTF-8')
-        tox_options.contents.proxy_port = int(oArgs.proxy_port)
-        tox_options.contents.udp_enabled = False
-    else:
-        tox_options.contents.udp_enabled = oArgs.udp_enabled
-    if not os.path.exists('/proc/sys/net/ipv6'):
-        oArgs.ipv6_enabled = False
-    else:
-        tox_options.contents.ipv6_enabled = oArgs.ipv6_enabled
-
-    tox_options.contents.tcp_port = int(oArgs.tcp_port)
-    tox_options.contents.dht_announcements_enabled = oArgs.dht_announcements_enabled
-    tox_options.contents.hole_punching_enabled = oArgs.hole_punching_enabled
-
-    # overrides
-    tox_options.contents.local_discovery_enabled = False
-    tox_options.contents.experimental_thread_safety = False
-    # REQUIRED!!
-    if oArgs.ipv6_enabled and not os.path.exists('/proc/sys/net/ipv6'):
-        LOG.warning('Disabling IPV6 because /proc/sys/net/ipv6 does not exist' + repr(oArgs.ipv6_enabled))
-        tox_options.contents.ipv6_enabled = False
-    else:
-        tox_options.contents.ipv6_enabled = bool(oArgs.ipv6_enabled)
-
-    if data:  # load existing profile
-        tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['TOX_SAVE']
-        tox_options.contents.savedata_data = c_char_p(data)
-        tox_options.contents.savedata_length = len(data)
-    else:  # create new profile
-        tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['NONE']
-        tox_options.contents.savedata_data = None
-        tox_options.contents.savedata_length = 0
-
-    #? tox_options.contents.log_callback = LOG
-    if tox_options._options_pointer:
-        # LOG.debug("Adding logging to tox_options._options_pointer ")
-        ts.vAddLoggerCallback(tox_options, ts.on_log)
-    else:
-        LOG.warning("No tox_options._options_pointer " +repr(tox_options._options_pointer))
-
-    return tox_options
-
-def oArgparse(lArgv):
-    parser = ts.oMainArgparser()
-    parser.add_argument('profile', type=str, nargs='?', default=None,
-                        help='Path to Tox profile')
-    oArgs = parser.parse_args(lArgv)
-
-    for key in ts.lBOOLEANS:
-        if key not in oArgs: continue
-        val = getattr(oArgs, key)
-        setattr(oArgs, key, bool(val))
-
-    if hasattr(oArgs, 'sleep'):
-        if oArgs.sleep == 'qt':
-            pass # broken or gevent.sleep(idle_period)
-        elif oArgs.sleep == 'gevent':
-            pass # broken or gevent.sleep(idle_period)
-        else:
-            oArgs.sleep = 'time'
-
-    return oArgs
-
-def main(lArgs=None):
-    global     oTOX_OARGS
-    if lArgs is None: lArgs = []
-    oArgs = oArgparse(lArgs)
-    global bIS_LOCAL
-    bIS_LOCAL = oArgs.network in ['newlocal', 'localnew', 'local']
-    oTOX_OARGS = oArgs
-    setattr(oTOX_OARGS, 'bIS_LOCAL', bIS_LOCAL)
-    bIS_LOCAL = True
-    setattr(oTOX_OARGS, 'bIS_LOCAL', bIS_LOCAL)
-    # oTOX_OPTIONS = ToxOptions()
-    global oTOX_OPTIONS
-    oTOX_OPTIONS = oToxygenToxOptions(oArgs)
-    if coloredlogs:
-        # https://pypi.org/project/coloredlogs/
-        coloredlogs.install(level=oArgs.loglevel,
-                        logger=LOG,
-                        # %(asctime)s,%(msecs)03d %(hostname)s [%(process)d]
-                        fmt='%(name)s %(levelname)s %(message)s'
-                        )
-    else:
-        logging.basicConfig(level=oArgs.loglevel) #  logging.INFO
-
-    return iMain(oArgs)
-
-if __name__ == '__main__':
-    sys.exit(main(sys.argv[1:]))
-
-# Ran 33 tests in 51.733s
diff --git a/toxygen/third_party/__init__.py b/toxygen/third_party/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/third_party/qweechat/data/icons/README b/toxygen/third_party/qweechat/data/icons/README
deleted file mode 100644
index 0694819..0000000
--- a/toxygen/third_party/qweechat/data/icons/README
+++ /dev/null
@@ -1,41 +0,0 @@
-Copyright and license for images
-================================
-
-
-Files: weechat.png, bullet_green_8x8.png, bullet_yellow_8x8.png
-
-  Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
-  Released under GPLv3.
-
-
-
-Files: application-exit.png, dialog-close.png, dialog-ok-apply.png,
-       dialog-password.png, dialog-warning.png, document-save.png,
-       edit-find.png, help-about.png, network-connect.png,
-       network-disconnect.png, preferences-other.png
-
-  Files come from Debian package "oxygen-icon-theme":
-
-    The Oxygen Icon Theme
-      Copyright (C) 2007 Nuno Pinheiro <nuno@oxygen-icons.org>
-      Copyright (C) 2007 David Vignoni <david@icon-king.com>
-      Copyright (C) 2007 David Miller <miller@oxygen-icons.org>
-      Copyright (C) 2007 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
-      Copyright (C) 2007 Kenneth Wimer <kwwii@bootsplash.org>
-      Copyright (C) 2007 Riccardo Iaconelli <riccardo@oxygen-icons.org>
-      and others
-
-    License:
-
-      This library is free software; you can redistribute it and/or
-      modify it under the terms of the GNU Lesser General Public
-      License as published by the Free Software Foundation; either
-      version 3 of the License, or (at your option) any later version.
-
-      This library 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
-      Lesser General Public License for more details.
-
-      You should have received a copy of the GNU Lesser General Public
-      License along with this library. If not, see <http://www.gnu.org/licenses/>.
diff --git a/toxygen/third_party/qweechat/data/icons/application-exit.png b/toxygen/third_party/qweechat/data/icons/application-exit.png
deleted file mode 100644
index dd76354..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/application-exit.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/bullet_green_8x8.png b/toxygen/third_party/qweechat/data/icons/bullet_green_8x8.png
deleted file mode 100644
index ea80953..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/bullet_green_8x8.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/bullet_yellow_8x8.png b/toxygen/third_party/qweechat/data/icons/bullet_yellow_8x8.png
deleted file mode 100644
index 58ad5cf..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/bullet_yellow_8x8.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/dialog-close.png b/toxygen/third_party/qweechat/data/icons/dialog-close.png
deleted file mode 100644
index 2c2f99e..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/dialog-close.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/dialog-ok-apply.png b/toxygen/third_party/qweechat/data/icons/dialog-ok-apply.png
deleted file mode 100644
index f1d290c..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/dialog-ok-apply.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/dialog-password.png b/toxygen/third_party/qweechat/data/icons/dialog-password.png
deleted file mode 100644
index 2151029..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/dialog-password.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/dialog-warning.png b/toxygen/third_party/qweechat/data/icons/dialog-warning.png
deleted file mode 100644
index 43ca31a..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/dialog-warning.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/document-save.png b/toxygen/third_party/qweechat/data/icons/document-save.png
deleted file mode 100644
index 7fa489c..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/document-save.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/edit-find.png b/toxygen/third_party/qweechat/data/icons/edit-find.png
deleted file mode 100644
index 9b3fe6b..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/edit-find.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/help-about.png b/toxygen/third_party/qweechat/data/icons/help-about.png
deleted file mode 100644
index ee59e17..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/help-about.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/network-connect.png b/toxygen/third_party/qweechat/data/icons/network-connect.png
deleted file mode 100644
index 4e32020..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/network-connect.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/network-disconnect.png b/toxygen/third_party/qweechat/data/icons/network-disconnect.png
deleted file mode 100644
index 623c8e0..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/network-disconnect.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/preferences-other.png b/toxygen/third_party/qweechat/data/icons/preferences-other.png
deleted file mode 100644
index 711881e..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/preferences-other.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/data/icons/weechat.png b/toxygen/third_party/qweechat/data/icons/weechat.png
deleted file mode 100644
index 7eca5c8..0000000
Binary files a/toxygen/third_party/qweechat/data/icons/weechat.png and /dev/null differ
diff --git a/toxygen/third_party/qweechat/weechat/__init__.py b/toxygen/third_party/qweechat/weechat/__init__.py
deleted file mode 100644
index f510618..0000000
--- a/toxygen/third_party/qweechat/weechat/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
-#
-# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
-#
-# QWeeChat 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.
-#
-# QWeeChat 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 QWeeChat.  If not, see <http://www.gnu.org/licenses/>.
-#
diff --git a/toxygen/third_party/qweechat/weechat/color.py b/toxygen/third_party/qweechat/weechat/color.py
deleted file mode 100644
index 0ed52ef..0000000
--- a/toxygen/third_party/qweechat/weechat/color.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# color.py - remove/replace colors in WeeChat strings
-#
-# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
-#
-# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
-#
-# QWeeChat 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.
-#
-# QWeeChat 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 QWeeChat.  If not, see <http://www.gnu.org/licenses/>.
-#
-
-"""Remove/replace colors in WeeChat strings."""
-
-import re
-import logging
-
-RE_COLOR_ATTRS = r'[*!/_|]*'
-RE_COLOR_STD = r'(?:%s\d{2})' % RE_COLOR_ATTRS
-RE_COLOR_EXT = r'(?:@%s\d{5})' % RE_COLOR_ATTRS
-RE_COLOR_ANY = r'(?:%s|%s)' % (RE_COLOR_STD, RE_COLOR_EXT)
-# \x19: color code, \x1A: set attribute, \x1B: remove attribute, \x1C: reset
-RE_COLOR = re.compile(
-    r'(\x19(?:\d{2}|F%s|B\d{2}|B@\d{5}|E|\\*%s(~%s)?|@\d{5}|b.|\x1C))|\x1A.|'
-    r'\x1B.|\x1C'
-    % (RE_COLOR_ANY, RE_COLOR_ANY, RE_COLOR_ANY))
-
-TERMINAL_COLORS = \
-    '000000cd000000cd00cdcd000000cdcd00cd00cdcde5e5e5' \
-    '4d4d4dff000000ff00ffff000000ffff00ff00ffffffffff' \
-    '00000000002a0000550000800000aa0000d4002a00002a2a' \
-    '002a55002a80002aaa002ad400550000552a005555005580' \
-    '0055aa0055d400800000802a0080550080800080aa0080d4' \
-    '00aa0000aa2a00aa5500aa8000aaaa00aad400d40000d42a' \
-    '00d45500d48000d4aa00d4d42a00002a002a2a00552a0080' \
-    '2a00aa2a00d42a2a002a2a2a2a2a552a2a802a2aaa2a2ad4' \
-    '2a55002a552a2a55552a55802a55aa2a55d42a80002a802a' \
-    '2a80552a80802a80aa2a80d42aaa002aaa2a2aaa552aaa80' \
-    '2aaaaa2aaad42ad4002ad42a2ad4552ad4802ad4aa2ad4d4' \
-    '55000055002a5500555500805500aa5500d4552a00552a2a' \
-    '552a55552a80552aaa552ad455550055552a555555555580' \
-    '5555aa5555d455800055802a5580555580805580aa5580d4' \
-    '55aa0055aa2a55aa5555aa8055aaaa55aad455d40055d42a' \
-    '55d45555d48055d4aa55d4d480000080002a800055800080' \
-    '8000aa8000d4802a00802a2a802a55802a80802aaa802ad4' \
-    '80550080552a8055558055808055aa8055d480800080802a' \
-    '8080558080808080aa8080d480aa0080aa2a80aa5580aa80' \
-    '80aaaa80aad480d40080d42a80d45580d48080d4aa80d4d4' \
-    'aa0000aa002aaa0055aa0080aa00aaaa00d4aa2a00aa2a2a' \
-    'aa2a55aa2a80aa2aaaaa2ad4aa5500aa552aaa5555aa5580' \
-    'aa55aaaa55d4aa8000aa802aaa8055aa8080aa80aaaa80d4' \
-    'aaaa00aaaa2aaaaa55aaaa80aaaaaaaaaad4aad400aad42a' \
-    'aad455aad480aad4aaaad4d4d40000d4002ad40055d40080' \
-    'd400aad400d4d42a00d42a2ad42a55d42a80d42aaad42ad4' \
-    'd45500d4552ad45555d45580d455aad455d4d48000d4802a' \
-    'd48055d48080d480aad480d4d4aa00d4aa2ad4aa55d4aa80' \
-    'd4aaaad4aad4d4d400d4d42ad4d455d4d480d4d4aad4d4d4' \
-    '0808081212121c1c1c2626263030303a3a3a4444444e4e4e' \
-    '5858586262626c6c6c7676768080808a8a8a9494949e9e9e' \
-    'a8a8a8b2b2b2bcbcbcc6c6c6d0d0d0dadadae4e4e4eeeeee'
-
-# WeeChat basic colors (color name, index in terminal colors)
-WEECHAT_BASIC_COLORS = (
-    ('default', 0), ('black', 0), ('darkgray', 8), ('red', 1),
-    ('lightred', 9), ('green', 2), ('lightgreen', 10), ('brown', 3),
-    ('yellow', 11), ('blue', 4), ('lightblue', 12), ('magenta', 5),
-    ('lightmagenta', 13), ('cyan', 6), ('lightcyan', 14), ('gray', 7),
-    ('white', 0))
-
-
-log = logging.getLogger(__name__)
-
-
-class Color():
-    def __init__(self, color_options, debug=False):
-        self.color_options = color_options
-        self.debug = debug
-
-    def _rgb_color(self, index):
-        color = TERMINAL_COLORS[index*6:(index*6)+6]
-        col_r = int(color[0:2], 16) * 0.85
-        col_g = int(color[2:4], 16) * 0.85
-        col_b = int(color[4:6], 16) * 0.85
-        return '%02x%02x%02x' % (col_r, col_g, col_b)
-
-    def _convert_weechat_color(self, color):
-        try:
-            index = int(color)
-            return '\x01(Fr%s)' % self.color_options[index]
-        except Exception:  # noqa: E722
-            log.debug('Error decoding WeeChat color "%s"', color)
-            return ''
-
-    def _convert_terminal_color(self, fg_bg, attrs, color):
-        try:
-            index = int(color)
-            return '\x01(%s%s#%s)' % (fg_bg, attrs, self._rgb_color(index))
-        except Exception:  # noqa: E722
-            log.debug('Error decoding terminal color "%s"', color)
-            return ''
-
-    def _convert_color_attr(self, fg_bg, color):
-        extended = False
-        if color[0].startswith('@'):
-            extended = True
-            color = color[1:]
-        attrs = ''
-        # keep_attrs = False
-        while color.startswith(('*', '!', '/', '_', '|')):
-            # TODO: manage the "keep attributes" flag
-            # if color[0] == '|':
-            #    keep_attrs = True
-            attrs += color[0]
-            color = color[1:]
-        if extended:
-            return self._convert_terminal_color(fg_bg, attrs, color)
-        try:
-            index = int(color)
-            return self._convert_terminal_color(fg_bg, attrs,
-                                                WEECHAT_BASIC_COLORS[index][1])
-        except Exception:  # noqa: E722
-            log.debug('Error decoding color "%s"', color)
-            return ''
-
-    def _attrcode_to_char(self, code):
-        codes = {
-            '\x01': '*',
-            '\x02': '!',
-            '\x03': '/',
-            '\x04': '_',
-        }
-        return codes.get(code, '')
-
-    def _convert_color(self, match):
-        color = match.group(0)
-        if color[0] == '\x19':
-            if color[1] == 'b':
-                # bar code, ignored
-                return ''
-            if color[1] == '\x1C':
-                # reset
-                return '\x01(Fr)\x01(Br)'
-            if color[1] in ('F', 'B'):
-                # foreground or background
-                return self._convert_color_attr(color[1], color[2:])
-            if color[1] == '*':
-                # foreground with optional background
-                items = color[2:].split(',')
-                str_col = self._convert_color_attr('F', items[0])
-                if len(items) > 1:
-                    str_col += self._convert_color_attr('B', items[1])
-                return str_col
-            if color[1] == '@':
-                # direct ncurses pair number, ignored
-                return ''
-            if color[1] == 'E':
-                # text emphasis, ignored
-                return ''
-            if color[1:].isdigit():
-                return self._convert_weechat_color(int(color[1:]))
-        elif color[0] == '\x1A':
-            # set attribute
-            return '\x01(+%s)' % self._attrcode_to_char(color[1])
-        elif color[0] == '\x1B':
-            # remove attribute
-            return '\x01(-%s)' % self._attrcode_to_char(color[1])
-        elif color[0] == '\x1C':
-            # reset
-            return '\x01(Fr)\x01(Br)'
-        # should never be executed!
-        return match.group(0)
-
-    def _convert_color_debug(self, match):
-        group = match.group(0)
-        for code in (0x01, 0x02, 0x03, 0x04, 0x19, 0x1A, 0x1B):
-            group = group.replace(chr(code), '<x%02X>' % code)
-        return group
-
-    def convert(self, text):
-        if not text:
-            return ''
-        if self.debug:
-            return RE_COLOR.sub(self._convert_color_debug, text)
-        return RE_COLOR.sub(self._convert_color, text)
-
-
-def remove(text):
-    """Remove colors in a WeeChat string."""
-    if not text:
-        return ''
-    return re.sub(RE_COLOR, '', text)
diff --git a/toxygen/third_party/qweechat/weechat/protocol.py b/toxygen/third_party/qweechat/weechat/protocol.py
deleted file mode 100644
index 90ce7d2..0000000
--- a/toxygen/third_party/qweechat/weechat/protocol.py
+++ /dev/null
@@ -1,361 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# protocol.py - decode binary messages received from WeeChat/relay
-#
-# Copyright (C) 2011-2022 Sébastien Helleu <flashcode@flashtux.org>
-#
-# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
-#
-# QWeeChat 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.
-#
-# QWeeChat 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 QWeeChat.  If not, see <http://www.gnu.org/licenses/>.
-#
-
-#
-# For info about protocol and format of messages, please read document
-# "WeeChat Relay Protocol", available at:  https://weechat.org/doc/
-#
-# History:
-#
-# 2011-11-23, Sébastien Helleu <flashcode@flashtux.org>:
-#     start dev
-#
-
-"""Decode binary messages received from WeeChat/relay."""
-
-import collections
-import struct
-import zlib
-
-
-class WeechatDict(collections.OrderedDict):
-    def __str__(self):
-        return '{%s}' % ', '.join(
-            ['%s: %s' % (repr(key), repr(self[key])) for key in self])
-
-
-class WeechatObject:
-    def __init__(self, objtype, value, separator='\n'):
-        self.objtype = objtype
-        self.value = value
-        self.separator = separator
-        self.indent = '  ' if separator == '\n' else ''
-        self.separator1 = '\n%s' % self.indent if separator == '\n' else ''
-
-    def _str_value(self, val):
-        if isinstance(val, str) and val is not None:
-            return '\'%s\'' % val
-        return str(val)
-
-    def _str_value_hdata(self):
-        lines = ['%skeys: %s%s%spath: %s' % (self.separator1,
-                                             str(self.value['keys']),
-                                             self.separator,
-                                             self.indent,
-                                             str(self.value['path']))]
-        for i, item in enumerate(self.value['items']):
-            lines.append('  item %d:%s%s' % (
-                (i + 1), self.separator,
-                self.separator.join(
-                    ['%s%s: %s' % (self.indent * 2, key,
-                                   self._str_value(value))
-                     for key, value in item.items()])))
-        return '\n'.join(lines)
-
-    def _str_value_infolist(self):
-        lines = ['%sname: %s' % (self.separator1, self.value['name'])]
-        for i, item in enumerate(self.value['items']):
-            lines.append('  item %d:%s%s' % (
-                (i + 1), self.separator,
-                self.separator.join(
-                    ['%s%s: %s' % (self.indent * 2, key,
-                                   self._str_value(value))
-                     for key, value in item.items()])))
-        return '\n'.join(lines)
-
-    def _str_value_other(self):
-        return self._str_value(self.value)
-
-    def __str__(self):
-        obj_cb = {
-            'hda': self._str_value_hdata,
-            'inl': self._str_value_infolist,
-        }
-        return '%s: %s' % (self.objtype,
-                           obj_cb.get(self.objtype, self._str_value_other)())
-
-
-class WeechatObjects(list):
-    def __init__(self, separator='\n'):
-        super().__init__()
-        self.separator = separator
-
-    def __str__(self):
-        return self.separator.join([str(obj) for obj in self])
-
-
-class WeechatMessage:
-    def __init__(self, size, size_uncompressed, compression, uncompressed,
-                 msgid, objects):
-        self.size = size
-        self.size_uncompressed = size_uncompressed
-        self.compression = compression
-        self.uncompressed = uncompressed
-        self.msgid = msgid
-        self.objects = objects
-
-    def __str__(self):
-        if self.compression != 0:
-            return 'size: %d/%d (%d%%), id=\'%s\', objects:\n%s' % (
-                self.size, self.size_uncompressed,
-                100 - ((self.size * 100) // self.size_uncompressed),
-                self.msgid, self.objects)
-        return 'size: %d, id=\'%s\', objects:\n%s' % (self.size,
-                                                      self.msgid,
-                                                      self.objects)
-
-
-class Protocol:
-    """Decode binary message received from WeeChat/relay."""
-
-    def __init__(self):
-        self.data = ''
-        self._obj_cb = {
-            'chr': self._obj_char,
-            'int': self._obj_int,
-            'lon': self._obj_long,
-            'str': self._obj_str,
-            'buf': self._obj_buffer,
-            'ptr': self._obj_ptr,
-            'tim': self._obj_time,
-            'htb': self._obj_hashtable,
-            'hda': self._obj_hdata,
-            'inf': self._obj_info,
-            'inl': self._obj_infolist,
-            'arr': self._obj_array,
-        }
-
-    def _obj_type(self):
-        """Read type in data (3 chars)."""
-        if len(self.data) < 3:
-            self.data = ''
-            return ''
-        objtype = self.data[0:3].decode()
-        self.data = self.data[3:]
-        return objtype
-
-    def _obj_len_data(self, length_size):
-        """Read length (1 or 4 bytes), then value with this length."""
-        if len(self.data) < length_size:
-            self.data = ''
-            return None
-        if length_size == 1:
-            length = struct.unpack('B', self.data[0:1])[0]
-            self.data = self.data[1:]
-        else:
-            length = self._obj_int()
-        if length < 0:
-            return None
-        if length > 0:
-            value = self.data[0:length]
-            self.data = self.data[length:]
-        else:
-            value = ''
-        return value
-
-    def _obj_char(self):
-        """Read a char in data."""
-        if len(self.data) < 1:
-            return 0
-        value = struct.unpack('b', self.data[0:1])[0]
-        self.data = self.data[1:]
-        return value
-
-    def _obj_int(self):
-        """Read an integer in data (4 bytes)."""
-        if len(self.data) < 4:
-            self.data = ''
-            return 0
-        value = struct.unpack('>i', self.data[0:4])[0]
-        self.data = self.data[4:]
-        return value
-
-    def _obj_long(self):
-        """Read a long integer in data (length on 1 byte + value as string)."""
-        value = self._obj_len_data(1)
-        if value is None:
-            return None
-        return int(value)
-
-    def _obj_str(self):
-        """Read a string in data (length on 4 bytes + content)."""
-        value = self._obj_len_data(4)
-        if value in ("", None):
-            return ""
-        return value.decode()
-
-    def _obj_buffer(self):
-        """Read a buffer in data (length on 4 bytes + data)."""
-        return self._obj_len_data(4)
-
-    def _obj_ptr(self):
-        """Read a pointer in data (length on 1 byte + value as string)."""
-        value = self._obj_len_data(1)
-        if value is None:
-            return None
-        return '0x%s' % value
-
-    def _obj_time(self):
-        """Read a time in data (length on 1 byte + value as string)."""
-        value = self._obj_len_data(1)
-        if value is None:
-            return None
-        return int(value)
-
-    def _obj_hashtable(self):
-        """
-        Read a hashtable in data
-        (type for keys + type for values + count + items).
-        """
-        type_keys = self._obj_type()
-        type_values = self._obj_type()
-        count = self._obj_int()
-        hashtable = WeechatDict()
-        for _ in range(count):
-            key = self._obj_cb[type_keys]()
-            value = self._obj_cb[type_values]()
-            hashtable[key] = value
-        return hashtable
-
-    def _obj_hdata(self):
-        """Read a hdata in data."""
-        path = self._obj_str()
-        keys = self._obj_str()
-        count = self._obj_int()
-        list_path = path.split('/') if path else []
-        list_keys = keys.split(',') if keys else []
-        keys_types = []
-        dict_keys = WeechatDict()
-        for key in list_keys:
-            items = key.split(':')
-            keys_types.append(items)
-            dict_keys[items[0]] = items[1]
-        items = []
-        for _ in range(count):
-            item = WeechatDict()
-            item['__path'] = []
-            pointers = []
-            for _ in enumerate(list_path):
-                pointers.append(self._obj_ptr())
-            for key, objtype in keys_types:
-                item[key] = self._obj_cb[objtype]()
-            item['__path'] = pointers
-            items.append(item)
-        return {
-            'path': list_path,
-            'keys': dict_keys,
-            'count': count,
-            'items': items,
-        }
-
-    def _obj_info(self):
-        """Read an info in data."""
-        name = self._obj_str()
-        value = self._obj_str()
-        return (name, value)
-
-    def _obj_infolist(self):
-        """Read an infolist in data."""
-        name = self._obj_str()
-        count_items = self._obj_int()
-        items = []
-        for _ in range(count_items):
-            count_vars = self._obj_int()
-            variables = WeechatDict()
-            for _ in range(count_vars):
-                var_name = self._obj_str()
-                var_type = self._obj_type()
-                var_value = self._obj_cb[var_type]()
-                variables[var_name] = var_value
-            items.append(variables)
-        return {
-            'name': name,
-            'items': items
-        }
-
-    def _obj_array(self):
-        """Read an array of values in data."""
-        type_values = self._obj_type()
-        count_values = self._obj_int()
-        values = []
-        for _ in range(count_values):
-            values.append(self._obj_cb[type_values]())
-        return values
-
-    def decode(self, data, separator='\n'):
-        """Decode binary data and return list of objects."""
-        self.data = data
-        size = len(self.data)
-        size_uncompressed = size
-        uncompressed = None
-        # uncompress data (if it is compressed)
-        compression = struct.unpack('b', self.data[4:5])[0]
-        if compression:
-            uncompressed = zlib.decompress(self.data[5:])
-            size_uncompressed = len(uncompressed) + 5
-            uncompressed = b'%s%s%s' % (struct.pack('>i', size_uncompressed),
-                                        struct.pack('b', 0), uncompressed)
-            self.data = uncompressed
-        else:
-            uncompressed = self.data[:]
-        # skip length and compression flag
-        self.data = self.data[5:]
-        # read id
-        msgid = self._obj_str()
-        if msgid is None:
-            msgid = ''
-        # read objects
-        objects = WeechatObjects(separator=separator)
-        while len(self.data) > 0:
-            objtype = self._obj_type()
-            value = self._obj_cb[objtype]()
-            objects.append(WeechatObject(objtype, value, separator=separator))
-        return WeechatMessage(size, size_uncompressed, compression,
-                              uncompressed, msgid, objects)
-
-
-def hex_and_ascii(data, bytes_per_line=10):
-    """Convert a QByteArray to hex + ascii output."""
-    num_lines = ((len(data) - 1) // bytes_per_line) + 1
-    if num_lines == 0:
-        return ''
-    lines = []
-    for i in range(num_lines):
-        str_hex = []
-        str_ascii = []
-        for j in range(bytes_per_line):
-            # We can't easily iterate over individual bytes, so we are going to
-            # do it this way.
-            index = (i*bytes_per_line) + j
-            char = data[index:index+1]
-            if not char:
-                char = b'x'
-            byte = struct.unpack('B', char)[0]
-            str_hex.append(b'%02X' % int(byte))
-            if 32 <= byte <= 127:
-                str_ascii.append(char)
-            else:
-                str_ascii.append(b'.')
-        fmt = b'%%-%ds %%s' % ((bytes_per_line * 3) - 1)
-        lines.append(fmt % (b' '.join(str_hex),
-                            b''.join(str_ascii)))
-    return b'\n'.join(lines)
diff --git a/toxygen/third_party/qweechat/weechat/testproto.py b/toxygen/third_party/qweechat/weechat/testproto.py
deleted file mode 100644
index 2afabd9..0000000
--- a/toxygen/third_party/qweechat/weechat/testproto.py
+++ /dev/null
@@ -1,252 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# testproto.py - command-line program for testing WeeChat/relay protocol
-#
-# Copyright (C) 2013-2022 Sébastien Helleu <flashcode@flashtux.org>
-#
-# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
-#
-# QWeeChat 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.
-#
-# QWeeChat 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 QWeeChat.  If not, see <http://www.gnu.org/licenses/>.
-#
-
-"""Command-line program for testing WeeChat/relay protocol."""
-
-import argparse
-import os
-import select
-import shlex
-import socket
-import struct
-import sys
-import time
-import traceback
-
-from qweechat.weechat import protocol
-
-qweechat_version = '0.1'
-
-NAME = 'qweechat-testproto'
-
-
-class TestProto(object):
-    """Test of WeeChat/relay protocol."""
-
-    def __init__(self, args):
-        self.args = args
-        self.sock = None
-        self.has_quit = False
-        self.address = '{self.args.hostname}/{self.args.port} ' \
-            '(IPv{0})'.format(6 if self.args.ipv6 else 4, self=self)
-
-    def connect(self):
-        """
-        Connect to WeeChat/relay.
-        Return True if OK, False if error.
-        """
-        inet = socket.AF_INET6 if self.args.ipv6 else socket.AF_INET
-        try:
-            self.sock = socket.socket(inet, socket.SOCK_STREAM)
-            self.sock.connect((self.args.hostname, self.args.port))
-        except Exception:
-            if self.sock:
-                self.sock.close()
-            print('Failed to connect to', self.address)
-            return False
-
-        print(f'Connected to {self.address} socket {self.sock}')
-        return True
-
-    def send(self, messages):
-        """
-        Send a text message to WeeChat/relay.
-        Return True if OK, False if error.
-        """
-        try:
-            for msg in messages.split(b'\n'):
-                if msg == b'quit':
-                    self.has_quit = True
-                self.sock.sendall(msg + b'\n')
-                sys.stdout.write(
-                    (b'\x1b[33m<-- ' + msg + b'\x1b[0m\n').decode())
-        except Exception:  # noqa: E722
-            traceback.print_exc()
-            print('Failed to send message')
-            return False
-        return True
-
-    def decode(self, message):
-        """
-        Decode a binary message received from WeeChat/relay.
-        Return True if OK, False if error.
-        """
-        try:
-            proto = protocol.Protocol()
-            msgd = proto.decode(message,
-                                separator=b'\n' if self.args.debug > 0
-                                else ', ')
-            print('')
-            if self.args.debug >= 2 and msgd.uncompressed:
-                # display raw message
-                print('\x1b[32m--> message uncompressed ({0} bytes):\n'
-                      '{1}\x1b[0m'
-                      ''.format(msgd.size_uncompressed,
-                                protocol.hex_and_ascii(msgd.uncompressed, 20)))
-            # display decoded message
-            print('\x1b[32m--> {0}\x1b[0m'.format(msgd))
-        except Exception:  # noqa: E722
-            traceback.print_exc()
-            print('Error while decoding message from WeeChat')
-            return False
-        return True
-
-    def send_stdin(self):
-        """
-        Send commands from standard input if some data is available.
-        Return True if OK (it's OK if stdin has no commands),
-        False if error.
-        """
-        inr = select.select([sys.stdin], [], [], 0)[0]
-        if inr:
-            data = os.read(sys.stdin.fileno(), 4096)
-            if data:
-                if not self.send(data.strip()):
-                    self.sock.close()
-                    return False
-            # open stdin to read user commands
-            sys.stdin = open('/dev/tty')
-        return True
-
-    def mainloop(self):
-        """
-        Main loop: read keyboard, send commands, read socket,
-        decode/display binary messages received from WeeChat/relay.
-        Return 0 if OK, 4 if send error, 5 if decode error.
-        """
-        if self.has_quit:
-            return 0
-        message = b''
-        recvbuf = b''
-        prompt = b'\x1b[36mrelay> \x1b[0m'
-        sys.stdout.write(prompt.decode())
-        sys.stdout.flush()
-        try:
-            while not self.has_quit:
-                inr = select.select([sys.stdin, self.sock], [], [], 1)[0]
-                for _file in inr:
-                    if _file == sys.stdin:
-                        buf = os.read(_file.fileno(), 4096)
-                        if buf:
-                            message += buf
-                            if b'\n' in message:
-                                messages = message.split(b'\n')
-                                msgsent = b'\n'.join(messages[:-1])
-                                if msgsent and not self.send(msgsent):
-                                    return 4
-                                message = messages[-1]
-                                sys.stdout.write((prompt + message).decode())
-                                # sys.stdout.write(prompt + message)
-                                sys.stdout.flush()
-                    else:
-                        buf = _file.recv(4096)
-                        if buf:
-                            recvbuf += buf
-                            while len(recvbuf) >= 4:
-                                remainder = None
-                                length = struct.unpack('>i', recvbuf[0:4])[0]
-                                if len(recvbuf) < length:
-                                    # partial message, just wait for the
-                                    # end of message
-                                    break
-                                # more than one message?
-                                if length < len(recvbuf):
-                                    # save beginning of another message
-                                    remainder = recvbuf[length:]
-                                    recvbuf = recvbuf[0:length]
-                                if not self.decode(recvbuf):
-                                    return 5
-                                if remainder:
-                                    recvbuf = remainder
-                                else:
-                                    recvbuf = b''
-                            sys.stdout.write((prompt + message).decode())
-                            sys.stdout.flush()
-        except Exception:  # noqa: E722
-            traceback.print_exc()
-            self.send(b'quit')
-        return 0
-
-    def __del__(self):
-        print('Closing connection with', self.address)
-        time.sleep(0.5)
-        self.sock.close()
-
-
-def main():
-    """Main function."""
-    # parse command line arguments
-    parser = argparse.ArgumentParser(
-        formatter_class=argparse.RawDescriptionHelpFormatter,
-        fromfile_prefix_chars='@',
-        description='Command-line program for testing WeeChat/relay protocol.',
-        epilog='''
-Environment variable "QWEECHAT_PROTO_OPTIONS" can be set with default options.
-Argument "@file.txt" can be used to read default options in a file.
-
-Some commands can be piped to the script, for example:
-  echo "init password=xxxx" | {name} localhost 5000
-  {name} localhost 5000 < commands.txt
-
-The script returns:
-  0: OK
-  2: wrong arguments (command line)
-  3: connection error
-  4: send error (message sent to WeeChat)
-  5: decode error (message received from WeeChat)
-'''.format(name=NAME))
-    parser.add_argument('-6', '--ipv6', action='store_true',
-                        help='connect using IPv6')
-    parser.add_argument('-d', '--debug', action='count', default=0,
-                        help='debug mode: long objects view '
-                        '(-dd: display raw messages)')
-    parser.add_argument('-v', '--version', action='version',
-                        version=qweechat_version)
-    parser.add_argument('hostname',
-                        help='hostname (or IP address) of machine running '
-                        'WeeChat/relay')
-    parser.add_argument('port', type=int,
-                        help='port of machine running WeeChat/relay')
-    if len(sys.argv) == 1:
-        parser.print_help()
-        sys.exit(0)
-    _args = parser.parse_args(
-        shlex.split(os.getenv('QWEECHAT_PROTO_OPTIONS') or '') + sys.argv[1:])
-
-    test = TestProto(_args)
-
-    # connect to WeeChat/relay
-    if not test.connect():
-        sys.exit(3)
-
-    # send commands from standard input if some data is available
-    if not test.send_stdin():
-        sys.exit(4)
-
-    # main loop (wait commands, display messages received)
-    returncode = test.mainloop()
-    del test
-    sys.exit(returncode)
-
-
-if __name__ == "__main__":
-    main()
diff --git a/toxygen/tox.py b/toxygen/tox.py
new file mode 100644
index 0000000..a783e5a
--- /dev/null
+++ b/toxygen/tox.py
@@ -0,0 +1,2460 @@
+# -*- coding: utf-8 -*-
+from ctypes import c_char_p, Structure, c_bool, byref, c_int, c_size_t, POINTER, c_uint16, c_void_p, c_uint64
+from ctypes import create_string_buffer, ArgumentError, CFUNCTYPE, c_uint32, sizeof, c_uint8
+from toxcore_enums_and_consts import *
+from toxav import ToxAV
+from libtox import LibToxCore
+
+
+class ToxOptions(Structure):
+    _fields_ = [
+        ('ipv6_enabled', c_bool),
+        ('udp_enabled', c_bool),
+        ('proxy_type', c_int),
+        ('proxy_host', c_char_p),
+        ('proxy_port', c_uint16),
+        ('start_port', c_uint16),
+        ('end_port', c_uint16),
+        ('tcp_port', c_uint16),
+        ('savedata_type', c_int),
+        ('savedata_data', c_char_p),
+        ('savedata_length', c_size_t)
+    ]
+
+
+def string_to_bin(tox_id):
+    return c_char_p(bytes.fromhex(tox_id)) if tox_id is not None else None
+
+
+def bin_to_string(raw_id, length):
+    res = ''.join('{:02x}'.format(ord(raw_id[i])) for i in range(length))
+    return res.upper()
+
+
+class Tox:
+
+    libtoxcore = LibToxCore()
+    
+    def __init__(self, tox_options=None, tox_pointer=None):
+        """
+        Creates and initialises a new Tox instance with the options passed.
+
+        This function will bring the instance into a valid state. Running the event loop with a new instance will
+        operate correctly.
+
+        :param tox_options: An options object. If this parameter is None, the default options are used.
+        :param tox_pointer: Tox instance pointer. If this parameter is not None, tox_options will be ignored.
+        """
+        if tox_pointer is not None:
+            self._tox_pointer = tox_pointer
+        else:
+            tox_err_new = c_int()
+            Tox.libtoxcore.tox_new.restype = POINTER(c_void_p)
+            self._tox_pointer = Tox.libtoxcore.tox_new(tox_options, byref(tox_err_new))
+            tox_err_new = tox_err_new.value
+            if tox_err_new == TOX_ERR_NEW['NULL']:
+                raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+            elif tox_err_new == TOX_ERR_NEW['MALLOC']:
+                raise MemoryError('The function was unable to allocate enough '
+                                  'memory to store the internal structures for the Tox object.')
+            elif tox_err_new == TOX_ERR_NEW['PORT_ALLOC']:
+                raise RuntimeError('The function was unable to bind to a port. This may mean that all ports have '
+                                   'already been bound, e.g. by other Tox instances, or it may mean a permission error.'
+                                   ' You may be able to gather more information from errno.')
+            elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_TYPE']:
+                raise ArgumentError('proxy_type was invalid.')
+            elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_HOST']:
+                raise ArgumentError('proxy_type was valid but the proxy_host passed had an invalid format or was NULL.')
+            elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_PORT']:
+                raise ArgumentError('proxy_type was valid, but the proxy_port was invalid.')
+            elif tox_err_new == TOX_ERR_NEW['PROXY_NOT_FOUND']:
+                raise ArgumentError('The proxy address passed could not be resolved.')
+            elif tox_err_new == TOX_ERR_NEW['LOAD_ENCRYPTED']:
+                raise ArgumentError('The byte array to be loaded contained an encrypted save.')
+            elif tox_err_new == TOX_ERR_NEW['LOAD_BAD_FORMAT']:
+                raise ArgumentError('The data format was invalid. This can happen when loading data that was saved by'
+                                    ' an older version of Tox, or when the data has been corrupted. When loading from'
+                                    ' badly formatted data, some data may have been loaded, and the rest is discarded.'
+                                    ' Passing an invalid length parameter also causes this error.')
+
+            self.self_connection_status_cb = None
+            self.friend_name_cb = None
+            self.friend_status_message_cb = None
+            self.friend_status_cb = None
+            self.friend_connection_status_cb = None
+            self.friend_request_cb = None
+            self.friend_read_receipt_cb = None
+            self.friend_typing_cb = None
+            self.friend_message_cb = None
+            self.file_recv_control_cb = None
+            self.file_chunk_request_cb = None
+            self.file_recv_cb = None
+            self.file_recv_chunk_cb = None
+            self.friend_lossy_packet_cb = None
+            self.friend_lossless_packet_cb = None
+            self.group_moderation_cb = None
+            self.group_join_fail_cb = None
+            self.group_self_join_cb = None
+            self.group_invite_cb = None
+            self.group_custom_packet_cb = None
+            self.group_private_message_cb = None
+            self.group_private_message_cb = None
+            self.group_message_cb = None
+            self.group_password_cb = None
+            self.group_peer_limit_cb = None
+            self.group_privacy_state_cb = None
+            self.group_topic_cb = None
+            self.group_peer_status_cb = None
+            self.group_peer_name_cb = None
+            self.group_peer_exit_cb = None
+            self.group_peer_join_cb = None
+
+            self.AV = ToxAV(self._tox_pointer)
+
+    def __del__(self):
+        del self.AV
+        Tox.libtoxcore.tox_kill(self._tox_pointer)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Startup options
+    # -----------------------------------------------------------------------------------------------------------------
+
+    @staticmethod
+    def options_default(tox_options):
+        """
+        Initialises a Tox_Options object with the default options.
+
+        The result of this function is independent of the original options. All values will be overwritten, no values
+        will be read (so it is permissible to pass an uninitialised object).
+
+        If options is NULL, this function has no effect.
+
+        :param tox_options: A pointer to options object to be filled with default options.
+        """
+        Tox.libtoxcore.tox_options_default(tox_options)
+
+    @staticmethod
+    def options_new():
+        """
+        Allocates a new Tox_Options object and initialises it with the default options. This function can be used to
+        preserve long term ABI compatibility by giving the responsibility of allocation and deallocation to the Tox
+        library.
+
+        Objects returned from this function must be freed using the tox_options_free function.
+
+        :return: A pointer to new ToxOptions object with default options or raise MemoryError.
+        """
+        tox_err_options_new = c_int()
+        f = Tox.libtoxcore.tox_options_new
+        f.restype = POINTER(ToxOptions)
+        result = f(byref(tox_err_options_new))
+        tox_err_options_new = tox_err_options_new.value
+        if tox_err_options_new == TOX_ERR_OPTIONS_NEW['OK']:
+            return result
+        elif tox_err_options_new == TOX_ERR_OPTIONS_NEW['MALLOC']:
+            raise MemoryError('The function failed to allocate enough memory for the options struct.')
+
+    @staticmethod
+    def options_free(tox_options):
+        """
+        Releases all resources associated with an options objects.
+
+        Passing a pointer that was not returned by tox_options_new results in undefined behaviour.
+
+        :param tox_options: A pointer to new ToxOptions object
+        """
+        Tox.libtoxcore.tox_options_free(tox_options)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Creation and destruction
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def get_savedata_size(self):
+        """
+        Calculates the number of bytes required to store the tox instance with tox_get_savedata.
+        This function cannot fail. The result is always greater than 0.
+
+        :return: number of bytes
+        """
+        return Tox.libtoxcore.tox_get_savedata_size(self._tox_pointer)
+
+    def get_savedata(self, savedata=None):
+        """
+        Store all information associated with the tox instance to a byte array.
+
+        :param savedata: pointer (c_char_p) to a memory region large enough to store the tox instance data.
+        Call tox_get_savedata_size to find the number of bytes required. If this parameter is None, this function
+        allocates memory for the tox instance data.
+        :return: pointer (c_char_p) to a memory region with the tox instance data
+        """
+        if savedata is None:
+            savedata_size = self.get_savedata_size()
+            savedata = create_string_buffer(savedata_size)
+        Tox.libtoxcore.tox_get_savedata(self._tox_pointer, savedata)
+        return savedata[:]
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Connection lifecycle and event loop
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def bootstrap(self, address, port, public_key):
+        """
+        Sends a "get nodes" request to the given bootstrap node with IP, port, and public key to setup connections.
+
+        This function will attempt to connect to the node using UDP. You must use this function even if
+        Tox_Options.udp_enabled was set to false.
+
+        :param address: The hostname or IP address (IPv4 or IPv6) of the node.
+        :param port: The port on the host on which the bootstrap Tox instance is listening.
+        :param public_key: The long term public key of the bootstrap node (TOX_PUBLIC_KEY_SIZE bytes).
+        :return: True on success.
+        """
+        tox_err_bootstrap = c_int()
+        result = Tox.libtoxcore.tox_bootstrap(self._tox_pointer, c_char_p(address), c_uint16(port),
+                                              string_to_bin(public_key), byref(tox_err_bootstrap))
+        tox_err_bootstrap = tox_err_bootstrap.value
+        if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']:
+            return bool(result)
+        elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_HOST']:
+            raise ArgumentError('The address could not be resolved to an IP '
+                                'address, or the IP address passed was invalid.')
+        elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_PORT']:
+            raise ArgumentError('The port passed was invalid. The valid port range is (1, 65535).')
+
+    def add_tcp_relay(self, address, port, public_key):
+        """
+        Adds additional host:port pair as TCP relay.
+
+        This function can be used to initiate TCP connections to different ports on the same bootstrap node, or to add
+         TCP relays without using them as bootstrap nodes.
+
+        :param address: The hostname or IP address (IPv4 or IPv6) of the TCP relay.
+        :param port: The port on the host on which the TCP relay is listening.
+        :param public_key: The long term public key of the TCP relay (TOX_PUBLIC_KEY_SIZE bytes).
+        :return: True on success.
+        """
+        tox_err_bootstrap = c_int()
+        result = Tox.libtoxcore.tox_add_tcp_relay(self._tox_pointer, c_char_p(address), c_uint16(port),
+                                                  string_to_bin(public_key), byref(tox_err_bootstrap))
+        tox_err_bootstrap = tox_err_bootstrap.value
+        if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']:
+            return bool(result)
+        elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_HOST']:
+            raise ArgumentError('The address could not be resolved to an IP '
+                                'address, or the IP address passed was invalid.')
+        elif tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_PORT']:
+            raise ArgumentError('The port passed was invalid. The valid port range is (1, 65535).')
+
+    def self_get_connection_status(self):
+        """
+        Return whether we are connected to the DHT. The return value is equal to the last value received through the
+        `self_connection_status` callback.
+
+        :return: TOX_CONNECTION
+        """
+        return Tox.libtoxcore.tox_self_get_connection_status(self._tox_pointer)
+
+    def callback_self_connection_status(self, callback, user_data):
+        """
+        Set the callback for the `self_connection_status` event. Pass None to unset.
+
+        This event is triggered whenever there is a change in the DHT connection state. When disconnected, a client may
+        choose to call tox_bootstrap again, to reconnect to the DHT. Note that this state may frequently change for
+        short amounts of time. Clients should therefore not immediately bootstrap on receiving a disconnect.
+
+        :param callback: Python function. Should take pointer (c_void_p) to Tox object,
+        TOX_CONNECTION (c_int),
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_int, c_void_p)
+        self.self_connection_status_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_self_connection_status(self._tox_pointer,
+                                                           self.self_connection_status_cb, user_data)
+
+    def iteration_interval(self):
+        """
+        Return the time in milliseconds before tox_iterate() should be called again for optimal performance.
+        :return: time in milliseconds
+        """
+        return Tox.libtoxcore.tox_iteration_interval(self._tox_pointer)
+
+    def iterate(self):
+        """
+        The main loop that needs to be run in intervals of tox_iteration_interval() milliseconds.
+        """
+        Tox.libtoxcore.tox_iterate(self._tox_pointer)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Internal client information (Tox address/id)
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def self_get_address(self, address=None):
+        """
+        Writes the Tox friend address of the client to a byte array. The address is not in human-readable format. If a
+        client wants to display the address, formatting is required.
+
+        :param address: pointer (c_char_p) to a memory region of at least TOX_ADDRESS_SIZE bytes. If this parameter is
+        None, this function allocates memory for address.
+        :return: Tox friend address
+        """
+        if address is None:
+            address = create_string_buffer(TOX_ADDRESS_SIZE)
+        Tox.libtoxcore.tox_self_get_address(self._tox_pointer, address)
+        return bin_to_string(address, TOX_ADDRESS_SIZE)
+
+    def self_set_nospam(self, nospam):
+        """
+        Set the 4-byte nospam part of the address.
+
+        :param nospam: Any 32 bit unsigned integer.
+        """
+        Tox.libtoxcore.tox_self_set_nospam(self._tox_pointer, c_uint32(nospam))
+
+    def self_get_nospam(self):
+        """
+        Get the 4-byte nospam part of the address.
+
+        :return: nospam part of the address
+        """
+        return Tox.libtoxcore.tox_self_get_nospam(self._tox_pointer)
+
+    def self_get_public_key(self, public_key=None):
+        """
+        Copy the Tox Public Key (long term) from the Tox object.
+
+        :param public_key: A memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this parameter is NULL, this
+        function allocates memory for Tox Public Key.
+        :return: Tox Public Key
+        """
+        if public_key is None:
+            public_key = create_string_buffer(TOX_PUBLIC_KEY_SIZE)
+        Tox.libtoxcore.tox_self_get_public_key(self._tox_pointer, public_key)
+        return bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE)
+
+    def self_get_secret_key(self, secret_key=None):
+        """
+        Copy the Tox Secret Key from the Tox object.
+
+        :param secret_key: pointer (c_char_p) to a memory region of at least TOX_SECRET_KEY_SIZE bytes. If this
+        parameter is NULL, this function allocates memory for Tox Secret Key.
+        :return: Tox Secret Key
+        """
+        if secret_key is None:
+            secret_key = create_string_buffer(TOX_SECRET_KEY_SIZE)
+        Tox.libtoxcore.tox_self_get_secret_key(self._tox_pointer, secret_key)
+        return bin_to_string(secret_key, TOX_SECRET_KEY_SIZE)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # User-visible client information (nickname/status)
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def self_set_name(self, name):
+        """
+        Set the nickname for the Tox client.
+
+        Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length is 0, the name parameter is ignored
+        (it can be None), and the nickname is set back to empty.
+        :param name: New nickname.
+        :return: True on success.
+        """
+        tox_err_set_info = c_int()
+        result = Tox.libtoxcore.tox_self_set_name(self._tox_pointer, c_char_p(name),
+                                                  c_size_t(len(name)), byref(tox_err_set_info))
+        tox_err_set_info = tox_err_set_info.value
+        if tox_err_set_info == TOX_ERR_SET_INFO['OK']:
+            return bool(result)
+        elif tox_err_set_info == TOX_ERR_SET_INFO['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_set_info == TOX_ERR_SET_INFO['TOO_LONG']:
+            raise ArgumentError('Information length exceeded maximum permissible size.')
+
+    def self_get_name_size(self):
+        """
+        Return the length of the current nickname as passed to tox_self_set_name.
+
+        If no nickname was set before calling this function, the name is empty, and this function returns 0.
+
+        :return: length of the current nickname
+        """
+        return Tox.libtoxcore.tox_self_get_name_size(self._tox_pointer)
+
+    def self_get_name(self, name=None):
+        """
+        Write the nickname set by tox_self_set_name to a byte array.
+
+        If no nickname was set before calling this function, the name is empty, and this function has no effect.
+
+        Call tox_self_get_name_size to find out how much memory to allocate for the result.
+
+        :param name: pointer (c_char_p) to a memory region location large enough to hold the nickname. If this parameter
+        is NULL, the function allocates memory for the nickname.
+        :return: nickname
+        """
+        if name is None:
+            name = create_string_buffer(self.self_get_name_size())
+        Tox.libtoxcore.tox_self_get_name(self._tox_pointer, name)
+        return str(name.value, 'utf-8')
+
+    def self_set_status_message(self, status_message):
+        """
+        Set the client's status message.
+
+        Status message length cannot exceed TOX_MAX_STATUS_MESSAGE_LENGTH. If length is 0, the status parameter is
+        ignored, and the user status is set back to empty.
+
+        :param status_message: new status message
+        :return: True on success.
+        """
+        tox_err_set_info = c_int()
+        result = Tox.libtoxcore.tox_self_set_status_message(self._tox_pointer, c_char_p(status_message),
+                                                            c_size_t(len(status_message)), byref(tox_err_set_info))
+        tox_err_set_info = tox_err_set_info.value
+        if tox_err_set_info == TOX_ERR_SET_INFO['OK']:
+            return bool(result)
+        elif tox_err_set_info == TOX_ERR_SET_INFO['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_set_info == TOX_ERR_SET_INFO['TOO_LONG']:
+            raise ArgumentError('Information length exceeded maximum permissible size.')
+
+    def self_get_status_message_size(self):
+        """
+        Return the length of the current status message as passed to tox_self_set_status_message.
+
+        If no status message was set before calling this function, the status is empty, and this function returns 0.
+
+        :return: length of the current status message
+        """
+        return Tox.libtoxcore.tox_self_get_status_message_size(self._tox_pointer)
+
+    def self_get_status_message(self, status_message=None):
+        """
+        Write the status message set by tox_self_set_status_message to a byte array.
+
+        If no status message was set before calling this function, the status is empty, and this function has no effect.
+
+        Call tox_self_get_status_message_size to find out how much memory to allocate for the result.
+
+        :param status_message: pointer (c_char_p) to a valid memory location large enough to hold the status message.
+        If this parameter is None, the function allocates memory for the status message.
+        :return: status message
+        """
+        if status_message is None:
+            status_message = create_string_buffer(self.self_get_status_message_size())
+        Tox.libtoxcore.tox_self_get_status_message(self._tox_pointer, status_message)
+        return str(status_message.value, 'utf-8')
+
+    def self_set_status(self, status):
+        """
+        Set the client's user status.
+
+        :param status: One of the user statuses listed in the enumeration TOX_USER_STATUS.
+        """
+        Tox.libtoxcore.tox_self_set_status(self._tox_pointer, c_int(status))
+
+    def self_get_status(self):
+        """
+        Returns the client's user status.
+
+        :return: client's user status
+        """
+        return Tox.libtoxcore.tox_self_get_status(self._tox_pointer)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Friend list management
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def friend_add(self, address, message):
+        """
+        Add a friend to the friend list and send a friend request.
+
+        A friend request message must be at least 1 byte long and at most TOX_MAX_FRIEND_REQUEST_LENGTH.
+
+        Friend numbers are unique identifiers used in all functions that operate on friends. Once added, a friend number
+        is stable for the lifetime of the Tox object. After saving the state and reloading it, the friend numbers may
+        not be the same as before. Deleting a friend creates a gap in the friend number set, which is filled by the next
+        adding of a friend. Any pattern in friend numbers should not be relied on.
+
+        If more than INT32_MAX friends are added, this function causes undefined behaviour.
+
+        :param address: The address of the friend (returned by tox_self_get_address of the friend you wish to add) it
+        must be TOX_ADDRESS_SIZE bytes.
+        :param message: The message that will be sent along with the friend request.
+        :return: the friend number on success, UINT32_MAX on failure.
+        """
+        tox_err_friend_add = c_int()
+        result = Tox.libtoxcore.tox_friend_add(self._tox_pointer, string_to_bin(address), c_char_p(message),
+                                               c_size_t(len(message)), byref(tox_err_friend_add))
+        tox_err_friend_add = tox_err_friend_add.value
+        if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OK']:
+            return result
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['TOO_LONG']:
+            raise ArgumentError('The length of the friend request message exceeded TOX_MAX_FRIEND_REQUEST_LENGTH.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NO_MESSAGE']:
+            raise ArgumentError('The friend request message was empty. This, and the TOO_LONG code will never be'
+                                ' returned from tox_friend_add_norequest.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['OWN_KEY']:
+            raise ArgumentError('The friend address belongs to the sending client.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['ALREADY_SENT']:
+            raise ArgumentError('A friend request has already been sent, or the address belongs to a friend that is'
+                                ' already on the friend list.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['BAD_CHECKSUM']:
+            raise ArgumentError('The friend address checksum failed.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['SET_NEW_NOSPAM']:
+            raise ArgumentError('The friend was already there, but the nospam value was different.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['MALLOC']:
+            raise MemoryError('A memory allocation failed when trying to increase the friend list size.')
+
+    def friend_add_norequest(self, public_key):
+        """
+        Add a friend without sending a friend request.
+
+        This function is used to add a friend in response to a friend request. If the client receives a friend request,
+        it can be reasonably sure that the other client added this client as a friend, eliminating the need for a friend
+        request.
+
+        This function is also useful in a situation where both instances are controlled by the same entity, so that this
+        entity can perform the mutual friend adding. In this case, there is no need for a friend request, either.
+
+        :param public_key: A byte array of length TOX_PUBLIC_KEY_SIZE containing the Public Key (not the Address) of the
+        friend to add.
+        :return: the friend number on success, UINT32_MAX on failure.
+        """
+        tox_err_friend_add = c_int()
+        result = Tox.libtoxcore.tox_friend_add_norequest(self._tox_pointer, string_to_bin(public_key),
+                                                         byref(tox_err_friend_add))
+        tox_err_friend_add = tox_err_friend_add.value
+        if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OK']:
+            return result
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['TOO_LONG']:
+            raise ArgumentError('The length of the friend request message exceeded TOX_MAX_FRIEND_REQUEST_LENGTH.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['NO_MESSAGE']:
+            raise ArgumentError('The friend request message was empty. This, and the TOO_LONG code will never be'
+                                ' returned from tox_friend_add_norequest.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['OWN_KEY']:
+            raise ArgumentError('The friend address belongs to the sending client.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['ALREADY_SENT']:
+            raise ArgumentError('A friend request has already been sent, or the address belongs to a friend that is'
+                                ' already on the friend list.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['BAD_CHECKSUM']:
+            raise ArgumentError('The friend address checksum failed.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['SET_NEW_NOSPAM']:
+            raise ArgumentError('The friend was already there, but the nospam value was different.')
+        elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['MALLOC']:
+            raise MemoryError('A memory allocation failed when trying to increase the friend list size.')
+
+    def friend_delete(self, friend_number):
+        """
+        Remove a friend from the friend list.
+
+        This does not notify the friend of their deletion. After calling this function, this client will appear offline
+        to the friend and no communication can occur between the two.
+
+        :param friend_number: Friend number for the friend to be deleted.
+        :return: True on success.
+        """
+        tox_err_friend_delete = c_int()
+        result = Tox.libtoxcore.tox_friend_delete(self._tox_pointer, c_uint32(friend_number),
+                                                  byref(tox_err_friend_delete))
+        tox_err_friend_delete = tox_err_friend_delete.value
+        if tox_err_friend_delete == TOX_ERR_FRIEND_DELETE['OK']:
+            return bool(result)
+        elif tox_err_friend_delete == TOX_ERR_FRIEND_DELETE['FRIEND_NOT_FOUND']:
+            raise ArgumentError('There was no friend with the given friend number. No friends were deleted.')
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Friend list queries
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def friend_by_public_key(self, public_key):
+        """
+        Return the friend number associated with that Public Key.
+
+        :param public_key: A byte array containing the Public Key.
+        :return: friend number
+        """
+        tox_err_friend_by_public_key = c_int()
+        result = Tox.libtoxcore.tox_friend_by_public_key(self._tox_pointer, string_to_bin(public_key),
+                                                         byref(tox_err_friend_by_public_key))
+        tox_err_friend_by_public_key = tox_err_friend_by_public_key.value
+        if tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['OK']:
+            return result
+        elif tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['NOT_FOUND']:
+            raise ArgumentError('No friend with the given Public Key exists on the friend list.')
+
+    def friend_exists(self, friend_number):
+        """
+        Checks if a friend with the given friend number exists and returns true if it does.
+        """
+        return bool(Tox.libtoxcore.tox_friend_exists(self._tox_pointer, c_uint32(friend_number)))
+
+    def self_get_friend_list_size(self):
+        """
+        Return the number of friends on the friend list.
+
+        This function can be used to determine how much memory to allocate for tox_self_get_friend_list.
+
+        :return: number of friends
+        """
+        return Tox.libtoxcore.tox_self_get_friend_list_size(self._tox_pointer)
+
+    def self_get_friend_list(self, friend_list=None):
+        """
+        Copy a list of valid friend numbers into an array.
+
+        Call tox_self_get_friend_list_size to determine the number of elements to allocate.
+
+        :param friend_list: pointer (c_char_p) to a memory region with enough space to hold the friend list. If this
+        parameter is None, this function allocates memory for the friend list.
+        :return: friend list
+        """
+        friend_list_size = self.self_get_friend_list_size()
+        if friend_list is None:
+            friend_list = create_string_buffer(sizeof(c_uint32) * friend_list_size)
+            friend_list = POINTER(c_uint32)(friend_list)
+        Tox.libtoxcore.tox_self_get_friend_list(self._tox_pointer, friend_list)
+        return friend_list[0:friend_list_size]
+
+    def friend_get_public_key(self, friend_number, public_key=None):
+        """
+        Copies the Public Key associated with a given friend number to a byte array.
+
+        :param friend_number: The friend number you want the Public Key of.
+        :param public_key: pointer (c_char_p) to a memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this
+        parameter is None, this function allocates memory for Tox Public Key.
+        :return: Tox Public Key
+        """
+        if public_key is None:
+            public_key = create_string_buffer(TOX_PUBLIC_KEY_SIZE)
+        tox_err_friend_get_public_key = c_int()
+        Tox.libtoxcore.tox_friend_get_public_key(self._tox_pointer, c_uint32(friend_number), public_key,
+                                                 byref(tox_err_friend_get_public_key))
+        tox_err_friend_get_public_key = tox_err_friend_get_public_key.value
+        if tox_err_friend_get_public_key == TOX_ERR_FRIEND_GET_PUBLIC_KEY['OK']:
+            return bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE)
+        elif tox_err_friend_get_public_key == TOX_ERR_FRIEND_GET_PUBLIC_KEY['FRIEND_NOT_FOUND']:
+            raise ArgumentError('No friend with the given number exists on the friend list.')
+
+    def friend_get_last_online(self, friend_number):
+        """
+        Return a unix-time timestamp of the last time the friend associated with a given friend number was seen online.
+        This function will return UINT64_MAX on error.
+
+        :param friend_number: The friend number you want to query.
+        :return: unix-time timestamp
+        """
+        tox_err_last_online = c_int()
+        result = Tox.libtoxcore.tox_friend_get_last_online(self._tox_pointer, c_uint32(friend_number),
+                                                           byref(tox_err_last_online))
+        tox_err_last_online = tox_err_last_online.value
+        if tox_err_last_online == TOX_ERR_FRIEND_GET_LAST_ONLINE['OK']:
+            return result
+        elif tox_err_last_online == TOX_ERR_FRIEND_GET_LAST_ONLINE['FRIEND_NOT_FOUND']:
+            raise ArgumentError('No friend with the given number exists on the friend list.')
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Friend-specific state queries (can also be received through callbacks)
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def friend_get_name_size(self, friend_number):
+        """
+        Return the length of the friend's name. If the friend number is invalid, the return value is unspecified.
+
+        The return value is equal to the `length` argument received by the last `friend_name` callback.
+        """
+        tox_err_friend_query = c_int()
+        result = Tox.libtoxcore.tox_friend_get_name_size(self._tox_pointer, c_uint32(friend_number),
+                                                         byref(tox_err_friend_query))
+        tox_err_friend_query = tox_err_friend_query.value
+        if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']:
+            return result
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']:
+            raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike'
+                                ' the `_self_` variants of these functions, which have no effect when a parameter is'
+                                ' NULL, these functions return an error in that case.')
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number did not designate a valid friend.')
+
+    def friend_get_name(self, friend_number, name=None):
+        """
+        Write the name of the friend designated by the given friend number to a byte array.
+
+        Call tox_friend_get_name_size to determine the allocation size for the `name` parameter.
+
+        The data written to `name` is equal to the data received by the last `friend_name` callback.
+
+        :param friend_number: number of friend
+        :param name: pointer (c_char_p) to a valid memory region large enough to store the friend's name.
+        :return: name of the friend
+        """
+        if name is None:
+            name = create_string_buffer(self.friend_get_name_size(friend_number))
+        tox_err_friend_query = c_int()
+        Tox.libtoxcore.tox_friend_get_name(self._tox_pointer, c_uint32(friend_number), name,
+                                           byref(tox_err_friend_query))
+        tox_err_friend_query = tox_err_friend_query.value
+        if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']:
+            return str(name.value, 'utf-8')
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']:
+            raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike'
+                                ' the `_self_` variants of these functions, which have no effect when a parameter is'
+                                ' NULL, these functions return an error in that case.')
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number did not designate a valid friend.')
+
+    def callback_friend_name(self, callback, user_data):
+        """
+        Set the callback for the `friend_name` event. Pass None to unset.
+
+        This event is triggered when a friend changes their name.
+
+        :param callback: Python function. Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend whose name changed,
+        A byte array (c_char_p) containing the same data as tox_friend_get_name would write to its `name` parameter,
+        A value (c_size_t) equal to the return value of tox_friend_get_name_size,
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p)
+        self.friend_name_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_friend_name(self._tox_pointer, self.friend_name_cb, user_data)
+
+    def friend_get_status_message_size(self, friend_number):
+        """
+        Return the length of the friend's status message. If the friend number is invalid, the return value is SIZE_MAX.
+
+        :return: length of the friend's status message
+        """
+        tox_err_friend_query = c_int()
+        result = Tox.libtoxcore.tox_friend_get_status_message_size(self._tox_pointer, c_uint32(friend_number),
+                                                                   byref(tox_err_friend_query))
+        tox_err_friend_query = tox_err_friend_query.value
+        if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']:
+            return result
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']:
+            raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike'
+                                ' the `_self_` variants of these functions, which have no effect when a parameter is'
+                                ' NULL, these functions return an error in that case.')
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number did not designate a valid friend.')
+
+    def friend_get_status_message(self, friend_number, status_message=None):
+        """
+        Write the status message of the friend designated by the given friend number to a byte array.
+
+        Call tox_friend_get_status_message_size to determine the allocation size for the `status_name` parameter.
+
+        The data written to `status_message` is equal to the data received by the last `friend_status_message` callback.
+
+        :param friend_number:
+        :param status_message: pointer (c_char_p) to a valid memory region large enough to store the friend's status
+        message.
+        :return: status message of the friend
+        """
+        if status_message is None:
+            status_message = create_string_buffer(self.friend_get_status_message_size(friend_number))
+        tox_err_friend_query = c_int()
+        Tox.libtoxcore.tox_friend_get_status_message(self._tox_pointer, c_uint32(friend_number), status_message,
+                                                     byref(tox_err_friend_query))
+        tox_err_friend_query = tox_err_friend_query.value
+        if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']:
+            return str(status_message.value, 'utf-8')
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']:
+            raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike'
+                                ' the `_self_` variants of these functions, which have no effect when a parameter is'
+                                ' NULL, these functions return an error in that case.')
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number did not designate a valid friend.')
+
+    def callback_friend_status_message(self, callback, user_data):
+        """
+        Set the callback for the `friend_status_message` event. Pass NULL to unset.
+
+        This event is triggered when a friend changes their status message.
+
+        :param callback: Python function. Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend whose status message changed,
+        A byte array (c_char_p) containing the same data as tox_friend_get_status_message would write to its
+        `status_message` parameter,
+        A value (c_size_t) equal to the return value of tox_friend_get_status_message_size,
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p)
+        self.friend_status_message_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_friend_status_message(self._tox_pointer,
+                                                          self.friend_status_message_cb, c_void_p(user_data))
+
+    def friend_get_status(self, friend_number):
+        """
+        Return the friend's user status (away/busy/...). If the friend number is invalid, the return value is
+        unspecified.
+
+        The status returned is equal to the last status received through the `friend_status` callback.
+
+        :return: TOX_USER_STATUS
+        """
+        tox_err_friend_query = c_int()
+        result = Tox.libtoxcore.tox_friend_get_status(self._tox_pointer, c_uint32(friend_number),
+                                                      byref(tox_err_friend_query))
+        tox_err_friend_query = tox_err_friend_query.value
+        if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']:
+            return result
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']:
+            raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike'
+                                ' the `_self_` variants of these functions, which have no effect when a parameter is'
+                                ' NULL, these functions return an error in that case.')
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number did not designate a valid friend.')
+
+    def callback_friend_status(self, callback, user_data):
+        """
+        Set the callback for the `friend_status` event. Pass None to unset.
+
+        This event is triggered when a friend changes their user status.
+
+        :param callback: Python function. Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend whose user status changed,
+        The new user status (TOX_USER_STATUS),
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p)
+        self.friend_status_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_friend_status(self._tox_pointer, self.friend_status_cb, c_void_p(user_data))
+
+    def friend_get_connection_status(self, friend_number):
+        """
+        Check whether a friend is currently connected to this client.
+
+        The result of this function is equal to the last value received by the `friend_connection_status` callback.
+
+        :param friend_number: The friend number for which to query the connection status.
+        :return: the friend's connection status (TOX_CONNECTION) as it was received through the
+        `friend_connection_status` event.
+        """
+        tox_err_friend_query = c_int()
+        result = Tox.libtoxcore.tox_friend_get_connection_status(self._tox_pointer, c_uint32(friend_number),
+                                                                 byref(tox_err_friend_query))
+        tox_err_friend_query = tox_err_friend_query.value
+        if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']:
+            return result
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']:
+            raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike'
+                                ' the `_self_` variants of these functions, which have no effect when a parameter is'
+                                ' NULL, these functions return an error in that case.')
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number did not designate a valid friend.')
+
+    def callback_friend_connection_status(self, callback, user_data):
+        """
+        Set the callback for the `friend_connection_status` event. Pass NULL to unset.
+
+        This event is triggered when a friend goes offline after having been online, or when a friend goes online.
+
+        This callback is not called when adding friends. It is assumed that when adding friends, their connection status
+        is initially offline.
+
+        :param callback: Python function. Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend whose connection status changed,
+        The result of calling tox_friend_get_connection_status (TOX_CONNECTION) on the passed friend_number,
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p)
+        self.friend_connection_status_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_friend_connection_status(self._tox_pointer,
+                                                             self.friend_connection_status_cb, c_void_p(user_data))
+
+    def friend_get_typing(self, friend_number):
+        """
+        Check whether a friend is currently typing a message.
+
+        :param friend_number: The friend number for which to query the typing status.
+        :return: true if the friend is typing.
+        """
+        tox_err_friend_query = c_int()
+        result = Tox.libtoxcore.tox_friend_get_typing(self._tox_pointer, c_uint32(friend_number),
+                                                      byref(tox_err_friend_query))
+        tox_err_friend_query = tox_err_friend_query.value
+        if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']:
+            return bool(result)
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']:
+            raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike'
+                                ' the `_self_` variants of these functions, which have no effect when a parameter is'
+                                ' NULL, these functions return an error in that case.')
+        elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number did not designate a valid friend.')
+
+    def callback_friend_typing(self, callback, user_data):
+        """
+        Set the callback for the `friend_typing` event. Pass NULL to unset.
+
+        This event is triggered when a friend starts or stops typing.
+
+        :param callback: Python function. Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend who started or stopped typing,
+        The result of calling tox_friend_get_typing (c_bool) on the passed friend_number,
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_void_p)
+        self.friend_typing_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_friend_typing(self._tox_pointer, self.friend_typing_cb, c_void_p(user_data))
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Sending private messages
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def self_set_typing(self, friend_number, typing):
+        """
+        Set the client's typing status for a friend.
+
+        The client is responsible for turning it on or off.
+
+        :param friend_number: The friend to which the client is typing a message.
+        :param typing: The typing status. True means the client is typing.
+        :return: True on success.
+        """
+        tox_err_set_typing = c_int()
+        result = Tox.libtoxcore.tox_self_set_typing(self._tox_pointer, c_uint32(friend_number),
+                                                    c_bool(typing), byref(tox_err_set_typing))
+        tox_err_set_typing = tox_err_set_typing.value
+        if tox_err_set_typing == TOX_ERR_SET_TYPING['OK']:
+            return bool(result)
+        elif tox_err_set_typing == TOX_ERR_SET_TYPING['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend number did not designate a valid friend.')
+
+    def friend_send_message(self, friend_number, message_type, message):
+        """
+        Send a text chat message to an online friend.
+
+        This function creates a chat message packet and pushes it into the send queue.
+
+        The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages must be split by the client and sent
+        as separate messages. Other clients can then reassemble the fragments. Messages may not be empty.
+
+        The return value of this function is the message ID. If a read receipt is received, the triggered
+        `friend_read_receipt` event will be passed this message ID.
+
+        Message IDs are unique per friend. The first message ID is 0. Message IDs are incremented by 1 each time a
+        message is sent. If UINT32_MAX messages were sent, the next message ID is 0.
+
+        :param friend_number: The friend number of the friend to send the message to.
+        :param message_type: Message type (TOX_MESSAGE_TYPE).
+        :param message: A non-None message text.
+        :return: message ID
+        """
+        tox_err_friend_send_message = c_int()
+        result = Tox.libtoxcore.tox_friend_send_message(self._tox_pointer, c_uint32(friend_number),
+                                                        c_int(message_type), c_char_p(message), c_size_t(len(message)),
+                                                        byref(tox_err_friend_send_message))
+        tox_err_friend_send_message = tox_err_friend_send_message.value
+        if tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['OK']:
+            return result
+        elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend number did not designate a valid friend.')
+        elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_CONNECTED']:
+            raise ArgumentError('This client is currently not connected to the friend.')
+        elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['SENDQ']:
+            raise MemoryError('An allocation error occurred while increasing the send queue size.')
+        elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['TOO_LONG']:
+            raise ArgumentError('Message length exceeded TOX_MAX_MESSAGE_LENGTH.')
+        elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['EMPTY']:
+            raise ArgumentError('Attempted to send a zero-length message.')
+
+    def callback_friend_read_receipt(self, callback, user_data):
+        """
+        Set the callback for the `friend_read_receipt` event. Pass None to unset.
+
+        This event is triggered when the friend receives the message sent with tox_friend_send_message with the
+        corresponding message ID.
+
+        :param callback: Python function. Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend who received the message,
+        The message ID (c_uint32) as returned from tox_friend_send_message corresponding to the message sent,
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p)
+        self.friend_read_receipt_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_friend_read_receipt(self._tox_pointer,
+                                                        self.friend_read_receipt_cb, c_void_p(user_data))
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Receiving private messages and friend requests
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def callback_friend_request(self, callback, user_data):
+        """
+        Set the callback for the `friend_request` event. Pass None to unset.
+
+        This event is triggered when a friend request is received.
+
+        :param callback: Python function. Should take pointer (c_void_p) to Tox object,
+        The Public Key (c_uint8 array) of the user who sent the friend request,
+        The message (c_char_p) they sent along with the request,
+        The size (c_size_t) of the message byte array,
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, POINTER(c_uint8), c_char_p, c_size_t, c_void_p)
+        self.friend_request_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_friend_request(self._tox_pointer, self.friend_request_cb, c_void_p(user_data))
+
+    def callback_friend_message(self, callback, user_data):
+        """
+        Set the callback for the `friend_message` event. Pass None to unset.
+
+        This event is triggered when a message from a friend is received.
+
+        :param callback: Python function. Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend who sent the message,
+        Message type (TOX_MESSAGE_TYPE),
+        The message data (c_char_p) they sent,
+        The size (c_size_t) of the message byte array.
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_char_p, c_size_t, c_void_p)
+        self.friend_message_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_friend_message(self._tox_pointer, self.friend_message_cb, c_void_p(user_data))
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # File transmission: common between sending and receiving
+    # -----------------------------------------------------------------------------------------------------------------
+
+    @staticmethod
+    def hash(data, hash=None):
+        """
+        Generates a cryptographic hash of the given data.
+
+        This function may be used by clients for any purpose, but is provided primarily for validating cached avatars.
+        This use is highly recommended to avoid unnecessary avatar updates.
+
+        If hash is NULL or data is NULL while length is not 0 the function returns false, otherwise it returns true.
+
+        This function is a wrapper to internal message-digest functions.
+
+        :param hash: A valid memory location the hash data. It must be at least TOX_HASH_LENGTH bytes in size.
+        :param data: Data to be hashed or NULL.
+        :return: true if hash was not NULL.
+        """
+        if hash is None:
+            hash = create_string_buffer(TOX_HASH_LENGTH)
+        Tox.libtoxcore.tox_hash(hash, c_char_p(data), len(data))
+        return bin_to_string(hash, TOX_HASH_LENGTH)
+
+    def file_control(self, friend_number, file_number, control):
+        """
+        Sends a file control command to a friend for a given file transfer.
+
+        :param friend_number: The friend number of the friend the file is being transferred to or received from.
+        :param file_number: The friend-specific identifier for the file transfer.
+        :param control: The control (TOX_FILE_CONTROL) command to send.
+        :return: True on success.
+        """
+        tox_err_file_control = c_int()
+        result = Tox.libtoxcore.tox_file_control(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number),
+                                                 c_int(control), byref(tox_err_file_control))
+        tox_err_file_control = tox_err_file_control.value
+        if tox_err_file_control == TOX_ERR_FILE_CONTROL['OK']:
+            return bool(result)
+        elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number passed did not designate a valid friend.')
+        elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_CONNECTED']:
+            raise ArgumentError('This client is currently not connected to the friend.')
+        elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_FOUND']:
+            raise ArgumentError('No file transfer with the given file number was found for the given friend.')
+        elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_PAUSED']:
+            raise RuntimeError('A RESUME control was sent, but the file transfer is running normally.')
+        elif tox_err_file_control == TOX_ERR_FILE_CONTROL['DENIED']:
+            raise RuntimeError('A RESUME control was sent, but the file transfer was paused by the other party. Only '
+                               'the party that paused the transfer can resume it.')
+        elif tox_err_file_control == TOX_ERR_FILE_CONTROL['ALREADY_PAUSED']:
+            raise RuntimeError('A PAUSE control was sent, but the file transfer was already paused.')
+        elif tox_err_file_control == TOX_ERR_FILE_CONTROL['SENDQ']:
+            raise RuntimeError('Packet queue is full.')
+
+    def callback_file_recv_control(self, callback, user_data):
+        """
+        Set the callback for the `file_recv_control` event. Pass NULL to unset.
+
+        This event is triggered when a file control command is received from a friend.
+
+        :param callback: Python function.
+        When receiving TOX_FILE_CONTROL_CANCEL, the client should release the resources associated with the file number
+        and consider the transfer failed.
+
+        Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend who is sending the file.
+        The friend-specific file number (c_uint32) the data received is associated with.
+        The file control (TOX_FILE_CONTROL) command received.
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p)
+        self.file_recv_control_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_file_recv_control(self._tox_pointer,
+                                                      self.file_recv_control_cb, user_data)
+
+    def file_seek(self, friend_number, file_number, position):
+        """
+        Sends a file seek control command to a friend for a given file transfer.
+
+        This function can only be called to resume a file transfer right before TOX_FILE_CONTROL_RESUME is sent.
+
+        :param friend_number: The friend number of the friend the file is being received from.
+        :param file_number: The friend-specific identifier for the file transfer.
+        :param position: The position that the file should be seeked to.
+        :return: True on success.
+        """
+        tox_err_file_seek = c_int()
+        result = Tox.libtoxcore.tox_file_control(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number),
+                                                 c_uint64(position), byref(tox_err_file_seek))
+        tox_err_file_seek = tox_err_file_seek.value
+        if tox_err_file_seek == TOX_ERR_FILE_SEEK['OK']:
+            return bool(result)
+        elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number passed did not designate a valid friend.')
+        elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_CONNECTED']:
+            raise ArgumentError('This client is currently not connected to the friend.')
+        elif tox_err_file_seek == TOX_ERR_FILE_SEEK['NOT_FOUND']:
+            raise ArgumentError('No file transfer with the given file number was found for the given friend.')
+        elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SEEK_DENIED']:
+            raise IOError('File was not in a state where it could be seeked.')
+        elif tox_err_file_seek == TOX_ERR_FILE_SEEK['INVALID_POSITION']:
+            raise ArgumentError('Seek position was invalid')
+        elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SENDQ']:
+            raise RuntimeError('Packet queue is full.')
+
+    def file_get_file_id(self, friend_number, file_number, file_id=None):
+        """
+        Copy the file id associated to the file transfer to a byte array.
+
+        :param friend_number: The friend number of the friend the file is being transferred to or received from.
+        :param file_number: The friend-specific identifier for the file transfer.
+        :param file_id: A pointer (c_char_p) to memory region of at least TOX_FILE_ID_LENGTH bytes. If this parameter is
+        None, this function has no effect.
+        :return: file id.
+        """
+        if file_id is None:
+            file_id = create_string_buffer(TOX_FILE_ID_LENGTH)
+        tox_err_file_get = c_int()
+        Tox.libtoxcore.tox_file_get_file_id(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), file_id,
+                                            byref(tox_err_file_get))
+        tox_err_file_get = tox_err_file_get.value
+        if tox_err_file_get == TOX_ERR_FILE_GET['OK']:
+            return bin_to_string(file_id, TOX_FILE_ID_LENGTH)
+        elif tox_err_file_get == TOX_ERR_FILE_GET['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_file_get == TOX_ERR_FILE_GET['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number passed did not designate a valid friend.')
+        elif tox_err_file_get == TOX_ERR_FILE_GET['NOT_FOUND']:
+            raise ArgumentError('No file transfer with the given file number was found for the given friend.')
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # File transmission: sending
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def file_send(self, friend_number, kind, file_size, file_id, filename):
+        """
+        Send a file transmission request.
+
+        Maximum filename length is TOX_MAX_FILENAME_LENGTH bytes. The filename should generally just be a file name, not
+        a path with directory names.
+
+        If a non-UINT64_MAX file size is provided, it can be used by both sides to determine the sending progress. File
+        size can be set to UINT64_MAX for streaming data of unknown size.
+
+        File transmission occurs in chunks, which are requested through the `file_chunk_request` event.
+
+        When a friend goes offline, all file transfers associated with the friend are purged from core.
+
+        If the file contents change during a transfer, the behaviour is unspecified in general. What will actually
+        happen depends on the mode in which the file was modified and how the client determines the file size.
+
+        - If the file size was increased
+            - and sending mode was streaming (file_size = UINT64_MAX), the behaviour will be as expected.
+            - and sending mode was file (file_size != UINT64_MAX), the file_chunk_request callback will receive length =
+            0 when Core thinks the file transfer has finished. If the client remembers the file size as it was when
+            sending the request, it will terminate the transfer normally. If the client re-reads the size, it will think
+            the friend cancelled the transfer.
+        - If the file size was decreased
+            - and sending mode was streaming, the behaviour is as expected.
+            - and sending mode was file, the callback will return 0 at the new (earlier) end-of-file, signalling to the
+            friend that the transfer was cancelled.
+        - If the file contents were modified
+            - at a position before the current read, the two files (local and remote) will differ after the transfer
+            terminates.
+            - at a position after the current read, the file transfer will succeed as expected.
+            - In either case, both sides will regard the transfer as complete and successful.
+
+        :param friend_number: The friend number of the friend the file send request should be sent to.
+        :param kind: The meaning of the file to be sent.
+        :param file_size: Size in bytes of the file the client wants to send, UINT64_MAX if unknown or streaming.
+        :param file_id: A file identifier of length TOX_FILE_ID_LENGTH that can be used to uniquely identify file
+        transfers across core restarts. If NULL, a random one will be generated by core. It can then be obtained by
+        using tox_file_get_file_id().
+        :param filename: Name of the file. Does not need to be the actual name. This name will be sent along with the
+        file send request.
+        :return: A file number used as an identifier in subsequent callbacks. This number is per friend. File numbers
+        are reused after a transfer terminates. On failure, this function returns UINT32_MAX. Any pattern in file
+        numbers should not be relied on.
+        """
+        tox_err_file_send = c_int()
+        result = self.libtoxcore.tox_file_send(self._tox_pointer, c_uint32(friend_number), c_uint32(kind),
+                                               c_uint64(file_size),
+                                               string_to_bin(file_id),
+                                               c_char_p(filename),
+                                               c_size_t(len(filename)), byref(tox_err_file_send))
+        tox_err_file_send = tox_err_file_send.value
+        if tox_err_file_send == TOX_ERR_FILE_SEND['OK']:
+            return result
+        elif tox_err_file_send == TOX_ERR_FILE_SEND['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number passed did not designate a valid friend.')
+        elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_CONNECTED']:
+            raise ArgumentError('This client is currently not connected to the friend.')
+        elif tox_err_file_send == TOX_ERR_FILE_SEND['NAME_TOO_LONG']:
+            raise ArgumentError('Filename length exceeded TOX_MAX_FILENAME_LENGTH bytes.')
+        elif tox_err_file_send == TOX_ERR_FILE_SEND['TOO_MANY']:
+            raise RuntimeError('Too many ongoing transfers. The maximum number of concurrent file transfers is 256 per'
+                               'friend per direction (sending and receiving).')
+
+    def file_send_chunk(self, friend_number, file_number, position, data):
+        """
+        Send a chunk of file data to a friend.
+
+        This function is called in response to the `file_chunk_request` callback. The length parameter should be equal
+        to the one received though the callback. If it is zero, the transfer is assumed complete. For files with known
+        size, Core will know that the transfer is complete after the last byte has been received, so it is not necessary
+        (though not harmful) to send a zero-length chunk to terminate. For streams, core will know that the transfer is
+        finished if a chunk with length less than the length requested in the callback is sent.
+
+        :param friend_number: The friend number of the receiving friend for this file.
+        :param file_number: The file transfer identifier returned by tox_file_send.
+        :param position: The file or stream position from which to continue reading.
+        :param data: Chunk of file data
+        :return: true on success.
+        """
+        tox_err_file_send_chunk = c_int()
+        result = self.libtoxcore.tox_file_send_chunk(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number),
+                                                     c_uint64(position), c_char_p(data), c_size_t(len(data)),
+                                                     byref(tox_err_file_send_chunk))
+        tox_err_file_send_chunk = tox_err_file_send_chunk.value
+        if tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['OK']:
+            return bool(result)
+        elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NULL']:
+            raise ArgumentError('The length parameter was non-zero, but data was NULL.')
+        elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['FRIEND_NOT_FOUND']:
+            ArgumentError('The friend_number passed did not designate a valid friend.')
+        elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['FRIEND_NOT_CONNECTED']:
+            raise ArgumentError('This client is currently not connected to the friend.')
+        elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NOT_FOUND']:
+            raise ArgumentError('No file transfer with the given file number was found for the given friend.')
+        elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NOT_TRANSFERRING']:
+            raise ArgumentError('File transfer was found but isn\'t in a transferring state: (paused, done, broken, '
+                                'etc...) (happens only when not called from the request chunk callback).')
+        elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['INVALID_LENGTH']:
+            raise ArgumentError('Attempted to send more or less data than requested. The requested data size is '
+                                'adjusted according to maximum transmission unit and the expected end of the file. '
+                                'Trying to send less or more than requested will return this error.')
+        elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['SENDQ']:
+            raise RuntimeError('Packet queue is full.')
+        elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['WRONG_POSITION']:
+            raise ArgumentError('Position parameter was wrong.')
+
+    def callback_file_chunk_request(self, callback, user_data):
+        """
+        Set the callback for the `file_chunk_request` event. Pass None to unset.
+
+        This event is triggered when Core is ready to send more file data.
+
+        :param callback: Python function.
+        If the length parameter is 0, the file transfer is finished, and the client's resources associated with the file
+        number should be released. After a call with zero length, the file number can be reused for future file
+        transfers.
+
+        If the requested position is not equal to the client's idea of the current file or stream position, it will need
+        to seek. In case of read-once streams, the client should keep the last read chunk so that a seek back can be
+        supported. A seek-back only ever needs to read from the last requested chunk. This happens when a chunk was
+        requested, but the send failed. A seek-back request can occur an arbitrary number of times for any given chunk.
+
+        In response to receiving this callback, the client should call the function `tox_file_send_chunk` with the
+        requested chunk. If the number of bytes sent through that function is zero, the file transfer is assumed
+        complete. A client must send the full length of data requested with this callback.
+
+        Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the receiving friend for this file.
+        The file transfer identifier (c_uint32) returned by tox_file_send.
+        The file or stream position (c_uint64) from which to continue reading.
+        The number of bytes (c_size_t) requested for the current chunk.
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, c_size_t, c_void_p)
+        self.file_chunk_request_cb = c_callback(callback)
+        self.libtoxcore.tox_callback_file_chunk_request(self._tox_pointer, self.file_chunk_request_cb, user_data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # File transmission: receiving
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def callback_file_recv(self, callback, user_data):
+        """
+        Set the callback for the `file_recv` event. Pass None to unset.
+
+        This event is triggered when a file transfer request is received.
+
+        :param callback: Python function.
+        The client should acquire resources to be associated with the file transfer. Incoming file transfers start in
+        the PAUSED state. After this callback returns, a transfer can be rejected by sending a TOX_FILE_CONTROL_CANCEL
+        control command before any other control commands. It can be accepted by sending TOX_FILE_CONTROL_RESUME.
+
+        Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend who is sending the file transfer request.
+        The friend-specific file number (c_uint32) the data received is associated with.
+        The meaning of the file (c_uint32) to be sent.
+        Size in bytes (c_uint64) of the file the client wants to send, UINT64_MAX if unknown or streaming.
+        Name of the file (c_char_p). Does not need to be the actual name. This name will be sent along with the file
+        send request.
+        Size in bytes (c_size_t) of the filename.
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t, c_void_p)
+        self.file_recv_cb = c_callback(callback)
+        self.libtoxcore.tox_callback_file_recv(self._tox_pointer, self.file_recv_cb, user_data)
+
+    def callback_file_recv_chunk(self, callback, user_data):
+        """
+        Set the callback for the `file_recv_chunk` event. Pass NULL to unset.
+
+        This event is first triggered when a file transfer request is received, and subsequently when a chunk of file
+        data for an accepted request was received.
+
+        :param callback: Python function.
+        When length is 0, the transfer is finished and the client should release the resources it acquired for the
+        transfer. After a call with length = 0, the file number can be reused for new file transfers.
+
+        If position is equal to file_size (received in the file_receive callback) when the transfer finishes, the file
+        was received completely. Otherwise, if file_size was UINT64_MAX, streaming ended successfully when length is 0.
+
+        Should take pointer (c_void_p) to Tox object,
+        The friend number (c_uint32) of the friend who is sending the file.
+        The friend-specific file number (c_uint32) the data received is associated with.
+        The file position (c_uint64) of the first byte in data.
+        A byte array (c_char_p) containing the received chunk.
+        The length (c_size_t) of the received chunk.
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, POINTER(c_uint8), c_size_t, c_void_p)
+        self.file_recv_chunk_cb = c_callback(callback)
+        self.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, self.file_recv_chunk_cb, user_data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Low-level custom packet sending and receiving
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def friend_send_lossy_packet(self, friend_number, data):
+        """
+        Send a custom lossy packet to a friend.
+        The first byte of data must be in the range 200-254. Maximum length of a
+        custom packet is TOX_MAX_CUSTOM_PACKET_SIZE.
+
+        Lossy packets behave like UDP packets, meaning they might never reach the
+        other side or might arrive more than once (if someone is messing with the
+        connection) or might arrive in the wrong order.
+
+        Unless latency is an issue, it is recommended that you use lossless custom packets instead.
+
+        :param friend_number: The friend number of the friend this lossy packet
+        :param data: python string containing the packet data
+        :return: True on success.
+        """
+        tox_err_friend_custom_packet = c_int()
+        result = self.libtoxcore.tox_friend_send_lossy_packet(self._tox_pointer, c_uint32(friend_number),
+                                                              c_char_p(data), c_size_t(len(data)),
+                                                              byref(tox_err_friend_custom_packet))
+        tox_err_friend_custom_packet = tox_err_friend_custom_packet.value
+        if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']:
+            return bool(result)
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend number did not designate a valid friend.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']:
+            raise ArgumentError('This client is currently not connected to the friend.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']:
+            raise ArgumentError('The first byte of data was not in the specified range for the packet type.'
+                                'This range is 200-254 for lossy, and 160-191 for lossless packets.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']:
+            raise ArgumentError('Attempted to send an empty packet.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']:
+            raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']:
+            raise RuntimeError('Packet queue is full.')
+
+    def friend_send_lossless_packet(self, friend_number, data):
+        """
+        Send a custom lossless packet to a friend.
+        The first byte of data must be in the range 160-191. Maximum length of a
+        custom packet is TOX_MAX_CUSTOM_PACKET_SIZE.
+
+        Lossless packet behaviour is comparable to TCP (reliability, arrive in order)
+        but with packets instead of a stream.
+
+        :param friend_number: The friend number of the friend this lossless packet
+        :param data: python string containing the packet data
+        :return: True on success.
+        """
+        tox_err_friend_custom_packet = c_int()
+        result = self.libtoxcore.tox_friend_send_lossless_packet(self._tox_pointer, c_uint32(friend_number),
+                                                                 c_char_p(data), c_size_t(len(data)),
+                                                                 byref(tox_err_friend_custom_packet))
+        tox_err_friend_custom_packet = tox_err_friend_custom_packet.value
+        if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']:
+            return bool(result)
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend number did not designate a valid friend.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']:
+            raise ArgumentError('This client is currently not connected to the friend.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']:
+            raise ArgumentError('The first byte of data was not in the specified range for the packet type.'
+                                'This range is 200-254 for lossy, and 160-191 for lossless packets.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']:
+            raise ArgumentError('Attempted to send an empty packet.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']:
+            raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.')
+        elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']:
+            raise RuntimeError('Packet queue is full.')
+
+    def callback_friend_lossy_packet(self, callback, user_data):
+        """
+        Set the callback for the `friend_lossy_packet` event. Pass NULL to unset.
+
+        :param callback: Python function.
+        Should take pointer (c_void_p) to Tox object,
+        friend_number (c_uint32) - The friend number of the friend who sent a lossy packet,
+        A byte array (c_uint8 array) containing the received packet data,
+        length (c_size_t) - The length of the packet data byte array,
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p)
+        self.friend_lossy_packet_cb = c_callback(callback)
+        self.libtoxcore.tox_callback_friend_lossy_packet(self._tox_pointer, self.friend_lossy_packet_cb, user_data)
+
+    def callback_friend_lossless_packet(self, callback, user_data):
+        """
+        Set the callback for the `friend_lossless_packet` event. Pass NULL to unset.
+
+        :param callback: Python function.
+        Should take pointer (c_void_p) to Tox object,
+        friend_number (c_uint32) - The friend number of the friend who sent a lossless packet,
+        A byte array (c_uint8 array) containing the received packet data,
+        length (c_size_t) - The length of the packet data byte array,
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p)
+        self.friend_lossless_packet_cb = c_callback(callback)
+        self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, self.friend_lossless_packet_cb,
+                                                            user_data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Low-level network information
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def self_get_dht_id(self, dht_id=None):
+        """
+        Writes the temporary DHT public key of this instance to a byte array.
+
+        This can be used in combination with an externally accessible IP address and the bound port (from
+        tox_self_get_udp_port) to run a temporary bootstrap node.
+
+        Be aware that every time a new instance is created, the DHT public key changes, meaning this cannot be used to
+        run a permanent bootstrap node.
+
+        :param dht_id: pointer (c_char_p) to a memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this parameter is
+        None, this function allocates memory for dht_id.
+        :return: dht_id
+        """
+        if dht_id is None:
+            dht_id = create_string_buffer(TOX_PUBLIC_KEY_SIZE)
+        Tox.libtoxcore.tox_self_get_dht_id(self._tox_pointer, dht_id)
+        return bin_to_string(dht_id, TOX_PUBLIC_KEY_SIZE)
+
+    def self_get_udp_port(self):
+        """
+        Return the UDP port this Tox instance is bound to.
+        """
+        tox_err_get_port = c_int()
+        result = Tox.libtoxcore.tox_self_get_udp_port(self._tox_pointer, byref(tox_err_get_port))
+        tox_err_get_port = tox_err_get_port.value
+        if tox_err_get_port == TOX_ERR_GET_PORT['OK']:
+            return result
+        elif tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']:
+            raise RuntimeError('The instance was not bound to any port.')
+
+    def self_get_tcp_port(self):
+        """
+        Return the TCP port this Tox instance is bound to. This is only relevant if the instance is acting as a TCP
+        relay.
+        """
+        tox_err_get_port = c_int()
+        result = Tox.libtoxcore.tox_self_get_tcp_port(self._tox_pointer, byref(tox_err_get_port))
+        tox_err_get_port = tox_err_get_port.value
+        if tox_err_get_port == TOX_ERR_GET_PORT['OK']:
+            return result
+        elif tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']:
+            raise RuntimeError('The instance was not bound to any port.')
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group chat instance management
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def group_new(self, privacy_state, group_name):
+        """
+        Creates a new group chat.
+
+        This function creates a new group chat object and adds it to the chats array.
+
+        The client should initiate its peer list with self info after calling this function, as
+        the peer_join callback will not be triggered.
+
+        :param privacy_state: The privacy state of the group. If this is set to TOX_GROUP_PRIVACY_STATE_PUBLIC,
+        the group will attempt to announce itself to the DHT and anyone with the Chat ID may join.
+        Otherwise a friend invite will be required to join the group.
+        :param group_name: The name of the group. The name must be non-NULL.
+
+        :return group number on success, UINT32_MAX on failure.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_new(self._tox_pointer, privacy_state, group_name,
+                      len(group_name), byref(error))
+        return result
+
+    def group_join(self, chat_id, password):
+        """
+        Joins a group chat with specified Chat ID.
+
+        This function creates a new group chat object, adds it to the chats array, and sends
+        a DHT announcement to find peers in the group associated with chat_id. Once a peer has been
+        found a join attempt will be initiated.
+
+        :param chat_id: The Chat ID of the group you wish to join. This must be TOX_GROUP_CHAT_ID_SIZE bytes.
+        :param password: The password required to join the group. Set to NULL if no password is required.
+
+        :return groupnumber on success, UINT32_MAX on failure.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_join(self._tox_pointer, string_to_bin(chat_id),
+                                               password,
+                                               len(password) if password is not None else 0,
+                                               byref(error))
+        return result
+
+    def group_reconnect(self, groupnumber):
+        """
+        Reconnects to a group.
+
+        This function disconnects from all peers in the group, then attempts to reconnect with the group.
+        The caller's state is not changed (i.e. name, status, role, chat public key etc.)
+
+        :param groupnumber: The group number of the group we wish to reconnect to.
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_reconnect(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_leave(self, groupnumber, message):
+        """
+        Leaves a group.
+
+        This function sends a parting packet containing a custom (non-obligatory) message to all
+        peers in a group, and deletes the group from the chat array. All group state information is permanently
+        lost, including keys and role credentials.
+
+        :param groupnumber: The group number of the group we wish to leave.
+        :param message: The parting message to be sent to all the peers. Set to NULL if we do not wish to
+        send a parting message.
+
+        :return True if the group chat instance was successfully deleted.
+        """
+
+        error = c_int()
+        f = Tox.libtoxcore.tox_group_leave
+        f.restype = c_bool
+        result = f(self._tox_pointer, groupnumber, message,
+                   len(message) if message is not None else 0, byref(error))
+        return result
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group user-visible client information (nickname/status/role/public key)
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def group_self_set_name(self, groupnumber, name):
+        """
+        Set the client's nickname for the group instance designated by the given group number.
+
+        Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length is equal to zero or name is a NULL
+        pointer, the function call will fail.
+
+        :param name: A byte array containing the new nickname.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_self_set_name(self._tox_pointer, groupnumber, name, len(name), byref(error))
+        return result
+
+    def group_self_get_name_size(self, groupnumber):
+        """
+        Return the length of the client's current nickname for the group instance designated
+        by groupnumber as passed to tox_group_self_set_name.
+
+        If no nickname was set before calling this function, the name is empty,
+        and this function returns 0.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_self_get_name_size(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_self_get_name(self, groupnumber):
+        """
+        Write the nickname set by tox_group_self_set_name to a byte array.
+
+        If no nickname was set before calling this function, the name is empty,
+        and this function has no effect.
+
+        Call tox_group_self_get_name_size to find out how much memory to allocate for the result.
+        :return nickname
+        """
+
+        error = c_int()
+        size = self.group_self_get_name_size(groupnumber)
+        name = create_string_buffer(size)
+        result = Tox.libtoxcore.tox_group_self_get_name(self._tox_pointer, groupnumber, name, byref(error))
+        return str(name[:size], 'utf-8')
+
+    def group_self_set_status(self, groupnumber, status):
+
+        """
+        Set the client's status for the group instance. Status must be a TOX_USER_STATUS.
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_self_set_status(self._tox_pointer, groupnumber, status, byref(error))
+        return result
+
+    def group_self_get_status(self, groupnumber):
+        """
+        returns the client's status for the group instance on success.
+        return value is unspecified on failure.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_self_get_status(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_self_get_role(self, groupnumber):
+        """
+        returns the client's role for the group instance on success.
+        return value is unspecified on failure.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_self_get_role(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_self_get_peer_id(self, groupnumber):
+        """
+        returns the client's peer id for the group instance on success.
+        return value is unspecified on failure.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_self_get_peer_id(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_self_get_public_key(self, groupnumber):
+        """
+        Write the client's group public key designated by the given group number to a byte array.
+
+        This key will be permanently tied to the client's identity for this particular group until
+        the client explicitly leaves the group or gets kicked/banned. This key is the only way for
+        other peers to reliably identify the client across client restarts.
+
+        `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes.
+
+        :return public key
+        """
+
+        error = c_int()
+        key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE)
+        result = Tox.libtoxcore.tox_group_self_get_public_key(self._tox_pointer, groupnumber,
+                                                              key, byref(error))
+        return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Peer-specific group state queries.
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def group_peer_get_name_size(self, groupnumber, peer_id):
+        """
+        Return the length of the peer's name. If the group number or ID is invalid, the
+        return value is unspecified.
+
+        The return value is equal to the `length` argument received by the last
+        `group_peer_name` callback.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_peer_get_name_size(self._tox_pointer, groupnumber, peer_id, byref(error))
+        return result
+
+    def group_peer_get_name(self, groupnumber, peer_id):
+        """
+        Write the name of the peer designated by the given ID to a byte
+        array.
+
+        Call tox_group_peer_get_name_size to determine the allocation size for the `name` parameter.
+
+        The data written to `name` is equal to the data received by the last
+        `group_peer_name` callback.
+
+        :param groupnumber: The group number of the group we wish to query.
+        :param peer_id: The ID of the peer whose name we want to retrieve.
+
+        :return name.
+        """
+        error = c_int()
+        size = self.group_peer_get_name_size(groupnumber, peer_id)
+        name = create_string_buffer(size)
+        result = Tox.libtoxcore.tox_group_peer_get_name(self._tox_pointer, groupnumber, peer_id, name, byref(error))
+        return str(name[:], 'utf-8')
+
+    def group_peer_get_status(self, groupnumber, peer_id):
+        """
+        Return the peer's user status (away/busy/...). If the ID or group number is
+        invalid, the return value is unspecified.
+
+        The status returned is equal to the last status received through the
+        `group_peer_status` callback.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_peer_get_status(self._tox_pointer, groupnumber, peer_id, byref(error))
+        return result
+
+    def group_peer_get_role(self, groupnumber, peer_id):
+        """
+        Return the peer's role (user/moderator/founder...). If the ID or group number is
+        invalid, the return value is unspecified.
+
+        The role returned is equal to the last role received through the
+        `group_moderation` callback.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_peer_get_role(self._tox_pointer, groupnumber, peer_id, byref(error))
+        return result
+
+    def group_peer_get_public_key(self, groupnumber, peer_id):
+        """
+        Write the group public key with the designated peer_id for the designated group number to public_key.
+
+        This key will be parmanently tied to a particular peer until they explicitly leave the group or
+        get kicked/banned, and is the only way to reliably identify the same peer across client restarts.
+
+        `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes.
+
+        :return public key
+        """
+
+        error = c_int()
+        key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE)
+        result = Tox.libtoxcore.tox_group_peer_get_public_key(self._tox_pointer, groupnumber, peer_id,
+                                                              key, byref(error))
+        return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE)
+
+    def callback_group_peer_name(self, callback, user_data):
+        """
+        Set the callback for the `group_peer_name` event. Pass NULL to unset.
+        This event is triggered when a peer changes their nickname.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p)
+        self.group_peer_name_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_peer_name(self._tox_pointer, self.group_peer_name_cb, user_data)
+
+    def callback_group_peer_status(self, callback, user_data):
+        """
+        Set the callback for the `group_peer_status` event. Pass NULL to unset.
+        This event is triggered when a peer changes their status.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p)
+        self.group_peer_status_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_peer_status(self._tox_pointer, self.group_peer_status_cb, user_data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group chat state queries and events.
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def group_set_topic(self, groupnumber, topic):
+        """
+        Set the group topic and broadcast it to the rest of the group.
+
+        topic length cannot be longer than TOX_GROUP_MAX_TOPIC_LENGTH. If length is equal to zero or
+        topic is set to NULL, the topic will be unset.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_set_topic(self._tox_pointer, groupnumber, topic, len(topic), byref(error))
+        return result
+
+    def group_get_topic_size(self, groupnumber):
+        """
+        Return the length of the group topic. If the group number is invalid, the
+        return value is unspecified.
+
+        The return value is equal to the `length` argument received by the last
+        `group_topic` callback.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_get_topic_size(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_get_topic(self, groupnumber):
+        """
+        Write the topic designated by the given group number to a byte array.
+        Call tox_group_get_topic_size to determine the allocation size for the `topic` parameter.
+        The data written to `topic` is equal to the data received by the last
+        `group_topic` callback.
+
+        :return topic
+        """
+
+        error = c_int()
+        size = self.group_get_topic_size(groupnumber)
+        topic = create_string_buffer(size)
+        result = Tox.libtoxcore.tox_group_get_topic(self._tox_pointer, groupnumber, topic, byref(error))
+        return str(topic[:size], 'utf-8')
+
+    def group_get_name_size(self, groupnumber):
+        """
+        Return the length of the group name. If the group number is invalid, the
+        return value is unspecified.
+        """
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_get_name_size(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_get_name(self, groupnumber):
+        """
+        Write the name of the group designated by the given group number to a byte array.
+        Call tox_group_get_name_size to determine the allocation size for the `name` parameter.
+        :return true on success.
+        """
+
+        error = c_int()
+        size = self.group_get_name_size(groupnumber)
+        name = create_string_buffer(size)
+        result = Tox.libtoxcore.tox_group_get_name(self._tox_pointer, groupnumber,
+                                                   name, byref(error))
+        return str(name[:size], 'utf-8')
+
+    def group_get_chat_id(self, groupnumber):
+        """
+        Write the Chat ID designated by the given group number to a byte array.
+        `chat_id` should have room for at least TOX_GROUP_CHAT_ID_SIZE bytes.
+        :return chat id.
+        """
+
+        error = c_int()
+        buff = create_string_buffer(TOX_GROUP_CHAT_ID_SIZE)
+        result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer, groupnumber,
+                                                      buff, byref(error))
+        return bin_to_string(buff, TOX_GROUP_CHAT_ID_SIZE)
+
+    def group_get_number_groups(self):
+        """
+        Return the number of groups in the Tox chats array.
+        """
+
+        result = Tox.libtoxcore.tox_group_get_number_groups(self._tox_pointer)
+        return result
+
+    def group_get_privacy_state(self, groupnumber):
+        """
+        Return the privacy state of the group designated by the given group number. If group number
+        is invalid, the return value is unspecified.
+
+        The value returned is equal to the data received by the last
+        `group_privacy_state` callback.
+
+        see the `Group chat founder controls` section for the respective set function.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_get_privacy_state(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_get_peer_limit(self, groupnumber):
+        """
+        Return the maximum number of peers allowed for the group designated by the given group number.
+        If the group number is invalid, the return value is unspecified.
+
+        The value returned is equal to the data received by the last
+        `group_peer_limit` callback.
+
+        see the `Group chat founder controls` section for the respective set function.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_get_peer_limit(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_get_password_size(self, groupnumber):
+        """
+        Return the length of the group password. If the group number is invalid, the
+        return value is unspecified.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_get_password_size(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_get_password(self, groupnumber):
+        """
+        Write the password for the group designated by the given group number to a byte array.
+
+        Call tox_group_get_password_size to determine the allocation size for the `password` parameter.
+
+        The data received is equal to the data received by the last
+        `group_password` callback.
+
+        see the `Group chat founder controls` section for the respective set function.
+
+        :return password
+        """
+
+        error = c_int()
+        size = self.group_get_password_size(groupnumber)
+        password = create_string_buffer(size)
+        result = Tox.libtoxcore.tox_group_get_password(self._tox_pointer, groupnumber,
+                                                       password, byref(error))
+        return str(password[:size], 'utf-8')
+
+    def callback_group_topic(self, callback, user_data):
+        """
+        Set the callback for the `group_topic` event. Pass NULL to unset.
+        This event is triggered when a peer changes the group topic.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p)
+        self.group_topic_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_topic(self._tox_pointer, self.group_topic_cb, user_data)
+
+    def callback_group_privacy_state(self, callback, user_data):
+        """
+        Set the callback for the `group_privacy_state` event. Pass NULL to unset.
+        This event is triggered when the group founder changes the privacy state.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p)
+        self.group_privacy_state_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_privacy_state(self._tox_pointer, self.group_privacy_state_cb, user_data)
+
+    def callback_group_peer_limit(self, callback, user_data):
+        """
+        Set the callback for the `group_peer_limit` event. Pass NULL to unset.
+        This event is triggered when the group founder changes the maximum peer limit.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p)
+        self.group_peer_limit_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_peer_limit(self._tox_pointer, self.group_peer_limit_cb, user_data)
+
+    def callback_group_password(self, callback, user_data):
+        """
+        Set the callback for the `group_password` event. Pass NULL to unset.
+        This event is triggered when the group founder changes the group password.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p)
+        self.group_password_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_password(self._tox_pointer, self.group_password_cb, user_data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group message sending
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def group_send_custom_packet(self, groupnumber, lossless, data):
+        """
+        Send a custom packet to the group.
+
+        If lossless is true the packet will be lossless. Lossless packet behaviour is comparable
+        to TCP (reliability, arrive in order) but with packets instead of a stream.
+
+        If lossless is false, the packet will be lossy. Lossy packets behave like UDP packets,
+        meaning they might never reach the other side or might arrive more than once (if someone
+        is messing with the connection) or might arrive in the wrong order.
+
+        Unless latency is an issue or message reliability is not important, it is recommended that you use
+        lossless custom packets.
+
+        :param groupnumber: The group number of the group the message is intended for.
+        :param lossless: True if the packet should be lossless.
+        :param data A byte array containing the packet data.
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_send_custom_packet(self._tox_pointer, groupnumber, lossless, data,
+                                                             len(data), byref(error))
+        return result
+
+    def group_send_private_message(self, groupnumber, peer_id, message):
+        """
+        Send a text chat message to the specified peer in the specified group.
+
+        This function creates a group private message packet and pushes it into the send
+        queue.
+
+        The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages
+        must be split by the client and sent as separate messages. Other clients can
+        then reassemble the fragments. Messages may not be empty.
+
+        :param groupnumber: The group number of the group the message is intended for.
+        :param peer_id: The ID of the peer the message is intended for.
+        :param message: A non-NULL pointer to the first element of a byte array containing the message text.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_send_private_message(self._tox_pointer, groupnumber, peer_id, message,
+                                                               len(message), byref(error))
+        return result
+
+    def group_send_message(self, groupnumber, type, message):
+        """
+        Send a text chat message to the group.
+
+        This function creates a group message packet and pushes it into the send
+        queue.
+
+        The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages
+        must be split by the client and sent as separate messages. Other clients can
+        then reassemble the fragments. Messages may not be empty.
+
+        :param groupnumber: The group number of the group the message is intended for.
+        :param type: Message type (normal, action, ...).
+        :param message: A non-NULL pointer to the first element of a byte array containing the message text.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_send_message(self._tox_pointer, groupnumber, type, message, len(message),
+                                                       byref(error))
+        return result
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group message receiving
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def callback_group_message(self, callback, user_data):
+        """
+        Set the callback for the `group_message` event. Pass NULL to unset.
+        This event is triggered when the client receives a group message.
+
+        Callback: python function with params:
+        tox Tox* instance
+        groupnumber The group number of the group the message is intended for.
+        peer_id The ID of the peer who sent the message.
+        type The type of message (normal, action, ...).
+        message The message data.
+        length The length of the message.
+        user_data - user data
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_char_p, c_size_t, c_void_p)
+        self.group_message_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_message(self._tox_pointer, self.group_message_cb, user_data)
+
+    def callback_group_private_message(self, callback, user_data):
+        """
+        Set the callback for the `group_private_message` event. Pass NULL to unset.
+        This event is triggered when the client receives a private message.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p)
+        self.group_private_message_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_private_message(self._tox_pointer, self.group_private_message_cb, user_data)
+
+    def callback_group_custom_packet(self, callback, user_data):
+        """
+        Set the callback for the `group_custom_packet` event. Pass NULL to unset.
+
+        This event is triggered when the client receives a custom packet.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, POINTER(c_uint8), c_void_p)
+        self.group_custom_packet_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_custom_packet(self._tox_pointer, self.group_custom_packet_cb, user_data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group chat inviting and join/part events
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def group_invite_friend(self, groupnumber, friend_number):
+        """
+        Invite a friend to a group.
+
+        This function creates an invite request packet and pushes it to the send queue.
+
+        :param groupnumber: The group number of the group the message is intended for.
+        :param friend_number: The friend number of the friend the invite is intended for.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, groupnumber, friend_number, byref(error))
+        return result
+
+    def group_invite_accept(self, invite_data, friend_number, password=None):
+        """
+        Accept an invite to a group chat that the client previously received from a friend. The invite
+        is only valid while the inviter is present in the group.
+
+        :param invite_data: The invite data received from the `group_invite` event.
+        :param password: The password required to join the group. Set to NULL if no password is required.
+        :return the groupnumber on success, UINT32_MAX on failure.
+        """
+
+        error = c_int()
+        f = Tox.libtoxcore.tox_group_invite_accept
+        f.restype = c_uint32
+        result = f(self._tox_pointer, friend_number, invite_data, len(invite_data), password,
+                   len(password) if password is not None else 0, byref(error))
+        print('Invite accept. Result:', result, 'Error:', error.value)
+        return result
+
+    def callback_group_invite(self, callback, user_data):
+        """
+        Set the callback for the `group_invite` event. Pass NULL to unset.
+
+        This event is triggered when the client receives a group invite from a friend. The client must store
+        invite_data which is used to join the group via tox_group_invite_accept.
+
+        Callback: python function with params:
+        tox - Tox*
+        friend_number The friend number of the contact who sent the invite.
+        invite_data The invite data.
+        length The length of invite_data.
+        user_data - user data
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p)
+        self.group_invite_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, self.group_invite_cb, user_data)
+
+    def callback_group_peer_join(self, callback, user_data):
+        """
+        Set the callback for the `group_peer_join` event. Pass NULL to unset.
+
+        This event is triggered when a peer other than self joins the group.
+        Callback: python function with params:
+        tox - Tox*
+        group_number - group number
+        peer_id - peer id
+        user_data - user data
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p)
+        self.group_peer_join_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_peer_join(self._tox_pointer, self.group_peer_join_cb, user_data)
+
+    def callback_group_peer_exit(self, callback, user_data):
+        """
+        Set the callback for the `group_peer_exit` event. Pass NULL to unset.
+
+        This event is triggered when a peer other than self exits the group.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p)
+        self.group_peer_exit_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_peer_exit(self._tox_pointer, self.group_peer_exit_cb, user_data)
+
+    def callback_group_self_join(self, callback, user_data):
+        """
+        Set the callback for the `group_self_join` event. Pass NULL to unset.
+
+        This event is triggered when the client has successfully joined a group. Use this to initialize
+        any group information the client may need.
+        Callback: python fucntion with params:
+        tox - *Tox
+        group_number - group number
+        user_data - user data
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_void_p)
+        self.group_self_join_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_self_join(self._tox_pointer, self.group_self_join_cb, user_data)
+
+    def callback_group_join_fail(self, callback, user_data):
+        """
+        Set the callback for the `group_join_fail` event. Pass NULL to unset.
+
+        This event is triggered when the client fails to join a group.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p)
+        self.group_join_fail_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_join_fail(self._tox_pointer, self.group_join_fail_cb, user_data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group chat founder controls (these only work for the group founder)
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def group_founder_set_password(self, groupnumber, password):
+        """
+        Set or unset the group password.
+
+        This function sets the groups password, creates a new group shared state including the change,
+        and distributes it to the rest of the group.
+
+        :param groupnumber: The group number of the group for which we wish to set the password.
+        :param password: The password we want to set. Set password to NULL to unset the password.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_founder_set_password(self._tox_pointer, groupnumber, password,
+                                                               len(password), byref(error))
+        return result
+
+    def group_founder_set_privacy_state(self, groupnumber, privacy_state):
+        """
+        Set the group privacy state.
+
+        This function sets the group's privacy state, creates a new group shared state
+        including the change, and distributes it to the rest of the group.
+
+        If an attempt is made to set the privacy state to the same state that the group is already
+        in, the function call will be successful and no action will be taken.
+
+        :param groupnumber: The group number of the group for which we wish to change the privacy state.
+        :param privacy_state: The privacy state we wish to set the group to.
+
+        :return true on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_founder_set_privacy_state(self._tox_pointer, groupnumber, privacy_state,
+                                                                    byref(error))
+        return result
+
+    def group_founder_set_peer_limit(self, groupnumber, max_peers):
+        """
+        Set the group peer limit.
+
+        This function sets a limit for the number of peers who may be in the group, creates a new
+        group shared state including the change, and distributes it to the rest of the group.
+
+        :param groupnumber: The group number of the group for which we wish to set the peer limit.
+        :param max_peers: The maximum number of peers to allow in the group.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_founder_set_peer_limit(self._tox_pointer, groupnumber,
+                                                                 max_peers, byref(error))
+        return result
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group chat moderation
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def group_toggle_ignore(self, groupnumber, peer_id, ignore):
+        """
+        Ignore or unignore a peer.
+
+        :param groupnumber: The group number of the group the in which you wish to ignore a peer.
+        :param peer_id: The ID of the peer who shall be ignored or unignored.
+        :param ignore: True to ignore the peer, false to unignore the peer.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_toggle_ignore(self._tox_pointer, groupnumber, peer_id, ignore, byref(error))
+        return result
+
+    def group_mod_set_role(self, groupnumber, peer_id, role):
+        """
+        Set a peer's role.
+
+        This function will first remove the peer's previous role and then assign them a new role.
+        It will also send a packet to the rest of the group, requesting that they perform
+        the role reassignment. Note: peers cannot be set to the founder role.
+
+        :param groupnumber: The group number of the group the in which you wish set the peer's role.
+        :param peer_id: The ID of the peer whose role you wish to set.
+        :param role: The role you wish to set the peer to.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_mod_set_role(self._tox_pointer, groupnumber, peer_id, role, byref(error))
+        return result
+
+    def group_mod_remove_peer(self, groupnumber, peer_id, set_ban):
+        """
+        Kick/ban a peer.
+
+        This function will remove a peer from the caller's peer list and optionally add their IP address
+        to the ban list. It will also send a packet to all group members requesting them
+        to do the same.
+
+        :param groupnumber: The group number of the group the ban is intended for.
+        :param peer_id: The ID of the peer who will be kicked and/or added to the ban list.
+        :param set_ban: Set to true if a ban shall be set on the peer's IP address.
+
+        :return True on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_mod_remove_peer(self._tox_pointer, groupnumber, peer_id,
+                                                          set_ban, byref(error))
+        return result
+
+    def group_mod_remove_ban(self, groupnumber, ban_id):
+        """
+        Removes a ban.
+
+        This function removes a ban entry from the ban list, and sends a packet to the rest of
+        the group requesting that they do the same.
+
+        :param groupnumber: The group number of the group in which the ban is to be removed.
+        :param ban_id: The ID of the ban entry that shall be removed.
+
+        :return True on success
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_mod_remove_ban(self._tox_pointer, groupnumber, ban_id, byref(error))
+        return result
+
+    def callback_group_moderation(self, callback, user_data):
+        """
+        Set the callback for the `group_moderation` event. Pass NULL to unset.
+
+        This event is triggered when a moderator or founder executes a moderation event.
+        """
+
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_int, c_void_p)
+        self.group_moderation_cb = c_callback(callback)
+        Tox.libtoxcore.tox_callback_group_moderation(self._tox_pointer, self.group_moderation_cb, user_data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Group chat ban list queries
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def group_ban_get_list_size(self, groupnumber):
+        """
+        Return the number of entries in the ban list for the group designated by
+        the given group number. If the group number is invalid, the return value is unspecified.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_ban_get_list_size(self._tox_pointer, groupnumber, byref(error))
+        return result
+
+    def group_ban_get_list(self, groupnumber):
+        """
+        Copy a list of valid ban list ID's into an array.
+
+        Call tox_group_ban_get_list_size to determine the number of elements to allocate.
+        return true on success.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_ban_get_list(self._tox_pointer, groupnumber, POINTER(c_uint32)(
+            create_string_buffer(sizeof(c_uint32) * self.group_ban_get_list_size(groupnumber)), byref(error)))
+        return result
+
+    def group_ban_get_name_size(self, groupnumber, ban_id):
+        """
+        Return the length of the name for the ban list entry designated by ban_id, in the
+        group designated by the given group number. If either groupnumber or ban_id is invalid,
+        the return value is unspecified.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_ban_get_name_size(self._tox_pointer, groupnumber, ban_id, byref(error))
+        return result
+
+    def group_ban_get_name(self, groupnumber, ban_id):
+        """
+        Write the name of the ban entry designated by ban_id in the group designated by the
+        given group number to a byte array.
+
+        Call tox_group_ban_get_name_size to find out how much memory to allocate for the result.
+
+        :return name
+        """
+
+        error = c_int()
+        size = self.group_ban_get_name_size(groupnumber, ban_id)
+        name = create_string_buffer()
+
+        result = Tox.libtoxcore.tox_group_ban_get_name(self._tox_pointer, groupnumber, ban_id,
+                                                       name, byref(error))
+        return str(name[:size], 'utf-8')
+
+    def group_ban_get_time_set(self, groupnumber, ban_id):
+        """
+        Return a time stamp indicating the time the ban was set, for the ban list entry
+        designated by ban_id, in the group designated by the given group number.
+        If either groupnumber or ban_id is invalid, the return value is unspecified.
+        """
+
+        error = c_int()
+        result = Tox.libtoxcore.tox_group_ban_get_time_set(self._tox_pointer, groupnumber, ban_id, byref(error))
+        return result
diff --git a/toxygen/tox_dns.py b/toxygen/tox_dns.py
new file mode 100644
index 0000000..ec8582f
--- /dev/null
+++ b/toxygen/tox_dns.py
@@ -0,0 +1,61 @@
+import json
+import urllib.request
+from util import log
+import settings
+try:
+    from PySide import QtNetwork, QtCore
+except:
+    from PyQt4 import QtNetwork, QtCore
+
+
+def tox_dns(email):
+    """
+    TOX DNS 4
+    :param email: data like 'groupbot@toxme.io'
+    :return: tox id on success else None
+    """
+    site = email.split('@')[1]
+    data = {"action": 3, "name": "{}".format(email)}
+    urls = ('https://{}/api'.format(site), 'http://{}/api'.format(site))
+    s = settings.Settings.get_instance()
+    if not s['proxy_type']:  # no proxy
+        for url in urls:
+            try:
+                return send_request(url, data)
+            except Exception as ex:
+                log('TOX DNS ERROR: ' + str(ex))
+    else:  # proxy
+        netman = QtNetwork.QNetworkAccessManager()
+        proxy = QtNetwork.QNetworkProxy()
+        proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy if s['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy)
+        proxy.setHostName(s['proxy_host'])
+        proxy.setPort(s['proxy_port'])
+        netman.setProxy(proxy)
+        for url in urls:
+            try:
+                request = QtNetwork.QNetworkRequest(url)
+                request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, "application/json")
+                reply = netman.post(request, bytes(json.dumps(data), 'utf-8'))
+
+                while not reply.isFinished():
+                    QtCore.QThread.msleep(1)
+                    QtCore.QCoreApplication.processEvents()
+                data = bytes(reply.readAll().data())
+                result = json.loads(str(data, 'utf-8'))
+                if not result['c']:
+                    return result['tox_id']
+            except Exception as ex:
+                log('TOX DNS ERROR: ' + str(ex))
+
+    return None  # error
+
+
+def send_request(url, data):
+    req = urllib.request.Request(url)
+    req.add_header('Content-Type', 'application/json')
+    response = urllib.request.urlopen(req, bytes(json.dumps(data), 'utf-8'))
+    res = json.loads(str(response.read(), 'utf-8'))
+    if not res['c']:
+        return res['tox_id']
+    else:
+        raise LookupError()
diff --git a/toxygen/toxav.py b/toxygen/toxav.py
new file mode 100644
index 0000000..a9f46b3
--- /dev/null
+++ b/toxygen/toxav.py
@@ -0,0 +1,363 @@
+from ctypes import c_int, POINTER, c_void_p, byref, ArgumentError, c_uint32, CFUNCTYPE, c_size_t, c_uint8, c_uint16
+from ctypes import c_char_p, c_int32, c_bool, cast
+from libtox import LibToxAV
+from toxav_enums import *
+
+
+class ToxAV:
+    """
+    The ToxAV instance type. Each ToxAV instance can be bound to only one Tox instance, and Tox instance can have only
+    one ToxAV instance. One must make sure to close ToxAV instance prior closing Tox instance otherwise undefined
+    behaviour occurs. Upon closing of ToxAV instance, all active calls will be forcibly terminated without notifying
+    peers.
+    """
+
+    libtoxav = LibToxAV()
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Creation and destruction
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def __init__(self, tox_pointer):
+        """
+        Start new A/V session. There can only be only one session per Tox instance.
+
+        :param tox_pointer: pointer to Tox instance
+        """
+        toxav_err_new = c_int()
+        ToxAV.libtoxav.toxav_new.restype = POINTER(c_void_p)
+        self._toxav_pointer = ToxAV.libtoxav.toxav_new(tox_pointer, byref(toxav_err_new))
+        toxav_err_new = toxav_err_new.value
+        if toxav_err_new == TOXAV_ERR_NEW['NULL']:
+            raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
+        elif toxav_err_new == TOXAV_ERR_NEW['MALLOC']:
+            raise MemoryError('Memory allocation failure while trying to allocate structures required for the A/V '
+                              'session.')
+        elif toxav_err_new == TOXAV_ERR_NEW['MULTIPLE']:
+            raise RuntimeError('Attempted to create a second session for the same Tox instance.')
+
+        self.call_state_cb = None
+        self.audio_receive_frame_cb = None
+        self.video_receive_frame_cb = None
+        self.call_cb = None
+
+    def __del__(self):
+        """
+        Releases all resources associated with the A/V session.
+
+        If any calls were ongoing, these will be forcibly terminated without notifying peers. After calling this
+        function, no other functions may be called and the av pointer becomes invalid.
+        """
+        ToxAV.libtoxav.toxav_kill(self._toxav_pointer)
+
+    def get_tox_pointer(self):
+        """
+        Returns the Tox instance the A/V object was created for.
+
+        :return: pointer to the Tox instance
+        """
+        ToxAV.libtoxav.toxav_get_tox.restype = POINTER(c_void_p)
+        return ToxAV.libtoxav.toxav_get_tox(self._toxav_pointer)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # A/V event loop
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def iteration_interval(self):
+        """
+        Returns the interval in milliseconds when the next toxav_iterate call should be. If no call is active at the
+        moment, this function returns 200.
+
+        :return: interval in milliseconds
+        """
+        return ToxAV.libtoxav.toxav_iteration_interval(self._toxav_pointer)
+
+    def iterate(self):
+        """
+        Main loop for the session. This function needs to be called in intervals of toxav_iteration_interval()
+        milliseconds. It is best called in the separate thread from tox_iterate.
+        """
+        ToxAV.libtoxav.toxav_iterate(self._toxav_pointer)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Call setup
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def call(self, friend_number, audio_bit_rate, video_bit_rate):
+        """
+        Call a friend. This will start ringing the friend.
+
+        It is the client's responsibility to stop ringing after a certain timeout, if such behaviour is desired. If the
+        client does not stop ringing, the library will not stop until the friend is disconnected. Audio and video
+        receiving are both enabled by default.
+
+        :param friend_number: The friend number of the friend that should be called.
+        :param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending.
+        :param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending.
+        :return: True on success.
+        """
+        toxav_err_call = c_int()
+        result = ToxAV.libtoxav.toxav_call(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate),
+                                           c_uint32(video_bit_rate), byref(toxav_err_call))
+        toxav_err_call = toxav_err_call.value
+        if toxav_err_call == TOXAV_ERR_CALL['OK']:
+            return bool(result)
+        elif toxav_err_call == TOXAV_ERR_CALL['MALLOC']:
+            raise MemoryError('A resource allocation error occurred while trying to create the structures required for '
+                              'the call.')
+        elif toxav_err_call == TOXAV_ERR_CALL['SYNC']:
+            raise RuntimeError('Synchronization error occurred.')
+        elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend number did not designate a valid friend.')
+        elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_CONNECTED']:
+            raise ArgumentError('The friend was valid, but not currently connected.')
+        elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_ALREADY_IN_CALL']:
+            raise ArgumentError('Attempted to call a friend while already in an audio or video call with them.')
+        elif toxav_err_call == TOXAV_ERR_CALL['INVALID_BIT_RATE']:
+            raise ArgumentError('Audio or video bit rate is invalid.')
+
+    def callback_call(self, callback, user_data):
+        """
+        Set the callback for the `call` event. Pass None to unset.
+
+        :param callback: The function for the call callback.
+
+        Should take pointer (c_void_p) to ToxAV object,
+        The friend number (c_uint32) from which the call is incoming.
+        True (c_bool) if friend is sending audio.
+        True (c_bool) if friend is sending video.
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_bool, c_void_p)
+        self.call_cb = c_callback(callback)
+        ToxAV.libtoxav.toxav_callback_call(self._toxav_pointer, self.call_cb, user_data)
+
+    def answer(self, friend_number, audio_bit_rate, video_bit_rate):
+        """
+        Accept an incoming call.
+
+        If answering fails for any reason, the call will still be pending and it is possible to try and answer it later.
+        Audio and video receiving are both enabled by default.
+
+        :param friend_number: The friend number of the friend that is calling.
+        :param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending.
+        :param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending.
+        :return: True on success.
+        """
+        toxav_err_answer = c_int()
+        result = ToxAV.libtoxav.toxav_answer(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate),
+                                             c_uint32(video_bit_rate), byref(toxav_err_answer))
+        toxav_err_answer = toxav_err_answer.value
+        if toxav_err_answer == TOXAV_ERR_ANSWER['OK']:
+            return bool(result)
+        elif toxav_err_answer == TOXAV_ERR_ANSWER['SYNC']:
+            raise RuntimeError('Synchronization error occurred.')
+        elif toxav_err_answer == TOXAV_ERR_ANSWER['CODEC_INITIALIZATION']:
+            raise RuntimeError('Failed to initialize codecs for call session. Note that codec initiation will fail if '
+                               'there is no receive callback registered for either audio or video.')
+        elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend number did not designate a valid friend.')
+        elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_CALLING']:
+            raise ArgumentError('The friend was valid, but they are not currently trying to initiate a call. This is '
+                                'also returned if this client is already in a call with the friend.')
+        elif toxav_err_answer == TOXAV_ERR_ANSWER['INVALID_BIT_RATE']:
+            raise ArgumentError('Audio or video bit rate is invalid.')
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Call state graph
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def callback_call_state(self, callback, user_data):
+        """
+        Set the callback for the `call_state` event. Pass None to unset.
+
+        :param callback: Python function.
+        The function for the call_state callback.
+
+        Should take pointer (c_void_p) to ToxAV object,
+        The friend number (c_uint32) for which the call state changed.
+        The bitmask of the new call state which is guaranteed to be different than the previous state. The state is set
+        to 0 when the call is paused. The bitmask represents all the activities currently performed by the friend.
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p)
+        self.call_state_cb = c_callback(callback)
+        ToxAV.libtoxav.toxav_callback_call_state(self._toxav_pointer, self.call_state_cb, user_data)
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # Call control
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def call_control(self, friend_number, control):
+        """
+        Sends a call control command to a friend.
+
+        :param friend_number: The friend number of the friend this client is in a call with.
+        :param control: The control command to send.
+        :return: True on success.
+        """
+        toxav_err_call_control = c_int()
+        result = ToxAV.libtoxav.toxav_call_control(self._toxav_pointer, c_uint32(friend_number), c_int(control),
+                                                   byref(toxav_err_call_control))
+        toxav_err_call_control = toxav_err_call_control.value
+        if toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['OK']:
+            return bool(result)
+        elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['SYNC']:
+            raise RuntimeError('Synchronization error occurred.')
+        elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number passed did not designate a valid friend.')
+        elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_IN_CALL']:
+            raise RuntimeError('This client is currently not in a call with the friend. Before the call is answered, '
+                               'only CANCEL is a valid control.')
+        elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['INVALID_TRANSITION']:
+            raise RuntimeError('Happens if user tried to pause an already paused call or if trying to resume a call '
+                               'that is not paused.')
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # TODO Controlling bit rates
+    # -----------------------------------------------------------------------------------------------------------------
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # A/V sending
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def audio_send_frame(self, friend_number, pcm, sample_count, channels, sampling_rate):
+        """
+        Send an audio frame to a friend.
+
+        The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
+        Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
+        For mono audio, this has no meaning, every sample is subsequent. For stereo, this means the expected format is
+        LRLRLR... with samples for left and right alternating.
+
+        :param friend_number: The friend number of the friend to which to send an audio frame.
+        :param pcm: An array of audio samples. The size of this array must be sample_count * channels.
+        :param sample_count: Number of samples in this frame. Valid numbers here are
+        ((sample rate) * (audio length) / 1000), where audio length can be 2.5, 5, 10, 20, 40 or 60 milliseconds.
+        :param channels: Number of audio channels. Sulpported values are 1 and 2.
+        :param sampling_rate: Audio sampling rate used in this frame. Valid sampling rates are 8000, 12000, 16000,
+        24000, or 48000.
+        """
+        toxav_err_send_frame = c_int()
+        result = ToxAV.libtoxav.toxav_audio_send_frame(self._toxav_pointer, c_uint32(friend_number),
+                                                       cast(pcm, c_void_p),
+                                                       c_size_t(sample_count), c_uint8(channels),
+                                                       c_uint32(sampling_rate), byref(toxav_err_send_frame))
+        toxav_err_send_frame = toxav_err_send_frame.value
+        if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']:
+            return bool(result)
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']:
+            raise ArgumentError('The samples data pointer was NULL.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number passed did not designate a valid friend.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']:
+            raise RuntimeError('This client is currently not in a call with the friend.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']:
+            raise RuntimeError('Synchronization error occurred.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']:
+            raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too '
+                                'large, or the audio sampling rate may be unsupported.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']:
+            raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said'
+                               'payload.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']:
+            RuntimeError('Failed to push frame through rtp interface.')
+
+    def video_send_frame(self, friend_number, width, height, y, u, v):
+        """
+        Send a video frame to a friend.
+
+        Y - plane should be of size: height * width
+        U - plane should be of size: (height/2) * (width/2)
+        V - plane should be of size: (height/2) * (width/2)
+
+        :param friend_number: The friend number of the friend to which to send a video frame.
+        :param width: Width of the frame in pixels.
+        :param height: Height of the frame in pixels.
+        :param y: Y (Luminance) plane data.
+        :param u: U (Chroma) plane data.
+        :param v: V (Chroma) plane data.
+        """
+        toxav_err_send_frame = c_int()
+        result = ToxAV.libtoxav.toxav_video_send_frame(self._toxav_pointer, c_uint32(friend_number), c_uint16(width),
+                                                       c_uint16(height), c_char_p(y), c_char_p(u), c_char_p(v),
+                                                       byref(toxav_err_send_frame))
+        toxav_err_send_frame = toxav_err_send_frame.value
+        if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']:
+            return bool(result)
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']:
+            raise ArgumentError('One of Y, U, or V was NULL.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']:
+            raise ArgumentError('The friend_number passed did not designate a valid friend.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']:
+            raise RuntimeError('This client is currently not in a call with the friend.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']:
+            raise RuntimeError('Synchronization error occurred.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']:
+            raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too '
+                                'large, or the audio sampling rate may be unsupported.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']:
+            raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said'
+                               'payload.')
+        elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']:
+            RuntimeError('Failed to push frame through rtp interface.')
+
+    # -----------------------------------------------------------------------------------------------------------------
+    # A/V receiving
+    # -----------------------------------------------------------------------------------------------------------------
+
+    def callback_audio_receive_frame(self, callback, user_data):
+        """
+        Set the callback for the `audio_receive_frame` event. Pass None to unset.
+
+        :param callback: Python function.
+        Function for the audio_receive_frame callback. The callback can be called multiple times per single
+        iteration depending on the amount of queued frames in the buffer. The received format is the same as in send
+        function.
+
+        Should take pointer (c_void_p) to ToxAV object,
+        The friend number (c_uint32) of the friend who sent an audio frame.
+        An array (c_uint8) of audio samples (sample_count * channels elements).
+        The number (c_size_t) of audio samples per channel in the PCM array.
+        Number (c_uint8) of audio channels.
+        Sampling rate (c_uint32) used in this frame.
+        pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_uint8, c_uint32, c_void_p)
+        self.audio_receive_frame_cb = c_callback(callback)
+        ToxAV.libtoxav.toxav_callback_audio_receive_frame(self._toxav_pointer, self.audio_receive_frame_cb, user_data)
+
+    def callback_video_receive_frame(self, callback, user_data):
+        """
+        Set the callback for the `video_receive_frame` event. Pass None to unset.
+
+        :param callback: Python function.
+        The function type for the video_receive_frame callback.
+
+        Should take
+        toxAV           pointer (c_void_p) to ToxAV object,
+        friend_number   The friend number (c_uint32) of the friend who sent a video frame.
+        width           Width (c_uint16) of the frame in pixels.
+        height          Height (c_uint16) of the frame in pixels.
+        y
+        u
+        v               Plane data (POINTER(c_uint8)).
+                            The size of plane data is derived from width and height where
+                            Y = MAX(width, abs(ystride)) * height,
+                            U = MAX(width/2, abs(ustride)) * (height/2) and
+                            V = MAX(width/2, abs(vstride)) * (height/2).
+        ystride
+        ustride
+        vstride         Strides data (c_int32). Strides represent padding for each plane that may or may not be present. You must
+                        handle strides in your image processing code. Strides are negative if the image is bottom-up
+                        hence why you MUST abs() it when calculating plane buffer size.
+        user_data       pointer (c_void_p) to user_data
+        :param user_data: pointer (c_void_p) to user data
+        """
+        c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint16, c_uint16, POINTER(c_uint8), POINTER(c_uint8),
+                               POINTER(c_uint8), c_int32, c_int32, c_int32, c_void_p)
+        self.video_receive_frame_cb = c_callback(callback)
+        ToxAV.libtoxav.toxav_callback_video_receive_frame(self._toxav_pointer, self.video_receive_frame_cb, user_data)
diff --git a/toxygen/toxav_enums.py b/toxygen/toxav_enums.py
new file mode 100644
index 0000000..3f3977a
--- /dev/null
+++ b/toxygen/toxav_enums.py
@@ -0,0 +1,131 @@
+TOXAV_ERR_NEW = {
+    # The function returned successfully.
+    'OK': 0,
+    # One of the arguments to the function was NULL when it was not expected.
+    'NULL': 1,
+    # Memory allocation failure while trying to allocate structures required for the A/V session.
+    'MALLOC': 2,
+    # Attempted to create a second session for the same Tox instance.
+    'MULTIPLE': 3,
+}
+
+TOXAV_ERR_CALL = {
+    # The function returned successfully.
+    'OK': 0,
+    # A resource allocation error occurred while trying to create the structures required for the call.
+    'MALLOC': 1,
+    # Synchronization error occurred.
+    'SYNC': 2,
+    # The friend number did not designate a valid friend.
+    'FRIEND_NOT_FOUND': 3,
+    # The friend was valid, but not currently connected.
+    'FRIEND_NOT_CONNECTED': 4,
+    # Attempted to call a friend while already in an audio or video call with them.
+    'FRIEND_ALREADY_IN_CALL': 5,
+    # Audio or video bit rate is invalid.
+    'INVALID_BIT_RATE': 6,
+}
+
+TOXAV_ERR_ANSWER = {
+    # The function returned successfully.
+    'OK': 0,
+    # Synchronization error occurred.
+    'SYNC': 1,
+    # Failed to initialize codecs for call session. Note that codec initiation will fail if there is no receive callback
+    # registered for either audio or video.
+    'CODEC_INITIALIZATION': 2,
+    # The friend number did not designate a valid friend.
+    'FRIEND_NOT_FOUND': 3,
+    # The friend was valid, but they are not currently trying to initiate a call. This is also returned if this client
+    # is already in a call with the friend.
+    'FRIEND_NOT_CALLING': 4,
+    # Audio or video bit rate is invalid.
+    'INVALID_BIT_RATE': 5,
+}
+
+TOXAV_FRIEND_CALL_STATE = {
+    # Set by the AV core if an error occurred on the remote end or if friend timed out. This is the final state after
+    # which no more state transitions can occur for the call. This call state will never be triggered in combination
+    # with other call states.
+    'ERROR': 1,
+    # The call has finished. This is the final state after which no more state transitions can occur for the call. This
+    # call state will never be triggered in combination with other call states.
+    'FINISHED': 2,
+    # The flag that marks that friend is sending audio.
+    'SENDING_A': 4,
+    # The flag that marks that friend is sending video.
+    'SENDING_V': 8,
+    # The flag that marks that friend is receiving audio.
+    'ACCEPTING_A': 16,
+    # The flag that marks that friend is receiving video.
+    'ACCEPTING_V': 32,
+}
+
+TOXAV_CALL_CONTROL = {
+    # Resume a previously paused call. Only valid if the pause was caused by this client, if not, this control is
+    # ignored. Not valid before the call is accepted.
+    'RESUME': 0,
+    # Put a call on hold. Not valid before the call is accepted.
+    'PAUSE': 1,
+    # Reject a call if it was not answered, yet. Cancel a call after it was answered.
+    'CANCEL': 2,
+    # Request that the friend stops sending audio. Regardless of the friend's compliance, this will cause the
+    # audio_receive_frame event to stop being triggered on receiving an audio frame from the friend.
+    'MUTE_AUDIO': 3,
+    # Calling this control will notify client to start sending audio again.
+    'UNMUTE_AUDIO': 4,
+    # Request that the friend stops sending video. Regardless of the friend's compliance, this will cause the
+    # video_receive_frame event to stop being triggered on receiving a video frame from the friend.
+    'HIDE_VIDEO': 5,
+    # Calling this control will notify client to start sending video again.
+    'SHOW_VIDEO': 6,
+}
+
+TOXAV_ERR_CALL_CONTROL = {
+    # The function returned successfully.
+    'OK': 0,
+    # Synchronization error occurred.
+    'SYNC': 1,
+    # The friend_number passed did not designate a valid friend.
+    'FRIEND_NOT_FOUND': 2,
+    # This client is currently not in a call with the friend. Before the call is answered, only CANCEL is a valid
+    # control.
+    'FRIEND_NOT_IN_CALL': 3,
+    # Happens if user tried to pause an already paused call or if trying to resume a call that is not paused.
+    'INVALID_TRANSITION': 4,
+}
+
+TOXAV_ERR_BIT_RATE_SET = {
+    # The function returned successfully.
+    'OK': 0,
+    # Synchronization error occurred.
+    'SYNC': 1,
+    # The audio bit rate passed was not one of the supported values.
+    'INVALID_AUDIO_BIT_RATE': 2,
+    # The video bit rate passed was not one of the supported values.
+    'INVALID_VIDEO_BIT_RATE': 3,
+    # The friend_number passed did not designate a valid friend.
+    'FRIEND_NOT_FOUND': 4,
+    # This client is currently not in a call with the friend.
+    'FRIEND_NOT_IN_CALL': 5,
+}
+
+TOXAV_ERR_SEND_FRAME = {
+    # The function returned successfully.
+    'OK': 0,
+    # In case of video, one of Y, U, or V was NULL. In case of audio, the samples data pointer was NULL.
+    'NULL': 1,
+    # The friend_number passed did not designate a valid friend.
+    'FRIEND_NOT_FOUND': 2,
+    # This client is currently not in a call with the friend.
+    'FRIEND_NOT_IN_CALL': 3,
+    # Synchronization error occurred.
+    'SYNC': 4,
+    # One of the frame parameters was invalid. E.g. the resolution may be too small or too large, or the audio sampling
+    # rate may be unsupported.
+    'INVALID': 5,
+    # Either friend turned off audio or video receiving or we turned off sending for the said payload.
+    'PAYLOAD_TYPE_DISABLED': 6,
+    # Failed to push frame through rtp interface.
+    'RTP_FAILED': 7,
+}
diff --git a/toxygen/toxcore_enums_and_consts.py b/toxygen/toxcore_enums_and_consts.py
new file mode 100644
index 0000000..6942d3a
--- /dev/null
+++ b/toxygen/toxcore_enums_and_consts.py
@@ -0,0 +1,944 @@
+TOX_USER_STATUS = {
+    'NONE': 0,
+    'AWAY': 1,
+    'BUSY': 2,
+}
+
+TOX_MESSAGE_TYPE = {
+    'NORMAL': 0,
+    'ACTION': 1,
+}
+
+TOX_PROXY_TYPE = {
+    'NONE': 0,
+    'HTTP': 1,
+    'SOCKS5': 2,
+}
+
+TOX_SAVEDATA_TYPE = {
+    'NONE': 0,
+    'TOX_SAVE': 1,
+    'SECRET_KEY': 2,
+}
+
+TOX_ERR_OPTIONS_NEW = {
+    'OK': 0,
+    'MALLOC': 1,
+}
+
+TOX_ERR_NEW = {
+    'OK': 0,
+    'NULL': 1,
+    'MALLOC': 2,
+    'PORT_ALLOC': 3,
+    'PROXY_BAD_TYPE': 4,
+    'PROXY_BAD_HOST': 5,
+    'PROXY_BAD_PORT': 6,
+    'PROXY_NOT_FOUND': 7,
+    'LOAD_ENCRYPTED': 8,
+    'LOAD_BAD_FORMAT': 9,
+}
+
+TOX_ERR_BOOTSTRAP = {
+    'OK': 0,
+    'NULL': 1,
+    'BAD_HOST': 2,
+    'BAD_PORT': 3,
+}
+
+TOX_CONNECTION = {
+    'NONE': 0,
+    'TCP': 1,
+    'UDP': 2,
+}
+
+TOX_ERR_SET_INFO = {
+    'OK': 0,
+    'NULL': 1,
+    'TOO_LONG': 2,
+}
+
+TOX_ERR_FRIEND_ADD = {
+    'OK': 0,
+    'NULL': 1,
+    'TOO_LONG': 2,
+    'NO_MESSAGE': 3,
+    'OWN_KEY': 4,
+    'ALREADY_SENT': 5,
+    'BAD_CHECKSUM': 6,
+    'SET_NEW_NOSPAM': 7,
+    'MALLOC': 8,
+}
+
+TOX_ERR_FRIEND_DELETE = {
+    'OK': 0,
+    'FRIEND_NOT_FOUND': 1,
+}
+
+TOX_ERR_FRIEND_BY_PUBLIC_KEY = {
+    'OK': 0,
+    'NULL': 1,
+    'NOT_FOUND': 2,
+}
+
+TOX_ERR_FRIEND_GET_PUBLIC_KEY = {
+    'OK': 0,
+    'FRIEND_NOT_FOUND': 1,
+}
+
+TOX_ERR_FRIEND_GET_LAST_ONLINE = {
+    'OK': 0,
+    'FRIEND_NOT_FOUND': 1,
+}
+
+TOX_ERR_FRIEND_QUERY = {
+    'OK': 0,
+    'NULL': 1,
+    'FRIEND_NOT_FOUND': 2,
+}
+
+TOX_ERR_SET_TYPING = {
+    'OK': 0,
+    'FRIEND_NOT_FOUND': 1,
+}
+
+TOX_ERR_FRIEND_SEND_MESSAGE = {
+    'OK': 0,
+    'NULL': 1,
+    'FRIEND_NOT_FOUND': 2,
+    'FRIEND_NOT_CONNECTED': 3,
+    'SENDQ': 4,
+    'TOO_LONG': 5,
+    'EMPTY': 6,
+}
+
+TOX_FILE_KIND = {
+    'DATA': 0,
+    'AVATAR': 1,
+}
+
+TOX_FILE_CONTROL = {
+    'RESUME': 0,
+    'PAUSE': 1,
+    'CANCEL': 2,
+}
+
+TOX_ERR_FILE_CONTROL = {
+    'OK': 0,
+    'FRIEND_NOT_FOUND': 1,
+    'FRIEND_NOT_CONNECTED': 2,
+    'NOT_FOUND': 3,
+    'NOT_PAUSED': 4,
+    'DENIED': 5,
+    'ALREADY_PAUSED': 6,
+    'SENDQ': 7,
+}
+
+TOX_ERR_FILE_SEEK = {
+    'OK': 0,
+    'FRIEND_NOT_FOUND': 1,
+    'FRIEND_NOT_CONNECTED': 2,
+    'NOT_FOUND': 3,
+    'DENIED': 4,
+    'INVALID_POSITION': 5,
+    'SENDQ': 6,
+}
+
+TOX_ERR_FILE_GET = {
+    'OK': 0,
+    'NULL': 1,
+    'FRIEND_NOT_FOUND': 2,
+    'NOT_FOUND': 3,
+}
+
+TOX_ERR_FILE_SEND = {
+    'OK': 0,
+    'NULL': 1,
+    'FRIEND_NOT_FOUND': 2,
+    'FRIEND_NOT_CONNECTED': 3,
+    'NAME_TOO_LONG': 4,
+    'TOO_MANY': 5,
+}
+
+TOX_ERR_FILE_SEND_CHUNK = {
+    'OK': 0,
+    'NULL': 1,
+    'FRIEND_NOT_FOUND': 2,
+    'FRIEND_NOT_CONNECTED': 3,
+    'NOT_FOUND': 4,
+    'NOT_TRANSFERRING': 5,
+    'INVALID_LENGTH': 6,
+    'SENDQ': 7,
+    'WRONG_POSITION': 8,
+}
+
+TOX_ERR_FRIEND_CUSTOM_PACKET = {
+    'OK': 0,
+    'NULL': 1,
+    'FRIEND_NOT_FOUND': 2,
+    'FRIEND_NOT_CONNECTED': 3,
+    'INVALID': 4,
+    'EMPTY': 5,
+    'TOO_LONG': 6,
+    'SENDQ': 7,
+}
+
+TOX_ERR_GET_PORT = {
+    'OK': 0,
+    'NOT_BOUND': 1,
+}
+
+TOX_GROUP_PRIVACY_STATE = {
+
+    #
+    # The group is considered to be public. Anyone may join the group using the Chat ID.
+    #
+    # If the group is in this state, even if the Chat ID is never explicitly shared
+    # with someone outside of the group, information including the Chat ID, IP addresses,
+    # and peer ID's (but not Tox ID's) is visible to anyone with access to a node
+    # storing a DHT entry for the given group.
+    #
+    'TOX_GROUP_PRIVACY_STATE_PUBLIC': 0,
+
+    #
+    # The group is considered to be private. The only way to join the group is by having
+    # someone in your contact list send you an invite.
+    #
+    # If the group is in this state, no group information (mentioned above) is present in the DHT;
+    # the DHT is not used for any purpose at all. If a public group is set to private,
+    # all DHT information related to the group will expire shortly.
+    #
+    'TOX_GROUP_PRIVACY_STATE_PRIVATE': 1
+}
+
+TOX_GROUP_ROLE = {
+
+    #
+    # May kick and ban all other peers as well as set their role to anything (except founder).
+    # Founders may also set the group password, toggle the privacy state, and set the peer limit.
+    #
+    'TOX_GROUP_ROLE_FOUNDER': 0,
+
+    #
+    # May kick, ban and set the user and observer roles for peers below this role.
+    # May also set the group topic.
+    #
+    'TOX_GROUP_ROLE_MODERATOR': 1,
+
+    #
+    # May communicate with other peers normally.
+    #
+    'TOX_GROUP_ROLE_USER': 2,
+
+    #
+    # May observe the group and ignore peers; may not communicate with other peers or with the group.
+    #
+    'TOX_GROUP_ROLE_OBSERVER': 3
+}
+
+TOX_ERR_GROUP_NEW = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_NEW_OK': 0,
+
+    #
+    # The group name exceeded TOX_GROUP_MAX_GROUP_NAME_LENGTH.
+    #
+    'TOX_ERR_GROUP_NEW_TOO_LONG': 1,
+
+    #
+    # group_name is NULL or length is zero.
+    #
+    'TOX_ERR_GROUP_NEW_EMPTY': 2,
+
+    #
+    # TOX_GROUP_PRIVACY_STATE is an invalid type.
+    #
+    'TOX_ERR_GROUP_NEW_PRIVACY': 3,
+
+    #
+    # The group instance failed to initialize.
+    #
+    'TOX_ERR_GROUP_NEW_INIT': 4,
+
+    #
+    # The group state failed to initialize. This usually indicates that something went wrong
+    # related to cryptographic signing.
+    #
+    'TOX_ERR_GROUP_NEW_STATE': 5,
+
+    #
+    # The group failed to announce to the DHT. This indicates a network related error.
+    #
+    'TOX_ERR_GROUP_NEW_ANNOUNCE': 6,
+}
+
+TOX_ERR_GROUP_JOIN = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_JOIN_OK': 0,
+
+    #
+    # The group instance failed to initialize.
+    #
+    'TOX_ERR_GROUP_JOIN_INIT': 1,
+
+    #
+    # The chat_id pointer is set to NULL or a group with chat_id already exists. This usually
+    # happens if the client attempts to create multiple sessions for the same group.
+    #
+    'TOX_ERR_GROUP_JOIN_BAD_CHAT_ID': 2,
+
+    #
+    # Password length exceeded TOX_GROUP_MAX_PASSWORD_SIZE.
+    #
+    'TOX_ERR_GROUP_JOIN_TOO_LONG': 3,
+}
+
+TOX_ERR_GROUP_RECONNECT = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_RECONNECT_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND': 1,
+}
+
+TOX_ERR_GROUP_LEAVE = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_LEAVE_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_LEAVE_GROUP_NOT_FOUND': 1,
+
+    #
+    # Message length exceeded 'TOX_GROUP_MAX_PART_LENGTH.
+    #
+    'TOX_ERR_GROUP_LEAVE_TOO_LONG': 2,
+
+    #
+    # The parting packet failed to send.
+    #
+    'TOX_ERR_GROUP_LEAVE_FAIL_SEND': 3,
+
+    #
+    # The group chat instance failed to be deleted. This may occur due to memory related errors.
+    #
+    'TOX_ERR_GROUP_LEAVE_DELETE_FAIL': 4,
+}
+
+TOX_ERR_GROUP_SELF_QUERY = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_SELF_QUERY_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND': 1,
+}
+
+
+TOX_ERR_GROUP_SELF_NAME_SET = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_SELF_NAME_SET_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_SELF_NAME_SET_GROUP_NOT_FOUND': 1,
+
+    #
+    # Name length exceeded 'TOX_MAX_NAME_LENGTH.
+    #
+    'TOX_ERR_GROUP_SELF_NAME_SET_TOO_LONG': 2,
+
+    #
+    # The length given to the set function is zero or name is a NULL pointer.
+    #
+    'TOX_ERR_GROUP_SELF_NAME_SET_INVALID': 3,
+
+    #
+    # The name is already taken by another peer in the group.
+    #
+    'TOX_ERR_GROUP_SELF_NAME_SET_TAKEN': 4,
+
+    #
+    # The packet failed to send.
+    #
+    'TOX_ERR_GROUP_SELF_NAME_SET_FAIL_SEND': 5
+}
+
+TOX_ERR_GROUP_SELF_STATUS_SET = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_SELF_STATUS_SET_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_SELF_STATUS_SET_GROUP_NOT_FOUND': 1,
+
+    #
+    # An invalid type was passed to the set function.
+    #
+    'TOX_ERR_GROUP_SELF_STATUS_SET_INVALID': 2,
+
+    #
+    # The packet failed to send.
+    #
+    'TOX_ERR_GROUP_SELF_STATUS_SET_FAIL_SEND': 3
+}
+
+TOX_ERR_GROUP_PEER_QUERY = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_PEER_QUERY_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND': 1,
+
+    #
+    # The ID passed did not designate a valid peer.
+    #
+    'TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND': 2
+}
+
+TOX_ERR_GROUP_STATE_QUERIES = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_STATE_QUERIES_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND': 1
+}
+
+
+TOX_ERR_GROUP_TOPIC_SET = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_TOPIC_SET_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_TOPIC_SET_GROUP_NOT_FOUND': 1,
+
+    #
+    # Topic length exceeded 'TOX_GROUP_MAX_TOPIC_LENGTH.
+    #
+    'TOX_ERR_GROUP_TOPIC_SET_TOO_LONG': 2,
+
+    #
+    # The caller does not have the required permissions to set the topic.
+    #
+    'TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS': 3,
+
+    #
+    # The packet could not be created. This error is usually related to cryptographic signing.
+    #
+    'TOX_ERR_GROUP_TOPIC_SET_FAIL_CREATE': 4,
+
+    #
+    # The packet failed to send.
+    #
+    'TOX_ERR_GROUP_TOPIC_SET_FAIL_SEND': 5
+}
+
+TOX_ERR_GROUP_SEND_MESSAGE = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_SEND_MESSAGE_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_SEND_MESSAGE_GROUP_NOT_FOUND': 1,
+
+    #
+    # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH.
+    #
+    'TOX_ERR_GROUP_SEND_MESSAGE_TOO_LONG': 2,
+
+    #
+    # The message pointer is null or length is zero.
+    #
+    'TOX_ERR_GROUP_SEND_MESSAGE_EMPTY': 3,
+
+    #
+    # The message type is invalid.
+    #
+    'TOX_ERR_GROUP_SEND_MESSAGE_BAD_TYPE': 4,
+
+    #
+    # The caller does not have the required permissions to send group messages.
+    #
+    'TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS': 5,
+
+    #
+    # Packet failed to send.
+    #
+    'TOX_ERR_GROUP_SEND_MESSAGE_FAIL_SEND': 6
+}
+
+TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_GROUP_NOT_FOUND': 1,
+
+    #
+    # The ID passed did not designate a valid peer.
+    #
+    'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PEER_NOT_FOUND': 2,
+
+    #
+    # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH.
+    #
+    'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_TOO_LONG': 3,
+
+    #
+    # The message pointer is null or length is zero.
+    #
+    'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY': 4,
+
+    #
+    # The caller does not have the required permissions to send group messages.
+    #
+    'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS': 5,
+
+    #
+    # Packet failed to send.
+    #
+    'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_FAIL_SEND': 6
+}
+
+TOX_ERR_GROUP_SEND_CUSTOM_PACKET = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_GROUP_NOT_FOUND': 1,
+
+    #
+    # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH.
+    #
+    'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_TOO_LONG': 2,
+
+    #
+    # The message pointer is null or length is zero.
+    #
+    'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_EMPTY': 3,
+
+    #
+    # The caller does not have the required permissions to send group messages.
+    #
+    'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_PERMISSIONS': 4
+}
+
+TOX_ERR_GROUP_INVITE_FRIEND = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_INVITE_FRIEND_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_INVITE_FRIEND_GROUP_NOT_FOUND': 1,
+
+    #
+    # The friend number passed did not designate a valid friend.
+    #
+    'TOX_ERR_GROUP_INVITE_FRIEND_FRIEND_NOT_FOUND': 2,
+
+    #
+    # Creation of the invite packet failed. This indicates a network related error.
+    #
+    'TOX_ERR_GROUP_INVITE_FRIEND_INVITE_FAIL': 3,
+
+    #
+    # Packet failed to send.
+    #
+    'TOX_ERR_GROUP_INVITE_FRIEND_FAIL_SEND': 4
+}
+
+TOX_ERR_GROUP_INVITE_ACCEPT = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_INVITE_ACCEPT_OK': 0,
+
+    #
+    # The invite data is not in the expected format.
+    #
+    'TOX_ERR_GROUP_INVITE_ACCEPT_BAD_INVITE': 1,
+
+    #
+    # The group instance failed to initialize.
+    #
+    'TOX_ERR_GROUP_INVITE_ACCEPT_INIT_FAILED': 2,
+
+    #
+    # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE.
+    #
+    'TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG': 3
+}
+
+TOX_GROUP_JOIN_FAIL = {
+
+    #
+    # You are using the same nickname as someone who is already in the group.
+    #
+    'TOX_GROUP_JOIN_FAIL_NAME_TAKEN': 0,
+
+    #
+    # The group peer limit has been reached.
+    #
+    'TOX_GROUP_JOIN_FAIL_PEER_LIMIT': 1,
+
+    #
+    # You have supplied an invalid password.
+    #
+    'TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD': 2,
+
+    #
+    # The join attempt failed due to an unspecified error. This often occurs when the group is
+    # not found in the DHT.
+    #
+    'TOX_GROUP_JOIN_FAIL_UNKNOWN': 3
+}
+
+TOX_ERR_GROUP_FOUNDER_SET_PASSWORD = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_GROUP_NOT_FOUND': 1,
+
+    #
+    # The caller does not have the required permissions to set the password.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS': 2,
+
+    #
+    # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG': 3,
+
+    #
+    # The packet failed to send.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_FAIL_SEND': 4
+}
+
+TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_GROUP_NOT_FOUND': 1,
+
+    #
+    # 'TOX_GROUP_PRIVACY_STATE is an invalid type.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_INVALID': 2,
+
+    #
+    # The caller does not have the required permissions to set the privacy state.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS': 3,
+
+    #
+    # The privacy state could not be set. This may occur due to an error related to
+    # cryptographic signing of the new shared state.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SET': 4,
+
+    #
+    # The packet failed to send.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SEND': 5
+}
+
+TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_GROUP_NOT_FOUND': 1,
+
+    #
+    # The caller does not have the required permissions to set the peer limit.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS': 2,
+
+    #
+    # The peer limit could not be set. This may occur due to an error related to
+    # cryptographic signing of the new shared state.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SET': 3,
+
+    #
+    # The packet failed to send.
+    #
+    'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SEND': 4
+}
+
+TOX_ERR_GROUP_TOGGLE_IGNORE = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_TOGGLE_IGNORE_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_TOGGLE_IGNORE_GROUP_NOT_FOUND': 1,
+
+    #
+    # The ID passed did not designate a valid peer.
+    #
+    'TOX_ERR_GROUP_TOGGLE_IGNORE_PEER_NOT_FOUND': 2
+}
+
+TOX_ERR_GROUP_MOD_SET_ROLE = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_MOD_SET_ROLE_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_MOD_SET_ROLE_GROUP_NOT_FOUND': 1,
+
+    #
+    # The ID passed did not designate a valid peer. Note: you cannot set your own role.
+    #
+    'TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND': 2,
+
+    #
+    # The caller does not have the required permissions for this action.
+    #
+    'TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS': 3,
+
+    #
+    # The role assignment is invalid. This will occur if you try to set a peer's role to
+    # the role they already have.
+    #
+    'TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT': 4,
+
+    #
+    # The role was not successfully set. This may occur if something goes wrong with role setting': ,
+    # or if the packet fails to send.
+    #
+    'TOX_ERR_GROUP_MOD_SET_ROLE_FAIL_ACTION': 5
+}
+
+TOX_ERR_GROUP_MOD_REMOVE_PEER = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_PEER_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_PEER_GROUP_NOT_FOUND': 1,
+
+    #
+    # The ID passed did not designate a valid peer.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_PEER_PEER_NOT_FOUND': 2,
+
+    #
+    # The caller does not have the required permissions for this action.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS': 3,
+
+    #
+    # The peer could not be removed from the group.
+    #
+    # If a ban was set': , this error indicates that the ban entry could not be created.
+    # This is usually due to the peer's IP address already occurring in the ban list. It may also
+    # be due to the entry containing invalid peer information': , or a failure to cryptographically
+    # authenticate the entry.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_ACTION': 4,
+
+    #
+    # The packet failed to send.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_SEND': 5
+}
+
+TOX_ERR_GROUP_MOD_REMOVE_BAN = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_BAN_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_BAN_GROUP_NOT_FOUND': 1,
+
+    #
+    # The caller does not have the required permissions for this action.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS': 2,
+
+    #
+    # The ban entry could not be removed. This may occur if ban_id does not designate
+    # a valid ban entry.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION': 3,
+
+    #
+    # The packet failed to send.
+    #
+    'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_SEND': 4
+}
+
+TOX_GROUP_MOD_EVENT = {
+
+    #
+    # A peer has been kicked from the group.
+    #
+    'TOX_GROUP_MOD_EVENT_KICK': 0,
+
+    #
+    # A peer has been banned from the group.
+    #
+    'TOX_GROUP_MOD_EVENT_BAN': 1,
+
+    #
+    # A peer as been given the observer role.
+    #
+    'TOX_GROUP_MOD_EVENT_OBSERVER': 2,
+
+    #
+    # A peer has been given the user role.
+    #
+    'TOX_GROUP_MOD_EVENT_USER': 3,
+
+    #
+    # A peer has been given the moderator role.
+    #
+    'TOX_GROUP_MOD_EVENT_MODERATOR': 4,
+}
+
+TOX_ERR_GROUP_BAN_QUERY = {
+
+    #
+    # The function returned successfully.
+    #
+    'TOX_ERR_GROUP_BAN_QUERY_OK': 0,
+
+    #
+    # The group number passed did not designate a valid group.
+    #
+    'TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND': 1,
+
+    #
+    # The ban_id does not designate a valid ban list entry.
+    #
+    'TOX_ERR_GROUP_BAN_QUERY_BAD_ID': 2,
+}
+
+TOX_PUBLIC_KEY_SIZE = 32
+
+TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6
+
+TOX_MAX_FRIEND_REQUEST_LENGTH = 1016
+
+TOX_MAX_MESSAGE_LENGTH = 1372
+
+TOX_GROUP_MAX_TOPIC_LENGTH = 512
+
+TOX_GROUP_MAX_PART_LENGTH = 128
+
+TOX_GROUP_MAX_GROUP_NAME_LENGTH = 48
+
+TOX_GROUP_MAX_PASSWORD_SIZE = 32
+
+TOX_GROUP_CHAT_ID_SIZE = 32
+
+TOX_GROUP_PEER_PUBLIC_KEY_SIZE = 32
+
+TOX_MAX_NAME_LENGTH = 128
+
+TOX_MAX_STATUS_MESSAGE_LENGTH = 1007
+
+TOX_SECRET_KEY_SIZE = 32
+
+TOX_FILE_ID_LENGTH = 32
+
+TOX_HASH_LENGTH = 32
+
+TOX_MAX_CUSTOM_PACKET_SIZE = 1373
diff --git a/toxygen/toxencryptsave.py b/toxygen/toxencryptsave.py
new file mode 100644
index 0000000..14d3aee
--- /dev/null
+++ b/toxygen/toxencryptsave.py
@@ -0,0 +1,114 @@
+import libtox
+import util
+from ctypes import c_size_t, create_string_buffer, byref, c_int, ArgumentError, c_char_p, c_bool
+
+
+TOX_ERR_ENCRYPTION = {
+    # The function returned successfully.
+    'OK': 0,
+    # Some input data, or maybe the output pointer, was null.
+    'NULL': 1,
+    # The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The
+    # functions accepting keys do not produce this error.
+    'KEY_DERIVATION_FAILED': 2,
+    # The encryption itself failed.
+    'FAILED': 3
+}
+
+TOX_ERR_DECRYPTION = {
+    # The function returned successfully.
+    'OK': 0,
+    # Some input data, or maybe the output pointer, was null.
+    'NULL': 1,
+    # The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes
+    'INVALID_LENGTH': 2,
+    # The input data is missing the magic number (i.e. wasn't created by this module, or is corrupted)
+    'BAD_FORMAT': 3,
+    # The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The
+    # functions accepting keys do not produce this error.
+    'KEY_DERIVATION_FAILED': 4,
+    # The encrypted byte array could not be decrypted. Either the data was corrupt or the password/key was incorrect.
+    'FAILED': 5,
+}
+
+TOX_PASS_ENCRYPTION_EXTRA_LENGTH = 80
+
+
+class ToxEncryptSave(util.Singleton):
+
+    libtoxencryptsave = libtox.LibToxEncryptSave()
+
+    def __init__(self):
+        super().__init__()
+        self._passphrase = None
+
+    def set_password(self, passphrase):
+        self._passphrase = passphrase
+
+    def has_password(self):
+        return bool(self._passphrase)
+
+    def is_password(self, password):
+        return self._passphrase == password
+
+    def is_data_encrypted(self, data):
+        func = self.libtoxencryptsave.tox_is_data_encrypted
+        func.restype = c_bool
+        result = func(c_char_p(bytes(data)))
+        return result
+
+    def pass_encrypt(self, data):
+        """
+        Encrypts the given data with the given passphrase.
+
+        :return: output array
+        """
+        out = create_string_buffer(len(data) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
+        tox_err_encryption = c_int()
+        self.libtoxencryptsave.tox_pass_encrypt(c_char_p(data),
+                                                c_size_t(len(data)),
+                                                c_char_p(bytes(self._passphrase, 'utf-8')),
+                                                c_size_t(len(self._passphrase)),
+                                                out,
+                                                byref(tox_err_encryption))
+        tox_err_encryption = tox_err_encryption.value
+        if tox_err_encryption == TOX_ERR_ENCRYPTION['OK']:
+            return out[:]
+        elif tox_err_encryption == TOX_ERR_ENCRYPTION['NULL']:
+            raise ArgumentError('Some input data, or maybe the output pointer, was null.')
+        elif tox_err_encryption == TOX_ERR_ENCRYPTION['KEY_DERIVATION_FAILED']:
+            raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a'
+                               ' lack of memory issue. The functions accepting keys do not produce this error.')
+        elif tox_err_encryption == TOX_ERR_ENCRYPTION['FAILED']:
+            raise RuntimeError('The encryption itself failed.')
+
+    def pass_decrypt(self, data):
+        """
+        Decrypts the given data with the given passphrase.
+
+        :return: output array
+        """
+        out = create_string_buffer(len(data) - TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
+        tox_err_decryption = c_int()
+        self.libtoxencryptsave.tox_pass_decrypt(c_char_p(bytes(data)),
+                                                c_size_t(len(data)),
+                                                c_char_p(bytes(self._passphrase, 'utf-8')),
+                                                c_size_t(len(self._passphrase)),
+                                                out,
+                                                byref(tox_err_decryption))
+        tox_err_decryption = tox_err_decryption.value
+        if tox_err_decryption == TOX_ERR_DECRYPTION['OK']:
+            return out[:]
+        elif tox_err_decryption == TOX_ERR_DECRYPTION['NULL']:
+            raise ArgumentError('Some input data, or maybe the output pointer, was null.')
+        elif tox_err_decryption == TOX_ERR_DECRYPTION['INVALID_LENGTH']:
+            raise ArgumentError('The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes')
+        elif tox_err_decryption == TOX_ERR_DECRYPTION['BAD_FORMAT']:
+            raise ArgumentError('The input data is missing the magic number (i.e. wasn\'t created by this module, or is'
+                                ' corrupted)')
+        elif tox_err_decryption == TOX_ERR_DECRYPTION['KEY_DERIVATION_FAILED']:
+            raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a'
+                               ' lack of memory issue. The functions accepting keys do not produce this error.')
+        elif tox_err_decryption == TOX_ERR_DECRYPTION['FAILED']:
+            raise RuntimeError('The encrypted byte array could not be decrypted. Either the data was corrupt or the '
+                               'password/key was incorrect.')
diff --git a/toxygen/toxygen.pro b/toxygen/toxygen.pro
index 9643c8b..aeafe07 100644
--- a/toxygen/toxygen.pro
+++ b/toxygen/toxygen.pro
@@ -1,2 +1,2 @@
-SOURCES = main.py  profile.py  menu.py  list_items.py  loginscreen.py  mainscreen.py plugins/plugin_super_class.py callbacks.py widgets.py avwidgets.py mainscreen_widgets.py passwordscreen.py
-TRANSLATIONS = translations/en_GB.ts  translations/ru_RU.ts  translations/fr_FR.ts translations/uk_UA.ts
+SOURCES = main.py  profile.py  menu.py  list_items.py  loginscreen.py  mainscreen.py plugins/plugin_super_class.py callbacks.py widgets.py avwidgets.py mainscreen_widgets.py
+TRANSLATIONS = translations/en_GB.ts  translations/ru_RU.ts  translations/fr_FR.ts
diff --git a/toxygen/translations/en_GB.ts b/toxygen/translations/en_GB.ts
index 7186005..045735f 100644
--- a/toxygen/translations/en_GB.ts
+++ b/toxygen/translations/en_GB.ts
@@ -3,22 +3,22 @@
 <context>
     <name>AddContact</name>
     <message>
-        <location filename="menu.py" line="73"/>
+        <location filename="menu.py" line="70"/>
         <source>Add contact</source>
         <translation>Add contact</translation>
     </message>
     <message>
-        <location filename="menu.py" line="75"/>
+        <location filename="menu.py" line="72"/>
         <source>TOX ID:</source>
         <translation>TOX ID:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="76"/>
+        <location filename="menu.py" line="73"/>
         <source>Message:</source>
         <translation>Message:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="77"/>
+        <location filename="menu.py" line="74"/>
         <source>TOX ID or public key of contact</source>
         <translation type="unfinished"></translation>
     </message>
@@ -26,7 +26,7 @@
 <context>
     <name>Callback</name>
     <message>
-        <location filename="callbacks.py" line="229"/>
+        <location filename="callbacks.py" line="183"/>
         <source>File from</source>
         <translation type="unfinished"></translation>
     </message>
@@ -34,32 +34,32 @@
 <context>
     <name>Form</name>
     <message>
-        <location filename="menu.py" line="74"/>
+        <location filename="menu.py" line="71"/>
         <source>Send request</source>
         <translation>Send request</translation>
     </message>
     <message>
-        <location filename="menu.py" line="345"/>
+        <location filename="menu.py" line="334"/>
         <source>IPv6</source>
         <translation>IPv6</translation>
     </message>
     <message>
-        <location filename="menu.py" line="346"/>
+        <location filename="menu.py" line="335"/>
         <source>UDP</source>
         <translation>UDP</translation>
     </message>
     <message>
-        <location filename="menu.py" line="347"/>
+        <location filename="menu.py" line="336"/>
         <source>Proxy</source>
         <translation>Proxy</translation>
     </message>
     <message>
-        <location filename="menu.py" line="348"/>
+        <location filename="menu.py" line="337"/>
         <source>IP:</source>
         <translation>IP:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="349"/>
+        <location filename="menu.py" line="338"/>
         <source>Port:</source>
         <translation>Port:</translation>
     </message>
@@ -69,118 +69,113 @@
         <translation type="obsolete">Online contacts</translation>
     </message>
     <message>
-        <location filename="menu.py" line="351"/>
+        <location filename="menu.py" line="340"/>
         <source>HTTP</source>
         <translation>HTTP</translation>
     </message>
     <message>
-        <location filename="menu.py" line="353"/>
+        <location filename="menu.py" line="342"/>
         <source>WARNING:
 using proxy with enabled UDP
 can produce IP leak</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <location filename="menu.py" line="352"/>
-        <source>Download nodes list from tox.chat</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>MainWindow</name>
     <message>
-        <location filename="mainscreen.py" line="121"/>
+        <location filename="mainscreen.py" line="101"/>
         <source>Profile</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="127"/>
+        <location filename="mainscreen.py" line="107"/>
         <source>Settings</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="420"/>
+        <location filename="mainscreen.py" line="359"/>
         <source>About</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="119"/>
+        <location filename="mainscreen.py" line="100"/>
         <source>Add contact</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="122"/>
+        <location filename="mainscreen.py" line="102"/>
         <source>Privacy</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="123"/>
+        <location filename="mainscreen.py" line="103"/>
         <source>Interface</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="124"/>
+        <location filename="mainscreen.py" line="104"/>
         <source>Notifications</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="125"/>
+        <location filename="mainscreen.py" line="105"/>
         <source>Network</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="126"/>
+        <location filename="mainscreen.py" line="106"/>
         <source>About program</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="profile.py" line="854"/>
+        <location filename="profile.py" line="753"/>
         <source>User {} wants to add you to contact list. Message:
 {}</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="profile.py" line="856"/>
+        <location filename="profile.py" line="755"/>
         <source>Friend request</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="523"/>
+        <location filename="mainscreen.py" line="430"/>
         <source>Choose file</source>
         <translation>Choose file</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="589"/>
+        <location filename="mainscreen.py" line="493"/>
         <source>Disallow auto accept</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="590"/>
+        <location filename="mainscreen.py" line="494"/>
         <source>Allow auto accept</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="594"/>
+        <location filename="mainscreen.py" line="496"/>
         <source>Set alias</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="598"/>
+        <location filename="mainscreen.py" line="497"/>
         <source>Clear history</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="609"/>
+        <location filename="mainscreen.py" line="504"/>
         <source>Remove friend</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="profile.py" line="694"/>
+        <location filename="profile.py" line="592"/>
         <source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
         <translation>Enter new alias for friend {} or leave empty to use friend&apos;s name:</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="128"/>
+        <location filename="mainscreen.py" line="108"/>
         <source>Audio</source>
         <translation>Audio</translation>
     </message>
@@ -190,24 +185,24 @@ can produce IP leak</source>
         <translation type="obsolete">Find contact</translation>
     </message>
     <message>
-        <location filename="profile.py" line="826"/>
+        <location filename="profile.py" line="725"/>
         <source>Friend added</source>
         <translation>Friend added</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="421"/>
+        <location filename="mainscreen.py" line="360"/>
         <source>Toxygen is Tox client written on Python.
 Version: </source>
         <translation>Toxygen is Tox client written on Python.
 Version:</translation>
     </message>
     <message>
-        <location filename="profile.py" line="827"/>
+        <location filename="profile.py" line="726"/>
         <source>Friend added without sending friend request</source>
         <translation>Friend added without sending friend request</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="665"/>
+        <location filename="list_items.py" line="486"/>
         <source>Choose folder</source>
         <translation>Choose folder</translation>
     </message>
@@ -222,300 +217,180 @@ Version:</translation>
         <translation type="obsolete">Send file</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="132"/>
+        <location filename="mainscreen.py" line="110"/>
         <source>Send message</source>
         <translation>Send message</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="133"/>
+        <location filename="mainscreen.py" line="111"/>
         <source>Start audio call with friend</source>
         <translation>Start audio call with friend</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="625"/>
+        <location filename="mainscreen.py" line="509"/>
         <source>Plugins</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="115"/>
+        <location filename="mainscreen.py" line="96"/>
         <source>List of plugins</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="412"/>
+        <location filename="mainscreen.py" line="109"/>
         <source>Search</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="135"/>
+        <location filename="mainscreen.py" line="113"/>
         <source>All</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="136"/>
+        <location filename="mainscreen.py" line="114"/>
         <source>Online</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="611"/>
+        <location filename="mainscreen.py" line="505"/>
         <source>Notes</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="650"/>
+        <location filename="mainscreen.py" line="527"/>
         <source>Notes about user</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="widgets.py" line="124"/>
+        <location filename="widgets.py" line="83"/>
         <source>Copy link location</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="widgets.py" line="126"/>
+        <location filename="widgets.py" line="86"/>
         <source>Copy</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="widgets.py" line="128"/>
+        <location filename="widgets.py" line="89"/>
         <source>Select all</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="widgets.py" line="130"/>
+        <location filename="widgets.py" line="92"/>
         <source>Delete</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="widgets.py" line="132"/>
+        <location filename="widgets.py" line="95"/>
         <source>Paste</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="widgets.py" line="134"/>
+        <location filename="widgets.py" line="98"/>
         <source>Cut</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="widgets.py" line="136"/>
+        <location filename="widgets.py" line="101"/>
         <source>Undo</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="widgets.py" line="138"/>
+        <location filename="widgets.py" line="104"/>
         <source>Redo</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="widgets.py" line="159"/>
+        <location filename="widgets.py" line="125"/>
         <source>Save</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="profile.py" line="329"/>
+        <location filename="profile.py" line="259"/>
         <source>User {} is now known as {}</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="list_items.py" line="164"/>
+        <location filename="list_items.py" line="145"/>
         <source>Delete message</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="113"/>
+        <location filename="mainscreen.py" line="94"/>
         <source>Lock</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="497"/>
+        <location filename="mainscreen.py" line="403"/>
         <source>Cannot lock app</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="499"/>
+        <location filename="mainscreen.py" line="406"/>
         <source>Error. Profile password is not set.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="603"/>
+        <location filename="mainscreen.py" line="499"/>
         <source>Name</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="604"/>
+        <location filename="mainscreen.py" line="500"/>
         <source>Status message</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="606"/>
+        <location filename="mainscreen.py" line="501"/>
         <source>Public key</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <location filename="main.py" line="101"/>
-        <source>Error</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="103"/>
-        <source>Profile with this name already exists</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="672"/>
-        <source>Choose folder with sticker pack</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="684"/>
-        <source>Choose folder with smiley pack</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="144"/>
-        <source>Import plugin</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="476"/>
-        <source>Choose folder with plugin</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="485"/>
-        <source>Restart Toxygen</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="487"/>
-        <source>Plugin will be loaded after restart</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="list_items.py" line="45"/>
-        <source>Quote selected text</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="597"/>
-        <source>Chat history</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="599"/>
-        <source>Export as text</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="600"/>
-        <source>Export as HTML</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="130"/>
-        <source>Updates</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="137"/>
-        <source>Online first</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="139"/>
-        <source>Online and by name</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="140"/>
-        <source>Online first and by name</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="610"/>
-        <source>Block friend</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="474"/>
-        <source>Not found</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="472"/>
-        <source>Text &quot;{}&quot; was not found</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="145"/>
-        <source>Reload plugins</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="129"/>
-        <source>Video</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1337"/>
-        <source>User {} invites you to group chat. Accept?</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1338"/>
-        <source>Group chat invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1374"/>
-        <source>{} users in chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1395"/>
-        <source>Enter new title for group {}:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="634"/>
-        <source>Set title</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="120"/>
-        <source>Create group chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="615"/>
-        <source>Invite to group chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="633"/>
-        <source>Leave chat</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>MenuWindow</name>
     <message>
-        <location filename="mainscreen_widgets.py" line="217"/>
+        <location filename="avwidgets.py" line="121"/>
+        <source>Send audio message to friend {}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="avwidgets.py" line="126"/>
+        <source>Start recording</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="avwidgets.py" line="135"/>
+        <source>Stop recording</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="248"/>
         <source>Send screenshot</source>
         <translation type="unfinished">Send screenshot</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="218"/>
+        <location filename="mainscreen_widgets.py" line="249"/>
         <source>Send file</source>
         <translation type="unfinished">Send file</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="219"/>
+        <location filename="mainscreen_widgets.py" line="250"/>
+        <source>Send audio message</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="251"/>
+        <source>Send video message</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="252"/>
         <source>Add smiley</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="220"/>
+        <location filename="mainscreen_widgets.py" line="253"/>
         <source>Send sticker</source>
         <translation type="unfinished"></translation>
     </message>
@@ -523,63 +398,25 @@ Version:</translation>
 <context>
     <name>NetworkSettings</name>
     <message>
-        <location filename="menu.py" line="344"/>
+        <location filename="menu.py" line="333"/>
         <source>Network settings</source>
         <translation>Network settings</translation>
     </message>
     <message>
-        <location filename="menu.py" line="350"/>
+        <location filename="menu.py" line="339"/>
         <source>Restart TOX core</source>
         <translation>Restart Tox core</translation>
     </message>
 </context>
-<context>
-    <name>PasswordScreen</name>
-    <message>
-        <location filename="passwordscreen.py" line="132"/>
-        <source>Profile password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="134"/>
-        <source>Password (at least 8 symbols)</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="136"/>
-        <source>Confirm password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="138"/>
-        <source>Set password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="153"/>
-        <source>Passwords do not match</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="141"/>
-        <source>There is no way to recover lost passwords</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="150"/>
-        <source>Password must be at least 8 symbols</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
 <context>
     <name>PluginWindow</name>
     <message>
-        <location filename="plugins/plugin_super_class.py" line="139"/>
+        <location filename="plugins/plugin_super_class.py" line="132"/>
         <source>List of commands for plugin {}</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="plugins/plugin_super_class.py" line="140"/>
+        <location filename="plugins/plugin_super_class.py" line="133"/>
         <source>No commands available</source>
         <translation type="unfinished"></translation>
     </message>
@@ -587,42 +424,42 @@ Version:</translation>
 <context>
     <name>PluginsForm</name>
     <message>
-        <location filename="menu.py" line="972"/>
+        <location filename="menu.py" line="761"/>
         <source>Plugins</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="973"/>
+        <location filename="menu.py" line="762"/>
         <source>Open selected plugin</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="985"/>
+        <location filename="menu.py" line="775"/>
         <source>No GUI found for this plugin</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="1000"/>
+        <location filename="menu.py" line="791"/>
         <source>No description available</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="1016"/>
+        <location filename="menu.py" line="807"/>
         <source>Disable plugin</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="1018"/>
+        <location filename="menu.py" line="809"/>
         <source>Enable plugin</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="1008"/>
+        <location filename="menu.py" line="799"/>
         <source>No plugins found</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="985"/>
+        <location filename="menu.py" line="776"/>
         <source>Error</source>
         <translation type="unfinished"></translation>
     </message>
@@ -630,67 +467,67 @@ Version:</translation>
 <context>
     <name>ProfileSettingsForm</name>
     <message>
-        <location filename="menu.py" line="173"/>
+        <location filename="menu.py" line="169"/>
         <source>Export profile</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="174"/>
+        <location filename="menu.py" line="170"/>
         <source>Profile settings</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="175"/>
+        <location filename="menu.py" line="171"/>
         <source>Name:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="176"/>
+        <location filename="menu.py" line="172"/>
         <source>Status:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="177"/>
+        <location filename="menu.py" line="173"/>
         <source>TOX ID:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="178"/>
+        <location filename="menu.py" line="174"/>
         <source>Copy TOX ID</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="179"/>
+        <location filename="menu.py" line="175"/>
         <source>New avatar</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="180"/>
+        <location filename="menu.py" line="176"/>
         <source>Reset avatar</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="181"/>
+        <location filename="menu.py" line="177"/>
         <source>New NoSpam</source>
         <translation>New NoSpam</translation>
     </message>
     <message>
-        <location filename="menu.py" line="182"/>
+        <location filename="menu.py" line="178"/>
         <source>Profile password</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="183"/>
+        <location filename="menu.py" line="179"/>
         <source>Password (at least 8 symbols)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="184"/>
+        <location filename="menu.py" line="180"/>
         <source>Confirm password</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="185"/>
+        <location filename="menu.py" line="181"/>
         <source>Set password</source>
         <translation type="unfinished"></translation>
     </message>
@@ -700,17 +537,17 @@ Version:</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="187"/>
+        <location filename="menu.py" line="183"/>
         <source>Leaving blank will reset current password</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="188"/>
+        <location filename="menu.py" line="184"/>
         <source>There is no way to recover lost passwords</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="218"/>
+        <location filename="menu.py" line="217"/>
         <source>Password must be at least 8 symbols</source>
         <translation type="unfinished"></translation>
     </message>
@@ -720,128 +557,108 @@ Version:</translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="189"/>
+        <location filename="menu.py" line="185"/>
         <source>Online</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="190"/>
+        <location filename="menu.py" line="186"/>
         <source>Away</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="191"/>
+        <location filename="menu.py" line="187"/>
         <source>Busy</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="205"/>
+        <location filename="menu.py" line="202"/>
         <source>Mark as not default profile</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="208"/>
+        <location filename="menu.py" line="206"/>
         <source>Mark as default profile</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="192"/>
+        <location filename="menu.py" line="188"/>
         <source>Copy public key</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <location filename="menu.py" line="269"/>
-        <source>Use new path</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="271"/>
-        <source>Do you want to move your profile to this location?</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>WelcomeScreen</name>
     <message>
-        <location filename="mainscreen_widgets.py" line="295"/>
+        <location filename="mainscreen_widgets.py" line="329"/>
         <source>Don&apos;t show again</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="296"/>
+        <location filename="mainscreen_widgets.py" line="331"/>
         <source>Tip of the day</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="301"/>
+        <location filename="mainscreen_widgets.py" line="337"/>
         <source>Press Esc if you want hide app to tray.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="307"/>
+        <location filename="mainscreen_widgets.py" line="345"/>
         <source>You can use Tox over Tor. For more info read &lt;a href=&quot;https://wiki.tox.chat/users/tox_over_tor_tot&quot;&gt;this post&lt;/a&gt;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="313"/>
+        <location filename="mainscreen_widgets.py" line="353"/>
         <source>Set profile password via Profile -&gt; Settings. Password allows Toxygen encrypt your history and settings.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="304"/>
+        <location filename="mainscreen_widgets.py" line="357"/>
+        <source>Since v0.1.3 Toxygen supports plugins. &lt;a href=&quot;https://github.com/xveduk/toxygen/blob/master/docs/plugins.md&quot;&gt;Read more&lt;/a&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="361"/>
+        <source>New in Toxygen v0.2.2:&lt;br&gt;Users can lock application using profile password.&lt;br&gt;Compact contact list support&lt;br&gt;Bug fixes&lt;br&gt;Tox DNS improvements</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="341"/>
         <source>Right click on screenshot button hides app to tray during screenshot.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="310"/>
+        <location filename="mainscreen_widgets.py" line="349"/>
         <source>Use Settings -&gt; Interface to customize interface.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="319"/>
+        <location filename="mainscreen_widgets.py" line="365"/>
         <source>Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="331"/>
+        <location filename="mainscreen_widgets.py" line="369"/>
         <source>Set new NoSpam to avoid spam friend requests: Profile -&gt; Settings -&gt; Set new NoSpam.</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="325"/>
-        <source>Delete single message in chat: make right click on spinner or message time and choose &quot;Delete&quot; in menu</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="328"/>
-        <source>Use right click on inline image to save it</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="316"/>
-        <source>Since v0.1.3 Toxygen supports plugins. &lt;a href=&quot;https://github.com/toxygen-project/toxygen/blob/master/docs/plugins.md&quot;&gt;Read more&lt;/a&gt;</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="322"/>
-        <source>New in Toxygen 0.4.1:&lt;br&gt;Downloading nodes from tox.chat&lt;br&gt;Bug fixes</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>audioSettingsForm</name>
     <message>
-        <location filename="menu.py" line="805"/>
+        <location filename="menu.py" line="718"/>
         <source>Audio settings</source>
         <translation>Audio settings</translation>
     </message>
     <message>
-        <location filename="menu.py" line="806"/>
+        <location filename="menu.py" line="719"/>
         <source>Input device:</source>
         <translation>Input device:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="807"/>
+        <location filename="menu.py" line="720"/>
         <source>Output device:</source>
         <translation>Output device:</translation>
     </message>
@@ -849,32 +666,32 @@ Version:</translation>
 <context>
     <name>incoming_call</name>
     <message>
-        <location filename="profile.py" line="1253"/>
+        <location filename="profile.py" line="1132"/>
         <source>Incoming video call</source>
         <translation>Incoming video call</translation>
     </message>
     <message>
-        <location filename="profile.py" line="1255"/>
+        <location filename="profile.py" line="1135"/>
         <source>Incoming audio call</source>
         <translation>Incoming audio call</translation>
     </message>
     <message>
-        <location filename="profile.py" line="1236"/>
+        <location filename="profile.py" line="1115"/>
         <source>Outgoing video call</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="profile.py" line="1238"/>
+        <location filename="profile.py" line="1118"/>
         <source>Outgoing audio call</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="profile.py" line="1284"/>
+        <location filename="profile.py" line="1164"/>
         <source>Call declined</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="profile.py" line="1286"/>
+        <location filename="profile.py" line="1166"/>
         <source>Call finished</source>
         <translation type="unfinished"></translation>
     </message>
@@ -882,274 +699,206 @@ Version:</translation>
 <context>
     <name>interfaceForm</name>
     <message>
-        <location filename="menu.py" line="655"/>
+        <location filename="menu.py" line="619"/>
         <source>Interface settings</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="656"/>
+        <location filename="menu.py" line="620"/>
         <source>Theme:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="657"/>
+        <location filename="menu.py" line="621"/>
         <source>Language:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="658"/>
+        <location filename="menu.py" line="622"/>
         <source>Smileys</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="659"/>
+        <location filename="menu.py" line="623"/>
         <source>Smiley pack:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="660"/>
+        <location filename="menu.py" line="624"/>
         <source>Mirror mode</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="661"/>
+        <location filename="menu.py" line="625"/>
         <source>Messages font size:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="754"/>
+        <location filename="menu.py" line="667"/>
         <source>Restart app to apply settings</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="754"/>
+        <location filename="menu.py" line="668"/>
         <source>Restart required</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="662"/>
+        <location filename="menu.py" line="626"/>
         <source>Select unread messages notification color</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="663"/>
+        <location filename="menu.py" line="627"/>
         <source>Compact contact list</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <location filename="menu.py" line="664"/>
-        <source>Import smiley pack</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="665"/>
-        <source>Import sticker pack</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="654"/>
-        <source>Show avatars in chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="666"/>
-        <source>Close to tray</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="667"/>
-        <source>Select font</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>login</name>
     <message>
-        <location filename="loginscreen.py" line="70"/>
+        <location filename="loginscreen.py" line="75"/>
         <source>Log in</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="71"/>
+        <location filename="loginscreen.py" line="76"/>
         <source>Create</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="72"/>
+        <location filename="loginscreen.py" line="77"/>
         <source>Profile name:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="73"/>
+        <location filename="loginscreen.py" line="78"/>
         <source>Load profile</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="74"/>
+        <location filename="loginscreen.py" line="79"/>
         <source>Use as default</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="75"/>
+        <location filename="loginscreen.py" line="80"/>
         <source>Load existing profile</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="76"/>
+        <location filename="loginscreen.py" line="81"/>
         <source>Create new profile</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="77"/>
+        <location filename="loginscreen.py" line="82"/>
         <source>toxygen</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="69"/>
+        <location filename="loginscreen.py" line="74"/>
         <source>Profile name</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="main.py" line="166"/>
+        <location filename="main.py" line="127"/>
         <source>Other instance of Toxygen uses this profile or profile was not properly closed. Continue?</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <location filename="main.py" line="113"/>
-        <source>Do you want to set profile password?</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="124"/>
-        <source>Do you want to save profile in default folder? If no, profile will be saved in program folder</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="138"/>
-        <source>Profile saving error! Does Toxygen have permission to write to this directory?</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="288"/>
-        <source>Update for Toxygen was found. Download and install it?</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>notificationsForm</name>
     <message>
-        <location filename="menu.py" line="543"/>
+        <location filename="menu.py" line="530"/>
         <source>Notification settings</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="544"/>
+        <location filename="menu.py" line="531"/>
         <source>Enable notifications</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="546"/>
+        <location filename="menu.py" line="532"/>
         <source>Enable call&apos;s sound</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="547"/>
+        <location filename="menu.py" line="533"/>
         <source>Enable sound notifications</source>
         <translation></translation>
     </message>
-    <message>
-        <location filename="menu.py" line="545"/>
-        <source>Notify about all messages in groups</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>pass</name>
-    <message>
-        <location filename="passwordscreen.py" line="61"/>
-        <source>Enter password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="62"/>
-        <source>Password:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="63"/>
-        <source>Incorrect password</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>privacySettings</name>
     <message>
-        <location filename="menu.py" line="438"/>
+        <location filename="menu.py" line="426"/>
         <source>Privacy settings</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="439"/>
+        <location filename="menu.py" line="427"/>
         <source>Save chat history</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="440"/>
+        <location filename="menu.py" line="428"/>
         <source>Allow file auto accept</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="441"/>
+        <location filename="menu.py" line="429"/>
         <source>Send typing notifications</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="442"/>
+        <location filename="menu.py" line="430"/>
         <source>Auto accept default path:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="443"/>
+        <location filename="menu.py" line="431"/>
         <source>Change</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="444"/>
+        <location filename="menu.py" line="432"/>
         <source>Allow inlines</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="486"/>
+        <location filename="menu.py" line="477"/>
         <source>Chat history</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="488"/>
+        <location filename="menu.py" line="480"/>
         <source>History will be cleaned! Continue?</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="menu.py" line="446"/>
+        <location filename="menu.py" line="434"/>
         <source>Blocked users:</source>
         <translation>Blocked users:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="447"/>
+        <location filename="menu.py" line="435"/>
         <source>Unblock</source>
         <translation>Unblock</translation>
     </message>
     <message>
-        <location filename="menu.py" line="448"/>
+        <location filename="menu.py" line="436"/>
         <source>Block user</source>
         <translation>Block user</translation>
     </message>
     <message>
-        <location filename="menu.py" line="460"/>
+        <location filename="menu.py" line="448"/>
         <source>Add to friend list</source>
         <translation>Add to friend list</translation>
     </message>
     <message>
-        <location filename="menu.py" line="461"/>
+        <location filename="menu.py" line="449"/>
         <source>Do you want to add this user to friend list?</source>
         <translation>Do you want to add this user to friend list?</translation>
     </message>
@@ -1159,12 +908,12 @@ Version:</translation>
         <translation type="obsolete">Block by TOX ID:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="445"/>
+        <location filename="menu.py" line="433"/>
         <source>Block by public key:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="449"/>
+        <location filename="menu.py" line="437"/>
         <source>Save unsent messages only</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1172,115 +921,34 @@ Version:</translation>
 <context>
     <name>tray</name>
     <message>
-        <location filename="main.py" line="224"/>
+        <location filename="main.py" line="176"/>
         <source>Open Toxygen</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="main.py" line="233"/>
+        <location filename="main.py" line="185"/>
         <source>Exit</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="main.py" line="225"/>
+        <location filename="main.py" line="177"/>
         <source>Set status</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="main.py" line="226"/>
+        <location filename="main.py" line="178"/>
         <source>Online</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="main.py" line="227"/>
+        <location filename="main.py" line="179"/>
         <source>Away</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="main.py" line="228"/>
+        <location filename="main.py" line="180"/>
         <source>Busy</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
-<context>
-    <name>updateSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="1056"/>
-        <source>Update settings</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1057"/>
-        <source>Select update mode:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1058"/>
-        <source>Update Toxygen</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1059"/>
-        <source>Disabled</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1060"/>
-        <source>Manual</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1061"/>
-        <source>Auto</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1080"/>
-        <source>Error</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1073"/>
-        <source>Problems with internet connection</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1081"/>
-        <source>Updater not found</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1092"/>
-        <source>No updates found</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1093"/>
-        <source>Toxygen is up to date</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>videoSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="887"/>
-        <source>Video settings</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="888"/>
-        <source>Device:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="867"/>
-        <source>Desktop</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="889"/>
-        <source>Select region</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
 </TS>
diff --git a/toxygen/translations/fr_FR.qm b/toxygen/translations/fr_FR.qm
index 33b0cbc..e7ab531 100644
Binary files a/toxygen/translations/fr_FR.qm and b/toxygen/translations/fr_FR.qm differ
diff --git a/toxygen/translations/fr_FR.ts b/toxygen/translations/fr_FR.ts
index 1931a26..bb91c4f 100644
--- a/toxygen/translations/fr_FR.ts
+++ b/toxygen/translations/fr_FR.ts
@@ -3,63 +3,63 @@
 <context>
     <name>AddContact</name>
     <message>
-        <location filename="menu.py" line="73"/>
+        <location filename="menu.py" line="70"/>
         <source>Add contact</source>
-        <translation>Ajouter un contact</translation>
+        <translation>Rajouter un contact</translation>
     </message>
     <message>
-        <location filename="menu.py" line="75"/>
+        <location filename="menu.py" line="72"/>
         <source>TOX ID:</source>
-        <translation>ID Tox :</translation>
+        <translation>ID TOX :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="76"/>
+        <location filename="menu.py" line="73"/>
         <source>Message:</source>
         <translation>Message :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="77"/>
+        <location filename="menu.py" line="74"/>
         <source>TOX ID or public key of contact</source>
-        <translation>ID Tox ou clé publique de contact</translation>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>Callback</name>
     <message>
-        <location filename="callbacks.py" line="229"/>
+        <location filename="callbacks.py" line="183"/>
         <source>File from</source>
-        <translation>Fichier de</translation>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>Form</name>
     <message>
-        <location filename="menu.py" line="74"/>
+        <location filename="menu.py" line="71"/>
         <source>Send request</source>
         <translation>Envoyer une demande</translation>
     </message>
     <message>
-        <location filename="menu.py" line="345"/>
+        <location filename="menu.py" line="334"/>
         <source>IPv6</source>
         <translation>IPv6</translation>
     </message>
     <message>
-        <location filename="menu.py" line="346"/>
+        <location filename="menu.py" line="335"/>
         <source>UDP</source>
         <translation>UDP</translation>
     </message>
     <message>
-        <location filename="menu.py" line="347"/>
+        <location filename="menu.py" line="336"/>
         <source>Proxy</source>
         <translation>Proxy</translation>
     </message>
     <message>
-        <location filename="menu.py" line="348"/>
+        <location filename="menu.py" line="337"/>
         <source>IP:</source>
         <translation>IP :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="349"/>
+        <location filename="menu.py" line="338"/>
         <source>Port:</source>
         <translation>Port :</translation>
     </message>
@@ -69,82 +69,75 @@
         <translation type="obsolete">Contacts connectés</translation>
     </message>
     <message>
-        <location filename="menu.py" line="351"/>
+        <location filename="menu.py" line="340"/>
         <source>HTTP</source>
         <translation>HTTP</translation>
     </message>
     <message>
-        <location filename="menu.py" line="353"/>
+        <location filename="menu.py" line="342"/>
         <source>WARNING:
 using proxy with enabled UDP
 can produce IP leak</source>
-        <translation>ATTENTION :
-Utiliser un proxy avec UDP
-peut entrainer une fuite d&apos;IP</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="352"/>
-        <source>Download nodes list from tox.chat</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>MainWindow</name>
     <message>
-        <location filename="mainscreen.py" line="121"/>
+        <location filename="mainscreen.py" line="101"/>
         <source>Profile</source>
-        <translation>Profil</translation>
+        <translation>Profile</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="127"/>
+        <location filename="mainscreen.py" line="107"/>
         <source>Settings</source>
-        <translation>Paramètres</translation>
+        <translation>Paramêtres</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="420"/>
+        <location filename="mainscreen.py" line="359"/>
         <source>About</source>
         <translation>À Propos</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="119"/>
+        <location filename="mainscreen.py" line="100"/>
         <source>Add contact</source>
-        <translation>Ajouter un contact</translation>
+        <translation>Rajouter un contact</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="122"/>
+        <location filename="mainscreen.py" line="102"/>
         <source>Privacy</source>
         <translation>Confidentialité</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="123"/>
+        <location filename="mainscreen.py" line="103"/>
         <source>Interface</source>
         <translation>Interface</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="124"/>
+        <location filename="mainscreen.py" line="104"/>
         <source>Notifications</source>
         <translation>Notifications</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="125"/>
+        <location filename="mainscreen.py" line="105"/>
         <source>Network</source>
         <translation>Réseau</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="126"/>
+        <location filename="mainscreen.py" line="106"/>
         <source>About program</source>
-        <translation>À propos de toxygen</translation>
+        <translation>À propos du programme</translation>
     </message>
     <message>
-        <location filename="profile.py" line="854"/>
+        <location filename="profile.py" line="753"/>
         <source>User {} wants to add you to contact list. Message:
 {}</source>
-        <translation>L&apos;Utilisateur {} veut vous ajouter à sa liste de contacts. Message : {}</translation>
+        <translation>L&apos;Utilisateur {} veut vout rajouter à sa liste de contacts. Message : {}</translation>
     </message>
     <message>
-        <location filename="profile.py" line="856"/>
+        <location filename="profile.py" line="755"/>
         <source>Friend request</source>
-        <translation>Demande de contact</translation>
+        <translation>Demande d&apos;amis</translation>
     </message>
     <message>
         <location filename="mainscreen.py" line="319"/>
@@ -152,27 +145,27 @@ peut entrainer une fuite d&apos;IP</translation>
         <translation type="obsolete">Toxygen est un client Tox écris en Python 2.7. Version : </translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="523"/>
+        <location filename="mainscreen.py" line="430"/>
         <source>Choose file</source>
-        <translation>Sélectionner un fichier</translation>
+        <translation>Choisir un fichier</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="589"/>
+        <location filename="mainscreen.py" line="493"/>
         <source>Disallow auto accept</source>
         <translation>Désactiver l&apos;auto-réception</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="590"/>
+        <location filename="mainscreen.py" line="494"/>
         <source>Allow auto accept</source>
         <translation>Activer l&apos;auto-réception</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="594"/>
+        <location filename="mainscreen.py" line="496"/>
         <source>Set alias</source>
         <translation>Définir un alias</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="598"/>
+        <location filename="mainscreen.py" line="497"/>
         <source>Clear history</source>
         <translation>Vider l&apos;historique</translation>
     </message>
@@ -182,17 +175,17 @@ peut entrainer une fuite d&apos;IP</translation>
         <translation type="obsolete">Copier la clé publique</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="609"/>
+        <location filename="mainscreen.py" line="504"/>
         <source>Remove friend</source>
-        <translation>Retirer ce contact</translation>
+        <translation>Retirer un ami</translation>
     </message>
     <message>
-        <location filename="profile.py" line="694"/>
+        <location filename="profile.py" line="592"/>
         <source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
-        <translation>Entrez un nouvel alias pour le contact {} ou laissez vide pour garder son nom de base :</translation>
+        <translation>Entrez un nouvel alias pour l&apos;ami {} ou laissez vide pour garder son nom de base :</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="128"/>
+        <location filename="mainscreen.py" line="108"/>
         <source>Audio</source>
         <translation>Audio</translation>
     </message>
@@ -202,26 +195,26 @@ peut entrainer une fuite d&apos;IP</translation>
         <translation type="obsolete">Trouver le contact</translation>
     </message>
     <message>
-        <location filename="profile.py" line="826"/>
+        <location filename="profile.py" line="725"/>
         <source>Friend added</source>
-        <translation>Contact ajouté</translation>
+        <translation>Ami rajouté</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="421"/>
+        <location filename="mainscreen.py" line="360"/>
         <source>Toxygen is Tox client written on Python.
 Version: </source>
         <translation>Toxygen est un client Tox écrit en Python.
 Version :</translation>
     </message>
     <message>
-        <location filename="profile.py" line="827"/>
+        <location filename="profile.py" line="726"/>
         <source>Friend added without sending friend request</source>
-        <translation>Contact ajouté sans envoi de demande</translation>
+        <translation>Ami rajouté sans avoir envoyé de demande</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="665"/>
+        <location filename="list_items.py" line="486"/>
         <source>Choose folder</source>
-        <translation>Sélectionner un dossier</translation>
+        <translation>Choisir le dossier</translation>
     </message>
     <message>
         <location filename="mainscreen.py" line="135"/>
@@ -234,631 +227,448 @@ Version :</translation>
         <translation type="obsolete">Envoyer le fichier</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="132"/>
+        <location filename="mainscreen.py" line="110"/>
         <source>Send message</source>
         <translation>Envoyer le message</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="133"/>
+        <location filename="mainscreen.py" line="111"/>
         <source>Start audio call with friend</source>
-        <translation>Démarrer un appel audio avec un ami</translation>
+        <translation>Lancer un appel audio avec un ami</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="625"/>
+        <location filename="mainscreen.py" line="509"/>
         <source>Plugins</source>
-        <translation>Plugins</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="115"/>
+        <location filename="mainscreen.py" line="96"/>
         <source>List of plugins</source>
-        <translation>Liste de plugins</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="412"/>
+        <location filename="mainscreen.py" line="109"/>
         <source>Search</source>
-        <translation>Chercher</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="135"/>
-        <source>All</source>
-        <translation>Tous</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="136"/>
-        <source>Online</source>
-        <translation>En ligne</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="611"/>
-        <source>Notes</source>
-        <translation>Notes</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="650"/>
-        <source>Notes about user</source>
-        <translation>Notes sur l&apos;utilisateur</translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="124"/>
-        <source>Copy link location</source>
-        <translation>Copier l&apos;emplacement du lien</translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="126"/>
-        <source>Copy</source>
-        <translation>Copier</translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="128"/>
-        <source>Select all</source>
-        <translation>Tout sélectionner</translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="130"/>
-        <source>Delete</source>
-        <translation>Supprimer</translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="132"/>
-        <source>Paste</source>
-        <translation>Coller</translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="134"/>
-        <source>Cut</source>
-        <translation>Couper</translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="136"/>
-        <source>Undo</source>
-        <translation>Annuler</translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="138"/>
-        <source>Redo</source>
-        <translation>Refaire</translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="159"/>
-        <source>Save</source>
-        <translation>Sauvegarder</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="329"/>
-        <source>User {} is now known as {}</source>
-        <translation>L&apos;utilisateur {} s&apos;appelle désormais {}</translation>
-    </message>
-    <message>
-        <location filename="list_items.py" line="164"/>
-        <source>Delete message</source>
-        <translation>Supprimer ce message</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="mainscreen.py" line="113"/>
-        <source>Lock</source>
-        <translation>Verrouiller</translation>
+        <source>All</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="497"/>
+        <location filename="mainscreen.py" line="114"/>
+        <source>Online</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen.py" line="505"/>
+        <source>Notes</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen.py" line="527"/>
+        <source>Notes about user</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="widgets.py" line="83"/>
+        <source>Copy link location</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="widgets.py" line="86"/>
+        <source>Copy</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="widgets.py" line="89"/>
+        <source>Select all</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="widgets.py" line="92"/>
+        <source>Delete</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="widgets.py" line="95"/>
+        <source>Paste</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="widgets.py" line="98"/>
+        <source>Cut</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="widgets.py" line="101"/>
+        <source>Undo</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="widgets.py" line="104"/>
+        <source>Redo</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="widgets.py" line="125"/>
+        <source>Save</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="profile.py" line="259"/>
+        <source>User {} is now known as {}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="list_items.py" line="145"/>
+        <source>Delete message</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen.py" line="94"/>
+        <source>Lock</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen.py" line="403"/>
         <source>Cannot lock app</source>
-        <translation>Impossible de verrouiller l&apos;application</translation>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen.py" line="406"/>
+        <source>Error. Profile password is not set.</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="mainscreen.py" line="499"/>
-        <source>Error. Profile password is not set.</source>
-        <translation>Erreur. Le profil n&apos;a pas de mot de passe.</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="603"/>
         <source>Name</source>
-        <translation>Nom</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="604"/>
+        <location filename="mainscreen.py" line="500"/>
         <source>Status message</source>
-        <translation>Status</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="606"/>
+        <location filename="mainscreen.py" line="501"/>
         <source>Public key</source>
-        <translation>Clé publique</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="101"/>
-        <source>Error</source>
-        <translation>Erreur</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="103"/>
-        <source>Profile with this name already exists</source>
-        <translation>Un profil ayant ce nom existe déjà</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="672"/>
-        <source>Choose folder with sticker pack</source>
-        <translation>Sélectionner le dossier contenant le pack de stickers</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="684"/>
-        <source>Choose folder with smiley pack</source>
-        <translation>Sélectionner le dossier contenant le pack de smileys</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="144"/>
-        <source>Import plugin</source>
-        <translation>Importer un plugin</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="476"/>
-        <source>Choose folder with plugin</source>
-        <translation>Sélectionner un dossier avec des plugins</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="485"/>
-        <source>Restart Toxygen</source>
-        <translation>Redémarrer Toxyger</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="487"/>
-        <source>Plugin will be loaded after restart</source>
-        <translation>Le plugin sera chargé après le redémarrage</translation>
-    </message>
-    <message>
-        <location filename="list_items.py" line="45"/>
-        <source>Quote selected text</source>
-        <translation>Citer le texte sélectionné</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="597"/>
-        <source>Chat history</source>
-        <translation>Historique de la conversation</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="599"/>
-        <source>Export as text</source>
-        <translation>Exporter comme texte</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="600"/>
-        <source>Export as HTML</source>
-        <translation>Exporter comme HTML</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="130"/>
-        <source>Updates</source>
-        <translation>Mises à jour</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="137"/>
-        <source>Online first</source>
-        <translation>En ligne d&apos;abord</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="139"/>
-        <source>Online and by name</source>
-        <translation>En ligne et par nom</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="140"/>
-        <source>Online first and by name</source>
-        <translation>En ligne d&apos;abord puis par nom</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="610"/>
-        <source>Block friend</source>
-        <translation>Bloquer le contact</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="474"/>
-        <source>Not found</source>
-        <translation>Non trouvé</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="472"/>
-        <source>Text &quot;{}&quot; was not found</source>
-        <translation>Le texte &quot;{}&quot; n&apos;a pas été trouvé</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="145"/>
-        <source>Reload plugins</source>
-        <translation>Recharger les plugins</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="129"/>
-        <source>Video</source>
-        <translation>Vidéo</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1337"/>
-        <source>User {} invites you to group chat. Accept?</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1338"/>
-        <source>Group chat invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1374"/>
-        <source>{} users in chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1395"/>
-        <source>Enter new title for group {}:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="634"/>
-        <source>Set title</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="120"/>
-        <source>Create group chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="615"/>
-        <source>Invite to group chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="633"/>
-        <source>Leave chat</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>MenuWindow</name>
     <message>
-        <location filename="mainscreen_widgets.py" line="217"/>
+        <location filename="avwidgets.py" line="121"/>
+        <source>Send audio message to friend {}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="avwidgets.py" line="126"/>
+        <source>Start recording</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="avwidgets.py" line="135"/>
+        <source>Stop recording</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="248"/>
         <source>Send screenshot</source>
-        <translation>Envoyer une capture d&apos;écran</translation>
+        <translation type="unfinished">Envoyer une capture d&apos;écran</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="218"/>
+        <location filename="mainscreen_widgets.py" line="249"/>
         <source>Send file</source>
-        <translation>Envoyer un fichier</translation>
+        <translation type="unfinished">Envoyer le fichier</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="219"/>
+        <location filename="mainscreen_widgets.py" line="250"/>
+        <source>Send audio message</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="251"/>
+        <source>Send video message</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="252"/>
         <source>Add smiley</source>
-        <translation>Ajouter un smiley</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="220"/>
+        <location filename="mainscreen_widgets.py" line="253"/>
         <source>Send sticker</source>
-        <translation>Ajouter un sticker</translation>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>NetworkSettings</name>
     <message>
-        <location filename="menu.py" line="344"/>
+        <location filename="menu.py" line="333"/>
         <source>Network settings</source>
         <translation>Paramètres réseaux</translation>
     </message>
     <message>
-        <location filename="menu.py" line="350"/>
+        <location filename="menu.py" line="339"/>
         <source>Restart TOX core</source>
-        <translation>Relancer le noyau Tox</translation>
-    </message>
-</context>
-<context>
-    <name>PasswordScreen</name>
-    <message>
-        <location filename="passwordscreen.py" line="132"/>
-        <source>Profile password</source>
-        <translation>Mot de passe du profil</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="134"/>
-        <source>Password (at least 8 symbols)</source>
-        <translation>Mot de passe (8 symboles minimum)</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="136"/>
-        <source>Confirm password</source>
-        <translation>Confirmation</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="138"/>
-        <source>Set password</source>
-        <translation>Enregistrer le mot de passe</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="153"/>
-        <source>Passwords do not match</source>
-        <translation>Les mots de passes sont différents</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="141"/>
-        <source>There is no way to recover lost passwords</source>
-        <translation>Il est impossible de récuperer un mot de passe perdu</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="150"/>
-        <source>Password must be at least 8 symbols</source>
-        <translation>Un mot de passe doit faire 8 symboles minimum</translation>
+        <translation>Relancer le noyau TOX</translation>
     </message>
 </context>
 <context>
     <name>PluginWindow</name>
     <message>
-        <location filename="plugins/plugin_super_class.py" line="139"/>
+        <location filename="plugins/plugin_super_class.py" line="132"/>
         <source>List of commands for plugin {}</source>
-        <translation>Liste de commandes du plugin {}</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="plugins/plugin_super_class.py" line="140"/>
+        <location filename="plugins/plugin_super_class.py" line="133"/>
         <source>No commands available</source>
-        <translation>Pas de commandes disponibles</translation>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>PluginsForm</name>
     <message>
-        <location filename="menu.py" line="972"/>
+        <location filename="menu.py" line="761"/>
         <source>Plugins</source>
-        <translation>Plugins</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="973"/>
+        <location filename="menu.py" line="762"/>
         <source>Open selected plugin</source>
-        <translation>Ouvrir le plugin sélectionné</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="985"/>
+        <location filename="menu.py" line="775"/>
         <source>No GUI found for this plugin</source>
-        <translation>Pas d&apos;interface pour ce plugin</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="1000"/>
+        <location filename="menu.py" line="791"/>
         <source>No description available</source>
-        <translation>Pas de description</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="1016"/>
+        <location filename="menu.py" line="807"/>
         <source>Disable plugin</source>
-        <translation>Désactiver le plugin</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="1018"/>
+        <location filename="menu.py" line="809"/>
         <source>Enable plugin</source>
-        <translation>Activer le plugin</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="1008"/>
+        <location filename="menu.py" line="799"/>
         <source>No plugins found</source>
-        <translation>Pas de plugin trouvé</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="985"/>
+        <location filename="menu.py" line="776"/>
         <source>Error</source>
-        <translation>Erreur</translation>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>ProfileSettingsForm</name>
     <message>
-        <location filename="menu.py" line="173"/>
+        <location filename="menu.py" line="169"/>
         <source>Export profile</source>
-        <translation>Exporter le profil</translation>
+        <translation>Exporter le profile</translation>
     </message>
     <message>
-        <location filename="menu.py" line="174"/>
+        <location filename="menu.py" line="170"/>
         <source>Profile settings</source>
-        <translation>Paramètres du profil</translation>
+        <translation>Paramêtres du profil</translation>
     </message>
     <message>
-        <location filename="menu.py" line="175"/>
+        <location filename="menu.py" line="171"/>
         <source>Name:</source>
         <translation>Nom :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="176"/>
+        <location filename="menu.py" line="172"/>
         <source>Status:</source>
         <translation>Status :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="177"/>
+        <location filename="menu.py" line="173"/>
         <source>TOX ID:</source>
         <translation>ID TOX :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="178"/>
+        <location filename="menu.py" line="174"/>
         <source>Copy TOX ID</source>
-        <translation>Copier l&apos;ID Tox</translation>
+        <translation>Copier l&apos;ID TOX</translation>
     </message>
     <message>
-        <location filename="menu.py" line="179"/>
+        <location filename="menu.py" line="175"/>
         <source>New avatar</source>
         <translation>Nouvel avatar</translation>
     </message>
     <message>
-        <location filename="menu.py" line="180"/>
+        <location filename="menu.py" line="176"/>
         <source>Reset avatar</source>
         <translation>Réinitialiser l&apos;avatar</translation>
     </message>
     <message>
-        <location filename="menu.py" line="181"/>
+        <location filename="menu.py" line="177"/>
         <source>New NoSpam</source>
         <translation>Nouveau NoSpam</translation>
     </message>
     <message>
-        <location filename="menu.py" line="182"/>
+        <location filename="menu.py" line="178"/>
         <source>Profile password</source>
-        <translation>Mot de passe du profil</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="183"/>
+        <location filename="menu.py" line="179"/>
         <source>Password (at least 8 symbols)</source>
-        <translation>Mot de passe (8 symboles minimum)</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="184"/>
+        <location filename="menu.py" line="180"/>
         <source>Confirm password</source>
-        <translation>Confirmation</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="185"/>
+        <location filename="menu.py" line="181"/>
         <source>Set password</source>
-        <translation>Sauvegarder le mot de passe</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="menu.py" line="221"/>
         <source>Passwords do not match</source>
-        <translation>Les mots de passe sont différents</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="187"/>
+        <location filename="menu.py" line="183"/>
         <source>Leaving blank will reset current password</source>
-        <translation>Laisser vide réinitialisera le mot de passe actuel</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="188"/>
+        <location filename="menu.py" line="184"/>
         <source>There is no way to recover lost passwords</source>
-        <translation>Il est impossible de récupérer un mot de passe perdu</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="218"/>
+        <location filename="menu.py" line="217"/>
         <source>Password must be at least 8 symbols</source>
-        <translation>Le mot de passe doit faire 8 symboles minimum</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="menu.py" line="250"/>
         <source>Choose avatar</source>
-        <translation>Choisir l&apos;avatar</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="189"/>
+        <location filename="menu.py" line="185"/>
         <source>Online</source>
-        <translation>En ligne</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="190"/>
+        <location filename="menu.py" line="186"/>
         <source>Away</source>
-        <translation>Absent</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="191"/>
+        <location filename="menu.py" line="187"/>
         <source>Busy</source>
-        <translation>Occupé</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="205"/>
+        <location filename="menu.py" line="202"/>
         <source>Mark as not default profile</source>
-        <translation>Ne plus en faire le profil par défaut</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="208"/>
+        <location filename="menu.py" line="206"/>
         <source>Mark as default profile</source>
-        <translation>En faire le profil par défaut</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="192"/>
+        <location filename="menu.py" line="188"/>
         <source>Copy public key</source>
-        <translation>Copier la clé publique</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="269"/>
-        <source>Use new path</source>
-        <translation>Utiliser un nouveau chemin</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="271"/>
-        <source>Do you want to move your profile to this location?</source>
-        <translation>Déplacer le profil dans ce dossier ?</translation>
+        <translation type="unfinished">Copier la clé publique</translation>
     </message>
 </context>
 <context>
     <name>WelcomeScreen</name>
     <message>
-        <location filename="mainscreen_widgets.py" line="295"/>
+        <location filename="mainscreen_widgets.py" line="329"/>
         <source>Don&apos;t show again</source>
-        <translation>Ne plus montrer</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="296"/>
-        <source>Tip of the day</source>
-        <translation>Astuce du jou</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="301"/>
-        <source>Press Esc if you want hide app to tray.</source>
-        <translation>Appuyez sur échap pour réduire l&apos;application.</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="307"/>
-        <source>You can use Tox over Tor. For more info read &lt;a href=&quot;https://wiki.tox.chat/users/tox_over_tor_tot&quot;&gt;this post&lt;/a&gt;</source>
-        <translation>Vous pouvez utiliser Tox avec Tor. Pour plus d&apos;informations, voir &lt;a href=&quot;https://wiki.tox.chat/users/tox_over_tor_tot&quot;&gt;cet article&lt;/a&gt;</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="313"/>
-        <source>Set profile password via Profile -&gt; Settings. Password allows Toxygen encrypt your history and settings.</source>
-        <translation>Vous pouvez mettre un mot de passe dans Profil -&gt; Paramètres -&gt; Mot de passe pour que toxygen encrypte votre historique et vos paramètres.</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="304"/>
-        <source>Right click on screenshot button hides app to tray during screenshot.</source>
-        <translation>Faire un clic droit sur le bouton de capture d&apos;écran réduit l&apos;application avant de capturer l&apos;écran.</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="310"/>
-        <source>Use Settings -&gt; Interface to customize interface.</source>
-        <translation>Vous pouvez customizer votre interface dans Paramètres -&gt; Interface.</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="319"/>
-        <source>Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.</source>
-        <translation>Toxygen permet d&apos;envoyer des messages et fichiers en différé. Envoyez des messages ou fichiers à un contact hors ligne et il le recevra plus tard.</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="mainscreen_widgets.py" line="331"/>
+        <source>Tip of the day</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="337"/>
+        <source>Press Esc if you want hide app to tray.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="345"/>
+        <source>You can use Tox over Tor. For more info read &lt;a href=&quot;https://wiki.tox.chat/users/tox_over_tor_tot&quot;&gt;this post&lt;/a&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="353"/>
+        <source>Set profile password via Profile -&gt; Settings. Password allows Toxygen encrypt your history and settings.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="357"/>
+        <source>Since v0.1.3 Toxygen supports plugins. &lt;a href=&quot;https://github.com/xveduk/toxygen/blob/master/docs/plugins.md&quot;&gt;Read more&lt;/a&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="361"/>
+        <source>New in Toxygen v0.2.2:&lt;br&gt;Users can lock application using profile password.&lt;br&gt;Compact contact list support&lt;br&gt;Bug fixes&lt;br&gt;Tox DNS improvements</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="341"/>
+        <source>Right click on screenshot button hides app to tray during screenshot.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="349"/>
+        <source>Use Settings -&gt; Interface to customize interface.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="365"/>
+        <source>Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="mainscreen_widgets.py" line="369"/>
         <source>Set new NoSpam to avoid spam friend requests: Profile -&gt; Settings -&gt; Set new NoSpam.</source>
-        <translation>Vous pouvez empecher le spam dans les demandes de contact avec Profil -&gt; Paramètres -&gt; Nouveau NoSpam.</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="325"/>
-        <source>Delete single message in chat: make right click on spinner or message time and choose &quot;Delete&quot; in menu</source>
-        <translation>Pour supprimer un seul message dans une conversation, faites un clic droit sur l&apos;heure du message et sélectionnez &quot;Supprimer ce message&quot; dans le menu</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="328"/>
-        <source>Use right click on inline image to save it</source>
-        <translation>Pour sauvegarder une image intégrée, faites un clic droit dessus</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="316"/>
-        <source>Since v0.1.3 Toxygen supports plugins. &lt;a href=&quot;https://github.com/toxygen-project/toxygen/blob/master/docs/plugins.md&quot;&gt;Read more&lt;/a&gt;</source>
-        <translation>Depuis la version 0.1.3 Toxygen supporte les plugins. &lt;a href=&quot;https://github.com/toxygen-project/toxygen/blob/master/docs/plugins.md&quot;&gt;En savoir plus&lt;/a&gt;</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="322"/>
-        <source>New in Toxygen 0.3.0:&lt;br&gt;Video calls&lt;br&gt;Python3.6 support&lt;br&gt;Migration to PyQt5</source>
-        <translation type="obsolete">Nouveau dans Toxygen 0.3.0 : &lt;br&gt;Appels vidéo&lt;br&gt;Support de Python3.6&lt;br&gt;Migration vers PyQt5</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="322"/>
-        <source>New in Toxygen 0.4.1:&lt;br&gt;Downloading nodes from tox.chat&lt;br&gt;Bug fixes</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>audioSettingsForm</name>
     <message>
-        <location filename="menu.py" line="805"/>
+        <location filename="menu.py" line="718"/>
         <source>Audio settings</source>
         <translation>Paramètres audio</translation>
     </message>
     <message>
-        <location filename="menu.py" line="806"/>
+        <location filename="menu.py" line="719"/>
         <source>Input device:</source>
         <translation>Péripherique d&apos;entrée :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="807"/>
+        <location filename="menu.py" line="720"/>
         <source>Output device:</source>
         <translation>Péripherique de sortie :</translation>
     </message>
@@ -866,158 +676,133 @@ Version :</translation>
 <context>
     <name>incoming_call</name>
     <message>
-        <location filename="profile.py" line="1253"/>
+        <location filename="profile.py" line="1132"/>
         <source>Incoming video call</source>
         <translation>Appel vidéo entrant</translation>
     </message>
     <message>
-        <location filename="profile.py" line="1255"/>
+        <location filename="profile.py" line="1135"/>
         <source>Incoming audio call</source>
         <translation>Appel audio entrant</translation>
     </message>
     <message>
-        <location filename="profile.py" line="1236"/>
+        <location filename="profile.py" line="1115"/>
         <source>Outgoing video call</source>
-        <translation>Appel vidéo sortant</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="profile.py" line="1238"/>
+        <location filename="profile.py" line="1118"/>
         <source>Outgoing audio call</source>
-        <translation>Appel audio sortant</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="profile.py" line="1284"/>
+        <location filename="profile.py" line="1164"/>
         <source>Call declined</source>
-        <translation>Appel refusé</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="profile.py" line="1286"/>
+        <location filename="profile.py" line="1166"/>
         <source>Call finished</source>
-        <translation>Appel terminé</translation>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>interfaceForm</name>
     <message>
-        <location filename="menu.py" line="655"/>
+        <location filename="menu.py" line="619"/>
         <source>Interface settings</source>
-        <translation>Paramètres de l&apos;interface</translation>
+        <translation>Paramêtres de l&apos;interface</translation>
     </message>
     <message>
-        <location filename="menu.py" line="656"/>
+        <location filename="menu.py" line="620"/>
         <source>Theme:</source>
         <translation>Thème :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="657"/>
+        <location filename="menu.py" line="621"/>
         <source>Language:</source>
         <translation>Langue :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="658"/>
+        <location filename="menu.py" line="622"/>
         <source>Smileys</source>
-        <translation>Smileys</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="659"/>
+        <location filename="menu.py" line="623"/>
         <source>Smiley pack:</source>
-        <translation>Pack de smileys :</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="660"/>
+        <location filename="menu.py" line="624"/>
         <source>Mirror mode</source>
-        <translation>Mode miroir</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="661"/>
+        <location filename="menu.py" line="625"/>
         <source>Messages font size:</source>
-        <translation>Taille des messages :</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="754"/>
-        <source>Restart app to apply settings</source>
-        <translation>Redémarrer toxygen pour appliquer les paramètres</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="754"/>
-        <source>Restart required</source>
-        <translation>Redémarrage nécessaire</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="662"/>
-        <source>Select unread messages notification color</source>
-        <translation>Sélectionner la couleur des messages non-lus</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="663"/>
-        <source>Compact contact list</source>
-        <translation>Liste de contacts compacte</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="664"/>
-        <source>Import smiley pack</source>
-        <translation>Importer un pack de smileys</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="665"/>
-        <source>Import sticker pack</source>
-        <translation>Importer un pack de stickers</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="654"/>
-        <source>Show avatars in chat</source>
-        <translation>Montrer les avatars dans la conversation</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="666"/>
-        <source>Close to tray</source>
-        <translation>Réduire</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="menu.py" line="667"/>
-        <source>Select font</source>
-        <translation>Sélectionner la police</translation>
+        <source>Restart app to apply settings</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="menu.py" line="668"/>
+        <source>Restart required</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="menu.py" line="626"/>
+        <source>Select unread messages notification color</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="menu.py" line="627"/>
+        <source>Compact contact list</source>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>login</name>
     <message>
-        <location filename="loginscreen.py" line="70"/>
+        <location filename="loginscreen.py" line="75"/>
         <source>Log in</source>
         <translation>Se connecter</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="71"/>
+        <location filename="loginscreen.py" line="76"/>
         <source>Create</source>
         <translation>Créer</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="72"/>
+        <location filename="loginscreen.py" line="77"/>
         <source>Profile name:</source>
         <translation>Nom du profil :</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="73"/>
+        <location filename="loginscreen.py" line="78"/>
         <source>Load profile</source>
         <translation>Charger le profil</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="74"/>
+        <location filename="loginscreen.py" line="79"/>
         <source>Use as default</source>
         <translation>Utiliser par défaut</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="75"/>
+        <location filename="loginscreen.py" line="80"/>
         <source>Load existing profile</source>
         <translation>Charger un profil existant</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="76"/>
+        <location filename="loginscreen.py" line="81"/>
         <source>Create new profile</source>
         <translation>Créer un nouveau profil</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="77"/>
+        <location filename="loginscreen.py" line="82"/>
         <source>toxygen</source>
         <translation>toxygen</translation>
     </message>
@@ -1027,153 +812,110 @@ Version :</translation>
         <translation type="obsolete">Il semble qu&apos;une autre instance de Toxygen utilise ce profil ! Continuer ?</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="69"/>
+        <location filename="loginscreen.py" line="74"/>
         <source>Profile name</source>
-        <translation>Nom de profil</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="main.py" line="166"/>
+        <location filename="main.py" line="127"/>
         <source>Other instance of Toxygen uses this profile or profile was not properly closed. Continue?</source>
-        <translation>Ce profil semble être utilisé par une autre instance de toxygen ou avoir été incorrectement fermé . Continuer ?</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="113"/>
-        <source>Do you want to set profile password?</source>
-        <translation>Souhaitez vous protéger le profil par un mot de passe ?</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="124"/>
-        <source>Do you want to save profile in default folder? If no, profile will be saved in program folder</source>
-        <translation>Souhaitez vous conserver le profil dans le dossier par défaut ? Si non, il sera conservé dans le dossier du programme</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="138"/>
-        <source>Profile saving error! Does Toxygen have permission to write to this directory?</source>
-        <translation>Un problème est survenu lors de la sauvegarde du profil ! Toxygen as t&apos;il le droit d&apos;écrire dans ce dossier ?</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="288"/>
-        <source>Update for Toxygen was found. Download and install it?</source>
-        <translation>Une mise à jour est disponible. La télécharger et l&apos;installer ?</translation>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>notificationsForm</name>
     <message>
-        <location filename="menu.py" line="543"/>
+        <location filename="menu.py" line="530"/>
         <source>Notification settings</source>
-        <translation>Paramètres de notification</translation>
+        <translation>Paramêtres de notification</translation>
     </message>
     <message>
-        <location filename="menu.py" line="544"/>
+        <location filename="menu.py" line="531"/>
         <source>Enable notifications</source>
         <translation>Activer les notifications</translation>
     </message>
     <message>
-        <location filename="menu.py" line="546"/>
+        <location filename="menu.py" line="532"/>
         <source>Enable call&apos;s sound</source>
         <translation>Activer les sons d&apos;appel</translation>
     </message>
     <message>
-        <location filename="menu.py" line="547"/>
+        <location filename="menu.py" line="533"/>
         <source>Enable sound notifications</source>
         <translation>Activer les sons de notifications</translation>
     </message>
-    <message>
-        <location filename="menu.py" line="545"/>
-        <source>Notify about all messages in groups</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>pass</name>
-    <message>
-        <location filename="passwordscreen.py" line="61"/>
-        <source>Enter password</source>
-        <translation>Entrer le mot de passe</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="62"/>
-        <source>Password:</source>
-        <translation>Mot de passe :</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="63"/>
-        <source>Incorrect password</source>
-        <translation>Mot de passe incorrect</translation>
-    </message>
 </context>
 <context>
     <name>privacySettings</name>
     <message>
-        <location filename="menu.py" line="438"/>
+        <location filename="menu.py" line="426"/>
         <source>Privacy settings</source>
-        <translation>Paramètres de confidentialité</translation>
+        <translation>Paramêtres de confidentialité</translation>
     </message>
     <message>
-        <location filename="menu.py" line="439"/>
+        <location filename="menu.py" line="427"/>
         <source>Save chat history</source>
-        <translation>Sauvegarder l&apos;historique de conversation</translation>
+        <translation>Sauvegarder l&apos;historique de chat</translation>
     </message>
     <message>
-        <location filename="menu.py" line="440"/>
+        <location filename="menu.py" line="428"/>
         <source>Allow file auto accept</source>
         <translation>Autoriser les fichier automatiquement</translation>
     </message>
     <message>
-        <location filename="menu.py" line="441"/>
+        <location filename="menu.py" line="429"/>
         <source>Send typing notifications</source>
-        <translation>Informer de la frappe</translation>
+        <translation>Notifier la frappe</translation>
     </message>
     <message>
-        <location filename="menu.py" line="442"/>
+        <location filename="menu.py" line="430"/>
         <source>Auto accept default path:</source>
-        <translation>Chemin par défaut des fichiers acceptés automatiquement :</translation>
+        <translation>Chemin d&apos;accès des fichiers acceptés automatiquement :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="443"/>
+        <location filename="menu.py" line="431"/>
         <source>Change</source>
         <translation>Modifier</translation>
     </message>
     <message>
-        <location filename="menu.py" line="444"/>
+        <location filename="menu.py" line="432"/>
         <source>Allow inlines</source>
-        <translation>Activer l&apos;affichage integré</translation>
+        <translation>Activer l&apos;auto-réception</translation>
     </message>
     <message>
-        <location filename="menu.py" line="486"/>
+        <location filename="menu.py" line="477"/>
         <source>Chat history</source>
-        <translation>Historique de conversation</translation>
+        <translation>Historique de chat</translation>
     </message>
     <message>
-        <location filename="menu.py" line="488"/>
+        <location filename="menu.py" line="480"/>
         <source>History will be cleaned! Continue?</source>
-        <translation>L&apos;Historique va être vidé ! Confirmer ?</translation>
+        <translation>L&apos;Historique va être nettoyé ! Confirmer ?</translation>
     </message>
     <message>
-        <location filename="menu.py" line="446"/>
+        <location filename="menu.py" line="434"/>
         <source>Blocked users:</source>
         <translation>Utilisateurs bloqués :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="447"/>
+        <location filename="menu.py" line="435"/>
         <source>Unblock</source>
         <translation>Débloquer</translation>
     </message>
     <message>
-        <location filename="menu.py" line="448"/>
+        <location filename="menu.py" line="436"/>
         <source>Block user</source>
         <translation>Bloquer l&apos;utilisateur</translation>
     </message>
     <message>
-        <location filename="menu.py" line="460"/>
+        <location filename="menu.py" line="448"/>
         <source>Add to friend list</source>
-        <translation>Ajouter à la liste de contacts</translation>
+        <translation>Ajouter à la liste des amis</translation>
     </message>
     <message>
-        <location filename="menu.py" line="461"/>
+        <location filename="menu.py" line="449"/>
         <source>Do you want to add this user to friend list?</source>
-        <translation>Voulez vous aajouter cet utilisateur à votre liste de contacts ?</translation>
+        <translation>Voulez vous rajouter cet utilisateur à votre liste d&apos;amis ?</translation>
     </message>
     <message>
         <location filename="menu.py" line="393"/>
@@ -1181,127 +923,46 @@ Version :</translation>
         <translation type="obsolete">Bloquer l&apos;ID TOX :</translation>
     </message>
     <message>
-        <location filename="menu.py" line="445"/>
+        <location filename="menu.py" line="433"/>
         <source>Block by public key:</source>
-        <translation>Bloquer par clé publique :</translation>
+        <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="449"/>
+        <location filename="menu.py" line="437"/>
         <source>Save unsent messages only</source>
-        <translation>Sauvegarder les messages non envoyés uniquement</translation>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>tray</name>
     <message>
-        <location filename="main.py" line="224"/>
+        <location filename="main.py" line="176"/>
         <source>Open Toxygen</source>
         <translation>Ouvrir Toxygen</translation>
     </message>
     <message>
-        <location filename="main.py" line="233"/>
+        <location filename="main.py" line="185"/>
         <source>Exit</source>
         <translation>Quitter</translation>
     </message>
     <message>
-        <location filename="main.py" line="225"/>
+        <location filename="main.py" line="177"/>
         <source>Set status</source>
-        <translation>Changer le status</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="226"/>
-        <source>Online</source>
-        <translation>En ligne</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="227"/>
-        <source>Away</source>
-        <translation>Absent</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="228"/>
-        <source>Busy</source>
-        <translation>Occupé</translation>
-    </message>
-</context>
-<context>
-    <name>updateSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="1056"/>
-        <source>Update settings</source>
-        <translation>Paramètres de mise à jour</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1057"/>
-        <source>Select update mode:</source>
-        <translation>Sélectionner le mode de mise à jour :</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1058"/>
-        <source>Update Toxygen</source>
-        <translation>Mettre à jour toxygen</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1059"/>
-        <source>Disabled</source>
-        <translation>Désactivé</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1060"/>
-        <source>Manual</source>
-        <translation>Manuel</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1061"/>
-        <source>Auto</source>
-        <translation>Automatique</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1080"/>
-        <source>Error</source>
-        <translation>Erreur</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1073"/>
-        <source>Problems with internet connection</source>
-        <translation>Il y à des problèmes avec votre connexion internet</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1081"/>
-        <source>Updater not found</source>
-        <translation>Updater non trouvé</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1092"/>
-        <source>No updates found</source>
-        <translation>Pas de mises à jour trouvés</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1093"/>
-        <source>Toxygen is up to date</source>
-        <translation>Toxygen est à jour</translation>
-    </message>
-</context>
-<context>
-    <name>videoSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="887"/>
-        <source>Video settings</source>
-        <translation>Paramètres vidéo</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="888"/>
-        <source>Device:</source>
-        <translation>Périphérique :</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="867"/>
-        <source>Desktop</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="menu.py" line="889"/>
-        <source>Select region</source>
+        <location filename="main.py" line="178"/>
+        <source>Online</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.py" line="179"/>
+        <source>Away</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="main.py" line="180"/>
+        <source>Busy</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
diff --git a/toxygen/translations/ru_RU.qm b/toxygen/translations/ru_RU.qm
index 1231884..76bc9b3 100644
Binary files a/toxygen/translations/ru_RU.qm and b/toxygen/translations/ru_RU.qm differ
diff --git a/toxygen/translations/ru_RU.ts b/toxygen/translations/ru_RU.ts
index 8d6c63c..0f51862 100644
--- a/toxygen/translations/ru_RU.ts
+++ b/toxygen/translations/ru_RU.ts
@@ -1,24 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS><TS version="1.1" language="ru_RU">
+<!DOCTYPE TS>
+<TS version="2.0" language="ru_RU">
 <context>
     <name>AddContact</name>
     <message>
-        <location filename="menu.py" line="73"/>
+        <location filename="menu.py" line="70"/>
         <source>Add contact</source>
         <translation>Добавить контакт</translation>
     </message>
     <message>
-        <location filename="menu.py" line="75"/>
+        <location filename="menu.py" line="72"/>
         <source>TOX ID:</source>
         <translation>TOX ID:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="76"/>
+        <location filename="menu.py" line="73"/>
         <source>Message:</source>
         <translation>Сообщение:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="77"/>
+        <location filename="menu.py" line="74"/>
         <source>TOX ID or public key of contact</source>
         <translation>TOX ID или публичный ключ контакта</translation>
     </message>
@@ -26,7 +27,7 @@
 <context>
     <name>Callback</name>
     <message>
-        <location filename="callbacks.py" line="229"/>
+        <location filename="callbacks.py" line="183"/>
         <source>File from</source>
         <translation>Файл от</translation>
     </message>
@@ -34,32 +35,32 @@
 <context>
     <name>Form</name>
     <message>
-        <location filename="menu.py" line="74"/>
+        <location filename="menu.py" line="71"/>
         <source>Send request</source>
         <translation>Отправить запрос</translation>
     </message>
     <message>
-        <location filename="menu.py" line="345"/>
+        <location filename="menu.py" line="334"/>
         <source>IPv6</source>
         <translation>IPv6</translation>
     </message>
     <message>
-        <location filename="menu.py" line="346"/>
+        <location filename="menu.py" line="335"/>
         <source>UDP</source>
         <translation>UDP</translation>
     </message>
     <message>
-        <location filename="menu.py" line="347"/>
+        <location filename="menu.py" line="336"/>
         <source>Proxy</source>
         <translation>Прокси</translation>
     </message>
     <message>
-        <location filename="menu.py" line="348"/>
+        <location filename="menu.py" line="337"/>
         <source>IP:</source>
         <translation>IP:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="349"/>
+        <location filename="menu.py" line="338"/>
         <source>Port:</source>
         <translation>Порт:</translation>
     </message>
@@ -69,12 +70,12 @@
         <translation type="obsolete">Контакты в сети</translation>
     </message>
     <message>
-        <location filename="menu.py" line="351"/>
+        <location filename="menu.py" line="340"/>
         <source>HTTP</source>
         <translation>HTTP</translation>
     </message>
     <message>
-        <location filename="menu.py" line="353"/>
+        <location filename="menu.py" line="342"/>
         <source>WARNING:
 using proxy with enabled UDP
 can produce IP leak</source>
@@ -82,93 +83,88 @@ can produce IP leak</source>
 использование прокси с UDP
 может привести к утечке IP</translation>
     </message>
-    <message>
-        <location filename="menu.py" line="352"/>
-        <source>Download nodes list from tox.chat</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>MainWindow</name>
     <message>
-        <location filename="mainscreen.py" line="121"/>
+        <location filename="mainscreen.py" line="101"/>
         <source>Profile</source>
         <translation>Профиль</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="127"/>
+        <location filename="mainscreen.py" line="107"/>
         <source>Settings</source>
         <translation>Настройки</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="420"/>
+        <location filename="mainscreen.py" line="359"/>
         <source>About</source>
         <translation>О программе</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="119"/>
+        <location filename="mainscreen.py" line="100"/>
         <source>Add contact</source>
         <translation>Добавить контакт</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="122"/>
+        <location filename="mainscreen.py" line="102"/>
         <source>Privacy</source>
         <translation>Приватность</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="123"/>
+        <location filename="mainscreen.py" line="103"/>
         <source>Interface</source>
         <translation>Интерфейс</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="124"/>
+        <location filename="mainscreen.py" line="104"/>
         <source>Notifications</source>
         <translation>Уведомления</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="125"/>
+        <location filename="mainscreen.py" line="105"/>
         <source>Network</source>
         <translation>Сеть</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="126"/>
+        <location filename="mainscreen.py" line="106"/>
         <source>About program</source>
         <translation>О программе</translation>
     </message>
     <message>
-        <location filename="profile.py" line="854"/>
+        <location filename="profile.py" line="753"/>
         <source>User {} wants to add you to contact list. Message:
 {}</source>
         <translation>Пользователь {} хочет добавить Вас в список контактов. Сообщение:
 {}</translation>
     </message>
     <message>
-        <location filename="profile.py" line="856"/>
+        <location filename="profile.py" line="755"/>
         <source>Friend request</source>
         <translation>Запрос на добавление в друзья</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="523"/>
+        <location filename="mainscreen.py" line="430"/>
         <source>Choose file</source>
         <translation>Выберите файл</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="589"/>
+        <location filename="mainscreen.py" line="493"/>
         <source>Disallow auto accept</source>
         <translation>Запретить автоматическое получение файлов</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="590"/>
+        <location filename="mainscreen.py" line="494"/>
         <source>Allow auto accept</source>
         <translation>Разрешить автоматическое получение файлов</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="594"/>
+        <location filename="mainscreen.py" line="496"/>
         <source>Set alias</source>
         <translation>Изменить псевдоним</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="598"/>
+        <location filename="mainscreen.py" line="497"/>
         <source>Clear history</source>
         <translation>Очистить историю</translation>
     </message>
@@ -178,17 +174,17 @@ can produce IP leak</source>
         <translation type="obsolete">Копировать публичный ключ</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="609"/>
+        <location filename="mainscreen.py" line="504"/>
         <source>Remove friend</source>
         <translation>Удалить друга</translation>
     </message>
     <message>
-        <location filename="profile.py" line="694"/>
+        <location filename="profile.py" line="592"/>
         <source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
         <translation>Введите новый псевдоним для друга {} или оставьте пустым для использования его имени:</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="128"/>
+        <location filename="mainscreen.py" line="108"/>
         <source>Audio</source>
         <translation>Аудио</translation>
     </message>
@@ -198,23 +194,23 @@ can produce IP leak</source>
         <translation type="obsolete">Найти контакт</translation>
     </message>
     <message>
-        <location filename="profile.py" line="826"/>
+        <location filename="profile.py" line="725"/>
         <source>Friend added</source>
         <translation>Друг добавлен</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="421"/>
+        <location filename="mainscreen.py" line="360"/>
         <source>Toxygen is Tox client written on Python.
 Version: </source>
         <translation>Toxygen - клиент для мессенджера Tox, написанный на Python. Версия: </translation>
     </message>
     <message>
-        <location filename="profile.py" line="827"/>
+        <location filename="profile.py" line="726"/>
         <source>Friend added without sending friend request</source>
         <translation>Друг добавлен без отправки запроса на добавление в друзья</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="665"/>
+        <location filename="list_items.py" line="486"/>
         <source>Choose folder</source>
         <translation>Выбрать папку</translation>
     </message>
@@ -229,325 +225,180 @@ Version: </source>
         <translation type="obsolete">Отправить файл</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="132"/>
+        <location filename="mainscreen.py" line="110"/>
         <source>Send message</source>
         <translation>Отправить сообщение</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="133"/>
+        <location filename="mainscreen.py" line="111"/>
         <source>Start audio call with friend</source>
         <translation>Начать аудиозвонок с другом</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="625"/>
+        <location filename="mainscreen.py" line="509"/>
         <source>Plugins</source>
         <translation>Плагины</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="115"/>
+        <location filename="mainscreen.py" line="96"/>
         <source>List of plugins</source>
         <translation>Список плагинов</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="412"/>
+        <location filename="mainscreen.py" line="109"/>
         <source>Search</source>
         <translation>Поиск</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="135"/>
+        <location filename="mainscreen.py" line="113"/>
         <source>All</source>
         <translation>Все</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="136"/>
+        <location filename="mainscreen.py" line="114"/>
         <source>Online</source>
         <translation>Онлайн</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="611"/>
+        <location filename="mainscreen.py" line="505"/>
         <source>Notes</source>
         <translation>Заметки</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="650"/>
+        <location filename="mainscreen.py" line="527"/>
         <source>Notes about user</source>
         <translation>Заметки о пользователе</translation>
     </message>
     <message>
-        <location filename="widgets.py" line="124"/>
+        <location filename="widgets.py" line="83"/>
         <source>Copy link location</source>
         <translation>Копировать адрес ссылки</translation>
     </message>
     <message>
-        <location filename="widgets.py" line="126"/>
+        <location filename="widgets.py" line="86"/>
         <source>Copy</source>
         <translation>Копировать</translation>
     </message>
     <message>
-        <location filename="widgets.py" line="128"/>
+        <location filename="widgets.py" line="89"/>
         <source>Select all</source>
         <translation>Выделить всё</translation>
     </message>
     <message>
-        <location filename="widgets.py" line="130"/>
+        <location filename="widgets.py" line="92"/>
         <source>Delete</source>
         <translation>Удалить</translation>
     </message>
     <message>
-        <location filename="widgets.py" line="132"/>
+        <location filename="widgets.py" line="95"/>
         <source>Paste</source>
         <translation>Вставить</translation>
     </message>
     <message>
-        <location filename="widgets.py" line="134"/>
+        <location filename="widgets.py" line="98"/>
         <source>Cut</source>
         <translation>Вырезать</translation>
     </message>
     <message>
-        <location filename="widgets.py" line="136"/>
+        <location filename="widgets.py" line="101"/>
         <source>Undo</source>
         <translation>Отменить</translation>
     </message>
     <message>
-        <location filename="widgets.py" line="138"/>
+        <location filename="widgets.py" line="104"/>
         <source>Redo</source>
         <translation>Повторить</translation>
     </message>
     <message>
-        <location filename="widgets.py" line="159"/>
+        <location filename="widgets.py" line="125"/>
         <source>Save</source>
         <translation>Сохранить</translation>
     </message>
     <message>
-        <location filename="profile.py" line="329"/>
+        <location filename="profile.py" line="259"/>
         <source>User {} is now known as {}</source>
         <translation>Пользователь {} сейчас известен как {}</translation>
     </message>
     <message>
-        <location filename="list_items.py" line="164"/>
+        <location filename="list_items.py" line="145"/>
         <source>Delete message</source>
         <translation>Удалить сообщение</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="113"/>
+        <location filename="mainscreen.py" line="94"/>
         <source>Lock</source>
         <translation>Заблокировать</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="497"/>
+        <location filename="mainscreen.py" line="403"/>
         <source>Cannot lock app</source>
         <translation>Невозможно заблокировать приложение</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="499"/>
+        <location filename="mainscreen.py" line="406"/>
         <source>Error. Profile password is not set.</source>
         <translation>Ошибка. Пароль профиля не установлен.</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="603"/>
+        <location filename="mainscreen.py" line="499"/>
         <source>Name</source>
         <translation>Имя</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="604"/>
+        <location filename="mainscreen.py" line="500"/>
         <source>Status message</source>
         <translation>Статус</translation>
     </message>
     <message>
-        <location filename="mainscreen.py" line="606"/>
+        <location filename="mainscreen.py" line="501"/>
         <source>Public key</source>
         <translation>Публичный ключ</translation>
     </message>
-    <message>
-        <location filename="main.py" line="101"/>
-        <source>Error</source>
-        <translation>Ошибка</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="103"/>
-        <source>Profile with this name already exists</source>
-        <translation>Профиль с данным именем уже существует</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="672"/>
-        <source>Choose folder with sticker pack</source>
-        <translation>Выберите папку в паком стикеров</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="684"/>
-        <source>Choose folder with smiley pack</source>
-        <translation>Выберите папку с паком смайлов</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="144"/>
-        <source>Import plugin</source>
-        <translation>Импортировать плагин</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="476"/>
-        <source>Choose folder with plugin</source>
-        <translation>Выберите папку с плагином</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="485"/>
-        <source>Restart Toxygen</source>
-        <translation>Перезапустите Toxygen</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="487"/>
-        <source>Plugin will be loaded after restart</source>
-        <translation>Плагин будет загружен после перезапуска</translation>
-    </message>
-    <message>
-        <location filename="list_items.py" line="45"/>
-        <source>Quote selected text</source>
-        <translation>Цитировать выбранный текст</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="597"/>
-        <source>Chat history</source>
-        <translation>История чата</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="599"/>
-        <source>Export as text</source>
-        <translation>Экспортировать как текст</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="600"/>
-        <source>Export as HTML</source>
-        <translation>Экспортировать как HTML</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="130"/>
-        <source>Updates</source>
-        <translation>Обновления</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="137"/>
-        <source>Online first</source>
-        <translation>Сначала онлайн</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="139"/>
-        <source>Online and by name</source>
-        <translation>Онлайн и по имени</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="140"/>
-        <source>Online first and by name</source>
-        <translation>Сначала онлайн и по имени</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="610"/>
-        <source>Block friend</source>
-        <translation>Заблокировать друга</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="474"/>
-        <source>Not found</source>
-        <translation>Не найдено</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="472"/>
-        <source>Text &quot;{}&quot; was not found</source>
-        <translation>Текст &quot;{}&quot; не был найден</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="145"/>
-        <source>Reload plugins</source>
-        <translation>Перезагрузить плагины</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="129"/>
-        <source>Video</source>
-        <translation>Видео</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1337"/>
-        <source>User {} invites you to group chat. Accept?</source>
-        <translation>Пользователь {} приглашает Вас в групповой чат. Принять приглашение?</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1338"/>
-        <source>Group chat invite</source>
-        <translation>Приглашение в групповой чат</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1374"/>
-        <source>{} users in chat</source>
-        <translation>{} пользователей в чате</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1395"/>
-        <source>Enter new title for group {}:</source>
-        <translation>Введите название для группы {}:</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="634"/>
-        <source>Set title</source>
-        <translation>Изменить название</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="120"/>
-        <source>Create group chat</source>
-        <translation>Создать групповой чат</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="615"/>
-        <source>Invite to group chat</source>
-        <translation>Пригласить в групповой чат</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="633"/>
-        <source>Leave chat</source>
-        <translation>Покинуть чат</translation>
-    </message>
 </context>
 <context>
     <name>MenuWindow</name>
     <message>
         <location filename="avwidgets.py" line="121"/>
         <source>Send audio message to friend {}</source>
-        <translation type="obsolete">Отправить аудиосообщение другу</translation>
+        <translation>Отправить аудиосообщение другу</translation>
     </message>
     <message>
         <location filename="avwidgets.py" line="126"/>
         <source>Start recording</source>
-        <translation type="obsolete">Начать запись</translation>
+        <translation>Начать запись</translation>
     </message>
     <message>
         <location filename="avwidgets.py" line="135"/>
         <source>Stop recording</source>
-        <translation type="obsolete">Остановить запись</translation>
+        <translation>Остановить запись</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="217"/>
+        <location filename="mainscreen_widgets.py" line="248"/>
         <source>Send screenshot</source>
         <translation>Отправить снимок экрана</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="218"/>
+        <location filename="mainscreen_widgets.py" line="249"/>
         <source>Send file</source>
         <translation>Отправить файл</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="259"/>
+        <location filename="mainscreen_widgets.py" line="250"/>
         <source>Send audio message</source>
-        <translation type="obsolete">Отправить аудиосообщение</translation>
+        <translation>Отправить аудиосообщение</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="260"/>
+        <location filename="mainscreen_widgets.py" line="251"/>
         <source>Send video message</source>
-        <translation type="obsolete">Отправить видеосообщение</translation>
+        <translation>Отправить видеосообщение</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="219"/>
+        <location filename="mainscreen_widgets.py" line="252"/>
         <source>Add smiley</source>
         <translation>Добавить смайлик</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="220"/>
+        <location filename="mainscreen_widgets.py" line="253"/>
         <source>Send sticker</source>
         <translation>Отправить стикер</translation>
     </message>
@@ -555,63 +406,25 @@ Version: </source>
 <context>
     <name>NetworkSettings</name>
     <message>
-        <location filename="menu.py" line="344"/>
+        <location filename="menu.py" line="333"/>
         <source>Network settings</source>
         <translation>Настройки сети</translation>
     </message>
     <message>
-        <location filename="menu.py" line="350"/>
+        <location filename="menu.py" line="339"/>
         <source>Restart TOX core</source>
         <translation>Перезапустить ядро TOX</translation>
     </message>
 </context>
-<context>
-    <name>PasswordScreen</name>
-    <message>
-        <location filename="passwordscreen.py" line="132"/>
-        <source>Profile password</source>
-        <translation>Пароль профиля</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="134"/>
-        <source>Password (at least 8 symbols)</source>
-        <translation>Пароль (минимум 8 символов)</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="136"/>
-        <source>Confirm password</source>
-        <translation>Подтверждение пароля</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="138"/>
-        <source>Set password</source>
-        <translation>Изменить пароль</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="153"/>
-        <source>Passwords do not match</source>
-        <translation>Пароли не совпадают</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="141"/>
-        <source>There is no way to recover lost passwords</source>
-        <translation>Восстановление забытых паролей не поддерживается</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="150"/>
-        <source>Password must be at least 8 symbols</source>
-        <translation>Пароль должен быть длиной не менее 8 символов</translation>
-    </message>
-</context>
 <context>
     <name>PluginWindow</name>
     <message>
-        <location filename="plugins/plugin_super_class.py" line="139"/>
+        <location filename="plugins/plugin_super_class.py" line="132"/>
         <source>List of commands for plugin {}</source>
         <translation>Список команд для плагина {}</translation>
     </message>
     <message>
-        <location filename="plugins/plugin_super_class.py" line="140"/>
+        <location filename="plugins/plugin_super_class.py" line="133"/>
         <source>No commands available</source>
         <translation>Команды не найдены</translation>
     </message>
@@ -619,42 +432,42 @@ Version: </source>
 <context>
     <name>PluginsForm</name>
     <message>
-        <location filename="menu.py" line="972"/>
+        <location filename="menu.py" line="761"/>
         <source>Plugins</source>
         <translation>Плагины</translation>
     </message>
     <message>
-        <location filename="menu.py" line="973"/>
+        <location filename="menu.py" line="762"/>
         <source>Open selected plugin</source>
         <translation>Открыть выбранный плагин</translation>
     </message>
     <message>
-        <location filename="menu.py" line="985"/>
+        <location filename="menu.py" line="775"/>
         <source>No GUI found for this plugin</source>
         <translation>GUI для данного плагина не найден</translation>
     </message>
     <message>
-        <location filename="menu.py" line="1000"/>
+        <location filename="menu.py" line="791"/>
         <source>No description available</source>
         <translation>Описание недоступно</translation>
     </message>
     <message>
-        <location filename="menu.py" line="1016"/>
+        <location filename="menu.py" line="807"/>
         <source>Disable plugin</source>
         <translation>Отключить плагин</translation>
     </message>
     <message>
-        <location filename="menu.py" line="1018"/>
+        <location filename="menu.py" line="809"/>
         <source>Enable plugin</source>
         <translation>Включить плагин</translation>
     </message>
     <message>
-        <location filename="menu.py" line="1008"/>
+        <location filename="menu.py" line="799"/>
         <source>No plugins found</source>
         <translation>Плагины не найдены</translation>
     </message>
     <message>
-        <location filename="menu.py" line="985"/>
+        <location filename="menu.py" line="776"/>
         <source>Error</source>
         <translation>Ошибка</translation>
     </message>
@@ -662,32 +475,32 @@ Version: </source>
 <context>
     <name>ProfileSettingsForm</name>
     <message>
-        <location filename="menu.py" line="173"/>
+        <location filename="menu.py" line="169"/>
         <source>Export profile</source>
         <translation>Экспорт профиля</translation>
     </message>
     <message>
-        <location filename="menu.py" line="174"/>
+        <location filename="menu.py" line="170"/>
         <source>Profile settings</source>
         <translation>Настройки профиля</translation>
     </message>
     <message>
-        <location filename="menu.py" line="175"/>
+        <location filename="menu.py" line="171"/>
         <source>Name:</source>
         <translation>Имя:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="176"/>
+        <location filename="menu.py" line="172"/>
         <source>Status:</source>
         <translation>Статус:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="177"/>
+        <location filename="menu.py" line="173"/>
         <source>TOX ID:</source>
         <translation>TOX ID:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="178"/>
+        <location filename="menu.py" line="174"/>
         <source>Copy TOX ID</source>
         <translation>Копировать TOX ID</translation>
     </message>
@@ -697,37 +510,37 @@ Version: </source>
         <translation type="obsolete">Язык:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="179"/>
+        <location filename="menu.py" line="175"/>
         <source>New avatar</source>
         <translation>Новый аватар</translation>
     </message>
     <message>
-        <location filename="menu.py" line="180"/>
+        <location filename="menu.py" line="176"/>
         <source>Reset avatar</source>
         <translation>Сбросить аватар</translation>
     </message>
     <message>
-        <location filename="menu.py" line="181"/>
+        <location filename="menu.py" line="177"/>
         <source>New NoSpam</source>
         <translation>Новый NoSpam</translation>
     </message>
     <message>
-        <location filename="menu.py" line="182"/>
+        <location filename="menu.py" line="178"/>
         <source>Profile password</source>
         <translation>Пароль профиля</translation>
     </message>
     <message>
-        <location filename="menu.py" line="183"/>
+        <location filename="menu.py" line="179"/>
         <source>Password (at least 8 symbols)</source>
         <translation>Пароль (минимум 8 символов)</translation>
     </message>
     <message>
-        <location filename="menu.py" line="184"/>
+        <location filename="menu.py" line="180"/>
         <source>Confirm password</source>
         <translation>Подтверждение пароля</translation>
     </message>
     <message>
-        <location filename="menu.py" line="185"/>
+        <location filename="menu.py" line="181"/>
         <source>Set password</source>
         <translation>Изменить пароль</translation>
     </message>
@@ -737,17 +550,17 @@ Version: </source>
         <translation>Пароли не совпадают</translation>
     </message>
     <message>
-        <location filename="menu.py" line="187"/>
+        <location filename="menu.py" line="183"/>
         <source>Leaving blank will reset current password</source>
         <translation>Пустое поле сбросит текущий пароль</translation>
     </message>
     <message>
-        <location filename="menu.py" line="188"/>
+        <location filename="menu.py" line="184"/>
         <source>There is no way to recover lost passwords</source>
         <translation>Восстановление забытых паролей не поддерживается</translation>
     </message>
     <message>
-        <location filename="menu.py" line="218"/>
+        <location filename="menu.py" line="217"/>
         <source>Password must be at least 8 symbols</source>
         <translation>Пароль должен быть длиной не менее 8 символов</translation>
     </message>
@@ -757,60 +570,50 @@ Version: </source>
         <translation>Выбрать аватар</translation>
     </message>
     <message>
-        <location filename="menu.py" line="189"/>
+        <location filename="menu.py" line="185"/>
         <source>Online</source>
         <translation>Онлайн</translation>
     </message>
     <message>
-        <location filename="menu.py" line="190"/>
+        <location filename="menu.py" line="186"/>
         <source>Away</source>
         <translation>Нет на месте</translation>
     </message>
     <message>
-        <location filename="menu.py" line="191"/>
+        <location filename="menu.py" line="187"/>
         <source>Busy</source>
         <translation>Занят</translation>
     </message>
     <message>
-        <location filename="menu.py" line="205"/>
+        <location filename="menu.py" line="202"/>
         <source>Mark as not default profile</source>
         <translation>Отключить автозагрузку профиля</translation>
     </message>
     <message>
-        <location filename="menu.py" line="208"/>
+        <location filename="menu.py" line="206"/>
         <source>Mark as default profile</source>
         <translation>Сделать профилем по умолчанию</translation>
     </message>
     <message>
-        <location filename="menu.py" line="192"/>
+        <location filename="menu.py" line="188"/>
         <source>Copy public key</source>
         <translation>Копировать публичный ключ</translation>
     </message>
-    <message>
-        <location filename="menu.py" line="269"/>
-        <source>Use new path</source>
-        <translation>Использовать новый путь</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="271"/>
-        <source>Do you want to move your profile to this location?</source>
-        <translation>Вы хотите переместить ваш профиль в эту папку?</translation>
-    </message>
 </context>
 <context>
     <name>WelcomeScreen</name>
     <message>
-        <location filename="mainscreen_widgets.py" line="295"/>
+        <location filename="mainscreen_widgets.py" line="329"/>
         <source>Don&apos;t show again</source>
         <translation>Не показывать снова</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="296"/>
+        <location filename="mainscreen_widgets.py" line="331"/>
         <source>Tip of the day</source>
         <translation>Подсказка дня</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="301"/>
+        <location filename="mainscreen_widgets.py" line="337"/>
         <source>Press Esc if you want hide app to tray.</source>
         <translation>Нажатие Esc сворачивает приложение в трей.</translation>
     </message>
@@ -820,7 +623,7 @@ Version: </source>
         <translation type="obsolete">Правый клик на кнопке скриншота сворачивает приложение в трей на время скриншота</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="307"/>
+        <location filename="mainscreen_widgets.py" line="345"/>
         <source>You can use Tox over Tor. For more info read &lt;a href=&quot;https://wiki.tox.chat/users/tox_over_tor_tot&quot;&gt;this post&lt;/a&gt;</source>
         <translation>Вы можете использовать Tox через Tor. Дополнительная информация &lt;a href=&quot;https://wiki.tox.chat/users/tox_over_tor_tot&quot;&gt;тут&lt;/a&gt;</translation>
     </message>
@@ -830,19 +633,19 @@ Version: </source>
         <translation type="obsolete">Используйте Настройки -&gt; Интерфейс для настройки интерфейса</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="313"/>
+        <location filename="mainscreen_widgets.py" line="353"/>
         <source>Set profile password via Profile -&gt; Settings. Password allows Toxygen encrypt your history and settings.</source>
         <translation>Установите пароль профиля: Профиль -&gt; Настройки. Пароль позволяет шифровать историю переписки и настройки.</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="366"/>
+        <location filename="mainscreen_widgets.py" line="357"/>
         <source>Since v0.1.3 Toxygen supports plugins. &lt;a href=&quot;https://github.com/xveduk/toxygen/blob/master/docs/plugins.md&quot;&gt;Read more&lt;/a&gt;</source>
-        <translation type="obsolete">С версии 0.1.3 Toxygen поддерживает плагины. &lt;a href=&quot;https://github.com/xveduk/toxygen/blob/master/docs/plugins.md&quot;&gt;Узнать больше.&lt;/a&gt;</translation>
+        <translation>С версии 0.1.3 Toxygen поддерживает плагины. &lt;a href=&quot;https://github.com/xveduk/toxygen/blob/master/docs/plugins.md&quot;&gt;Узнать больше.&lt;/a&gt;</translation>
     </message>
     <message>
         <location filename="mainscreen_widgets.py" line="361"/>
         <source>New in Toxygen v0.2.2:&lt;br&gt;Users can lock application using profile password.&lt;br&gt;Compact contact list support&lt;br&gt;Bug fixes&lt;br&gt;Tox DNS improvements</source>
-        <translation type="obsolete">С версии 0.1.3 Toxygen поддерживает плагины. &lt;a href=&quot;https://github.com/xveduk/toxygen/blob/master/docs/plugins.md&quot;&gt;Узнать больше.&lt;/a&gt;</translation>
+        <translation>С версии 0.1.3 Toxygen поддерживает плагины. &lt;a href=&quot;https://github.com/xveduk/toxygen/blob/master/docs/plugins.md&quot;&gt;Узнать больше.&lt;/a&gt;</translation>
     </message>
     <message>
         <location filename="mainscreen_widgets.py" line="367"/>
@@ -855,80 +658,40 @@ Version: </source>
         <translation type="obsolete">Установите новый NoSpam, чтобы избежать спам запросов в друзья: Профиль-&gt;Настройки-&gt;Новый NoSpam</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="304"/>
+        <location filename="mainscreen_widgets.py" line="341"/>
         <source>Right click on screenshot button hides app to tray during screenshot.</source>
         <translation>Правый клик на кнопке скриншота сворачивает приложение в трей на время скриншота.</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="310"/>
+        <location filename="mainscreen_widgets.py" line="349"/>
         <source>Use Settings -&gt; Interface to customize interface.</source>
         <translation>Используйте Настройки -&gt; Интерфейс для настройки интерфейса.</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="319"/>
+        <location filename="mainscreen_widgets.py" line="365"/>
         <source>Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.</source>
         <translation>Toxygen поддерживает псевдооффлайн сообщения и файл трансферы.</translation>
     </message>
     <message>
-        <location filename="mainscreen_widgets.py" line="331"/>
+        <location filename="mainscreen_widgets.py" line="369"/>
         <source>Set new NoSpam to avoid spam friend requests: Profile -&gt; Settings -&gt; Set new NoSpam.</source>
         <translation>Установите новый NoSpam, чтобы избежать спам запросов в друзья: Профиль-&gt;Настройки-&gt;Новый NoSpam.</translation>
     </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="361"/>
-        <source>New in Toxygen v0.2.3:&lt;br&gt;TCS compliance&lt;br&gt;Plugins, smileys and stickers import&lt;br&gt;Bug fixes</source>
-        <translation type="obsolete">Новое в Toxygen 0.2.3:&lt;br&gt;Соответствие TCS&lt;br&gt;Импорт плагинов, смайлов и стикеров&lt;br&gt;Исправления ошибок</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="325"/>
-        <source>Delete single message in chat: make right click on spinner or message time and choose &quot;Delete&quot; in menu</source>
-        <translation>Чтобы удалить отдельное сообщение в чате сделайте правый клик на спиннер или время сообщения и выберите &quot;Удалить&quot; в меню</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="328"/>
-        <source>Use right click on inline image to save it</source>
-        <translation>Правый клик на инлайн изображении позволит сохранить его</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="370"/>
-        <source>New in Toxygen v0.2.4:&lt;br&gt;File transfers update&lt;br&gt;Autoreconnection&lt;br&gt;Improvements&lt;br&gt;Bug fixes</source>
-        <translation type="obsolete">Новое в Toxygen v0.2.4:&lt;br&gt;Передача файлов обновлена&lt;br&gt;Автопереподключение&lt;br&gt;Улучшения&lt;br&gt;Исправления ошибок</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="370"/>
-        <source>New in Toxygen v0.2.6:&lt;br&gt;Updater&lt;br&gt;Better contact sorting&lt;br&gt;Plugins improvements</source>
-        <translation type="obsolete">Новое в Toxygen v0.2.6:&lt;br&gt;Поддержка обновлений&lt;br&gt;Улучшенная сортировка контактов&lt;br&gt;Улучшения в работе плагинов</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="316"/>
-        <source>Since v0.1.3 Toxygen supports plugins. &lt;a href=&quot;https://github.com/toxygen-project/toxygen/blob/master/docs/plugins.md&quot;&gt;Read more&lt;/a&gt;</source>
-        <translation>С версии 0.1.3 Toxygen поддерживает плагины. &lt;a href=&quot;https://github.com/toxygen-project/toxygen/blob/master/docs/plugins.md&quot;&gt;Узнать больше.&lt;/a&gt;</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="322"/>
-        <source>New in Toxygen 0.3.0:&lt;br&gt;Video calls&lt;br&gt;Python3.6 support&lt;br&gt;Migration to PyQt5</source>
-        <translation type="obsolete">Новое в Toxygen 0.3.0:&lt;br&gt;Видеозвонки&lt;br&gt;Поддержка Python3.6&lt;br&gt;Миграция на PyQt5</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="322"/>
-        <source>New in Toxygen 0.4.1:&lt;br&gt;Downloading nodes from tox.chat&lt;br&gt;Bug fixes</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>audioSettingsForm</name>
     <message>
-        <location filename="menu.py" line="805"/>
+        <location filename="menu.py" line="718"/>
         <source>Audio settings</source>
         <translation>Настройки аудио</translation>
     </message>
     <message>
-        <location filename="menu.py" line="806"/>
+        <location filename="menu.py" line="719"/>
         <source>Input device:</source>
         <translation>Устройство ввода:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="807"/>
+        <location filename="menu.py" line="720"/>
         <source>Output device:</source>
         <translation>Устройство вывода:</translation>
     </message>
@@ -936,32 +699,32 @@ Version: </source>
 <context>
     <name>incoming_call</name>
     <message>
-        <location filename="profile.py" line="1253"/>
+        <location filename="profile.py" line="1132"/>
         <source>Incoming video call</source>
         <translation>Входящий видеозвонок</translation>
     </message>
     <message>
-        <location filename="profile.py" line="1255"/>
+        <location filename="profile.py" line="1135"/>
         <source>Incoming audio call</source>
         <translation>Входящий аудиозвонок</translation>
     </message>
     <message>
-        <location filename="profile.py" line="1236"/>
+        <location filename="profile.py" line="1115"/>
         <source>Outgoing video call</source>
         <translation>Исходящий видеозвонок</translation>
     </message>
     <message>
-        <location filename="profile.py" line="1238"/>
+        <location filename="profile.py" line="1118"/>
         <source>Outgoing audio call</source>
         <translation>Исходящий аудиозвонок</translation>
     </message>
     <message>
-        <location filename="profile.py" line="1284"/>
+        <location filename="profile.py" line="1164"/>
         <source>Call declined</source>
         <translation>Звонок отменен</translation>
     </message>
     <message>
-        <location filename="profile.py" line="1286"/>
+        <location filename="profile.py" line="1166"/>
         <source>Call finished</source>
         <translation>Звонок завершен</translation>
     </message>
@@ -969,125 +732,100 @@ Version: </source>
 <context>
     <name>interfaceForm</name>
     <message>
-        <location filename="menu.py" line="655"/>
+        <location filename="menu.py" line="619"/>
         <source>Interface settings</source>
         <translation>Настройки интерфейса</translation>
     </message>
     <message>
-        <location filename="menu.py" line="656"/>
+        <location filename="menu.py" line="620"/>
         <source>Theme:</source>
         <translation>Тема:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="657"/>
+        <location filename="menu.py" line="621"/>
         <source>Language:</source>
         <translation>Язык:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="658"/>
+        <location filename="menu.py" line="622"/>
         <source>Smileys</source>
         <translation>Смайлики</translation>
     </message>
     <message>
-        <location filename="menu.py" line="659"/>
+        <location filename="menu.py" line="623"/>
         <source>Smiley pack:</source>
         <translation>Набор смайликов:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="660"/>
+        <location filename="menu.py" line="624"/>
         <source>Mirror mode</source>
         <translation>Зеркальный режим</translation>
     </message>
     <message>
-        <location filename="menu.py" line="661"/>
+        <location filename="menu.py" line="625"/>
         <source>Messages font size:</source>
         <translation>Размер шрифта сообщений:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="754"/>
+        <location filename="menu.py" line="667"/>
         <source>Restart app to apply settings</source>
         <translation>Для применения настроек необходимо перезапустить приложение</translation>
     </message>
     <message>
-        <location filename="menu.py" line="754"/>
+        <location filename="menu.py" line="668"/>
         <source>Restart required</source>
         <translation>Требуется перезапуск</translation>
     </message>
     <message>
-        <location filename="menu.py" line="662"/>
+        <location filename="menu.py" line="626"/>
         <source>Select unread messages notification color</source>
         <translation>Цвет уведомления о сообщении</translation>
     </message>
     <message>
-        <location filename="menu.py" line="663"/>
+        <location filename="menu.py" line="627"/>
         <source>Compact contact list</source>
         <translation>Компактный список контактов</translation>
     </message>
-    <message>
-        <location filename="menu.py" line="664"/>
-        <source>Import smiley pack</source>
-        <translation>Импортировать смайлы</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="665"/>
-        <source>Import sticker pack</source>
-        <translation>Импортировать стикеры</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="654"/>
-        <source>Show avatars in chat</source>
-        <translation>Показывать аватары в чате</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="666"/>
-        <source>Close to tray</source>
-        <translation>Сворачивать в трей</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="667"/>
-        <source>Select font</source>
-        <translation>Выбрать шрифт</translation>
-    </message>
 </context>
 <context>
     <name>login</name>
     <message>
-        <location filename="loginscreen.py" line="70"/>
+        <location filename="loginscreen.py" line="75"/>
         <source>Log in</source>
         <translation>Вход</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="71"/>
+        <location filename="loginscreen.py" line="76"/>
         <source>Create</source>
         <translation>Создать</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="72"/>
+        <location filename="loginscreen.py" line="77"/>
         <source>Profile name:</source>
         <translation>Имя профиля:</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="73"/>
+        <location filename="loginscreen.py" line="78"/>
         <source>Load profile</source>
         <translation>Загрузить профиль</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="74"/>
+        <location filename="loginscreen.py" line="79"/>
         <source>Use as default</source>
         <translation>По умолчанию</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="75"/>
+        <location filename="loginscreen.py" line="80"/>
         <source>Load existing profile</source>
         <translation>Загрузить профиль</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="76"/>
+        <location filename="loginscreen.py" line="81"/>
         <source>Create new profile</source>
         <translation>Создать новый профиль</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="77"/>
+        <location filename="loginscreen.py" line="82"/>
         <source>toxygen</source>
         <translation>toxygen</translation>
     </message>
@@ -1097,152 +835,109 @@ Version: </source>
         <translation type="obsolete">Похоже, что этот профиль используется другим экземпляром Toxygen! Продолжить?</translation>
     </message>
     <message>
-        <location filename="loginscreen.py" line="69"/>
+        <location filename="loginscreen.py" line="74"/>
         <source>Profile name</source>
         <translation>Имя профиля</translation>
     </message>
     <message>
-        <location filename="main.py" line="166"/>
+        <location filename="main.py" line="127"/>
         <source>Other instance of Toxygen uses this profile or profile was not properly closed. Continue?</source>
         <translation>Этот профиль используется другим экземпляром Toxygen или не был правильно закрыт. Продолжить?</translation>
     </message>
-    <message>
-        <location filename="main.py" line="113"/>
-        <source>Do you want to set profile password?</source>
-        <translation>Хотите ли вы установить пароль профиля?</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="124"/>
-        <source>Do you want to save profile in default folder? If no, profile will be saved in program folder</source>
-        <translation>Вы хотите сохранить профиль в папку по умолчанию? Если нет, профиль будет сохранен в папке с программой</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="138"/>
-        <source>Profile saving error! Does Toxygen have permission to write to this directory?</source>
-        <translation>Ошибка сохранения профиля! Toxygen имеет разрешение на запись в данную папку?</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="288"/>
-        <source>Update for Toxygen was found. Download and install it?</source>
-        <translation>Обновление для Toxygen было найдено. Загрузить и установить его?</translation>
-    </message>
 </context>
 <context>
     <name>notificationsForm</name>
     <message>
-        <location filename="menu.py" line="543"/>
+        <location filename="menu.py" line="530"/>
         <source>Notification settings</source>
         <translation>Настройки уведомлений</translation>
     </message>
     <message>
-        <location filename="menu.py" line="544"/>
+        <location filename="menu.py" line="531"/>
         <source>Enable notifications</source>
         <translation>Включить уведомления</translation>
     </message>
     <message>
-        <location filename="menu.py" line="546"/>
+        <location filename="menu.py" line="532"/>
         <source>Enable call&apos;s sound</source>
         <translation>Включить звук звонка</translation>
     </message>
     <message>
-        <location filename="menu.py" line="547"/>
+        <location filename="menu.py" line="533"/>
         <source>Enable sound notifications</source>
         <translation>Включить звуковые уведомления
 </translation>
     </message>
-    <message>
-        <location filename="menu.py" line="545"/>
-        <source>Notify about all messages in groups</source>
-        <translation>Уведомлять обо всех сообщениях в группах</translation>
-    </message>
-</context>
-<context>
-    <name>pass</name>
-    <message>
-        <location filename="passwordscreen.py" line="61"/>
-        <source>Enter password</source>
-        <translation>Введите пароль</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="62"/>
-        <source>Password:</source>
-        <translation>Пароль:</translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="63"/>
-        <source>Incorrect password</source>
-        <translation>Неверный пароль</translation>
-    </message>
 </context>
 <context>
     <name>privacySettings</name>
     <message>
-        <location filename="menu.py" line="438"/>
+        <location filename="menu.py" line="426"/>
         <source>Privacy settings</source>
         <translation>Настройки приватности</translation>
     </message>
     <message>
-        <location filename="menu.py" line="439"/>
+        <location filename="menu.py" line="427"/>
         <source>Save chat history</source>
         <translation>Сохранять историю переписки</translation>
     </message>
     <message>
-        <location filename="menu.py" line="440"/>
+        <location filename="menu.py" line="428"/>
         <source>Allow file auto accept</source>
         <translation>Разрешить автополучение файлов</translation>
     </message>
     <message>
-        <location filename="menu.py" line="441"/>
+        <location filename="menu.py" line="429"/>
         <source>Send typing notifications</source>
         <translation>Посылать уведомления о наборе текста</translation>
     </message>
     <message>
-        <location filename="menu.py" line="442"/>
+        <location filename="menu.py" line="430"/>
         <source>Auto accept default path:</source>
         <translation>Путь автоприема файлов:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="443"/>
+        <location filename="menu.py" line="431"/>
         <source>Change</source>
         <translation>Изменить</translation>
     </message>
     <message>
-        <location filename="menu.py" line="444"/>
+        <location filename="menu.py" line="432"/>
         <source>Allow inlines</source>
         <translation>Разрешать инлайны</translation>
     </message>
     <message>
-        <location filename="menu.py" line="486"/>
+        <location filename="menu.py" line="477"/>
         <source>Chat history</source>
         <translation>История чата</translation>
     </message>
     <message>
-        <location filename="menu.py" line="488"/>
+        <location filename="menu.py" line="480"/>
         <source>History will be cleaned! Continue?</source>
         <translation>История переписки будет очищена! Продолжить?</translation>
     </message>
     <message>
-        <location filename="menu.py" line="446"/>
+        <location filename="menu.py" line="434"/>
         <source>Blocked users:</source>
         <translation>Заблокированные пользователи:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="447"/>
+        <location filename="menu.py" line="435"/>
         <source>Unblock</source>
         <translation>Разблокировать</translation>
     </message>
     <message>
-        <location filename="menu.py" line="448"/>
+        <location filename="menu.py" line="436"/>
         <source>Block user</source>
         <translation>Заблокировать пользователя</translation>
     </message>
     <message>
-        <location filename="menu.py" line="460"/>
+        <location filename="menu.py" line="448"/>
         <source>Add to friend list</source>
         <translation>Добавить в список друзей</translation>
     </message>
     <message>
-        <location filename="menu.py" line="461"/>
+        <location filename="menu.py" line="449"/>
         <source>Do you want to add this user to friend list?</source>
         <translation>Добавить этого пользователя в список друзей?</translation>
     </message>
@@ -1252,12 +947,12 @@ Version: </source>
         <translation type="obsolete">Блокировать по TOX ID:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="445"/>
+        <location filename="menu.py" line="433"/>
         <source>Block by public key:</source>
         <translation>Блокировать по публичному ключу:</translation>
     </message>
     <message>
-        <location filename="menu.py" line="449"/>
+        <location filename="menu.py" line="437"/>
         <source>Save unsent messages only</source>
         <translation>Сохранять только неотправленные сообщения</translation>
     </message>
@@ -1265,115 +960,34 @@ Version: </source>
 <context>
     <name>tray</name>
     <message>
-        <location filename="main.py" line="224"/>
+        <location filename="main.py" line="176"/>
         <source>Open Toxygen</source>
         <translation>Открыть Toxygen</translation>
     </message>
     <message>
-        <location filename="main.py" line="233"/>
+        <location filename="main.py" line="185"/>
         <source>Exit</source>
         <translation>Выход</translation>
     </message>
     <message>
-        <location filename="main.py" line="225"/>
+        <location filename="main.py" line="177"/>
         <source>Set status</source>
         <translation>Изменить статус</translation>
     </message>
     <message>
-        <location filename="main.py" line="226"/>
+        <location filename="main.py" line="178"/>
         <source>Online</source>
         <translation>Онлайн</translation>
     </message>
     <message>
-        <location filename="main.py" line="227"/>
+        <location filename="main.py" line="179"/>
         <source>Away</source>
         <translation>Нет на месте</translation>
     </message>
     <message>
-        <location filename="main.py" line="228"/>
+        <location filename="main.py" line="180"/>
         <source>Busy</source>
         <translation>Занят</translation>
     </message>
 </context>
-<context>
-    <name>updateSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="1056"/>
-        <source>Update settings</source>
-        <translation>Обновить настройки</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1057"/>
-        <source>Select update mode:</source>
-        <translation>Выбрать режим обновлений:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1058"/>
-        <source>Update Toxygen</source>
-        <translation>Обновить Toxygen</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1059"/>
-        <source>Disabled</source>
-        <translation>Отключены</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1060"/>
-        <source>Manual</source>
-        <translation>Вручную</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1061"/>
-        <source>Auto</source>
-        <translation>Автоматически</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1080"/>
-        <source>Error</source>
-        <translation>Ошибка</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1073"/>
-        <source>Problems with internet connection</source>
-        <translation>Проблемы с соединением</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1081"/>
-        <source>Updater not found</source>
-        <translation>Апдейтер не был найден</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1092"/>
-        <source>No updates found</source>
-        <translation>Обновления не найдены</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1093"/>
-        <source>Toxygen is up to date</source>
-        <translation>Toxygen уже обновлен</translation>
-    </message>
-</context>
-<context>
-    <name>videoSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="887"/>
-        <source>Video settings</source>
-        <translation>Настройки видео</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="888"/>
-        <source>Device:</source>
-        <translation>Устройство:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="867"/>
-        <source>Desktop</source>
-        <translation>Рабочий стол</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="889"/>
-        <source>Select region</source>
-        <translation>Выберите область</translation>
-    </message>
-</context>
 </TS>
diff --git a/toxygen/translations/uk_UA.qm b/toxygen/translations/uk_UA.qm
deleted file mode 100644
index a4082ef..0000000
Binary files a/toxygen/translations/uk_UA.qm and /dev/null differ
diff --git a/toxygen/translations/uk_UA.ts b/toxygen/translations/uk_UA.ts
deleted file mode 100644
index c36ecf0..0000000
--- a/toxygen/translations/uk_UA.ts
+++ /dev/null
@@ -1,1297 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE TS><TS version="1.1" language="uk_UA">
-<context>
-    <name>AddContact</name>
-    <message>
-        <location filename="menu.py" line="75"/>
-        <source>TOX ID:</source>
-        <translation>TOX ID:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="73"/>
-        <source>Add contact</source>
-        <translation>Додати контакт</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="76"/>
-        <source>Message:</source>
-        <translation>Повідомлення:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="77"/>
-        <source>TOX ID or public key of contact</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>Callback</name>
-    <message>
-        <location filename="callbacks.py" line="229"/>
-        <source>File from</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>Form</name>
-    <message>
-        <location filename="menu.py" line="348"/>
-        <source>IP:</source>
-        <translation>IP:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="346"/>
-        <source>UDP</source>
-        <translation>UDP</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="351"/>
-        <source>HTTP</source>
-        <translation>HTTP</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="345"/>
-        <source>IPv6</source>
-        <translation>IPv6</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="349"/>
-        <source>Port:</source>
-        <translation>Порт:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="347"/>
-        <source>Proxy</source>
-        <translation>Проксі</translation>
-    </message>
-    <message>
-        <location filename="" line="3080307"/>
-        <source>Online contacts</source>
-        <translation type="obsolete">Контактів онлайн</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="74"/>
-        <source>Send request</source>
-        <translation>Відправити запит</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="353"/>
-        <source>WARNING:
-using proxy with enabled UDP
-can produce IP leak</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="352"/>
-        <source>Download nodes list from tox.chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>MainWindow</name>
-    <message>
-        <location filename="mainscreen.py" line="126"/>
-        <source>About program</source>
-        <translation>Про проґраму</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="856"/>
-        <source>Friend request</source>
-        <translation>Запит дружби</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="420"/>
-        <source>About</source>
-        <translation>Про</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="128"/>
-        <source>Audio</source>
-        <translation>Звук</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="826"/>
-        <source>Friend added</source>
-        <translation>Друга додано</translation>
-    </message>
-    <message>
-        <location filename="" line="3080307"/>
-        <source>Send file</source>
-        <translation type="obsolete">Надіслати файл</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="854"/>
-        <source>User {} wants to add you to contact list. Message:
-{}</source>
-        <translation>Користувач {} хоче додати вас до списку контактів. Повідомлення
-{}</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="125"/>
-        <source>Network</source>
-        <translation>Мережа</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="598"/>
-        <source>Clear history</source>
-        <translation>Очистити журнал</translation>
-    </message>
-    <message>
-        <location filename="" line="3080307"/>
-        <source>Copy public key</source>
-        <translation type="obsolete">Копіювати публічний ключ</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="132"/>
-        <source>Send message</source>
-        <translation>Надіслати повідомлення</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="594"/>
-        <source>Set alias</source>
-        <translation>Встановити скорочення</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="122"/>
-        <source>Privacy</source>
-        <translation>Приватність</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="121"/>
-        <source>Profile</source>
-        <translation>Профіль</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="421"/>
-        <source>Toxygen is Tox client written on Python.
-Version: </source>
-        <translation>Toxygen — це клієнт Tox написаний на Python.
-Версія:</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="523"/>
-        <source>Choose file</source>
-        <translation>Обрати файл</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="694"/>
-        <source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
-        <translation>Введіть нове скорочення для друга {} або залишіть порожнім, щоб використовувати його псевдо:</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="119"/>
-        <source>Add contact</source>
-        <translation>Додати контакт</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="827"/>
-        <source>Friend added without sending friend request</source>
-        <translation>Друга додано без надсилання запиту дружби</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="123"/>
-        <source>Interface</source>
-        <translation>Зовнішній вигляд</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="127"/>
-        <source>Settings</source>
-        <translation>Налаштування</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="124"/>
-        <source>Notifications</source>
-        <translation>Сповіщення</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="609"/>
-        <source>Remove friend</source>
-        <translation>Вилучити друга</translation>
-    </message>
-    <message>
-        <location filename="" line="3080307"/>
-        <source>Find contact</source>
-        <translation type="obsolete">Знайти контакт</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="665"/>
-        <source>Choose folder</source>
-        <translation>Обрати теку</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="590"/>
-        <source>Allow auto accept</source>
-        <translation>Дозволити автоприймання</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="589"/>
-        <source>Disallow auto accept</source>
-        <translation>Заборонити автоприймання</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="133"/>
-        <source>Start audio call with friend</source>
-        <translation>Почати звуковий дзвінок</translation>
-    </message>
-    <message>
-        <location filename="" line="3080307"/>
-        <source>Send screenshot</source>
-        <translation type="obsolete">Надіслати знімок екрану</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="101"/>
-        <source>Error</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="103"/>
-        <source>Profile with this name already exists</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="329"/>
-        <source>User {} is now known as {}</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="672"/>
-        <source>Choose folder with sticker pack</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="684"/>
-        <source>Choose folder with smiley pack</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="list_items.py" line="45"/>
-        <source>Quote selected text</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="625"/>
-        <source>Plugins</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="list_items.py" line="164"/>
-        <source>Delete message</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="113"/>
-        <source>Lock</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="115"/>
-        <source>List of plugins</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="129"/>
-        <source>Video</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="130"/>
-        <source>Updates</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="412"/>
-        <source>Search</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="135"/>
-        <source>All</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="136"/>
-        <source>Online</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="137"/>
-        <source>Online first</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="603"/>
-        <source>Name</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="139"/>
-        <source>Online and by name</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="140"/>
-        <source>Online first and by name</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="144"/>
-        <source>Import plugin</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="145"/>
-        <source>Reload plugins</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="476"/>
-        <source>Choose folder with plugin</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="485"/>
-        <source>Restart Toxygen</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="487"/>
-        <source>Plugin will be loaded after restart</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="497"/>
-        <source>Cannot lock app</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="499"/>
-        <source>Error. Profile password is not set.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="597"/>
-        <source>Chat history</source>
-        <translation type="unfinished">Журнал бесіди</translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="599"/>
-        <source>Export as text</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="600"/>
-        <source>Export as HTML</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="126"/>
-        <source>Copy</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="604"/>
-        <source>Status message</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="606"/>
-        <source>Public key</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="610"/>
-        <source>Block friend</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="611"/>
-        <source>Notes</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="650"/>
-        <source>Notes about user</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="124"/>
-        <source>Copy link location</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="128"/>
-        <source>Select all</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="130"/>
-        <source>Delete</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="132"/>
-        <source>Paste</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="134"/>
-        <source>Cut</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="136"/>
-        <source>Undo</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="138"/>
-        <source>Redo</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="widgets.py" line="159"/>
-        <source>Save</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="472"/>
-        <source>Text &quot;{}&quot; was not found</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="474"/>
-        <source>Not found</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1337"/>
-        <source>User {} invites you to group chat. Accept?</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1338"/>
-        <source>Group chat invite</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1374"/>
-        <source>{} users in chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1395"/>
-        <source>Enter new title for group {}:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="634"/>
-        <source>Set title</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="120"/>
-        <source>Create group chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="615"/>
-        <source>Invite to group chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen.py" line="633"/>
-        <source>Leave chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>MenuWindow</name>
-    <message>
-        <location filename="mainscreen_widgets.py" line="217"/>
-        <source>Send screenshot</source>
-        <translation type="unfinished">Надіслати знімок екрану</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="218"/>
-        <source>Send file</source>
-        <translation type="unfinished">Надіслати файл</translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="219"/>
-        <source>Add smiley</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="220"/>
-        <source>Send sticker</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>NetworkSettings</name>
-    <message>
-        <location filename="menu.py" line="344"/>
-        <source>Network settings</source>
-        <translation>Налаштування мережі</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="350"/>
-        <source>Restart TOX core</source>
-        <translation>Перезапустити ядро Tox</translation>
-    </message>
-</context>
-<context>
-    <name>PasswordScreen</name>
-    <message>
-        <location filename="passwordscreen.py" line="132"/>
-        <source>Profile password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="134"/>
-        <source>Password (at least 8 symbols)</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="136"/>
-        <source>Confirm password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="138"/>
-        <source>Set password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="153"/>
-        <source>Passwords do not match</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="141"/>
-        <source>There is no way to recover lost passwords</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="150"/>
-        <source>Password must be at least 8 symbols</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>PluginWindow</name>
-    <message>
-        <location filename="plugins/plugin_super_class.py" line="139"/>
-        <source>List of commands for plugin {}</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="plugins/plugin_super_class.py" line="140"/>
-        <source>No commands available</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>PluginsForm</name>
-    <message>
-        <location filename="menu.py" line="972"/>
-        <source>Plugins</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="973"/>
-        <source>Open selected plugin</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="985"/>
-        <source>No GUI found for this plugin</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="985"/>
-        <source>Error</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1000"/>
-        <source>No description available</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1016"/>
-        <source>Disable plugin</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1018"/>
-        <source>Enable plugin</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1008"/>
-        <source>No plugins found</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>ProfileSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="175"/>
-        <source>Name:</source>
-        <translation>Псевдо:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="174"/>
-        <source>Profile settings</source>
-        <translation>Налаштування профілю</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="180"/>
-        <source>Reset avatar</source>
-        <translation>Скинути аватар</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="181"/>
-        <source>New NoSpam</source>
-        <translation>Новий NoSpam</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="178"/>
-        <source>Copy TOX ID</source>
-        <translation>Копіювати TOX ID</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="179"/>
-        <source>New avatar</source>
-        <translation>Новий аватар</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="173"/>
-        <source>Export profile</source>
-        <translation>Експортувати профіль</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="177"/>
-        <source>TOX ID:</source>
-        <translation>TOX ID:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="176"/>
-        <source>Status:</source>
-        <translation>Статус:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="182"/>
-        <source>Profile password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="183"/>
-        <source>Password (at least 8 symbols)</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="184"/>
-        <source>Confirm password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="185"/>
-        <source>Set password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="221"/>
-        <source>Passwords do not match</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="187"/>
-        <source>Leaving blank will reset current password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="188"/>
-        <source>There is no way to recover lost passwords</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="189"/>
-        <source>Online</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="190"/>
-        <source>Away</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="191"/>
-        <source>Busy</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="192"/>
-        <source>Copy public key</source>
-        <translation type="unfinished">Копіювати публічний ключ</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="205"/>
-        <source>Mark as not default profile</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="208"/>
-        <source>Mark as default profile</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="218"/>
-        <source>Password must be at least 8 symbols</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="250"/>
-        <source>Choose avatar</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="269"/>
-        <source>Use new path</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="271"/>
-        <source>Do you want to move your profile to this location?</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>WelcomeScreen</name>
-    <message>
-        <location filename="mainscreen_widgets.py" line="295"/>
-        <source>Don&apos;t show again</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="296"/>
-        <source>Tip of the day</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="301"/>
-        <source>Press Esc if you want hide app to tray.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="304"/>
-        <source>Right click on screenshot button hides app to tray during screenshot.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="307"/>
-        <source>You can use Tox over Tor. For more info read &lt;a href=&quot;https://wiki.tox.chat/users/tox_over_tor_tot&quot;&gt;this post&lt;/a&gt;</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="310"/>
-        <source>Use Settings -&gt; Interface to customize interface.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="313"/>
-        <source>Set profile password via Profile -&gt; Settings. Password allows Toxygen encrypt your history and settings.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="316"/>
-        <source>Since v0.1.3 Toxygen supports plugins. &lt;a href=&quot;https://github.com/toxygen-project/toxygen/blob/master/docs/plugins.md&quot;&gt;Read more&lt;/a&gt;</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="319"/>
-        <source>Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="325"/>
-        <source>Delete single message in chat: make right click on spinner or message time and choose &quot;Delete&quot; in menu</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="328"/>
-        <source>Use right click on inline image to save it</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="331"/>
-        <source>Set new NoSpam to avoid spam friend requests: Profile -&gt; Settings -&gt; Set new NoSpam.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="mainscreen_widgets.py" line="322"/>
-        <source>New in Toxygen 0.4.1:&lt;br&gt;Downloading nodes from tox.chat&lt;br&gt;Bug fixes</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>audioSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="807"/>
-        <source>Output device:</source>
-        <translation>Пристрій виводу:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="805"/>
-        <source>Audio settings</source>
-        <translation>Налаштування звуку</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="806"/>
-        <source>Input device:</source>
-        <translation>Пристрій вводу:</translation>
-    </message>
-</context>
-<context>
-    <name>incoming_call</name>
-    <message>
-        <location filename="profile.py" line="1253"/>
-        <source>Incoming video call</source>
-        <translation>Вхідний відеодзвінок</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1255"/>
-        <source>Incoming audio call</source>
-        <translation>Вхідний аудіодзвінок</translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1236"/>
-        <source>Outgoing video call</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1238"/>
-        <source>Outgoing audio call</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1284"/>
-        <source>Call declined</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="profile.py" line="1286"/>
-        <source>Call finished</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>interfaceForm</name>
-    <message>
-        <location filename="menu.py" line="657"/>
-        <source>Language:</source>
-        <translation>Мова:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="656"/>
-        <source>Theme:</source>
-        <translation>Тема:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="655"/>
-        <source>Interface settings</source>
-        <translation>Налаштування зовнішнього вигляду</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="654"/>
-        <source>Show avatars in chat</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="658"/>
-        <source>Smileys</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="659"/>
-        <source>Smiley pack:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="660"/>
-        <source>Mirror mode</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="661"/>
-        <source>Messages font size:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="662"/>
-        <source>Select unread messages notification color</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="663"/>
-        <source>Compact contact list</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="664"/>
-        <source>Import smiley pack</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="665"/>
-        <source>Import sticker pack</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="666"/>
-        <source>Close to tray</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="667"/>
-        <source>Select font</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="754"/>
-        <source>Restart app to apply settings</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="754"/>
-        <source>Restart required</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>login</name>
-    <message>
-        <location filename="loginscreen.py" line="72"/>
-        <source>Profile name:</source>
-        <translation>Псевдо профілю:</translation>
-    </message>
-    <message>
-        <location filename="loginscreen.py" line="73"/>
-        <source>Load profile</source>
-        <translation>Завантажити профіль</translation>
-    </message>
-    <message>
-        <location filename="loginscreen.py" line="74"/>
-        <source>Use as default</source>
-        <translation>За замовчуванням</translation>
-    </message>
-    <message>
-        <location filename="loginscreen.py" line="76"/>
-        <source>Create new profile</source>
-        <translation>Створити новий профіль</translation>
-    </message>
-    <message>
-        <location filename="loginscreen.py" line="71"/>
-        <source>Create</source>
-        <translation>Створити</translation>
-    </message>
-    <message>
-        <location filename="loginscreen.py" line="70"/>
-        <source>Log in</source>
-        <translation>Увійти</translation>
-    </message>
-    <message>
-        <location filename="loginscreen.py" line="75"/>
-        <source>Load existing profile</source>
-        <translation>Завантажити існуючий</translation>
-    </message>
-    <message>
-        <location filename="loginscreen.py" line="77"/>
-        <source>toxygen</source>
-        <translation>toxygen</translation>
-    </message>
-    <message>
-        <location filename="" line="3080307"/>
-        <source>Looks like other instance of Toxygen uses this profile! Continue?</source>
-        <translation type="obsolete">Схоже, що інша копія Toxygenʼу використовує цей профіль! Продовжити?</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="113"/>
-        <source>Do you want to set profile password?</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="124"/>
-        <source>Do you want to save profile in default folder? If no, profile will be saved in program folder</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="138"/>
-        <source>Profile saving error! Does Toxygen have permission to write to this directory?</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="166"/>
-        <source>Other instance of Toxygen uses this profile or profile was not properly closed. Continue?</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="288"/>
-        <source>Update for Toxygen was found. Download and install it?</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="loginscreen.py" line="69"/>
-        <source>Profile name</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>notificationsForm</name>
-    <message>
-        <location filename="menu.py" line="547"/>
-        <source>Enable sound notifications</source>
-        <translation>Увімкнути звукові сповіщення</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="544"/>
-        <source>Enable notifications</source>
-        <translation>Увімкнути сповіщення</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="543"/>
-        <source>Notification settings</source>
-        <translation>Налаштування сповіщень</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="546"/>
-        <source>Enable call&apos;s sound</source>
-        <translation>Увімкнути звук дзвінка</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="545"/>
-        <source>Notify about all messages in groups</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>pass</name>
-    <message>
-        <location filename="passwordscreen.py" line="61"/>
-        <source>Enter password</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="62"/>
-        <source>Password:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="passwordscreen.py" line="63"/>
-        <source>Incorrect password</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>privacySettings</name>
-    <message>
-        <location filename="menu.py" line="438"/>
-        <source>Privacy settings</source>
-        <translation>Налаштування приватності</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="460"/>
-        <source>Add to friend list</source>
-        <translation>Додати до списку друзів</translation>
-    </message>
-    <message>
-        <location filename="" line="3080307"/>
-        <source>Block by TOX ID:</source>
-        <translation type="obsolete">Блокувати по TOX ID:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="446"/>
-        <source>Blocked users:</source>
-        <translation>Блоковані користувачі:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="443"/>
-        <source>Change</source>
-        <translation>Змінити</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="441"/>
-        <source>Send typing notifications</source>
-        <translation>Надсилати сповіщення про те, що я друкую</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="440"/>
-        <source>Allow file auto accept</source>
-        <translation>Дозволити автоприймання файлів</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="444"/>
-        <source>Allow inlines</source>
-        <translation>Дозволити інлайни</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="439"/>
-        <source>Save chat history</source>
-        <translation>Зберігати журнал бесіди</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="448"/>
-        <source>Block user</source>
-        <translation>Блокувати користувача</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="486"/>
-        <source>Chat history</source>
-        <translation>Журнал бесіди</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="447"/>
-        <source>Unblock</source>
-        <translation>Розблокувати</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="488"/>
-        <source>History will be cleaned! Continue?</source>
-        <translation>Журнал буде очищено! Продовжити?</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="442"/>
-        <source>Auto accept default path:</source>
-        <translation>Шлях за замовчуванням для автоприймання:</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="461"/>
-        <source>Do you want to add this user to friend list?</source>
-        <translation>Ви хочете додати цього користувача у список друзів?</translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="445"/>
-        <source>Block by public key:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="449"/>
-        <source>Save unsent messages only</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>tray</name>
-    <message>
-        <location filename="main.py" line="233"/>
-        <source>Exit</source>
-        <translation>Вихід</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="224"/>
-        <source>Open Toxygen</source>
-        <translation>Відкрити Toxygen</translation>
-    </message>
-    <message>
-        <location filename="main.py" line="225"/>
-        <source>Set status</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="226"/>
-        <source>Online</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="227"/>
-        <source>Away</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="main.py" line="228"/>
-        <source>Busy</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>updateSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="1056"/>
-        <source>Update settings</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1057"/>
-        <source>Select update mode:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1058"/>
-        <source>Update Toxygen</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1059"/>
-        <source>Disabled</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1060"/>
-        <source>Manual</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1061"/>
-        <source>Auto</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1080"/>
-        <source>Error</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1073"/>
-        <source>Problems with internet connection</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1081"/>
-        <source>Updater not found</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1092"/>
-        <source>No updates found</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="1093"/>
-        <source>Toxygen is up to date</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>videoSettingsForm</name>
-    <message>
-        <location filename="menu.py" line="887"/>
-        <source>Video settings</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="888"/>
-        <source>Device:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="867"/>
-        <source>Desktop</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="menu.py" line="889"/>
-        <source>Select region</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-</TS>
diff --git a/toxygen/ui/__init__.py b/toxygen/ui/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/ui/av_widgets.py b/toxygen/ui/av_widgets.py
deleted file mode 100644
index e750231..0000000
--- a/toxygen/ui/av_widgets.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import logging
-import threading
-import wave
-
-from  qtpy import QtCore, QtGui, QtWidgets
-
-from ui import widgets
-import utils.util as util
-import toxygen_wrapper.tests.support_testing as ts
-with ts.ignoreStderr():
-    import pyaudio
-
-global LOG
-LOG = logging.getLogger('app.'+__name__)
-
-class IncomingCallWidget(widgets.CenteredWidget):
-
-    def __init__(self, settings, calls_manager, friend_number, text, name):
-        super().__init__()
-        self._settings = settings
-        self._calls_manager = calls_manager
-        self.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint) #  | QtCore.Qt.WindowStaysOnTopHint
-        self.resize(QtCore.QSize(500, 270))
-        self.avatar_label = QtWidgets.QLabel(self)
-        self.avatar_label.setGeometry(QtCore.QRect(10, 20, 64, 64))
-        self.avatar_label.setScaledContents(False)
-        self.name = widgets.DataLabel(self)
-        self.name.setGeometry(QtCore.QRect(90, 20, 300, 25))
-        self._friend_number = friend_number
-        font = QtGui.QFont()
-        font.setFamily(settings['font'])
-        font.setPointSize(16)
-        font.setBold(True)
-        self.name.setFont(font)
-        self.call_type = widgets.DataLabel(self)
-        self.call_type.setGeometry(QtCore.QRect(90, 55, 300, 25))
-        self.call_type.setFont(font)
-        self.accept_audio = QtWidgets.QPushButton(self)
-        self.accept_audio.setGeometry(QtCore.QRect(20, 100, 150, 150))
-        self.accept_video = QtWidgets.QPushButton(self)
-        self.accept_video.setGeometry(QtCore.QRect(170, 100, 150, 150))
-        self.decline = QtWidgets.QPushButton(self)
-        self.decline.setGeometry(QtCore.QRect(320, 100, 150, 150))
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'accept_audio.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.accept_audio.setIcon(icon)
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'accept_video.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.accept_video.setIcon(icon)
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'decline_call.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.decline.setIcon(icon)
-        self.accept_audio.setIconSize(QtCore.QSize(150, 150))
-        self.accept_video.setIconSize(QtCore.QSize(140, 140))
-        self.decline.setIconSize(QtCore.QSize(140, 140))
-        #self.accept_audio.setStyleSheet("QPushButton { border: none }")
-        #self.accept_video.setStyleSheet("QPushButton { border: none }")
-        #self.decline.setStyleSheet("QPushButton { border: none }")
-        self.setWindowTitle(text)
-        self.name.setText(name)
-        self.call_type.setText(text)
-        self._processing = False
-        self.accept_audio.clicked.connect(self.accept_call_with_audio)
-        self.accept_video.clicked.connect(self.accept_call_with_video)
-        self.decline.clicked.connect(self.decline_call)
-
-        output_device_index = self._settings._oArgs.audio['output']
-
-        if self._settings['calls_sound']:
-            class SoundPlay(QtCore.QThread):
-
-                def __init__(self):
-                    QtCore.QThread.__init__(self)
-                    self.a = None
-
-                def run(self):
-                    class AudioFile:
-                        chunk = 1024
-
-                        def __init__(self, fl):
-                            self.stop = False
-                            self.fl = fl
-                            self.wf = wave.open(self.fl, 'rb')
-                            self.p = pyaudio.PyAudio()
-                            self.stream = self.p.open(
-                                format=self.p.get_format_from_width(self.wf.getsampwidth()),
-                                channels=self.wf.getnchannels(),
-                                rate=self.wf.getframerate(),
-                                # why no device?
-                                output_device_index=output_device_index,
-                                output=True)
-
-                        def play(self):
-                            while not self.stop:
-                                data = self.wf.readframes(self.chunk)
-                                # dunno
-                                if not data: break
-                                while data and not self.stop:
-                                    self.stream.write(data)
-                                    data = self.wf.readframes(self.chunk)
-                                self.wf = wave.open(self.fl, 'rb')
-
-                        def close(self):
-                            try:
-                                self.stream.close()
-                                self.p.terminate()
-                            except Exception as e:
-                                # malloc_consolidate(): unaligned fastbin chunk detected
-                                LOG.warn("SoundPlay close exception {e}")
-
-                    self.a = AudioFile(util.join_path(util.get_sounds_directory(), 'call.wav'))
-                    self.a.play()
-                    self.a.close()
-
-            self.thread = SoundPlay()
-            self.thread.start()
-        else:
-            self.thread = None
-
-    def stop(self):
-        LOG.debug(f"stop from friend_number={self._friend_number}")
-        if self._processing:
-            self.close()
-        if self.thread is not None:
-            self.thread.a.stop = True
-            i = 0
-            while i < ts.iTHREAD_JOINS:
-                self.thread.wait(ts.iTHREAD_TIMEOUT)
-                if not self.thread.isRunning(): break
-                i = i + 1
-            else:
-                LOG.warn(f"stop {self.thread.a} BLOCKED")
-                self.thread.a.stream.close()
-                self.thread.a.p.terminate()
-                self.thread.a.close()
-            # dunno -failsafe
-            self.thread.terminate()
-        #? dunno
-        self._processing = False
-
-    def accept_call_with_audio(self):
-        if self._processing:
-            LOG.warn(f" accept_call_with_audio  from {self._friend_number}")
-            return
-        LOG.debug(f" accept_call_with_audio  from {self._friend_number}")
-        self._processing = True
-        try:
-            self._calls_manager.accept_call(self._friend_number, True, False)
-        finally:
-            #? self.stop()
-            LOG.debug(f" accept_call_with_audio NOT stop from={self._friend_number}")
-            pass
-
-    def accept_call_with_video(self):
-        # ts.trepan_handler()
-
-        if self._processing:
-            LOG.warn(f" accept_call_with_video from {self._friend_number}")
-            return
-        self.setWindowTitle('Answering video call')
-        self._processing = True
-        LOG.debug(f" accept_call_with_video from {self._friend_number}")
-        try:
-            self._calls_manager.accept_call(self._friend_number, True, True)
-        finally:
-            self.stop()
-
-    def decline_call(self):
-        LOG.debug(f"decline_call from {self._friend_number}")
-        if self._processing:
-            return
-        self._processing = True
-        try:
-            self._calls_manager.stop_call(self._friend_number, False)
-        except Exception as e:
-            LOG.warn(f"decline_call from {self._friend_number} {e}")
-        finally:
-            self.stop()
-
-    def set_pixmap(self, pixmap):
-        self.avatar_label.setPixmap(pixmap)
diff --git a/toxygen/ui/contact_items.py b/toxygen/ui/contact_items.py
deleted file mode 100644
index bdff447..0000000
--- a/toxygen/ui/contact_items.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from  qtpy import QtCore, QtGui, QtWidgets
-from toxygen_wrapper.toxcore_enums_and_consts import *
-
-from utils.util import *
-from ui.widgets import DataLabel
-
-
-class ContactItem(QtWidgets.QWidget):
-    """
-    Contact in friends list
-    """
-
-    def __init__(self, settings, parent=None, kind='friend'):
-        QtWidgets.QWidget.__init__(self, parent)
-        mode = settings['compact_mode']
-        self.setBaseSize(QtCore.QSize(250, 40 if mode else 70))
-        self.avatar_label = QtWidgets.QLabel(self)
-        size = 32 if mode else 64
-        self.avatar_label.setGeometry(QtCore.QRect(3, 4, size, size))
-        self.avatar_label.setScaledContents(False)
-        self.avatar_label.setAlignment(QtCore.Qt.AlignCenter)
-        self.name = DataLabel(self)
-        self.name.setGeometry(QtCore.QRect(50 if mode else 75, 3 if mode else 10, 150, 15 if mode else 25))
-        font = QtGui.QFont()
-        font.setFamily(settings['font'])
-        font.setPointSize(10 if mode else 12)
-        font.setBold(True)
-        self.name.setFont(font)
-        self.status_message = DataLabel(self)
-        self.status_message.setGeometry(QtCore.QRect(50 if mode else 75, 20 if mode else 30, 170, 15 if mode else 20))
-        font.setPointSize(10)
-        font.setBold(False)
-        self.status_message.setFont(font)
-        self.kind = DataLabel(self)
-        self.kind.setGeometry(QtCore.QRect(50 if mode else 75, 38 if mode else 48, 190, 15 if mode else 20))
-        font.setBold(False)
-        font.setItalic(True)
-        self.kind.setFont(font)
-        self.connection_status = StatusCircle(self)
-        self.connection_status.setGeometry(QtCore.QRect(230, -2 if mode else 5, 32, 32))
-        self.messages = UnreadMessagesCount(settings, self)
-        self.messages.setGeometry(QtCore.QRect(20 if mode else 52, 20 if mode else 50, 30, 20))
-
-
-class StatusCircle(QtWidgets.QWidget):
-    """
-    Connection status
-    """
-    def __init__(self, parent):
-        QtWidgets.QWidget.__init__(self, parent)
-        self.setGeometry(0, 0, 32, 32)
-        self.label = QtWidgets.QLabel(self)
-        self.label.setGeometry(QtCore.QRect(0, 0, 32, 32))
-        self.unread = False
-
-    def update(self, status, unread_messages=None):
-        if unread_messages is None:
-            unread_messages = self.unread
-        else:
-            self.unread = unread_messages
-        if status == TOX_USER_STATUS['NONE']:
-            name = 'online'
-        elif status == TOX_USER_STATUS['AWAY']:
-            name = 'idle'
-        elif status == TOX_USER_STATUS['BUSY']:
-            name = 'busy'
-        else:
-            name = 'offline'
-        if unread_messages:
-            name += '_notification'
-            self.label.setGeometry(QtCore.QRect(0, 0, 32, 32))
-        else:
-            self.label.setGeometry(QtCore.QRect(2, 0, 32, 32))
-        pixmap = QtGui.QPixmap(join_path(get_images_directory(), '{}.png'.format(name)))
-        self.label.setPixmap(pixmap)
-
-
-class UnreadMessagesCount(QtWidgets.QWidget):
-
-    def __init__(self, settings, parent=None):
-        super().__init__(parent)
-        self._settings = settings
-        self.resize(30, 20)
-        self.label = QtWidgets.QLabel(self)
-        self.label.setGeometry(QtCore.QRect(0, 0, 30, 20))
-        self.label.setVisible(False)
-        font = QtGui.QFont()
-        font.setFamily(settings['font'])
-        font.setPointSize(12)
-        font.setBold(True)
-        self.label.setFont(font)
-        self.label.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignCenter)
-        color = settings['unread_color']
-        self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }')
-
-    def update(self, messages_count):
-        color = self._settings['unread_color']
-        self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }')
-        if messages_count:
-            self.label.setVisible(True)
-            self.label.setText(str(messages_count))
-        else:
-            self.label.setVisible(False)
diff --git a/toxygen/ui/create_profile_screen.py b/toxygen/ui/create_profile_screen.py
deleted file mode 100644
index f507a1d..0000000
--- a/toxygen/ui/create_profile_screen.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import uic
-
-from ui.widgets import *
-import utils.util as util
-import utils.ui as util_ui
-
-class CreateProfileScreenResult:
-
-    def __init__(self, save_into_default_folder, password):
-        self._save_into_default_folder = save_into_default_folder
-        self._password = password
-
-    def get_save_into_default_folder(self):
-        return self._save_into_default_folder
-
-    save_into_default_folder = property(get_save_into_default_folder)
-
-    def get_password(self):
-        return self._password
-
-    password = property(get_password)
-
-
-class CreateProfileScreen(CenteredWidget, DialogWithResult):
-
-    def __init__(self):
-        CenteredWidget.__init__(self)
-        DialogWithResult.__init__(self)
-        uic.loadUi(util.get_views_path('create_profile_screen'), self)
-        self.center()
-        self.createProfile.clicked.connect(self._create_profile)
-        self._retranslate_ui()
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr('New profile settings'))
-        self.defaultFolder.setText(util_ui.tr('Save in default folder'))
-        self.programFolder.setText(util_ui.tr('Save in program folder'))
-        self.password.setPlaceholderText(util_ui.tr('Password'))
-        self.confirmPassword.setPlaceholderText(util_ui.tr('Confirm password'))
-        self.createProfile.setText(util_ui.tr('Create profile'))
-        self.passwordLabel.setText(util_ui.tr('Password (at least 8 symbols):'))
-
-    def _create_profile(self):
-        password = self.password.text()
-        if password != self.confirmPassword.text():
-            self.errorLabel.setText(util_ui.tr('Passwords do not match'))
-            return
-        if 0 < len(password) < 8:
-            self.errorLabel.setText(util_ui.tr('Password must be at least 8 symbols'))
-            return
-        result = CreateProfileScreenResult(self.defaultFolder.isChecked(), password)
-        self.close_with_result(result)
diff --git a/toxygen/ui/group_bans_widgets.py b/toxygen/ui/group_bans_widgets.py
deleted file mode 100644
index 60f1e9e..0000000
--- a/toxygen/ui/group_bans_widgets.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import uic, QtWidgets, QtCore
-
-from ui.widgets import CenteredWidget
-import utils.util as util
-import utils.ui as util_ui
-
-class GroupBanItem(QtWidgets.QWidget):
-
-    def __init__(self, ban, cancel_ban, can_cancel_ban, parent=None):
-        super().__init__(parent)
-        self._ban = ban
-        self._cancel_ban = cancel_ban
-        self._can_cancel_ban = can_cancel_ban
-
-        uic.loadUi(util.get_views_path('gc_ban_item'), self)
-        self._update_ui()
-
-    def _update_ui(self):
-        self._retranslate_ui()
-
-        self.banTargetLabel.setText(self._ban.ban_target)
-        ban_time = self._ban.ban_time
-        self.banTimeLabel.setText(util.unix_time_to_long_str(ban_time))
-
-        self.cancelPushButton.clicked.connect(self.cancel_ban)
-        self.cancelPushButton.setEnabled(self.can_cancel_ban)
-
-    def _retranslate_ui(self):
-        self.cancelPushButton.setText(util_ui.tr('Cancel ban'))
-
-    def cancel_ban(self): # pylint: disable=method-hidden
-        # FixMe broken
-        # self._cancel_ban(self._ban.ban_id)
-        pass
-
-    def can_cancel_ban(self): # pylint: disable=method-hidden
-        # FixMe missing
-        pass
-
-class GroupBansScreen(CenteredWidget):
-
-    def __init__(self, groups_service, group):
-        super().__init__()
-        self._groups_service = groups_service
-        self._group = group
-
-        uic.loadUi(util.get_views_path('bans_list_screen'), self)
-        self._update_ui()
-
-    def _update_ui(self):
-        self._retranslate_ui()
-
-        self._refresh_bans_list()
-
-    def _retranslate_ui(self):
-#        self.setWindowTitle(util_ui.tr('Bans list for group "{}"').format(self._group.name))
-        pass
-
-    def _refresh_bans_list(self):
-        self.bansListWidget.clear()
-        can_cancel_ban = self._group.is_self_moderator_or_founder()
-        for ban in self._group.bans:
-            self._create_ban_item(ban, can_cancel_ban)
-
-    def _create_ban_item(self, ban, can_cancel_ban):
-        item = GroupBanItem(ban, self._on_ban_cancelled, can_cancel_ban, self.bansListWidget)
-        elem = QtWidgets.QListWidgetItem()
-        elem.setSizeHint(QtCore.QSize(item.width(), item.height()))
-        self.bansListWidget.addItem(elem)
-        self.bansListWidget.setItemWidget(elem, item)
-
-    def _on_ban_cancelled(self, ban_id):
-        self._groups_service.cancel_ban(self._group.number, ban_id)
-        self._refresh_bans_list()
diff --git a/toxygen/ui/group_invites_widgets.py b/toxygen/ui/group_invites_widgets.py
deleted file mode 100644
index 6b9fa9e..0000000
--- a/toxygen/ui/group_invites_widgets.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import logging
-from  qtpy import uic, QtWidgets
-
-import utils.util as util
-from ui.widgets import *
-
-global LOG
-LOG = logging.getLogger('app')
-
-class GroupInviteItem(QtWidgets.QWidget):
-
-    def __init__(self, parent, chat_name, avatar, friend_name):
-        super().__init__(parent)
-        uic.loadUi(util.get_views_path('gc_invite_item'), self)
-
-        self.groupNameLabel.setText(chat_name)
-        self.friendNameLabel.setText(friend_name)
-        self.friendAvatarLabel.setPixmap(avatar)
-
-    def is_selected(self):
-        return self.selectCheckBox.isChecked()
-
-    def subscribe_checked_event(self, callback):
-        self.selectCheckBox.clicked.connect(callback)
-
-
-class GroupInvitesScreen(CenteredWidget):
-
-    def __init__(self, groups_service, profile, contacts_provider):
-        super().__init__()
-        self._groups_service = groups_service
-        self._profile = profile
-        self._contacts_provider = contacts_provider
-        self._tox = self._groups_service._tox
-
-        uic.loadUi(util.get_views_path('group_invites_screen'), self)
-
-        self._update_ui()
-
-    def _update_ui(self):
-        self._retranslate_ui()
-
-        self._refresh_invites_list()
-
-        self.nickLineEdit.setText(self._profile.name)
-        self.statusComboBox.setCurrentIndex(self._profile.status or 0)
-
-        self.nickLineEdit.textChanged.connect(self._nick_changed)
-        self.acceptPushButton.clicked.connect(self._accept_invites)
-        self.declinePushButton.clicked.connect(self._decline_invites)
-
-        self.invitesListWidget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
-        self.invitesListWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
-
-        self._update_buttons_state()
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr('Group chat invites'))
-        self.noInvitesLabel.setText(util_ui.tr('No group invites found'))
-        self.acceptPushButton.setText(util_ui.tr('Accept'))
-        self.declinePushButton.setText(util_ui.tr('Decline'))
-        self.statusComboBox.addItem(util_ui.tr('Online'))
-        self.statusComboBox.addItem(util_ui.tr('Away'))
-        self.statusComboBox.addItem(util_ui.tr('Busy'))
-        self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat'))
-        self.passwordLineEdit.setPlaceholderText(util_ui.tr('Optional password'))
-
-    def _get_friend(self, public_key):
-        return self._contacts_provider.get_friend_by_public_key(public_key)
-
-    def _accept_invites(self):
-        nick = self.nickLineEdit.text()
-        password = self.passwordLineEdit.text()
-        status = self.statusComboBox.currentIndex()
-
-        if not nick:
-            nick = self._tox.self_get_name()
-        selected_invites = self._get_selected_invites()
-        for invite in selected_invites:
-            LOG.debug(f"_accept_invites {nick}")
-            self._groups_service.accept_group_invite(invite, nick, status, password)
-
-        self._refresh_invites_list()
-        self._close_window_if_needed()
-
-    def _decline_invites(self):
-        selected_invites = self._get_selected_invites()
-        for invite in selected_invites:
-            LOG.debug(f"_groups_service.decline_group_invite")
-            self._groups_service.decline_group_invite(invite)
-
-        self._refresh_invites_list()
-        self._close_window_if_needed()
-
-    def _get_selected_invites(self):
-        all_invites = self._groups_service.get_group_invites()
-        selected = []
-        items_count = len(all_invites)
-        for index in range(items_count):
-            list_item = self.invitesListWidget.item(index)
-            item_widget = self.invitesListWidget.itemWidget(list_item)
-            if item_widget and item_widget.is_selected():
-                selected.append(all_invites[index])
-
-        return selected
-
-    def _refresh_invites_list(self):
-        self.invitesListWidget.clear()
-        invites = self._groups_service.get_group_invites()
-        for invite in invites:
-            self._create_invite_item(invite)
-
-    def _create_invite_item(self, invite):
-        friend = self._get_friend(invite.friend_public_key)
-        item = GroupInviteItem(self.invitesListWidget, invite.chat_name, friend.get_pixmap(), friend.name)
-        item.subscribe_checked_event(self._item_selected)
-        elem = QtWidgets.QListWidgetItem()
-        elem.setSizeHint(QtCore.QSize(item.width(), item.height()))
-        self.invitesListWidget.addItem(elem)
-        self.invitesListWidget.setItemWidget(elem, item)
-
-    def _item_selected(self):
-        self._update_buttons_state()
-
-    def _nick_changed(self):
-        self._update_buttons_state()
-
-    def _update_buttons_state(self):
-        nick = self.nickLineEdit.text()
-        selected_items = self._get_selected_invites()
-        self.acceptPushButton.setEnabled(bool(nick) and len(selected_items))
-        self.declinePushButton.setEnabled(len(selected_items) > 0)
-
-    def _close_window_if_needed(self):
-        if self._groups_service.group_invites_count == 0:
-            self.close()
diff --git a/toxygen/ui/group_peers_list.py b/toxygen/ui/group_peers_list.py
deleted file mode 100644
index ec8f95d..0000000
--- a/toxygen/ui/group_peers_list.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from ui.widgets import *
-from toxygen_wrapper.toxcore_enums_and_consts import *
-
-class PeerItem(QtWidgets.QWidget):
-
-    def __init__(self, peer, handler, width, parent=None):
-        super().__init__(parent)
-        self.resize(QtCore.QSize(width, 34))
-        self.nameLabel = DataLabel(self)
-        self.nameLabel.setGeometry(5, 0, width - 5, 34)
-        name = peer.name
-        if peer.is_current_user:
-            name += util_ui.tr(' *')
-        self.nameLabel.setText(name)
-        if peer.status == TOX_USER_STATUS['NONE']:
-            if peer.is_current_user:
-                style = 'QLabel {color: magenta}'
-            else:
-                style = 'QLabel {color: green}'
-        elif peer.status == TOX_USER_STATUS['AWAY']:
-            style = 'QLabel {color: blue}'
-        else:
-            style = 'QLabel {color: red}'
-        self.nameLabel.setStyleSheet(style)
-        self.nameLabel.mousePressEvent = lambda x: handler(peer.id)
-
-
-class PeerTypeItem(QtWidgets.QWidget):
-
-    def __init__(self, text, width, parent=None):
-        super().__init__(parent)
-        self.resize(QtCore.QSize(width, 34))
-        self.nameLabel = DataLabel(self)
-        self.nameLabel.setGeometry(5, 0, width - 5, 34)
-        self.nameLabel.setText(text)
diff --git a/toxygen/ui/group_settings_widgets.py b/toxygen/ui/group_settings_widgets.py
deleted file mode 100644
index 5fd04d4..0000000
--- a/toxygen/ui/group_settings_widgets.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from ui.widgets import CenteredWidget
-from  qtpy import uic
-import utils.util as util
-import utils.ui as util_ui
-
-class GroupManagementScreen(CenteredWidget):
-
-    def __init__(self, groups_service, group):
-        super().__init__()
-        self._groups_service = groups_service
-        self._group = group
-
-        uic.loadUi(util.get_views_path('group_management_screen'), self)
-        self._update_ui()
-
-    def _update_ui(self):
-        self._retranslate_ui()
-
-        self.passwordLineEdit.setText(self._group.password)
-        self.privacyStateComboBox.setCurrentIndex(1 if self._group.is_private else 0)
-        self.peersLimitSpinBox.setValue(self._group.peers_limit)
-
-        self.deletePushButton.clicked.connect(self._delete)
-        self.savePushButton.clicked.connect(self._save)
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr('Group "{}"').format(self._group.name))
-        self.passwordLabel.setText(util_ui.tr('Password:'))
-        self.peerLimitLabel.setText(util_ui.tr('Peer limit:'))
-        self.privacyStateLabel.setText(util_ui.tr('Privacy state:'))
-        self.deletePushButton.setText(util_ui.tr('Delete'))
-        self.savePushButton.setText(util_ui.tr('Save'))
-
-        self.privacyStateComboBox.clear()
-        self.privacyStateComboBox.addItem(util_ui.tr('Public'))
-        self.privacyStateComboBox.addItem(util_ui.tr('Private'))
-
-    def _delete(self):
-        self._groups_service.leave_group(self._group.number)
-        self.close()
-
-    def _disconnect(self):
-        self._groups_service.disconnect_from_group(self._group.number)
-        self.close()
-
-    def _save(self):
-        password = self.passwordLineEdit.text()
-        privacy_state = self.privacyStateComboBox.currentIndex()
-        peers_limit = self.peersLimitSpinBox.value()
-
-        self._groups_service.set_group_password(self._group, password)
-        self._groups_service.set_group_privacy_state(self._group, privacy_state)
-        self._groups_service.set_group_peers_limit(self._group, peers_limit)
-
-        self.close()
-
-
-class GroupSettingsScreen(CenteredWidget):
-
-    def __init__(self, group):
-        super().__init__()
-        self._group = group
-
-        uic.loadUi(util.get_views_path('gc_settings_screen'), self)
-        self._update_ui()
-
-    def _update_ui(self):
-        self._retranslate_ui()
-
-        self.copyPasswordPushButton.clicked.connect(self._copy_password)
-        self.copyPasswordPushButton.setEnabled(bool(self._group.password))
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr('Group "{}"').format(self._group.name))
-        if self._group.password:
-            password_label_text = '{} {}'.format(util_ui.tr('Password:'), self._group.password)
-        else:
-            password_label_text = util_ui.tr('Password is not set')
-        self.passwordLabel.setText(password_label_text)
-        self.peerLimitLabel.setText('{} {}'.format(util_ui.tr('Peer limit:'), self._group.peers_limit))
-        privacy_state = util_ui.tr('Private') if self._group.is_private else util_ui.tr('Public')
-        self.privacyStateLabel.setText('{} {}'.format(util_ui.tr('Privacy state:'), privacy_state))
-        self.copyPasswordPushButton.setText(util_ui.tr('Copy password'))
-
-    def _copy_password(self):
-        util_ui.copy_to_clipboard(self._group.password)
diff --git a/toxygen/ui/groups_widgets.py b/toxygen/ui/groups_widgets.py
deleted file mode 100644
index ceda0ef..0000000
--- a/toxygen/ui/groups_widgets.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import uic
-
-import utils.util as util
-from ui.widgets import *
-from toxygen_wrapper.toxcore_enums_and_consts import *
-
-class BaseGroupScreen(CenteredWidget):
-
-    def __init__(self, groups_service, profile):
-        super().__init__()
-        self._groups_service = groups_service
-        self._profile = profile
-
-    def _retranslate_ui(self):
-        self.nickLineEdit.setPlaceholderText(util_ui.tr('Your nick in chat'))
-        self.nickLabel.setText(util_ui.tr('Nickname:'))
-        self.statusLabel.setText(util_ui.tr('Status:'))
-        self.statusComboBox.addItem(util_ui.tr('Online'))
-        self.statusComboBox.addItem(util_ui.tr('Away'))
-        self.statusComboBox.addItem(util_ui.tr('Busy'))
-
-
-class CreateGroupScreen(BaseGroupScreen):
-
-    def __init__(self, groups_service, profile):
-        super().__init__(groups_service, profile)
-        uic.loadUi(util.get_views_path('create_group_screen'), self)
-        self.center()
-        self._update_ui()
-
-    def _update_ui(self):
-        self._retranslate_ui()
-
-        self.statusComboBox.setCurrentIndex(self._profile.status or 0)
-        self.nickLineEdit.setText(self._profile.name)
-
-        self.addGroupButton.clicked.connect(self._create_group)
-        self.groupNameLineEdit.textChanged.connect(self._group_name_changed)
-        self.nickLineEdit.textChanged.connect(self._nick_changed)
-
-    def _retranslate_ui(self):
-        super()._retranslate_ui()
-        self.setWindowTitle(util_ui.tr('Create new group chat'))
-        self.groupNameLabel.setText(util_ui.tr('Group name:'))
-        self.groupTypeLabel.setText(util_ui.tr('Group type:'))
-        self.groupNameLineEdit.setPlaceholderText(util_ui.tr('Group\'s persistent name'))
-        self.addGroupButton.setText(util_ui.tr('Create group'))
-        self.groupTypeComboBox.addItem(util_ui.tr('Public'))
-        self.groupTypeComboBox.addItem(util_ui.tr('Private'))
-        self.groupTypeComboBox.setCurrentIndex(1)
-
-    def _create_group(self):
-        group_name = self.groupNameLineEdit.text()
-        privacy_state = self.groupTypeComboBox.currentIndex()
-        nick = self.nickLineEdit.text()
-        status = self.statusComboBox.currentIndex()
-        self._groups_service.create_new_gc(group_name, privacy_state, nick, status)
-        self.close()
-
-    def _nick_changed(self):
-        self._update_button_state()
-
-    def _group_name_changed(self):
-        self._update_button_state()
-
-    def _update_button_state(self):
-        is_nick_set = bool(self.nickLineEdit.text())
-        is_group_name_set = bool(self.groupNameLineEdit.text())
-        self.addGroupButton.setEnabled(is_nick_set and is_group_name_set)
-
-
-class JoinGroupScreen(BaseGroupScreen):
-
-    def __init__(self, groups_service, profile):
-        super().__init__(groups_service, profile)
-        uic.loadUi(util.get_views_path('join_group_screen'), self)
-        self.center()
-        self._update_ui()
-
-    def _update_ui(self):
-        self._retranslate_ui()
-
-        self.statusComboBox.setCurrentIndex(self._profile.status or 0)
-        self.nickLineEdit.setText(self._profile.name)
-
-        self.chatIdLineEdit.textChanged.connect(self._chat_id_changed)
-        self.joinGroupButton.clicked.connect(self._join_group)
-        self.nickLineEdit.textChanged.connect(self._nick_changed)
-
-    def _retranslate_ui(self):
-        super()._retranslate_ui()
-        self.setWindowTitle(util_ui.tr('Join public group chat'))
-        self.chatIdLabel.setText(util_ui.tr('Group ID:'))
-        self.passwordLabel.setText(util_ui.tr('Password:'))
-        self.chatIdLineEdit.setPlaceholderText(util_ui.tr('Group\'s chat ID'))
-        self.joinGroupButton.setText(util_ui.tr('Join group'))
-        self.passwordLineEdit.setPlaceholderText(util_ui.tr('Optional password'))
-
-    def _chat_id_changed(self):
-        self._update_button_state()
-
-    def _nick_changed(self):
-        self._update_button_state()
-
-    def _update_button_state(self):
-        chat_id = self._get_chat_id()
-        is_nick_set = bool(self.nickLineEdit.text())
-        self.joinGroupButton.setEnabled(len(chat_id) == TOX_GROUP_CHAT_ID_SIZE * 2 and is_nick_set)
-
-    def _join_group(self):
-        chat_id = self._get_chat_id()
-        password = self.passwordLineEdit.text()
-        nick = self.nickLineEdit.text()
-        status = self.statusComboBox.currentIndex()
-        self._groups_service.join_gc_by_id(chat_id, password, nick, status)
-        self.close()
-
-    def _get_chat_id(self):
-        chat_id = self.chatIdLineEdit.text().strip()
-        if chat_id.startswith('tox:'):
-            chat_id = chat_id[4:]
-
-        return chat_id
diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py
deleted file mode 100644
index 530839c..0000000
--- a/toxygen/ui/items_factories.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from ui.contact_items import *
-from ui.messages_widgets import *
-
-class ContactItemsFactory:
-
-    def __init__(self, settings, main_screen):
-        self._settings = settings
-        self._friends_list = main_screen.friends_list
-
-    def create_contact_item(self):
-        item = ContactItem(self._settings)
-        elem = QtWidgets.QListWidgetItem(self._friends_list)
-        elem.setSizeHint(QtCore.QSize(250, 40 if self._settings['compact_mode'] else 70))
-        self._friends_list.addItem(elem)
-        self._friends_list.setItemWidget(elem, item)
-
-        return item
-
-
-class MessagesItemsFactory:
-
-    def __init__(self, settings, plugin_loader, smiley_loader, main_screen, delete_action):
-        self._file_transfers_handler = None
-        self._settings, self._plugin_loader = settings, plugin_loader
-        self._smiley_loader, self._delete_action = smiley_loader, delete_action
-        self._messages = main_screen.messages
-        self._message_edit = main_screen.messageEdit
-
-    def set_file_transfers_handler(self, file_transfers_handler):
-        self._file_transfers_handler = file_transfers_handler
-
-    def create_message_item(self, message, append=True, pixmap=None):
-        item = message.get_widget(self._settings, self._create_message_browser,
-                                  self._delete_action, self._messages)
-        if pixmap is not None:
-            item.set_avatar(pixmap)
-        elem = QtWidgets.QListWidgetItem()
-        elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
-        if append:
-            self._messages.addItem(elem)
-        else:
-            self._messages.insertItem(0, elem)
-        self._messages.setItemWidget(elem, item)
-
-        return item
-
-#   File "/var/local/src/toxygen/toxygen/file_transfers/file_transfers_handler.py", line 216, in transfer_finished
-#     self._file_transfers_message_service.add_inline_message(transfer, index)
-#   File "/var/local/src/toxygen/toxygen/file_transfers/file_transfers_messages_service.py", line 47, in add_inline_message
-#     self._create_inline_item(transfer.data, count + index + 1)
-#   File "/var/local/src/toxygen/toxygen/file_transfers/file_transfers_messages_service.py", line 75, in _create_inline_item
-#     return self._messages_items_factory.create_inline_item(data, False, position)
-#   File "/var/local/src/toxygen/toxygen/ui/items_factories.py", line 50, in create_inline_item
-#     item = InlineImageItem(message.data, self._messages.width(), elem, self._messages)
-# AttributeError: 'bytes' object has no attribute 'data'
-
-    def create_inline_item(self, message, append=True, position=0):
-        elem = QtWidgets.QListWidgetItem()
-        # AttributeError: 'bytes' object has no attribute 'data'
-        if type(message) == bytes:
-            # was used
-            data = message
-        elif hasattr(message, 'data'):
-            # used
-            data = message.data
-        else:
-            # unreached
-            return None
-        item = InlineImageItem(data, self._messages.width(), elem, self._messages)
-        elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
-        if append:
-            self._messages.addItem(elem)
-        else:
-            self._messages.insertItem(position, elem)
-        self._messages.setItemWidget(elem, item)
-
-        return item
-
-    def create_unsent_file_item(self, message, append=True):
-        item = message.get_widget(self._file_transfers_handler, self._settings, self._messages.width(), self._messages)
-        elem = QtWidgets.QListWidgetItem()
-        elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34))
-        if append:
-            self._messages.addItem(elem)
-        else:
-            self._messages.insertItem(0, elem)
-        self._messages.setItemWidget(elem, item)
-
-        return item
-
-    def create_file_transfer_item(self, message, append=True):
-        item = message.get_widget(self._file_transfers_handler, self._settings, self._messages.width(), self._messages)
-        elem = QtWidgets.QListWidgetItem()
-        elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34))
-        if append:
-            self._messages.addItem(elem)
-        else:
-            self._messages.insertItem(0, elem)
-        self._messages.setItemWidget(elem, item)
-
-        return item
-
-    # Private methods
-
-    def _create_message_browser(self, text, width, message_type, parent=None):
-        return MessageBrowser(self._settings, self._message_edit, self._smiley_loader, self._plugin_loader,
-                              text, width, message_type, parent)
diff --git a/toxygen/ui/login_screen.py b/toxygen/ui/login_screen.py
deleted file mode 100644
index 93362fd..0000000
--- a/toxygen/ui/login_screen.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import os.path
-
-from  qtpy import uic
-
-from ui.widgets import *
-import utils.util as util
-import utils.ui as util_ui
-
-class LoginScreenResult:
-
-    def __init__(self, profile_path, load_as_default, password=None):
-        self._profile_path = profile_path
-        self._load_as_default = load_as_default
-        self._password = password
-
-    def get_profile_path(self):
-        return self._profile_path
-
-    profile_path = property(get_profile_path)
-
-    def get_load_as_default(self):
-        return self._load_as_default
-
-    load_as_default = property(get_load_as_default)
-
-    def get_password(self):
-        return self._password
-
-    password = property(get_password)
-
-    def is_new_profile(self):
-        return not os.path.isfile(self._profile_path)
-
-
-class LoginScreen(CenteredWidget, DialogWithResult):
-
-    def __init__(self):
-        CenteredWidget.__init__(self)
-        DialogWithResult.__init__(self)
-        uic.loadUi(util.get_views_path('login_screen'), self)
-        self.center()
-        self._profiles = []
-        self._update_ui()
-
-    def update_select(self, profiles):
-        profiles = sorted(profiles, key=lambda p: p[1])
-        self._profiles = list(profiles)
-        self.profilesComboBox.addItems(list(map(lambda p: p[1], profiles)))
-        self.loadProfilePushButton.setEnabled(len(profiles) > 0)
-
-    def _update_ui(self):
-        self.profileNameLineEdit = LineEditWithEnterSupport(self._create_profile, self)
-        self.profileNameLineEdit.setGeometry(QtCore.QRect(20, 100, 160, 30))
-        self._retranslate_ui()
-        self.createProfilePushButton.clicked.connect(self._create_profile)
-        self.loadProfilePushButton.clicked.connect(self._load_existing_profile)
-
-    def _create_profile(self):
-        path = self.profileNameLineEdit.text()
-        load_as_default = self.defaultProfileCheckBox.isChecked()
-        result = LoginScreenResult(path, load_as_default)
-        self.close_with_result(result)
-
-    def _load_existing_profile(self):
-        index = self.profilesComboBox.currentIndex()
-        load_as_default = self.defaultProfileCheckBox.isChecked()
-        path = util.join_path(self._profiles[index][0], self._profiles[index][1] + '.tox')
-        result = LoginScreenResult(path, load_as_default)
-        self.close_with_result(result)
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr('Log in'))
-        self.profileNameLineEdit.setPlaceholderText(util_ui.tr('Profile name'))
-        self.createProfilePushButton.setText(util_ui.tr('Create'))
-        self.loadProfilePushButton.setText(util_ui.tr('Load profile'))
-        self.defaultProfileCheckBox.setText(util_ui.tr('Use as default'))
-        self.existingProfileGroupBox.setTitle(util_ui.tr('Load existing profile'))
-        self.newProfileGroupBox.setTitle(util_ui.tr('Create new profile'))
diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py
deleted file mode 100644
index f65a0af..0000000
--- a/toxygen/ui/main_screen.py
+++ /dev/null
@@ -1,1073 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import os
-import traceback
-
-from  qtpy import uic
-from  qtpy import QtCore, QtGui, QtWidgets
-from qtpy.QtGui import (QColor, QTextCharFormat, QFont, QSyntaxHighlighter, QFontMetrics)
-
-from ui.contact_items import *
-from ui.widgets import MultilineEdit
-from ui.main_screen_widgets import *
-import utils.util as util
-import utils.ui as util_ui
-from user_data.settings import Settings
-
-import logging
-global LOG
-LOG = logging.getLogger('app.'+'mains')
-
-iMAX = 70
-
-try:
-    # https://github.com/pyqtconsole/pyqtconsole
-    from pyqtconsole.console import PythonConsole
-    import pyqtconsole.highlighter as hl
-except Exception as e:
-    LOG.warn(e)
-    PythonConsole = None
-else:
-    if True:
-        # I want to do reverse video but I cant figure how
-        bg='white'
-        def hl_format(color, style=''):
-            """Return a QTextCharFormat with the given attributes.
-            """
-            _color = QColor()
-            _color.setNamedColor(color)
-
-            _format = QTextCharFormat()
-            _format.setForeground(_color)
-            if 'bold' in style:
-                _format.setFontWeight(QFont.Bold)
-            if 'italic' in style:
-                _format.setFontItalic(True)
-
-            _bgcolor = QColor()
-            _bgcolor.setNamedColor(bg)
-            _format.setBackground(_bgcolor)
-            return _format
-
-        aFORMATS = {
-            'keyword':    hl_format('blue', 'bold'),
-            'operator':   hl_format('red'),
-            'brace':      hl_format('darkGray'),
-            'defclass':   hl_format('black', 'bold'),
-            'string':     hl_format('magenta'),
-            'string2':    hl_format('darkMagenta'),
-            'comment':    hl_format('darkGreen', 'italic'),
-            'self':       hl_format('black', 'italic'),
-            'numbers':    hl_format('brown'),
-            'inprompt':   hl_format('darkBlue', 'bold'),
-            'outprompt':  hl_format('darkRed', 'bold'),
-        }
-    else:
-        bg = 'black'
-        def hl_format(color, style=''):
-
-            """Return a QTextCharFormat with the given attributes.
-            unused
-            """
-            _color = QColor()
-            _color.setNamedColor(color)
-
-            _format = QTextCharFormat()
-            _format.setForeground(_color)
-            if 'bold' in style:
-                _format.setFontWeight(QFont.Bold)
-            if 'italic' in style:
-                _format.setFontItalic(True)
-
-            _bgcolor = QColor()
-            _bgcolor.setNamedColor(bg)
-            _format.setBackground(_bgcolor)
-            return _format
-        aFORMATS = {
-            'keyword':    hl_format('blue', 'bold'),
-            'operator':   hl_format('red'),
-            'brace':      hl_format('lightGray'),
-            'defclass':   hl_format('white', 'bold'),
-            'string':     hl_format('magenta'),
-            'string2':    hl_format('lightMagenta'),
-            'comment':    hl_format('lightGreen', 'italic'),
-            'self':       hl_format('white', 'italic'),
-            'numbers':    hl_format('lightBrown'),
-            'inprompt':   hl_format('lightBlue', 'bold'),
-            'outprompt':  hl_format('lightRed', 'bold'),
-        }
-
-class QTextEditLogger(logging.Handler):
-    def __init__(self, parent, app):
-        super().__init__()
-        self.widget = QtWidgets.QPlainTextEdit(parent)
-        self.widget.setReadOnly(True)
-
-        if app and app._settings:
-            size = app._settings['message_font_size']
-            font_name = app._settings['font']
-        else:
-            size = 12
-            font_name = "Courier New"
-        font = QtGui.QFont(font_name, size, QtGui.QFont.Bold)
-        self.widget.setFont(font)
-
-    def emit(self, record):
-        msg = self.format(record)
-        self.widget.appendPlainText(msg)
-
-
-class LogDialog(QtWidgets.QDialog, QtWidgets.QPlainTextEdit):
-
-    def __init__(self, parent=None, app=None):
-        global iMAX
-        super().__init__(parent)
-
-        logTextBox = QTextEditLogger(self, app)
-        # You can format what is printed to text box - %(levelname)s
-        logTextBox.setFormatter(logging.Formatter('%(name)s %(asctime)-4s - %(message)s'))
-        logTextBox.setLevel(app._args.loglevel)
-        logging.getLogger().addHandler(logTextBox)
-
-        self._button = QtWidgets.QPushButton(self)
-        self._button.setText('Copy All')
-        self._logTextBox = logTextBox
-
-        layout = QtWidgets.QVBoxLayout()
-        # Add the new logging box widget to the layout
-        layout.addWidget(logTextBox.widget)
-        layout.addWidget(self._button)
-        self.setLayout(layout)
-        settings = Settings.get_default_settings(app._args)
-        #self.setBaseSize(
-        self.resize(min(iMAX * settings['message_font_size'], parent.width()), 350)
-
-        # Connect signal to slot
-        self._button.clicked.connect(self.test)
-
-    def test(self):
-        # FixMe: 65:8: E1101: Instance of 'QTextEditLogger' has no 'selectAll' member (no-member)
-        # :66:8: E1101: Instance of 'QTextEditLogger' has no 'copy' member (no-member)
-        if hasattr(self._logTextBox, 'selectAll'):
-            self._logTextBox.selectAll()
-            self._logTextBox.copy()
-
-class MainWindow(QtWidgets.QMainWindow):
-
-    def __init__(self, settings, tray, app):
-        super().__init__()
-        self._settings = settings
-        self._contacts_manager = None
-        self._tray = tray
-        self._app = app
-        self._tox = app._tox
-        self._widget_factory = None
-        self._modal_window = None
-        self._plugins_loader = None
-        self.setAcceptDrops(True)
-        self._saved = False
-        self._smiley_window = None
-        self._profile = None
-        self._toxes = None
-        self._messenger = None
-        self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None
-        self._should_show_group_peers_list = False
-        self.initUI()
-        global iMAX
-        if iMAX == 100:
-            # take a rough guess of 2/3 the default width at the default font
-            iMAX = settings['width'] * 2/3 / settings['message_font_size']
-        self._me = LogDialog(self, app)
-        self._pe = None
-        self._we = None
-
-    def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader,
-                         file_transfer_handler, history_loader, calls_manager, groups_service, toxes, app):
-        self._widget_factory = widget_factory
-        self._tray = tray
-        self._contacts_manager = contacts_manager
-        self._profile = profile
-        self._plugins_loader = plugins_loader
-        self._file_transfer_handler = file_transfer_handler
-        self._history_loader = history_loader
-        self._calls_manager = calls_manager
-        self._groups_service = groups_service
-        self._toxes = toxes
-        self._app = app
-        self._messenger = messenger
-        self._contacts_manager.active_contact_changed.add_callback(self._new_contact_selected)
-        self.messageEdit.set_dependencies(messenger, contacts_manager, file_transfer_handler)
-
-        self.update_gc_invites_button_state()
-
-    def show(self):
-        super().show()
-        self._contacts_manager.update()
-        if self._settings['show_welcome_screen']:
-            self._modal_window = self._widget_factory.create_welcome_window()
-
-    def setup_menu(self, window):
-        self.menubar = QtWidgets.QMenuBar(window)
-        self.menubar.setObjectName("menubar")
-        self.menubar.setNativeMenuBar(True) # was False
-        self.menubar.setMinimumSize(self.width(), 250)
-        self.menubar.setMaximumSize(self.width(), 32)
-        self.menubar.setBaseSize(self.width(), 250)
-
-        self.actionTest_tox = QtWidgets.QAction(window)
-        self.actionTest_tox.setObjectName("actionTest_tox")
-        self.actionTest_nmap = QtWidgets.QAction(window)
-        self.actionTest_nmap.setObjectName("actionTest_nmap")
-        self.actionTest_main = QtWidgets.QAction(window)
-        self.actionTest_main.setObjectName("actionTest_main")
-        self.actionQuit_program = QtWidgets.QAction(window)
-        self.actionQuit_program.setObjectName("actionQuit_program")
-
-        self.menuProfile = QtWidgets.QMenu(self.menubar)
-        self.menuProfile.setObjectName("menuProfile")
-        self.menuGC = QtWidgets.QMenu(self.menubar)
-        self.menuSettings = QtWidgets.QMenu(self.menubar)
-        self.menuSettings.setObjectName("menuSettings")
-        self.menuPlugins = QtWidgets.QMenu(self.menubar)
-        self.menuPlugins.setObjectName("menuPlugins")
-        self.menuAbout = QtWidgets.QMenu(self.menubar) # alignment=QtCore.Qt.AlignRight
-        self.menuAbout.setObjectName("menuAbout")
-
-        self.actionAdd_friend = QtWidgets.QAction(window)
-        self.actionAdd_friend.setObjectName("actionAdd_friend")
-
-        self.actionProfile_settings = QtWidgets.QAction(window)
-        self.actionProfile_settings.setObjectName("actionProfile_settings")
-        self.actionPrivacy_settings = QtWidgets.QAction(window)
-        self.actionPrivacy_settings.setObjectName("actionPrivacy_settings")
-        self.actionInterface_settings = QtWidgets.QAction(window)
-        self.actionInterface_settings.setObjectName("actionInterface_settings")
-        self.actionNotifications = QtWidgets.QAction(window)
-        self.actionNotifications.setObjectName("actionNotifications")
-        self.actionNetwork = QtWidgets.QAction(window)
-        self.actionNetwork.setObjectName("actionNetwork")
-        self.actionAbout_program = QtWidgets.QAction(window)
-        self.actionAbout_program.setObjectName("actionAbout_program")
-
-        self.actionLog_console = QtWidgets.QAction(window)
-        self.actionLog_console.setObjectName("actionLog_console")
-        self.actionPython_console = QtWidgets.QAction(window)
-        self.actionPython_console.setObjectName("actionLog_console")
-        self.actionWeechat_console = QtWidgets.QAction(window)
-        self.actionWeechat_console.setObjectName("actionLog_console")
-        self.updateSettings = QtWidgets.QAction(window)
-        self.actionSettings = QtWidgets.QAction(window)
-        self.actionSettings.setObjectName("actionSettings")
-        self.audioSettings = QtWidgets.QAction(window)
-        self.videoSettings = QtWidgets.QAction(window)
-        self.pluginData = QtWidgets.QAction(window)
-        self.importPlugin = QtWidgets.QAction(window)
-        self.reloadPlugins = QtWidgets.QAction(window)
-        self.reloadToxchat = QtWidgets.QAction(window)
-
-        self.lockApp = QtWidgets.QAction(window)
-        self.createGC = QtWidgets.QAction(window)
-        self.joinGC = QtWidgets.QAction(window)
-        self.gc_invites = QtWidgets.QAction(window)
-
-        self.menuProfile.addAction(self.actionAdd_friend)
-        self.menuProfile.addAction(self.actionSettings)
-        self.menuProfile.addAction(self.lockApp)
-        self.menuProfile.addAction(self.actionTest_tox)
-        self.menuProfile.addAction(self.actionTest_nmap)
-        self.menuProfile.addAction(self.actionTest_main)
-        self.menuProfile.addAction(self.actionQuit_program)
-
-        self.menuGC.addAction(self.createGC)
-        self.menuGC.addAction(self.joinGC)
-        self.menuGC.addAction(self.gc_invites)
-        self.menuSettings.addAction(self.actionProfile_settings)
-        self.menuSettings.addAction(self.actionPrivacy_settings)
-        self.menuSettings.addAction(self.actionInterface_settings)
-        self.menuSettings.addAction(self.actionNotifications)
-        self.menuSettings.addAction(self.actionNetwork)
-        self.menuSettings.addAction(self.audioSettings)
-        self.menuSettings.addAction(self.videoSettings)
-##        self.menuSettings.addAction(self.updateSettings)
-        self.menuPlugins.addAction(self.pluginData)
-        self.menuPlugins.addAction(self.importPlugin)
-        self.menuPlugins.addAction(self.reloadPlugins)
-        self.menuPlugins.addAction(self.reloadToxchat)
-        self.menuPlugins.addAction(self.actionLog_console)
-        self.menuPlugins.addAction(self.actionPython_console)
-        self.menuPlugins.addAction(self.actionWeechat_console)
-
-        self.menuAbout.addAction(self.actionAbout_program)
-
-        self.menubar.addAction(self.menuProfile.menuAction())
-        self.menubar.addAction(self.menuGC.menuAction())
-        self.menubar.addAction(self.menuSettings.menuAction())
-        self.menubar.addAction(self.menuPlugins.menuAction())
-        self.menubar.addAction(self.menuAbout.menuAction())
-
-        self.actionTest_nmap.triggered.connect(self.test_nmap)
-        self.actionTest_main.triggered.connect(self.test_main)
-        self.actionTest_tox.triggered.connect(self.test_tox)
-
-        self.actionQuit_program.triggered.connect(self.quit_program)
-        self.actionAbout_program.triggered.connect(self.about_program)
-        self.actionLog_console.triggered.connect(self.log_console)
-        self.actionPython_console.triggered.connect(self.python_console)
-        self.actionWeechat_console.triggered.connect(self.weechat_console)
-        self.actionNetwork.triggered.connect(self.network_settings)
-        self.actionAdd_friend.triggered.connect(self.add_contact_triggered)
-        self.createGC.triggered.connect(self.create_gc)
-        self.joinGC.triggered.connect(self.join_gc)
-        self.actionProfile_settings.triggered.connect(self.profile_settings)
-        self.actionPrivacy_settings.triggered.connect(self.privacy_settings)
-        self.actionInterface_settings.triggered.connect(self.interface_settings)
-        self.actionNotifications.triggered.connect(self.notification_settings)
-        self.audioSettings.triggered.connect(self.audio_settings)
-        self.videoSettings.triggered.connect(self.video_settings)
-##        self.updateSettings.triggered.connect(self.update_settings)
-        self.pluginData.triggered.connect(self.plugins_menu)
-        self.lockApp.triggered.connect(self.lock_app)
-        self.importPlugin.triggered.connect(self.import_plugin)
-        self.reloadPlugins.triggered.connect(self.reload_plugins)
-        self.reloadToxchat.triggered.connect(self.reload_toxchat)
-        self.gc_invites.triggered.connect(self._open_gc_invites_list)
-
-    def languageChange(self, *args, **kwargs):
-        self.retranslateUi()
-
-    def event(self, event):
-        if event.type() == QtCore.QEvent.WindowActivate:
-            if hasattr(self, '_tray') and self._tray:
-                self._tray.setIcon(QtGui.QIcon(util.join_path(util.get_images_directory(), 'icon.png')))
-            self.messages.repaint()
-        return super().event(event)
-
-    def status(self, line):
-        """For now, this uses the unused space on the menubar line
-           It could be a status line at the bottom, or a statusline with history."""
-        self.menuAbout.setTitle(line[:iMAX])
-        return line
-
-    def retranslateUi(self):
-        self.lockApp.setText(util_ui.tr("Lock"))
-        self.menuPlugins.setTitle(util_ui.tr("Plugins"))
-        self.menuGC.setTitle(util_ui.tr("Group chats"))
-        self.pluginData.setText(util_ui.tr("List of plugins"))
-        self.menuProfile.setTitle(util_ui.tr("Profile"))
-        self.menuSettings.setTitle(util_ui.tr("Settings"))
-        self.menuAbout.setTitle(util_ui.tr("About"))
-        self.actionAdd_friend.setText(util_ui.tr("Add contact"))
-        self.createGC.setText(util_ui.tr("Create group chat"))
-        self.joinGC.setText(util_ui.tr("Join group chat"))
-        self.gc_invites.setText(util_ui.tr("Group invites"))
-        self.actionProfile_settings.setText(util_ui.tr("Profile"))
-        self.actionPrivacy_settings.setText(util_ui.tr("Privacy"))
-        self.actionInterface_settings.setText(util_ui.tr("Interface"))
-        self.actionNotifications.setText(util_ui.tr("Notifications"))
-        self.actionNetwork.setText(util_ui.tr("Network"))
-        self.actionAbout_program.setText(util_ui.tr("About program"))
-        self.actionLog_console.setText(util_ui.tr("Console Log"))
-        self.actionPython_console.setText(util_ui.tr("Python Console"))
-        self.actionWeechat_console.setText(util_ui.tr("Weechat Console"))
-        self.actionTest_tox.setText(util_ui.tr("Bootstrap"))
-        self.actionTest_nmap.setText(util_ui.tr("Test Nodes"))
-        self.actionTest_main.setText(util_ui.tr("Test Program"))
-        self.actionQuit_program.setText(util_ui.tr("Quit program"))
-        self.actionSettings.setText(util_ui.tr("Settings"))
-        self.audioSettings.setText(util_ui.tr("Audio"))
-        self.videoSettings.setText(util_ui.tr("Video"))
-        self.updateSettings.setText(util_ui.tr("Updates"))
-        self.importPlugin.setText(util_ui.tr("Import plugin"))
-        self.reloadPlugins.setText(util_ui.tr("Reload plugins"))
-        self.reloadToxchat.setText(util_ui.tr("Reload tox.chat"))
-
-        self.searchLineEdit.setPlaceholderText(util_ui.tr("Search"))
-        self.sendMessageButton.setToolTip(util_ui.tr("Send message"))
-        self.callButton.setToolTip(util_ui.tr("Start audio call with friend"))
-        self.contactsFilterComboBox.clear()
-        self.contactsFilterComboBox.addItem(util_ui.tr("All"))
-        self.contactsFilterComboBox.addItem(util_ui.tr("Online"))
-        self.contactsFilterComboBox.addItem(util_ui.tr("Online first"))
-        self.contactsFilterComboBox.addItem(util_ui.tr("Name"))
-        self.contactsFilterComboBox.addItem(util_ui.tr("Online and by name"))
-        self.contactsFilterComboBox.addItem(util_ui.tr("Online first and by name"))
-        self.contactsFilterComboBox.addItem(util_ui.tr("Kind"))
-
-    def setup_right_bottom(self, Form):
-        Form.resize(650, 60)
-        self.messageEdit = MessageArea(Form, self)
-        self.messageEdit.setGeometry(QtCore.QRect(0, 3, 450, 55))
-        font = QtGui.QFont()
-        font.setPointSize(11)
-        font.setBold(True)
-        font.setFamily(self._settings['font'])
-        self.messageEdit.setFont(font)
-
-        self.sendMessageButton = QtWidgets.QPushButton(Form)
-        self.sendMessageButton.setGeometry(QtCore.QRect(565, 3, 60, 55))
-
-        self.menuButton = MenuButton(Form, self.show_menu)
-        self.menuButton.setGeometry(QtCore.QRect(QtCore.QRect(455, 3, 55, 55)))
-
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'send.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.sendMessageButton.setIcon(icon)
-        self.sendMessageButton.setIconSize(QtCore.QSize(45, 60))
-
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.menuButton.setIcon(icon)
-        self.menuButton.setIconSize(QtCore.QSize(40, 40))
-
-        self.sendMessageButton.clicked.connect(self.send_message)
-
-        QtCore.QMetaObject.connectSlotsByName(Form)
-
-    def setup_left_column(self, left_column):
-        uic.loadUi(util.get_views_path('ms_left_column'), left_column)
-
-        pixmap = QtGui.QPixmap()
-        pixmap.load(util.join_path(util.get_images_directory(), 'search.png'))
-        left_column.searchLabel.setPixmap(pixmap)
-
-        self.name = DataLabel(left_column)
-        self.name.setGeometry(QtCore.QRect(75, 15, 150, 25))
-        font = QtGui.QFont()
-        font.setFamily(self._settings['font'])
-        font.setPointSize(14)
-        font.setBold(True)
-        self.name.setFont(font)
-
-        self.status_message = DataLabel(left_column)
-        self.status_message.setGeometry(QtCore.QRect(75, 35, 170, 25))
-
-        self.connection_status = StatusCircle(left_column)
-        self.connection_status.setGeometry(QtCore.QRect(230, 10, 32, 32))
-
-        left_column.contactsFilterComboBox.activated[int].connect(lambda x: self._filtering())
-
-        self.avatar_label = left_column.avatarLabel
-        self.searchLineEdit = left_column.searchLineEdit
-        self.contacts_filter = self.contactsFilterComboBox = left_column.contactsFilterComboBox
-
-        self.groupInvitesPushButton = left_column.groupInvitesPushButton
-
-        self.groupInvitesPushButton.clicked.connect(self._open_gc_invites_list)
-        self.avatar_label.mouseReleaseEvent = self.profile_settings
-        self.status_message.mouseReleaseEvent = self.profile_settings
-        self.name.mouseReleaseEvent = self.profile_settings
-
-        self.friends_list = left_column.friendsListWidget
-        self.friends_list.itemSelectionChanged.connect(self._selected_contact_changed)
-        self.friends_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
-        self.friends_list.customContextMenuRequested.connect(self._friend_right_click)
-        self.friends_list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
-        self.friends_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
-        self.friends_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-        self.friends_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu)
-
-    def setup_right_top(self, Form):
-        Form.resize(650, 75)
-        self.account_avatar = QtWidgets.QLabel(Form)
-        self.account_avatar.setGeometry(QtCore.QRect(10, 5, 64, 64))
-        self.account_avatar.setScaledContents(False)
-        self.account_name = DataLabel(Form)
-        self.account_name.setGeometry(QtCore.QRect(100, 0, 400, 25))
-        self.account_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse)
-        font = QtGui.QFont()
-        font.setFamily(self._settings['font'])
-        font.setPointSize(14)
-        font.setBold(True)
-        self.account_name.setFont(font)
-        self.account_status = DataLabel(Form)
-        self.account_status.setGeometry(QtCore.QRect(100, 20, 400, 25))
-        self.account_status.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse)
-        font.setPointSize(12)
-        font.setBold(False)
-        self.account_status.setFont(font)
-        self.account_status.setObjectName("account_status")
-        self.callButton = QtWidgets.QPushButton(Form)
-        self.callButton.setGeometry(QtCore.QRect(550, 5, 50, 50))
-        self.callButton.setObjectName("callButton")
-        self.callButton.clicked.connect(lambda: self._calls_manager.call_click(True))
-        self.videocallButton = QtWidgets.QPushButton(Form)
-        self.videocallButton.setGeometry(QtCore.QRect(550, 5, 50, 50))
-        self.videocallButton.setObjectName("videocallButton")
-        self.videocallButton.clicked.connect(lambda: self._calls_manager.call_click(True, True))
-        self.groupMenuButton = QtWidgets.QPushButton(Form)
-        self.groupMenuButton.setGeometry(QtCore.QRect(470, 10, 50, 50))
-        self.groupMenuButton.clicked.connect(self._toggle_gc_peers_list)
-        self.groupMenuButton.setVisible(False)
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.groupMenuButton.setIcon(icon)
-        self.groupMenuButton.setIconSize(QtCore.QSize(45, 60))
-        self.update_call_state('call')
-        self.typing = QtWidgets.QLabel(Form)
-        self.typing.setGeometry(QtCore.QRect(500, 25, 50, 30))
-        pixmap = QtGui.QPixmap(QtCore.QSize(50, 30))
-        pixmap.load(util.join_path(util.get_images_directory(), 'typing.png'))
-        self.typing.setScaledContents(False)
-        self.typing.setPixmap(pixmap.scaled(50, 30, QtCore.Qt.KeepAspectRatio))
-        self.typing.setVisible(False)
-        QtCore.QMetaObject.connectSlotsByName(Form)
-
-    def setup_right_center(self, widget):
-        self.messages = QtWidgets.QListWidget(widget)
-        self.messages.setGeometry(0, 0, 620, 310)
-        self.messages.setObjectName("messages")
-        self.messages.setSpacing(1)
-        self.messages.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
-        self.messages.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-        self.messages.focusOutEvent = lambda event: self.messages.clearSelection()
-        self.messages.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu)
-
-        def load(pos):
-            if not pos:
-                contact = self._contacts_manager.get_curr_contact()
-                self._history_loader.load_history(contact)
-                self.messages.verticalScrollBar().setValue(1)
-        self.messages.verticalScrollBar().valueChanged.connect(load)
-        self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
-        self.messages.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
-
-        self.peers_list = QtWidgets.QListWidget(widget)
-        self.peers_list.setGeometry(0, 0, 0, 0)
-        self.peers_list.setObjectName("peersList")
-        self.peers_list.setSpacing(1)
-        self.peers_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
-        self.peers_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-        self.peers_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu)
-        self.peers_list.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
-
-    def initUI(self):
-        self.setMinimumSize(920, 500)
-        s = self._settings
-        self.setGeometry(s['x'], s['y'], s['width'], s['height'])
-        self.setWindowTitle('Toxygen')
-        menu = QtWidgets.QWidget()
-        main = QtWidgets.QWidget()
-        grid = QtWidgets.QGridLayout()
-        info = QtWidgets.QWidget()
-        left_column = QtWidgets.QWidget()
-        messages = QtWidgets.QWidget()
-        message_buttons = QtWidgets.QWidget()
-        self.setup_right_center(messages)
-        self.setup_right_top(info)
-        self.setup_right_bottom(message_buttons)
-        self.setup_left_column(left_column)
-        self.setup_menu(menu)
-        if not s['mirror_mode']:
-            grid.addWidget(left_column, 1, 0, 4, 1)
-            grid.addWidget(messages, 2, 1, 2, 1)
-            grid.addWidget(info, 1, 1)
-            grid.addWidget(message_buttons, 4, 1)
-            grid.setColumnMinimumWidth(1, 500)
-            grid.setColumnMinimumWidth(0, 270)
-        else:
-            grid.addWidget(left_column, 1, 1, 4, 1)
-            grid.addWidget(messages, 2, 0, 2, 1)
-            grid.addWidget(info, 1, 0)
-            grid.addWidget(message_buttons, 4, 0)
-            grid.setColumnMinimumWidth(0, 500)
-            grid.setColumnMinimumWidth(1, 270)
-
-        grid.addWidget(menu, 0, 0, 1, 2)
-        grid.setSpacing(0)
-        grid.setContentsMargins(0, 0, 0, 0)
-        grid.setRowMinimumHeight(0, 25)
-        grid.setRowMinimumHeight(1, 75)
-        grid.setRowMinimumHeight(2, 25)
-        grid.setRowMinimumHeight(3, 320)
-        grid.setRowMinimumHeight(4, 55)
-        grid.setColumnStretch(1, 1)
-        grid.setRowStretch(3, 1)
-        main.setLayout(grid)
-        self.setCentralWidget(main)
-        self.messageEdit.setFocus()
-        self.friend_info = info
-        self.retranslateUi()
-
-    def closeEvent(self, event):
-        close_setting = self._settings['close_app']
-        if close_setting == 0 or self._settings.closing:
-            if self._saved:
-                return
-            self._saved = True
-            self._settings['x'] = self.geometry().x()
-            self._settings['y'] = self.geometry().y()
-            self._settings['width'] = self.width()
-            self._settings['height'] = self.height()
-            self._settings.save()
-            util_ui.close_all_windows()
-            event.accept()
-        elif close_setting == 2 and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable():
-            event.ignore()
-            self.hide()
-        else:
-            event.ignore()
-            self.showMinimized()
-
-    def close_window(self):
-        self._settings.closing = True
-        self.close()
-
-    def resizeEvent(self, *args, **kwargs):
-        width = self.width() - 270
-        if not self._should_show_group_peers_list:
-            self.messages.setGeometry(0, 0, width, self.height() - 155)
-            self.peers_list.setGeometry(0, 0, 0, 0)
-        else:
-            self.messages.setGeometry(0, 0, width * 3 // 4, self.height() - 155)
-            self.peers_list.setGeometry(width * 3 // 4, 0, width - width * 3 // 4, self.height() - 155)
-
-        invites_button_visible = self.groupInvitesPushButton.isVisible()
-#        LOG.debug(f"invites_button_visible={invites_button_visible}")
-        self.friends_list.setGeometry(0, 125 if invites_button_visible else 100,
-                                      270, self.height() - 150 if invites_button_visible else self.height() - 125)
-
-        self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 10, 50, 50))
-        self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 10, 50, 50))
-        self.groupMenuButton.setGeometry(QtCore.QRect(self.width() - 450, 10, 50, 50))
-        self.typing.setGeometry(QtCore.QRect(self.width() - 450, 20, 50, 30))
-
-        self.messageEdit.setGeometry(QtCore.QRect(55, 0, self.width() - 395, 55))
-        self.menuButton.setGeometry(QtCore.QRect(0, 0, 55, 55))
-        self.sendMessageButton.setGeometry(QtCore.QRect(self.width() - 340, 0, 70, 55))
-
-        self.account_name.setGeometry(QtCore.QRect(100, 15, self.width() - 560, 25))
-        self.account_status.setGeometry(QtCore.QRect(100, 35, self.width() - 560, 25))
-        self.messageEdit.setFocus()
-
-    def keyPressEvent(self, event):
-        key, modifiers = event.key(), event.modifiers()
-        if key == QtCore.Qt.Key_Escape and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable():
-            self.hide()
-        elif key == QtCore.Qt.Key_C and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes():
-            rows = list(map(lambda x: self.messages.row(x), self.messages.selectedItems()))
-            indexes = (rows[0] - self.messages.count(), rows[-1] - self.messages.count())
-            s = self._history_loader.export_history(self._contacts_manager.get_curr_friend(), True, indexes)
-            self.copy_text(s)
-        elif key == QtCore.Qt.Key_Z and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes():
-            self.messages.clearSelection()
-        elif key == QtCore.Qt.Key_F and modifiers & QtCore.Qt.ControlModifier:
-            self.show_search_field()
-        else:
-            super().keyPressEvent(event)
-
-    # Functions which called when user click in menu
-
-    def log_console(self):
-        self._me.show()
-
-    def python_console(self):
-        if not PythonConsole: return
-        app = self._app
-        if app and app._settings:
-            size = app._settings['message_font_size']
-            font_name = app._settings['font']
-        else:
-            size = 12
-            font_name = "Courier New"
-
-        size = font_width = 10
-        font_name = "DejaVu Sans Mono"
-
-        try:
-            if not self._pe:
-                self._pe = PythonConsole(formats=aFORMATS)
-            self._pe.setWindowTitle('variable: app is the application')
-#                self._pe.edit.setStyleSheet('foreground: white; background-color: black;}')
-            # Fix the pyconsole geometry
-
-            font = self._pe.edit.document().defaultFont()
-            font.setFamily(font_name)
-            font.setBold(True)
-            if font_width is None:
-                font_width = QFontMetrics(font).width('M')
-            self._pe.setFont(font)
-            geometry = self._pe.geometry()
-            geometry.setWidth(int(font_width*50+20))
-            geometry.setHeight(int(font_width*24*13/8))
-            self._pe.setGeometry(geometry)
-            self._pe.resize(int(font_width*50+20), int(font_width*24*13/8))
-
-            self._pe.show()
-            self._pe.eval_queued()
-            # or self._pe.eval_in_thread()
-            return
-        except Exception as e:
-            LOG.warn(f"python_console EXCEPTION {e}")
-
-    def weechat_console(self):
-        if self._we:
-            self._we.show()
-            return
-        try:
-            from qweechat import qweechat
-            from qweechat.config import write
-            LOG.info("Loading WeechatConsole")
-        except ImportError as e:
-            LOG.error(f"ImportError Loading import qweechat {e} {sys.path}")
-            LOG.debug(traceback.print_exc())
-            text = f"ImportError Loading import qweechat {e} {sys.path}"
-            title = util_ui.tr('Error importing qweechat')
-            util_ui.message_box(text, title)
-            return
-
-        try:
-            # WeeChat backported from PySide6 to PyQt5
-            LOG.info("Adding WeechatConsole")
-            class WeechatConsole(qweechat.MainWindow):
-                def __init__(self, *args):
-                    qweechat.MainWindow.__init__(self, *args)
-
-                def closeEvent(self, event):
-                    """Called when QWeeChat window is closed."""
-                    self.network.disconnect_weechat()
-                    if self.network.debug_dialog:
-                        self.network.debug_dialog.close()
-                    write(self.config)
-        except Exception as e:
-            LOG.exception(f"ERROR WeechatConsole {e}")
-            MainWindow = None
-            return
-        app = self._app
-        if app and app._settings:
-            size = app._settings['message_font_size']
-            font_name = app._settings['font']
-        else:
-            size = 12
-            font_name = "Courier New"
-
-        font_name = "DejaVu Sans Mono"
-
-        try:
-            LOG.info("Creating WeechatConsole")
-            self._we = WeechatConsole()
-            self._we.show()
-            self._we.setWindowTitle('File/Connect to 127.0.0.1:9000')
-            # Fix the pyconsole geometry
-            try:
-                font = self._we.buffers[0].widget.chat.defaultFont()
-                font.setFamily(font_name)
-                font.setBold(True)
-                if font_width is None:
-                    font_width = QFontMetrics(font).width('M')
-                self._we.setFont(font)
-            except Exception as e:
-#                LOG.debug(e)
-                font_width = size
-            geometry = self._we.geometry()
-            # make this configable?
-            geometry.setWidth(int(font_width*70))
-            geometry.setHeight(int(font_width*(2+24)*11/8))
-            self._we.setGeometry(geometry)
-            #? QtCore.QSize()
-            self._we.resize(int(font_width*80+20), int(font_width*(2+24)*11/8))
-
-            self._we.list_buffers.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
-                                                QtWidgets.QSizePolicy.Preferred)
-            self._we.stacked_buffers.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
-                                                   QtWidgets.QSizePolicy.Expanding)
-
-            LOG.info("Showing WeechatConsole")
-            self._we.show()
-            # or self._we.eval_in_thread()
-            return
-        except Exception as e:
-            LOG.exception(f"Error creating WeechatConsole {e}")
-
-    def about_program(self):
-        # TODO: replace with window
-        text = util_ui.tr('Toxygen is Tox client written in Python.\nVersion: ')
-        text += '' + '\nGitHub: https://git.plastiras.org/emdee/toxygen'
-        title = util_ui.tr('About')
-        util_ui.message_box(text, title)
-
-    def network_settings(self):
-        self._modal_window = self._widget_factory.create_network_settings_window()
-        self._modal_window.show()
-
-    def plugins_menu(self):
-        self._modal_window = self._widget_factory.create_plugins_settings_window()
-        self._modal_window.show()
-
-    def add_contact_triggered(self, _):
-        self.add_contact()
-
-    def add_contact(self, link=''):
-        self._modal_window = self._widget_factory.create_add_contact_window(link)
-        self._modal_window.show()
-
-    def create_gc(self):
-        self._modal_window = self._widget_factory.create_group_screen_window()
-        self._modal_window.show()
-
-    def join_gc(self):
-        self._modal_window = self._widget_factory.create_join_group_screen_window()
-        self._modal_window.show()
-
-    def profile_settings(self, _):
-        self._modal_window = self._widget_factory.create_profile_settings_window()
-        self._modal_window.show()
-
-    def privacy_settings(self):
-        self._modal_window = self._widget_factory.create_privacy_settings_window()
-        self._modal_window.show()
-
-    def notification_settings(self):
-        self._modal_window = self._widget_factory.create_notification_settings_window()
-        self._modal_window.show()
-
-    def interface_settings(self):
-        self._modal_window = self._widget_factory.create_interface_settings_window()
-        self._modal_window.show()
-
-    def audio_settings(self):
-        self._modal_window = self._widget_factory.create_audio_settings_window()
-        self._modal_window.show()
-
-    def video_settings(self):
-        self._modal_window = self._widget_factory.create_video_settings_window()
-        self._modal_window.show()
-
-    def update_settings(self):
-        self._modal_window = self._widget_factory.create_update_settings_window()
-        self._modal_window.show()
-
-    def reload_plugins(self):
-        if hasattr(self, '_plugin_loader') and self._plugin_loader is not None:
-            self._plugin_loader.reload()
-
-    def reload_toxchat(self):
-        pass
-
-    @staticmethod
-    def import_plugin():
-        directory = util_ui.directory_dialog(util_ui.tr('Choose folder with plugins'))
-        if directory and os.path.isdir(directory):
-            src = directory + '/'
-            dest = util.get_plugins_directory()
-            util.copy(src, dest)
-            util_ui.message_box(util_ui.tr('Plugin will be loaded after restart'), util_ui.tr("Restart Toxygen"))
-
-    def lock_app(self):
-        if self._toxes.has_password():
-            self._settings.locked = True
-            self.hide()
-        else:
-            util_ui.message_box(util_ui.tr('Error. Profile password is not set.'), util_ui.tr("Cannot lock app"))
-
-    def test_tox(self):
-        self._app._test_tox()
-
-    def test_nmap(self):
-        self._app._test_nmap()
-
-    def test_main(self):
-        self._app._test_main()
-
-    def quit_program(self):
-        try:
-            self.close_window()
-            self._app._stop_app()
-        except KeyboardInterrupt:
-            pass
-        sys.stderr.write('sys.exit' +'\n')
-        # unreached?
-        sys.exit(0)
-
-    def show_menu(self):
-        if not hasattr(self, 'menu'):
-            self.menu = DropdownMenu(self)
-        self.menu.setGeometry(QtCore.QRect(0 if self._settings['mirror_mode'] else 270,
-                                           self.height() - 120,
-                                           180,
-                                           120))
-        self.menu.show()
-
-    # Messages, calls and file transfers
-
-    def send_message(self):
-        self._messenger.send_message()
-
-    def send_file(self):
-        self.menu.hide()
-        if self._contacts_manager.is_active_a_friend():
-            caption = util_ui.tr('Choose file')
-            name = util_ui.file_dialog(caption)
-            if name[0]:
-                self._file_transfer_handler.send_file(name[0], self._contacts_manager.get_active_number())
-
-    def send_screenshot(self, hide=False):
-        self.menu.hide()
-        if self._contacts_manager.is_active_a_friend():
-            self.sw = self._widget_factory.create_screenshot_window(self)
-            self.sw.show()
-            if hide:
-                self.hide()
-
-    def send_smiley(self):
-        self.menu.hide()
-        if self._contacts_manager.get_curr_contact() is None:
-            return
-        self._smiley_window = self._widget_factory.create_smiley_window(self)
-        rect = QtCore.QRect(self.menu.x(),
-                            self.menu.y() - self.menu.height(),
-                            self._smiley_window.width(),
-                            self._smiley_window.height())
-        self._smiley_window.setGeometry(rect)
-        self._smiley_window.show()
-
-    def send_sticker(self):
-        self.menu.hide()
-        if self._contacts_manager.is_active_a_friend():
-            self.sticker = self._widget_factory.create_sticker_window()
-            self.sticker.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(),
-                                                  self.y() + self.height() - 200,
-                                                  self.sticker.width(),
-                                                  self.sticker.height()))
-            self.sticker.show()
-
-    def active_call(self):
-        self.update_call_state('finish_call')
-
-    def incoming_call(self):
-        self.update_call_state('incoming_call')
-
-    def call_finished(self):
-        self.update_call_state('call')
-
-    def update_call_state(self, state):
-        pixmap = QtGui.QPixmap(os.path.join(util.get_images_directory(), '{}.png'.format(state)))
-        icon = QtGui.QIcon(pixmap)
-        self.callButton.setIcon(icon)
-        self.callButton.setIconSize(QtCore.QSize(50, 50))
-
-        pixmap = QtGui.QPixmap(os.path.join(util.get_images_directory(), '{}_video.png'.format(state)))
-        icon = QtGui.QIcon(pixmap)
-        self.videocallButton.setIcon(icon)
-        self.videocallButton.setIconSize(QtCore.QSize(35, 35))
-
-    # Functions which called when user open context menu in friends list
-
-    def _friend_right_click(self, pos):
-        item = self.friends_list.itemAt(pos)
-        number = self.friends_list.indexFromItem(item).row()
-        contact = self._contacts_manager.get_contact(number)
-        if contact is None or item is None:
-            return
-        generator = contact.get_context_menu_generator()
-        self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, self._settings, number,
-                                           self._groups_service, self._history_loader)
-        parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0))
-        self.listMenu.move(parent_position + pos)
-        self.listMenu.show()
-
-    def show_note(self, friend):
-        note = self._settings['notes'][friend.tox_id] if friend.tox_id in self._settings['notes'] else ''
-        user = util_ui.tr('Notes about user')
-        user = '{} {}'.format(user, friend.name)
-
-        def save_note(text):
-            if friend.tox_id in self._settings['notes']:
-                del self._settings['notes'][friend.tox_id]
-            if text:
-                self._settings['notes'][friend.tox_id] = text
-            self._settings.save()
-        self.note = MultilineEdit(user, note, save_note)
-        self.note.show()
-
-    def set_alias(self, num):
-        self._contacts_manager.set_alias(num)
-
-    def remove_friend(self, num):
-        self._contacts_manager.delete_friend(num)
-
-    def block_friend(self, num):
-        friend = self._contacts_manager.get_contact(num)
-        self._contacts_manager.block_user(friend.tox_id)
-
-    @staticmethod
-    def copy_text(text):
-        util_ui.copy_to_clipboard(text)
-
-    def auto_accept(self, num, value):
-        tox_id = self._contacts_manager.friend_public_key(num)
-        if value:
-            self._settings['auto_accept_from_friends'].append(tox_id)
-        else:
-            self._settings['auto_accept_from_friends'].remove(tox_id)
-        self._settings.save()
-
-    def invite_friend_to_gc(self, friend_number, group_number):
-        self._contacts_manager.invite_friend(friend_number, group_number)
-
-    def select_contact_row(self, row_index):
-        self.friends_list.setCurrentRow(row_index)
-
-    # Functions which called when user click somewhere else
-
-    def _selected_contact_changed(self):
-        num = self.friends_list.currentRow()
-        if self._contacts_manager.active_contact != num:
-            self._contacts_manager.active_contact = num
-        self.groupMenuButton.setVisible(self._contacts_manager.is_active_a_group())
-
-    def mouseReleaseEvent(self, event):
-        pos = self.connection_status.pos()
-        x, y = pos.x(), pos.y() + 25
-        if (x < event.x() < x + 32) and (y < event.y() < y + 32):
-            self._profile.change_status()
-        else:
-            super().mouseReleaseEvent(event)
-
-    def _filtering(self):
-        index = self.contactsFilterComboBox.currentIndex()
-        search_text = self.searchLineEdit.text()
-        self._contacts_manager.filtration_and_sorting(index, search_text)
-
-    def show_search_field(self):
-        if hasattr(self, 'search_field') and self.search_field.isVisible():
-            #?
-            self.search_field.show()
-            return
-        if not hasattr(self._contacts_manager, 'get_curr_friend') or \
-            self._contacts_manager.get_curr_friend() is None:
-            #? return
-            pass
-        self.search_field = self._widget_factory.create_search_screen(self.messages)
-        x, y = self.messages.x(), self.messages.y() + self.messages.height() - 40
-        self.search_field.setGeometry(x, y, self.messages.width(), 40)
-        self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40)
-        if self._should_show_group_peers_list:
-            self.peers_list.setFixedHeight(self.peers_list.height() - 40)
-        self.search_field.show()
-
-    def _toggle_gc_peers_list(self):
-        self._should_show_group_peers_list = not self._should_show_group_peers_list
-        self.resizeEvent()
-        if self._should_show_group_peers_list:
-            self._groups_service.generate_peers_list()
-
-    def _new_contact_selected(self, _):
-        if self._should_show_group_peers_list:
-            self._toggle_gc_peers_list()
-        index = self.friends_list.currentRow()
-        if self._contacts_manager.active_contact != index:
-            self.friends_list.setCurrentRow(self._contacts_manager.active_contact)
-        self.resizeEvent()
-
-    def _open_gc_invites_list(self):
-        self._modal_window = self._widget_factory.create_group_invites_window()
-        self._modal_window.show()
-
-    def update_gc_invites_button_state(self):
-        invites_count = self._groups_service.group_invites_count
-        LOG.debug(f"update_gc_invites_button_state invites_count={invites_count}")
-
-        # Fixme
-        self.groupInvitesPushButton.setVisible(True) # invites_count > 0
-        text = util_ui.tr(f'{invites_count} new invites to group chats')
-        self.groupInvitesPushButton.setText(text)
-        self.resizeEvent()
diff --git a/toxygen/ui/main_screen_widgets.py b/toxygen/ui/main_screen_widgets.py
deleted file mode 100644
index 064cb09..0000000
--- a/toxygen/ui/main_screen_widgets.py
+++ /dev/null
@@ -1,512 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import urllib
-import re
-
-from  qtpy import QtCore, QtGui, QtWidgets
-from  qtpy.QtCore import Signal
-
-from ui.widgets import RubberBandWindow, create_menu, QRightClickButton, CenteredWidget, LineEdit
-import utils.util as util
-import utils.ui as util_ui
-from stickers.stickers import load_stickers
-
-import logging
-LOG = logging.getLogger('app.'+'msw')
-
-class MessageArea(QtWidgets.QPlainTextEdit):
-    """User types messages here"""
-
-    def __init__(self, parent, form):
-        super().__init__(parent)
-        self._messenger = None
-        self._contacts_manager = self._file_transfer_handler = None
-        self.parent = form
-        self.setAcceptDrops(True)
-        self._timer = QtCore.QTimer(self)
-        self._timer.timeout.connect(lambda: self._messenger.send_typing(False))
-
-    def set_dependencies(self, messenger, contacts_manager, file_transfer_handler):
-        self._messenger = messenger
-        self._contacts_manager = contacts_manager
-        self._file_transfer_handler = file_transfer_handler
-
-    def keyPressEvent(self, event):
-        if event.matches(QtGui.QKeySequence.Paste):
-            mimeData = QtWidgets.QApplication.clipboard().mimeData()
-            if mimeData.hasUrls():
-                for url in mimeData.urls():
-                    self.pasteEvent(url.toString())
-            else:
-                self.pasteEvent()
-
-        elif event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
-            modifiers = event.modifiers()
-            if modifiers & QtCore.Qt.ControlModifier or modifiers & QtCore.Qt.ShiftModifier:
-                self.insertPlainText('\n')
-            else:
-                if self._timer.isActive():
-                    self._timer.stop()
-                try:
-                    self._messenger.send_typing(False)
-                    self._messenger.send_message()
-                except Exception as e:
-                    LOG.error(f"keyPressEvent ERROR send_message to {self._messenger}")
-                    util_ui.message_box(str(e),
-                                        util_ui.tr(f"keyPressEvent ERROR send_message to {self._messenger}"))
-
-        elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText():
-            self.appendPlainText(self._messenger.get_last_message())
-
-        elif event.key() == QtCore.Qt.Key_Tab and self._contacts_manager.is_active_a_group():
-            text = self.toPlainText()
-            text_cursor = self.textCursor()
-            pos = text_cursor.position()
-            current_word = re.split(r"\s+", text[:pos])[-1]
-            start_index = text.rindex(current_word, 0, pos)
-            peer_name = self._contacts_manager.get_gc_peer_name(current_word)
-            self.setPlainText(text[:start_index] + peer_name + text[pos:])
-            new_pos = start_index + len(peer_name)
-            text_cursor.setPosition(new_pos, QtGui.QTextCursor.MoveAnchor)
-            self.setTextCursor(text_cursor)
-        else:
-            self._messenger.send_typing(True)
-            if self._timer.isActive():
-                self._timer.stop()
-            self._timer.start(5000)
-            super().keyPressEvent(event)
-
-    def contextMenuEvent(self, event):
-        menu = create_menu(self.createStandardContextMenu())
-        menu.exec_(event.globalPos())
-        del menu
-
-    def dragEnterEvent(self, e):
-        e.accept()
-
-    def dragMoveEvent(self, e):
-        e.accept()
-
-    def dropEvent(self, e):
-        if e.mimeData().hasFormat('text/plain') or e.mimeData().hasFormat('text/html'):
-            e.accept()
-            self.pasteEvent(e.mimeData().text())
-        elif e.mimeData().hasUrls():
-            for url in e.mimeData().urls():
-                self.pasteEvent(url.toString())
-            e.accept()
-        else:
-            e.ignore()
-
-    def pasteEvent(self, text=None):
-        text = text or QtWidgets.QApplication.clipboard().text()
-        if text.startswith('file://'):
-            if not self._contacts_manager.is_active_a_friend():
-                return
-            friend_number = self._contacts_manager.get_active_number()
-            file_path = self._parse_file_path(text)
-            self._file_transfer_handler.send_file(file_path, friend_number)
-        else:
-            self.insertPlainText(text)
-
-    @staticmethod
-    def _parse_file_path(file_name):
-        if file_name.endswith('\r\n'):
-            file_name = file_name[:-2]
-        file_name = urllib.parse.unquote(file_name)
-
-        return file_name[8 if util.get_platform() == 'Windows' else 7:]
-
-
-class ScreenShotWindow(RubberBandWindow):
-
-    def __init__(self, file_transfer_handler, contacts_manager, *args):
-        super().__init__(*args)
-        self._file_transfer_handler = file_transfer_handler
-        self._contacts_manager = contacts_manager
-
-    def closeEvent(self, *args):
-        if self.parent.isHidden():
-            self.parent.show()
-
-    def mouseReleaseEvent(self, event):
-        if self.rubberband.isVisible():
-            self.rubberband.hide()
-            rect = self.rubberband.geometry()
-            if rect.width() and rect.height():
-                screen = QtWidgets.QApplication.primaryScreen()
-                p = screen.grabWindow(0,
-                                      rect.x() + 4,
-                                      rect.y() + 4,
-                                      rect.width() - 8,
-                                      rect.height() - 8)
-                byte_array = QtCore.QByteArray()
-                buffer = QtCore.QBuffer(byte_array)
-                buffer.open(QtCore.QIODevice.WriteOnly)
-                p.save(buffer, 'PNG')
-                friend = self._contacts_manager.get_curr_contact()
-                self._file_transfer_handler.send_screenshot(bytes(byte_array.data()), friend.number)
-            self.close()
-
-
-class SmileyWindow(QtWidgets.QWidget):
-    """
-    Smiley selection window
-    """
-
-    def __init__(self, parent, smiley_loader):
-        super().__init__(parent)
-        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
-        self._parent = parent
-        self._data = smiley_loader.get_smileys()
-
-        count = len(self._data)
-        if not count:
-            self.close()
-
-        self._page_size = int(pow(count / 8, 0.5) + 1) * 8  # smileys per page
-        if count % self._page_size == 0:
-            self._page_count = count // self._page_size
-        else:
-            self._page_count = round(count / self._page_size + 0.5)
-        self._page = -1
-        self._radio = []
-
-        for i in range(self._page_count):  # pages - radio buttons
-            elem = QtWidgets.QRadioButton(self)
-            elem.setGeometry(QtCore.QRect(i * 20 + 5, 160, 20, 20))
-            elem.clicked.connect(lambda c, t=i: self._checked(t))
-            self._radio.append(elem)
-
-        width = max(self._page_count * 20 + 30, (self._page_size + 5) * 8 // 10)
-        self.setMaximumSize(width, 200)
-        self.setMinimumSize(width, 200)
-        self._buttons = []
-
-        for i in range(self._page_size):  # buttons with smileys
-            b = QtWidgets.QPushButton(self)
-            b.setGeometry(QtCore.QRect((i // 8) * 20 + 5, (i % 8) * 20, 20, 20))
-            b.clicked.connect(lambda c, t=i: self._clicked(t))
-            self._buttons.append(b)
-        self._checked(0)
-
-    def leaveEvent(self, event):
-        self.close()
-
-    def _checked(self, pos):  # new page opened
-        self._radio[self._page].setChecked(False)
-        self._radio[pos].setChecked(True)
-        self._page = pos
-        start = self._page * self._page_size
-        for i in range(self._page_size):
-            try:
-                self._buttons[i].setVisible(True)
-                pixmap = QtGui.QPixmap(self._data[start + i][1])
-                icon = QtGui.QIcon(pixmap)
-                self._buttons[i].setIcon(icon)
-            except:
-                self._buttons[i].setVisible(False)
-
-    def _clicked(self, pos):  # smiley selected
-        pos += self._page * self._page_size
-        smiley = self._data[pos][0]
-        self._parent.messageEdit.insertPlainText(smiley)
-        self.close()
-
-
-class MenuButton(QtWidgets.QPushButton):
-
-    def __init__(self, parent, enter):
-        super().__init__(parent)
-        self.enter = enter
-
-    def enterEvent(self, event):
-        self.enter()
-        super().enterEvent(event)
-
-
-class DropdownMenu(QtWidgets.QWidget):
-
-    def __init__(self, parent):
-        super().__init__(parent)
-        self.installEventFilter(self)
-        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
-        self.setMaximumSize(120, 120)
-        self.setMinimumSize(120, 120)
-        self.screenshotButton = QRightClickButton(self)
-        self.screenshotButton.setGeometry(QtCore.QRect(0, 60, 60, 60))
-        self.screenshotButton.setObjectName("screenshotButton")
-
-        self.fileTransferButton = QtWidgets.QPushButton(self)
-        self.fileTransferButton.setGeometry(QtCore.QRect(60, 60, 60, 60))
-        self.fileTransferButton.setObjectName("fileTransferButton")
-
-        self.smileyButton = QtWidgets.QPushButton(self)
-        self.smileyButton.setGeometry(QtCore.QRect(0, 0, 60, 60))
-
-        self.stickerButton = QtWidgets.QPushButton(self)
-        self.stickerButton.setGeometry(QtCore.QRect(60, 0, 60, 60))
-
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'file.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.fileTransferButton.setIcon(icon)
-        self.fileTransferButton.setIconSize(QtCore.QSize(50, 50))
-
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'screenshot.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.screenshotButton.setIcon(icon)
-        self.screenshotButton.setIconSize(QtCore.QSize(50, 60))
-
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'smiley.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.smileyButton.setIcon(icon)
-        self.smileyButton.setIconSize(QtCore.QSize(50, 50))
-
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'sticker.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.stickerButton.setIcon(icon)
-        self.stickerButton.setIconSize(QtCore.QSize(55, 55))
-
-        self.screenshotButton.setToolTip(util_ui.tr("Send screenshot"))
-        self.fileTransferButton.setToolTip(util_ui.tr("Send file"))
-        self.smileyButton.setToolTip(util_ui.tr("Add smiley"))
-        self.stickerButton.setToolTip(util_ui.tr("Send sticker"))
-
-        self.fileTransferButton.clicked.connect(parent.send_file)
-        self.screenshotButton.clicked.connect(parent.send_screenshot)
-        self.screenshotButton.rightClicked.connect(lambda: parent.send_screenshot(True))
-        self.smileyButton.clicked.connect(parent.send_smiley)
-        self.stickerButton.clicked.connect(parent.send_sticker)
-
-    def leaveEvent(self, event):
-        self.close()
-
-    def eventFilter(self, obj, event):
-        if event.type() == QtCore.QEvent.WindowDeactivate:
-            self.close()
-        return False
-
-
-class StickerItem(QtWidgets.QWidget):
-
-    def __init__(self, fl):
-        super().__init__()
-        self._image_label = QtWidgets.QLabel(self)
-        self.path = fl
-        self.pixmap = QtGui.QPixmap()
-        self.pixmap.load(fl)
-        if self.pixmap.width() > 150:
-            self.pixmap = self.pixmap.scaled(150, 200, QtCore.Qt.KeepAspectRatio)
-        self.setFixedSize(150, self.pixmap.height())
-        self._image_label.setPixmap(self.pixmap)
-
-
-class StickerWindow(QtWidgets.QWidget):
-    """Sticker selection window"""
-
-    def __init__(self, file_transfer_handler, contacts_manager):
-        super().__init__()
-        self._file_transfer_handler = file_transfer_handler
-        self._contacts_manager = contacts_manager
-        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
-        self.setMaximumSize(250, 200)
-        self.setMinimumSize(250, 200)
-        self.list = QtWidgets.QListWidget(self)
-        self.list.setGeometry(QtCore.QRect(0, 0, 250, 200))
-        self._stickers = load_stickers()
-        for sticker in self._stickers:
-            item = StickerItem(sticker)
-            elem = QtWidgets.QListWidgetItem()
-            elem.setSizeHint(QtCore.QSize(250, item.height()))
-            self.list.addItem(elem)
-            self.list.setItemWidget(elem, item)
-        self.list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
-        self.list.setSpacing(3)
-        self.list.clicked.connect(self.click)
-
-    def click(self, index):
-        num = index.row()
-        friend = self._contacts_manager.get_curr_contact()
-        self._file_transfer_handler.send_sticker(self._stickers[num], friend.number)
-        self.close()
-
-    def leaveEvent(self, event):
-        self.close()
-
-
-class WelcomeScreen(CenteredWidget):
-
-    def __init__(self, settings):
-        super().__init__()
-        self._settings = settings
-        self.setMaximumSize(250, 200)
-        self.setMinimumSize(250, 200)
-        self.center()
-        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
-        self.text = QtWidgets.QTextBrowser(self)
-        self.text.setGeometry(QtCore.QRect(0, 0, 250, 170))
-        self.text.setOpenExternalLinks(True)
-        self.checkbox = QtWidgets.QCheckBox(self)
-        self.checkbox.setGeometry(QtCore.QRect(5, 170, 240, 30))
-        self.checkbox.setText(util_ui.tr( "Don't show again"))
-        self.setWindowTitle(util_ui.tr( 'Tip of the day'))
-        import random
-        num = random.randint(0, 10)
-        if num == 0:
-            text = util_ui.tr('Press Esc if you want hide app to tray.')
-        elif num == 1:
-            text = util_ui.tr('Right click on screenshot button hides app to tray during screenshot.')
-        elif num == 2:
-            text = util_ui.tr('You can use Tox over Tor. For more info read <a href="https://wiki.tox.chat/users/tox_over_tor_tot">this post</a>')
-        elif num == 3:
-            text = util_ui.tr('Use Settings -> Interface to customize interface.')
-        elif num == 4:
-            text = util_ui.tr('Set profile password via Profile -> Settings. Password allows Toxygen encrypt your history and settings.')
-        elif num == 5:
-            text = util_ui.tr('Since v0.1.3 Toxygen supports plugins. <a href="https://github.com/toxygen-project/toxygen/blob/master/docs/plugins.md">Read more</a>')
-        elif num == 6:
-            text = util_ui.tr('Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.')
-        elif num == 7:
-            text = util_ui.tr('New in Toxygen 0.4.1:<br>Downloading nodes from tox.chat<br>Bug fixes')
-        elif num == 8:
-            text = util_ui.tr('Delete single message in chat: make right click on spinner or message time and choose "Delete" in menu')
-        elif num == 9:
-            text = util_ui.tr( 'Use right click on inline image to save it')
-        else:
-            text = util_ui.tr('Set new NoSpam to avoid spam friend requests: Profile -> Settings -> Set new NoSpam.')
-        self.text.setHtml(text)
-        self.checkbox.stateChanged.connect(self.not_show)
-        QtCore.QTimer.singleShot(1000, self.show)
-
-    def not_show(self):
-        self._settings['show_welcome_screen'] = False
-        self._settings.save()
-
-
-class MainMenuButton(QtWidgets.QPushButton):
-
-    def __init__(self, *args):
-        super().__init__(*args)
-        self.setObjectName("mainmenubutton")
-
-    def setText(self, text):
-        metrics = QtGui.QFontMetrics(self.font())
-        self.setFixedWidth(metrics.size(QtCore.Qt.TextSingleLine, text).width() + 20)
-        super().setText(text)
-
-
-class ClickableLabel(QtWidgets.QLabel):
-    # FixMe: AttributeError: module 'qtpy.QtCore' has no attribute 'pyqtSignal'
-    clicked = Signal()
-
-    def __init__(self, *args):
-        super().__init__(*args)
-
-    def mouseReleaseEvent(self, ev):
-        self.clicked.emit()
-
-
-class SearchScreen(QtWidgets.QWidget):
-
-    def __init__(self, contacts_manager, history_loader, messages, width, *args):
-        super().__init__(*args)
-        self._contacts_manager = contacts_manager
-        self._history_loader = history_loader
-        self.setMaximumSize(width, 40)
-        self.setMinimumSize(width, 40)
-        self._messages = messages
-
-        self.search_text = LineEdit(self)
-        self.search_text.setGeometry(0, 0, width - 160, 40)
-
-        self.search_button = ClickableLabel(self)
-        self.search_button.setGeometry(width - 160, 0, 40, 40)
-        pixmap = QtGui.QPixmap()
-        pixmap.load(util.join_path(util.get_images_directory(), 'search.png'))
-        self.search_button.setScaledContents(False)
-        self.search_button.setAlignment(QtCore.Qt.AlignCenter)
-        self.search_button.setPixmap(pixmap)
-        self.search_button.clicked.connect(self.search)
-
-        font = QtGui.QFont()
-        font.setPointSize(32)
-        font.setBold(True)
-
-        self.prev_button = QtWidgets.QPushButton(self)
-        self.prev_button.setGeometry(width - 120, 0, 40, 40)
-        self.prev_button.clicked.connect(self.prev)
-        self.prev_button.setText('\u25B2')
-
-        self.next_button = QtWidgets.QPushButton(self)
-        self.next_button.setGeometry(width - 80, 0, 40, 40)
-        self.next_button.clicked.connect(self.next)
-        self.next_button.setText('\u25BC')
-
-        self.close_button = QtWidgets.QPushButton(self)
-        self.close_button.setGeometry(width - 40, 0, 40, 40)
-        self.close_button.clicked.connect(self.close)
-        self.close_button.setText('×')
-        self.close_button.setFont(font)
-
-        font.setPointSize(18)
-        self.next_button.setFont(font)
-        self.prev_button.setFont(font)
-
-        self.retranslateUi()
-
-    def retranslateUi(self):
-        self.search_text.setPlaceholderText(util_ui.tr('Search'))
-
-    def show(self):
-        super().show()
-        self.search_text.setFocus()
-
-    def search(self):
-        self._contacts_manager.update()
-        text = self.search_text.text()
-        contact = self._contacts_manager.get_curr_contact()
-        if text and contact and util.is_re_valid(text):
-            index = contact.search_string(text)
-            self.load_messages(index)
-
-    def prev(self):
-        contact = self._contacts_manager.get_curr_contact()
-        if contact is not None:
-            index = contact.search_prev()
-            self.load_messages(index)
-
-    def next(self):
-        contact = self._contacts_manager.get_curr_contact()
-        text = self.search_text.text()
-        if contact is not None:
-            index = contact.search_next()
-            if index is not None:
-                count = self._messages.count()
-                index += count
-                item = self._messages.item(index)
-                self._messages.scrollToItem(item)
-                self._messages.itemWidget(item).select_text(text)
-            else:
-                self.not_found(text)
-
-    def load_messages(self, index):
-        text = self.search_text.text()
-        if index is not None:
-            count = self._messages.count()
-            while count + index < 0:
-                self._history_loader.load_history()
-                count = self._messages.count()
-            index += count
-            item = self._messages.item(index)
-            self._messages.scrollToItem(item)
-            self._messages.itemWidget(item).select_text(text)
-        else:
-            self.not_found(text)
-
-    def closeEvent(self, *args):
-        self._messages.setGeometry(0, 0, self._messages.width(), self._messages.height() + 40)
-        super().closeEvent(*args)
-
-    @staticmethod
-    def not_found(text):
-        util_ui.message_box(util_ui.tr('Text "{}" was not found').format(text), util_ui.tr('Not found'))
diff --git a/toxygen/ui/menu.py b/toxygen/ui/menu.py
deleted file mode 100644
index 9f0fc05..0000000
--- a/toxygen/ui/menu.py
+++ /dev/null
@@ -1,804 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from  qtpy import QtCore, QtGui, QtWidgets, uic
-
-import toxygen_wrapper.tests.support_testing as ts
-with ts.ignoreStderr(): # not out
-    import pyaudio
-
-from user_data.settings import *
-from utils.util import *
-from ui.widgets import CenteredWidget, DataLabel, LineEdit, RubberBandWindow
-import updater.updater as updater
-import utils.ui as util_ui
-from user_data import settings
-
-global LOG
-import logging
-LOG = logging.getLogger('app.'+__name__)
-
-global oPYA
-oPYA = pyaudio.PyAudio()
-class AddContact(CenteredWidget):
-    """Add contact form"""
-
-    def __init__(self, dsettings, contacts_manager, tox_id=''):
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._settings = dsettings
-        self._contacts_manager = contacts_manager
-        uic.loadUi(get_views_path('add_contact_screen'), self)
-        self._update_ui(tox_id)
-        self._adding = False
-        self._bootstrap = False
-
-    def _update_ui(self, tox_id):
-        self.toxIdLineEdit = LineEdit(self)
-        self.toxIdLineEdit.setGeometry(QtCore.QRect(50, 40, 460, 30))
-        self.toxIdLineEdit.setText(tox_id)
-
-        self.messagePlainTextEdit.document().setPlainText(util_ui.tr('Hello! Please add me to your contact list.'))
-        self.addContactPushButton.clicked.connect(self._add_friend)
-
-        # self.addBootstrapPushButton.clicked.connect(self._add_bootstrap)
-        self._retranslate_ui()
-
-    def _add_bootstrap(self):
-        if self._bootstrap:
-            return
-        self._bootstrap = True
-
-    def _add_friend(self):
-        if self._adding:
-            return
-        self._adding = True
-        tox_id = self.toxIdLineEdit.text().strip()
-        if tox_id.startswith('tox:'):
-            tox_id = tox_id[4:]
-        message = self.messagePlainTextEdit.toPlainText()
-        send = self._contacts_manager.send_friend_request(tox_id, message)
-        self._adding = False
-        if send is True:
-            # request was successful
-            pass
-        elif send and type(send) == str:  # print error data
-            self.errorLabel.setText(send)
-        self.close()
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr('Add contact'))
-        self.addContactPushButton.setText(util_ui.tr('Send request'))
-        self.toxIdLabel.setText(util_ui.tr('TOX ID:'))
-        self.messageLabel.setText(util_ui.tr('Message:'))
-        self.toxIdLineEdit.setPlaceholderText(util_ui.tr('TOX ID or public key of contact'))
-
-# unfinished copy of addContact
-class AddBootstrap(CenteredWidget):
-    """Add bootstrap form"""
-
-    def __init__(self, dsettings, bootstraps_manager, tox_id=''):
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._settings = dsettings
-        self._bootstraps_manager = bootstraps_manager
-        uic.loadUi(get_views_path('add_bootstrap_screen'), self)
-        self._update_ui(tox_id)
-        self._adding = False
-        self._bootstrap = False
-
-    def _update_ui(self, tox_id):
-        self.toxIdLineEdit = LineEdit(self)
-        self.toxIdLineEdit.setGeometry(QtCore.QRect(50, 40, 460, 30))
-        self.toxIdLineEdit.setText(tox_id)
-
-        self.messagePlainTextEdit.document().setPlainText(util_ui.tr('Hello! Please add me to your bootstrap list.'))
-        self.addBootstrapPushButton.clicked.connect(self._add_friend)
-
-        # self.addBootstrapPushButton.clicked.connect(self._add_bootstrap)
-        self._retranslate_ui()
-
-    def _add_bootstrap(self):
-        if self._bootstrap:
-            return
-        self._bootstrap = True
-        tox_id = self.toxIdLineEdit.text().strip()
-        if tox_id.startswith('tox:'):
-            tox_id = tox_id[4:]
-        message = self.messagePlainTextEdit.toPlainText()
-        send = self._bootstraps_manager.send_friend_request(tox_id, message)
-        self._adding = False
-        if send is True:
-            # request was successful
-            self.close()
-        else:  # print error data
-            self.errorLabel.setText(send)
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr('Add bootstrap'))
-        self.addBootstrapPushButton.setText(util_ui.tr('Send request'))
-        self.toxIdLabel.setText(util_ui.tr('Port:'))
-        self.messageLabel.setText(util_ui.tr('Message:'))
-        self.toxIdLineEdit.setPlaceholderText(util_ui.tr('IP or hostname of public key of bootstrap'))
-
-
-class NetworkSettings(CenteredWidget):
-    """Network settings form: UDP, Ipv6 and proxy"""
-    def __init__(self, dsettings, reset):
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._settings = dsettings
-        self._reset = reset
-        uic.loadUi(get_views_path('network_settings_screen'), self)
-        self._update_ui()
-
-    def _update_ui(self):
-        self.ipLineEdit = LineEdit(self)
-        self.ipLineEdit.setGeometry(100, 280, 270, 30)
-
-        self.portLineEdit = LineEdit(self)
-        self.portLineEdit.setGeometry(100, 325, 270, 30)
-
-        self.urlLineEdit = LineEdit(self)
-        self.urlLineEdit.setGeometry(100, 370, 270, 30)
-
-        self.restartCorePushButton.clicked.connect(self._restart_core)
-        self.ipv6CheckBox.setChecked(self._settings['ipv6_enabled'])
-        self.udpCheckBox.setChecked(self._settings['udp_enabled'])
-        self.proxyCheckBox.setChecked(self._settings['proxy_type'])
-        self.ipLineEdit.setText(self._settings['proxy_host'])
-        self.portLineEdit.setText(str(self._settings['proxy_port']))
-        self.urlLineEdit.setText(str(self._settings['download_nodes_url']))
-        self.httpProxyRadioButton.setChecked(self._settings['proxy_type'] == 1)
-        self.socksProxyRadioButton.setChecked(self._settings['proxy_type'] != 1)
-        self.downloadNodesCheckBox.setChecked(self._settings['download_nodes_list'])
-        self.lanCheckBox.setChecked(self._settings['local_discovery_enabled'])
-        self._retranslate_ui()
-        self.proxyCheckBox.stateChanged.connect(lambda x: self._activate_proxy())
-        self._activate_proxy()
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr("Network settings"))
-        self.ipv6CheckBox.setText(util_ui.tr("IPv6"))
-        self.udpCheckBox.setText(util_ui.tr("UDP"))
-        self.lanCheckBox.setText(util_ui.tr("LAN"))
-        self.proxyCheckBox.setText(util_ui.tr("Proxy"))
-        self.ipLabel.setText(util_ui.tr("IP:"))
-        self.portLabel.setText(util_ui.tr("Port:"))
-        self.urlLabel.setText(util_ui.tr("ChatUrl:"))
-        self.restartCorePushButton.setText(util_ui.tr("Restart TOX core"))
-        self.httpProxyRadioButton.setText(util_ui.tr("HTTP"))
-        self.socksProxyRadioButton.setText(util_ui.tr("Socks 5"))
-        self.downloadNodesCheckBox.setText(util_ui.tr("Download nodes list from tox.chat"))
-#        self.warningLabel.setText(util_ui.tr("WARNING:\nusing proxy with enabled UDP\ncan produce IP leak"))
-        self.warningLabel.setText(util_ui.tr("Changing settings require 'Restart TOX core'"))
-
-    def _activate_proxy(self):
-        bl = self.proxyCheckBox.isChecked()
-        self.ipLineEdit.setEnabled(bl)
-        self.portLineEdit.setEnabled(bl)
-        self.httpProxyRadioButton.setEnabled(bl)
-        self.socksProxyRadioButton.setEnabled(bl)
-        self.ipLabel.setEnabled(bl)
-        self.portLabel.setEnabled(bl)
-
-    def _restart_core(self):
-        try:
-            self._settings['ipv6_enabled'] = self.ipv6CheckBox.isChecked()
-            self._settings['udp_enabled'] = self.udpCheckBox.isChecked()
-            proxy_enabled = self.proxyCheckBox.isChecked()
-            self._settings['proxy_type'] = 2 - int(self.httpProxyRadioButton.isChecked()) if proxy_enabled else 0
-            self._settings['proxy_host'] = str(self.ipLineEdit.text())
-            self._settings['proxy_port'] = int(self.portLineEdit.text())
-            self._settings['download_nodes_url'] = str(self.urlLineEdit.text())
-            self._settings['download_nodes_list'] = self.downloadNodesCheckBox.isChecked()
-            self._settings['local_discovery_enabled'] = self.lanCheckBox.isChecked()
-            self._settings.save()
-            # recreate tox instance
-            self._reset()
-            self.close()
-        except Exception as ex:
-            LOG.error('ERROR: Exception in restart: ' + str(ex))
-
-
-class PrivacySettings(CenteredWidget):
-    """Privacy settings form: history, typing notifications"""
-
-    def __init__(self, contacts_manager, dsettings):
-        """
-        :type contacts_manager: ContactsManager
-        """
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._contacts_manager = contacts_manager
-        self._settings = dsettings
-        self.initUI()
-        self.center()
-
-    def initUI(self):
-        self.setObjectName("privacySettings")
-        self.resize(370, 600)
-        self.setMinimumSize(QtCore.QSize(370, 600))
-        self.setMaximumSize(QtCore.QSize(370, 600))
-        self.saveHistory = QtWidgets.QCheckBox(self)
-        self.saveHistory.setGeometry(QtCore.QRect(10, 20, 350, 22))
-        self.saveUnsentOnly = QtWidgets.QCheckBox(self)
-        self.saveUnsentOnly.setGeometry(QtCore.QRect(10, 60, 350, 22))
-
-        self.fileautoaccept = QtWidgets.QCheckBox(self)
-        self.fileautoaccept.setGeometry(QtCore.QRect(10, 100, 350, 22))
-
-        self.typingNotifications = QtWidgets.QCheckBox(self)
-        self.typingNotifications.setGeometry(QtCore.QRect(10, 140, 350, 30))
-        self.inlines = QtWidgets.QCheckBox(self)
-        self.inlines.setGeometry(QtCore.QRect(10, 180, 350, 30))
-        self.auto_path = QtWidgets.QLabel(self)
-        self.auto_path.setGeometry(QtCore.QRect(10, 230, 350, 30))
-        self.path = QtWidgets.QPlainTextEdit(self)
-        self.path.setGeometry(QtCore.QRect(10, 265, 350, 45))
-        self.change_path = QtWidgets.QPushButton(self)
-        self.change_path.setGeometry(QtCore.QRect(10, 320, 350, 30))
-        self.typingNotifications.setChecked(self._settings['typing_notifications'])
-        self.fileautoaccept.setChecked(self._settings['allow_auto_accept'])
-        self.saveHistory.setChecked(self._settings['save_history'])
-        self.inlines.setChecked(self._settings['allow_inline'])
-        self.saveUnsentOnly.setChecked(self._settings['save_unsent_only'])
-        self.saveUnsentOnly.setEnabled(self._settings['save_history'])
-        self.saveHistory.stateChanged.connect(self.update)
-        self.path.setPlainText(self._settings['auto_accept_path'] or curr_directory())
-        self.change_path.clicked.connect(self.new_path)
-        self.block_user_label = QtWidgets.QLabel(self)
-        self.block_user_label.setGeometry(QtCore.QRect(10, 360, 350, 30))
-        self.block_id = QtWidgets.QPlainTextEdit(self)
-        self.block_id.setGeometry(QtCore.QRect(10, 390, 350, 30))
-        self.block = QtWidgets.QPushButton(self)
-        self.block.setGeometry(QtCore.QRect(10, 430, 350, 30))
-        self.block.clicked.connect(lambda: self._contacts_manager.block_user(self.block_id.toPlainText()) or self.close())
-        self.blocked_users_label = QtWidgets.QLabel(self)
-        self.blocked_users_label.setGeometry(QtCore.QRect(10, 470, 350, 30))
-        self.comboBox = QtWidgets.QComboBox(self)
-        self.comboBox.setGeometry(QtCore.QRect(10, 500, 350, 30))
-        self.comboBox.addItems(self._settings['blocked'])
-        self.unblock = QtWidgets.QPushButton(self)
-        self.unblock.setGeometry(QtCore.QRect(10, 540, 350, 30))
-        self.unblock.clicked.connect(lambda: self.unblock_user())
-        self.retranslateUi()
-        QtCore.QMetaObject.connectSlotsByName(self)
-
-    def retranslateUi(self):
-        self.setWindowTitle(util_ui.tr("Privacy settings"))
-        self.saveHistory.setText(util_ui.tr("Save chat history"))
-        self.fileautoaccept.setText(util_ui.tr("Allow file auto accept"))
-        self.typingNotifications.setText(util_ui.tr("Send typing notifications"))
-        self.auto_path.setText(util_ui.tr("Auto accept default path:"))
-        self.change_path.setText(util_ui.tr("Change"))
-        self.inlines.setText(util_ui.tr("Allow inlines"))
-        self.block_user_label.setText(util_ui.tr("Block by public key:"))
-        self.blocked_users_label.setText(util_ui.tr("Blocked users:"))
-        self.unblock.setText(util_ui.tr("Unblock"))
-        self.block.setText(util_ui.tr("Block user"))
-        self.saveUnsentOnly.setText(util_ui.tr("Save unsent messages only"))
-
-    def update(self, new_state):
-        self.saveUnsentOnly.setEnabled(new_state)
-        if not new_state:
-            self.saveUnsentOnly.setChecked(False)
-
-    def unblock_user(self):
-        if not self.comboBox.count():
-            return
-        title = util_ui.tr("Add to friend list")
-        info = util_ui.tr("Do you want to add this user to friend list?")
-        reply = util_ui.question(info, title)
-        self._contacts_manager.unblock_user(self.comboBox.currentText(), reply)
-        self.close()
-
-    def closeEvent(self, event):
-        self._settings['typing_notifications'] = self.typingNotifications.isChecked()
-        self._settings['allow_auto_accept'] = self.fileautoaccept.isChecked()
-        text = util_ui.tr('History will be cleaned! Continue?')
-        title = util_ui.tr('Chat history')
-
-        if self._settings['save_history'] and not self.saveHistory.isChecked():  # clear history
-            reply = util_ui.question(text, title)
-            if reply:
-                self._history_loader.clear_history()
-                self._settings['save_history'] = self.saveHistory.isChecked()
-        else:
-            self._settings['save_history'] = self.saveHistory.isChecked()
-        if self.saveUnsentOnly.isChecked() and not self._settings['save_unsent_only']:
-            reply = util_ui.question(text, title)
-            if reply:
-                self._history_loader.clear_history(None, True)
-                self._settings['save_unsent_only'] = self.saveUnsentOnly.isChecked()
-        else:
-            self._settings['save_unsent_only'] = self.saveUnsentOnly.isChecked()
-        self._settings['auto_accept_path'] = self.path.toPlainText()
-        self._settings['allow_inline'] = self.inlines.isChecked()
-        self._settings.save()
-
-    def new_path(self):
-        directory = util_ui.directory_dialog()
-        if directory:
-            self.path.setPlainText(directory)
-
-
-class NotificationsSettings(CenteredWidget):
-    """Notifications settings form"""
-
-    def __init__(self, dsettings):
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._settings = dsettings # pylint: disable=undefined-variable
-        uic.loadUi(get_views_path('notifications_settings_screen'), self)
-        self._update_ui()
-        self.center()
-
-    def closeEvent(self, *args, **kwargs):
-        self._settings['notifications'] = self.notificationsCheckBox.isChecked()
-        self._settings['sound_notifications'] = self.soundNotificationsCheckBox.isChecked()
-        self._settings['group_notifications'] = self.groupNotificationsCheckBox.isChecked()
-        self._settings['calls_sound'] = self.callsSoundCheckBox.isChecked()
-        self._settings.save()
-
-    def _update_ui(self):
-        self.notificationsCheckBox.setChecked(self._settings['notifications'])
-        self.soundNotificationsCheckBox.setChecked(self._settings['sound_notifications'])
-        self.groupNotificationsCheckBox.setChecked(self._settings['group_notifications'])
-        self.callsSoundCheckBox.setChecked(self._settings['calls_sound'])
-        self._retranslate_ui()
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr("Notifications settings"))
-        self.notificationsCheckBox.setText(util_ui.tr("Enable notifications"))
-        self.groupNotificationsCheckBox.setText(util_ui.tr("Notify about all messages in groups"))
-        self.callsSoundCheckBox.setText(util_ui.tr("Enable call\'s sound"))
-        self.soundNotificationsCheckBox.setText(util_ui.tr("Enable sound notifications"))
-
-
-class InterfaceSettings(CenteredWidget):
-    """Interface settings form"""
-
-    def __init__(self, dsettings, smiley_loader):
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._settings = dsettings
-        self._smiley_loader = smiley_loader
-
-        uic.loadUi(get_views_path('interface_settings_screen'), self)
-        self._update_ui()
-        self.center()
-
-    def _update_ui(self):
-        themes = list(settings.built_in_themes().keys())
-        self.themeComboBox.addItems(themes)
-        theme = self._settings['theme']
-        if theme in settings.built_in_themes().keys():
-            index = themes.index(theme)
-        else:
-            index = 0
-        self.themeComboBox.setCurrentIndex(index)
-
-        supported_languages = sorted(Settings.supported_languages().keys(), reverse=True)
-        for key in supported_languages:
-            self.languageComboBox.insertItem(0, key)
-            if self._settings['language'] == key:
-                self.languageComboBox.setCurrentIndex(0)
-
-        smiley_packs = self._smiley_loader.get_packs_list()
-        self.smileysPackComboBox.addItems(smiley_packs)
-        try:
-            index = smiley_packs.index(self._settings['smiley_pack'])
-        except:
-            index = smiley_packs.index('default')
-        self.smileysPackComboBox.setCurrentIndex(index)
-
-        self._app_closing_setting = self._settings['close_app']
-        self.closeRadioButton.setChecked(self._app_closing_setting == 0)
-        self.hideRadioButton.setChecked(self._app_closing_setting == 1)
-        self.closeToTrayRadioButton.setChecked(self._app_closing_setting == 2)
-
-        self.compactModeCheckBox.setChecked(self._settings['compact_mode'])
-        self.showAvatarsCheckBox.setChecked(self._settings['show_avatars'])
-        self.smileysCheckBox.setChecked(self._settings['smileys'])
-
-        self.importSmileysPushButton.clicked.connect(self._import_smileys)
-        self.importStickersPushButton.clicked.connect(self._import_stickers)
-
-        self._retranslate_ui()
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr("Interface settings"))
-        self.showAvatarsCheckBox.setText(util_ui.tr("Show avatars in chat"))
-        self.themeLabel.setText(util_ui.tr("Theme:"))
-        self.languageLabel.setText(util_ui.tr("Language:"))
-        self.smileysGroupBox.setTitle(util_ui.tr("Smileys settings"))
-        self.smileysPackLabel.setText(util_ui.tr("Smiley pack:"))
-        self.smileysCheckBox.setText(util_ui.tr("Smileys"))
-        self.closeRadioButton.setText(util_ui.tr("Close app"))
-        self.hideRadioButton.setText(util_ui.tr("Hide app"))
-        self.closeToTrayRadioButton.setText(util_ui.tr("Close to tray"))
-#        self.mirrorModeCheckBox.setText(util_ui.tr("Mirror mode"))
-        self.compactModeCheckBox.setText(util_ui.tr("Compact contact list"))
-        self.importSmileysPushButton.setText(util_ui.tr("Import smiley pack"))
-        self.importStickersPushButton.setText(util_ui.tr("Import sticker pack"))
-        self.appClosingGroupBox.setTitle(util_ui.tr("App closing settings"))
-
-    @staticmethod
-    def _import_stickers():
-        directory = util_ui.directory_dialog(util_ui.tr('Choose folder with sticker pack'))
-        if directory:
-            dest = join_path(get_stickers_directory(), os.path.basename(directory))
-            copy(directory, dest)
-
-    @staticmethod
-    def _import_smileys():
-        directory = util_ui.directory_dialog(util_ui.tr('Choose folder with smiley pack'))
-        if not directory:
-            return
-        src = directory + '/'
-        dest = join_path(get_smileys_directory(), os.path.basename(directory))
-        copy(src, dest)
-
-    def closeEvent(self, event):
-
-        self._settings['theme'] = str(self.themeComboBox.currentText())
-        try:
-            theme = self._settings['theme']
-            styles_path = join_path(get_styles_directory(), settings.built_in_themes()[theme])
-            with open(styles_path) as fl:
-                style = fl.read()
-            self._app.setStyleSheet(style)
-        except IsADirectoryError:
-            pass
-
-        self._settings['smileys'] = self.smileysCheckBox.isChecked()
-
-        restart = False
-#        if self._settings['mirror_mode'] != self.mirrorModeCheckBox.isChecked():
-#            self._settings['mirror_mode'] = self.mirrorModeCheckBox.isChecked()
-#            restart = True
-
-        if self._settings['compact_mode'] != self.compactModeCheckBox.isChecked():
-            self._settings['compact_mode'] = self.compactModeCheckBox.isChecked()
-            restart = True
-
-        if self._settings['show_avatars'] != self.showAvatarsCheckBox.isChecked():
-            self._settings['show_avatars'] = self.showAvatarsCheckBox.isChecked()
-            restart = True
-
-        self._settings['smiley_pack'] = self.smileysPackComboBox.currentText()
-        self._smiley_loader.load_pack()
-
-        language = self.languageComboBox.currentText()
-        if self._settings['language'] != language:
-            self._settings['language'] = language
-            path = Settings.supported_languages()[language]
-            self._app.removeTranslator(self._app.translator)
-            self._app.translator.load(join_path(get_translations_directory(), path))
-            self._app.installTranslator(self._app.translator)
-
-        app_closing_setting = 0
-        if self.hideRadioButton.isChecked():
-            app_closing_setting = 1
-        elif self.closeToTrayRadioButton.isChecked():
-            app_closing_setting = 2
-        self._settings['close_app'] = app_closing_setting
-        self._settings.save()
-
-        if restart:
-            util_ui.message_box(util_ui.tr('Restart app to apply settings'), util_ui.tr('Restart required'))
-
-
-class AudioSettings(CenteredWidget):
-    """
-    Audio calls settings form
-    """
-
-    def __init__(self, dsettings):
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._settings = dsettings
-        self._in_indexes = self._out_indexes = None
-        uic.loadUi(get_views_path('audio_settings_screen'), self)
-        self._update_ui()
-        self.center()
-
-    def closeEvent(self, event):
-        if 'audio' not in self._settings:
-            ex = f"self._settings=id(self._settings) {self._settings}"
-            LOG.warn('AudioSettings.closeEvent settings error: ' + str(ex))
-        else:
-            self._settings['audio']['input'] = \
-                self._in_indexes[self.inputDeviceComboBox.currentIndex()]
-            self._settings['audio']['output'] = \
-                self._out_indexes[self.outputDeviceComboBox.currentIndex()]
-            self._settings.save()
-
-    def _update_ui(self):
-        p = oPYA
-        self._in_indexes, self._out_indexes = [], []
-        for i in range(p.get_device_count()):
-            device = p.get_device_info_by_index(i)
-            if device["maxInputChannels"]:
-                self.inputDeviceComboBox.addItem(str(device["name"]))
-                self._in_indexes.append(i)
-            if device["maxOutputChannels"]:
-                self.outputDeviceComboBox.addItem(str(device["name"]))
-                self._out_indexes.append(i)
-        try:
-            self.inputDeviceComboBox.setCurrentIndex(self._in_indexes.index(self._settings['audio']['input']))
-            self.outputDeviceComboBox.setCurrentIndex(self._out_indexes.index(self._settings['audio']['output']))
-        except: pass
-
-        self._retranslate_ui()
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr("Audio settings"))
-        self.inputDeviceLabel.setText(util_ui.tr("Input device:"))
-        self.outputDeviceLabel.setText(util_ui.tr("Output device:"))
-
-
-class DesktopAreaSelectionWindow(RubberBandWindow):
-
-    def mouseReleaseEvent(self, event):
-        if self.rubberband.isVisible():
-            self.rubberband.hide()
-            rect = self.rubberband.geometry()
-            width, height = rect.width(), rect.height()
-            if width >= 8 and height >= 8:
-                self.parent.save(rect.x(), rect.y(), width - (width % 4), height - (height % 4))
-            self.close()
-
-
-class VideoSettings(CenteredWidget):
-    """
-    Video calls settings form
-    """
-
-    def __init__(self, dsettings):
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._settings = dsettings
-        uic.loadUi(get_views_path('video_settings_screen'), self)
-        self._devices = self._frame_max_sizes = None
-        self._update_ui()
-        self.center()
-        self.desktopAreaSelection = None
-
-    def closeEvent(self, event):
-        if self.deviceComboBox.currentIndex() == 0:
-            return
-        try:
-            # AttributeError: 'VideoSettings' object has no attribute 'devices'
-            # ERROR: Saving video  settings error: 'VideoSettings' object has no attribute 'input'
-            index = self.deviceComboBox.currentIndex()
-            if index in self._devices:
-                self._settings['video']['device'] = self._devices[index]
-            else:
-                LOG.warn(f"{index} not in deviceComboBox self._devices {self._devices}")
-            text = self.resolutionComboBox.currentText()
-            if len(text.split(' ')[0]) > 1:
-                self._settings['video']['width'] = int(text.split(' ')[0])
-                self._settings['video']['height'] = int(text.split(' ')[-1])
-            self._settings.save()
-        except Exception as ex:
-            LOG.error('ERROR: Saving video  settings error: ' + str(ex))
-
-    def save(self, x, y, width, height):
-        self.desktopAreaSelection = None
-        self._settings['video']['device'] = -1
-        self._settings['video']['width'] = width
-        self._settings['video']['height'] = height
-        self._settings['video']['x'] = x
-        self._settings['video']['y'] = y
-        self._settings.save()
-
-    def _update_ui(self):
-        try:
-            with ts.ignoreStdout(): import cv2
-        except ImportError:
-            cv2 = None
-        self.deviceComboBox.currentIndexChanged.connect(self._device_changed)
-        self.selectRegionPushButton.clicked.connect(self._button_clicked)
-        self._devices = [-1]
-        screen = QtWidgets.QApplication.primaryScreen()
-        size = screen.size()
-        self._frame_max_sizes = [(size.width(), size.height())]
-        desktop = util_ui.tr("Desktop")
-        self.deviceComboBox.addItem(desktop)
-        with ts.ignoreStdout():
-            # was range(10)
-            for i in map(int, ts.get_video_indexes()):
-                v = cv2.VideoCapture(i) # pylint: disable=no-member
-                if v.isOpened():
-                    v.set(cv2.CAP_PROP_FRAME_WIDTH, 10000) # pylint: disable=no-member
-                    v.set(cv2.CAP_PROP_FRAME_HEIGHT, 10000) # pylint: disable=no-member
-
-                    width = int(v.get(cv2.CAP_PROP_FRAME_WIDTH)) # pylint: disable=no-member
-                    height = int(v.get(cv2.CAP_PROP_FRAME_HEIGHT)) # pylint: disable=no-member
-                    del v
-                    self._devices.append(i)
-                    self._frame_max_sizes.append((width, height))
-                    self.deviceComboBox.addItem(util_ui.tr('Device #') + str(i))
-
-        if 'device' not in self._settings['video']:
-            LOG.warn(f"'device' not in self._settings['video']: {self._settings}")
-            self._settings['video']['device'] = self._devices[-1]
-        iIndex = self._settings['video']['device']
-        try:
-            index = self._devices.index(iIndex)
-            self.deviceComboBox.setCurrentIndex(index)
-        except Exception as e:
-            # off by one - what's Desktop?
-            se = f"Video devices index error: index={iIndex} {e}"
-            LOG.warn(se)
-            # util_ui.message_box(se, util_ui.tr(f"ERROR: Video devices error"))
-            self._settings['video']['device'] = self._devices[-1]
-
-        self._retranslate_ui()
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr("Video settings"))
-        self.deviceLabel.setText(util_ui.tr("Device:"))
-        self.selectRegionPushButton.setText(util_ui.tr("Select region"))
-
-    def _button_clicked(self):
-        self.desktopAreaSelection = DesktopAreaSelectionWindow(self)
-
-    def _device_changed(self):
-        index = self.deviceComboBox.currentIndex()
-        self.selectRegionPushButton.setVisible(index == 0)
-        self.resolutionComboBox.setVisible(True) # index != 0
-        width, height = self._frame_max_sizes[index]
-        self.resolutionComboBox.clear()
-        dims = [
-            (320, 240),
-            (640, 360),
-            (640, 480),
-            (720, 480),
-            (1280, 720),
-            (1920, 1080),
-            (2560, 1440)
-        ]
-        for w, h in dims:
-            if w <= width and h <= height:
-                self.resolutionComboBox.addItem(str(w) + ' * ' + str(h))
-
-
-class PluginsSettings(CenteredWidget):
-    """
-    Plugins settings form
-    """
-
-    def __init__(self, plugin_loader):
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._plugin_loader = plugin_loader
-        self._window = None
-        self.initUI()
-        self.center()
-        self.retranslateUi()
-
-    def initUI(self):
-        self.resize(400, 210)
-        self.setMinimumSize(QtCore.QSize(400, 210))
-        self.setMaximumSize(QtCore.QSize(400, 210))
-        self.comboBox = QtWidgets.QComboBox(self)
-        self.comboBox.setGeometry(QtCore.QRect(30, 10, 340, 30))
-        self.label = QtWidgets.QLabel(self)
-        self.label.setGeometry(QtCore.QRect(30, 40, 340, 90))
-        self.label.setWordWrap(True)
-        self.button = QtWidgets.QPushButton(self)
-        self.button.setGeometry(QtCore.QRect(30, 130, 340, 30))
-        self.button.clicked.connect(self.button_click)
-        self.open = QtWidgets.QPushButton(self)
-        self.open.setGeometry(QtCore.QRect(30, 170, 340, 30))
-        self.open.clicked.connect(self.open_plugin)
-        self.update_list()
-        self.comboBox.currentIndexChanged.connect(self.show_data)
-        self.show_data()
-
-    def retranslateUi(self):
-        self.setWindowTitle(util_ui.tr("Plugins"))
-        self.open.setText(util_ui.tr("Open selected plugin"))
-
-    def open_plugin(self):
-
-        ind = self.comboBox.currentIndex()
-        plugin = self.data[ind] # ['SearchPlugin', True, 'Description', 'srch']
-        # key in self._plugins and hasattr(self._plugins[key], 'instance'):
-        window = self._plugin_loader.plugin_window(plugin[-1])
-        if window is not None and not hasattr(window, 'show'):
-            LOG.error(util_ui.tr('ERROR: No show for the plugin: ' +repr(window) +' ' +repr(window)))
-            util_ui.message_box(util_ui.tr('ERROR: No show for the plugin ' +repr(window)), util_ui.tr('Error'))
-        elif window is not None:
-            try:
-                self._window = window
-                self._window.show()
-            except Exception as e:
-                LOG.error(util_ui.tr('ERROR: Error for the plugin: ' +repr(window) +' ' +str(e)))
-                util_ui.message_box(util_ui.tr('ERROR: Error for the plugin: ' +repr(window)), util_ui.tr('Error'))
-        elif window is None:
-            LOG.warn(util_ui.tr('WARN: No GUI found for the plugin: by plugin_loader.plugin_window'))
-            util_ui.message_box(util_ui.tr('WARN: No GUI found for the plugin: by plugin_loader.plugin_window'), util_ui.tr('Error'))
-
-    def update_list(self):
-        self.comboBox.clear()
-        data = self._plugin_loader.get_plugins_list()
-        self.comboBox.addItems(list(map(lambda x: x[0], data)))
-        self.data = data
-
-    def show_data(self):
-        ind = self.comboBox.currentIndex()
-        if len(self.data):
-            plugin = self.data[ind]
-            descr = plugin[2] or util_ui.tr("No description available")
-            self.label.setText(descr)
-            if plugin[1]:
-                self.button.setText(util_ui.tr("Disable plugin"))
-            else:
-                self.button.setText(util_ui.tr("Enable plugin"))
-        else:
-            self.open.setVisible(False)
-            self.button.setVisible(False)
-            self.label.setText(util_ui.tr("No plugins found"))
-
-    def button_click(self):
-        ind = self.comboBox.currentIndex()
-        plugin = self.data[ind]
-        self._plugin_loader.toggle_plugin(plugin[-1])
-        plugin[1] = not plugin[1]
-        if plugin[1]:
-            self.button.setText(util_ui.tr("Disable plugin"))
-        else:
-            self.button.setText(util_ui.tr("Enable plugin"))
-
-
-class UpdateSettings(CenteredWidget):
-    """
-    Updates settings form
-    """
-
-    def __init__(self, dsettings, version):
-        super().__init__()
-        self._app = QtWidgets.QApplication.instance()
-        self._settings = dsettings
-        self._version = version
-        uic.loadUi(get_views_path('update_settings_screen'), self)
-        self._update_ui()
-        self.center()
-
-    def closeEvent(self, event):
-        self._settings['update'] = self.updateModeComboBox.currentIndex()
-        self._settings.save()
-
-    def _update_ui(self):
-        self.updatePushButton.clicked.connect(self._update_client)
-        self.updateModeComboBox.currentIndexChanged.connect(self._update_mode_changed)
-        self._retranslate_ui()
-        self.updateModeComboBox.setCurrentIndex(self._settings['update'])
-
-    def _update_mode_changed(self):
-        index = self.updateModeComboBox.currentIndex()
-        self.updatePushButton.setEnabled(index > 0)
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr("Update settings"))
-        self.updateModeLabel.setText(util_ui.tr("Select update mode:"))
-        self.updatePushButton.setText(util_ui.tr("Update Toxygen"))
-        self.updateModeComboBox.addItem(util_ui.tr("Disabled"))
-        self.updateModeComboBox.addItem(util_ui.tr("Manual"))
-        self.updateModeComboBox.addItem(util_ui.tr("Auto"))
-
-    def _update_client(self):
-        if not updater.connection_available():
-            util_ui.message_box(util_ui.tr('Problems with internet connection'), util_ui.tr("Error"))
-            return
-        if not updater.updater_available():
-            util_ui.message_box(util_ui.tr('Updater not found'), util_ui.tr("Error"))
-            return
-        version = updater.check_for_updates(self._version, self._settings)
-        if version is not None:
-            updater.download(version)
-            util_ui.close_all_windows()
-        else:
-            util_ui.message_box(util_ui.tr('Toxygen is up to date'), util_ui.tr("No updates found"))
diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py
deleted file mode 100644
index 2c0ddfb..0000000
--- a/toxygen/ui/messages_widgets.py
+++ /dev/null
@@ -1,463 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import html as h
-import re
-
-from  qtpy import QtCore, QtGui, QtWidgets
-
-from toxygen_wrapper.toxcore_enums_and_consts import *
-import ui.widgets as widgets
-import utils.util as util
-import ui.menu as menu
-from ui.widgets import *
-from messenger.messages import MESSAGE_AUTHOR
-from file_transfers.file_transfers import *
-
-class MessageBrowser(QtWidgets.QTextBrowser):
-
-    def __init__(self, settings, message_edit, smileys_loader, plugin_loader, text, width, message_type, parent=None):
-        super().__init__(parent)
-        self.urls = {}
-        self._message_edit = message_edit
-        self._smileys_loader = smileys_loader
-        self._plugin_loader = plugin_loader
-        self._add_contact = None
-        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-        self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere)
-        self.document().setTextWidth(width)
-        self.setOpenExternalLinks(True)
-        self.setAcceptRichText(True)
-        self.setOpenLinks(False)
-        path = smileys_loader.get_smileys_path()
-        if path is not None:
-            self.setSearchPaths([path])
-        self.document().setDefaultStyleSheet('a { color: #306EFF; }')
-        text = self.decoratedText(text)
-        if message_type != TOX_MESSAGE_TYPE['NORMAL']:
-            self.setHtml('<p style="color: #5CB3FF; font: italic; font-size: 20px;" >' + text + '</p>')
-        else:
-            self.setHtml(text)
-        font = QtGui.QFont()
-        font.setFamily(settings['font'])
-        font.setPixelSize(settings['message_font_size'])
-        font.setBold(False)
-        self.setFont(font)
-        try:
-            # was self.resize(width, self.document().size().height())
-            # guessing QSize
-            self.resize(QtCore.QSize(width, int(self.document().size().height())))
-        except TypeError as e:
-            # TypeError: arguments did not match any overloaded call:
-            # resize(self, a0: QSize): argument 1 has unexpected type 'int'
-            # resize(self, w: int, h: int): argument 2 has unexpected type 'float'
-            pass
-
-        self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse)
-        self.anchorClicked.connect(self.on_anchor_clicked)
-
-    def contextMenuEvent(self, event):
-        menu = widgets.create_menu(self.createStandardContextMenu(event.pos()))
-        quote = menu.addAction(util_ui.tr('Quote selected text'))
-        quote.triggered.connect(self.quote_text)
-        text = self.textCursor().selection().toPlainText()
-        if not text:
-            quote.setEnabled(False)
-        else:
-            sub_menu = self._plugin_loader.get_message_menu(menu, text)
-            if len(sub_menu):
-                plugins_menu = menu.addMenu(util_ui.tr('Plugins'))
-                plugins_menu.addActions(sub_menu)
-        menu.popup(event.globalPos())
-        menu.exec_(event.globalPos())
-        del menu
-
-    def quote_text(self):
-        text = self.textCursor().selection().toPlainText()
-        if not text:
-            return
-        text = '>' + '\n>'.join(text.split('\n'))
-        if self._message_edit.toPlainText():
-            text = '\n' + text
-        self._message_edit.appendPlainText(text)
-
-    def on_anchor_clicked(self, url):
-        text = str(url.toString())
-        if text.startswith('tox:'):
-            self._add_contact = menu.AddContact(text[4:])
-            self._add_contact.show()
-        else:
-            QtGui.QDesktopServices.openUrl(url)
-        self.clearFocus()
-
-    def addAnimation(self, url, file_name):
-        movie = QtGui.QMovie(self)
-        movie.setFileName(file_name)
-        self.urls[movie] = url
-        # Value 'movie.frameChanged' is unsubscriptable
-        movie.frameChanged().connect(lambda x: self.animate(movie))
-        movie.start()
-
-    def animate(self, movie):
-        self.document().addResource(QtGui.QTextDocument.ImageResource,
-                                    self.urls[movie],
-                                    movie.currentPixmap())
-        self.setLineWrapColumnOrWidth(self.lineWrapColumnOrWidth())
-
-    def decoratedText(self, text):
-        text = h.escape(text)  # replace < and >
-        exp = QtCore.QRegExp(
-            '('
-            '(?:\\b)((www\\.)|(http[s]?|ftp)://)'
-            '\\w+\\S+)'
-            '|(?:\\b)(file:///)([\\S| ]*)'
-            '|(?:\\b)(tox:[a-zA-Z\\d]{76}$)'
-            '|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)'
-            '|(?:\\b)(tox:\\S+@\\S+)')
-        offset = exp.indexIn(text, 0)
-        while offset != -1:  # add links
-            url = exp.cap()
-            if exp.cap(2) == 'www.':
-                html = '<a href="http://{0}">{0}</a>'.format(url)
-            else:
-                html = '<a href="{0}">{0}</a>'.format(url)
-            text = text[:offset] + html + text[offset + len(exp.cap()):]
-            offset += len(html)
-            offset = exp.indexIn(text, offset)
-        arr = text.split('\n')
-        for i in range(len(arr)):  # quotes
-            if arr[i].startswith('&gt;'):
-                arr[i] = '<font color="green"><b>' + arr[i][4:] + '</b></font>'
-        text = '<br>'.join(arr)
-        text = self._smileys_loader.add_smileys_to_text(text, self)
-        return text
-
-
-class MessageItem(QtWidgets.QWidget):
-    """
-    Message in messages list
-    """
-    def __init__(self, text_message, settings, message_browser_factory_method, delete_action, parent=None):
-        QtWidgets.QWidget.__init__(self, parent)
-        self._message = text_message
-        self._delete_action = delete_action
-        self.name = widgets.DataLabel(self)
-        self.name.setGeometry(QtCore.QRect(2, 2, 95, 23))
-        self.name.setTextFormat(QtCore.Qt.PlainText)
-        font = QtGui.QFont()
-        font.setFamily(settings['font'])
-        font.setPointSize(11)
-        font.setBold(True)
-        if text_message.author is not None:
-            self.name.setFont(font)
-            self.name.setText(text_message.author.name)
-
-        self.time = QtWidgets.QLabel(self)
-        self.time.setGeometry(QtCore.QRect(parent.width() - 60, 0, 50, 25))
-        font.setPointSize(10)
-        font.setBold(False)
-        self.time.setFont(font)
-        self._time = text_message.time
-        if text_message.author and text_message.author.type == MESSAGE_AUTHOR['NOT_SENT']:
-            movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif'))
-            self.time.setMovie(movie)
-            movie.start()
-            self.t = True
-        else:
-            self.time.setText(util.convert_time(text_message.time))
-            self.t = False
-
-        self.message = message_browser_factory_method(text_message.text, parent.width() - 160,
-                                                      text_message.type, self)
-        if text_message.type != TOX_MESSAGE_TYPE['NORMAL']:
-            self.name.setStyleSheet("QLabel { color: #5CB3FF; }")
-            self.message.setAlignment(QtCore.Qt.AlignCenter)
-            self.time.setStyleSheet("QLabel { color: #5CB3FF; }")
-        self.message.setGeometry(QtCore.QRect(100, 0, parent.width() - 160, self.message.height()))
-        self.setFixedHeight(self.message.height())
-
-    def mouseReleaseEvent(self, event):
-        if event.button() == QtCore.Qt.RightButton and event.x() > self.time.x():
-            self.listMenu = QtWidgets.QMenu()
-            delete_item = self.listMenu.addAction(util_ui.tr('Delete message'))
-            delete_item.triggered.connect(self.delete)
-            parent_position = self.time.mapToGlobal(QtCore.QPoint(0, 0))
-            self.listMenu.move(parent_position)
-            self.listMenu.show()
-
-    def delete(self):
-        self._delete_action(self._message)
-
-    def mark_as_sent(self):
-        if self.t:
-            self.time.setText(util.convert_time(self._time))
-            self.t = False
-            return True
-        return False
-
-    def set_avatar(self, pixmap):
-        self.name.setAlignment(QtCore.Qt.AlignCenter)
-        self.message.setAlignment(QtCore.Qt.AlignVCenter)
-        self.setFixedHeight(max(self.height(), 36))
-        self.name.setFixedHeight(self.height())
-        self.message.setFixedHeight(self.height())
-        self.name.setPixmap(pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
-
-    def select_text(self, text):
-        tmp = self.message.toHtml()
-        text = h.escape(text)
-        strings = re.findall(text, tmp, flags=re.IGNORECASE)
-        for s in strings:
-            tmp = self.replace_all(tmp, s)
-        self.message.setHtml(tmp)
-
-    @staticmethod
-    def replace_all(text, substring):
-        i, l = 0, len(substring)
-        while i < len(text) - l + 1:
-            index = text[i:].find(substring)
-            if index == -1:
-                break
-            i += index
-            lgt, rgt = text[i:].find('<'), text[i:].find('>')
-            if rgt < lgt:
-                i += rgt + 1
-                continue
-            sub = '<font color="red"><b>{}</b></font>'.format(substring)
-            text = text[:i] + sub + text[i + l:]
-            i += len(sub)
-        return text
-
-
-class FileTransferItem(QtWidgets.QListWidget):
-
-    def __init__(self, transfer_message, file_transfer_handler, settings, width, parent=None):
-
-        QtWidgets.QListWidget.__init__(self, parent)
-        self._file_transfer_handler = file_transfer_handler
-        self.resize(QtCore.QSize(width, 34))
-        if transfer_message.state == FILE_TRANSFER_STATE['CANCELLED']:
-            self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
-        elif transfer_message.state in PAUSED_FILE_TRANSFERS:
-            self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
-        else:
-            self.setStyleSheet('QListWidget { border: 1px solid green; }')
-        self.state = transfer_message.state
-
-        self.name = DataLabel(self)
-        self.name.setGeometry(QtCore.QRect(3, 7, 95, 25))
-        self.name.setTextFormat(QtCore.Qt.PlainText)
-        font = QtGui.QFont()
-        font.setFamily(settings['font'])
-        font.setPointSize(11)
-        font.setBold(True)
-        self.name.setFont(font)
-        self.name.setText(transfer_message.author.name)
-
-        self.time = QtWidgets.QLabel(self)
-        self.time.setGeometry(QtCore.QRect(width - 60, 7, 50, 25))
-        font.setPointSize(10)
-        font.setBold(False)
-        self.time.setFont(font)
-        self.time.setText(util.convert_time(transfer_message.time))
-
-        self.cancel = QtWidgets.QPushButton(self)
-        self.cancel.setGeometry(QtCore.QRect(width - 125, 2, 30, 30))
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'decline.png'))
-        icon = QtGui.QIcon(pixmap)
-        self.cancel.setIcon(icon)
-        self.cancel.setIconSize(QtCore.QSize(30, 30))
-        self.cancel.setVisible(transfer_message.state in ACTIVE_FILE_TRANSFERS or
-                               transfer_message.state == FILE_TRANSFER_STATE['UNSENT'])
-        self.cancel.clicked.connect(
-            lambda: self.cancel_transfer(transfer_message.friend_number, transfer_message.file_number))
-        self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}')
-
-        self.accept_or_pause = QtWidgets.QPushButton(self)
-        self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30))
-        if transfer_message.state == FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']:
-            self.accept_or_pause.setVisible(True)
-            self.button_update('accept')
-        elif transfer_message.state in DO_NOT_SHOW_ACCEPT_BUTTON:
-            self.accept_or_pause.setVisible(False)
-        elif transfer_message.state == FILE_TRANSFER_STATE['PAUSED_BY_USER']:  # setup for continue
-            self.accept_or_pause.setVisible(True)
-            self.button_update('resume')
-        elif transfer_message.state == FILE_TRANSFER_STATE['UNSENT']:
-            self.accept_or_pause.setVisible(False)
-            self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
-        else:  # pause
-            self.accept_or_pause.setVisible(True)
-            self.button_update('pause')
-        self.accept_or_pause.clicked.connect(
-            lambda: self.accept_or_pause_transfer(transfer_message.friend_number, transfer_message.file_number,
-                                                  transfer_message.size))
-
-        self.accept_or_pause.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none}')
-
-        self.pb = QtWidgets.QProgressBar(self)
-        self.pb.setGeometry(QtCore.QRect(100, 7, 100, 20))
-        self.pb.setValue(0)
-        self.pb.setStyleSheet('QProgressBar { background-color: #302F2F; }')
-        self.pb.setVisible(transfer_message.state in SHOW_PROGRESS_BAR)
-
-        self.file_name = DataLabel(self)
-        self.file_name.setGeometry(QtCore.QRect(210, 7, width - 420, 20))
-        font.setPointSize(12)
-        self.file_name.setFont(font)
-        file_size = transfer_message.size // 1024
-        if not file_size:
-            file_size = '{}B'.format(transfer_message.size)
-        elif file_size >= 1024:
-            file_size = '{}MB'.format(file_size // 1024)
-        else:
-            file_size = '{}KB'.format(file_size)
-        file_data = '{} {}'.format(file_size, transfer_message.file_name)
-        self.file_name.setText(file_data)
-        self.file_name.setToolTip(transfer_message.file_name)
-        self.saved_name = transfer_message.file_name
-        self.time_left = QtWidgets.QLabel(self)
-        self.time_left.setGeometry(QtCore.QRect(width - 92, 7, 30, 20))
-        font.setPointSize(10)
-        self.time_left.setFont(font)
-        self.time_left.setVisible(transfer_message.state == FILE_TRANSFER_STATE['RUNNING'])
-        self.setFocusPolicy(QtCore.Qt.NoFocus)
-        self.paused = False
-
-    def cancel_transfer(self, friend_number, file_number):
-        self._file_transfer_handler.cancel_transfer(friend_number, file_number)
-        self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
-        self.cancel.setVisible(False)
-        self.accept_or_pause.setVisible(False)
-        self.pb.setVisible(False)
-
-    def accept_or_pause_transfer(self, friend_number, file_number, size):
-        if self.state == FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']:
-            directory = util_ui.directory_dialog(util_ui.tr('Choose folder'))
-            self.pb.setVisible(True)
-            if directory:
-                self._file_transfer_handler.accept_transfer(directory + '/' + self.saved_name,
-                                                            friend_number, file_number, size)
-                self.button_update('pause')
-        elif self.state == FILE_TRANSFER_STATE['PAUSED_BY_USER']:  # resume
-            self.paused = False
-            self._file_transfer_handler.resume_transfer(friend_number, file_number)
-            self.button_update('pause')
-            self.state = FILE_TRANSFER_STATE['RUNNING']
-        else:  # pause
-            self.paused = True
-            self.state = FILE_TRANSFER_STATE['PAUSED_BY_USER']
-            self._file_transfer_handler.pause_transfer(friend_number, file_number)
-            self.button_update('resume')
-        self.accept_or_pause.clearFocus()
-
-    def button_update(self, path):
-        pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), '{}.png'.format(path)))
-        icon = QtGui.QIcon(pixmap)
-        self.accept_or_pause.setIcon(icon)
-        self.accept_or_pause.setIconSize(QtCore.QSize(30, 30))
-
-    def update_transfer_state(self, state, progress, time):
-        self.pb.setValue(int(progress * 100))
-        if time + 1:
-            m, s = divmod(time, 60)
-            self.time_left.setText('{0:02d}:{1:02d}'.format(m, s))
-        if self.state != state and self.state in ACTIVE_FILE_TRANSFERS:
-            if state == FILE_TRANSFER_STATE['CANCELLED']:
-                self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
-                self.cancel.setVisible(False)
-                self.accept_or_pause.setVisible(False)
-                self.pb.setVisible(False)
-                self.state = state
-                self.time_left.setVisible(False)
-            elif state == FILE_TRANSFER_STATE['FINISHED']:
-                self.accept_or_pause.setVisible(False)
-                self.pb.setVisible(False)
-                self.cancel.setVisible(False)
-                self.setStyleSheet('QListWidget { border: 1px solid green; }')
-                self.state = state
-                self.time_left.setVisible(False)
-            elif state == FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']:
-                self.accept_or_pause.setVisible(False)
-                self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
-                self.state = state
-                self.time_left.setVisible(False)
-            elif state == FILE_TRANSFER_STATE['PAUSED_BY_USER']:
-                self.button_update('resume')  # setup button continue
-                self.setStyleSheet('QListWidget { border: 1px solid green; }')
-                self.state = state
-                self.time_left.setVisible(False)
-            elif state == FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']:
-                self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
-                self.accept_or_pause.setVisible(False)
-                self.time_left.setVisible(False)
-                self.pb.setVisible(False)
-            elif not self.paused:  # active
-                self.pb.setVisible(True)
-                self.accept_or_pause.setVisible(True)  # setup to pause
-                self.button_update('pause')
-                self.setStyleSheet('QListWidget { border: 1px solid green; }')
-                self.state = state
-                self.time_left.setVisible(True)
-
-
-class UnsentFileItem(FileTransferItem):
-
-    def __init__(self, transfer_message, file_transfer_handler, settings, width, parent=None):
-        super().__init__(transfer_message, file_transfer_handler, settings, width, parent)
-        self._time = time
-        movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif'))
-        self.time.setMovie(movie)
-        movie.start()
-        self._message_id = transfer_message.message_id
-        self._friend_number = transfer_message.friend_number
-
-    def cancel_transfer(self, *args):
-        self._file_transfer_handler.cancel_not_started_transfer(self._friend_number, self._message_id)
-
-
-class InlineImageItem(QtWidgets.QScrollArea):
-
-    def __init__(self, data, width, elem, parent=None):
-
-        QtWidgets.QScrollArea.__init__(self, parent)
-        self.setFocusPolicy(QtCore.Qt.NoFocus)
-        self._elem = elem
-        self._image_label = QtWidgets.QLabel(self)
-        self._image_label.raise_()
-        self.setWidget(self._image_label)
-        self._image_label.setScaledContents(False)
-        self._pixmap = QtGui.QPixmap()
-        self._pixmap.loadFromData(data, 'PNG')
-        self._max_size = width - 30
-        self._resize_needed = not (self._pixmap.width() <= self._max_size)
-        self._full_size = not self._resize_needed
-        if not self._resize_needed:
-            self._image_label.setPixmap(self._pixmap)
-            self.resize(QtCore.QSize(self._max_size + 5, self._pixmap.height() + 5))
-            self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height())
-        else:
-            pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio)
-            self._image_label.setPixmap(pixmap)
-            self.resize(QtCore.QSize(self._max_size + 5, pixmap.height()))
-            self._image_label.setGeometry(5, 0, self._max_size + 5, pixmap.height())
-        self._elem.setSizeHint(QtCore.QSize(self.width(), self.height()))
-
-    def mouseReleaseEvent(self, event):
-        if event.button() == QtCore.Qt.LeftButton and self._resize_needed:  # scale inline
-            if self._full_size:
-                pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio)
-                self._image_label.setPixmap(pixmap)
-                self.resize(QtCore.QSize(self._max_size, pixmap.height()))
-                self._image_label.setGeometry(5, 0, pixmap.width(), pixmap.height())
-            else:
-                self._image_label.setPixmap(self._pixmap)
-                self.resize(QtCore.QSize(self._max_size, self._pixmap.height() + 17))
-                self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height())
-            self._full_size = not self._full_size
-            self._elem.setSizeHint(QtCore.QSize(self.width(), self.height()))
-        elif event.button() == QtCore.Qt.RightButton:  # save inline
-            directory = util_ui.directory_dialog(util_ui.tr('Choose folder'))
-            if directory:
-                fl = QtCore.QFile(directory + '/toxygen_inline_' + util.curr_time().replace(':', '_') + '.png')
-                self._pixmap.save(fl, 'PNG')
diff --git a/toxygen/ui/peer_screen.py b/toxygen/ui/peer_screen.py
deleted file mode 100644
index 6bca903..0000000
--- a/toxygen/ui/peer_screen.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import uic
-
-from ui.widgets import CenteredWidget
-import utils.util as util
-import utils.ui as util_ui
-from ui.contact_items import *
-import toxygen_wrapper.toxcore_enums_and_consts as consts
-
-
-class PeerScreen(CenteredWidget):
-
-    def __init__(self, contacts_manager, groups_service, group, peer_id):
-        super().__init__()
-        self._contacts_manager = contacts_manager
-        self._groups_service = groups_service
-        self._group = group
-        self._peer = group.get_peer_by_id(peer_id)
-
-        self._roles = {
-            TOX_GROUP_ROLE['FOUNDER']: util_ui.tr('Administrator'),
-            TOX_GROUP_ROLE['MODERATOR']: util_ui.tr('Moderator'),
-            TOX_GROUP_ROLE['USER']: util_ui.tr('User'),
-            TOX_GROUP_ROLE['OBSERVER']: util_ui.tr('Observer')
-        }
-
-        uic.loadUi(util.get_views_path('peer_screen'), self)
-        self._update_ui()
-
-    def _update_ui(self):
-        self.statusCircle = StatusCircle(self)
-        self.statusCircle.setGeometry(50, 15, 30, 30)
-
-        if self._peer:
-            self.statusCircle.update(self._peer.status)
-            self.peerNameLabel.setText(self._peer.name)
-            self.ignorePeerCheckBox.setChecked(self._peer.is_muted)
-
-        self.ignorePeerCheckBox.clicked.connect(self._toggle_ignore)
-        self.sendPrivateMessagePushButton.clicked.connect(self._send_private_message)
-        self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key)
-        self.roleNameLabel.setText(self._get_role_name())
-        can_change_role_or_ban = self._can_change_role_or_ban()
-        self.rolesComboBox.setVisible(can_change_role_or_ban)
-        self.roleNameLabel.setVisible(not can_change_role_or_ban)
-        self.banGroupBox.setEnabled(can_change_role_or_ban)
-#        self.banPushButton.clicked.connect(self._ban_peer)
-        self.kickPushButton.clicked.connect(self._kick_peer)
-
-        self._retranslate_ui()
-
-        self.rolesComboBox.currentIndexChanged.connect(self._role_set)
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr('Peer details'))
-        self.ignorePeerCheckBox.setText(util_ui.tr('Ignore peer'))
-        self.roleLabel.setText(util_ui.tr('Role:'))
-        self.copyPublicKeyPushButton.setText(util_ui.tr('Copy public key'))
-        self.sendPrivateMessagePushButton.setText(util_ui.tr('Send private message'))
-#        self.banPushButton.setText(util_ui.tr('Ban peer'))
-        self.kickPushButton.setText(util_ui.tr('Kick peer'))
-        self.banGroupBox.setTitle(util_ui.tr('Ban peer'))
-        self.ipBanRadioButton.setText(util_ui.tr('IP'))
-        self.nickBanRadioButton.setText(util_ui.tr('Nickname'))
-        self.pkBanRadioButton.setText(util_ui.tr('Public key'))
-
-        self.rolesComboBox.clear()
-        index = self._group.get_self_peer().role
-        roles = list(self._roles.values())
-        for role in roles[index + 1:]:
-            self.rolesComboBox.addItem(role)
-        self.rolesComboBox.setCurrentIndex(self._peer.role - index - 1)
-
-    def _can_change_role_or_ban(self):
-        self_peer = self._group.get_self_peer()
-        if self_peer.role > TOX_GROUP_ROLE['MODERATOR']:
-            return False
-
-        return self_peer.role < self._peer.role
-
-    def _role_set(self):
-        index = self.rolesComboBox.currentIndex()
-        all_roles_count = len(self._roles)
-        diff = all_roles_count - self.rolesComboBox.count()
-        self._groups_service.set_new_peer_role(self._group, self._peer, index + diff)
-
-    def _get_role_name(self):
-        return self._roles[self._peer.role]
-
-    def _toggle_ignore(self):
-        ignore = self.ignorePeerCheckBox.isChecked()
-        self._groups_service.toggle_ignore_peer(self._group, self._peer, ignore)
-
-    def _send_private_message(self):
-        self._contacts_manager.add_group_peer(self._group, self._peer)
-        self.close()
-
-    def _copy_public_key(self):
-        util_ui.copy_to_clipboard(self._peer.public_key)
-
-    def _ban_peer(self):
-        ban_type = self._get_ban_type()
-        self._groups_service.ban_peer(self._group, self._peer.id, ban_type)
-        self.close()
-
-    def _kick_peer(self):
-        self._groups_service.kick_peer(self._group, self._peer.id)
-        self.close()
-
-    def _get_ban_type(self):
-        if self.ipBanRadioButton.isChecked():
-            return consts.TOX_GROUP_BAN_TYPE['IP_PORT']
-        elif self.nickBanRadioButton.isChecked():
-            return consts.TOX_GROUP_BAN_TYPE['NICK']
-        return consts.TOX_GROUP_BAN_TYPE['PUBLIC_KEY']
diff --git a/toxygen/ui/profile_settings_screen.py b/toxygen/ui/profile_settings_screen.py
deleted file mode 100644
index 5b4658f..0000000
--- a/toxygen/ui/profile_settings_screen.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import QtGui, QtCore, uic
-
-from ui.widgets import CenteredWidget
-import utils.ui as util_ui
-from utils.util import join_path, get_images_directory, get_views_path
-from user_data.settings import Settings
-
-class ProfileSettings(CenteredWidget):
-    """Form with profile settings such as name, status, TOX ID"""
-    def __init__(self, profile, profile_manager, settings, toxes):
-        super().__init__()
-        self._profile = profile
-        self._profile_manager = profile_manager
-        self._settings = settings
-        self._toxes = toxes
-        self._auto = False
-
-        uic.loadUi(get_views_path('profile_settings_screen'), self)
-
-        self._init_ui()
-        self.center()
-
-    def closeEvent(self, event):
-        self._profile.set_name(self.nameLineEdit.text())
-        self._profile.set_status_message(self.statusMessageLineEdit.text())
-        self._profile.set_status(self.statusComboBox.currentIndex())
-
-    def _init_ui(self):
-        self._auto = Settings.get_auto_profile() == self._profile_manager.get_path()
-        self.toxIdLabel.setText(self._profile.tox_id)
-        self.nameLineEdit.setText(self._profile.name)
-        self.statusMessageLineEdit.setText(str(self._profile.status_message))
-        self.defaultProfilePushButton.clicked.connect(self._toggle_auto_profile)
-        self.copyToxIdPushButton.clicked.connect(self._copy_tox_id)
-        self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key)
-        self.changePasswordPushButton.clicked.connect(self._save_password)
-        self.exportProfilePushButton.clicked.connect(self._export_profile)
-        self.newNoSpamPushButton.clicked.connect(self._set_new_no_spam)
-        self.newAvatarPushButton.clicked.connect(self._set_avatar)
-        self.resetAvatarPushButton.clicked.connect(self._reset_avatar)
-
-        self.invalidPasswordsLabel.setVisible(False)
-
-        self._retranslate_ui()
-
-        if self._profile.status is not None:
-            self.statusComboBox.setCurrentIndex(self._profile.status)
-        else:
-            self.statusComboBox.setVisible(False)
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr("Profile settings"))
-
-        self.exportProfilePushButton.setText(util_ui.tr("Export profile"))
-        self.nameLabel.setText(util_ui.tr("Name:"))
-        self.statusLabel.setText(util_ui.tr("Status:"))
-        self.toxIdTitleLabel.setText(util_ui.tr("TOX ID:"))
-        self.copyToxIdPushButton.setText(util_ui.tr("Copy TOX ID"))
-        self.newAvatarPushButton.setText(util_ui.tr("New avatar"))
-        self.resetAvatarPushButton.setText(util_ui.tr("Reset avatar"))
-        self.newNoSpamPushButton.setText(util_ui.tr("New NoSpam"))
-        self.profilePasswordLabel.setText(util_ui.tr("Profile password"))
-        self.passwordLineEdit.setPlaceholderText(util_ui.tr("Password (at least 8 symbols)"))
-        self.confirmPasswordLineEdit.setPlaceholderText(util_ui.tr("Confirm password"))
-        self.changePasswordPushButton.setText(util_ui.tr("Set password"))
-        self.invalidPasswordsLabel.setText(util_ui.tr("Passwords do not match"))
-        self.emptyPasswordLabel.setText(util_ui.tr("Leaving blank will reset current password"))
-        self.warningLabel.setText(util_ui.tr("There is no way to recover lost passwords"))
-        self.statusComboBox.addItem(util_ui.tr("Online"))
-        self.statusComboBox.addItem(util_ui.tr("Away"))
-        self.statusComboBox.addItem(util_ui.tr("Busy"))
-        self.copyPublicKeyPushButton.setText(util_ui.tr("Copy public key" +' (64)'))
-
-        self._set_default_profile_button_text()
-
-    def _toggle_auto_profile(self):
-        if self._auto:
-            Settings.reset_auto_profile()
-        else:
-            Settings.set_auto_profile(self._profile_manager.get_path())
-        self._auto = not self._auto
-        self._set_default_profile_button_text()
-
-    def _set_default_profile_button_text(self):
-        if self._auto:
-            self.defaultProfilePushButton.setText(util_ui.tr("Mark as not default profile"))
-        else:
-            self.defaultProfilePushButton.setText(util_ui.tr("Mark as default profile"))
-
-    def _save_password(self):
-        password = self.passwordLineEdit.text()
-        confirm_password = self.confirmPasswordLineEdit.text()
-        if password == confirm_password:
-            if not len(password) or len(password) >= 8:
-                self._toxes.set_password(password)
-                self.close()
-            else:
-                self.invalidPasswordsLabel.setText(
-                    util_ui.tr("Password must be at least 8 symbols"))
-            self.invalidPasswordsLabel.setVisible(True)
-        else:
-            self.invalidPasswordsLabel.setText(util_ui.tr("Passwords do not match"))
-            self.invalidPasswordsLabel.setVisible(True)
-
-    def _copy_tox_id(self):
-        util_ui.copy_to_clipboard(self._profile.tox_id)
-
-        icon = self._get_accept_icon()
-        self.copyToxIdPushButton.setIcon(icon)
-        self.copyToxIdPushButton.setIconSize(QtCore.QSize(10, 10))
-
-    def _copy_public_key(self):
-        util_ui.copy_to_clipboard(self._profile.tox_id[:64])
-
-        icon = self._get_accept_icon()
-        self.copyPublicKeyPushButton.setIcon(icon)
-        self.copyPublicKeyPushButton.setIconSize(QtCore.QSize(10, 10))
-
-    def _set_new_no_spam(self):
-        self.toxIdLabel.setText(self._profile.set_new_nospam())
-
-    def _reset_avatar(self):
-        self._profile.reset_avatar(self._settings['identicons'])
-
-    def _set_avatar(self):
-        choose = util_ui.tr("Choose avatar")
-        name = util_ui.file_dialog(choose, 'Images (*.png)')
-        if not name[0]:
-            return
-        bitmap = QtGui.QPixmap(name[0])
-        bitmap.scaled(QtCore.QSize(128, 128), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
-
-        byte_array = QtCore.QByteArray()
-        buffer = QtCore.QBuffer(byte_array)
-        buffer.open(QtCore.QIODevice.WriteOnly)
-        bitmap.save(buffer, 'PNG')
-
-        self._profile.set_avatar(bytes(byte_array.data()))
-
-    def _export_profile(self):
-        directory = util_ui.directory_dialog()
-        if not directory:
-            return
-
-        reply = util_ui.question(util_ui.tr('Do you want to move your profile to this location?'),
-                                 util_ui.tr('Use new path'))
-
-        self._settings.export(directory)
-        self._profile.export_db(directory)
-        self._profile_manager.export_profile(self._settings, directory, reply)
-
-    @staticmethod
-    def _get_accept_icon():
-        pixmap = QtGui.QPixmap(join_path(get_images_directory(), 'accept.png'))
-
-        return QtGui.QIcon(pixmap)
-
diff --git a/toxygen/ui/self_peer_screen.py b/toxygen/ui/self_peer_screen.py
deleted file mode 100644
index 7f30653..0000000
--- a/toxygen/ui/self_peer_screen.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import uic
-
-from ui.widgets import CenteredWidget, LineEdit
-import utils.util as util
-import utils.ui as util_ui
-from ui.contact_items import *
-
-
-class SelfPeerScreen(CenteredWidget):
-
-    def __init__(self, contacts_manager, groups_service, group):
-        super().__init__()
-        self._contacts_manager = contacts_manager
-        self._groups_service = groups_service
-        self._group = group
-        self._peer = group.get_self_peer()
-        self._roles = {
-            TOX_GROUP_ROLE['FOUNDER']: util_ui.tr('Administrator'),
-            TOX_GROUP_ROLE['MODERATOR']: util_ui.tr('Moderator'),
-            TOX_GROUP_ROLE['USER']: util_ui.tr('User'),
-            TOX_GROUP_ROLE['OBSERVER']: util_ui.tr('Observer')
-        }
-
-        uic.loadUi(util.get_views_path('self_peer_screen'), self)
-        self._update_ui()
-
-    def _update_ui(self):
-        self.lineEdit = LineEdit(self)
-        self.lineEdit.setGeometry(140, 40, 400, 30)
-        self.lineEdit.setText(self._peer.name)
-        self.lineEdit.textChanged.connect(self._nick_changed)
-
-        self.savePushButton.clicked.connect(self._save)
-        self.copyPublicKeyPushButton.clicked.connect(self._copy_public_key)
-
-        self._retranslate_ui()
-
-        self.statusComboBox.setCurrentIndex(self._peer.status)
-
-    def _retranslate_ui(self):
-        self.setWindowTitle(util_ui.tr('Change credentials in group'))
-        self.lineEdit.setPlaceholderText(util_ui.tr('Your nickname in group'))
-        self.nameLabel.setText(util_ui.tr('Name:'))
-        self.roleLabel.setText(util_ui.tr('Role:'))
-        self.statusLabel.setText(util_ui.tr('Status:'))
-        self.copyPublicKeyPushButton.setText(util_ui.tr('Copy public key'))
-        self.savePushButton.setText(util_ui.tr('Save'))
-        self.roleNameLabel.setText(self._get_role_name())
-        self.statusComboBox.addItem(util_ui.tr('Online'))
-        self.statusComboBox.addItem(util_ui.tr('Away'))
-        self.statusComboBox.addItem(util_ui.tr('Busy'))
-
-    def _get_role_name(self):
-        return self._roles[self._peer.role]
-
-    def _nick_changed(self):
-        nick = self.lineEdit.text()
-        self.savePushButton.setEnabled(bool(nick))
-
-    def _save(self):
-        nick = self.lineEdit.text()
-        status = self.statusComboBox.currentIndex()
-        self._groups_service.set_self_info(self._group, nick, status)
-        self.close()
-
-    def _copy_public_key(self):
-        util_ui.copy_to_clipboard(self._peer.public_key)
diff --git a/toxygen/ui/tray.py b/toxygen/ui/tray.py
deleted file mode 100644
index c838a0d..0000000
--- a/toxygen/ui/tray.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import QtWidgets, QtGui, QtCore
-# from PyQt5.QtCore import pyqtSignal as Signal
-from  qtpy.QtCore import Signal
-
-from utils.ui import tr
-from utils.util import *
-from ui.password_screen import UnlockAppScreen
-import os.path
-
-class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
-    # FixMe: AttributeError: module 'qtpy.QtCore' has no attribute 'pyqtSignal'
-    leftClicked = Signal()
-
-    def __init__(self, icon, parent=None):
-        super().__init__(icon, parent)
-        self.activated.connect(self.icon_activated)
-
-    def icon_activated(self, reason):
-        if reason == QtWidgets.QSystemTrayIcon.Trigger:
-            self.leftClicked.emit()
-
-
-class Menu(QtWidgets.QMenu):
-
-    def __init__(self, settings, profile, *args):
-        super().__init__(*args)
-        self._settings = settings
-        self._profile = profile
-
-    def new_status(self, status):
-        if not self._settings.locked:
-            self._profile.set_status(status)
-            self.about_to_show_handler()
-            self.hide()
-
-    def about_to_show_handler(self):
-        status = self._profile.status
-        act = self.act
-        if status is None or self._settings.locked:
-            self.actions()[1].setVisible(False)
-        else:
-            self.actions()[1].setVisible(True)
-            act.actions()[0].setChecked(False)
-            act.actions()[1].setChecked(False)
-            act.actions()[2].setChecked(False)
-            act.actions()[status].setChecked(True)
-        self.actions()[2].setVisible(not self._settings.locked)
-
-    def languageChange(self, *args, **kwargs):
-        self.actions()[0].setText(tr('Open Toxygen'))
-        self.actions()[1].setText(tr('Set status'))
-        self.actions()[2].setText(tr('Exit'))
-        self.act.actions()[0].setText(tr('Online'))
-        self.act.actions()[1].setText(tr('Away'))
-        self.act.actions()[2].setText(tr('Busy'))
-
-
-def init_tray(profile, settings, main_screen, toxes):
-    icon = os.path.join(get_images_directory(), 'icon.png')
-    tray = SystemTrayIcon(QtGui.QIcon(icon))
-
-    menu = Menu(settings, profile)
-    show = menu.addAction(tr('Open Toxygen'))
-    sub = menu.addMenu(tr('Set status'))
-    online = sub.addAction(tr('Online'))
-    away = sub.addAction(tr('Away'))
-    busy = sub.addAction(tr('Busy'))
-    online.setCheckable(True)
-    away.setCheckable(True)
-    busy.setCheckable(True)
-    menu.act = sub
-    exit = menu.addAction(tr('Exit'))
-
-    def show_window():
-        def show():
-            if not main_screen.isActiveWindow():
-                main_screen.setWindowState(
-                    main_screen.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
-                main_screen.activateWindow()
-                main_screen.show()
-        if not settings.locked:
-            show()
-        else:
-            def correct_pass():
-                show()
-                settings.locked = False
-                settings.unlockScreen = False
-            if not settings.unlockScreen:
-                settings.unlockScreen = True
-                show_window.screen = UnlockAppScreen(toxes, correct_pass)
-                show_window.screen.show()
-
-    def tray_activated(reason):
-        if reason == QtWidgets.QSystemTrayIcon.DoubleClick:
-            show_window()
-
-    def close_app():
-        if not settings.locked:
-            settings.closing = True
-            main_screen.close()
-
-    show.triggered.connect(show_window)
-    exit.triggered.connect(close_app)
-    menu.aboutToShow.connect(menu.about_to_show_handler)
-    online.triggered.connect(lambda: menu.new_status(0))
-    away.triggered.connect(lambda: menu.new_status(1))
-    busy.triggered.connect(lambda: menu.new_status(2))
-
-    tray.setContextMenu(menu)
-    tray.show()
-    tray.activated.connect(tray_activated)
-
-    return tray
diff --git a/toxygen/ui/views/add_bootstrap_screen.ui b/toxygen/ui/views/add_bootstrap_screen.ui
deleted file mode 100644
index 0549e90..0000000
--- a/toxygen/ui/views/add_bootstrap_screen.ui
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>560</width>
-    <height>320</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>560</width>
-    <height>320</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>560</width>
-    <height>320</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="toxIdLabel">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>10</y>
-     <width>150</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="messageLabel">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>70</y>
-     <width>150</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPlainTextEdit" name="messagePlainTextEdit">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>110</y>
-     <width>460</width>
-     <height>150</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="addBootstrapPushButton">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>270</y>
-     <width>460</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="errorLabel">
-   <property name="enabled">
-    <bool>true</bool>
-   </property>
-   <property name="geometry">
-    <rect>
-     <x>220</x>
-     <y>10</y>
-     <width>321</width>
-     <height>31</height>
-    </rect>
-   </property>
-   <property name="contextMenuPolicy">
-    <enum>Qt::NoContextMenu</enum>
-   </property>
-   <property name="text">
-    <string/>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/add_contact_screen.ui b/toxygen/ui/views/add_contact_screen.ui
deleted file mode 100644
index 0f26a25..0000000
--- a/toxygen/ui/views/add_contact_screen.ui
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>560</width>
-    <height>320</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>560</width>
-    <height>320</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>560</width>
-    <height>320</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="toxIdLabel">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>10</y>
-     <width>150</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="messageLabel">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>70</y>
-     <width>150</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPlainTextEdit" name="messagePlainTextEdit">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>110</y>
-     <width>460</width>
-     <height>150</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="addContactPushButton">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>270</y>
-     <width>460</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="errorLabel">
-   <property name="enabled">
-    <bool>true</bool>
-   </property>
-   <property name="geometry">
-    <rect>
-     <x>220</x>
-     <y>10</y>
-     <width>321</width>
-     <height>31</height>
-    </rect>
-   </property>
-   <property name="contextMenuPolicy">
-    <enum>Qt::NoContextMenu</enum>
-   </property>
-   <property name="text">
-    <string/>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/audio_settings_screen.ui b/toxygen/ui/views/audio_settings_screen.ui
deleted file mode 100644
index a404592..0000000
--- a/toxygen/ui/views/audio_settings_screen.ui
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>315</width>
-    <height>218</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>315</width>
-    <height>218</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>315</width>
-    <height>218</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="inputDeviceLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>10</y>
-     <width>261</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="font">
-    <font>
-     <pointsize>16</pointsize>
-    </font>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="outputDeviceLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>100</y>
-     <width>261</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="font">
-    <font>
-     <pointsize>16</pointsize>
-    </font>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="inputDeviceComboBox">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>50</y>
-     <width>255</width>
-     <height>41</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="outputDeviceComboBox">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>140</y>
-     <width>255</width>
-     <height>41</height>
-    </rect>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/bans_list_screen.ui b/toxygen/ui/views/bans_list_screen.ui
deleted file mode 100644
index 16339d8..0000000
--- a/toxygen/ui/views/bans_list_screen.ui
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>500</width>
-    <height>375</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QListWidget" name="bansListWidget">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>500</width>
-     <height>375</height>
-    </rect>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/create_group_screen.ui b/toxygen/ui/views/create_group_screen.ui
deleted file mode 100644
index 3a3358a..0000000
--- a/toxygen/ui/views/create_group_screen.ui
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>640</width>
-    <height>300</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QPushButton" name="addGroupButton">
-   <property name="enabled">
-    <bool>false</bool>
-   </property>
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>250</y>
-     <width>601</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string/>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="groupNameLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>150</x>
-     <y>20</y>
-     <width>470</width>
-     <height>35</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="groupTypeComboBox">
-   <property name="geometry">
-    <rect>
-     <x>150</x>
-     <y>80</y>
-     <width>470</width>
-     <height>35</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLabel" name="groupNameLabel">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>20</y>
-     <width>121</width>
-     <height>31</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="groupTypeLabel">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>80</y>
-     <width>121</width>
-     <height>31</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="statusLabel">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>200</y>
-     <width>111</width>
-     <height>17</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="nickLabel">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>150</y>
-     <width>111</width>
-     <height>17</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="nickLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>150</x>
-     <y>140</y>
-     <width>470</width>
-     <height>35</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="statusComboBox">
-   <property name="geometry">
-    <rect>
-     <x>150</x>
-     <y>190</y>
-     <width>470</width>
-     <height>35</height>
-    </rect>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/create_profile_screen.ui b/toxygen/ui/views/create_profile_screen.ui
deleted file mode 100644
index bfffee5..0000000
--- a/toxygen/ui/views/create_profile_screen.ui
+++ /dev/null
@@ -1,128 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>400</width>
-    <height>340</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>400</width>
-    <height>340</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>400</width>
-    <height>340</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QPushButton" name="createProfile">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>270</y>
-     <width>341</width>
-     <height>51</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="confirmPassword">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>170</y>
-     <width>341</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="echoMode">
-    <enum>QLineEdit::Password</enum>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="password">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>120</y>
-     <width>341</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="echoMode">
-    <enum>QLineEdit::Password</enum>
-   </property>
-  </widget>
-  <widget class="QLabel" name="passwordLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>80</y>
-     <width>330</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QRadioButton" name="defaultFolder">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>10</y>
-     <width>330</width>
-     <height>23</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>RadioButton</string>
-   </property>
-   <property name="checked">
-    <bool>true</bool>
-   </property>
-  </widget>
-  <widget class="QRadioButton" name="programFolder">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>40</y>
-     <width>330</width>
-     <height>23</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>RadioButton</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="errorLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>220</y>
-     <width>341</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string/>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignCenter</set>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/gc_ban_item.ui b/toxygen/ui/views/gc_ban_item.ui
deleted file mode 100644
index a57d0e1..0000000
--- a/toxygen/ui/views/gc_ban_item.ui
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>500</width>
-    <height>100</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QPushButton" name="cancelPushButton">
-   <property name="geometry">
-    <rect>
-     <x>330</x>
-     <y>30</y>
-     <width>161</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="banTargetLabel">
-   <property name="geometry">
-    <rect>
-     <x>15</x>
-     <y>20</y>
-     <width>305</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="banTimeLabel">
-   <property name="geometry">
-    <rect>
-     <x>15</x>
-     <y>50</y>
-     <width>305</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/gc_invite_item.ui b/toxygen/ui/views/gc_invite_item.ui
deleted file mode 100644
index 6eddbeb..0000000
--- a/toxygen/ui/views/gc_invite_item.ui
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>600</width>
-    <height>150</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="friendNameLabel">
-   <property name="geometry">
-    <rect>
-     <x>250</x>
-     <y>30</y>
-     <width>300</width>
-     <height>21</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="groupNameLabel">
-   <property name="geometry">
-    <rect>
-     <x>250</x>
-     <y>70</y>
-     <width>300</width>
-     <height>21</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="friendAvatarLabel">
-   <property name="geometry">
-    <rect>
-     <x>140</x>
-     <y>30</y>
-     <width>60</width>
-     <height>60</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QCheckBox" name="selectCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>40</x>
-     <y>50</y>
-     <width>20</width>
-     <height>23</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string/>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/gc_settings_screen.ui b/toxygen/ui/views/gc_settings_screen.ui
deleted file mode 100644
index 526c156..0000000
--- a/toxygen/ui/views/gc_settings_screen.ui
+++ /dev/null
@@ -1,83 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>400</width>
-    <height>220</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>400</width>
-    <height>220</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>400</width>
-    <height>220</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="passwordLabel">
-   <property name="geometry">
-    <rect>
-     <x>10</x>
-     <y>20</y>
-     <width>380</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="copyPasswordPushButton">
-   <property name="geometry">
-    <rect>
-     <x>10</x>
-     <y>60</y>
-     <width>380</width>
-     <height>40</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="peerLimitLabel">
-   <property name="geometry">
-    <rect>
-     <x>10</x>
-     <y>120</y>
-     <width>380</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="privacyStateLabel">
-   <property name="geometry">
-    <rect>
-     <x>10</x>
-     <y>160</y>
-     <width>380</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/group_invites_screen.ui b/toxygen/ui/views/group_invites_screen.ui
deleted file mode 100644
index 183f801..0000000
--- a/toxygen/ui/views/group_invites_screen.ui
+++ /dev/null
@@ -1,113 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>600</width>
-    <height>500</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>600</width>
-    <height>500</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>600</width>
-    <height>500</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="noInvitesLabel">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>150</y>
-     <width>600</width>
-     <height>25</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignCenter</set>
-   </property>
-  </widget>
-  <widget class="QListWidget" name="invitesListWidget">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>600</width>
-     <height>341</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="nickLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>10</x>
-     <y>360</y>
-     <width>350</width>
-     <height>35</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="passwordLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>10</x>
-     <y>410</y>
-     <width>350</width>
-     <height>35</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="statusComboBox">
-   <property name="geometry">
-    <rect>
-     <x>390</x>
-     <y>390</y>
-     <width>200</width>
-     <height>35</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="acceptPushButton">
-   <property name="geometry">
-    <rect>
-     <x>40</x>
-     <y>460</y>
-     <width>201</width>
-     <height>31</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="declinePushButton">
-   <property name="geometry">
-    <rect>
-     <x>360</x>
-     <y>460</y>
-     <width>201</width>
-     <height>31</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/group_management_screen.ui b/toxygen/ui/views/group_management_screen.ui
deleted file mode 100644
index de7c21e..0000000
--- a/toxygen/ui/views/group_management_screen.ui
+++ /dev/null
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>658</width>
-    <height>283</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLineEdit" name="passwordLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>180</x>
-     <y>20</y>
-     <width>450</width>
-     <height>41</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLabel" name="passwordLabel">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>30</y>
-     <width>145</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="peerLimitLabel">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>80</y>
-     <width>145</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QSpinBox" name="peersLimitSpinBox">
-   <property name="geometry">
-    <rect>
-     <x>180</x>
-     <y>70</y>
-     <width>450</width>
-     <height>40</height>
-    </rect>
-   </property>
-   <property name="minimum">
-    <number>2</number>
-   </property>
-   <property name="maximum">
-    <number>9999</number>
-   </property>
-   <property name="value">
-    <number>512</number>
-   </property>
-  </widget>
-  <widget class="QLabel" name="privacyStateLabel">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>130</y>
-     <width>145</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="privacyStateComboBox">
-   <property name="geometry">
-    <rect>
-     <x>180</x>
-     <y>120</y>
-     <width>450</width>
-     <height>40</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="deletePushButton">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>180</y>
-     <width>300</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="savePushButton">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>220</y>
-     <width>611</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/interface_settings_screen.ui b/toxygen/ui/views/interface_settings_screen.ui
deleted file mode 100644
index b762903..0000000
--- a/toxygen/ui/views/interface_settings_screen.ui
+++ /dev/null
@@ -1,255 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>552</width>
-    <height>847</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout_2">
-   <item>
-    <widget class="QScrollArea" name="scrollArea">
-     <property name="verticalScrollBarPolicy">
-      <enum>Qt::ScrollBarAsNeeded</enum>
-     </property>
-     <property name="widgetResizable">
-      <bool>true</bool>
-     </property>
-     <widget class="QWidget" name="scrollAreaWidgetContents_3">
-      <property name="geometry">
-       <rect>
-        <x>0</x>
-        <y>0</y>
-        <width>532</width>
-        <height>827</height>
-       </rect>
-      </property>
-      <widget class="QLabel" name="themeLabel">
-       <property name="geometry">
-        <rect>
-         <x>30</x>
-         <y>140</y>
-         <width>67</width>
-         <height>17</height>
-        </rect>
-       </property>
-       <property name="text">
-        <string>TextLabel</string>
-       </property>
-      </widget>
-      <widget class="QComboBox" name="themeComboBox">
-       <property name="geometry">
-        <rect>
-         <x>20</x>
-         <y>180</y>
-         <width>471</width>
-         <height>31</height>
-        </rect>
-       </property>
-      </widget>
-      <widget class="QComboBox" name="languageComboBox">
-       <property name="geometry">
-        <rect>
-         <x>20</x>
-         <y>60</y>
-         <width>471</width>
-         <height>31</height>
-        </rect>
-       </property>
-      </widget>
-      <widget class="QLabel" name="languageLabel">
-       <property name="geometry">
-        <rect>
-         <x>30</x>
-         <y>20</y>
-         <width>67</width>
-         <height>17</height>
-        </rect>
-       </property>
-       <property name="text">
-        <string>TextLabel</string>
-       </property>
-      </widget>
-      <!--
-      <widget class="QCheckBox" name="mirrorModeCheckBox">
-       <property name="geometry">
-        <rect>
-         <x>30</x>
-         <y>220</y>
-         <width>461</width>
-         <height>23</height>
-        </rect>
-       </property>
-       <property name="text">
-        <string>CheckBox</string>
-       </property>
-      </widget>
--->
-      <widget class="QGroupBox" name="smileysGroupBox">
-       <property name="geometry">
-        <rect>
-         <x>30</x>
-         <y>280</y>
-         <width>461</width>
-         <height>221</height>
-        </rect>
-       </property>
-       <property name="title">
-        <string>GroupBox</string>
-       </property>
-       <widget class="QCheckBox" name="smileysCheckBox">
-        <property name="geometry">
-         <rect>
-          <x>30</x>
-          <y>40</y>
-          <width>92</width>
-          <height>23</height>
-         </rect>
-        </property>
-        <property name="text">
-         <string>CheckBox</string>
-        </property>
-       </widget>
-       <widget class="QLabel" name="smileysPackLabel">
-        <property name="geometry">
-         <rect>
-          <x>30</x>
-          <y>80</y>
-          <width>411</width>
-          <height>17</height>
-         </rect>
-        </property>
-        <property name="text">
-         <string>TextLabel</string>
-        </property>
-       </widget>
-       <widget class="QComboBox" name="smileysPackComboBox">
-        <property name="geometry">
-         <rect>
-          <x>30</x>
-          <y>120</y>
-          <width>411</width>
-          <height>31</height>
-         </rect>
-        </property>
-       </widget>
-      </widget>
-      <widget class="QCheckBox" name="compactModeCheckBox">
-       <property name="geometry">
-        <rect>
-         <x>30</x>
-         <y>250</y>
-         <width>461</width>
-         <height>23</height>
-        </rect>
-       </property>
-       <property name="text">
-        <string>CheckBox</string>
-       </property>
-      </widget>
-      <widget class="QPushButton" name="importStickersPushButton">
-       <property name="geometry">
-        <rect>
-         <x>30</x>
-         <y>750</y>
-         <width>471</width>
-         <height>40</height>
-        </rect>
-       </property>
-       <property name="text">
-        <string>PushButton</string>
-       </property>
-      </widget>
-      <widget class="QPushButton" name="importSmileysPushButton">
-       <property name="geometry">
-        <rect>
-         <x>30</x>
-         <y>690</y>
-         <width>471</width>
-         <height>40</height>
-        </rect>
-       </property>
-       <property name="text">
-        <string>PushButton</string>
-       </property>
-      </widget>
-      <widget class="QCheckBox" name="showAvatarsCheckBox">
-       <property name="geometry">
-        <rect>
-         <x>30</x>
-         <y>520</y>
-         <width>461</width>
-         <height>23</height>
-        </rect>
-       </property>
-       <property name="text">
-        <string>CheckBox</string>
-       </property>
-      </widget>
-      <widget class="QGroupBox" name="appClosingGroupBox">
-       <property name="geometry">
-        <rect>
-         <x>30</x>
-         <y>550</y>
-         <width>471</width>
-         <height>131</height>
-        </rect>
-       </property>
-       <property name="title">
-        <string>GroupBox</string>
-       </property>
-       <widget class="QRadioButton" name="closeRadioButton">
-        <property name="geometry">
-         <rect>
-          <x>30</x>
-          <y>30</y>
-          <width>421</width>
-          <height>23</height>
-         </rect>
-        </property>
-        <property name="text">
-         <string>RadioButton</string>
-        </property>
-       </widget>
-       <widget class="QRadioButton" name="hideRadioButton">
-        <property name="geometry">
-         <rect>
-          <x>30</x>
-          <y>60</y>
-          <width>431</width>
-          <height>23</height>
-         </rect>
-        </property>
-        <property name="text">
-         <string>RadioButton</string>
-        </property>
-       </widget>
-       <widget class="QRadioButton" name="closeToTrayRadioButton">
-        <property name="geometry">
-         <rect>
-          <x>30</x>
-          <y>90</y>
-          <width>421</width>
-          <height>23</height>
-         </rect>
-        </property>
-        <property name="text">
-         <string>RadioButton</string>
-        </property>
-       </widget>
-      </widget>
-     </widget>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/join_group_screen.ui b/toxygen/ui/views/join_group_screen.ui
deleted file mode 100644
index 077a332..0000000
--- a/toxygen/ui/views/join_group_screen.ui
+++ /dev/null
@@ -1,139 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>740</width>
-    <height>320</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>740</width>
-    <height>320</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>740</width>
-    <height>320</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="chatIdLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>30</y>
-     <width>67</width>
-     <height>17</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="passwordLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>90</y>
-     <width>67</width>
-     <height>17</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="joinGroupButton">
-   <property name="enabled">
-    <bool>false</bool>
-   </property>
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>260</y>
-     <width>680</width>
-     <height>51</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string/>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="chatIdLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>190</x>
-     <y>20</y>
-     <width>520</width>
-     <height>41</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="passwordLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>190</x>
-     <y>80</y>
-     <width>520</width>
-     <height>41</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLabel" name="nickLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>150</y>
-     <width>67</width>
-     <height>17</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="statusLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>210</y>
-     <width>67</width>
-     <height>17</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="nickLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>190</x>
-     <y>140</y>
-     <width>520</width>
-     <height>41</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="statusComboBox">
-   <property name="geometry">
-    <rect>
-     <x>190</x>
-     <y>200</y>
-     <width>520</width>
-     <height>41</height>
-    </rect>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/login_screen.ui b/toxygen/ui/views/login_screen.ui
deleted file mode 100644
index d100803..0000000
--- a/toxygen/ui/views/login_screen.ui
+++ /dev/null
@@ -1,135 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>loginScreen</class>
- <widget class="QWidget" name="loginScreen">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>400</width>
-    <height>200</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>400</width>
-    <height>200</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>400</width>
-    <height>200</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="toxygenLabel">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>5</y>
-     <width>401</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="font">
-    <font>
-     <pointsize>16</pointsize>
-     <weight>75</weight>
-     <bold>true</bold>
-    </font>
-   </property>
-   <property name="text">
-    <string>Toxygen</string>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignCenter</set>
-   </property>
-  </widget>
-  <widget class="QGroupBox" name="newProfileGroupBox">
-   <property name="geometry">
-    <rect>
-     <x>10</x>
-     <y>40</y>
-     <width>180</width>
-     <height>150</height>
-    </rect>
-   </property>
-   <property name="title">
-    <string>GroupBox</string>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignCenter</set>
-   </property>
-   <widget class="QPushButton" name="createProfilePushButton">
-    <property name="geometry">
-     <rect>
-      <x>10</x>
-      <y>110</y>
-      <width>160</width>
-      <height>27</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>PushButton</string>
-    </property>
-   </widget>
-  </widget>
-  <widget class="QGroupBox" name="existingProfileGroupBox">
-   <property name="geometry">
-    <rect>
-     <x>210</x>
-     <y>40</y>
-     <width>180</width>
-     <height>150</height>
-    </rect>
-   </property>
-   <property name="title">
-    <string>GroupBox</string>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignCenter</set>
-   </property>
-   <widget class="QComboBox" name="profilesComboBox">
-    <property name="geometry">
-     <rect>
-      <x>10</x>
-      <y>40</y>
-      <width>160</width>
-      <height>27</height>
-     </rect>
-    </property>
-   </widget>
-   <widget class="QCheckBox" name="defaultProfileCheckBox">
-    <property name="geometry">
-     <rect>
-      <x>10</x>
-      <y>75</y>
-      <width>160</width>
-      <height>27</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>CheckBox</string>
-    </property>
-   </widget>
-   <widget class="QPushButton" name="loadProfilePushButton">
-    <property name="geometry">
-     <rect>
-      <x>10</x>
-      <y>110</y>
-      <width>160</width>
-      <height>27</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>PushButton</string>
-    </property>
-   </widget>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/ms_left_column.ui b/toxygen/ui/views/ms_left_column.ui
deleted file mode 100644
index ffbff71..0000000
--- a/toxygen/ui/views/ms_left_column.ui
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>270</width>
-    <height>500</height>
-   </rect>
-  </property>
-  <property name="cursor">
-   <cursorShape>PointingHandCursor</cursorShape>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="avatarLabel">
-   <property name="geometry">
-    <rect>
-     <x>5</x>
-     <y>5</y>
-     <width>64</width>
-     <height>64</height>
-    </rect>
-   </property>
-   <property name="cursor">
-    <cursorShape>PointingHandCursor</cursorShape>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="searchLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>75</y>
-     <width>150</width>
-     <height>25</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="contactsFilterComboBox">
-   <property name="geometry">
-    <rect>
-     <x>150</x>
-     <y>75</y>
-     <width>120</width>
-     <height>25</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLabel" name="searchLabel">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>77</y>
-     <width>20</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QListWidget" name="friendsListWidget">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>100</y>
-     <width>270</width>
-     <height>400</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="groupInvitesPushButton">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>100</y>
-     <width>270</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/network_settings_screen.ui b/toxygen/ui/views/network_settings_screen.ui
deleted file mode 100644
index f6e2960..0000000
--- a/toxygen/ui/views/network_settings_screen.ui
+++ /dev/null
@@ -1,196 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>400</width>
-    <height>545</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>400</width>
-    <height>545</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>400</width>
-    <height>545</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QCheckBox" name="ipv6CheckBox">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>20</y>
-     <width>150</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
-  <widget class="QCheckBox" name="udpCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>210</x>
-     <y>20</y>
-     <width>150</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
-  <widget class="QCheckBox" name="proxyCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>140</y>
-     <width>150</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
-  <widget class="QRadioButton" name="httpProxyRadioButton">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>190</y>
-     <width>150</width>
-     <height>25</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>RadioButton</string>
-   </property>
-  </widget>
-  <widget class="QRadioButton" name="socksProxyRadioButton">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>230</y>
-     <width>150</width>
-     <height>25</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>RadioButton</string>
-   </property>
-  </widget>
-  <widget class="QCheckBox" name="lanCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>100</y>
-     <width>150</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="ipLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>280</y>
-     <width>60</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-   </property>
-  </widget>
-  <widget class="QLabel" name="portLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>330</y>
-     <width>60</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-   </property>
-  </widget>
-  <widget class="QLabel" name="urlLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>380</y>
-     <width>60</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>Chat Url</string>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="restartCorePushButton">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>430</y>
-     <width>340</width>
-     <height>40</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QCheckBox" name="downloadNodesCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>60</y>
-     <width>340</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="warningLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>480</y>
-     <width>340</width>
-     <height>65</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/notifications_settings_screen.ui b/toxygen/ui/views/notifications_settings_screen.ui
deleted file mode 100644
index 67e2dc6..0000000
--- a/toxygen/ui/views/notifications_settings_screen.ui
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>320</width>
-    <height>201</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QCheckBox" name="notificationsCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>20</y>
-     <width>271</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
-  <widget class="QCheckBox" name="soundNotificationsCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>60</y>
-     <width>271</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
-  <widget class="QCheckBox" name="groupNotificationsCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>100</y>
-     <width>271</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
-  <widget class="QCheckBox" name="callsSoundCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>20</x>
-     <y>140</y>
-     <width>271</width>
-     <height>41</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/peer_screen.ui b/toxygen/ui/views/peer_screen.ui
deleted file mode 100644
index 086dd18..0000000
--- a/toxygen/ui/views/peer_screen.ui
+++ /dev/null
@@ -1,202 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>600</width>
-    <height>500</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>600</width>
-    <height>500</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>600</width>
-    <height>500</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="peerNameLabel">
-   <property name="geometry">
-    <rect>
-     <x>110</x>
-     <y>10</y>
-     <width>431</width>
-     <height>40</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="sendPrivateMessagePushButton">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>140</y>
-     <width>500</width>
-     <height>50</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QCheckBox" name="ignorePeerCheckBox">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>100</y>
-     <width>500</width>
-     <height>23</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>CheckBox</string>
-   </property>
-  </widget>
-  <widget class="QGroupBox" name="banGroupBox">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>300</y>
-     <width>500</width>
-     <height>161</height>
-    </rect>
-   </property>
-   <property name="title">
-    <string>GroupBox</string>
-   </property>
-   <!--
-   <widget class="QPushButton" name="banPushButton">
-    <property name="geometry">
-     <rect>
-      <x>380</x>
-      <y>50</y>
-      <width>101</width>
-      <height>41</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>PushButton</string>
-    </property>
-   </widget>
--->
-   <widget class="QRadioButton" name="ipBanRadioButton">
-    <property name="geometry">
-     <rect>
-      <x>40</x>
-      <y>40</y>
-      <width>251</width>
-      <height>23</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>RadioButton</string>
-    </property>
-    <property name="checked">
-     <bool>true</bool>
-    </property>
-   </widget>
-   <widget class="QRadioButton" name="nickBanRadioButton">
-    <property name="geometry">
-     <rect>
-      <x>40</x>
-      <y>80</y>
-      <width>251</width>
-      <height>23</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>RadioButton</string>
-    </property>
-   </widget>
-   <widget class="QRadioButton" name="pkBanRadioButton">
-    <property name="geometry">
-     <rect>
-      <x>40</x>
-      <y>120</y>
-      <width>251</width>
-      <height>23</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>RadioButton</string>
-    </property>
-   </widget>
-   <widget class="QPushButton" name="kickPushButton">
-    <property name="geometry">
-     <rect>
-      <x>380</x>
-      <y>100</y>
-      <width>101</width>
-      <height>41</height>
-     </rect>
-    </property>
-    <property name="text">
-     <string>PushButton</string>
-    </property>
-   </widget>
-  </widget>
-  <widget class="QLabel" name="roleLabel">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>60</y>
-     <width>67</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="roleNameLabel">
-   <property name="geometry">
-    <rect>
-     <x>130</x>
-     <y>60</y>
-     <width>411</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="copyPublicKeyPushButton">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>210</y>
-     <width>500</width>
-     <height>50</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="rolesComboBox">
-   <property name="geometry">
-    <rect>
-     <x>130</x>
-     <y>55</y>
-     <width>291</width>
-     <height>30</height>
-    </rect>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/profile_settings_screen.ui b/toxygen/ui/views/profile_settings_screen.ui
deleted file mode 100644
index 1c899ab..0000000
--- a/toxygen/ui/views/profile_settings_screen.ui
+++ /dev/null
@@ -1,280 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>900</width>
-    <height>680</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="nameLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>10</y>
-     <width>161</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="statusLabel">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>90</y>
-     <width>161</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="nameLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>50</y>
-     <width>421</width>
-     <height>30</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="statusMessageLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>30</x>
-     <y>130</y>
-     <width>421</width>
-     <height>30</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="statusComboBox">
-   <property name="geometry">
-    <rect>
-     <x>520</x>
-     <y>30</y>
-     <width>311</width>
-     <height>30</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLabel" name="toxIdTitleLabel">
-   <property name="geometry">
-    <rect>
-     <x>40</x>
-     <y>180</y>
-     <width>131</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="toxIdLabel">
-   <property name="geometry">
-    <rect>
-     <x>40</x>
-     <y>210</y>
-     <width>831</width>
-     <height>60</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-   <property name="wordWrap">
-    <bool>true</bool>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="copyToxIdPushButton">
-   <property name="geometry">
-    <rect>
-     <x>40</x>
-     <y>280</y>
-     <width>371</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="copyPublicKeyPushButton">
-   <property name="geometry">
-    <rect>
-     <x>440</x>
-     <y>280</y>
-     <width>371</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="newAvatarPushButton">
-   <property name="geometry">
-    <rect>
-     <x>520</x>
-     <y>80</y>
-     <width>321</width>
-     <height>34</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="resetAvatarPushButton">
-   <property name="geometry">
-    <rect>
-     <x>520</x>
-     <y>130</y>
-     <width>321</width>
-     <height>34</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="profilePasswordLabel">
-   <property name="geometry">
-    <rect>
-     <x>60</x>
-     <y>380</y>
-     <width>161</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="passwordLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>420</y>
-     <width>421</width>
-     <height>30</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLineEdit" name="confirmPasswordLineEdit">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>470</y>
-     <width>421</width>
-     <height>30</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLabel" name="emptyPasswordLabel">
-   <property name="geometry">
-    <rect>
-     <x>500</x>
-     <y>420</y>
-     <width>381</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="warningLabel">
-   <property name="geometry">
-    <rect>
-     <x>60</x>
-     <y>580</y>
-     <width>381</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="defaultProfilePushButton">
-   <property name="geometry">
-    <rect>
-     <x>40</x>
-     <y>630</y>
-     <width>831</width>
-     <height>34</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="changePasswordPushButton">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>520</y>
-     <width>421</width>
-     <height>34</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="invalidPasswordsLabel">
-   <property name="geometry">
-    <rect>
-     <x>500</x>
-     <y>470</y>
-     <width>381</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="exportProfilePushButton">
-   <property name="geometry">
-    <rect>
-     <x>40</x>
-     <y>330</y>
-     <width>371</width>
-     <height>34</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="newNoSpamPushButton">
-   <property name="geometry">
-    <rect>
-     <x>440</x>
-     <y>330</y>
-     <width>371</width>
-     <height>34</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/self_peer_screen.ui b/toxygen/ui/views/self_peer_screen.ui
deleted file mode 100644
index 38e1f88..0000000
--- a/toxygen/ui/views/self_peer_screen.ui
+++ /dev/null
@@ -1,119 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>600</width>
-    <height>500</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>600</width>
-    <height>500</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>600</width>
-    <height>500</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="statusLabel">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>120</y>
-     <width>67</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="copyPublicKeyPushButton">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>250</y>
-     <width>500</width>
-     <height>50</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="statusComboBox">
-   <property name="geometry">
-    <rect>
-     <x>140</x>
-     <y>110</y>
-     <width>400</width>
-     <height>40</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLabel" name="nameLabel">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>40</y>
-     <width>67</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="roleLabel">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>190</y>
-     <width>67</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="roleNameLabel">
-   <property name="geometry">
-    <rect>
-     <x>140</x>
-     <y>190</y>
-     <width>411</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="savePushButton">
-   <property name="geometry">
-    <rect>
-     <x>50</x>
-     <y>330</y>
-     <width>500</width>
-     <height>50</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/update_settings_screen.ui b/toxygen/ui/views/update_settings_screen.ui
deleted file mode 100644
index 76e7c57..0000000
--- a/toxygen/ui/views/update_settings_screen.ui
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>400</width>
-    <height>120</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>400</width>
-    <height>120</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>400</width>
-    <height>120</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="updateModeLabel">
-   <property name="geometry">
-    <rect>
-     <x>25</x>
-     <y>5</y>
-     <width>350</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="updateModeComboBox">
-   <property name="geometry">
-    <rect>
-     <x>25</x>
-     <y>30</y>
-     <width>350</width>
-     <height>30</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="updatePushButton">
-   <property name="geometry">
-    <rect>
-     <x>25</x>
-     <y>70</y>
-     <width>350</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/views/video_settings_screen.ui b/toxygen/ui/views/video_settings_screen.ui
deleted file mode 100644
index cfa36fb..0000000
--- a/toxygen/ui/views/video_settings_screen.ui
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Form</class>
- <widget class="QWidget" name="Form">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>400</width>
-    <height>120</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>400</width>
-    <height>120</height>
-   </size>
-  </property>
-  <property name="maximumSize">
-   <size>
-    <width>400</width>
-    <height>120</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <widget class="QLabel" name="deviceLabel">
-   <property name="geometry">
-    <rect>
-     <x>25</x>
-     <y>5</y>
-     <width>350</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>TextLabel</string>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="deviceComboBox">
-   <property name="geometry">
-    <rect>
-     <x>25</x>
-     <y>30</y>
-     <width>350</width>
-     <height>30</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QPushButton" name="selectRegionPushButton">
-   <property name="geometry">
-    <rect>
-     <x>25</x>
-     <y>70</y>
-     <width>350</width>
-     <height>30</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>PushButton</string>
-   </property>
-  </widget>
-  <widget class="QComboBox" name="resolutionComboBox">
-   <property name="geometry">
-    <rect>
-     <x>25</x>
-     <y>70</y>
-     <width>350</width>
-     <height>30</height>
-    </rect>
-   </property>
-  </widget>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/toxygen/ui/widgets.py b/toxygen/ui/widgets.py
deleted file mode 100644
index 78e9a0a..0000000
--- a/toxygen/ui/widgets.py
+++ /dev/null
@@ -1,213 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from  qtpy import QtCore, QtGui, QtWidgets
-# from PyQt5.QtCore import pyqtSignal as Signal
-from  qtpy.QtCore import Signal
-
-import utils.ui as util_ui
-import logging
-
-global LOG
-LOG = logging.getLogger('app')
-
-class DataLabel(QtWidgets.QLabel):
-    """
-    Label with elided text
-    """
-    def setText(self, text):
-        try:
-            text = ''.join('\u25AF' if len(bytes(str(c), 'utf-8')) >= 4 else c for c in str(text))
-        except Exception as e:
-            LOG.error(f"DataLabel::setText:  {e}")
-            return
-
-        try:
-            metrics = QtGui.QFontMetrics(self.font())
-            text = metrics.elidedText(str(text), QtCore.Qt.ElideRight, self.width())
-        except Exception as e:
-            # RuntimeError: wrapped C/C++ object of type DataLabel has been deleted
-            text = str(text)
-
-        super().setText(text)
-
-class ComboBox(QtWidgets.QComboBox):
-
-    def __init__(self, *args):
-        super().__init__(*args)
-        self.view().setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
-
-
-class CenteredWidget(QtWidgets.QWidget):
-
-    def __init__(self):
-        super().__init__()
-        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
-        self.center()
-
-    def center(self):
-        qr = self.frameGeometry()
-        cp = QtWidgets.QDesktopWidget().availableGeometry().center()
-        qr.moveCenter(cp)
-        self.move(qr.topLeft())
-
-
-class DialogWithResult(QtWidgets.QWidget):
-
-    def __init__(self, parent=None):
-        super().__init__(parent)
-        self._result = None
-
-    def get_result(self):
-        return self._result
-
-    result = property(get_result)
-
-    def close_with_result(self, result):
-        self._result = result
-        self.close()
-
-
-class LineEdit(QtWidgets.QLineEdit):
-
-    def __init__(self, parent=None):
-        super().__init__(parent)
-
-    def contextMenuEvent(self, event):
-        menu = create_menu(self.createStandardContextMenu())
-        menu.exec_(event.globalPos())
-        del menu
-
-
-class QRightClickButton(QtWidgets.QPushButton):
-    """
-    Button with right click support
-    """
-    # FixMe: AttributeError: module 'qtpy.QtCore' has no attribute 'pyqtSignal'
-    rightClicked = Signal()
-
-    def __init__(self, parent=None):
-        super().__init__(parent)
-
-    def mousePressEvent(self, event):
-        if event.button() == QtCore.Qt.RightButton:
-            self.rightClicked.emit()
-        else:
-            super().mousePressEvent(event)
-
-
-class RubberBand(QtWidgets.QRubberBand):
-
-    def __init__(self):
-        super().__init__(QtWidgets.QRubberBand.Rectangle, None)
-        self.setPalette(QtGui.QPalette(QtCore.Qt.transparent))
-        self.pen = QtGui.QPen(QtCore.Qt.blue, 4)
-        self.pen.setStyle(QtCore.Qt.SolidLine)
-        self.painter = QtGui.QPainter()
-
-    def paintEvent(self, event):
-
-        self.painter.begin(self)
-        self.painter.setPen(self.pen)
-        self.painter.drawRect(event.rect())
-        self.painter.end()
-
-
-class RubberBandWindow(QtWidgets.QWidget):
-
-    def __init__(self, parent):
-        super().__init__()
-        self.parent = parent
-        self.setMouseTracking(True)
-        self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
-        self.showFullScreen()
-        self.setWindowOpacity(0.5)
-        self.rubberband = RubberBand()
-        self.rubberband.setWindowFlags(self.rubberband.windowFlags() | QtCore.Qt.FramelessWindowHint)
-        self.rubberband.setAttribute(QtCore.Qt.WA_TranslucentBackground)
-
-    def mousePressEvent(self, event):
-        self.origin = event.pos()
-        self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize()))
-        self.rubberband.show()
-        QtWidgets.QWidget.mousePressEvent(self, event)
-
-    def mouseMoveEvent(self, event):
-        if self.rubberband.isVisible():
-            self.rubberband.setGeometry(QtCore.QRect(self.origin, event.pos()).normalized())
-            left = QtGui.QRegion(QtCore.QRect(0, 0, self.rubberband.x(), self.height()))
-            right = QtGui.QRegion(QtCore.QRect(self.rubberband.x() + self.rubberband.width(), 0, self.width(), self.height()))
-            top = QtGui.QRegion(0, 0, self.width(), self.rubberband.y())
-            bottom = QtGui.QRegion(0, self.rubberband.y() + self.rubberband.height(), self.width(), self.height())
-            self.setMask(left + right + top + bottom)
-
-    def keyPressEvent(self, event):
-        if event.key() == QtCore.Qt.Key_Escape:
-            self.rubberband.setHidden(True)
-            self.close()
-        else:
-            super().keyPressEvent(event)
-
-
-def create_menu(menu):
-    """
-    :return translated menu
-    """
-    for action in menu.actions():
-        text = action.text()
-        if 'Link Location' in text:
-            text = text.replace('Copy &Link Location',
-                                util_ui.tr("Copy link location"))
-        elif '&Copy' in text:
-            text = text.replace('&Copy', util_ui.tr("Copy"))
-        elif 'All' in text:
-            text = text.replace('Select All', util_ui.tr("Select all"))
-        elif 'Delete' in text:
-            text = text.replace('Delete', util_ui.tr("Delete"))
-        elif '&Paste' in text:
-            text = text.replace('&Paste', util_ui.tr("Paste"))
-        elif 'Cu&t' in text:
-            text = text.replace('Cu&t', util_ui.tr("Cut"))
-        elif '&Undo' in text:
-            text = text.replace('&Undo', util_ui.tr("Undo"))
-        elif '&Redo' in text:
-            text = text.replace('&Redo', util_ui.tr("Redo"))
-        else:
-            menu.removeAction(action)
-            continue
-        action.setText(text)
-    return menu
-
-
-class MultilineEdit(CenteredWidget):
-
-    def __init__(self, title, text, save):
-        super(MultilineEdit, self).__init__()
-        self.resize(350, 200)
-        self.setMinimumSize(QtCore.QSize(350, 200))
-        self.setMaximumSize(QtCore.QSize(350, 200))
-        self.setWindowTitle(title)
-        self.edit = QtWidgets.QTextEdit(self)
-        self.edit.setGeometry(QtCore.QRect(0, 0, 350, 150))
-        self.edit.setText(text)
-        self.button = QtWidgets.QPushButton(self)
-        self.button.setGeometry(QtCore.QRect(0, 150, 350, 50))
-        self.button.setText(util_ui.tr("Save"))
-        self.button.clicked.connect(self.button_click)
-        self.center()
-        self.save = save
-
-    def button_click(self):
-        self.save(self.edit.toPlainText())
-        self.close()
-
-
-class LineEditWithEnterSupport(LineEdit):
-
-    def __init__(self, enter_action, parent=None):
-        super().__init__(parent)
-        self._action = enter_action
-
-    def keyPressEvent(self, event):
-        if event.key() == QtCore.Qt.Key_Return:
-            self._action()
-        else:
-            super().keyPressEvent(event)
diff --git a/toxygen/ui/widgets_factory.py b/toxygen/ui/widgets_factory.py
deleted file mode 100644
index 08861a4..0000000
--- a/toxygen/ui/widgets_factory.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-from ui.main_screen_widgets import *
-from ui.menu import *
-from ui.groups_widgets import *
-from ui.peer_screen import *
-from ui.self_peer_screen import *
-from ui.group_invites_widgets import *
-from ui.group_settings_widgets import *
-from ui.group_bans_widgets import *
-from ui.profile_settings_screen import ProfileSettings
-
-class WidgetsFactory:
-
-    def __init__(self, settings, profile, profile_manager, contacts_manager, file_transfer_handler, smiley_loader,
-                 plugin_loader, toxes, version, groups_service, history, contacts_provider):
-        self._settings = settings
-        self._profile = profile
-        self._profile_manager = profile_manager
-        self._contacts_manager = contacts_manager
-        self._file_transfer_handler = file_transfer_handler
-        self._smiley_loader = smiley_loader
-        self._plugin_loader = plugin_loader
-        self._toxes = toxes
-        self._version = version
-        self._groups_service = groups_service
-        self._history = history
-        self._contacts_provider = contacts_provider
-
-    def create_screenshot_window(self, *args):
-        return ScreenShotWindow(self._file_transfer_handler, self._contacts_manager, *args)
-
-    def create_welcome_window(self):
-        return WelcomeScreen(self._settings)
-
-    def create_profile_settings_window(self):
-        return ProfileSettings(self._profile, self._profile_manager,  self._settings, self._toxes)
-
-    def create_network_settings_window(self):
-        return NetworkSettings(self._settings, self._profile.restart)
-
-    def create_audio_settings_window(self):
-        return AudioSettings(self._settings)
-
-    def create_video_settings_window(self):
-        try:
-            with ts.ignoreStdout(): import cv2
-        except ImportError:
-            cv2 = None
-        if cv2 is None: return None
-        return VideoSettings(self._settings)
-
-    def create_update_settings_window(self):
-        return UpdateSettings(self._settings, self._version)
-
-    def create_plugins_settings_window(self):
-        return PluginsSettings(self._plugin_loader)
-
-    def create_add_contact_window(self, tox_id):
-        return AddContact(self._settings, self._contacts_manager, tox_id)
-
-    def create_privacy_settings_window(self):
-        return PrivacySettings(self._contacts_manager, self._settings)
-
-    def create_interface_settings_window(self):
-        return InterfaceSettings(self._settings, self._smiley_loader)
-
-    def create_notification_settings_window(self):
-        return NotificationsSettings(self._settings)
-
-    def create_smiley_window(self, parent):
-        return SmileyWindow(parent, self._smiley_loader)
-
-    def create_sticker_window(self):
-        return StickerWindow(self._file_transfer_handler, self._contacts_manager)
-
-    def create_group_screen_window(self):
-        return CreateGroupScreen(self._groups_service, self._profile)
-
-    def create_join_group_screen_window(self):
-        return JoinGroupScreen(self._groups_service, self._profile)
-
-    def create_search_screen(self, messages):
-        return SearchScreen(self._contacts_manager, self._history, messages, messages.parent())
-
-    def create_peer_screen_window(self, group, peer_id):
-        return PeerScreen(self._contacts_manager, self._groups_service, group, peer_id)
-
-    def create_self_peer_screen_window(self, group):
-        return SelfPeerScreen(self._contacts_manager, self._groups_service, group)
-
-    def create_group_invites_window(self):
-        return GroupInvitesScreen(self._groups_service, self._profile, self._contacts_provider)
-
-    def create_group_management_screen(self, group):
-        return GroupManagementScreen(self._groups_service, group)
-
-    @staticmethod
-    def create_group_settings_screen(group):
-        return GroupSettingsScreen(group)
-
-    def create_groups_bans_screen(self, group):
-        return GroupBansScreen(self._groups_service, group)
diff --git a/toxygen/updater/__init__.py b/toxygen/updater/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/updater/updater.py b/toxygen/updater/updater.py
deleted file mode 100644
index 0eb81f3..0000000
--- a/toxygen/updater/updater.py
+++ /dev/null
@@ -1,129 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import utils.util as util
-import utils.ui as util_ui
-import os
-import platform
-import urllib
-from  qtpy import QtNetwork, QtCore
-import subprocess
-
-global LOG
-import logging
-LOG = logging.getLogger('app.'+__name__)
-
-TIMEOUT=10
-
-def connection_available():
-    return False
-    try:
-        urllib.request.urlopen('http://216.58.192.142', timeout=TIMEOUT)  # google.com
-        return True
-    except:
-        return False
-
-def updater_available():
-    if is_from_sources():
-        return os.path.exists(util.curr_directory() + '/toxygen_updater.py')
-    elif platform.system() == 'Windows':
-        return os.path.exists(util.curr_directory() + '/toxygen_updater.exe')
-    else:
-        return os.path.exists(util.curr_directory() + '/toxygen_updater')
-
-
-def check_for_updates(current_version, settings):
-    major, minor, patch = list(map(lambda x: int(x), current_version.split('.')))
-    versions = generate_versions(major, minor, patch)
-    for version in versions:
-        if send_request(version, settings):
-            return version
-    return None  # no new version was found
-
-
-def is_from_sources():
-    return __file__.endswith('.py')
-
-
-def test_url(version):
-    return 'https://github.com/toxygen-project/toxygen/releases/tag/v' + version
-
-
-def get_url(version):
-    if is_from_sources():
-        return 'https://github.com/toxygen-project/toxygen/archive/v' + version + '.zip'
-    else:
-        if platform.system() == 'Windows':
-            name = 'toxygen_windows.zip'
-        elif util.is_64_bit():
-            name = 'toxygen_linux_64.tar.gz'
-        else:
-            name = 'toxygen_linux.tar.gz'
-        return 'https://github.com/toxygen-project/toxygen/releases/download/v{}/{}'.format(version, name)
-
-
-def get_params(url, version):
-    if is_from_sources():
-        if platform.system() == 'Windows':
-            return ['python', 'toxygen_updater.py', url, version]
-        else:
-            return ['python3', 'toxygen_updater.py', url, version]
-    elif platform.system() == 'Windows':
-        return [util.curr_directory() + '/toxygen_updater.exe', url, version]
-    else:
-        return ['./toxygen_updater', url, version]
-
-
-def download(version):
-    os.chdir(util.curr_directory())
-    url = get_url(version)
-    params = get_params(url, version)
-    LOG.info('Updating Toxygen')
-    try:
-        subprocess.Popen(params)
-    except Exception as ex:
-        LOG.error('running updater failed with ' + str(ex))
-
-
-def send_request(version, settings):
-    netman = QtNetwork.QNetworkAccessManager()
-    proxy = QtNetwork.QNetworkProxy()
-    if settings['proxy_type']:
-        proxy.setType(QtNetwork.QNetworkProxy.Socks5Proxy if settings['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy)
-        proxy.setHostName(settings['proxy_host'])
-        proxy.setPort(settings['proxy_port'])
-        netman.setProxy(proxy)
-    url = test_url(version)
-    try:
-        request = QtNetwork.QNetworkRequest()
-        request.setUrl(QtCore.QUrl(url))
-        reply = netman.get(request)
-        while not reply.isFinished():
-            QtCore.QThread.msleep(1)
-            QtCore.QCoreApplication.processEvents()
-        attr = reply.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute)
-        return attr is not None and 200 <= attr < 300
-    except Exception as ex:
-        LOG.error('TOXYGEN UPDATER ' + str(ex))
-        return False
-
-
-def generate_versions(major, minor, patch):
-    new_major = '.'.join([str(major + 1), '0', '0'])
-    new_minor = '.'.join([str(major), str(minor + 1), '0'])
-    new_patch = '.'.join([str(major), str(minor), str(patch + 1)])
-    return new_major, new_minor, new_patch
-
-
-def start_update_if_needed(version, settings):
-    updating = False
-    if settings['update'] and updater_available() and connection_available():  # auto update
-        version = check_for_updates(version, settings)
-        if version is not None:
-            if settings['update'] == 2:
-                download(version)
-                updating = True
-            else:
-                reply = util_ui.question(util_ui.tr('Update for Toxygen was found. Download and install it?'))
-                if reply:
-                    download(version)
-                    updating = True
-    return updating
diff --git a/toxygen/user_data/__init__.py b/toxygen/user_data/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/user_data/backup_service.py b/toxygen/user_data/backup_service.py
deleted file mode 100644
index 9f3a051..0000000
--- a/toxygen/user_data/backup_service.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import os.path
-
-from utils.util import get_profile_name_from_path, join_path
-
-class BackupService:
-
-    def __init__(self, settings, profile_manager):
-        self._settings = settings
-        self._profile_name = get_profile_name_from_path(profile_manager.get_path())
-
-        settings.settings_saved_event.add_callback(self._settings_saved)
-        profile_manager.profile_saved_event.add_callback(self._profile_saved)
-
-    def _settings_saved(self, data):
-        if not self._check_if_should_save_backup():
-            return
-
-        file_path = join_path(self._get_backup_directory(), self._profile_name + '.json')
-
-        with open(file_path, 'wt') as fl:
-            fl.write(data)
-
-    def _profile_saved(self, data):
-        if not self._check_if_should_save_backup():
-            return
-
-        file_path = join_path(self._get_backup_directory(), self._profile_name + '.tox')
-
-        with open(file_path, 'wb') as fl:
-            fl.write(data)
-
-    def _check_if_should_save_backup(self):
-        backup_directory = self._get_backup_directory()
-        if backup_directory is None:
-            return False
-
-        return os.path.exists(backup_directory) and os.path.isdir(backup_directory)
-
-    def _get_backup_directory(self):
-        return self._settings['backup_directory']
diff --git a/toxygen/user_data/profile_manager.py b/toxygen/user_data/profile_manager.py
deleted file mode 100644
index a6f5df0..0000000
--- a/toxygen/user_data/profile_manager.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import utils.util as util
-import os
-
-from user_data.settings import Settings
-from common.event import Event
-from user_data.settings import get_user_config_path
-
-global LOG
-import logging
-LOG = logging.getLogger('app.'+__name__)
-from av.calls import LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG, LOG_TRACE
-
-class ProfileManager:
-    """
-    Class with methods for search, load and save profiles
-    """
-    def __init__(self, toxes, path, app=None):
-        assert path
-        self._toxes = toxes
-        self._path = path
-        assert path
-        self._app = app
-        self._directory = os.path.dirname(path)
-        self._profile_saved_event = Event()
-        # create /avatars if not exists:
-        avatars_directory = util.join_path(self._directory, 'avatars')
-        if not os.path.exists(avatars_directory):
-            os.makedirs(avatars_directory)
-
-    # Properties
-
-    def get_profile_saved_event(self):
-        return self._profile_saved_event
-
-    profile_saved_event = property(get_profile_saved_event)
-
-    # Public methods
-
-    def open_profile(self):
-        with open(self._path, 'rb') as fl:
-            data = fl.read()
-        if data:
-            return data
-        else:
-            raise IOError('Save file has zero size!')
-
-    def get_dir(self):
-        return self._directory
-
-    def get_path(self):
-        return self._path
-
-    def save_profile(self, data):
-        if self._toxes.has_password():
-            data = self._toxes.pass_encrypt(data)
-        profile_path = self._path.replace('.json', '.tox')
-        try:
-            suf = f"{os.getpid()}"
-            with open(profile_path+suf, 'wb') as fl:
-                fl.write(data)
-            stat = os.stat(profile_path+suf)
-            if hasattr(stat, 'st_blocks'):
-                assert stat.st_blocks > 0, f"Zero length file {profile_path+suf}"
-            os.rename(profile_path+suf,profile_path)
-            LOG_INFO('Profile saved successfully to' +profile_path)
-        except Exception as e:
-            LOG_WARN(f"Profile save failed to {profile_path}\n{e}")
-
-        self._profile_saved_event(data)
-
-    def export_profile(self, settings, new_path, use_new_path):
-        profile_path = self._path.replace('.json', '.tox')
-        with open(profile_path, 'rb') as fin:
-            data = fin.read()
-        path = new_path + os.path.basename(profile_path)
-        with open(path, 'wb') as fout:
-            fout.write(data)
-        LOG.info('Profile exported successfully to ' +path)
-        util.copy(os.path.join(self._directory, 'avatars'),
-                  os.path.join(new_path, 'avatars'))
-        if use_new_path:
-            profile_path = os.path.join(new_path, os.path.basename(profile_path))
-            self._directory = new_path
-            settings.update_path(new_path)
-
-    @staticmethod
-    def find_profiles():
-        """
-        Find available tox profiles
-        """
-        path = get_user_config_path()
-        result = []
-        # check default path
-        if not os.path.exists(path):
-            os.makedirs(path)
-        for fl in os.listdir(path):
-            if fl.endswith('.tox'):
-                name = fl[:-4]
-                result.append((path, name))
-        path = util.get_base_directory(__file__)
-        # check current directory
-        for fl in os.listdir(path):
-            if fl.endswith('.tox'):
-                name = fl[:-4]
-                result.append((path + '/', name))
-        return result
diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py
deleted file mode 100644
index c87eec3..0000000
--- a/toxygen/user_data/settings.py
+++ /dev/null
@@ -1,428 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-import os
-from platform import system
-import json
-
-from utils import util
-from utils.util import log, join_path
-from common.event import Event
-import utils.ui as util_ui
-import utils.util as util_utils
-import user_data
-from toxygen_wrapper.tests import support_testing as ts
-
-global LOG
-import logging
-LOG = logging.getLogger('settings')
-
-def merge_args_into_settings(args, settings):
-    if args:
-        if not hasattr(args, 'audio'):
-            LOG.warn('No audio ' +repr(args))
-        settings['audio'] = getattr(args, 'audio')
-        if not hasattr(args, 'video'):
-            LOG.warn('No video ' +repr(args))
-        settings['video'] = getattr(args, 'video')
-        for key in settings.keys():
-            # proxy_type proxy_port proxy_host
-            not_key = 'not_' +key
-            if hasattr(args, key):
-                val = getattr(args, key)
-                if type(val) == bytes:
-                    # proxy_host - ascii?
-                    # filenames - ascii?
-                    val = str(val, 'UTF-8')
-                settings[key] = val
-            elif hasattr(args, not_key):
-                val = not getattr(args, not_key)
-                settings[key] = val
-    clean_settings(settings)
-    return
-
-def clean_settings(self):
-    # failsafe to ensure C tox is bytes and Py settings is str
-
-    # overrides
-    self['mirror_mode'] = False
-    # REQUIRED!!
-    if not os.path.exists('/proc/sys/net/ipv6'):
-        LOG.warn('Disabling IPV6 because /proc/sys/net/ipv6 does not exist')
-        self['ipv6_enabled'] = False
-
-    if 'proxy_type' in self and self['proxy_type'] == 0:
-        self['proxy_host'] = ''
-        self['proxy_port'] = 0
-
-    if 'proxy_type' in self and self['proxy_type'] != 0 and \
-        'proxy_host' in self and self['proxy_host'] != '' and \
-        'proxy_port' in self and self['proxy_port'] != 0:
-        if 'udp_enabled' in self and self['udp_enabled']:
-            # We don't currently support UDP over proxy.
-            LOG.info("UDP enabled and proxy set: disabling UDP")
-        self['udp_enabled'] = False
-        if 'local_discovery_enabled' in self and self['local_discovery_enabled']:
-            LOG.info("local_discovery_enabled enabled and proxy set: disabling local_discovery_enabled")
-        self['local_discovery_enabled'] = False
-        if 'dht_announcements_enabled' in self and self['dht_announcements_enabled']:
-            LOG.info("dht_announcements_enabled enabled and proxy set: disabling dht_announcements_enabled")
-        self['dht_announcements_enabled'] = False
-
-    if 'auto_accept_path' in self and \
-       type(self['auto_accept_path']) == bytes:
-        self['auto_accept_path'] = str(self['auto_accept_path'], 'UTF-8')
-
-    for key in Settings.get_default_settings():
-        if key not in self: continue
-        if type(self[key]) == bytes:
-            LOG.warn('bytes setting in: ' +key \
-                     +' ' + repr(self[key]))
-            # ascii?
-            # self[key] = str(self[key], 'utf-8')
-    LOG.debug("Cleaned settings")
-
-def get_user_config_path():
-    system = util_utils.get_platform()
-    if system == 'Windows':
-        return os.path.join(os.getenv('APPDATA'), 'Tox/')
-    elif system == 'Darwin':
-        return os.path.join(os.getenv('HOME'), 'Library/Application Support/Tox/')
-    else:
-        return os.path.join(os.getenv('HOME'), '.config/tox/')
-
-def supported_languages():
-    return {
-        'English': 'en_EN',
-        'French': 'fr_FR',
-        'Russian': 'ru_RU',
-        'Ukrainian': 'uk_UA'
-    }
-
-def built_in_themes():
-    return {
-        'dark': 'dark_style.qss',
-        'default': 'style.qss'
-    }
-
-#def get_global_settings_path():
-#    return os.path.join(get_base_directory(), 'toxygen.json')
-
-def is_active_profile(profile_path):
-    sFile = profile_path + '.lock'
-    if not os.path.isfile(sFile):
-        return False
-    try:
-        import psutil
-    except Exception as e:
-        return True
-    with open(sFile, 'rb') as iFd:
-        sPid = iFd.read()
-    if sPid and int(sPid.strip()) in psutil.pids():
-        return True
-    LOG.debug('Unlinking stale lock file ' +sFile)
-    try:
-        os.unlink(sFile)
-    except:
-        pass
-    return False
-
-class Settings(dict):
-    """
-    Settings of current profile + global app settings
-    """
-
-    def __init__(self, toxes, json_path, app):
-        self._toxes = toxes
-        self._app = app
-        self._path = app._path
-        self._args = app._args
-        self._oArgs = app._args
-        self._log = lambda l: LOG.log(self._oArgs.loglevel, l)
-        self._profile_path = json_path.replace('.json', '.tox')
-
-        self._settings_saved_event = Event()
-        path = json_path.replace('.tox', '.json')
-        if path and os.path.isfile(path):
-            try:
-                with open(path, 'rb') as fl:
-                    data = fl.read()
-                if self._toxes.is_data_encrypted(data):
-                    data = self._toxes.pass_decrypt(data)
-                info = json.loads(str(data, 'utf-8'))
-                LOG.debug('Parsed settings from: ' + str(path))
-            except Exception as ex:
-                title = f"Error opening/parsing settings file:"
-                text = title +f"\n{path}\n"
-                LOG.error(text +str(ex))
-                util_ui.message_box(text, title)
-                info = Settings.get_default_settings(app._args)
-            user_data.settings.clean_settings(info)
-        else:
-            LOG.debug('get_default_settings for: ' + repr(path))
-            info = Settings.get_default_settings(app._args)
-
-        if not path or not os.path.exists(path):
-            merge_args_into_settings(app._args, info)
-        else:
-            aC = self._changed(app._args, info)
-            if aC:
-                title = 'Override profile with commandline - '
-                if path:
-                    title += os.path.basename(path)
-                text = 'Override profile with command-line settings? \n'
-    #            text += '\n'.join([str(key) +'=' +str(val) for
-    #                               key,val in self._changed(app._args).items()])
-                text += repr(aC)
-                reply = util_ui.question(text, title)
-                if reply:
-                    merge_args_into_settings(app._args, info)
-        info['audio'] = getattr(app._args, 'audio')
-        info['video'] = getattr(app._args, 'video')
-        if getattr(app._args, 'trace_enabled'):
-            info['trace_enabled'] = getattr(app._args, 'trace_enabled')
-        else:
-            LOG.warn("app._args, 'trace_enabled")
-            info['trace_enabled'] = False
-        super().__init__(info)
-        self._upgrade()
-
-        LOG.info('Parsed settings from: ' + str(path))
-        ex = f"self=id(self) {self}"
-        LOG.debug(ex)
-
-        self.save()
-        self.locked = False
-        self.closing = False
-        self.unlockScreen = False
-
-    # Properties
-
-    def get_settings_saved_event(self):
-        return self._settings_saved_event
-
-    settings_saved_event = property(get_settings_saved_event)
-
-    # Public methods
-
-    def save(self):
-        text = json.dumps(self)
-        if self._toxes.has_password():
-            text = bytes(self._toxes.pass_encrypt(bytes(text, 'utf-8')))
-        else:
-            text = bytes(text, 'utf-8')
-        json_path = os.path.join(get_user_config_path(), 'toxygen.json')
-        tmp = json_path + str(os.getpid())
-        try:
-            with open(tmp, 'wb') as fl:
-                fl.write(text)
-            if os.path.exists(json_path+'.bak'):
-                os.remove(json_path+'.bak')
-            os.rename(json_path, json_path+'.bak')
-            os.rename(tmp, json_path)
-        except Exception as e:
-            LOG.warn(f'Error saving to {json_path} ' +str(e))
-        else:
-            self._settings_saved_event(text)
-
-    def close(self):
-        path = self._profile_path + '.lock'
-        if os.path.isfile(path):
-            os.remove(path)
-
-    def set_active_profile(self, profile_path):
-        """
-        Mark current profile as active
-        """
-        if not profile_path:
-            profile_path = self.get_auto_profile()
-
-        path = profile_path + '.lock'
-        try:
-            import shutil
-        except:
-            pass
-        else:
-            shutil.copy2(profile_path, path)
-            # need to open this with the same perms as _profile_path
-            # copy profile_path and then write?
-        with open(path, 'wb') as fl:
-            fl.write(bytes(str(os.getpid()), 'ascii'))
-
-    def export(self, path):
-        text = json.dumps(self)
-        name = os.path.basename(self._path)
-        with open(join_path(path, str(name)), 'w') as fl:
-            fl.write(text)
-
-    def update_path(self, new_path):
-        self._path = new_path
-        self.save()
-
-    # Static methods
-
-    @staticmethod
-    def get_auto_profile(appdir=None):
-        if appdir is None:
-            appdir = ts.get_user_config_path()
-        # self._path =
-        p = os.path.join(appdir, 'toxygen.json')
-        if not os.path.isfile(p):
-            return None
-        try:
-            with open(p, 'rb') as fl:
-                data = fl.read()
-            if self._toxes.is_data_encrypted(data):
-                data = self._toxes.pass_decrypt(data)
-        except Exception as ex:
-            LOG.warn(f"fl.read {p}: {ex}")
-            return None
-        try:
-            auto = json.loads(str(data, 'utf-8'))
-        except Exception as ex:
-            LOG.warn(f"json.loads {p}: {ex}")
-            auto = {}
-        if 'profile_path' in auto:
-            path = str(auto['profile_path'])
-            if not os.path.isabs(path):
-                path = join_path(path, os.path.dirname(os.path.realpath(__file__)))
-            if os.path.isfile(path):
-                return path
-        return None
-
-    @staticmethod
-    def supported_languages():
-        # backwards
-        return supported_languages()
-
-    @staticmethod
-    def set_auto_profile(path):
-        p = os.path.join(os.path.dirname(path), 'toxygen.json')
-        if os.path.isfile(p):
-            with open(p) as fl:
-                data = fl.read()
-            data = json.loads(data)
-        else:
-            data = {}
-        data['profile_path'] = str(path)
-        with open(p, 'w') as fl:
-            fl.write(json.dumps(data))
-
-    @staticmethod
-    def reset_auto_profile():
-        appdir = ts.get_user_config_path()
-        p = os.path.join(appdir, 'toxygen.json')
-        if os.path.isfile(p):
-            with open(p) as fl:
-                data = fl.read()
-            data = json.loads(data)
-        else:
-            data = {}
-        if 'profile_path' in data:
-            del data['profile_path']
-        with open(p, 'w') as fl:
-            fl.write(json.dumps(data))
-
-    @staticmethod
-    def get_default_settings(args=None):
-        """
-        Default profile settings
-        """
-        retval = {
-            # FixMe: match? /var/local/src/c-toxcore/toxcore/tox.h
-            'ipv6_enabled': True,
-            'udp_enabled': True,
-            'trace_enabled': False,
-            'local_discovery_enabled': True,
-            'dht_announcements_enabled': True,
-            'proxy_type': 0,
-            'proxy_host': '',
-            'proxy_port': 0,
-            'start_port': 0,
-            'end_port': 0,
-            'tcp_port': 0,
-            'local_discovery_enabled': True,
-            'hole_punching_enabled': False,
-            # tox_log_cb *log_callback;
-            'experimental_thread_safety': False,
-            # operating_system
-
-            'theme': 'default',
-            'notifications': False,
-            'sound_notifications': False,
-            'language': 'English',
-            'calls_sound': False, # was True
-
-            'save_history': True,
-            'save_unsent_only': False,
-            'allow_inline': True,
-            'allow_auto_accept': True,
-            'auto_accept_path': None,
-            'sorting': 0,
-            'auto_accept_from_friends': [],
-            'paused_file_transfers': {},
-            'resend_files': True,
-            'friends_aliases': [],
-            'show_avatars': False,
-            'typing_notifications': False,
-            'blocked': [],
-            'plugins': [],
-            'notes': {},
-            'smileys': True,
-            'smiley_pack': 'default',
-            'mirror_mode': False,
-            'width': 920,
-            'height': 500,
-            'x': 400,
-            'y': 400,
-            'message_font_size': 14,
-            'unread_color': 'red',
-            'compact_mode': False,
-            'identicons': True,
-            'show_welcome_screen': False,
-            'close_app': 0,
-            'font': 'Times New Roman',
-            'update': 0,
-            'group_notifications': True,
-            'download_nodes_list': False, #
-            'download_nodes_url': 'https://nodes.tox.chat/json',
-            'notify_all_gc': False,
-            'backup_directory': None,
-
-            'audio': {'input': -1,
-                      'output': -1,
-                      'enabled': True},
-            'video': {'device': -1,
-                       'width': 320,
-                       'height': 240,
-                       'x': 0,
-                       'y': 0},
-            'current_nodes': None,
-            'network': 'new',
-            'tray_icon': False,
-        }
-        return retval
-
-    # Private methods
-
-    def _upgrade(self):
-        default = Settings.get_default_settings()
-        for key in default:
-            if key not in self:
-                print(key)
-                self[key] = default[key]
-
-    def _changed(self, aArgs, info):
-        aRet = dict()
-        default = Settings.get_default_settings()
-        for key in default:
-            if key in ['audio', 'video']: continue
-            if key not in aArgs.__dict__: continue
-            val = aArgs.__dict__[key]
-            if val in ['0.0.0.0']: continue
-            if key in aArgs.__dict__ and key not in info:
-                # dunno = network
-                continue
-            if key in aArgs.__dict__ and info[key] != val:
-                aRet[key] = val
-        return aRet
-
diff --git a/toxygen/user_data/toxes.py b/toxygen/user_data/toxes.py
deleted file mode 100644
index 84b8636..0000000
--- a/toxygen/user_data/toxes.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-class ToxES:
-
-    def __init__(self, tox_encrypt_save):
-        self._tox_encrypt_save = tox_encrypt_save
-        self._password = None
-
-    def set_password(self, password):
-        self._password = password
-
-    def has_password(self):
-        return bool(self._password)
-
-    def is_password(self, password):
-        return self._password == password
-
-    def is_data_encrypted(self, data):
-        return len(data) > 0 and self._tox_encrypt_save.is_data_encrypted(data)
-
-    def pass_encrypt(self, data):
-        return self._tox_encrypt_save.pass_encrypt(data, self._password)
-
-    def pass_decrypt(self, data):
-        return self._tox_encrypt_save.pass_decrypt(data, self._password)
diff --git a/toxygen/util.py b/toxygen/util.py
new file mode 100644
index 0000000..471e850
--- /dev/null
+++ b/toxygen/util.py
@@ -0,0 +1,49 @@
+import os
+import time
+import shutil
+
+program_version = '0.2.2'
+
+
+def log(data):
+    with open(curr_directory() + '/logs.log', 'a') as fl:
+        fl.write(str(data) + '\n')
+
+
+def curr_directory():
+    return os.path.dirname(os.path.realpath(__file__))
+
+
+def curr_time():
+    return time.strftime('%H:%M')
+
+
+def copy(src, dest):
+    if not os.path.exists(dest):
+        os.makedirs(dest)
+    src_files = os.listdir(src)
+    for file_name in src_files:
+        full_file_name = os.path.join(src, file_name)
+        if os.path.isfile(full_file_name):
+            shutil.copy(full_file_name, dest)
+        else:
+            copy(full_file_name, os.path.join(dest, file_name))
+
+
+def convert_time(t):
+    sec = int(t) - time.timezone
+    m, s = divmod(sec, 60)
+    h, m = divmod(m, 60)
+    d, h = divmod(h, 24)
+    return '%02d:%02d' % (h, m)
+
+
+class Singleton:
+    _instance = None
+
+    def __init__(self):
+        self.__class__._instance = self
+
+    @classmethod
+    def get_instance(cls):
+        return cls._instance
diff --git a/toxygen/utils/__init__.py b/toxygen/utils/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/toxygen/utils/ui.py b/toxygen/utils/ui.py
deleted file mode 100644
index f7d20e8..0000000
--- a/toxygen/utils/ui.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-
-from  qtpy import QtWidgets
-
-import utils.util as util
-
-def tr(s):
-    return QtWidgets.QApplication.translate('Toxygen', s)
-
-
-def question(text, title=None):
-    reply = QtWidgets.QMessageBox.question(None, title or 'Toxygen', text,
-                                           QtWidgets.QMessageBox.Yes,
-                                           QtWidgets.QMessageBox.No)
-    return reply == QtWidgets.QMessageBox.Yes
-
-
-def message_box(text, title=None):
-    m_box = QtWidgets.QMessageBox()
-    m_box.setText(tr(text))
-    m_box.setWindowTitle(title or 'Toxygen')
-    m_box.exec_()
-
-
-def text_dialog(text, title='', default_value=''):
-    text, ok = QtWidgets.QInputDialog.getText(None, title, text, QtWidgets.QLineEdit.Normal, default_value)
-
-    return text, ok
-
-
-def directory_dialog(caption=''):
-    return QtWidgets.QFileDialog.getExistingDirectory(None, caption, util.curr_directory(),
-                                                      QtWidgets.QFileDialog.DontUseNativeDialog)
-
-
-def file_dialog(caption, file_filter=None):
-    return QtWidgets.QFileDialog.getOpenFileName(None, caption, util.curr_directory(), file_filter,
-                                                 options=QtWidgets.QFileDialog.DontUseNativeDialog)
-
-
-def save_file_dialog(caption, file_filter=None):
-    return QtWidgets.QFileDialog.getSaveFileName(None, caption, util.curr_directory(),
-                                                 filter=file_filter,
-                                                 options=QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog)
-
-
-def close_all_windows():
-    QtWidgets.QApplication.closeAllWindows()
-
-
-def copy_to_clipboard(text):
-    clipboard = QtWidgets.QApplication.clipboard()
-    clipboard.setText(text)
-
-
-# TODO: all dialogs
diff --git a/toxygen/utils/util.py b/toxygen/utils/util.py
deleted file mode 100644
index 70cc7ff..0000000
--- a/toxygen/utils/util.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
-import datetime
-import os
-import platform
-import re
-import shutil
-import sys
-import time
-
-
-def cached(func):
-    saved_result = None
-
-    def wrapped_func():
-        nonlocal saved_result
-        if saved_result is None:
-            saved_result = func()
-
-        return saved_result
-
-    return wrapped_func
-
-oFD=None
-def log(data=None):
-    global oFD
-    if not oFD:
-        if 'TMPDIR' in os.environ:
-            logdir = os.environ['TMPDIR']
-        else:
-            logdir = '/tmp'
-        try:
-            oFD = open(join_path(logdir, 'toxygen.log'), 'a')
-        except Exception as ex:
-            oFD = None
-            print(f"ERROR: opening toxygen.log:  {ex}")
-            return ''
-    if data is None: return oFD
-    try:
-        oFD.write(str(data) +'\n')
-    except Exception as ex:
-        print(f"ERROR: writing to toxygen.log:  {ex}")
-    return data
-
-def curr_directory(current_file=None):
-    return os.path.dirname(os.path.realpath(current_file or __file__))
-
-
-def get_base_directory(current_file=None):
-    return os.path.dirname(curr_directory(current_file or __file__))
-
-
-@cached
-def get_images_directory():
-    return get_app_directory('images')
-
-
-@cached
-def get_styles_directory():
-    return get_app_directory('styles')
-
-
-@cached
-def get_sounds_directory():
-    return get_app_directory('sounds')
-
-
-@cached
-def get_stickers_directory():
-    return get_app_directory('stickers')
-
-
-@cached
-def get_smileys_directory():
-    return get_app_directory('smileys')
-
-
-@cached
-def get_translations_directory():
-    return get_app_directory('translations')
-
-
-@cached
-def get_plugins_directory():
-    return get_app_directory('plugins')
-
-
-@cached
-def get_libs_directory():
-    return get_app_directory('libs')
-
-
-def get_app_directory(directory_name):
-    return os.path.join(get_base_directory(), directory_name)
-
-
-def get_profile_name_from_path(path):
-    return os.path.basename(path)[:-4]
-
-
-def get_views_path(view_name):
-    ui_folder = os.path.join(get_base_directory(), 'ui')
-    views_folder = os.path.join(ui_folder, 'views')
-
-    return os.path.join(views_folder, view_name + '.ui')
-
-
-def curr_time():
-    return time.strftime('%H:%M')
-
-
-def get_unix_time():
-    return int(time.time())
-
-
-def join_path(a, b):
-    return os.path.join(a, b)
-
-
-def file_exists(file_path):
-    return os.path.exists(file_path)
-
-
-def copy(src, dest):
-    if not os.path.exists(dest):
-        os.makedirs(dest)
-    src_files = os.listdir(src)
-    for file_name in src_files:
-        full_file_name = os.path.join(src, file_name)
-        if os.path.isfile(full_file_name):
-            shutil.copy(full_file_name, dest)
-        else:
-            copy(full_file_name, os.path.join(dest, file_name))
-
-
-def remove(folder):
-    if os.path.isdir(folder):
-        shutil.rmtree(folder)
-
-
-def convert_time(t):
-    offset = time.timezone + time_offset() * 60
-    sec = int(t) - offset
-    m, s = divmod(sec, 60)
-    h, m = divmod(m, 60)
-    d, h = divmod(h, 24)
-    return '%02d:%02d' % (h, m)
-
-
-@cached
-def time_offset():
-    hours = int(time.strftime('%H'))
-    minutes = int(time.strftime('%M'))
-    sec = int(time.time()) - time.timezone
-    m, s = divmod(sec, 60)
-    h, m = divmod(m, 60)
-    d, h = divmod(h, 24)
-    result = hours * 60 + minutes - h * 60 - m
-    return result
-
-def unix_time_to_long_str(unix_time):
-    date_time = datetime.datetime.utcfromtimestamp(unix_time)
-
-    return date_time.strftime('%Y-%m-%d %H:%M:%S')
-
-
-@cached
-def is_64_bit():
-    return sys.maxsize > 2 ** 32
-
-
-def is_re_valid(regex):
-    try:
-        re.compile(regex)
-    except re.error:
-        return False
-    else:
-        return True
-
-
-@cached
-def get_platform():
-    return platform.system()
-
-def get_user_config_path():
-    if get_platform() == 'Windows':
-        return os.getenv('APPDATA') + '/Tox/'
-    elif get_platform() == 'Darwin':
-        return os.getenv('HOME') + '/Library/Application Support/Tox/'
-    else:
-        return os.getenv('HOME') + '/.config/tox/'
diff --git a/toxygen/widgets.py b/toxygen/widgets.py
new file mode 100644
index 0000000..1e5cfe8
--- /dev/null
+++ b/toxygen/widgets.py
@@ -0,0 +1,133 @@
+try:
+    from PySide import QtCore, QtGui
+except ImportError:
+    from PyQt4 import QtCore, QtGui
+
+
+class DataLabel(QtGui.QLabel):
+    """
+    Label with elided text
+    """
+    def setText(self, text):
+        text = ''.join(c if c <= '\u10FFFF' else '\u25AF' for c in text)
+        metrics = QtGui.QFontMetrics(self.font())
+        text = metrics.elidedText(text, QtCore.Qt.ElideRight, self.width())
+        super().setText(text)
+
+
+class CenteredWidget(QtGui.QWidget):
+
+    def __init__(self):
+        super(CenteredWidget, self).__init__()
+        self.center()
+
+    def center(self):
+        qr = self.frameGeometry()
+        cp = QtGui.QDesktopWidget().availableGeometry().center()
+        qr.moveCenter(cp)
+        self.move(qr.topLeft())
+
+
+class LineEdit(QtGui.QLineEdit):
+
+    def __init__(self, parent=None):
+        super(LineEdit, self).__init__(parent)
+
+    def contextMenuEvent(self, event):
+        menu = create_menu(self.createStandardContextMenu())
+        menu.exec_(event.globalPos())
+        del menu
+
+
+class QRightClickButton(QtGui.QPushButton):
+    """
+    Button with right click support
+    """
+
+    def __init__(self, parent):
+        super(QRightClickButton, self).__init__(parent)
+
+    def mousePressEvent(self, event):
+        if event.button() == QtCore.Qt.RightButton:
+            self.emit(QtCore.SIGNAL("rightClicked()"))
+        else:
+            super(QRightClickButton, self).mousePressEvent(event)
+
+
+class RubberBand(QtGui.QRubberBand):
+
+    def __init__(self):
+        super(RubberBand, self).__init__(QtGui.QRubberBand.Rectangle, None)
+        self.setPalette(QtGui.QPalette(QtCore.Qt.transparent))
+        self.pen = QtGui.QPen(QtCore.Qt.blue, 4)
+        self.pen.setStyle(QtCore.Qt.SolidLine)
+        self.painter = QtGui.QPainter()
+
+    def paintEvent(self, event):
+
+        self.painter.begin(self)
+        self.painter.setPen(self.pen)
+        self.painter.drawRect(event.rect())
+        self.painter.end()
+
+
+def create_menu(menu):
+    """
+    :return translated menu
+    """
+    for action in menu.actions():
+        text = action.text()
+        if 'Link Location' in text:
+            text = text.replace('Copy &Link Location',
+                                QtGui.QApplication.translate("MainWindow", "Copy link location", None,
+                                                             QtGui.QApplication.UnicodeUTF8))
+        elif '&Copy' in text:
+            text = text.replace('&Copy', QtGui.QApplication.translate("MainWindow", "Copy", None,
+                                                                      QtGui.QApplication.UnicodeUTF8))
+        elif 'All' in text:
+            text = text.replace('Select All', QtGui.QApplication.translate("MainWindow", "Select all", None,
+                                                                           QtGui.QApplication.UnicodeUTF8))
+        elif 'Delete' in text:
+            text = text.replace('Delete', QtGui.QApplication.translate("MainWindow", "Delete", None,
+                                                                       QtGui.QApplication.UnicodeUTF8))
+        elif '&Paste' in text:
+            text = text.replace('&Paste', QtGui.QApplication.translate("MainWindow", "Paste", None,
+                                                                       QtGui.QApplication.UnicodeUTF8))
+        elif 'Cu&t' in text:
+            text = text.replace('Cu&t', QtGui.QApplication.translate("MainWindow", "Cut", None,
+                                                                     QtGui.QApplication.UnicodeUTF8))
+        elif '&Undo' in text:
+            text = text.replace('&Undo', QtGui.QApplication.translate("MainWindow", "Undo", None,
+                                                                      QtGui.QApplication.UnicodeUTF8))
+        elif '&Redo' in text:
+            text = text.replace('&Redo', QtGui.QApplication.translate("MainWindow", "Redo", None,
+                                                                      QtGui.QApplication.UnicodeUTF8))
+        else:
+            menu.removeAction(action)
+            continue
+        action.setText(text)
+    return menu
+
+
+class MultilineEdit(CenteredWidget):
+
+    def __init__(self, title, text, save):
+        super(MultilineEdit, self).__init__()
+        self.resize(350, 200)
+        self.setMinimumSize(QtCore.QSize(350, 200))
+        self.setMaximumSize(QtCore.QSize(350, 200))
+        self.setWindowTitle(title)
+        self.edit = QtGui.QTextEdit(self)
+        self.edit.setGeometry(QtCore.QRect(0, 0, 350, 150))
+        self.edit.setText(text)
+        self.button = QtGui.QPushButton(self)
+        self.button.setGeometry(QtCore.QRect(0, 150, 350, 50))
+        self.button.setText(QtGui.QApplication.translate("MainWindow", "Save", None, QtGui.QApplication.UnicodeUTF8))
+        self.button.clicked.connect(self.button_click)
+        self.center()
+        self.save = save
+
+    def button_click(self):
+        self.save(self.edit.toPlainText())
+        self.close()
+