1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Merge branch 'master' into search

This commit is contained in:
TriKriSta 2018-08-14 01:08:54 +03:00 committed by GitHub
commit 01281e87cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 3037 additions and 1626 deletions

View File

@ -103,16 +103,22 @@ matrix:
cache:
directories:
- /opt/build-windows/x86_64
- stage: "macOS and AppImage"
- stage: "macOS, AppImage and Flatpak"
os: osx
osx_image: xcode7.3
env: JOB=build-osx
- stage: "macOS and AppImage"
- stage: "macOS, AppImage and Flatpak"
os: linux
env: JOB=APPIMAGE
script: ./appimage/build-appimage.sh
services:
- docker
- stage: "macOS, AppImage and Flatpak"
os: linux
env: JOB=FLATPAK
script: ./flatpak/build-flatpak.sh
services:
- docker
script: "./.travis/$JOB.sh"
@ -121,13 +127,26 @@ deploy:
- provider: releases
api_key:
secure: "BRbzTWRvadALRQSTihMKruOj64ydxusMUS9FQR//qFlS345ZYfYta43W//4LcWWDKtj6IvA6DRqNdabgWnpbpxpnm9gVftGUdOKlU3niPZhwsMkB2M12QHUnAP6DVOfGPvdciBV+6mu73SSxniEcrYjZ1CrRX7mknmehPpVKxNk="
file: ./output/qTox-"$TRAVIS_TAG".AppImage
file_glob: true
file: ./output/*
on:
condition: $JOB == APPIMAGE
repo: qTox/qTox
tags: true
skip_cleanup: true
# Linux Flatpak
- provider: releases
api_key:
secure: "BRbzTWRvadALRQSTihMKruOj64ydxusMUS9FQR//qFlS345ZYfYta43W//4LcWWDKtj6IvA6DRqNdabgWnpbpxpnm9gVftGUdOKlU3niPZhwsMkB2M12QHUnAP6DVOfGPvdciBV+6mu73SSxniEcrYjZ1CrRX7mknmehPpVKxNk="
file_glob: true
file: ./output/*
on:
condition: $JOB == FLATPAK
repo: qTox/qTox
tags: true
skip_cleanup: true
# osx binary
- provider: releases
api_key:

View File

@ -35,6 +35,7 @@ sudo apt-get install -y --force-yes \
libgdk-pixbuf2.0-dev \
libglib2.0-dev \
libgtk2.0-dev \
libkdeui5 \
libopenal-dev \
libopus-dev \
libqrencode-dev \
@ -170,7 +171,8 @@ build_qtox() {
cmake -H. -B"$BUILDDIR" \
-DSMILEYS=DISABLED \
-DENABLE_STATUSNOTIFIER=False \
-DENABLE_GTK_SYSTRAY=False
-DENABLE_GTK_SYSTRAY=False \
-DSPELL_CHECK=OFF
bdir

View File

@ -1,3 +1,99 @@
<a name=""></a>
## v1.16.3 (2018-07-18)
This point release fixes flatpak build. No feature changes.
<a name=""></a>
## v1.16.2 (2018-07-15)
This point release fixes dialog spam from receiving invalid filenames and logs
spam. No feature changes.
#### Bug Fixes
* **logging:** only log toxcore messages above TRACE level ([4dc74201](https://github.com/qTox/qTox/commit/4dc7420162e69095942b392048c309e6246d6b21))
* **ui:** don't emit filename change windows for every chat ([c1701345](https://github.com/qTox/qTox/commit/c1701345455ad5b253beeaa3d487daa01b8b1b21))
<a name=""></a>
## v1.16.1 (2018-07-04)
This point release fixes our deployment of Flapak and AppImage on Github. No
feature changes.
#### Features
* **deploy:** upload Flatpak bundle to Github releases ([59b5578c](https://github.com/qTox/qTox/commit/59b5578c7bffc56f6227c60bfcb38f97d39ec8d9))
#### Bug Fixes
* **deploy:** fix file path in AppImage deployment ([64602f38](https://github.com/qTox/qTox/commit/64602f38f154a3f3d2429146ae5d370b2202d1b8))
<a name=""></a>
## v1.16.0 (2018-07-02)
The most notable additions in this release are a new fullscreen mode for
video calls, a new call end sound and support for more camera resolutions. To
distribute qTox in a more user friendly manner we now publish Flatpak and
AppImage packages.
#### Bug Fixes
* remove full screen btn from audio group chat ([0d3f061b](https://github.com/qTox/qTox/commit/0d3f061ba80d9f3f8a971d2b8e11a7d9b59d180a))
* local toxcore install with bootstrap.sh ([9ca38750](https://github.com/qTox/qTox/commit/9ca3875079adf175f31f568e45aabc37e3409000), closes [#5199](https://github.com/qTox/qTox/issues/5199))
* simple_make.sh script ([ead2152d](https://github.com/qTox/qTox/commit/ead2152d6f0d15f7e662975fb3ed8525109794c3))
* Fix PR #5182. Eliminating the 'new' operator at ToxOptionsWrapper ([9b6cd1c0](https://github.com/qTox/qTox/commit/9b6cd1c0227006308d4fe556f2b721865c2d9b21))
* Fix usage of unitialized functions ([06ae7ead](https://github.com/qTox/qTox/commit/06ae7ead0c7c23935c1c05c75d9cb11ed516224b))
* two crashes, uncovered by the persistent groupchat patch ([48179b6a](https://github.com/qTox/qTox/commit/48179b6a19807383e298661a21f97db3b140eb44))
* delete double initialization callDuration ([dc1f5ea0](https://github.com/qTox/qTox/commit/dc1f5ea0a319bf4cbf05989c414ccaea898b4826))
* **Core:** fix use after free of proxyAddrData ([26b59d31](https://github.com/qTox/qTox/commit/26b59d312375ad6391228308aabe45f0a85a1194))
* **appimage:** build sqlcipher form source ([64a7c24b](https://github.com/qTox/qTox/commit/64a7c24b2b5ad11a6df5dbb11da6e3aa7c0fd6f3))
* **audio:**
* fix error introduced in 67f2605971cf43093c72f811e4df90ab70544dd6 ([40d30153](https://github.com/qTox/qTox/commit/40d30153aed223b65b596dc7d3bf17573b04f3e9))
* connect the correct audio callbacks ([a00af087](https://github.com/qTox/qTox/commit/a00af087778c6315ef55ed77c4209cbb63a6323d))
* close the audio device after playing a sound ([a3370173](https://github.com/qTox/qTox/commit/a3370173df24cd6880e3e3845ddbbc7c090b7aed))
* **build:**
* Elimination the build warnings (Wunused-variable, Wreorder) ([2cd65610](https://github.com/qTox/qTox/commit/2cd65610fcce0c3dcf8a5e9cb9f313a76167c09a))
* correct install script nsis for win64 ([25e69572](https://github.com/qTox/qTox/commit/25e69572f89d816cfab5a8c0d1c261bae34d3cdd))
* make qTox compile with ffmpeg 4.0 and newer ([44193176](https://github.com/qTox/qTox/commit/441931765ffe3de349b28a28bf10a006edcc9949))
* **chatform:**
* name in window title and close detached chats ([39968a31](https://github.com/qTox/qTox/commit/39968a313d78c727046837901e6cc3d6c31d18e0))
* check for empty path when exporting profile ([757791ee](https://github.com/qTox/qTox/commit/757791eea4be390bb6d1cdc908d1cd3c4b18728d), closes [#5146](https://github.com/qTox/qTox/issues/5146))
* **core:** Clean illegal chars from filenames ([ab85716f](https://github.com/qTox/qTox/commit/ab85716f00acfe00ff8035670919dd548d7f7f83))
* **docs:** update toxcore build instructions ([b00cbc1d](https://github.com/qTox/qTox/commit/b00cbc1d6f3a7f8406e4a96e732c534068fde22c))
* **file:** don't clean the filenames of avatar transfer ([2a8ab03e](https://github.com/qTox/qTox/commit/2a8ab03e46dd08efc4051a01bea56fe6a4c38a11))
* **history:** don't save both action prefix and displayed name ([dfd2de83](https://github.com/qTox/qTox/commit/dfd2de836eae605e02a1afb270620dd9274f6385))
* **leak:** Fix few memory leaks ([daaa5518](https://github.com/qTox/qTox/commit/daaa5518dd7c02c2de45690daa3f592206fc4023))
* **login:** start login screen on profile select by -p option ([1af3ad69](https://github.com/qTox/qTox/commit/1af3ad69e884bc4e74a4fcdd452a6aff10bffd62))
* **settings:**
* automatically disable UDP when a proxy is set ([977b7fc9](https://github.com/qTox/qTox/commit/977b7fc9a02b2b44164ffb77ab35f4cdfae90542))
* prevent segfault on wrong proxy settings ([dfd5232e](https://github.com/qTox/qTox/commit/dfd5232e2fb727685a20804d7ca3b932ea239332))
* **simple_make:** correct variable initialization ([1537f83e](https://github.com/qTox/qTox/commit/1537f83e85ff28dd73fb66161ae2cd5eeef692d1))
* **theme:** clear stylesheet cache on theme colour change ([8ba8ce91](https://github.com/qTox/qTox/commit/8ba8ce91f3317794b72fb4937c459dac2856d367))
* **ui:** increase number of low res camera options ([72931514](https://github.com/qTox/qTox/commit/72931514695a8691593d6a5abd2df1e340f95002))
* **video:** unsubscribe the video device correctly ([e55f86c6](https://github.com/qTox/qTox/commit/e55f86c6a5b0344642fcb3d7a2550df6e899a6e5))
* **wayland:** Fix desktop file name in Qt properties ([c1caeb58](https://github.com/qTox/qTox/commit/c1caeb585a8845eaa72c7db79fb334262eafdb8f))
#### Features
* Add ability to remove dialog from content dialog with middle click ([aae567ed](https://github.com/qTox/qTox/commit/aae567ed8e299fc0cdd700e2e0020042ee1cba11))
* Add ability to quit group with middle click ([228c431c](https://github.com/qTox/qTox/commit/228c431c890a7e68d078b441311892c691643926))
* Add middle mouse clicked signal for GenericChatroom ([65fc1dc2](https://github.com/qTox/qTox/commit/65fc1dc266da29e0679f2b645c31bc428f0cf575))
* **appimage:** build appimage on TravisCI ([f7345e4d](https://github.com/qTox/qTox/commit/f7345e4db264a5681490b9094981a65cac68d317))
* **call:** add call end sound ([65896e45](https://github.com/qTox/qTox/commit/65896e45017f8f748bc5b9db10a4400d7fd418dc))
* **chat:**
* add UI option to mute group peers ([2fae2a30](https://github.com/qTox/qTox/commit/2fae2a30f76978ce722c5b24236384c8052ebfc4))
* full screen video chat ([d6df8883](https://github.com/qTox/qTox/commit/d6df8883e399b95a55c5a5870497c1dcd45a3917))
* **core:** put c-toxcore log messages in the qTox log ([4faab075](https://github.com/qTox/qTox/commit/4faab0750d3841beeb08c7d17e85044b5013aea8))
* **history:** load set number of messages from history ([ca32e77d](https://github.com/qTox/qTox/commit/ca32e77d7400e23a6a839f6a8d1f322bfe48bbf0))
<a name="v1.15.0"></a>
## v1.15.0 (2018-04-18)
@ -45,88 +141,7 @@
<a name=""></a>
## (2018-03-12)
#### Bug Fixes
* Not quit on close if this setting is enabled ([e73dc10c](https://github.com/qTox/qTox/commit/e73dc10c7fd23b887cc5e2d5d4021bc02c8555ec))
* add search symbol ' in history ([3e05279c](https://github.com/qTox/qTox/commit/3e05279c097b33b09cedcebae4150c839a23af35))
* Use real channels number ([e74cc37a](https://github.com/qTox/qTox/commit/e74cc37a2d02e9d4cbd016bac9dbb7697e8445e7))
* Allocate memory to input buffer ([900f2a1a](https://github.com/qTox/qTox/commit/900f2a1ad3b328359a0ae089e778b15280512a9d))
* Call doAudio on timer timeout ([2353a66f](https://github.com/qTox/qTox/commit/2353a66fded32174421c9663ced5cfe4ceabe00b))
* [un]subscribe output in avform ([8c05399e](https://github.com/qTox/qTox/commit/8c05399e418f2c0147ce2d9c7dd220a0cdc97765))
* Correct display the call confirm window (CallConfirmWidget) ([f4fe343e](https://github.com/qTox/qTox/commit/f4fe343eca3eaf84f9ce300b59be9e83a70c204e))
* elimination of warning '-Wreorder' ([0869d3d8](https://github.com/qTox/qTox/commit/0869d3d8fdc9e9de2f1df51c377ddba71a1ce523))
* Use epsilon to compare float ([91dabf11](https://github.com/qTox/qTox/commit/91dabf11d31807f499d6e949373bf22762e80f5b))
* **UI:** prevent deadlocks on logout and profile delete ([a49e3458](https://github.com/qTox/qTox/commit/a49e34589f40edfb3fc46d5700573f87d5dfe3d0))
* **build:**
* move Appdata file installation to /usr/share/metainfo ([5db0bdd3](https://github.com/qTox/qTox/commit/5db0bdd381f0f08c5685501702f2a2eb9d2f5674))
* add needed ffmpeg decoder to configuration ([8973a521](https://github.com/qTox/qTox/commit/8973a5216f49e65adc48d5fada8a574db598cced))
* Add missing dependency for openSUSE ([f7e089f7](https://github.com/qTox/qTox/commit/f7e089f7a71c41ff31d311fe7148e57b5c6fb60a))
* **chatform:** Broaden URL matching to include unicode ([e564b85e](https://github.com/qTox/qTox/commit/e564b85e3c485b283855bfdf00dfc0ec5427fad4))
* **chatlog:**
* Match multi-character emoticons again ([9643e48e](https://github.com/qTox/qTox/commit/9643e48ef1d68948d52feec4e1be28c3ad61c0da))
* parse multi-length emoji properly ([5df63f9c](https://github.com/qTox/qTox/commit/5df63f9c2e6d78f4799447b0a22cdb9fb70c3fea))
* **chatwidget:** fix send file button not working ([af1aebfd](https://github.com/qTox/qTox/commit/af1aebfd1a7409ea821be2a616067561b62751c0))
* **cmake:**
* fix platform extensions for windows ([7ad68e2f](https://github.com/qTox/qTox/commit/7ad68e2f43b458cd00ca27b9cfb20abf0b9ae46c))
* add missing dependency ([423f0956](https://github.com/qTox/qTox/commit/423f095622824a34d081fb69bddd83cddf83ca03))
* **core:**
* Adapt qtox to new conferences state change callback. ([1111949f](https://github.com/qTox/qTox/commit/1111949f450fb4fe63321386f7f452ee1663f07a))
* Use new callback API for bitrate set ([d2deec7c](https://github.com/qTox/qTox/commit/d2deec7c554b3df651fe789dfb7964748329eff4))
* Use new API for bitrate set ([2c8f03da](https://github.com/qTox/qTox/commit/2c8f03dada443e30d6189050c7cf6d42e01827c5))
* **cpu:** Reduce CPU usage by avatar render ([8db61f96](https://github.com/qTox/qTox/commit/8db61f96ec78ac53479dd8db36eb192f6a1ddbcd))
* **friendwidget:** Use queued connection to avoid removing 'this' ([9b4972e0](https://github.com/qTox/qTox/commit/9b4972e0459de2921370cda9de645eb64e37ecfc))
* **group:** Show correct count of user on first creation ([0a590336](https://github.com/qTox/qTox/commit/0a590336b1467405a903464085dcdfc4474f93e6))
* **install:** Fix gzip invalid usage ([266f63f6](https://github.com/qTox/qTox/commit/266f63f6dfb1869aa2339d48cdc9b52ece3597ce))
* **l10n:**
* Correction of the translation into Russian ([3fb42b75](https://github.com/qTox/qTox/commit/3fb42b75d75bf6c0240748ffff368b912b14a838))
* Correction of the translation into Russian ([9229fdd1](https://github.com/qTox/qTox/commit/9229fdd17e013a8bd60102648a200734890c2140))
* **smiley:** change license of classic smileys to CC BY-SA 4.0 ([da7c12e2](https://github.com/qTox/qTox/commit/da7c12e20cac1ac7340b4bb4ec89f782e2e4a159))
* **travis:**
* try working around Travis + gitstats issue ([4c980945](https://github.com/qTox/qTox/commit/4c98094551ff4a1e7377a206b72fedd470b8be96))
* switch back to older Ubuntu Image ([378daeaa](https://github.com/qTox/qTox/commit/378daeaad4c5992a7acd2b650ff081d213556e10))
* **video:**
* improve debug message ([ff2fc18b](https://github.com/qTox/qTox/commit/ff2fc18be164fcbc89bfd46d64f4b0096a97aee5))
* choose first available resolution in preview automatically ([81522dea](https://github.com/qTox/qTox/commit/81522deabdc3fb11fd8d3e1feb59274a96583121))
* use float framerates also for V4L2 ([a2927de2](https://github.com/qTox/qTox/commit/a2927de27d4776b52303e07c07ce89e8dadf86c5))
* allow not integer framerates ([db7ee65d](https://github.com/qTox/qTox/commit/db7ee65d0efbe23a45e385a148b20701e521a5c5))
* Fix square form of a video ([8de8c14a](https://github.com/qTox/qTox/commit/8de8c14a76908cf84a322a0bfd9e2c7ad2b4fa16))
* **widget:** Fix status pic alignment ([d9118cfc](https://github.com/qTox/qTox/commit/d9118cfc71e2b030914187df7fd9fb3d98378cf1))
* **windows:** %APPDATA -> %APPDATA% in template ([f53b8282](https://github.com/qTox/qTox/commit/f53b82825bf76be5a6793d18f2d102ed7b222313))
#### Features
* Add the cmake option USE_CCACHE ([aa9cff31](https://github.com/qTox/qTox/commit/aa9cff315d659a7ca2010fb4791893abc8c5abdb))
* update to the new c-toxcore 0.2.0 conferences api ([d3d81bbd](https://github.com/qTox/qTox/commit/d3d81bbdf3c198a7c1258c6ad6405c6ab61cedd4))
* add hot keys for search ([ffb51e8a](https://github.com/qTox/qTox/commit/ffb51e8a0ea7dc3fb01f1f7650edc80b779a9be2))
* optimise search in history ([18fa8a74](https://github.com/qTox/qTox/commit/18fa8a745bdafddc00ba2f577c36451f40edfd61))
* add search in text in group chats ([7718734c](https://github.com/qTox/qTox/commit/7718734c9ab9705c1a1274b2a447611c1a2e22b4))
* remove search button and add line in context menu ([8bb80c77](https://github.com/qTox/qTox/commit/8bb80c770c1d21d1bdfc03c3d0569fabe6535e8f))
* edit load history for search ([de9c9061](https://github.com/qTox/qTox/commit/de9c9061175c97a9ee203d18a39e73f77544d5e6))
* add text search ([b881d32d](https://github.com/qTox/qTox/commit/b881d32d1bddb7352b8d24e2442ef6277ff0d583))
* add form for search ([863c46c7](https://github.com/qTox/qTox/commit/863c46c73d1a2fc677f9142ba8d7a2e8dc659c2a))
* add a button to search ([47d9da98](https://github.com/qTox/qTox/commit/47d9da98cf6811a30d35a1204e5342a4f7f4bf94))
* Prefere new line as message break ([3b52402f](https://github.com/qTox/qTox/commit/3b52402fa20d2d5418e129e5f001b626401a9ae5))
* **UI:** new status icons for message notifications ([4288785d](https://github.com/qTox/qTox/commit/4288785d31e215bc379223577f7d4dd65664ed86))
* **avatar:** Add outline hightlight on mouse hover ([bb26485d](https://github.com/qTox/qTox/commit/bb26485db6fed706f4ebccaffe35740394210032))
* **groupchat:** mark blocked users with different color ([a729f2f8](https://github.com/qTox/qTox/commit/a729f2f8c00d29d2837b6e380f5af1b95c344bad))
* **l10n:**
* add Macedonian translation ([1a06f85d](https://github.com/qTox/qTox/commit/1a06f85d3ccc91ff6f759a38534483fa40aaaa29))
* add Macedonian translation using Weblate ([41420331](https://github.com/qTox/qTox/commit/414203310a30720e02e06719bfcafbb8bcff9018))
* update French translation from Weblate ([a7e90969](https://github.com/qTox/qTox/commit/a7e9096919d4c0b89f061e8b77741d517f574838))
* update Portuguese translation from Weblate ([3bad087b](https://github.com/qTox/qTox/commit/3bad087bbff2fbff4c4d543df1f96931784c93df))
* update Portuguese translation from Weblate ([8c3be522](https://github.com/qTox/qTox/commit/8c3be5225f484469aed43dde04f03bc588ca2c15))
#### Performance
* **widget:** don't save on setExpanded if categorywidget is unchanged Fix #4932 ([b9845e1d](https://github.com/qTox/qTox/commit/b9845e1d23eb23380f447692e3a813413e897c2d))
<a name=""></a>
## (2018-03-12)
## v1.14.0 (2018-03-12)
#### Bug Fixes

View File

@ -12,6 +12,7 @@ option(USE_FILTERAUDIO "Enable the echo canceling backend" ON)
# AUTOUPDATE is currently broken and thus disabled
option(AUTOUPDATE "Enable the auto updater" OFF)
option(USE_CCACHE "Use ccache when available" ON)
option(SPELL_CHECK "Enable spell cheching support" ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
@ -256,6 +257,10 @@ set(${PROJECT_NAME}_SOURCES
src/core/toxfile.h
src/core/toxid.cpp
src/core/toxid.h
src/core/toxlogger.cpp
src/core/toxlogger.h
src/core/toxoptions.cpp
src/core/toxoptions.h
src/core/toxpk.cpp
src/core/toxpk.h
src/core/toxstring.cpp
@ -269,6 +274,11 @@ set(${PROJECT_NAME}_SOURCES
src/model/about/aboutfriend.cpp
src/model/about/aboutfriend.h
src/model/about/iaboutfriend.h
src/model/chatroom/chatroom.h
src/model/chatroom/friendchatroom.cpp
src/model/chatroom/friendchatroom.h
src/model/chatroom/groupchatroom.cpp
src/model/chatroom/groupchatroom.h
src/model/contact.cpp
src/model/contact.h
src/model/friend.cpp

View File

@ -1,22 +1,22 @@
- [Filing an issue](#filing-an-issue)
- [Must read](#must-read)
- [Good to know](#good-to-know)
- [Must read](#must-read)
- [Good to know](#good-to-know)
- [How to start contributing](#how-to-start-contributing)
- [Before you start…](#before-you-start)
- [Must read](#must-read)
- [Pull request](#pull-request)
- [How to open a pull request](#how-to-open-a-pull-reqeust)
- [How to deal with large amounts of merge conflicts](#merge-conflicts)
- [Git Commit Guidelines](#commit)
- [Commit Message Format](#commit-message-format)
- [Header](#header)
- [Type](#type)
- [Scope](#scope)
- [Subject](#subject)
- [Body](#body)
- [Reviewing](#reviewing)
- [Before you start…](#before-you-start)
- [Must read](#must-read)
- [Pull request](#pull-request)
- [How to open a pull request](#how-to-open-a-pull-request)
- [How to deal with large amounts of merge conflicts](#merge-conflicts)
- [Git Commit Guidelines](#commit)
- [Commit Message Format](#commit-message-format)
- [Header](#header)
- [Type](#type)
- [Scope](#scope)
- [Subject](#subject)
- [Body](#body)
- [Reviewing](#reviewing)
- [Testing PRs](#testing-prs)
- [Git config](#git-config)
- [Git config](#git-config)
- [Coding guidelines](#coding-guidelines)
@ -122,6 +122,9 @@ git clone git@github.com:<YOUR_USER>/qTox.git
# Add the "upstream" remote to be able to fetch from the qTox upstream repository:
git remote add upstream https://github.com/qTox/qTox.git
# Fetch from the "upstream" repository
git fetch upstream
# Point the local "master" branch to the "upstream" repository
git branch master --set-upstream-to=upstream/master
```

View File

@ -632,7 +632,7 @@ cd ..
```bash
git clone https://github.com/toktok/c-toxcore.git toxcore
cd toxcore
git checkout v0.2.1
git checkout v0.2.3
autoreconf -if
./configure
make -j$(nproc)

View File

@ -5,6 +5,7 @@
- [Arch](#arch-easy)
- [Fedora](#fedora-easy)
- [Gentoo](#gentoo-easy)
- [openSUSE](#opensuse-easy)
- [Slackware](#slackware-easy)
- [FreeBSD](#freebsd-easy)
- [Install git](#install-git)
@ -22,13 +23,6 @@
- [Slackware](#slackware-other-deps)
- [Ubuntu >=15.04](#ubuntu-other-deps)
- [Ubuntu >=16.04](#ubuntu-other-1604-deps)
- [toxcore dependencies](#toxcore-dependencies)
- [Arch](#arch-toxcore)
- [Debian](#debian-toxcore)
- [Fedora](#fedora-toxcore)
- [openSUSE](#opensuse-toxcore)
- [Slackware](#slackware-toxcore)
- [Ubuntu >=15.04](#ubuntu-toxcore)
- [sqlcipher](#sqlcipher)
- [Compile toxcore](#compile-toxcore)
- [Compile qTox](#compile-qtox)
@ -69,6 +63,18 @@ dependencies are missing.
|---------|---------|
| [Check] | >= 0.9 |
### Spell checking support
| Name | Version |
|----------|---------|
| [sonnet] | >= 5.45 |
Use `-DSPELL_CHECK=OFF` to disable it.
**Note:** Specified version was tested and works well. You can try to use older
version, but in this case you may have some errors (including a complete lack
of spell check).
### Linux
#### Auto-away support
@ -155,6 +161,24 @@ To install:
emerge qtox
```
<a name="opensuse-easy" />
#### openSUSE
qTox is available in openSUSE Factory.
To install in openSUSE 15.0 or newer:
```bash
zypper in qtox
```
To install in openSUSE 42.3:
```bash
zypper ar -f https://download.opensuse.org/repositories/server:/messaging/openSUSE_Leap_42.3 server:messaging
zypper in qtox
```
<a name="slackware-easy" />
@ -263,7 +287,7 @@ corresponding parts.
#### Arch Linux
```bash
sudo pacman -S --needed base-devel qt5 openal libxss qrencode ffmpeg
sudo pacman -S --needed base-devel qt5 openal libxss qrencode ffmpeg opus libvpx libsodium
```
@ -275,18 +299,28 @@ sudo pacman -S --needed base-devel qt5 openal libxss qrencode ffmpeg
```bash
sudo apt-get install \
automake \
autotools-dev \
build-essential \
check \
checkinstall \
cmake \
ffmpeg \
libavcodec-dev \
libavdevice-dev \
libexif-dev \
libgdk-pixbuf2.0-dev \
libgtk2.0-dev \
libkdeui5 \
libopenal-dev \
libopus-dev \
libqrencode-dev \
libqt5opengl5-dev \
libqt5svg5-dev \
libsodium-dev \
libsqlcipher-dev \
libtool \
libvpx-dev \
libxss-dev \
pkg-config \
qrencode \
@ -310,20 +344,28 @@ have to compile it yourself, otherwise compiling qTox will fail.**
sudo dnf groupinstall "Development Tools" "C Development Tools and Libraries"
# (can also use sudo dnf install @"Development Tools")
sudo dnf install \
autoconf \
automake \
check \
check-devel \
ffmpeg-devel \
gtk2-devel \
kf5-sonnet \
libexif-devel \
libXScrnSaver-devel \
libsodium-devel \
libtool \
libvpx-devel \
libXScrnSaver-devel \
openal-soft-devel \
openssl-devel \
opus-devel \
qrencode-devel \
qt-creator \
qt-devel \
qt-doc \
qt5-linguist \
qt5-qtsvg \
qt5-qtsvg-devel \
qt-creator \
qt-devel \
qt-doc \
qtsingleapplication \
sqlcipher \
sqlcipher-devel
@ -338,25 +380,32 @@ sudo dnf install \
```bash
sudo zypper install \
libexif-devel \
libffmpeg-devel \
libopus-devel \
libQt5Concurrent-devel \
libqt5-linguist \
libQt5Network-devel \
libQt5OpenGL-devel \
libQt5Xml-devel \
libXScrnSaver-devel \
libffmpeg-devel \
libqt5-linguist \
libqt5-qtbase-common-devel \
libqt5-qtsvg-devel \
libQt5Xml-devel \
libsodium-devel \
libvpx-devel \
libXScrnSaver-devel \
openal-soft-devel \
patterns-openSUSE-devel_basis \
qrencode-devel \
sqlcipher-devel
sqlcipher-devel \
sonnet-devel
```
<a name="slackware-other-deps" />
#### Slackware
List of all the toxcore dependencies and their SlackBuilds can be found
here: http://slackbuilds.org/repository/14.2/network/toxcore/
List of all the qTox dependencies and their SlackBuilds can be found here:
http://slackbuilds.org/repository/14.2/network/qTox/
@ -367,7 +416,11 @@ http://slackbuilds.org/repository/14.2/network/qTox/
```bash
sudo apt-get install \
automake \
autotools-dev \
build-essential cmake \
check \
checkinstall \
libavcodec-ffmpeg-dev \
libavdevice-ffmpeg-dev \
libavfilter-ffmpeg-dev \
@ -376,13 +429,18 @@ sudo apt-get install \
libgdk-pixbuf2.0-dev \
libglib2.0-dev \
libgtk2.0-dev \
libkdeui5 \
libopenal-dev \
libopus-dev \
libqrencode-dev \
libqt5opengl5-dev \
libqt5svg5-dev \
libsodium-dev \
libsqlcipher-dev \
libswresample-ffmpeg-dev \
libswscale-ffmpeg-dev \
libtool \
libvpx-dev \
libxss-dev \
qrencode \
qt5-default \
@ -405,13 +463,17 @@ sudo apt-get install \
libgdk-pixbuf2.0-dev \
libglib2.0-dev \
libgtk2.0-dev \
libkdeui5 \
libopenal-dev \
libopus-dev \
libqrencode-dev \
libqt5opengl5-dev \
libqt5svg5-dev \
libsodium-dev \
libsqlcipher-dev \
libswresample-dev \
libswscale-dev \
libvpx-dev \
libxss-dev \
qrencode \
qt5-default \
@ -419,63 +481,6 @@ sudo apt-get install \
qttools5-dev
```
### toxcore dependencies
Install all of the toxcore dependencies.
<a name="arch-toxcore" />
#### Arch Linux
```bash
sudo pacman -S --needed opus libvpx libsodium
```
<a name="debian-toxcore" />
#### Debian
```bash
sudo apt-get install libtool autotools-dev automake checkinstall check \
libopus-dev libvpx-dev libsodium-dev libavdevice-dev
```
<a name="fedora-toxcore" />
#### Fedora
```bash
sudo dnf install libtool autoconf automake check check-devel libsodium-devel \
opus-devel libvpx-devel
```
<a name="opensuse-toxcore" />
#### openSUSE
```bash
sudo zypper install libsodium-devel libvpx-devel libopus-devel \
patterns-openSUSE-devel_basis
```
<a name="slackware-toxcore" />
#### Slackware
List of all the toxcore dependencies and their SlackBuilds can be found
here: http://slackbuilds.org/repository/14.2/network/toxcore/
<a name="ubuntu-toxcore" />
#### Ubuntu >=15.04
```bash
sudo apt-get install libtool autotools-dev automake checkinstall check \
libopus-dev libvpx-dev libsodium-dev
```
### sqlcipher
If you are not using an old version of Fedora, skip this section, and go
@ -494,12 +499,14 @@ cd ..
### Compile toxcore
Normally you don't want to do that, `bootstrap.sh` will do it for you.
Provided that you have all required dependencies installed, you can simply run:
```bash
git clone https://github.com/toktok/c-toxcore.git toxcore
cd toxcore
git checkout v0.2.2
git checkout v0.2.3
cmake .
make -j$(nproc)
sudo make install
@ -720,9 +727,9 @@ Download the MinGW installer for Windows from
[sourceforge.net](http://sourceforge.net/projects/mingw/files/Installer/). Make
sure to install MSYS (a set of Unix tools for Windows). The following steps
assume that MinGW is installed at `C:\MinGW`. If you decided to choose another
location, replace corresponding parts. Select `mingw-developer-toolkit`,
`mingw32-base`, `mingw32-gcc-g++`, `msys-base` and `mingw32-pthreads-w32`
packages using MinGW Installation Manager (`mingw-get.exe`). Check that the
location, replace corresponding parts. Select `mingw-developer-toolkit`,
`mingw32-base`, `mingw32-gcc-g++`, `msys-base` and `mingw32-pthreads-w32`
packages using MinGW Installation Manager (`mingw-get.exe`). Check that the
version of MinGW, corresponds to the version of the QT component!
#### Wget
@ -750,7 +757,7 @@ second box search for the `PATH` variable and press `Edit...`. The input box
separated with a semicolon. Extend the input box by adding
`;C:\MinGW\bin;C:\MinGW\msys\1.0\bin;C:\Program Files (x86)\CMake 2.8\bin;C:\Program Files (x86)\GnuWin32\bin`.
The very first semicolon must only be added if it is missing. CMake may be added
by installer automatically. Make sure that paths containing alternative `sh`,
by installer automatically. Make sure that paths containing alternative `sh`,
`bash` implementations such as `C:\Program Files\OpenSSH\bin` are at the end of
`PATH` or build may fail.
@ -815,3 +822,4 @@ Switches:
[sqlcipher]: https://www.zetetic.net/sqlcipher/
[toxcore]: https://github.com/TokTok/c-toxcore/
[filteraudio]: https://github.com/irungentoo/filter_audio
[sonnet]: https://github.com/KDE/sonnet

View File

@ -110,42 +110,16 @@ translations, on the other, it lessened problems that were happening with
To get translations into qTox:
1. Add Weblate: `git remote add weblate
https://hosted.weblate.org/git/tox/qtox/`
2. Fetch newest: `git fetch weblate`
3. Check what has been changed compared to master: `git log --no-merges
master..weblate/master`
4. Cherry-pick from the oldest commit.
- check if there are multiple commits from the same author for the same
translation. If there are, cherry-pick them accordingly:
```
git cherry-pick <commit1> <commit2>
```
5. If there were multiple commits, squash them into a single one, leaving only
a single `feat(l10n): …` line in the commit message.
6. Get rid of Weblate's formatting and amend the commit using
[`./tools/deweblate-translation-file.sh`], i.e.:
```
./tools/deweblate-translation-file.sh
```
7. For translations that haven't yet been cherry-picked repeat steps 4-6.
8. Once done with cherry-picking, update all translation files, so that Weblate
would get newest strings that changed in qTox:
```
./tools/update-translation-files.sh ALL
```
9. Once PR with translation gets merged, `Reset` Weblate to current `master`,
since without reset there would be a git conflict that would prevent Weblate
from getting new strings.
**It's a good idea to lock translations on Weblate while they're in merge
process, so that no translation effort would be lost when resetting Weblate.**
1. Go to `https://hosted.weblate.org/projects/tox/qtox/#repository` and lock
the repository for translations.
2. Make sure you have git setup to automatically gpg sign commits
3. In the root of the qTox repository execute the script
`tools/update-weblate.sh`
4. Check what has been changed compared to master: `git diff upstream/master`
5. Checkout a new branch with e.g. `git checkout -b update_weblate` and open
a Pull Request for it on Github.
6. After the Pull Request has been merged, unlock Weblate and `reset` it to
master
## Adding new translations
@ -215,6 +189,12 @@ Follow steps for adding translations from Weblate up to step 5. Next:
- Update download links on https://tox.chat to point to the new release.
- Write a short blog post for https://github.com/qTox/blog/ and advertise the post
on Tox IRC channels, popular Tox groups, reddit, or whatever other platforms.
- Open a PR to update the Flatpak manifest of our [Flathub repository] with the
changes from [`./flatpak/io.github.qtox.qTox.json`].
- Comment to the PR with `bot, build` to execute a test build
- After the build passed for qTox on all architectures on
[the Flathub build bot], merge the PR into the master branch of our
[Flathub repository].
# How to become a maintainer?
@ -236,3 +216,6 @@ helping for a while, ask to be added to the `qTox` organization on GitHub.
[`./tools/deweblate-translation-file.sh`]: /tools/deweblate-translation-file.sh
[`./tools/create-tarball.sh`]: /tools/create-tarball.sh
[`./tools/update-versions.sh`]: /tools/update-versions.sh
[Flathub repository]: https://github.com/flathub/io.github.qtox.qTox
[`./flatpak/io.github.qtox.qTox.json`]: flatpak/io.github.qtox.qTox.json
[the Flathub build bot]: https://flathub.org/builds/#/

View File

@ -45,9 +45,9 @@ while running on all major platforms.
Windows | Linux | OS X | FreeBSD
--------|-------|------|--------
**[64 bit release]**| **[Arch]**, **[Fedora]**, **[Gentoo]** | **[Latest release]** | **[Package & Port]**
[32 bit release]| | [Building instructions] |
[64 bit][64nightly], [32 bit][32nightly] nigthly | [Other] | |
**[64 bit release]**| **[Arch]**, **[Fedora]**, **[Gentoo]**, **[openSUSE]** | **[Latest release]** | **[Package & Port]**
[32 bit release]|**[AppImage]**, [Flatpak] | [Building instructions] |
[64 bit][64nightly], [32 bit][32nightly] nigthly | [From Source] | |
_**Bold** options are recommended._
@ -70,6 +70,25 @@ Some of them are:
* [Translating, it's easy]
* [Reviewing and testing pull requests] you don't need to be able to code to
do that :wink:
* Take a task from our Roadmap below
### Roadmap
Currently qTox is under a feature freeze to clean up our codebase and tools.
During this time we want to prepare qTox for upcoming new features of toxcore.
The next steps are:
* remove support for toxcore < 0.2.0
* move all toxcore abstractions into their own subproject
* write basic tests for this Core
* format the code base
* rework our TravisCI setup for faster PR checks
* rethink our Issue tracker
The current state is tracked in the [Code cleanup] project.
### Screenshots
@ -117,17 +136,17 @@ Active qTox maintainers:
```
7EB3 39FE 8817 47E7 01B7 D472 EBE3 6E66 A842 9B99 - Anthony Bilinski
3103 9166 FA90 2CA5 0D05 D608 5AF9 F2E2 9107 C727 Diadlo
C7A2 552D 0B25 0F98 3827 742C 1332 03A3 AC39 9151 initramfs
CA92 21C5 389B 7C50 AA5F 7793 52A5 0775 BE13 DF17 - noavarice
DA26 2CC9 3C0E 1E52 5AD2 1C85 9677 5D45 4B8E BF44 sudden6
141C 880E 8BA2 5B19 8D0F 850F 7C13 2143 C1A3 A7D4 tox-user
2880 C860 D95C 909D 3DA4 5C68 7E08 6DD6 6126 3264 tux3
BA78 83E2 2F9D 3594 5BA3 3760 5313 7C30 33F0 9008 zetok
```
Past qTox maintainers:
```
C7A2 552D 0B25 0F98 3827 742C 1332 03A3 AC39 9151 initramfs
BA78 83E2 2F9D 3594 5BA3 3760 5313 7C30 33F0 9008 zetok
F365 8D0A 04A5 76A4 1072 FC0D 296F 0B76 4741 106C agilob
1157 616B BD86 0C53 9926 F813 9591 A163 FF9B E04C antis81
1D29 8BC7 25B7 BE82 65BA EAB9 3DB8 E053 15C2 20AA Dubslow
@ -140,23 +159,26 @@ AED3 1134 9C23 A123 E5C4 AA4B 139C A045 3DA2 D773
```
[#qtox@freenode]: https://webchat.freenode.net/?channels=qtox
[64 bit release]: https://github.com/qTox/qTox/releases/download/v1.15.0/setup-qtox-x86_64-release.exe
[32 bit release]: https://github.com/qTox/qTox/releases/download/v1.15.0/setup-qtox-i686-release.exe
[64 bit release]: https://github.com/qTox/qTox/releases/download/v1.16.3/setup-qtox-x86_64-release.exe
[32 bit release]: https://github.com/qTox/qTox/releases/download/v1.16.3/setup-qtox-i686-release.exe
[32nightly]: https://build.tox.chat/view/qtox/job/qTox-cmake-nightly_build_windows_x86_release/lastSuccessfulBuild/artifact/qTox-cmake-nightly_build_windows_x86_release.zip
[64nightly]: https://build.tox.chat/view/qtox/job/qTox-cmake-nightly_build_windows_x86-64_release/lastSuccessfulBuild/artifact/qTox-cmake-nightly_build_windows_x86-64_release.zip
[Flatpak]: https://github.com/qTox/qTox/releases/download/v1.16.1/qTox-v1.16.1.x86_64.flatpak
[AppImage]: https://github.com/qTox/qTox/releases/download/v1.16.1/qTox-v1.16.1.x86_64.AppImage
[Arch]: /INSTALL.md#arch
[Building instructions]: /INSTALL.md#os-x
[Contributing]: /CONTRIBUTING.md#how-to-start-contributing
[easy issues]: https://github.com/qTox/qTox/labels/E-easy
[Latest release]: https://github.com/qTox/qTox/releases/download/v1.15.0/qTox.dmg
[Latest release]: https://github.com/qTox/qTox/releases/download/v1.16.3/qTox.dmg
[Fedora]: /INSTALL.md#fedora
[Gentoo]: /INSTALL.md#gentoo
[openSUSE]: /INSTALL.md#opensuse
[Install/Build]: /INSTALL.md
[IRC logs]: https://github.com/qTox/qtox-irc-logs
[issues that need help]: https://github.com/qTox/qTox/labels/help%20wanted
[Jenkins builds]: https://build.tox.chat/
[Mailing list]: https://lists.tox.chat
[Other]: /INSTALL.md#linux
[From Source]: /INSTALL.md#linux
[qTox-dev mailing list]: https://lists.tox.chat/listinfo/qtox-dev
[Package & Port]: /INSTALL.md#freebsd-easy
[Report bugs]: https://github.com/qTox/qTox/wiki/Writing-Useful-Bug-Reports
@ -169,3 +191,4 @@ AED3 1134 9C23 A123 E5C4 AA4B 139C A045 3DA2 D773
[Translating, it's easy]: /translations/README.md
[User Manual]: /doc/user_manual_en.md
[voice it in the issues that need it]: https://github.com/qTox/qTox/labels/I-feedback-wanted
[Code cleanup]: https://github.com/qTox/qTox/projects/3?fullscreen=true

View File

@ -22,6 +22,16 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# usage: ./appimage/build-appimage.sh [Debug]
#
# If [Debug] is set to "Debug" the container will run in interactive mode and
# stay open to poke around in the filesystem.
readonly DEBUG="$1"
# Fail out on error
set -exo pipefail
# This script should be run from the root of the repository
if [ ! -f ./appimage/build-appimage.sh ]; then
@ -35,14 +45,26 @@ fi
mkdir -p ./output
docker run --rm \
-v $PWD:/qtox \
-v $PWD/output:/output \
debian:stretch-slim \
/bin/bash -c "/qtox/appimage/build.sh"
if [ "$DEBUG" == "Debug" ]
then
echo "Execute: /qtox/appimage/build.sh to start the build script"
echo "Execute: exit to leave the container"
docker run --rm -it \
-v $PWD:/qtox \
-v $PWD/output:/output \
debian:stretch-slim \
/bin/bash
else
docker run --rm \
-v $PWD:/qtox \
-v $PWD/output:/output \
debian:stretch-slim \
/bin/bash -c "/qtox/appimage/build.sh"
fi
# use the version number in the name when building a tag on Travis CI
if [ -n "$TRAVIS_TAG" ]
then
mv ./output/*.AppImage ./output/qTox-"$TRAVIS_TAG".AppImage
mv ./output/*.AppImage ./output/qTox-"$TRAVIS_TAG".x86_64.AppImage
fi

View File

@ -31,8 +31,8 @@ readonly INSTALL_DIR=libs
readonly BASE_DIR="${SCRIPT_DIR}/${INSTALL_DIR}"
# versions of libs to checkout
readonly TOXCORE_VERSION="v0.2.2"
readonly SQLCIPHER_VERSION="v3.4.0"
readonly TOXCORE_VERSION="v0.2.3"
readonly SQLCIPHER_VERSION="v3.4.2"
# directory names of cloned repositories
readonly TOXCORE_DIR="libtoxcore-$TOXCORE_VERSION"
@ -93,32 +93,16 @@ install_toxcore() {
"${BASE_DIR}/${TOXCORE_DIR}"
pushd ${BASE_DIR}/${TOXCORE_DIR}
./autogen.sh
# configure
if [[ $SYSTEM_WIDE = "false" ]]
then
./configure --prefix=${BASE_DIR}
else
./configure
fi
# ensure A/V support is enabled
if ! grep -Fxq "BUILD_AV_TRUE=''" config.log
then
echo "A/V support of libtoxcore is disabled but required by qTox. Aborting."
echo "Maybe the dev-packages of libopus and libvpx are not installed?"
exit 1
fi
# compile
make -j $(nproc)
# install
# compile and install
if [[ $SYSTEM_WIDE = "false" ]]
then
cmake . -DCMAKE_INSTALL_PREFIX=${BASE_DIR}
make -j $(nproc)
make install
else
cmake .
make -j $(nproc)
sudo make install
sudo ldconfig
fi

View File

@ -112,6 +112,16 @@ search_dependency(LIBSWSCALE PACKAGE libswscale)
search_dependency(SQLCIPHER PACKAGE sqlcipher)
search_dependency(VPX PACKAGE vpx)
if(${SPELL_CHECK})
find_package(KF5Sonnet)
if(KF5Sonnet_FOUND)
add_definitions(-DSPELL_CHECKING)
add_dependency(KF5::SonnetUi)
else()
message(WARNING "Sonnet not found. Spell checking will be disabled.")
endif()
endif()
# Try to find cmake toxcore libraries
if(WIN32)
search_dependency(TOXCORE PACKAGE toxcore OPTIONAL STATIC_PACKAGE)

55
doc/TCS_state.md Normal file
View File

@ -0,0 +1,55 @@
# qTox TCS Support state
All section number refer to a section in the [TCS document].
|Symbol|Description|
|------|-----------|
|Y| qTox implements this point|
|N| qTox is missing this point, add issue number|
|U| Unknown if qTox supports this point|
|TCS Section | qTox State |
|------------|------------|
| 1.0.1 |U|
| 1.0.2 |U|
| 1.0.3 |U|
| 1.0.4 |U|
| 2.1.1 |U|
| 2.1.2 |U|
| 2.2.1 |U|
| 2.2.2 |U|
| 2.2.3 |U|
| 2.2.4 |U|
| 2.2.5 |U|
| 2.2.6 |U|
| 2.2.7 |U|
| 2.2.8 |U|
| 2.2.9 |U|
| 2.2.10 |U|
| 2.2.11 |U|
| 3.1.1 |U|
| 3.1.2 |U|
| 3.2.1 |U|
| 3.2.2 |U|
| 3.2.3 |U|
| 3.3.1 |U|
| 3.3.2 |U|
| 3.3.3 |U|
| 3.4.1 |U|
| 3.4.2 |U|
| 3.3.1 |U|
| 3.3.2 |U|
| 3.3.3 |U|
| 3.3.4 |U|
| 3.3.5 |U|
| 4.0.1 |U|
| 4.0.2 |U|
| 5.0.1 |U|
| 5.0.2 |U|
| 5.0.3 |U|
| 5.0.4 |U|
| 5.1.1 |U|
| 5.1.2 |U|
| 5.1.3 |U|
[TCS document]: https://tox.gitbooks.io/tox-client-standard/content/

46
docker/Dockerfile.debian Normal file
View File

@ -0,0 +1,46 @@
FROM debian:stretch
RUN apt-get update && \
apt-get -y --force-yes install \
automake \
autotools-dev \
build-essential \
check \
checkinstall \
cmake \
ffmpeg \
git \
libavcodec-dev \
libavdevice-dev \
libexif-dev \
libgdk-pixbuf2.0-dev \
libgtk2.0-dev \
libopenal-dev \
libopus-dev \
libqrencode-dev \
libqt5opengl5-dev \
libqt5svg5-dev \
libsodium-dev \
libsqlcipher-dev \
libtool \
libvpx-dev \
libxss-dev \
pkg-config \
qrencode \
qt5-default \
qttools5-dev \
qttools5-dev-tools \
yasm
RUN git clone https://github.com/toktok/c-toxcore.git /toxcore
WORKDIR /toxcore
RUN git checkout v0.2.2 && \
cmake . && \
cmake --build . && \
make install && \
echo '/usr/local/lib/' >> /etc/ld.so.conf.d/locallib.conf && \
ldconfig
COPY . /qtox
WORKDIR /qtox
RUN cmake . && cmake --build .

43
docker/Dockerfile.ubuntu Normal file
View File

@ -0,0 +1,43 @@
FROM ubuntu:16.04
RUN apt-get update && \
apt-get -y --force-yes install \
build-essential \
cmake \
git \
libavcodec-dev \
libavdevice-dev \
libavfilter-dev \
libavutil-dev \
libexif-dev \
libgdk-pixbuf2.0-dev \
libglib2.0-dev \
libgtk2.0-dev \
libopenal-dev \
libopus-dev \
libqrencode-dev \
libqt5opengl5-dev \
libqt5svg5-dev \
libsodium-dev \
libsqlcipher-dev \
libswresample-dev \
libswscale-dev \
libvpx-dev \
libxss-dev \
qrencode \
qt5-default \
qttools5-dev-tools \
qttools5-dev
RUN git clone https://github.com/toktok/c-toxcore.git /toxcore
WORKDIR /toxcore
RUN git checkout v0.2.2 && \
cmake . && \
cmake --build . && \
make install && \
echo '/usr/local/lib/' >> /etc/ld.so.conf.d/locallib.conf && \
ldconfig
COPY . /qtox
WORKDIR /qtox
RUN cmake . && cmake --build .

5
docker/build-debian.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
cd "$(dirname "$0")/.."
docker build . -f docker/Dockerfile.debian -t qtox
cd -

5
docker/build-ubuntu.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
cd "$(dirname "$0")/.."
docker build . -f docker/Dockerfile.ubuntu -t qtox
cd -

7
docker/start-qtox.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti --rm -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e DISPLAY=$DISPLAY -e XAUTHORITY=$XAUTH qtox ./qtox

52
flatpak/build-flatpak.sh Executable file
View File

@ -0,0 +1,52 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-3.0+
#
# Copyright © 2018 by The qTox Project Contributors
#
# This script should be run from the root of the repository
# usage: ./flatpak/build-flatpak.sh [Debug]
#
# If [Debug] is set to "Debug" the container will run in interactive mode and
# stay open to poke around in the filesystem.
readonly DEBUG="$1"
# Fail out on error
set -exo pipefail
if [ ! -f ./flatpak/build-flatpak.sh ]; then
echo ""
echo "You are attempting to run the build-flatpak.sh from a wrong directory."
echo "If you wish to run this script, you'll have to have"
echo "the repository root directory as the working directory."
echo ""
exit 1
fi
mkdir -p ./output
if [ "$DEBUG" == "Debug" ]
then
echo "Execute: /qtox/appimage/build.sh to start the build script"
echo "Execute: exit to leave the container"
docker run --rm --privileged -it \
-v $PWD:/qtox \
-v $PWD/output:/output \
debian:stretch-slim \
/bin/bash
else
docker run --rm --privileged \
-v $PWD:/qtox \
-v $PWD/output:/output \
debian:stretch-slim \
/bin/bash -c "/qtox/flatpak/build.sh"
fi
# use the version number in the name when building a tag on Travis CI
if [ -n "$TRAVIS_TAG" ]
then
mv ./output/*.flatpak ./output/qTox-"$TRAVIS_TAG".x86_64.flatpak
fi

56
flatpak/build.sh Executable file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-3.0+
#
# Copyright © 2018 by The qTox Project Contributors
# Fail out on error
set -exuo pipefail
# directory paths
readonly QTOX_SRC_DIR="/qtox"
readonly OUTPUT_DIR="/output"
readonly BUILD_DIR="/build"
readonly QTOX_BUILD_DIR="$BUILD_DIR"/qtox
readonly FP_BUILD_DIR="$BUILD_DIR"/flatpak
readonly APT_FLAGS="-y --no-install-recommends"
# flatpak manifest download location
readonly MANIFEST_FILE="flatpak/io.github.qtox.qTox.json"
# directory containing necessary patches
readonly PATCH_DIR="flatpak/patches"
# use multiple cores when building
export MAKEFLAGS="-j$(nproc)"
# add backports repo, needed for a recent enough flatpak
echo "deb http://ftp.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/stretch-backports.list
# Get packages
apt-get update
apt-get install $APT_FLAGS ca-certificates git elfutils wget xz-utils patch bzip2
# install recent flatpak packages
apt-get install $APT_FLAGS -t stretch-backports flatpak flatpak-builder
# create build directory
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
# copy qtox source
cp -r "$QTOX_SRC_DIR" "$QTOX_BUILD_DIR"
cd "$QTOX_BUILD_DIR"
# create flatpak build directory
mkdir -p "$FP_BUILD_DIR"
cd "$FP_BUILD_DIR"
# Add 'https://flathub.org' remote:
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
# Build the qTox flatpak
flatpak-builder --disable-rofiles-fuse --install-deps-from=flathub --force-clean --repo=tox-repo qTox-flatpak "$QTOX_BUILD_DIR"/flatpak/io.github.qtox.qTox.json
# Create a bundle for distribution
flatpak build-bundle tox-repo "$OUTPUT_DIR"/qtox.flatpak io.github.qtox.qTox
# Chmod since everything is root:root
chmod 755 -R "$OUTPUT_DIR"

View File

@ -0,0 +1,157 @@
{
"app-id": "io.github.qtox.qTox",
"runtime": "org.kde.Platform",
"sdk": "org.kde.Sdk",
"runtime-version": "5.11",
"command": "qtox",
"rename-icon": "qtox",
"finish-args": [
"--share=network",
"--socket=pulseaudio",
"--socket=wayland",
"--socket=x11",
"--share=ipc",
"--filesystem=xdg-desktop",
"--filesystem=xdg-documents",
"--filesystem=xdg-download",
"--filesystem=xdg-music",
"--filesystem=xdg-pictures",
"--filesystem=xdg-videos",
"--filesystem=/media",
"--device=all"
],
"build-options": {
"cflags": "-O3 -DSQLITE_HAS_CODEC",
"cxxflags": "-O3"
},
"cleanup": [
"/include",
"/lib/pkgconfig",
"/share/man"
],
"modules": [
{
"name": "libv4l2",
"config-opts":
[
"--disable-libdvbv5",
"--disable-v4l-utils",
"--disable-qv4l2"
],
"sources":
[
{
"type": "archive",
"url": "https://linuxtv.org/downloads/v4l-utils/v4l-utils-1.14.2.tar.bz2",
"sha256" : "e6b962c4b1253cf852c31da13fd6b5bb7cbe5aa9e182881aec55123bae680692"
}
]
},
{
"name": "ffmpeg",
"config-opts": [
"--disable-everything",
"--enable-gpl",
"--disable-debug",
"--enable-optimizations",
"--enable-shared",
"--disable-programs",
"--disable-protocols",
"--disable-doc",
"--disable-avfilter",
"--disable-avresample",
"--disable-filters",
"--disable-iconv",
"--disable-network",
"--disable-postproc",
"--enable-libv4l2",
"--enable-indev=v4l2",
"--enable-libxcb",
"--enable-indev=xcbgrab",
"--enable-demuxer=h264",
"--enable-demuxer=mjpeg",
"--enable-parser=h264",
"--enable-parser=mjpeg",
"--enable-decoder=h264",
"--enable-decoder=mjpeg",
"--enable-decoder=rawvideo"
],
"sources": [
{
"type": "archive",
"url": "https://ffmpeg.org/releases/ffmpeg-4.0.1.tar.bz2",
"sha256" : "7ee591b1e7fb66f055fa514fbd5d98e092ddb3dbe37d2e50ea5c16ab51c21670"
}
]
},
{
"name": "sqlcipher",
"rm-configure": true,
"config-opts": [
"--enable-tempstore=yes",
"--disable-tcl"
],
"sources": [
{
"type": "git",
"url": "https://github.com/sqlcipher/sqlcipher",
"tag": "v3.4.2",
"commit": "c6f709fca81c910ba133aaf6330c28e01ccfe5f8",
"disable-fsckobjects" : true
},
{
"type": "script",
"dest-filename": "autogen.sh",
"commands": [
"AUTOMAKE=\"automake --foreign\" autoreconf -vfi"
]
}
]
},
{
"name": "libsodium",
"sources": [
{
"type": "git",
"url": "https://github.com/jedisct1/libsodium",
"tag": "1.0.16",
"commit": "675149b9b8b66ff44152553fb3ebf9858128363d"
}
]
},
{
"name": "libqrencode",
"sources": [
{
"type": "git",
"url": "https://github.com/fukuchi/libqrencode",
"tag": "v4.0.2",
"commit": "59ee597f913fcfda7a010a6e106fbee2595f68e4"
}
]
},
{
"name": "c-toxcore",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "git",
"url": "https://github.com/toktok/c-toxcore",
"tag": "v0.2.3",
"commit": "ae7899cab8104fa3c3078a3e61ddfa58a826e39a"
}
]
},
{
"name": "qTox",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "dir",
"path": "/build/qtox/"
}
]
}
]
}

View File

@ -65,7 +65,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.15.0</string>
<string>1.16.3</string>
<key>CFBundleSignature</key>
<string>toxq</string>
<key>CFBundleURLTypes</key>
@ -84,7 +84,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.15.0</string>
<string>1.16.3</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>UTImportedTypeDeclarations</key>

View File

@ -32,7 +32,8 @@ then
MAIN_DIR="${TRAVIS_BUILD_DIR}"
QTOX_DIR="${MAIN_DIR}"
else
MAIN_DIR="/Users/${USER}"
# the directory which qTox is cloned in, wherever that is
MAIN_DIR="$(dirname $(readlink -f $0))/../.."
QTOX_DIR="${MAIN_DIR}/qTox${SUBGIT}"
fi
QT_DIR="/usr/local/Cellar/qt5" # Folder name of QT install
@ -70,11 +71,10 @@ build_toxcore() {
[[ $TRAVIS != true ]] \
&& sleep 3
autoreconf -if
mkdir _build && cd _build
fcho "Starting cmake ..."
#Make sure the correct version of libsodium is used
./configure --with-libsodium-headers="${LS_DIR_VER}/include/" --with-libsodium-libs="${LS_DIR_VER}/lib/" --prefix="${LIB_INSTALL_PREFIX}"
cmake -DBOOTSTRAP_DAEMON=OFF -DLIBSODIUM_CFLAGS="-I${LS_DIR_VER}/include/" -DLIBSODIUM_LDFLAGS="L${LS_DIR_VER}/lib/" -DCMAKE_INSTALL_PREFIX="${LIB_INSTALL_PREFIX}" ..
make clean &> /dev/null
fcho "Compiling toxcore."
make > /dev/null || exit 1
@ -112,7 +112,7 @@ install() {
if [[ $TRAVIS != true ]]
then
sleep 3
brew install git wget libtool autoconf automake pkgconfig
brew install git wget libtool cmake pkgconfig
fi
brew install check libvpx opus libsodium
@ -127,7 +127,7 @@ install() {
git pull
else
fcho "Cloning Toxcore git ... "
git clone --branch v0.2.2 --depth=1 https://github.com/toktok/c-toxcore "$TOXCORE_DIR"
git clone --branch v0.2.3 --depth=1 https://github.com/toktok/c-toxcore "$TOXCORE_DIR"
fi
# qTox
if [[ $TRAVIS = true ]]
@ -167,7 +167,11 @@ install() {
else
brew install cmake
fi
brew install ffmpeg libexif qrencode qt5 sqlcipher openal-soft
# needed for kf5-sonnet
brew tap kde-mac/kde
brew install ffmpeg libexif qrencode qt5 sqlcipher openal-soft kf5-sonnet
fcho "Cloning filter_audio ... "
git clone --branch v0.0.1 --depth=1 https://github.com/irungentoo/filter_audio "$FILTERAUIO_DIR"

View File

@ -1,11 +1,11 @@
#!/usr/bin/env bash
set -eu -o pipefail
# additional flags for apt-get, used for CI
readonly APT_FLAGS=$1
readonly WITHOUT_SQLCIPHER=$2
set -eu -o pipefail
apt_install() {
local apt_packages=(
automake
@ -35,7 +35,7 @@ apt_install() {
)
if [ "$WITHOUT_SQLCIPHER" != "True" ]; then
apt_packages+=libsqlcipher-dev
apt_packages+=("libsqlcipher-dev")
fi
sudo apt-get install $APT_FLAGS "${apt_packages[@]}"

View File

@ -318,16 +318,23 @@ void OpenAL2::doOutput()
}
ALdouble latency[2] = {0};
alGetSourcedvSOFT(alProxySource, AL_SEC_OFFSET_LATENCY_SOFT, latency);
if (echoCancelSupported) {
alGetSourcedvSOFT(alProxySource, AL_SEC_OFFSET_LATENCY_SOFT, latency);
}
checkAlError();
ALshort outBuf[AUDIO_FRAME_SAMPLE_COUNT_PER_CHANNEL] = {0};
alcMakeContextCurrent(alProxyContext);
alcRenderSamplesSOFT(alProxyDev, outBuf, AUDIO_FRAME_SAMPLE_COUNT_PER_CHANNEL);
checkAlcError(alProxyDev);
if (echoCancelSupported) {
alcMakeContextCurrent(alProxyContext);
alcRenderSamplesSOFT(alProxyDev, outBuf, AUDIO_FRAME_SAMPLE_COUNT_PER_CHANNEL);
checkAlcError(alProxyDev);
alcMakeContextCurrent(alOutContext);
}
alcMakeContextCurrent(alOutContext);
alBufferData(bufids[0], AL_FORMAT_MONO16, outBuf, AUDIO_FRAME_SAMPLE_COUNT_PER_CHANNEL * 2, AUDIO_SAMPLE_RATE);
alSourceQueueBuffers(alProxySource, 1, bufids);
// initialize echo canceler if supported

View File

@ -197,8 +197,14 @@ void ChatLog::mousePressEvent(QMouseEvent* ev)
clearSelection();
}
// Counts only single clicks and first click of doule click
clickCount++;
if (lastClickButton == ev->button()) {
// Counts only single clicks and first click of doule click
clickCount++;
}
else {
clickCount = 1; // restarting counter
lastClickButton = ev->button();
}
lastClickPos = ev->pos();
// Triggers on odd click counts
@ -477,8 +483,14 @@ void ChatLog::mouseDoubleClickEvent(QMouseEvent* ev)
emit selectionChanged();
}
// Counts the second click of double click
clickCount++;
if (lastClickButton == ev->button()) {
// Counts the second click of double click
clickCount++;
}
else {
clickCount = 1; // restarting counter
lastClickButton = ev->button();
}
lastClickPos = ev->pos();
// Triggers on even click counts

View File

@ -155,6 +155,7 @@ private:
AutoScrollDirection selectionScrollDir = NoDirection;
int clickCount = 0;
QPoint lastClickPos;
Qt::MouseButton lastClickButton;
// worker vars
int workerLastIndex = 0;

View File

@ -124,7 +124,7 @@ ChatMessage::Ptr ChatMessage::createChatInfoMessage(const QString& rawMessage,
msg->addColumn(new Image(QSize(18, 18), img),
ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Text("<b>" + text + "</b>", baseFont, false, ""),
msg->addColumn(new Text("<b>" + text + "</b>", baseFont, false, text),
ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left));
msg->addColumn(new Timestamp(date, Settings::getInstance().getTimestampFormat(), baseFont),
ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));

File diff suppressed because it is too large Load Diff

View File

@ -24,18 +24,21 @@
#include "toxfile.h"
#include "toxid.h"
#include "src/core/dhtserver.h"
#include <tox/tox.h>
#include <QMutex>
#include <QObject>
#include <QThread>
#include <QTimer>
#include <functional>
#include <memory>
class CoreAV;
class ICoreSettings;
class GroupInvite;
class Profile;
class QTimer;
enum class Status
{
@ -45,11 +48,24 @@ enum class Status
Offline
};
class Core;
using ToxCorePtr = std::unique_ptr<Core>;
class Core : public QObject
{
Q_OBJECT
public:
Core(QThread* coreThread, Profile& profile, const ICoreSettings* const settings);
enum class ToxCoreErrors
{
BAD_PROXY,
INVALID_SAVE,
FAILED_TO_START,
ERROR_ALLOC
};
static ToxCorePtr makeToxCore(const QByteArray& savedata, const ICoreSettings* const settings,
ToxCoreErrors* err = nullptr);
static Core* getInstance();
const CoreAV* getAv() const;
CoreAV* getAv();
@ -85,10 +101,7 @@ public:
void sendFile(uint32_t friendId, QString filename, QString filePath, long long filesize);
public slots:
void start(const QByteArray& savedata);
void reset();
void process();
void bootstrapDht();
void start();
QByteArray getToxSaveData();
@ -126,51 +139,23 @@ signals:
void disconnected();
void friendRequestReceived(const ToxPk& friendPk, const QString& message);
void friendMessageReceived(uint32_t friendId, const QString& message, bool isAction);
void friendAvatarChanged(const ToxPk& friendPk, const QPixmap& pic);
void friendAvatarData(const ToxPk& friendPk, const QByteArray& data);
void friendAvatarRemoved(const ToxPk& friendPk);
void friendAdded(uint32_t friendId, const ToxPk& friendPk);
void requestSent(const ToxPk& friendPk, const QString& message);
void friendStatusChanged(uint32_t friendId, Status status);
void friendStatusMessageChanged(uint32_t friendId, const QString& message);
void friendUsernameChanged(uint32_t friendId, const QString& username);
void friendTypingChanged(uint32_t friendId, bool isTyping);
void friendAvatarChanged(uint32_t friendId, const QPixmap& pic);
void friendAvatarRemoved(uint32_t friendId);
void friendRemoved(uint32_t friendId);
void friendLastSeenChanged(uint32_t friendId, const QDateTime& dateTime);
void emptyGroupCreated(int groupnumber);
void groupInviteReceived(const GroupInvite& inviteInfo);
void groupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
void groupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
void groupPeerlistChanged(int groupnumber);
void groupPeerNameChanged(int groupnumber, int peernumber, const QString& newName);
void groupTitleChanged(int groupnumber, const QString& author, const QString& title);
void groupPeerAudioPlaying(int groupnumber, int peernumber);
void failedToAddFriend(const ToxPk& friendPk, const QString& errorInfo = QString());
void usernameSet(const QString& username);
void statusMessageSet(const QString& message);
void statusSet(Status status);
void idSet(const ToxId& id);
void messageSentResult(uint32_t friendId, const QString& message, int messageId);
void groupSentFailed(int groupId);
void actionSentResult(uint32_t friendId, const QString& action, int success);
void receiptRecieved(int friedId, int receipt);
void failedToAddFriend(const ToxPk& friendPk, const QString& errorInfo = QString());
void failedToRemoveFriend(uint32_t friendId);
void failedToSetUsername(const QString& username);
void failedToSetStatusMessage(const QString& message);
void failedToSetStatus(Status status);
void failedToSetTyping(bool typing);
void failedToStart();
void badProxy();
void avReady();
void fileSendStarted(ToxFile file);
@ -184,11 +169,50 @@ signals:
void fileTransferInfo(ToxFile file);
void fileTransferRemotePausedUnpaused(ToxFile file, bool paused);
void fileTransferBrokenUnbroken(ToxFile file, bool broken);
void fileNameChanged();
void fileNameChanged(const ToxPk& friendPk);
void saveRequest();
/**
* @deprecated prefer signals using ToxPk
*/
void fileAvatarOfferReceived(uint32_t friendId, uint32_t fileId, const QByteArray& avatarHash);
void friendMessageReceived(uint32_t friendId, const QString& message, bool isAction);
void friendAdded(uint32_t friendId, const ToxPk& friendPk);
void friendStatusChanged(uint32_t friendId, Status status);
void friendStatusMessageChanged(uint32_t friendId, const QString& message);
void friendUsernameChanged(uint32_t friendId, const QString& username);
void friendTypingChanged(uint32_t friendId, bool isTyping);
void friendAvatarChangedDeprecated(uint32_t friendId, const QPixmap& pic);
void friendRemoved(uint32_t friendId);
void friendLastSeenChanged(uint32_t friendId, const QDateTime& dateTime);
void emptyGroupCreated(int groupnumber);
void groupInviteReceived(const GroupInvite& inviteInfo);
void groupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
void groupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
void groupPeerlistChanged(int groupnumber);
void groupPeerNameChanged(int groupnumber, int peernumber, const QString& newName);
void groupTitleChanged(int groupnumber, const QString& author, const QString& title);
void groupPeerAudioPlaying(int groupnumber, int peernumber);
void messageSentResult(uint32_t friendId, const QString& message, int messageId);
void groupSentFailed(int groupId);
void actionSentResult(uint32_t friendId, const QString& action, int success);
void receiptRecieved(int friedId, int receipt);
void failedToRemoveFriend(uint32_t friendId);
void fileSendFailed(uint32_t friendId, const QString& fname);
private:
Core(QThread* coreThread);
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage,
size_t cMessageSize, void* core);
static void onFriendMessage(Tox* tox, uint32_t friendId, TOX_MESSAGE_TYPE type,
@ -208,8 +232,8 @@ private:
const uint8_t* cMessage, size_t length, void* vCore);
#if TOX_VERSION_IS_API_COMPATIBLE(0, 2, 0)
static void onGroupPeerListChange(Tox*, uint32_t groupId, void* core);
static void onGroupPeerNameChange(Tox*, uint32_t groupId, uint32_t peerId,
const uint8_t* name, size_t length, void* core);
static void onGroupPeerNameChange(Tox*, uint32_t groupId, uint32_t peerId, const uint8_t* name,
size_t length, void* core);
#else
static void onGroupNamelistChange(Tox* tox, uint32_t groupId, uint32_t peerId,
TOX_CONFERENCE_STATE_CHANGE change, void* core);
@ -224,28 +248,41 @@ private:
bool checkConnection();
void checkEncryptedHistory();
void makeTox(QByteArray savedata);
void makeTox(QByteArray savedata, ICoreSettings* s);
void makeAv();
void loadFriends();
void bootstrapDht();
void checkLastOnline(uint32_t friendId);
void deadifyTox();
QString getFriendRequestErrorMessage(const ToxId& friendId, const QString& message) const;
static void registerCallbacks(Tox* tox);
private slots:
void killTimers(bool onlyStop);
void killTimers();
void process();
void onStarted();
private:
Tox* tox;
CoreAV* av;
QTimer* toxTimer;
Profile& profile;
QMutex messageSendMutex;
bool ready;
const ICoreSettings* const s;
struct ToxDeleter
{
void operator()(Tox* tox)
{
tox_kill(tox);
}
};
static QThread* coreThread;
using ToxPtr = std::unique_ptr<Tox, ToxDeleter>;
ToxPtr tox;
std::unique_ptr<CoreAV> av;
QTimer toxTimer;
// recursive, since we might call our own functions
// pointer so we can circumvent const functions
std::unique_ptr<QMutex> coreLoopLock = nullptr;
std::unique_ptr<QThread> coreThread = nullptr;
QList<DhtServer> bootstrapNodes{};
friend class Audio; ///< Audio can access our calls directly to reduce latency
friend class CoreFile; ///< CoreFile can access tox* and emit our signals

View File

@ -487,7 +487,7 @@ void CoreAV::groupCallCallback(void* tox, uint32_t group, uint32_t peer, const i
const Settings& s = Settings::getInstance();
// don't play the audio if it comes from a muted peer
if (s.getBlackList().contains(peerPk.toString())) {
return;
return;
}
CoreAV* cav = c->getAv();
@ -506,12 +506,11 @@ void CoreAV::groupCallCallback(void* tox, uint32_t group, uint32_t peer, const i
}
Audio& audio = Audio::getInstance();
if (!call.getPeers()[peer]) {
// FIXME: 0 is a valid sourceId, we shouldn't necessarily re-subscribe just because we have 0
audio.subscribeOutput(call.getPeers()[peer]);
if(!call.havePeer(peer)) {
call.addPeer(peer);
}
audio.playAudioBuffer(call.getPeers()[peer], data, samples, channels, sample_rate);
audio.playAudioBuffer(call.getAlSource(peer), data, samples, channels, sample_rate);
}
#else
void CoreAV::groupCallCallback(void* tox, int group, int peer, const int16_t* data,
@ -536,12 +535,11 @@ void CoreAV::groupCallCallback(void* tox, int group, int peer, const int16_t* da
}
Audio& audio = Audio::getInstance();
if (!call.getPeers()[peer]) {
// FIXME: 0 is a valid sourceId, we shouldn't necessarily re-subscribe just because we have 0
audio.subscribeOutput(call.getPeers()[peer]);
if(!call.havePeer(peer)) {
call.addPeer(peer);
}
audio.playAudioBuffer(call.getPeers()[peer], data, samples, channels, sample_rate);
audio.playAudioBuffer(call.getAlSource(peer), data, samples, channels, sample_rate);
}
#endif
@ -556,9 +554,7 @@ void CoreAV::invalidateGroupCallPeerSource(int group, int peer)
if (it == groupCalls.end()) {
return;
}
Audio& audio = Audio::getInstance();
audio.unsubscribeOutput(it->second.getPeers()[peer]);
it->second.getPeers()[peer] = 0;
it->second.removePeer(peer);
}
/**
@ -700,7 +696,7 @@ bool CoreAV::isGroupCallOutputMuted(const Group* g) const
*/
bool CoreAV::isGroupAvEnabled(int groupId) const
{
Tox* tox = Core::getInstance()->tox;
Tox* tox = Core::getInstance()->tox.get();
TOX_ERR_CONFERENCE_GET_TYPE error;
TOX_CONFERENCE_TYPE type = tox_conference_get_type(tox, groupId, &error);
switch (error) {
@ -752,7 +748,7 @@ bool CoreAV::isCallOutputMuted(const Friend* f) const
void CoreAV::invalidateCallSources()
{
for (auto& kv : groupCalls) {
kv.second.getPeers().clear();
kv.second.clearPeers();
}
for (auto& kv : calls) {
@ -801,7 +797,8 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
return;
}
auto it = self->calls.insert(std::pair<uint32_t, ToxFriendCall>(friendNum, ToxFriendCall{friendNum, video, *self}));
auto it = self->calls.insert(
std::pair<uint32_t, ToxFriendCall>(friendNum, ToxFriendCall{friendNum, video, *self}));
if (it.second == false) {
/// Hanging up from a callback is supposed to be UB,
/// but since currently the toxav callbacks are fired from the toxcore thread,
@ -923,8 +920,7 @@ void CoreAV::audioBitrateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t rat
Q_ARG(uint32_t, rate), Q_ARG(void*, vSelf));
}
qDebug() << "Recommended audio bitrate with" << friendNum << " is now " << rate
<< ", ignoring it";
qDebug() << "Recommended audio bitrate with" << friendNum << " is now " << rate << ", ignoring it";
}
void CoreAV::videoBitrateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t rate, void* vSelf)
@ -938,8 +934,7 @@ void CoreAV::videoBitrateCallback(ToxAV* toxav, uint32_t friendNum, uint32_t rat
Q_ARG(uint32_t, rate), Q_ARG(void*, vSelf));
}
qDebug() << "Recommended video bitrate with" << friendNum << " is now " << rate
<< ", ignoring it";
qDebug() << "Recommended video bitrate with" << friendNum << " is now " << rate << ", ignoring it";
}
void CoreAV::audioFrameCallback(ToxAV*, uint32_t friendNum, const int16_t* pcm, size_t sampleCount,

View File

@ -78,8 +78,9 @@ public:
void toggleMuteCallInput(const Friend* f);
void toggleMuteCallOutput(const Friend* f);
#if TOX_VERSION_IS_API_COMPATIBLE(0, 2, 0)
static void groupCallCallback(void* tox, uint32_t group, uint32_t peer, const int16_t* data, unsigned samples,
uint8_t channels, uint32_t sample_rate, void* core);
static void groupCallCallback(void* tox, uint32_t group, uint32_t peer, const int16_t* data,
unsigned samples, uint8_t channels, uint32_t sample_rate,
void* core);
#else
static void groupCallCallback(void* tox, int group, int peer, const int16_t* data, unsigned samples,
uint8_t channels, unsigned sample_rate, void* core);

View File

@ -18,8 +18,8 @@
*/
#include "core.h"
#include "corefile.h"
#include "core.h"
#include "toxfile.h"
#include "toxstring.h"
#include "src/persistence/profile.h"
@ -27,8 +27,8 @@
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QThread>
#include <QRegularExpression>
#include <QThread>
#include <memory>
/**
@ -71,7 +71,7 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d
QMutexLocker mlocker(&fileSendMutex);
if (data.isEmpty()) {
tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, 0, nullptr, nullptr, 0, nullptr);
tox_file_send(core->tox.get(), friendId, TOX_FILE_KIND_AVATAR, 0, nullptr, nullptr, 0, nullptr);
return;
}
@ -81,7 +81,7 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d
uint64_t filesize = data.size();
TOX_ERR_FILE_SEND error;
uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, filesize,
uint32_t fileNum = tox_file_send(core->tox.get(), friendId, TOX_FILE_KIND_AVATAR, filesize,
avatarHash, avatarHash, TOX_HASH_LENGTH, &error);
switch (error) {
@ -112,7 +112,8 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d
file.fileKind = TOX_FILE_KIND_AVATAR;
file.avatarData = data;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox, friendId, fileNum, (uint8_t*)file.resumeFileId.data(), nullptr);
tox_file_get_file_id(core->tox.get(), friendId, fileNum, (uint8_t*)file.resumeFileId.data(),
nullptr);
addFile(friendId, fileNum, file);
}
@ -122,8 +123,8 @@ void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString
QMutexLocker mlocker(&fileSendMutex);
QByteArray fileName = filename.toUtf8();
uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_DATA, filesize, nullptr,
(uint8_t*)fileName.data(), fileName.size(), nullptr);
uint32_t fileNum = tox_file_send(core->tox.get(), friendId, TOX_FILE_KIND_DATA, filesize,
nullptr, (uint8_t*)fileName.data(), fileName.size(), nullptr);
if (fileNum == std::numeric_limits<uint32_t>::max()) {
qWarning() << "sendFile: Can't create the Tox file sender";
emit core->fileSendFailed(friendId, filename);
@ -134,7 +135,8 @@ void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString
ToxFile file{fileNum, friendId, fileName, filePath, ToxFile::SENDING};
file.filesize = filesize;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox, friendId, fileNum, (uint8_t*)file.resumeFileId.data(), nullptr);
tox_file_get_file_id(core->tox.get(), friendId, fileNum, (uint8_t*)file.resumeFileId.data(),
nullptr);
if (!file.open(false)) {
qWarning() << QString("sendFile: Can't open file, error: %1").arg(file.file->errorString());
}
@ -154,11 +156,13 @@ void CoreFile::pauseResumeFileSend(Core* core, uint32_t friendId, uint32_t fileI
if (file->status == ToxFile::TRANSMITTING) {
file->status = ToxFile::PAUSED;
emit core->fileTransferPaused(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, nullptr);
tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE,
nullptr);
} else if (file->status == ToxFile::PAUSED) {
file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME,
nullptr);
} else {
qWarning() << "pauseResumeFileSend: File is stopped";
}
@ -174,11 +178,13 @@ void CoreFile::pauseResumeFileRecv(Core* core, uint32_t friendId, uint32_t fileI
if (file->status == ToxFile::TRANSMITTING) {
file->status = ToxFile::PAUSED;
emit core->fileTransferPaused(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, nullptr);
tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE,
nullptr);
} else if (file->status == ToxFile::PAUSED) {
file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME,
nullptr);
} else {
qWarning() << "pauseResumeFileRecv: File is stopped or broken";
}
@ -194,7 +200,7 @@ void CoreFile::cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId)
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
removeFile(friendId, fileId);
}
@ -207,7 +213,7 @@ void CoreFile::cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId)
}
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
removeFile(friendId, fileId);
}
@ -220,7 +226,7 @@ void CoreFile::rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fil
}
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
removeFile(friendId, fileId);
}
@ -238,7 +244,7 @@ void CoreFile::acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fil
}
file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
}
ToxFile* CoreFile::findFile(uint32_t friendId, uint32_t fileId)
@ -277,7 +283,7 @@ void CoreFile::removeFile(uint32_t friendId, uint32_t fileId)
QString CoreFile::getCleanFileName(QString filename)
{
QRegularExpression regex("[<>:\"/\\|?*]");
QRegularExpression regex{QStringLiteral(R"([<>:"/\\|?])")};
filename.replace(regex, "_");
return filename;
@ -289,60 +295,77 @@ void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, u
{
Core* core = static_cast<Core*>(vCore);
auto filename = ToxString(fname, fnameLen);
const auto cleanFileName = CoreFile::getCleanFileName(filename.getQString());
const ToxPk friendPk = core->getFriendPublicKey(friendId);
if (kind == TOX_FILE_KIND_AVATAR) {
const ToxPk friendPk = core->getFriendPublicKey(friendId);
if (!filesize) {
qDebug() << QString("Received empty avatar request %1:%2").arg(friendId).arg(fileId);
// Avatars of size 0 means explicitely no avatar
emit core->friendAvatarRemoved(friendId);
core->profile.removeAvatar(friendPk);
emit core->friendAvatarRemoved(core->getFriendPublicKey(friendId));
return;
} else {
static_assert(TOX_HASH_LENGTH <= TOX_FILE_ID_LENGTH,
"TOX_HASH_LENGTH > TOX_FILE_ID_LENGTH!");
uint8_t avatarHash[TOX_FILE_ID_LENGTH];
tox_file_get_file_id(core->tox, friendId, fileId, avatarHash, nullptr);
if (core->profile.getAvatarHash(friendPk)
== QByteArray((char*)avatarHash, TOX_HASH_LENGTH)) {
// If it's an avatar but we already have it cached, cancel
qDebug() << QString(
"Received avatar request %1:%2, reject, since we have it in cache.")
.arg(friendId)
.arg(fileId);
tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
return;
} else {
// It's an avatar and we don't have it, autoaccept the transfer
qDebug() << QString("Received avatar request %1:%2, accept, since we don't have it "
"in cache.")
.arg(friendId)
.arg(fileId);
tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_RESUME, nullptr);
}
tox_file_get_file_id(core->tox.get(), friendId, fileId, avatarHash, nullptr);
QByteArray avatarBytes{static_cast<const char*>(static_cast<const void*>(avatarHash)),
TOX_HASH_LENGTH};
emit core->fileAvatarOfferReceived(friendId, fileId, avatarBytes);
return;
}
} else {
const auto cleanFileName = CoreFile::getCleanFileName(filename.getQString());
if (cleanFileName != filename.getQString()) {
qDebug() << QStringLiteral("Cleaned filename");
filename = ToxString(cleanFileName);
emit core->fileNameChanged(friendPk);
} else {
qDebug() << QStringLiteral("filename already clean");
}
qDebug() << QString("Received file request %1:%2 kind %3").arg(friendId).arg(fileId).arg(kind);
}
if (cleanFileName != filename.getQString()) {
qDebug() << QStringLiteral("Cleaned filename from %1 to %2").arg(filename.getQString()).arg(cleanFileName);
filename = ToxString(cleanFileName);
emit core->fileNameChanged();
} else {
qDebug() << QStringLiteral("cleanFileName: filename already clean");
}
ToxFile file{fileId, friendId, filename.getBytes(), "", ToxFile::RECEIVING};
file.filesize = filesize;
file.fileKind = kind;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox, friendId, fileId, (uint8_t*)file.resumeFileId.data(), nullptr);
tox_file_get_file_id(core->tox.get(), friendId, fileId, (uint8_t*)file.resumeFileId.data(),
nullptr);
addFile(friendId, fileId, file);
if (kind != TOX_FILE_KIND_AVATAR)
emit core->fileReceiveRequested(file);
}
// TODO(sudden6): This whole method is a mess but needed to get stuff working for now
void CoreFile::handleAvatarOffer(uint32_t friendId, uint32_t fileId, bool accept)
{
// TODO(sudden6): evil evil evil
auto core = Core::getInstance();
if (!accept) {
// If it's an avatar but we already have it cached, cancel
qDebug() << QString("Received avatar request %1:%2, reject, since we have it in cache.")
.arg(friendId)
.arg(fileId);
tox_file_control(core->tox.get(), friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
return;
}
// It's an avatar and we don't have it, autoaccept the transfer
qDebug() << QString("Received avatar request %1:%2, accept, since we don't have it "
"in cache.")
.arg(friendId)
.arg(fileId);
tox_file_control(core->tox.get(), friendId, fileId, TOX_FILE_CONTROL_RESUME, nullptr);
ToxFile file{fileId, friendId, "<avatar>", "", ToxFile::RECEIVING};
file.filesize = 0;
file.fileKind = TOX_FILE_KIND_AVATAR;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox.get(), friendId, fileId, (uint8_t*)file.resumeFileId.data(),
nullptr);
addFile(friendId, fileId, file);
}
void CoreFile::onFileControlCallback(Tox*, uint32_t friendId, uint32_t fileId,
TOX_FILE_CONTROL control, void* core)
{
@ -447,9 +470,10 @@ void CoreFile::onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fil
pic.loadFromData(file->avatarData);
if (!pic.isNull()) {
qDebug() << "Got" << file->avatarData.size() << "bytes of avatar data from" << friendId;
core->profile.saveAvatar(file->avatarData,
core->getFriendPublicKey(friendId));
emit core->friendAvatarChanged(friendId, pic);
emit core->friendAvatarData(core->getFriendPublicKey(friendId), file->avatarData);
emit core->friendAvatarChanged(core->getFriendPublicKey(friendId), pic);
// TODO(sudden6): signal below is deprecated
emit core->friendAvatarChangedDeprecated(friendId, pic);
}
} else {
emit core->fileTransferFinished(*file);

View File

@ -39,6 +39,10 @@ class CoreFile
{
friend class Core;
public:
static void handleAvatarOffer(uint32_t friendId, uint32_t fileId, bool accept);
private:
CoreFile() = delete;

View File

@ -28,40 +28,10 @@
* @brief Keeps sources for users in group calls.
*/
ToxCall::ToxCall(uint32_t CallId, bool VideoEnabled, CoreAV& av)
ToxCall::ToxCall(bool VideoEnabled, CoreAV& av)
: av{&av}
, videoEnabled{VideoEnabled}
{
Audio& audio = Audio::getInstance();
audio.subscribeInput();
audio.subscribeOutput(alSource);
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
[&av, CallId](const int16_t* pcm, size_t samples, uint8_t chans,
uint32_t rate) {
av.sendCallAudio(CallId, pcm, samples, chans, rate);
});
if (!audioInConn) {
qDebug() << "Audio connection not working";
}
if (videoEnabled) {
videoSource = new CoreVideoSource();
CameraSource& source = CameraSource::getInstance();
if (source.isNone()) {
source.setupDefault();
}
source.subscribe();
videoInConn = QObject::connect(&source, &VideoSource::frameAvailable,
[&av, CallId](std::shared_ptr<VideoFrame> frame) {
av.sendCallVideo(CallId, frame);
});
if (!videoInConn) {
qDebug() << "Video connection not working";
}
}
}
/**
@ -73,7 +43,6 @@ ToxCall::ToxCall(ToxCall&& other) noexcept : active{other.active},
audioInConn{other.audioInConn},
muteMic{other.muteMic},
muteVol{other.muteVol},
alSource{other.alSource},
videoSource{other.videoSource},
videoInConn{other.videoInConn},
videoEnabled{other.videoEnabled},
@ -82,7 +51,6 @@ ToxCall::ToxCall(ToxCall&& other) noexcept : active{other.active},
Audio& audio = Audio::getInstance();
audio.subscribeInput();
other.audioInConn = QMetaObject::Connection();
other.alSource = 0;
other.videoInConn = QMetaObject::Connection();
other.videoEnabled = false; // we don't need to subscribe video because other won't unsubscribe
other.videoSource = nullptr;
@ -95,7 +63,6 @@ ToxCall::~ToxCall()
QObject::disconnect(audioInConn);
audio.unsubscribeInput();
audio.unsubscribeOutput(alSource);
if (videoEnabled) {
QObject::disconnect(videoInConn);
CameraSource::getInstance().unsubscribe();
@ -117,9 +84,6 @@ ToxCall& ToxCall::operator=(ToxCall&& other) noexcept
muteMic = other.muteMic;
muteVol = other.muteVol;
alSource = other.alSource;
other.alSource = 0;
Audio::getInstance().subscribeInput();
videoInConn = other.videoInConn;
@ -190,19 +154,73 @@ CoreVideoSource* ToxCall::getVideoSource() const
return videoSource;
}
quint32 ToxCall::getAlSource() const
quint32 ToxFriendCall::getAlSource() const
{
return alSource;
}
void ToxCall::setAlSource(const quint32& value)
void ToxFriendCall::setAlSource(const quint32& value)
{
alSource = value;
}
ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av)
: ToxCall(FriendNum, VideoEnabled, av)
: ToxCall(VideoEnabled, av)
{
// register audio
Audio& audio = Audio::getInstance();
audio.subscribeInput();
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
[&av, FriendNum](const int16_t* pcm, size_t samples, uint8_t chans,
uint32_t rate) {
av.sendCallAudio(FriendNum, pcm, samples, chans, rate);
});
if (!audioInConn) {
qDebug() << "Audio input connection not working";
}
audio.subscribeOutput(alSource);
// register video
if (videoEnabled) {
videoSource = new CoreVideoSource();
CameraSource& source = CameraSource::getInstance();
if (source.isNone()) {
source.setupDefault();
}
source.subscribe();
videoInConn = QObject::connect(&source, &VideoSource::frameAvailable,
[&av, FriendNum](std::shared_ptr<VideoFrame> frame) {
av.sendCallVideo(FriendNum, frame);
});
if (!videoInConn) {
qDebug() << "Video connection not working";
}
}
}
ToxFriendCall::ToxFriendCall(ToxFriendCall &&other) noexcept
: ToxCall(std::move(other))
, alSource{other.alSource}
{
other.alSource = 0;
}
ToxFriendCall& ToxFriendCall::operator=(ToxFriendCall &&other) noexcept
{
ToxCall::operator=(std::move(other));
alSource = other.alSource;
other.alSource = 0;
return *this;
}
ToxFriendCall::~ToxFriendCall()
{
auto& audio = Audio::getInstance();
audio.unsubscribeOutput(alSource);
}
void ToxFriendCall::startTimeout(uint32_t callId)
@ -239,11 +257,24 @@ void ToxFriendCall::setState(const TOXAV_FRIEND_CALL_STATE& value)
}
ToxGroupCall::ToxGroupCall(int GroupNum, CoreAV& av)
: ToxCall(static_cast<uint32_t>(GroupNum), false, av)
: ToxCall(false, av)
{
// register audio
Audio& audio = Audio::getInstance();
audio.subscribeInput();
audioInConn = QObject::connect(&Audio::getInstance(), &Audio::frameAvailable,
[&av, GroupNum](const int16_t* pcm, size_t samples, uint8_t chans,
uint32_t rate) {
av.sendGroupCallAudio(GroupNum, pcm, samples, chans, rate);
});
if (!audioInConn) {
qDebug() << "Audio input connection not working";
}
}
ToxGroupCall::ToxGroupCall(ToxGroupCall&& other) noexcept : ToxCall(std::move(other)), peers{other.peers}
ToxGroupCall::ToxGroupCall(ToxGroupCall&& other) noexcept
: ToxCall(std::move(other)), peers{other.peers}
{
// all peers were moved, this ensures audio output is unsubscribed only once
other.peers.clear();
@ -269,10 +300,48 @@ ToxGroupCall& ToxGroupCall::operator=(ToxGroupCall&& other) noexcept
void ToxGroupCall::removePeer(int peerId)
{
auto& audio = Audio::getInstance();
const auto& sourceId = peers.find(peerId);
if(sourceId == peers.constEnd()) {
qDebug() << "Peer:" << peerId << "does not have a source, can't remove";
return;
}
audio.unsubscribeOutput(sourceId.value());
peers.remove(peerId);
}
QMap<int, quint32>& ToxGroupCall::getPeers()
void ToxGroupCall::addPeer(int peerId)
{
return peers;
auto& audio = Audio::getInstance();
uint sourceId = 0;
audio.subscribeOutput(sourceId);
peers.insert(peerId, sourceId);
}
bool ToxGroupCall::havePeer(int peerId)
{
const auto& sourceId = peers.find(peerId);
return sourceId != peers.constEnd();
}
void ToxGroupCall::clearPeers()
{
Audio& audio = Audio::getInstance();
for (quint32 v : peers) {
audio.unsubscribeOutput(v);
}
peers.clear();
}
quint32 ToxGroupCall::getAlSource(int peer)
{
if(!havePeer(peer)) {
qWarning() << "Getting alSource for non-existant peer";
return 0;
}
return peers[peer];
}

View File

@ -18,7 +18,7 @@ class ToxCall
{
protected:
ToxCall() = delete;
ToxCall(uint32_t CallId, bool VideoEnabled, CoreAV& av);
ToxCall(bool VideoEnabled, CoreAV& av);
~ToxCall();
public:
@ -45,9 +45,6 @@ public:
CoreVideoSource* getVideoSource() const;
quint32 getAlSource() const;
void setAlSource(const quint32& value);
protected:
bool active{false};
CoreAV* av{nullptr};
@ -55,7 +52,6 @@ protected:
QMetaObject::Connection audioInConn;
bool muteMic{false};
bool muteVol{false};
quint32 alSource{0};
// video
CoreVideoSource* videoSource{nullptr};
QMetaObject::Connection videoInConn;
@ -68,8 +64,9 @@ class ToxFriendCall : public ToxCall
public:
ToxFriendCall() = delete;
ToxFriendCall(uint32_t friendId, bool VideoEnabled, CoreAV& av);
ToxFriendCall(ToxFriendCall&& other) noexcept = default;
ToxFriendCall& operator=(ToxFriendCall&& other) noexcept = default;
ToxFriendCall(ToxFriendCall&& other) noexcept;
ToxFriendCall& operator=(ToxFriendCall&& other) noexcept;
~ToxFriendCall();
void startTimeout(uint32_t callId);
void stopTimeout();
@ -77,12 +74,16 @@ public:
TOXAV_FRIEND_CALL_STATE getState() const;
void setState(const TOXAV_FRIEND_CALL_STATE& value);
quint32 getAlSource() const;
void setAlSource(const quint32& value);
protected:
std::unique_ptr<QTimer> timeoutTimer;
private:
TOXAV_FRIEND_CALL_STATE state{TOXAV_FRIEND_CALL_STATE_NONE};
static constexpr int CALL_TIMEOUT = 45000;
quint32 alSource{0};
};
class ToxGroupCall : public ToxCall
@ -96,8 +97,11 @@ public:
ToxGroupCall& operator=(ToxGroupCall&& other) noexcept;
void removePeer(int peerId);
void addPeer(int peerId);
bool havePeer(int peerId);
void clearPeers();
QMap<int, quint32>& getPeers();
quint32 getAlSource(int peer);
private:
QMap<int, quint32> peers;

60
src/core/toxlogger.cpp Normal file
View File

@ -0,0 +1,60 @@
#include "toxlogger.h"
#include <tox/tox.h>
#include <QDebug>
#include <QRegularExpression>
#include <QString>
#include <QStringBuilder>
/**
* @brief Map TOX_LOG_LEVEL to a string
* @param level log level
* @return Descriptive string for the log level
*/
QString getToxLogLevel(TOX_LOG_LEVEL level) {
switch (level) {
case TOX_LOG_LEVEL_TRACE:
return QLatin1Literal("TRACE");
case TOX_LOG_LEVEL_DEBUG:
return QLatin1Literal("DEBUG");
case TOX_LOG_LEVEL_INFO:
return QLatin1Literal("INFO ");
case TOX_LOG_LEVEL_WARNING:
return QLatin1Literal("WARN ");
case TOX_LOG_LEVEL_ERROR:
return QLatin1Literal("ERROR");
default:
// Invalid log level
return QLatin1Literal("INVAL");
}
}
/**
* @brief Log message handler for toxcore log messages
* @note See tox.h for the parameter definitions
*/
void ToxLogger::onLogMessage(Tox *tox, TOX_LOG_LEVEL level, const char *file, uint32_t line,
const char *func, const char *message, void *user_data)
{
// for privacy, make the path relative to the c-toxcore source directory
const QRegularExpression pathCleaner(QLatin1Literal{"[\\s|\\S]*c-toxcore."});
const QString cleanPath = QString{file}.remove(pathCleaner);
const QString logMsg = getToxLogLevel(level) % QLatin1Literal{":"} % cleanPath
% QLatin1Literal{":"} % func % QStringLiteral(":%1: ").arg(line)
% message;
switch (level) {
case TOX_LOG_LEVEL_TRACE:
return; // trace level generates too much noise to enable by default
case TOX_LOG_LEVEL_DEBUG:
case TOX_LOG_LEVEL_INFO:
qDebug() << logMsg;
break;
case TOX_LOG_LEVEL_WARNING:
case TOX_LOG_LEVEL_ERROR:
qWarning() << logMsg;
}
}

13
src/core/toxlogger.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef TOXLOGGER_H
#define TOXLOGGER_H
#include <tox/tox.h>
#include <cstdint>
namespace ToxLogger {
void onLogMessage(Tox *tox, TOX_LOG_LEVEL level, const char *file, uint32_t line,
const char *func, const char *message, void *user_data);
}
#endif // TOXLOGGER_H

133
src/core/toxoptions.cpp Normal file
View File

@ -0,0 +1,133 @@
#include "toxoptions.h"
#include "src/core/icoresettings.h"
#include "src/core/toxlogger.h"
#include <QByteArray>
#include <QDebug>
// TODO(sudden6): replace this constant with the function from toxcore 0.2.3
static const int MAX_PROXY_ADDRESS_LENGTH = 255;
/**
* @brief The ToxOptions class wraps the Tox_Options struct and the matching
* proxy address data. This is needed to ensure both have equal lifetime and
* are correctly deleted.
*/
ToxOptions::ToxOptions(Tox_Options* options, const QByteArray& proxyAddrData)
: options(options)
, proxyAddrData(proxyAddrData)
{}
ToxOptions::~ToxOptions()
{
tox_options_free(options);
}
ToxOptions::ToxOptions(ToxOptions&& from)
{
options = from.options;
proxyAddrData.swap(from.proxyAddrData);
from.options = nullptr;
from.proxyAddrData.clear();
}
const char* ToxOptions::getProxyAddrData() const
{
return proxyAddrData.constData();
}
ToxOptions::operator Tox_Options*()
{
return options;
}
/**
* @brief Initializes a ToxOptions instance
* @param savedata Previously saved Tox data
* @return ToxOptions instance initialized to create Tox instance
*/
std::unique_ptr<ToxOptions> ToxOptions::makeToxOptions(const QByteArray& savedata,
const ICoreSettings* s)
{
// IPv6 needed for LAN discovery, but can crash some weird routers. On by default, can be
// disabled in options.
const bool enableIPv6 = s->getEnableIPv6();
bool forceTCP = s->getForceTCP();
// LAN requiring UDP is a toxcore limitation, ideally wouldn't be related
const bool enableLanDiscovery = s->getEnableLanDiscovery() && !forceTCP;
ICoreSettings::ProxyType proxyType = s->getProxyType();
quint16 proxyPort = s->getProxyPort();
QString proxyAddr = s->getProxyAddr();
if (!enableLanDiscovery) {
qWarning() << "Core starting without LAN discovery. Peers can only be found through DHT.";
}
if (enableIPv6) {
qDebug() << "Core starting with IPv6 enabled";
} else if (enableLanDiscovery) {
qWarning() << "Core starting with IPv6 disabled. LAN discovery may not work properly.";
}
Tox_Options* tox_opts = tox_options_new(nullptr);
if (!tox_opts) {
return {};
}
auto toxOptions = std::unique_ptr<ToxOptions>(new ToxOptions(tox_opts, proxyAddr.toUtf8()));
// register log first, to get messages as early as possible
tox_options_set_log_callback(*toxOptions, ToxLogger::onLogMessage);
// savedata
tox_options_set_savedata_type(*toxOptions, !savedata.isNull() ? TOX_SAVEDATA_TYPE_TOX_SAVE
: TOX_SAVEDATA_TYPE_NONE);
tox_options_set_savedata_data(*toxOptions, reinterpret_cast<const uint8_t*>(savedata.data()),
savedata.size());
// No proxy by default
tox_options_set_proxy_type(*toxOptions, TOX_PROXY_TYPE_NONE);
tox_options_set_proxy_host(*toxOptions, nullptr);
tox_options_set_proxy_port(*toxOptions, 0);
if (proxyType != ICoreSettings::ProxyType::ptNone) {
if (proxyAddr.length() > MAX_PROXY_ADDRESS_LENGTH) {
qWarning() << "proxy address" << proxyAddr << "is too long";
} else if (!proxyAddr.isEmpty() && proxyPort > 0) {
qDebug() << "using proxy" << proxyAddr << ":" << proxyPort;
// protection against changings in TOX_PROXY_TYPE enum
if (proxyType == ICoreSettings::ProxyType::ptSOCKS5) {
tox_options_set_proxy_type(*toxOptions, TOX_PROXY_TYPE_SOCKS5);
} else if (proxyType == ICoreSettings::ProxyType::ptHTTP) {
tox_options_set_proxy_type(*toxOptions, TOX_PROXY_TYPE_HTTP);
}
tox_options_set_proxy_host(*toxOptions, toxOptions->getProxyAddrData());
tox_options_set_proxy_port(*toxOptions, proxyPort);
if (!forceTCP) {
qDebug() << "Proxy and UDP enabled, this is a security risk, forcing TCP only";
forceTCP = true;
}
}
}
// network options
tox_options_set_udp_enabled(*toxOptions, !forceTCP);
tox_options_set_ipv6_enabled(*toxOptions, enableIPv6);
tox_options_set_local_discovery_enabled(*toxOptions, enableLanDiscovery);
tox_options_set_start_port(*toxOptions, 0);
tox_options_set_end_port(*toxOptions, 0);
return toxOptions;
}
bool ToxOptions::getIPv6Enabled() const
{
return tox_options_get_ipv6_enabled(options);
}
void ToxOptions::setIPv6Enabled(bool enabled)
{
tox_options_set_ipv6_enabled(options, enabled);
}

31
src/core/toxoptions.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef TOXOPTIONS_H
#define TOXOPTIONS_H
#include <QByteArray>
#include <memory>
class ICoreSettings;
struct Tox_Options;
class ToxOptions
{
public:
~ToxOptions();
ToxOptions(ToxOptions&& from);
operator Tox_Options*();
const char* getProxyAddrData() const;
static std::unique_ptr<ToxOptions> makeToxOptions(const QByteArray& savedata,
const ICoreSettings* s);
bool getIPv6Enabled() const;
void setIPv6Enabled(bool enabled);
private:
ToxOptions(Tox_Options* options, const QByteArray& proxyAddrData);
private:
Tox_Options* options = nullptr;
QByteArray proxyAddrData;
};
#endif // TOXOPTIONS_H

View File

@ -140,7 +140,6 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext& ctxt, const QSt
int main(int argc, char* argv[])
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
@ -148,15 +147,16 @@ int main(int argc, char* argv[])
qInstallMessageHandler(logMessageHandler);
// initialize random number generator
qsrand(time(nullptr));
std::unique_ptr<QApplication> a(new QApplication(argc, argv));
#if defined(Q_OS_UNIX)
// PosixSignalNotifier is used only for terminating signals,
// so it's connected directly to quit() without any filtering.
QObject::connect(&PosixSignalNotifier::globalInstance(),
&PosixSignalNotifier::activated,
a.get(),
&QApplication::quit);
QObject::connect(&PosixSignalNotifier::globalInstance(), &PosixSignalNotifier::activated,
a.get(), &QApplication::quit);
PosixSignalNotifier::watchCommonTerminatingSignals();
#endif
@ -193,10 +193,14 @@ int main(int argc, char* argv[])
parser.addVersionOption();
parser.addPositionalArgument("uri", QObject::tr("Tox URI to parse"));
parser.addOption(
QCommandLineOption(QStringList() << "p" << "profile", QObject::tr("Starts new instance and loads specified profile."),
QCommandLineOption(QStringList() << "p"
<< "profile",
QObject::tr("Starts new instance and loads specified profile."),
QObject::tr("profile")));
parser.addOption(
QCommandLineOption(QStringList() << "l" << "login", QObject::tr("Starts new instance and opens the login screen.")));
QCommandLineOption(QStringList() << "l"
<< "login",
QObject::tr("Starts new instance and opens the login screen.")));
parser.process(*a);
uint32_t profileId = Settings::getInstance().getCurrentProfileId();
@ -279,7 +283,7 @@ int main(int argc, char* argv[])
profileName = parser.value("p");
if (!Profile::exists(profileName)) {
qWarning() << "-p profile" << profileName + ".tox"
<< "doesn't exist, opening login screen";
<< "doesn't exist, opening login screen";
doIpc = false;
autoLogin = false;
} else {
@ -315,8 +319,10 @@ int main(int argc, char* argv[])
// If someone else processed it, we're done here, no need to actually start qTox
if (ipc.waitUntilAccepted(event, 2)) {
if (eventType == "activate") {
qDebug() << "Another qTox instance is already running. If you want to start a second "
"instance, please open login screen (qtox -l) or start with a profile (qtox -p <profile name>).";
qDebug()
<< "Another qTox instance is already running. If you want to start a second "
"instance, please open login screen (qtox -l) or start with a profile (qtox "
"-p <profile name>).";
} else {
qDebug() << "Event" << eventType << "was handled by other client.";
}
@ -327,8 +333,7 @@ int main(int argc, char* argv[])
Profile* profile = nullptr;
// Autologin
if (autoLogin && Profile::exists(profileName) &&
!Profile::isEncrypted(profileName)) {
if (autoLogin && Profile::exists(profileName) && !Profile::isEncrypted(profileName)) {
profile = Profile::loadProfile(profileName);
} else {
LoginScreen loginScreen{profileName};

View File

@ -0,0 +1,31 @@
/*
Copyright © 2014-2018 by The qTox Project Contributors
This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
qTox 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 qTox. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MODEL_CHATROOM_H
#define MODEL_CHATROOM_H
#include "src/model/contact.h"
class Chatroom
{
public:
virtual Contact* getContact() = 0;
};
#endif /* MODEL_CHATROOM_H */

View File

@ -0,0 +1,143 @@
#include "src/grouplist.h"
#include "src/model/chatroom/friendchatroom.h"
#include "src/model/friend.h"
#include "src/model/group.h"
#include "src/persistence/settings.h"
#include "src/widget/contentdialog.h"
#include <QCollator>
namespace {
QString getShortName(const QString& name)
{
constexpr auto MAX_NAME_LENGTH = 30;
if (name.length() <= MAX_NAME_LENGTH) {
return name;
}
return name.left(MAX_NAME_LENGTH).trimmed() + "";
}
}
FriendChatroom::FriendChatroom(Friend* frnd)
: frnd{frnd}
{
}
Friend* FriendChatroom::getFriend()
{
return frnd;
}
Contact* FriendChatroom::getContact()
{
return frnd;
}
void FriendChatroom::setActive(bool _active)
{
if (active != _active) {
active = _active;
emit activeChanged(active);
}
}
bool FriendChatroom::canBeInvited() const
{
return frnd->getStatus() != Status::Offline;
}
int FriendChatroom::getCircleId() const
{
return Settings::getInstance().getFriendCircleID(frnd->getPublicKey());
}
QString FriendChatroom::getCircleName() const
{
const auto circleId = getCircleId();
return Settings::getInstance().getCircleName(circleId);
}
void FriendChatroom::inviteToNewGroup()
{
auto core = Core::getInstance();
const auto friendId = frnd->getId();
const auto groupId = core->createGroup();
core->groupInviteFriend(friendId, groupId);
}
QString FriendChatroom::getAutoAcceptDir() const
{
const auto pk = frnd->getPublicKey();
return Settings::getInstance().getAutoAcceptDir(pk);
}
void FriendChatroom::setAutoAcceptDir(const QString& dir)
{
const auto pk = frnd->getPublicKey();
Settings::getInstance().setAutoAcceptDir(pk, dir);
}
void FriendChatroom::disableAutoAccept()
{
setAutoAcceptDir(QString{});
}
bool FriendChatroom::autoAcceptEnabled() const
{
return getAutoAcceptDir().isEmpty();
}
void FriendChatroom::inviteFriend(const Group* group)
{
const auto friendId = frnd->getId();
const auto groupId = group->getId();
Core::getInstance()->groupInviteFriend(friendId, groupId);
}
QVector<GroupToDisplay> FriendChatroom::getGroups() const
{
QVector<GroupToDisplay> groups;
for (const auto group : GroupList::getAllGroups()) {
const auto name = getShortName(group->getName());
const GroupToDisplay groupToDisplay = { name, group };
groups.push_back(groupToDisplay);
}
return groups;
}
/**
* @brief Return sorted list of circles exclude current circle.
*/
QVector<CircleToDisplay> FriendChatroom::getOtherCircles() const
{
QVector<CircleToDisplay> circles;
const auto currentCircleId = getCircleId();
const auto& s = Settings::getInstance();
for (int i = 0; i < s.getCircleCount(); ++i) {
if (i == currentCircleId) {
continue;
}
const auto name = getShortName(s.getCircleName(i));
const CircleToDisplay circle = { name, i };
circles.push_back(circle);
}
std::sort(circles.begin(), circles.end(),
[](const CircleToDisplay& a, const CircleToDisplay& b) -> bool {
QCollator collator;
collator.setNumericMode(true);
return collator.compare(a.name, b.name) < 0;
});
return circles;
}
void FriendChatroom::resetEventFlags()
{
frnd->setEventFlag(false);
}

View File

@ -0,0 +1,84 @@
/*
Copyright © 2014-2017 by The qTox Project Contributors
This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
qTox 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 qTox. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FRIEND_CHATROOM_H
#define FRIEND_CHATROOM_H
#include "chatroom.h"
#include <QObject>
#include <QString>
#include <QVector>
class Friend;
class Group;
struct GroupToDisplay
{
QString name;
Group* group;
};
struct CircleToDisplay
{
QString name;
int circleId;
};
class FriendChatroom : public QObject, public Chatroom
{
Q_OBJECT
public:
FriendChatroom(Friend* frnd);
Contact* getContact() override;
public slots:
Friend* getFriend();
void setActive(bool active);
bool canBeInvited() const;
int getCircleId() const;
QString getCircleName() const;
void inviteToNewGroup();
void inviteFriend(const Group* group);
bool autoAcceptEnabled() const;
QString getAutoAcceptDir() const;
void disableAutoAccept();
void setAutoAcceptDir(const QString& dir);
QVector<GroupToDisplay> getGroups() const;
QVector<CircleToDisplay> getOtherCircles() const;
void resetEventFlags();
signals:
void activeChanged(bool activated);
private:
bool active{false};
Friend* frnd{nullptr};
};
#endif // FRIEND_H

View File

@ -0,0 +1,51 @@
#include "groupchatroom.h"
#include "src/core/core.h"
#include "src/core/toxpk.h"
#include "src/friendlist.h"
#include "src/model/friend.h"
#include "src/model/group.h"
#include "src/persistence/settings.h"
GroupChatroom::GroupChatroom(Group* group)
: group{group}
{
}
Contact* GroupChatroom::getContact()
{
return group;
}
Group* GroupChatroom::getGroup()
{
return group;
}
bool GroupChatroom::hasNewMessage() const
{
return group->getEventFlag();
}
void GroupChatroom::resetEventFlags()
{
group->setEventFlag(false);
group->setMentionedFlag(false);
}
bool GroupChatroom::friendExists(const ToxPk& pk)
{
return FriendList::findFriend(pk) != nullptr;
}
void GroupChatroom::inviteFriend(const ToxPk& pk)
{
const Friend* frnd = FriendList::findFriend(pk);
const auto friendId = frnd->getId();
const auto groupId = group->getId();
const auto canInvite = frnd->getStatus() != Status::Offline;
if (canInvite) {
Core::getInstance()->groupInviteFriend(friendId, groupId);
}
}

View File

@ -0,0 +1,49 @@
/*
Copyright © 2014-2018 by The qTox Project Contributors
This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
qTox 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 qTox. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GROUP_CHATROOM_H
#define GROUP_CHATROOM_H
#include "chatroom.h"
#include <QObject>
class Group;
class ToxPk;
class GroupChatroom : public QObject, public Chatroom
{
Q_OBJECT
public:
GroupChatroom(Group* group);
Contact* getContact() override;
Group* getGroup();
bool hasNewMessage() const;
void resetEventFlags();
bool friendExists(const ToxPk& pk);
void inviteFriend(const ToxPk& pk);
private:
Group* group{nullptr};
};
#endif /* GROUP_CHATROOM_H */

View File

@ -19,14 +19,14 @@
#include "profileinfo.h"
#include "src/core/core.h"
#include "src/nexus.h"
#include "src/persistence/profile.h"
#include "src/persistence/settings.h"
#include "src/nexus.h"
#include <QApplication>
#include <QBuffer>
#include <QClipboard>
#include <QFile>
#include <QBuffer>
/**
* @class ProfileInfo
@ -41,7 +41,7 @@
* @param profile Pointer to Profile.
* @note All pointers parameters shouldn't be null.
*/
ProfileInfo::ProfileInfo(Core* core, Profile *profile)
ProfileInfo::ProfileInfo(Core* core, Profile* profile)
: profile{profile}
, core{core}
{
@ -55,7 +55,7 @@ ProfileInfo::ProfileInfo(Core* core, Profile *profile)
* @param password New password.
* @return True on success, false otherwise.
*/
bool ProfileInfo::setPassword(const QString &password)
bool ProfileInfo::setPassword(const QString& password)
{
QString errorMsg = profile->setPassword(password);
return errorMsg.isEmpty();
@ -98,7 +98,7 @@ void ProfileInfo::copyId() const
* @brief Set self user name.
* @param name New name.
*/
void ProfileInfo::setUsername(const QString &name)
void ProfileInfo::setUsername(const QString& name)
{
core->setUsername(name);
}
@ -107,7 +107,7 @@ void ProfileInfo::setUsername(const QString &name)
* @brief Set self status message.
* @param status New status message.
*/
void ProfileInfo::setStatusMessage(const QString &status)
void ProfileInfo::setStatusMessage(const QString& status)
{
core->setStatusMessage(status);
}
@ -152,7 +152,7 @@ static QString sanitize(const QString& src)
* @param name New profile name.
* @return Result code of rename operation.
*/
IProfileInfo::RenameResult ProfileInfo::renameProfile(const QString &name)
IProfileInfo::RenameResult ProfileInfo::renameProfile(const QString& name)
{
QString cur = profile->getName();
if (name.isEmpty()) {
@ -191,7 +191,7 @@ static bool tryRemoveFile(const QString& filepath)
* @param path Path to save profile.
* @return Result code of save operation.
*/
IProfileInfo::SaveResult ProfileInfo::exportProfile(const QString &path) const
IProfileInfo::SaveResult ProfileInfo::exportProfile(const QString& path) const
{
QString current = profile->getName() + Core::TOX_EXT;
if (path.isEmpty()) {
@ -284,7 +284,7 @@ QByteArray picToPng(const QPixmap& pic)
* @param path Path to image, which should be the new avatar.
* @return Code of set avatar operation.
*/
IProfileInfo::SetAvatarResult ProfileInfo::setAvatar(const QString &path)
IProfileInfo::SetAvatarResult ProfileInfo::setAvatar(const QString& path)
{
if (path.isEmpty()) {
return SetAvatarResult::EmptyPath;
@ -327,7 +327,7 @@ IProfileInfo::SetAvatarResult ProfileInfo::setAvatar(const QString &path)
return SetAvatarResult::TooLarge;
}
profile->setAvatar(bytes, core->getSelfPublicKey());
profile->setAvatar(bytes);
return SetAvatarResult::OK;
}
@ -336,5 +336,5 @@ IProfileInfo::SetAvatarResult ProfileInfo::setAvatar(const QString &path)
*/
void ProfileInfo::removeAvatar()
{
profile->removeAvatar();
profile->removeSelfAvatar();
}

View File

@ -61,8 +61,7 @@ Nexus::Nexus(QObject* parent)
, profile{nullptr}
, widget{nullptr}
, running{true}
{
}
{}
Nexus::~Nexus()
{
@ -194,9 +193,9 @@ void Nexus::showMainGUI()
connect(core, &Core::connected, widget, &Widget::onConnected);
connect(core, &Core::disconnected, widget, &Widget::onDisconnected);
connect(core, &Core::failedToStart, widget, &Widget::onFailedToStartCore,
connect(profile, &Profile::failedToStart, widget, &Widget::onFailedToStartCore,
Qt::BlockingQueuedConnection);
connect(core, &Core::badProxy, widget, &Widget::onBadProxyCore, Qt::BlockingQueuedConnection);
connect(profile, &Profile::badProxy, widget, &Widget::onBadProxyCore, Qt::BlockingQueuedConnection);
connect(core, &Core::statusSet, widget, &Widget::onStatusSet);
connect(core, &Core::usernameSet, widget, &Widget::setUsername);
connect(core, &Core::statusMessageSet, widget, &Widget::setStatusMessage);
@ -209,7 +208,8 @@ void Nexus::showMainGUI()
connect(core, &Core::friendMessageReceived, widget, &Widget::onFriendMessageReceived);
connect(core, &Core::groupInviteReceived, widget, &Widget::onGroupInviteReceived);
connect(core, &Core::groupMessageReceived, widget, &Widget::onGroupMessageReceived);
connect(core, &Core::groupNamelistChanged, widget, &Widget::onGroupNamelistChangedOld); // TODO(sudden6): toxcore < 0.2.0, remove
connect(core, &Core::groupNamelistChanged, widget,
&Widget::onGroupNamelistChangedOld); // TODO(sudden6): toxcore < 0.2.0, remove
connect(core, &Core::groupPeerlistChanged, widget, &Widget::onGroupPeerlistChanged);
connect(core, &Core::groupPeerNameChanged, widget, &Widget::onGroupPeerNameChanged);
connect(core, &Core::groupTitleChanged, widget, &Widget::onGroupTitleChanged);
@ -224,6 +224,8 @@ void Nexus::showMainGUI()
connect(widget, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest);
profile->startCore();
GUI::setEnabled(true);
}
/**

View File

@ -32,6 +32,7 @@
#include "profilelocker.h"
#include "settings.h"
#include "src/core/core.h"
#include "src/core/corefile.h"
#include "src/net/avatarbroadcaster.h"
#include "src/nexus.h"
#include "src/widget/gui.h"
@ -51,6 +52,35 @@
QStringList Profile::profiles;
void Profile::initCore(const QByteArray& toxsave, ICoreSettings& s)
{
Core::ToxCoreErrors err;
core = Core::makeToxCore(toxsave, &s, &err);
if (!core) {
switch (err) {
case Core::ToxCoreErrors::BAD_PROXY:
emit badProxy();
break;
case Core::ToxCoreErrors::ERROR_ALLOC:
case Core::ToxCoreErrors::FAILED_TO_START:
case Core::ToxCoreErrors::INVALID_SAVE:
default:
emit failedToStart();
}
qDebug() << "failed to start ToxCore";
return;
}
// save tox file when Core requests it
connect(core.get(), &Core::saveRequest, this, &Profile::onSaveToxSave);
// react to avatar changes
connect(core.get(), &Core::friendAvatarRemoved, this, &Profile::removeAvatar);
connect(core.get(), &Core::friendAvatarData, this, &Profile::saveAvatar);
connect(core.get(), &Core::fileAvatarOfferReceived, this, &Profile::onAvatarOfferReceived,
Qt::ConnectionType::QueuedConnection);
}
Profile::Profile(QString name, const QString& password, bool isNewProfile, const QByteArray& toxsave)
: name{name}
, newProfile{isNewProfile}
@ -60,24 +90,10 @@ Profile::Profile(QString name, const QString& password, bool isNewProfile, const
s.setCurrentProfile(name);
s.saveGlobal();
coreThread = new QThread();
coreThread->setObjectName("qTox Core");
core = new Core(coreThread, *this, &Settings::getInstance());
QObject::connect(core, &Core::idSet, this,
[this, password](const ToxId& id) { loadDatabase(id, password); },
Qt::QueuedConnection);
core->moveToThread(coreThread);
QObject::connect(coreThread, &QThread::started, core, [=]() {
core->start(toxsave);
initCore(toxsave, s);
const ToxPk selfPk = core->getSelfPublicKey();
QByteArray data = loadAvatarData(selfPk);
if (data.isEmpty()) {
qDebug() << "Self avatar not found, will broadcast empty avatar to friends";
}
setAvatar(data, selfPk);
});
const ToxId& selfId = core->getSelfId();
loadDatabase(selfId, password);
}
/**
@ -91,8 +107,7 @@ Profile::Profile(QString name, const QString& password, bool isNewProfile, const
Profile* Profile::loadProfile(QString name, const QString& password)
{
if (ProfileLocker::hasLock()) {
qCritical() << "Tried to load profile " << name
<< ", but another profile is already locked!";
qCritical() << "Tried to load profile " << name << ", but another profile is already locked!";
return nullptr;
}
@ -186,8 +201,7 @@ Profile* Profile::createProfile(QString name, QString password)
}
if (ProfileLocker::hasLock()) {
qCritical() << "Tried to create profile " << name
<< ", but another profile is already locked!";
qCritical() << "Tried to create profile " << name << ", but another profile is already locked!";
return nullptr;
}
@ -214,14 +228,9 @@ Profile* Profile::createProfile(QString name, QString password)
Profile::~Profile()
{
if (!isRemoved && core->isReady()) {
saveToxSave();
onSaveToxSave();
}
core->deleteLater();
while (coreThread->isRunning())
qApp->processEvents();
delete coreThread;
if (!isRemoved) {
Settings::getInstance().savePersonal(this);
Settings::getInstance().sync();
@ -275,7 +284,8 @@ QStringList Profile::getProfiles()
Core* Profile::getCore()
{
return core;
// TODO(sudden6): this is evil
return core.get();
}
QString Profile::getName() const
@ -288,7 +298,18 @@ QString Profile::getName() const
*/
void Profile::startCore()
{
coreThread->start();
core->start();
const ToxId& selfId = core->getSelfId();
const ToxPk& selfPk = selfId.getPublicKey();
const QByteArray data = loadAvatarData(selfPk);
if (data.isEmpty()) {
qDebug() << "Self avatar not found, will broadcast empty avatar to friends";
}
// TODO(sudden6): moved here, because it crashes in the constructor
// reason: Core::getInstance() returns nullptr, because it's not yet initialized
// solution: kill Core::getInstance
setAvatar(data);
}
bool Profile::isNewProfile()
@ -300,7 +321,7 @@ bool Profile::isNewProfile()
* @brief Saves the profile's .tox save, encrypted if needed.
* @warning Invalid on deleted profiles.
*/
void Profile::saveToxSave()
void Profile::onSaveToxSave()
{
assert(core->isReady());
QByteArray data = core->getToxSaveData();
@ -308,12 +329,21 @@ void Profile::saveToxSave()
saveToxSave(data);
}
// TODO(sudden6): handle this better maybe?
void Profile::onAvatarOfferReceived(uint32_t friendId, uint32_t fileId, const QByteArray& avatarHash)
{
// accept if we don't have it already
const bool accept = getAvatarHash(core->getFriendPublicKey(friendId)) != avatarHash;
CoreFile::handleAvatarOffer(friendId, fileId, accept);
}
/**
* @brief Write the .tox save, encrypted if needed.
* @param data Byte array of profile save.
* @return true if successfully saved, false otherwise
* @warning Invalid on deleted profiles.
*/
void Profile::saveToxSave(QByteArray data)
bool Profile::saveToxSave(QByteArray data)
{
assert(!isRemoved);
ProfileLocker::assertLock();
@ -324,7 +354,7 @@ void Profile::saveToxSave(QByteArray data)
QSaveFile saveFile(path);
if (!saveFile.open(QIODevice::WriteOnly)) {
qCritical() << "Tox save file " << path << " couldn't be opened";
return;
return false;
}
if (encrypted) {
@ -332,7 +362,7 @@ void Profile::saveToxSave(QByteArray data)
if (data.isEmpty()) {
qCritical() << "Failed to encrypt, can't save!";
saveFile.cancelWriting();
return;
return false;
}
}
@ -345,7 +375,9 @@ void Profile::saveToxSave(QByteArray data)
} else {
saveFile.cancelWriting();
qCritical() << "Failed to write, can't save!";
return false;
}
return true;
}
/**
@ -374,8 +406,7 @@ QString Profile::avatarPath(const ToxPk& owner, bool forceUnencrypted)
QByteArray hash(hashSize, 0);
crypto_generichash((uint8_t*)hash.data(), hashSize, (uint8_t*)idData.data(), idData.size(),
(uint8_t*)pubkeyData.data(), pubkeyData.size());
return Settings::getInstance().getSettingsDirPath() + "avatars/" + hash.toHex().toUpper()
+ ".png";
return Settings::getInstance().getSettingsDirPath() + "avatars/" + hash.toHex().toUpper() + ".png";
}
/**
@ -465,26 +496,31 @@ void Profile::loadDatabase(const ToxId& id, QString password)
}
}
void Profile::setAvatar(QByteArray pic, const ToxPk& owner)
/**
* @brief Sets our own avatar
* @param pic Picture to use as avatar, if empty an Identicon will be used depending on settings
* @param owner
*/
void Profile::setAvatar(QByteArray pic)
{
QPixmap pixmap;
QByteArray avatarData;
const ToxPk& selfPk = core->getSelfPublicKey();
if (!pic.isEmpty()) {
pixmap.loadFromData(pic);
avatarData = pic;
} else {
if (Settings::getInstance().getShowIdenticons()) {
// with IDENTICON_ROWS=5 this gives a 160x160 image file
const QImage identicon = Identicon(owner.getKey()).toImage(32);
const QImage identicon = Identicon(selfPk.getKey()).toImage(32);
pixmap = QPixmap::fromImage(identicon);
} else {
pixmap.load(":/img/contact_dark.svg");
}
}
saveAvatar(avatarData, owner);
saveAvatar(selfPk, avatarData);
emit selfAvatarChanged(pixmap);
AvatarBroadcaster::setAvatar(avatarData);
@ -514,11 +550,10 @@ void Profile::onRequestSent(const ToxPk& friendPk, const QString& message)
* @param pic Picture to save.
* @param owner PK of avatar owner.
*/
void Profile::saveAvatar(QByteArray pic, const ToxPk& owner)
void Profile::saveAvatar(const ToxPk& owner, const QByteArray& avatar)
{
if (encrypted && !pic.isEmpty()) {
pic = passkey->encrypt(pic);
}
const bool needEncrypt = encrypted && !avatar.isEmpty();
const QByteArray& pic = needEncrypt ? passkey->encrypt(avatar) : avatar;
QString path = avatarPath(owner);
QDir(Settings::getInstance().getSettingsDirPath()).mkdir("avatars");
@ -551,7 +586,7 @@ QByteArray Profile::getAvatarHash(const ToxPk& owner)
/**
* @brief Removes our own avatar.
*/
void Profile::removeAvatar()
void Profile::removeSelfAvatar()
{
removeAvatar(core->getSelfId().getPublicKey());
}
@ -583,7 +618,7 @@ void Profile::removeAvatar(const ToxPk& owner)
{
QFile::remove(avatarPath(owner));
if (owner == core->getSelfId().getPublicKey()) {
setAvatar({}, core->getSelfPublicKey());
setAvatar({});
}
}
@ -716,11 +751,23 @@ const ToxEncrypt* Profile::getPasskey() const
void Profile::restartCore()
{
GUI::setEnabled(false); // Core::reset re-enables it
if (!isRemoved && core->isReady()) {
saveToxSave();
if (core && !isRemoved) {
// TODO(sudden6): there's a potential race condition between unlocking the core loop
// and killing the core
const QByteArray& savedata = core->getToxSaveData();
// save to disk just in case
if (saveToxSave(savedata)) {
qDebug() << "Restarting Core";
initCore(savedata, Settings::getInstance());
core->start();
} else {
qCritical() << "Failed to save, not restarting core";
}
}
QMetaObject::invokeMethod(core, "reset");
GUI::setEnabled(true);
}
/**
@ -749,7 +796,7 @@ QString Profile::setPassword(const QString& newPassword)
}
// apply new encryption
saveToxSave();
onSaveToxSave();
bool dbSuccess = false;
@ -767,13 +814,13 @@ QString Profile::setPassword(const QString& newPassword)
Nexus::getDesktopGUI()->reloadHistory();
QByteArray avatar = loadAvatarData(core->getSelfId().getPublicKey());
saveAvatar(avatar, core->getSelfId().getPublicKey());
saveAvatar(core->getSelfId().getPublicKey(), avatar);
QVector<uint32_t> friendList = core->getFriendList();
QVectorIterator<uint32_t> i(friendList);
while (i.hasNext()) {
const ToxPk friendPublicKey = core->getFriendPublicKey(i.next());
saveAvatar(loadAvatarData(friendPublicKey), friendPublicKey);
saveAvatar(friendPublicKey, loadAvatarData(friendPublicKey));
}
return error;
}

View File

@ -21,6 +21,7 @@
#ifndef PROFILE_H
#define PROFILE_H
#include "src/core/core.h"
#include "src/core/toxencrypt.h"
#include "src/core/toxid.h"
@ -33,9 +34,6 @@
#include <QVector>
#include <memory>
class Core;
class QThread;
class Profile : public QObject
{
Q_OBJECT
@ -55,17 +53,12 @@ public:
QString setPassword(const QString& newPassword);
const ToxEncrypt* getPasskey() const;
void saveToxSave();
void saveToxSave(QByteArray data);
QPixmap loadAvatar();
QPixmap loadAvatar(const ToxPk& owner);
QByteArray loadAvatarData(const ToxPk& owner);
void setAvatar(QByteArray pic, const ToxPk& owner);
void saveAvatar(QByteArray pic, const ToxPk& owner);
void setAvatar(QByteArray pic);
QByteArray getAvatarHash(const ToxPk& owner);
void removeAvatar(const ToxPk& owner);
void removeAvatar();
void removeSelfAvatar();
bool isHistoryEnabled();
History* getHistory();
@ -84,20 +77,30 @@ public:
signals:
void selfAvatarChanged(const QPixmap& pixmap);
// TODO(sudden6): this doesn't seem to be the right place for Core errors
void failedToStart();
void badProxy();
public slots:
void onRequestSent(const ToxPk& friendPk, const QString& message);
private slots:
void loadDatabase(const ToxId& id, QString password);
void saveAvatar(const ToxPk& owner, const QByteArray& avatar);
void removeAvatar(const ToxPk& owner);
void onSaveToxSave();
// TODO(sudden6): use ToxPk instead of friendId
void onAvatarOfferReceived(uint32_t friendId, uint32_t fileId, const QByteArray& avatarHash);
private:
Profile(QString name, const QString& password, bool newProfile, const QByteArray& toxsave);
static QStringList getFilesByExt(QString extension);
QString avatarPath(const ToxPk& owner, bool forceUnencrypted = false);
bool saveToxSave(QByteArray data);
void initCore(const QByteArray& toxsave, ICoreSettings& s);
private:
Core* core;
QThread* coreThread;
std::unique_ptr<Core> core = nullptr;
QString name;
std::unique_ptr<ToxEncrypt> passkey = nullptr;
std::shared_ptr<RawDatabase> database;

View File

@ -231,6 +231,7 @@ void Settings::loadGlobal()
lightTrayIcon = s.value("lightTrayIcon", false).toBool();
useEmoticons = s.value("useEmoticons", true).toBool();
statusChangeNotificationEnabled = s.value("statusChangeNotificationEnabled", false).toBool();
spellCheckingEnabled = s.value("spellCheckingEnabled", true).toBool();
themeColor = s.value("themeColor", 0).toInt();
style = s.value("style", "").toString();
if (style == "") // Default to Fusion if available, otherwise no style
@ -547,6 +548,7 @@ void Settings::saveGlobal()
s.setValue("themeColor", themeColor);
s.setValue("style", style);
s.setValue("statusChangeNotificationEnabled", statusChangeNotificationEnabled);
s.setValue("spellCheckingEnabled", spellCheckingEnabled);
}
s.endGroup();
@ -1046,6 +1048,22 @@ void Settings::setStatusChangeNotificationEnabled(bool newValue)
}
}
bool Settings::getSpellCheckingEnabled() const
{
const QMutexLocker locker{&bigLock};
return spellCheckingEnabled;
}
void Settings::setSpellCheckingEnabled(bool newValue)
{
QMutexLocker locker{&bigLock};
if (newValue != spellCheckingEnabled) {
spellCheckingEnabled = newValue;
emit statusChangeNotificationEnabledChanged(statusChangeNotificationEnabled);
}
}
bool Settings::getShowInFront() const
{
QMutexLocker locker{&bigLock};

View File

@ -91,6 +91,8 @@ class Settings : public QObject, public ICoreSettings, public IFriendSettings,
Q_PROPERTY(QString dateFormat READ getDateFormat WRITE setDateFormat NOTIFY dateFormatChanged FINAL)
Q_PROPERTY(bool statusChangeNotificationEnabled READ getStatusChangeNotificationEnabled WRITE
setStatusChangeNotificationEnabled NOTIFY statusChangeNotificationEnabledChanged FINAL)
Q_PROPERTY(bool spellCheckingEnabled READ getSpellCheckingEnabled WRITE
setSpellCheckingEnabled NOTIFY spellCheckingEnabledChanged FINAL)
// Privacy
Q_PROPERTY(bool typingNotification READ getTypingNotification WRITE setTypingNotification NOTIFY
@ -212,6 +214,7 @@ signals:
void timestampFormatChanged(const QString& format);
void dateFormatChanged(const QString& format);
void statusChangeNotificationEnabledChanged(bool enabled);
void spellCheckingEnabledChanged(bool enabled);
void fauxOfflineMessagingChanged(bool enabled);
// Privacy
@ -449,6 +452,9 @@ public:
bool getStatusChangeNotificationEnabled() const;
void setStatusChangeNotificationEnabled(bool newValue);
bool getSpellCheckingEnabled() const;
void setSpellCheckingEnabled(bool newValue);
// Privacy
bool getTypingNotification() const;
void setTypingNotification(bool enabled);
@ -641,6 +647,7 @@ private:
QString timestampFormat;
QString dateFormat;
bool statusChangeNotificationEnabled;
bool spellCheckingEnabled;
// Privacy
bool typingNotification;

View File

@ -50,7 +50,7 @@ private:
std::atomic_bool stopped;
friend class CoreAV;
friend class ToxCall;
friend class ToxFriendCall;
};
#endif // COREVIDEOSOURCE_H

View File

@ -50,11 +50,11 @@ public slots:
protected:
QVBoxLayout* verLayout;
VideoSurface* videoSurface;
QPushButton* enterFullScreenButton = nullptr;
private:
QHBoxLayout* buttonLayout = nullptr;
QPushButton* toggleMessagesButton = nullptr;
QPushButton* enterFullScreenButton = nullptr;
QFrame* buttonPanel = nullptr;
QPushButton* videoPreviewButton = nullptr;
QPushButton* volumeButton = nullptr;

View File

@ -20,8 +20,8 @@
#include "groupnetcamview.h"
#include "src/audio/audio.h"
#include "src/core/core.h"
#include "src/model/friend.h"
#include "src/friendlist.h"
#include "src/model/friend.h"
#include "src/nexus.h"
#include "src/persistence/profile.h"
#include "src/video/videosurface.h"
@ -56,9 +56,7 @@ public:
layout->addWidget(label);
}
~LabeledVideo()
{
}
~LabeledVideo() {}
VideoSurface* getVideoSurface() const
{
@ -117,6 +115,9 @@ GroupNetCamView::GroupNetCamView(int group, QWidget* parent)
videoLabelSurface->layout()->setMargin(0);
videoLabelSurface->setStyleSheet("QFrame { background-color: black; }");
// remove full screen button in audio group chat since it's useless there
enterFullScreenButton->hide();
QSplitter* splitter = new QSplitter(Qt::Vertical, this);
splitter->setChildrenCollapsible(false);
verLayout->insertWidget(0, splitter, 1);
@ -153,7 +154,7 @@ GroupNetCamView::GroupNetCamView(int group, QWidget* parent)
setActive();
});
connect(Core::getInstance(), &Core::friendAvatarChanged, this,
connect(Core::getInstance(), &Core::friendAvatarChangedDeprecated, this,
&GroupNetCamView::friendAvatarChanged);
selfVideoSurface->setText(Core::getInstance()->getUsername());

View File

@ -20,8 +20,8 @@
#include "netcamview.h"
#include "camerasource.h"
#include "src/core/core.h"
#include "src/model/friend.h"
#include "src/friendlist.h"
#include "src/model/friend.h"
#include "src/nexus.h"
#include "src/persistence/profile.h"
#include "src/persistence/settings.h"
@ -75,7 +75,7 @@ NetCamView::NetCamView(int friendId, QWidget* parent)
connections += connect(Nexus::getProfile(), &Profile::selfAvatarChanged,
[this](const QPixmap& pixmap) { selfVideoSurface->setAvatar(pixmap); });
connections += connect(Core::getInstance(), &Core::friendAvatarChanged,
connections += connect(Core::getInstance(), &Core::friendAvatarChangedDeprecated,
[this](int FriendId, const QPixmap& pixmap) {
if (this->friendId == FriendId)
videoSurface->setAvatar(pixmap);

View File

@ -4,10 +4,10 @@
#include <QFileDialog>
#include <QMessageBox>
AboutFriendForm::AboutFriendForm(QPointer<IAboutFriend> about, QWidget* parent)
AboutFriendForm::AboutFriendForm(std::unique_ptr<IAboutFriend> _about, QWidget* parent)
: QDialog(parent)
, ui(new Ui::AboutFriendForm)
, about{about}
, about{std::move(_about)}
{
ui->setupUi(this);
ui->label_4->hide();
@ -19,7 +19,7 @@ AboutFriendForm::AboutFriendForm(QPointer<IAboutFriend> about, QWidget* parent)
connect(ui->autogroupinvite, &QCheckBox::clicked, this, &AboutFriendForm::onAutoGroupInvite);
connect(ui->selectSaveDir, &QPushButton::clicked, this, &AboutFriendForm::onSelectDirClicked);
connect(ui->removeHistory, &QPushButton::clicked, this, &AboutFriendForm::onRemoveHistoryClicked);
about.data()->connectTo_autoAcceptDirChanged([=](const QString& dir){ onAutoAcceptDirChanged(dir); });
about->connectTo_autoAcceptDirChanged([=](const QString& dir){ onAutoAcceptDirChanged(dir); });
const QString dir = about->getAutoAcceptDir();
ui->autoacceptfile->setChecked(!dir.isEmpty());

View File

@ -6,6 +6,8 @@
#include <QDialog>
#include <QPointer>
#include <memory>
namespace Ui {
class AboutFriendForm;
}
@ -15,12 +17,12 @@ class AboutFriendForm : public QDialog
Q_OBJECT
public:
AboutFriendForm(QPointer<IAboutFriend> about, QWidget* parent = 0);
AboutFriendForm(std::unique_ptr<IAboutFriend> about, QWidget* parent = 0);
~AboutFriendForm();
private:
Ui::AboutFriendForm* ui;
QPointer<IAboutFriend> about;
const std::unique_ptr<IAboutFriend> about;
private slots:
void onAutoAcceptDirChanged(const QString& path);

View File

@ -156,6 +156,8 @@ ChatFormHeader::ChatFormHeader(QWidget* parent)
Translator::registerHandler(std::bind(&ChatFormHeader::retranslateUi, this), this);
}
ChatFormHeader::~ChatFormHeader() = default;
void ChatFormHeader::setName(const QString& newName)
{
nameLabel->setText(newName);

View File

@ -55,6 +55,7 @@ public:
};
ChatFormHeader(QWidget* parent = nullptr);
~ChatFormHeader();
void setName(const QString& newName);
void setMode(Mode mode);

View File

@ -27,21 +27,22 @@
#include <QShortcut>
#include <QSplitter>
#include "contentlayout.h"
#include "friendwidget.h"
#include "groupwidget.h"
#include "style.h"
#include "widget.h"
#include "src/core/core.h"
#include "src/model/friend.h"
#include "src/friendlist.h"
#include "src/model/group.h"
#include "src/grouplist.h"
#include "src/model/chatroom/friendchatroom.h"
#include "src/model/friend.h"
#include "src/model/group.h"
#include "src/persistence/settings.h"
#include "src/widget/contentlayout.h"
#include "src/widget/friendwidget.h"
#include "src/widget/groupwidget.h"
#include "src/widget/form/chatform.h"
#include "src/widget/friendlistlayout.h"
#include "src/widget/style.h"
#include "src/widget/tool/adjustingscrollarea.h"
#include "src/widget/translator.h"
#include "tool/adjustingscrollarea.h"
#include "src/widget/widget.h"
QString ContentDialog::username = "";
ContentDialog* ContentDialog::currentDialog = nullptr;
@ -162,11 +163,12 @@ ContentDialog::~ContentDialog()
Translator::unregister(this);
}
FriendWidget* ContentDialog::addFriend(const Friend* frnd, GenericChatForm* form)
FriendWidget* ContentDialog::addFriend(std::shared_ptr<FriendChatroom> chatroom, GenericChatForm* form)
{
bool compact = Settings::getInstance().getCompactLayout();
uint32_t friendId = frnd->getId();
FriendWidget* friendWidget = new FriendWidget(frnd, compact);
const auto compact = Settings::getInstance().getCompactLayout();
auto frnd = chatroom->getFriend();
auto friendId = frnd->getId();
auto friendWidget = new FriendWidget(chatroom, compact);
friendLayout->addFriendWidget(friendWidget, frnd->getStatus());
friendChatForms[friendId] = form;
@ -187,12 +189,12 @@ FriendWidget* ContentDialog::addFriend(const Friend* frnd, GenericChatForm* form
return friendWidget;
}
GroupWidget* ContentDialog::addGroup(const Group* g, GenericChatForm* form)
GroupWidget* ContentDialog::addGroup(std::shared_ptr<GroupChatroom> chatroom, GenericChatForm* form)
{
const auto g = chatroom->getGroup();
const auto groupId = g->getId();
const auto name = g->getName();
const auto compact = Settings::getInstance().getCompactLayout();
GroupWidget* groupWidget = new GroupWidget(groupId, name, compact);
GroupWidget* groupWidget = new GroupWidget(chatroom, compact);
groupLayout.addSortedWidget(groupWidget);
groupChatForms[groupId] = form;

View File

@ -20,27 +20,30 @@
#ifndef CONTENTDIALOG_H
#define CONTENTDIALOG_H
#include <tuple>
#include "src/widget/genericchatitemlayout.h"
#include "src/widget/tool/activatedialog.h"
#include <memory>
#include <tuple>
template <typename K, typename V>
class QHash;
template <typename T>
class QSet;
class QSplitter;
class QVBoxLayout;
class ContentDialog;
class ContentLayout;
class Friend;
class FriendChatroom;
class FriendListLayout;
class FriendWidget;
class GenericChatForm;
class GenericChatroomWidget;
class FriendWidget;
class GroupWidget;
class FriendListLayout;
class Friend;
class Group;
class GroupChatroom;
class GroupWidget;
class QSplitter;
class QVBoxLayout;
using ContactInfo = std::tuple<ContentDialog*, GenericChatroomWidget*>;
@ -51,8 +54,8 @@ public:
explicit ContentDialog(QWidget* parent = nullptr);
~ContentDialog() override;
FriendWidget* addFriend(const Friend* f, GenericChatForm* form);
GroupWidget* addGroup(const Group* g, GenericChatForm* form);
FriendWidget* addFriend(std::shared_ptr<FriendChatroom> chatroom, GenericChatForm* form);
GroupWidget* addGroup(std::shared_ptr<GroupChatroom> chatroom, GenericChatForm* form);
void removeFriend(int friendId);
void removeGroup(int groupId);
bool hasFriendWidget(int friendId, const GenericChatroomWidget* chatroomWidget) const;

View File

@ -27,21 +27,21 @@
#include "src/core/coreav.h"
#include "src/model/friend.h"
#include "src/nexus.h"
#include "src/persistence/history.h"
#include "src/persistence/offlinemsgengine.h"
#include "src/persistence/profile.h"
#include "src/persistence/settings.h"
#include "src/persistence/history.h"
#include "src/video/netcamview.h"
#include "src/widget/chatformheader.h"
#include "src/widget/form/loadhistorydialog.h"
#include "src/widget/maskablepixmapwidget.h"
#include "src/widget/searchform.h"
#include "src/widget/style.h"
#include "src/widget/tool/callconfirmwidget.h"
#include "src/widget/tool/chattextedit.h"
#include "src/widget/tool/screenshotgrabber.h"
#include "src/widget/translator.h"
#include "src/widget/widget.h"
#include "src/widget/searchform.h"
#include <QClipboard>
#include <QFileDialog>
@ -158,7 +158,8 @@ ChatForm::ChatForm(Friend* chatFriend, History* history)
const Core* core = Core::getInstance();
connect(core, &Core::fileReceiveRequested, this, &ChatForm::onFileRecvRequest);
connect(core, &Core::friendAvatarChanged, this, &ChatForm::onAvatarChange);
// TODO(sudden6): update slot to new API
connect(core, &Core::friendAvatarChangedDeprecated, this, &ChatForm::onAvatarChange);
connect(core, &Core::friendAvatarRemoved, this, &ChatForm::onAvatarRemoved);
connect(core, &Core::fileSendStarted, this, &ChatForm::startFileSend);
connect(core, &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
@ -196,12 +197,10 @@ ChatForm::ChatForm(Friend* chatFriend, History* history)
});
// reflect name changes in the header
connect(headWidget, &ChatFormHeader::nameChanged, this, [=](const QString& newName) {
f->setAlias(newName);
});
connect(headWidget, &ChatFormHeader::callAccepted, this, [this] {
onAnswerCallTriggered(lastCallIsVideo);
});
connect(headWidget, &ChatFormHeader::nameChanged, this,
[=](const QString& newName) { f->setAlias(newName); });
connect(headWidget, &ChatFormHeader::callAccepted, this,
[this] { onAnswerCallTriggered(lastCallIsVideo); });
connect(headWidget, &ChatFormHeader::callRejected, this, &ChatForm::onRejectCallTriggered);
updateCallButtons();
@ -219,6 +218,7 @@ ChatForm::~ChatForm()
Translator::unregister(this);
delete netcam;
netcam = nullptr;
delete offlineEngine;
}
void ChatForm::setStatusMessage(const QString& newMessage)
@ -233,8 +233,12 @@ void ChatForm::onSendTriggered()
SendMessageStr(msgEdit->toPlainText());
msgEdit->clear();
}
void ChatForm::onFileNameChanged()
void ChatForm::onFileNameChanged(const ToxPk& friendPk)
{
if(friendPk != f->getPublicKey()) {
return;
}
QMessageBox::warning(this, tr("Filename contained illegal characters"),
tr("Illegal characters have been changed to _ \n"
"so you can save the file on windows."));
@ -263,7 +267,8 @@ void ChatForm::onTextEditChanged()
void ChatForm::onAttachClicked()
{
QStringList paths = QFileDialog::getOpenFileNames(Q_NULLPTR, tr("Send a file"), QDir::homePath(), 0, 0);
QStringList paths =
QFileDialog::getOpenFileNames(Q_NULLPTR, tr("Send a file"), QDir::homePath(), 0, 0);
if (paths.isEmpty()) {
return;
@ -709,8 +714,9 @@ void ChatForm::dropEvent(QDropEvent* ev)
file.close();
if (file.isSequential()) {
QMessageBox::critical(0, tr("Bad idea"), tr("You're trying to send a sequential file, "
"which is not going to work!"));
QMessageBox::critical(0, tr("Bad idea"),
tr("You're trying to send a sequential file, "
"which is not going to work!"));
continue;
}
@ -720,9 +726,9 @@ void ChatForm::dropEvent(QDropEvent* ev)
}
}
void ChatForm::onAvatarRemoved(uint32_t friendId)
void ChatForm::onAvatarRemoved(const ToxPk& friendPk)
{
if (friendId != f->getId()) {
if (friendPk != f->getPublicKey()) {
return;
}
@ -818,7 +824,9 @@ void ChatForm::insertChatlines(QList<ChatLine::Ptr> chatLines)
verticalBar->setValue(savedSliderPos);
}
QDate ChatForm::addDateLineIfNeeded(QList<ChatLine::Ptr> msgs, QDate const& lastDate, History::HistMessage const& newMessage, MessageMetadata const& metadata)
QDate ChatForm::addDateLineIfNeeded(QList<ChatLine::Ptr> msgs, QDate const& lastDate,
History::HistMessage const& newMessage,
MessageMetadata const& metadata)
{
// Show the date every new day
QDate newDate = metadata.msgDateTime.date();
@ -842,11 +850,13 @@ ChatForm::MessageMetadata ChatForm::getMessageMetadata(History::HistMessage cons
return {isSelf, needSending, isAction, id, authorPk, msgDateTime};
}
ChatMessage::Ptr ChatForm::chatMessageFromHistMessage(History::HistMessage const& histMessage, MessageMetadata const& metadata)
ChatMessage::Ptr ChatForm::chatMessageFromHistMessage(History::HistMessage const& histMessage,
MessageMetadata const& metadata)
{
ToxPk authorPk(ToxId(histMessage.sender).getPublicKey());
QString authorStr = getMsgAuthorDispName(authorPk, histMessage.dispName);
QString messageText = metadata.isAction ? histMessage.message.mid(ACTION_PREFIX.length()) : histMessage.message;
QString messageText =
metadata.isAction ? histMessage.message.mid(ACTION_PREFIX.length()) : histMessage.message;
ChatMessage::MessageType type = metadata.isAction ? ChatMessage::ACTION : ChatMessage::NORMAL;
QDateTime dateTime = metadata.needSending ? QDateTime() : metadata.msgDateTime;
auto msg = ChatMessage::createChatMessage(authorStr, messageText, type, metadata.isSelf, dateTime);
@ -986,8 +996,7 @@ void ChatForm::stopCounter(bool error)
QString mess = error ? tr("Call with %1 ended unexpectedly. %2") : tr("Call with %1 ended. %2");
// TODO: add notification once notifications are implemented
addSystemInfoMessage(mess.arg(name, dhms), ChatMessage::INFO,
QDateTime::currentDateTime());
addSystemInfoMessage(mess.arg(name, dhms), ChatMessage::INFO, QDateTime::currentDateTime());
callDurationTimer->stop();
callDuration->setText("");
callDuration->hide();
@ -1062,9 +1071,9 @@ void ChatForm::SendMessageStr(QString msg)
QString pk = f->getPublicKey().toString();
QString name = Core::getInstance()->getUsername();
history->addNewMessage(pk, historyPart, selfPk, timestamp, status, name,
[offMsgEngine, rec, ma](int64_t id) {
offMsgEngine->registerReceipt(rec, id, ma);
});
[offMsgEngine, rec, ma](int64_t id) {
offMsgEngine->registerReceipt(rec, id, ma);
});
} else {
// TODO: Make faux-offline messaging work partially with the history disabled
ma->markAsSent(QDateTime::currentDateTime());
@ -1124,12 +1133,12 @@ void ChatForm::onExportChat()
QString buffer;
for (const auto& it : msgs) {
QString timestamp = it.timestamp.toString();
QString timestamp = it.timestamp.time().toString("hh:mm:ss");
QString datestamp = it.timestamp.date().toString("yyyy-MM-dd");
ToxPk authorPk(ToxId(it.sender).getPublicKey());
QString author = getMsgAuthorDispName(authorPk, it.dispName);
QString line = QString("%1\t%2\t%3\n").arg(timestamp, author, it.message);
buffer = buffer % line;
buffer = buffer % QString{datestamp % '\t' % timestamp % '\t' % author % '\t' % it.message % '\n'};
}
file.write(buffer.toUtf8());
file.close();

View File

@ -73,8 +73,8 @@ public slots:
void onAvStart(uint32_t friendId, bool video);
void onAvEnd(uint32_t friendId, bool error);
void onAvatarChange(uint32_t friendId, const QPixmap& pic);
void onAvatarRemoved(uint32_t friendId);
void onFileNameChanged();
void onAvatarRemoved(const ToxPk& friendPk);
void onFileNameChanged(const ToxPk& friendPk);
protected slots:
void searchInBegin(const QString& phrase, const ParameterSearch& parameter) override;
@ -110,25 +110,30 @@ private slots:
void onExportChat();
private:
struct MessageMetadata {
struct MessageMetadata
{
const bool isSelf;
const bool needSending;
const bool isAction;
const qint64 id;
const ToxPk authorPk;
const QDateTime msgDateTime;
MessageMetadata(bool isSelf, bool needSending, bool isAction, qint64 id, ToxPk authorPk, QDateTime msgDateTime) :
isSelf{isSelf},
needSending{needSending},
isAction{isAction},
id{id},
authorPk{authorPk},
msgDateTime{msgDateTime} {}
MessageMetadata(bool isSelf, bool needSending, bool isAction, qint64 id, ToxPk authorPk,
QDateTime msgDateTime)
: isSelf{isSelf}
, needSending{needSending}
, isAction{isAction}
, id{id}
, authorPk{authorPk}
, msgDateTime{msgDateTime}
{}
};
void handleLoadedMessages(QList<History::HistMessage> newHistMsgs, bool processUndelivered);
QDate addDateLineIfNeeded(QList<ChatLine::Ptr> msgs, QDate const& lastDate, History::HistMessage const& newMessage, MessageMetadata const& metadata);
QDate addDateLineIfNeeded(QList<ChatLine::Ptr> msgs, QDate const& lastDate,
History::HistMessage const& newMessage, MessageMetadata const& metadata);
MessageMetadata getMessageMetadata(History::HistMessage const& histMessage);
ChatMessage::Ptr chatMessageFromHistMessage(History::HistMessage const& histMessage, MessageMetadata const& metadata);
ChatMessage::Ptr chatMessageFromHistMessage(History::HistMessage const& histMessage,
MessageMetadata const& metadata);
void sendLoadedMessage(ChatMessage::Ptr chatMsg, MessageMetadata const& metadata);
void insertChatlines(QList<ChatLine::Ptr> chatLines);
void updateMuteMicButton();

View File

@ -46,6 +46,11 @@
#include <QKeyEvent>
#include <QMessageBox>
#include <QRegularExpression>
#include <QStringBuilder>
#ifdef SPELL_CHECKING
#include <KF5/SonnetUi/sonnet/spellcheckdecorator.h>
#endif
/**
* @class GenericChatForm
@ -143,6 +148,11 @@ GenericChatForm::GenericChatForm(const Contact* contact, QWidget* parent)
connect(&s, &Settings::chatMessageFontChanged, this, &GenericChatForm::onChatMessageFontChanged);
msgEdit = new ChatTextEdit();
#ifdef SPELL_CHECKING
if (s.getSpellCheckingEnabled()) {
decorator = new Sonnet::SpellCheckDecorator(msgEdit);
}
#endif
sendButton = createButton("sendButton", this, &GenericChatForm::onSendTriggered);
emoteButton = createButton("emoteButton", this, &GenericChatForm::onEmoteButtonClicked);
@ -481,17 +491,16 @@ void GenericChatForm::onSaveLogClicked()
for (ChatLine::Ptr l : lines) {
Timestamp* rightCol = qobject_cast<Timestamp*>(l->getContent(2));
if (!rightCol)
break;
ChatLineContent* middleCol = l->getContent(1);
ChatLineContent* leftCol = l->getContent(0);
QString timestamp = rightCol->getTime().isNull() ? tr("Not sent") : rightCol->getText();
QString nick = leftCol->getText();
QString nick = leftCol->getText().isNull() ? tr("[System message]") : leftCol->getText();
QString msg = middleCol->getText();
plainText += QString("[%2] %1\n%3\n\n").arg(nick, timestamp, msg);
QString timestamp = (rightCol == nullptr) ? tr("Not sent") : rightCol->getText();
plainText += QString{nick % "\t" % timestamp % "\t" % msg % "\n"};
}
file.write(plainText.toUtf8());

View File

@ -55,6 +55,12 @@ namespace Ui {
class MainWindow;
}
#ifdef SPELL_CHECKING
namespace Sonnet {
class SpellCheckDecorator;
}
#endif
class GenericChatForm : public QWidget
{
Q_OBJECT
@ -170,6 +176,9 @@ protected:
SearchForm *searchForm;
ChatLog* chatWidget;
ChatTextEdit* msgEdit;
#ifdef SPELL_CHECKING
Sonnet::SpellCheckDecorator* decorator{nullptr};
#endif
FlyoutOverlayWidget* fileFlyout;
GenericNetCamView* netcam;
Widget* parent;

View File

@ -55,18 +55,19 @@ AdvancedForm::AdvancedForm()
Settings& s = Settings::getInstance();
bodyUI->cbEnableIPv6->setChecked(s.getEnableIPv6());
bodyUI->cbMakeToxPortable->setChecked(Settings::getInstance().getMakeToxPortable());
const bool udpEnabled = !s.getForceTCP();
bodyUI->cbEnableUDP->setChecked(udpEnabled);
bodyUI->cbEnableLanDiscovery->setChecked(s.getEnableLanDiscovery());
bodyUI->cbEnableLanDiscovery->setEnabled(udpEnabled);
bodyUI->proxyAddr->setText(s.getProxyAddr());
quint16 port = s.getProxyPort();
if (port > 0)
if (port > 0) {
bodyUI->proxyPort->setValue(port);
}
int index = static_cast<int>(s.getProxyType());
bodyUI->proxyType->setCurrentIndex(index);
on_proxyType_currentIndexChanged(index);
const bool udpEnabled = !s.getForceTCP() && (s.getProxyType() == Settings::ProxyType::ptNone);
bodyUI->cbEnableUDP->setChecked(udpEnabled);
bodyUI->cbEnableLanDiscovery->setChecked(s.getEnableLanDiscovery() && udpEnabled);
bodyUI->cbEnableLanDiscovery->setEnabled(udpEnabled);
QString warningBody = tr("Unless you %1 know what you are doing, "
"please do %2 change anything here. Changes "
@ -176,7 +177,9 @@ void AdvancedForm::on_cbEnableUDP_stateChanged()
{
const bool enableUdp = bodyUI->cbEnableUDP->isChecked();
Settings::getInstance().setForceTCP(!enableUdp);
const bool enableLanDiscovery = Settings::getInstance().getEnableLanDiscovery();
bodyUI->cbEnableLanDiscovery->setEnabled(enableUdp);
bodyUI->cbEnableLanDiscovery->setChecked(enableUdp && enableLanDiscovery);
}
void AdvancedForm::on_cbEnableLanDiscovery_stateChanged()
@ -191,8 +194,9 @@ void AdvancedForm::on_proxyAddr_editingFinished()
void AdvancedForm::on_proxyPort_valueChanged(int port)
{
if (port <= 0)
if (port <= 0) {
port = 0;
}
Settings::getInstance().setProxyPort(port);
}
@ -200,9 +204,14 @@ void AdvancedForm::on_proxyPort_valueChanged(int port)
void AdvancedForm::on_proxyType_currentIndexChanged(int index)
{
Settings::ProxyType proxytype = static_cast<Settings::ProxyType>(index);
const bool proxyEnabled = proxytype != Settings::ProxyType::ptNone;
bodyUI->proxyAddr->setEnabled(proxyEnabled);
bodyUI->proxyPort->setEnabled(proxyEnabled);
// enabling UDP and proxy can be a privacy issue
bodyUI->cbEnableUDP->setEnabled(!proxyEnabled);
bodyUI->cbEnableUDP->setChecked(!proxyEnabled);
bodyUI->proxyAddr->setEnabled(proxytype != Settings::ProxyType::ptNone);
bodyUI->proxyPort->setEnabled(proxytype != Settings::ProxyType::ptNone);
Settings::getInstance().setProxyType(proxytype);
}

View File

@ -191,10 +191,7 @@ void AVForm::on_videoModescomboBox_currentIndexChanged(int index)
return;
}
// note: grabber is self-managed and will destroy itself when done
ScreenshotGrabber* screenshotGrabber = new ScreenshotGrabber;
auto onGrabbed = [screenshotGrabber, devName, this](QRect region) {
auto onGrabbed = [devName, this](QRect region) {
VideoMode mode(region);
mode.width = mode.width / 2 * 2;
mode.height = mode.height / 2 * 2;
@ -210,6 +207,9 @@ void AVForm::on_videoModescomboBox_currentIndexChanged(int index)
open(devName, mode);
};
// note: grabber is self-managed and will destroy itself when done
ScreenshotGrabber* screenshotGrabber = new ScreenshotGrabber;
connect(screenshotGrabber, &ScreenshotGrabber::regionChosen, this, onGrabbed,
Qt::QueuedConnection);
screenshotGrabber->showGrabber();

View File

@ -105,6 +105,11 @@ GeneralForm::GeneralForm(SettingsWidget* myParent)
#else
bodyUI->checkUpdates->setVisible(false);
#endif
#ifndef SPELL_CHECKING
bodyUI->cbSpellChecking->setVisible(false);
#endif
bodyUI->checkUpdates->setChecked(s.getCheckUpdates());
for (int i = 0; i < locales.size(); ++i) {
@ -126,6 +131,7 @@ GeneralForm::GeneralForm(SettingsWidget* myParent)
bodyUI->cbAutorun->setChecked(s.getAutorun());
bodyUI->cbSpellChecking->setChecked(s.getSpellCheckingEnabled());
bodyUI->lightTrayIcon->setChecked(s.getLightTrayIcon());
bool showSystemTray = s.getShowSystemTray();
@ -172,6 +178,11 @@ void GeneralForm::on_cbAutorun_stateChanged()
Settings::getInstance().setAutorun(bodyUI->cbAutorun->isChecked());
}
void GeneralForm::on_cbSpellChecking_stateChanged()
{
Settings::getInstance().setSpellCheckingEnabled(bodyUI->cbSpellChecking->isChecked());
}
void GeneralForm::on_showSystemTray_stateChanged()
{
Settings::getInstance().setShowSystemTray(bodyUI->showSystemTray->isChecked());

View File

@ -42,6 +42,7 @@ public:
private slots:
void on_transComboBox_currentIndexChanged(int index);
void on_cbAutorun_stateChanged();
void on_cbSpellChecking_stateChanged();
void on_showSystemTray_stateChanged();
void on_startInTray_stateChanged();
void on_closeToTray_stateChanged();

View File

@ -119,6 +119,13 @@
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="cbSpellChecking">
<property name="text">
<string>Spell checking</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="lightTrayIcon">
<property name="sizePolicy">

View File

@ -51,7 +51,7 @@ SettingsWidget::SettingsWidget(QWidget* parent)
setAttribute(Qt::WA_DeleteOnClose);
QVBoxLayout* bodyLayout = new QVBoxLayout();
bodyLayout = std::unique_ptr<QVBoxLayout>(new QVBoxLayout());
settingsWidgets = std::unique_ptr<QTabWidget>(new QTabWidget(this));
settingsWidgets->setTabPosition(QTabWidget::North);

View File

@ -56,6 +56,7 @@ private:
void retranslateUi();
private:
std::unique_ptr<QVBoxLayout> bodyLayout;
std::unique_ptr<QTabWidget> settingsWidgets;
std::array<std::unique_ptr<GenericForm>, 6> cfgForms;
int currentIndex;

View File

@ -24,11 +24,11 @@
#include "maskablepixmapwidget.h"
#include "src/core/core.h"
#include "src/model/friend.h"
#include "src/model/about/aboutfriend.h"
#include "src/friendlist.h"
#include "src/model/about/aboutfriend.h"
#include "src/model/chatroom/friendchatroom.h"
#include "src/model/friend.h"
#include "src/model/group.h"
#include "src/grouplist.h"
#include "src/persistence/settings.h"
#include "src/widget/about/aboutfriendform.h"
#include "src/widget/form/chatform.h"
@ -38,7 +38,6 @@
#include <QApplication>
#include <QBitmap>
#include <QCollator>
#include <QContextMenuEvent>
#include <QDebug>
#include <QDrag>
@ -49,11 +48,6 @@
#include <cassert>
namespace
{
constexpr auto MAX_NAME_LENGTH = 30;
}
/**
* @class FriendWidget
*
@ -61,21 +55,22 @@ constexpr auto MAX_NAME_LENGTH = 30;
* For example, used on friend list.
* When you click should open the chat with friend. Widget has a context menu.
*/
FriendWidget::FriendWidget(const Friend* f, bool compact)
FriendWidget::FriendWidget(std::shared_ptr<FriendChatroom> chatroom, bool compact)
: GenericChatroomWidget(compact)
, frnd{f}
, chatroom{chatroom}
, isDefaultAvatar{true}
{
avatar->setPixmap(QPixmap(":/img/contact.svg"));
statusPic.setPixmap(QPixmap(":/img/status/offline.svg"));
statusPic.setMargin(3);
setName(f->getDisplayedName());
nameLabel->setTextFormat(Qt::PlainText);
// update on changes of the displayed name
connect(f, &Friend::displayedNameChanged, this, &FriendWidget::setName);
auto frnd = chatroom->getFriend();
nameLabel->setText(frnd->getDisplayedName());
// update alias when edited
connect(nameLabel, &CroppingLabel::editFinished, f, &Friend::setAlias);
connect(nameLabel, &CroppingLabel::editFinished, frnd, &Friend::setAlias);
// update on changes of the displayed name
connect(frnd, &Friend::displayedNameChanged, nameLabel, &CroppingLabel::setText);
connect(chatroom.get(), &FriendChatroom::activeChanged, this, &FriendWidget::setActive);
statusMessageLabel->setTextFormat(Qt::PlainText);
}
@ -107,92 +102,77 @@ void FriendWidget::onContextMenuCalled(QContextMenuEvent* event)
QMenu menu;
const auto frnd = chatroom->getFriend();
const auto friendId = frnd->getId();
const ContentDialog* contentDialog = ContentDialog::getFriendDialog(friendId);
const auto contentDialog = ContentDialog::getFriendDialog(friendId);
// TODO: move to model
if (!contentDialog || contentDialog->chatroomWidgetCount() > 1) {
const auto openChatWindow = menu.addAction(tr("Open chat in new window"));
connect(openChatWindow, &QAction::triggered, [=]() { emit newWindowOpened(this); });
}
// TODO: move to model
if (contentDialog && contentDialog->hasFriendWidget(friendId, this)) {
const auto removeChatWindow = menu.addAction(tr("Remove chat from this window"));
connect(removeChatWindow, &QAction::triggered, this, &FriendWidget::removeChatWindow);
}
menu.addSeparator();
QMenu* inviteMenu = menu.addMenu(tr("Invite to group",
"Menu to invite a friend to a groupchat"));
inviteMenu->setEnabled(frnd->getStatus() != Status::Offline);
QMenu* inviteMenu =
menu.addMenu(tr("Invite to group", "Menu to invite a friend to a groupchat"));
inviteMenu->setEnabled(chatroom->canBeInvited());
const auto newGroupAction = inviteMenu->addAction(tr("To new group"));
connect(newGroupAction, &QAction::triggered, this, &FriendWidget::moveToNewGroup);
connect(newGroupAction, &QAction::triggered, chatroom.get(), &FriendChatroom::inviteToNewGroup);
inviteMenu->addSeparator();
for (const Group* group : GroupList::getAllGroups()) {
auto name = group->getName();
if (name.length() > MAX_NAME_LENGTH) {
name = name.left(MAX_NAME_LENGTH).trimmed() + "..";
}
const auto groupAction = inviteMenu->addAction(tr("Invite to group '%1'").arg(name));
connect(groupAction, &QAction::triggered, [=]() { inviteFriend(friendId, group); });
for (const auto group : chatroom->getGroups()) {
const auto groupAction = inviteMenu->addAction(tr("Invite to group '%1'").arg(group.name));
connect(groupAction, &QAction::triggered, [=]() {
chatroom->inviteFriend(group.group);
});
}
const auto& s = Settings::getInstance();
const auto circleId = s.getFriendCircleID(frnd->getPublicKey());
auto circleMenu = menu.addMenu(tr("Move to circle...",
"Menu to move a friend into a different circle"));
const auto circleId = chatroom->getCircleId();
auto circleMenu =
menu.addMenu(tr("Move to circle...", "Menu to move a friend into a different circle"));
const auto pk = frnd->getPublicKey();
const auto newCircleAction = circleMenu->addAction(tr("To new circle"));
connect(newCircleAction, &QAction::triggered, this, &FriendWidget::moveToNewCircle);
if (circleId != -1) {
const QString circleName = s.getCircleName(circleId);
const auto removeCircleAction = circleMenu->addAction(
tr("Remove from circle '%1'").arg(circleName));
const auto circleName = chatroom->getCircleName();
const auto removeCircleAction =
circleMenu->addAction(tr("Remove from circle '%1'").arg(circleName));
connect(removeCircleAction, &QAction::triggered, this, &FriendWidget::removeFromCircle);
}
circleMenu->addSeparator();
QList<QAction*> circleActionList;
for (int i = 0; i < s.getCircleCount(); ++i) {
if (i == circleId) {
continue;
}
const auto name = s.getCircleName(i);
QAction* action = new QAction(tr("Move to circle \"%1\"").arg(name), circleMenu);
connect(action, &QAction::triggered, [=]() { moveToCircle(i); });
circleActionList.push_back(action);
for (const auto circle : chatroom->getOtherCircles()) {
QAction* action = new QAction(tr("Move to circle \"%1\"").arg(circle.name), circleMenu);
connect(action, &QAction::triggered, [=]() { moveToCircle(circle.circleId); });
circleMenu->addAction(action);
}
std::sort(circleActionList.begin(), circleActionList.end(),
[](const QAction* lhs, const QAction* rhs) -> bool {
QCollator collator;
collator.setNumericMode(true);
return collator.compare(lhs->text(), rhs->text()) < 0;
});
circleMenu->addActions(circleActionList);
const auto setAlias = menu.addAction(tr("Set alias..."));
connect(setAlias, &QAction::triggered, [this]() { nameLabel->editBegin(); });
connect(setAlias, &QAction::triggered, nameLabel, &CroppingLabel::editBegin);
menu.addSeparator();
auto autoAccept = menu.addAction(tr("Auto accept files from this friend",
"context menu entry"));
const auto dir = s.getAutoAcceptDir(pk);
auto autoAccept =
menu.addAction(tr("Auto accept files from this friend", "context menu entry"));
autoAccept->setCheckable(true);
autoAccept->setChecked(!dir.isEmpty());
autoAccept->setChecked(!chatroom->autoAcceptEnabled());
connect(autoAccept, &QAction::triggered, this, &FriendWidget::changeAutoAccept);
menu.addSeparator();
// TODO: move to model
if (!contentDialog || !contentDialog->hasFriendWidget(friendId, this)) {
const auto removeAction = menu.addAction(
tr("Remove friend", "Menu to remove the friend from our friendlist"));
const auto removeAction =
menu.addAction(tr("Remove friend", "Menu to remove the friend from our friendlist"));
connect(removeAction, &QAction::triggered, this, [=]() { emit removeFriend(friendId); },
Qt::QueuedConnection);
Qt::QueuedConnection);
}
menu.addSeparator();
@ -211,28 +191,15 @@ void FriendWidget::onContextMenuCalled(QContextMenuEvent* event)
void FriendWidget::removeChatWindow()
{
const auto frnd = chatroom->getFriend();
const auto friendId = frnd->getId();
ContentDialog* contentDialog = ContentDialog::getFriendDialog(friendId);
contentDialog->removeFriend(friendId);
}
void FriendWidget::moveToNewGroup()
{
const auto friendId = frnd->getId();
const auto groupId = Core::getInstance()->createGroup();
Core::getInstance()->groupInviteFriend(friendId, groupId);
}
namespace {
void FriendWidget::inviteFriend(uint32_t friendId, const Group* group)
{
Core::getInstance()->groupInviteFriend(friendId, group->getId());
}
namespace
{
std::tuple<CircleWidget*, FriendListWidget*> getCircleAndFriendList(
const Friend* frnd, FriendWidget* fw)
std::tuple<CircleWidget*, FriendListWidget*> getCircleAndFriendList(const Friend* frnd, FriendWidget* fw)
{
const auto pk = frnd->getPublicKey();
const auto circleId = Settings::getInstance().getFriendCircleID(pk);
@ -242,10 +209,11 @@ std::tuple<CircleWidget*, FriendListWidget*> getCircleAndFriendList(
return std::make_tuple(circleWidget, friendList);
}
}
} // namespace
void FriendWidget::moveToNewCircle()
{
const auto frnd = chatroom->getFriend();
CircleWidget* circleWidget;
FriendListWidget* friendList;
std::tie(circleWidget, friendList) = getCircleAndFriendList(frnd, this);
@ -258,7 +226,7 @@ void FriendWidget::moveToNewCircle()
friendList->addCircleWidget(this);
} else {
const auto pk = frnd->getPublicKey();
auto &s = Settings::getInstance();
auto& s = Settings::getInstance();
auto circleId = s.addCircle();
s.setFriendCircleID(pk, circleId);
}
@ -266,6 +234,7 @@ void FriendWidget::moveToNewCircle()
void FriendWidget::removeFromCircle()
{
const auto frnd = chatroom->getFriend();
CircleWidget* circleWidget;
FriendListWidget* friendList;
std::tie(circleWidget, friendList) = getCircleAndFriendList(frnd, this);
@ -286,6 +255,7 @@ void FriendWidget::removeFromCircle()
void FriendWidget::moveToCircle(int newCircleId)
{
const auto frnd = chatroom->getFriend();
const auto pk = frnd->getPublicKey();
const auto oldCircleId = Settings::getInstance().getFriendCircleID(pk);
auto& s = Settings::getInstance();
@ -309,48 +279,47 @@ void FriendWidget::moveToCircle(int newCircleId)
void FriendWidget::changeAutoAccept(bool enable)
{
const auto pk = frnd->getPublicKey();
auto &s = Settings::getInstance();
if (enable) {
const auto oldDir = s.getAutoAcceptDir(pk);
const auto oldDir = chatroom->getAutoAcceptDir();
const auto newDir = QFileDialog::getExistingDirectory(
Q_NULLPTR, tr("Choose an auto accept directory", "popup title"), oldDir);
const auto friendId = frnd->getId();
qDebug() << "Setting auto accept dir for" << friendId << "to" << newDir;
s.setAutoAcceptDir(pk, newDir);
chatroom->setAutoAcceptDir(newDir);
} else {
qDebug() << "not checked";
s.setAutoAcceptDir(pk, "");
chatroom->disableAutoAccept();
}
}
void FriendWidget::showDetails()
{
const QPointer<IAboutFriend> about = new AboutFriend(frnd, &Settings::getInstance());
auto aboutUser = new AboutFriendForm(about, Widget::getInstance());
const auto frnd = chatroom->getFriend();
const auto iabout = new AboutFriend(frnd, &Settings::getInstance());
std::unique_ptr<IAboutFriend> about = std::unique_ptr<IAboutFriend>(iabout);
const auto aboutUser = new AboutFriendForm(std::move(about), Widget::getInstance());
aboutUser->show();
}
void FriendWidget::setAsActiveChatroom()
{
setActive(true);
if (isDefaultAvatar) {
avatar->setPixmap(QPixmap(":img/contact_dark.svg"));
}
}
void FriendWidget::setAsInactiveChatroom()
{
setActive(false);
}
void FriendWidget::setActive(bool active)
{
GenericChatroomWidget::setActive(active);
if (isDefaultAvatar) {
avatar->setPixmap(QPixmap(":img/contact.svg"));
const auto uri = active ? QStringLiteral(":img/contact_dark.svg")
: QStringLiteral(":img/contact.svg");
avatar->setPixmap(QPixmap{uri});
}
}
void FriendWidget::updateStatusLight()
{
// clang-format off
static const QString statuses[] = {
":img/status/online.svg",
":img/status/online_notification.svg",
@ -361,7 +330,9 @@ void FriendWidget::updateStatusLight()
":img/status/offline.svg",
":img/status/offline_notification.svg",
};
// clang-format on
const auto frnd = chatroom->getFriend();
const bool event = frnd->getEventFlag();
const int index = static_cast<int>(frnd->getStatus()) * 2 + event;
statusPic.setPixmap(QPixmap(statuses[index]));
@ -382,6 +353,7 @@ void FriendWidget::updateStatusLight()
QString FriendWidget::getStatusString() const
{
const auto frnd = chatroom->getFriend();
const int status = static_cast<int>(frnd->getStatus());
const bool event = frnd->getEventFlag();
@ -397,11 +369,12 @@ QString FriendWidget::getStatusString() const
const Friend* FriendWidget::getFriend() const
{
return frnd;
return chatroom->getFriend();
}
void FriendWidget::search(const QString& searchString, bool hide)
{
const auto frnd = chatroom->getFriend();
searchName(searchString, hide);
const Settings& s = Settings::getInstance();
const uint32_t circleId = s.getFriendCircleID(frnd->getPublicKey());
@ -413,14 +386,13 @@ void FriendWidget::search(const QString& searchString, bool hide)
void FriendWidget::resetEventFlags()
{
// Hack to avoid edit const Friend. TODO: Repalce on emit
Friend* f = FriendList::findFriend(frnd->getId());
f->setEventFlag(false);
chatroom->resetEventFlags();
}
void FriendWidget::onAvatarChange(uint32_t friendId, const QPixmap& pic)
void FriendWidget::onAvatarChange(const ToxPk& friendPk, const QPixmap& pic)
{
if (friendId != frnd->getId()) {
const auto frnd = chatroom->getFriend();
if (friendPk != frnd->getPublicKey()) {
return;
}
@ -428,9 +400,10 @@ void FriendWidget::onAvatarChange(uint32_t friendId, const QPixmap& pic)
avatar->setPixmap(pic);
}
void FriendWidget::onAvatarRemoved(uint32_t friendId)
void FriendWidget::onAvatarRemoved(const ToxPk& friendPk)
{
if (friendId != frnd->getId()) {
const auto frnd = chatroom->getFriend();
if (friendPk != frnd->getPublicKey()) {
return;
}

View File

@ -19,7 +19,11 @@
#define FRIENDWIDGET_H
#include "genericchatroomwidget.h"
#include "src/core/toxpk.h"
#include <memory>
class FriendChatroom;
class QPixmap;
class MaskablePixmapWidget;
@ -27,7 +31,8 @@ class FriendWidget : public GenericChatroomWidget
{
Q_OBJECT
public:
FriendWidget(const Friend* f, bool compact);
FriendWidget(std::shared_ptr<FriendChatroom> chatform, bool compact);
void contextMenuEvent(QContextMenuEvent* event) override final;
void setAsActiveChatroom() override final;
void setAsInactiveChatroom() override final;
@ -45,9 +50,10 @@ signals:
void contextMenuCalled(QContextMenuEvent* event);
public slots:
void onAvatarChange(uint32_t friendId, const QPixmap& pic);
void onAvatarRemoved(uint32_t friendId);
void onAvatarChange(const ToxPk& friendPk, const QPixmap& pic);
void onAvatarRemoved(const ToxPk& friendPk);
void onContextMenuCalled(QContextMenuEvent* event);
void setActive(bool active);
protected:
virtual void mousePressEvent(QMouseEvent* ev) override;
@ -56,8 +62,6 @@ protected:
private slots:
void removeChatWindow();
void moveToNewGroup();
void inviteFriend(uint32_t friendId, const Group* group);
void moveToNewCircle();
void removeFromCircle();
void moveToCircle(int circleId);
@ -65,7 +69,7 @@ private slots:
void showDetails();
public:
const Friend* frnd;
std::shared_ptr<FriendChatroom> chatroom;
bool isDefaultAvatar;
};

View File

@ -36,6 +36,7 @@ class GenericChatroomWidget : public GenericChatItemWidget
public:
explicit GenericChatroomWidget(bool compact, QWidget* parent = 0);
public slots:
virtual void setAsActiveChatroom() = 0;
virtual void setAsInactiveChatroom() = 0;
virtual void updateStatusLight() = 0;
@ -53,7 +54,6 @@ public:
virtual bool eventFilter(QObject*, QEvent*) final override;
bool isActive();
void setActive(bool active);
void setName(const QString& name);
void setStatusMsg(const QString& status);
@ -62,7 +62,6 @@ public:
void reloadTheme();
public slots:
void activate();
void compactChange(bool compact);
@ -75,10 +74,10 @@ protected:
void mouseReleaseEvent(QMouseEvent* event) override;
void enterEvent(QEvent* e) override;
void leaveEvent(QEvent* e) override;
QPoint dragStartPos;
void setActive(bool active);
protected:
QPoint dragStartPos;
QColor lastColor;
QHBoxLayout* mainLayout = nullptr;
QVBoxLayout* textLayout = nullptr;

View File

@ -40,22 +40,24 @@
#include "src/widget/translator.h"
#include "tool/croppinglabel.h"
GroupWidget::GroupWidget(int groupId, const QString& name, bool compact)
GroupWidget::GroupWidget(std::shared_ptr<GroupChatroom> chatroom, bool compact)
: GenericChatroomWidget(compact)
, groupId{groupId}
, groupId{static_cast<int>(chatroom->getGroup()->getId())}
, chatroom{chatroom}
{
avatar->setPixmap(Style::scaleSvgImage(":img/group.svg", avatar->width(), avatar->height()));
statusPic.setPixmap(QPixmap(":img/status/online.svg"));
statusPic.setMargin(3);
nameLabel->setText(name);
Group* g = chatroom->getGroup();
nameLabel->setText(g->getName());
updateUserCount();
setAcceptDrops(true);
Group* g = GroupList::findGroup(groupId);
connect(g, &Group::titleChanged, this, &GroupWidget::updateTitle);
connect(g, &Group::userListChanged, this, &GroupWidget::updateUserCount);
connect(nameLabel, &CroppingLabel::editFinished, this, &GroupWidget::setTitle);
connect(nameLabel, &CroppingLabel::editFinished, g, &Group::setName);
Translator::registerHandler(std::bind(&GroupWidget::retranslateUi, this), this);
}
@ -64,12 +66,6 @@ GroupWidget::~GroupWidget()
Translator::unregister(this);
}
void GroupWidget::setTitle(const QString& newName)
{
Group* g = GroupList::findGroup(groupId);
g->setName(newName);
}
void GroupWidget::updateTitle(uint32_t groupId, const QString& author, const QString& newName)
{
Q_UNUSED(groupId);
@ -79,8 +75,9 @@ void GroupWidget::updateTitle(uint32_t groupId, const QString& author, const QSt
void GroupWidget::contextMenuEvent(QContextMenuEvent* event)
{
if (!active)
if (!active) {
setBackgroundRole(QPalette::Highlight);
}
installEventFilter(this); // Disable leave event.
@ -89,14 +86,17 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent* event)
QAction* openChatWindow = nullptr;
QAction* removeChatWindow = nullptr;
// TODO: Move to model
ContentDialog* contentDialog = ContentDialog::getGroupDialog(groupId);
bool notAlone = contentDialog != nullptr && contentDialog->chatroomWidgetCount() > 1;
const bool notAlone = contentDialog != nullptr && contentDialog->chatroomWidgetCount() > 1;
if (contentDialog == nullptr || notAlone)
if (contentDialog == nullptr || notAlone) {
openChatWindow = menu.addAction(tr("Open chat in new window"));
}
if (contentDialog && contentDialog->hasGroupWidget(groupId, this))
if (contentDialog && contentDialog->hasGroupWidget(groupId, this)) {
removeChatWindow = menu.addAction(tr("Remove chat from this window"));
}
menu.addSeparator();
@ -107,8 +107,9 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent* event)
removeEventFilter(this);
if (!active)
if (!active) {
setBackgroundRole(QPalette::Window);
}
if (!selectedItem) {
return;
@ -119,6 +120,7 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent* event)
} else if (selectedItem == openChatWindow) {
emit newWindowOpened(this);
} else if (selectedItem == removeChatWindow) {
// TODO: move to model
ContentDialog* contentDialog = ContentDialog::getGroupDialog(groupId);
contentDialog->removeGroup(groupId);
} else if (selectedItem == setTitle) {
@ -128,16 +130,18 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent* event)
void GroupWidget::mousePressEvent(QMouseEvent* ev)
{
if (ev->button() == Qt::LeftButton)
if (ev->button() == Qt::LeftButton) {
dragStartPos = ev->pos();
}
GenericChatroomWidget::mousePressEvent(ev);
}
void GroupWidget::mouseMoveEvent(QMouseEvent* ev)
{
if (!(ev->buttons() & Qt::LeftButton))
if (!(ev->buttons() & Qt::LeftButton)) {
return;
}
if ((dragStartPos - ev->pos()).manhattanLength() > QApplication::startDragDistance()) {
QMimeData* mdata = new QMimeData;
@ -152,14 +156,8 @@ void GroupWidget::mouseMoveEvent(QMouseEvent* ev)
void GroupWidget::updateUserCount()
{
Group* g = GroupList::findGroup(groupId);
if (g) {
int peersCount = g->getPeersCount();
if (peersCount == 1)
statusMessageLabel->setText(tr("1 user in chat"));
else
statusMessageLabel->setText(tr("%1 users in chat").arg(peersCount));
}
int peersCount = chatroom->getGroup()->getPeersCount();
statusMessageLabel->setText(tr("%n user(s) in chat", "", peersCount));
}
void GroupWidget::setAsActiveChatroom()
@ -176,25 +174,24 @@ void GroupWidget::setAsInactiveChatroom()
void GroupWidget::updateStatusLight()
{
Group* g = GroupList::findGroup(groupId);
Group* g = chatroom->getGroup();
if (!g->getEventFlag()) {
statusPic.setPixmap(QPixmap(":img/status/online.svg"));
statusPic.setMargin(3);
} else {
if (g->getEventFlag()) {
statusPic.setPixmap(QPixmap(":img/status/online_notification.svg"));
statusPic.setMargin(1);
} else {
statusPic.setPixmap(QPixmap(":img/status/online.svg"));
statusPic.setMargin(3);
}
}
QString GroupWidget::getStatusString() const
{
Group* g = GroupList::findGroup(groupId);
if (!g->getEventFlag())
return "Online";
else
return "New Message";
if (chatroom->hasNewMessage()) {
return tr("New Message");
} else {
return tr("Online");
}
}
void GroupWidget::editName()
@ -202,49 +199,51 @@ void GroupWidget::editName()
nameLabel->editBegin();
}
// TODO: Remove
Group* GroupWidget::getGroup() const
{
return GroupList::findGroup(groupId);
return chatroom->getGroup();
}
void GroupWidget::resetEventFlags()
{
Group* g = GroupList::findGroup(groupId);
g->setEventFlag(false);
g->setMentionedFlag(false);
chatroom->resetEventFlags();
}
void GroupWidget::dragEnterEvent(QDragEnterEvent* ev)
{
ToxId toxId = ToxId(ev->mimeData()->text());
Friend* frnd = FriendList::findFriend(toxId.getPublicKey());
if (frnd)
// TODO: Send ToxPk in mimeData
const ToxId toxId = ToxId(ev->mimeData()->text());
const ToxPk pk = toxId.getPublicKey();
if (chatroom->friendExists(pk)) {
ev->acceptProposedAction();
}
if (!active)
if (!active) {
setBackgroundRole(QPalette::Highlight);
}
}
void GroupWidget::dragLeaveEvent(QDragLeaveEvent*)
{
if (!active)
if (!active) {
setBackgroundRole(QPalette::Window);
}
}
void GroupWidget::dropEvent(QDropEvent* ev)
{
ToxId toxId = ToxId(ev->mimeData()->text());
Friend* frnd = FriendList::findFriend(toxId.getPublicKey());
if (!frnd)
const ToxId toxId = ToxId(ev->mimeData()->text());
const ToxPk pk = toxId.getPublicKey();
if (!chatroom->friendExists(pk)) {
return;
int friendId = frnd->getId();
if (frnd->getStatus() != Status::Offline) {
Core::getInstance()->groupInviteFriend(friendId, groupId);
}
if (!active)
chatroom->inviteFriend(pk);
if (!active) {
setBackgroundRole(QPalette::Window);
}
}
void GroupWidget::setName(const QString& name)

View File

@ -22,11 +22,15 @@
#include "genericchatroomwidget.h"
#include "src/model/chatroom/groupchatroom.h"
#include <memory>
class GroupWidget final : public GenericChatroomWidget
{
Q_OBJECT
public:
GroupWidget(int GroupId, const QString& Name, bool compact);
GroupWidget(std::shared_ptr<GroupChatroom> chatroom, bool compact);
~GroupWidget();
void setAsInactiveChatroom() final override;
void setAsActiveChatroom() final override;
@ -51,12 +55,14 @@ protected:
private slots:
void retranslateUi();
void setTitle(const QString& newName);
void updateTitle(uint32_t groupId, const QString& author, const QString& newName);
void updateUserCount();
public:
int groupId;
private:
std::shared_ptr<GroupChatroom> chatroom;
};
#endif // GROUPWIDGET_H

View File

@ -35,9 +35,10 @@ public slots:
void setEditable(bool editable);
void setElideMode(Qt::TextElideMode elide);
void setText(const QString& text);
QString fullText();
public slots:
void setText(const QString& text);
void minimizeMaximumWidth();
signals:

View File

@ -44,7 +44,6 @@
#include "friendlistwidget.h"
#include "friendwidget.h"
#include "groupwidget.h"
#include "src/model/groupinvite.h"
#include "maskablepixmapwidget.h"
#include "splitterrestorer.h"
#include "systemtrayicon.h"
@ -52,11 +51,15 @@
#include "src/audio/audio.h"
#include "src/core/core.h"
#include "src/core/coreav.h"
#include "src/model/chatroom/friendchatroom.h"
#include "src/model/chatroom/groupchatroom.h"
#include "src/model/friend.h"
#include "src/friendlist.h"
#include "src/model/group.h"
#include "src/model/profile/profileinfo.h"
#include "src/grouplist.h"
#include "src/model/friend.h"
#include "src/model/group.h"
#include "src/model/groupinvite.h"
#include "src/model/profile/profileinfo.h"
#include "src/net/autoupdate.h"
#include "src/nexus.h"
#include "src/persistence/offlinemsgengine.h"
@ -528,11 +531,13 @@ Widget::~Widget()
delete icon;
delete profileForm;
delete profileInfo;
delete addFriendForm;
delete groupInviteForm;
delete filesForm;
delete timer;
delete contentLayout;
delete settingsWidget;
FriendList::clear();
GroupList::clear();
@ -980,11 +985,13 @@ void Widget::addFriend(uint32_t friendId, const ToxPk& friendPk)
s.updateFriendAddress(friendPk.toString());
Friend* newfriend = FriendList::addFriend(friendId, friendPk);
bool compact = s.getCompactLayout();
FriendWidget* widget = new FriendWidget(newfriend, compact);
History* history = Nexus::getProfile()->getHistory();
ChatForm* friendForm = new ChatForm(newfriend, history);
std::shared_ptr<FriendChatroom> chatroom(new FriendChatroom(newfriend));
const auto compact = Settings::getInstance().getCompactLayout();
auto widget = new FriendWidget(chatroom, compact);
auto history = Nexus::getProfile()->getHistory();
auto friendForm = new ChatForm(newfriend, history);
friendChatrooms[friendId] = chatroom;
friendWidgets[friendId] = widget;
chatForms[friendId] = friendForm;
@ -1020,7 +1027,7 @@ void Widget::addFriend(uint32_t friendId, const ToxPk& friendPk)
QPixmap avatar = Nexus::getProfile()->loadAvatar(friendPk);
if (!avatar.isNull()) {
friendForm->onAvatarChange(friendId, avatar);
widget->onAvatarChange(friendId, avatar);
widget->onAvatarChange(friendPk, avatar);
}
FilterCriteria filter = getFilterCriteria();
@ -1240,8 +1247,9 @@ void Widget::addFriendDialog(const Friend* frnd, ContentDialog* dialog)
onAddClicked();
}
ChatForm* form = chatForms[friendId];
FriendWidget* friendWidget = dialog->addFriend(frnd, form);
auto form = chatForms[friendId];
auto chatroom = friendChatrooms[friendId];
FriendWidget* friendWidget = dialog->addFriend(chatroom, form);
friendWidget->setStatusMsg(widget->getStatusMsg());
@ -1251,9 +1259,8 @@ void Widget::addFriendDialog(const Friend* frnd, ContentDialog* dialog)
auto widgetRemoveFriend = static_cast<void (Widget::*)(int)>(&Widget::removeFriend);
#endif
connect(friendWidget, &FriendWidget::removeFriend, this, widgetRemoveFriend);
connect(friendWidget, &FriendWidget::middleMouseClicked, dialog, [=]() {
dialog->removeFriend(friendId);
});
connect(friendWidget, &FriendWidget::middleMouseClicked, dialog,
[=]() { dialog->removeFriend(friendId); });
connect(friendWidget, &FriendWidget::copyFriendIdToClipboard, this,
&Widget::copyFriendIdToClipboard);
@ -1263,16 +1270,14 @@ void Widget::addFriendDialog(const Friend* frnd, ContentDialog* dialog)
connect(friendWidget, &FriendWidget::contextMenuCalled, widget,
[=](QContextMenuEvent* event) { emit widget->contextMenuCalled(event); });
connect(friendWidget, &FriendWidget::chatroomWidgetClicked,
[=](GenericChatroomWidget* w) {
Q_UNUSED(w);
emit widget->chatroomWidgetClicked(widget);
});
connect(friendWidget, &FriendWidget::newWindowOpened,
[=](GenericChatroomWidget* w) {
Q_UNUSED(w);
emit widget->newWindowOpened(widget);
});
connect(friendWidget, &FriendWidget::chatroomWidgetClicked, [=](GenericChatroomWidget* w) {
Q_UNUSED(w);
emit widget->chatroomWidgetClicked(widget);
});
connect(friendWidget, &FriendWidget::newWindowOpened, [=](GenericChatroomWidget* w) {
Q_UNUSED(w);
emit widget->newWindowOpened(widget);
});
// FIXME: emit should be removed
emit widget->chatroomWidgetClicked(widget);
@ -1282,7 +1287,7 @@ void Widget::addFriendDialog(const Friend* frnd, ContentDialog* dialog)
QPixmap avatar = Nexus::getProfile()->loadAvatar(frnd->getPublicKey());
if (!avatar.isNull()) {
friendWidget->onAvatarChange(friendId, avatar);
friendWidget->onAvatarChange(frnd->getPublicKey(), avatar);
}
}
@ -1298,7 +1303,8 @@ void Widget::addGroupDialog(Group* group, ContentDialog* dialog)
}
auto chatForm = groupChatForms[groupId];
auto groupWidget = dialog->addGroup(group, chatForm);
auto chatroom = groupChatrooms[groupId];
auto groupWidget = dialog->addGroup(chatroom, chatForm);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0))
auto removeGroup = QOverload<int>::of(&Widget::removeGroup);
#else
@ -1306,25 +1312,22 @@ void Widget::addGroupDialog(Group* group, ContentDialog* dialog)
#endif
connect(groupWidget, &GroupWidget::removeGroup, this, removeGroup);
connect(groupWidget, &GroupWidget::chatroomWidgetClicked, chatForm, &GroupChatForm::focusInput);
connect(groupWidget, &GroupWidget::middleMouseClicked, dialog, [=]() {
dialog->removeGroup(groupId);
});
connect(groupWidget, &GroupWidget::middleMouseClicked, dialog,
[=]() { dialog->removeGroup(groupId); });
connect(groupWidget, &GroupWidget::chatroomWidgetClicked, chatForm, &ChatForm::focusInput);
// Signal transmission from the created `groupWidget` (which shown in
// ContentDialog) to the `widget` (which shown in main widget)
// FIXME: emit should be removed
connect(groupWidget, &GroupWidget::chatroomWidgetClicked,
[=](GenericChatroomWidget* w) {
Q_UNUSED(w);
emit widget->chatroomWidgetClicked(widget);
});
connect(groupWidget, &GroupWidget::chatroomWidgetClicked, [=](GenericChatroomWidget* w) {
Q_UNUSED(w);
emit widget->chatroomWidgetClicked(widget);
});
connect(groupWidget, &GroupWidget::newWindowOpened,
[=](GenericChatroomWidget* w) {
Q_UNUSED(w);
emit widget->newWindowOpened(widget);
});
connect(groupWidget, &GroupWidget::newWindowOpened, [=](GenericChatroomWidget* w) {
Q_UNUSED(w);
emit widget->newWindowOpened(widget);
});
// FIXME: emit should be removed
emit widget->chatroomWidgetClicked(widget);
@ -1915,11 +1918,12 @@ Group* Widget::createGroup(int groupId)
bool enabled = coreAv->isGroupAvEnabled(groupId);
Group* newgroup = GroupList::addGroup(groupId, groupName, enabled, core->getUsername());
bool compact = Settings::getInstance().getCompactLayout();
GroupWidget* widget = new GroupWidget(groupId, groupName, compact);
groupWidgets[groupId] = widget;
std::shared_ptr<GroupChatroom> chatroom(new GroupChatroom(newgroup));
const auto compact = Settings::getInstance().getCompactLayout();
auto widget = new GroupWidget(chatroom, compact);
auto form = new GroupChatForm(newgroup);
groupWidgets[groupId] = widget;
groupChatrooms[groupId] = chatroom;
groupChatForms[groupId] = form;
contactListWidget->addGroupWidget(widget);
@ -1934,9 +1938,7 @@ Group* Widget::createGroup(int groupId)
auto widgetRemoveGroup = static_cast<void (Widget::*)(int)>(&Widget::removeGroup);
#endif
connect(widget, &GroupWidget::removeGroup, this, widgetRemoveGroup);
connect(widget, &GroupWidget::middleMouseClicked, this, [=]() {
removeGroup(groupId);
});
connect(widget, &GroupWidget::middleMouseClicked, this, [=]() { removeGroup(groupId); });
connect(widget, &GroupWidget::chatroomWidgetClicked, form, &ChatForm::focusInput);
connect(form, &GroupChatForm::sendMessage, core, &Core::sendGroupMessage);
connect(form, &GroupChatForm::sendAction, core, &Core::sendGroupAction);
@ -2275,8 +2277,7 @@ inline QIcon Widget::prepareIcon(QString path, int w, int h)
}
desktop = desktop.toLower();
if (desktop == "xfce" || desktop.contains("gnome") || desktop == "mate"
|| desktop == "x-cinnamon") {
if (desktop == "xfce" || desktop.contains("gnome") || desktop == "mate" || desktop == "x-cinnamon") {
if (w > 0 && h > 0) {
QSvgRenderer renderer(path);

View File

@ -48,11 +48,13 @@ class ContentLayout;
class Core;
class FilesForm;
class Friend;
class FriendChatroom;
class FriendListWidget;
class FriendWidget;
class GenericChatroomWidget;
class Group;
class GroupChatForm;
class GroupChatroom;
class GroupInvite;
class GroupInviteForm;
class GroupWidget;
@ -303,9 +305,12 @@ private:
unsigned int unreadGroupInvites;
int icon_size;
QMap<uint32_t, GroupWidget*> groupWidgets;
QMap<uint32_t, FriendWidget*> friendWidgets;
QMap<uint32_t, std::shared_ptr<FriendChatroom>> friendChatrooms;
QMap<uint32_t, ChatForm*> chatForms;
QMap<uint32_t, GroupWidget*> groupWidgets;
QMap<uint32_t, std::shared_ptr<GroupChatroom>> groupChatrooms;
QMap<uint32_t, GroupChatForm*> groupChatForms;
#ifdef Q_OS_MAC

76
translations/bg.ts vendored
View File

@ -81,39 +81,39 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Enables the experimental audio backend with echo cancelling support, needs qTox restart to take effect.</source>
<translation type="unfinished"></translation>
<translation>Включва експериментален звуков процесор с потискане на ехото, изисква рестарт на qTox.</translation>
</message>
<message>
<source>Enable experimental audio backend</source>
<translation type="unfinished"></translation>
<translation>Включва експериментален звуков процесор</translation>
</message>
<message>
<source>Audio quality</source>
<translation type="unfinished"></translation>
<translation>Качество на звука</translation>
</message>
<message>
<source>Transmitted audio quality. Lower this setting if your bandwidth is not high enough or if you want to lower the internet usage.</source>
<translation type="unfinished"></translation>
<translation>Качество на предавания звук. Намалете тази настройка ако нямате достатъчно широколентова връзка или ако искате да намалите използването на интернет.</translation>
</message>
<message>
<source>High (64 kbps)</source>
<translation type="unfinished"></translation>
<translation>Високо (64 kбита/с)</translation>
</message>
<message>
<source>Medium (32 kbps)</source>
<translation type="unfinished"></translation>
<translation>Средно (32 kбита/с)</translation>
</message>
<message>
<source>Low (16 kbps)</source>
<translation type="unfinished"></translation>
<translation>Ниско (16 kбита/с)</translation>
</message>
<message>
<source>Very low (8 kbps)</source>
<translation type="unfinished"></translation>
<translation>Много ниско (8 kбита/с)</translation>
</message>
<message>
<source>Threshold</source>
<translation type="unfinished"></translation>
<translation>Прагова стойност</translation>
</message>
</context>
<context>
@ -198,7 +198,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Public key:</source>
<translation type="unfinished">Публичен ключ:</translation>
<translation>Публичен ключ:</translation>
</message>
<message>
<source>Used aliases:</source>
@ -242,7 +242,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Automatically accept group chat invitations from this contact if set.</source>
<translation type="unfinished"></translation>
<translation>Автоматично приемане на покани за групов чат от този контакт ако е включено.</translation>
</message>
<message>
<source>Auto accept group invites</source>
@ -355,7 +355,7 @@ which may lead to problems with video calls.</source>
<message>
<source>%1 Tox ID is invalid or does not exist</source>
<comment>Toxme error</comment>
<translation type="unfinished"></translation>
<translation>%1 Tox ID е невалиден или не съществува</translation>
</message>
<message>
<source>You can&apos;t add yourself as a friend!</source>
@ -364,24 +364,24 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Open contact list</source>
<translation type="unfinished"></translation>
<translation>Отваряне на списък с контакти</translation>
</message>
<message>
<source>Couldn&apos;t open file</source>
<translation type="unfinished"></translation>
<translation>Не може да се отвори файла</translation>
</message>
<message>
<source>Couldn&apos;t open the contact file</source>
<extracomment>Error message when trying to open a contact list file to import</extracomment>
<translation type="unfinished"></translation>
<translation>Не може да се отвори файла с контакти</translation>
</message>
<message>
<source>Invalid file</source>
<translation type="unfinished"></translation>
<translation>Невалиден файл</translation>
</message>
<message>
<source>We couldn&apos;t find any contacts to import in this file!</source>
<translation type="unfinished"></translation>
<translation>Не са намерени контакти за зареждане в този файл!</translation>
</message>
<message>
<source>Tox ID</source>
@ -401,11 +401,11 @@ which may lead to problems with video calls.</source>
<message>
<source>Open</source>
<extracomment>Button to choose a file with a list of contacts to import</extracomment>
<translation type="unfinished"></translation>
<translation>Отваряне</translation>
</message>
<message>
<source>Send friend requests</source>
<translation type="unfinished"></translation>
<translation>Изпращане на заявка за приятелство</translation>
</message>
<message>
<source>%1 here! Tox me maybe?</source>
@ -414,7 +414,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Import a list of contacts, one Tox ID per line</source>
<translation type="unfinished"></translation>
<translation>Зареждане на списък с контакти, по един Tox ID на ред</translation>
</message>
<message numerus="yes">
<source>Ready to import %n contact(s), click send to confirm</source>
@ -426,7 +426,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Import contacts</source>
<translation type="unfinished"></translation>
<translation>Зареждане на контакти</translation>
</message>
</context>
<context>
@ -658,7 +658,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Export to file</source>
<translation type="unfinished"></translation>
<translation>Изнасяне към файл</translation>
</message>
<message>
<source>Save chat log</source>
@ -666,7 +666,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Call with %1 ended unexpectedly. %2</source>
<translation type="unfinished"></translation>
<translation>Връзката с %1 завърши неочаквано. %2</translation>
</message>
</context>
<context>
@ -929,7 +929,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Never</source>
<translation type="unfinished"></translation>
<translation>Никога</translation>
</message>
</context>
<context>
@ -1297,7 +1297,7 @@ instead of system taskbar.</source>
<name>GroupInviteWidget</name>
<message>
<source>Invited by %1 on %2 at %3.</source>
<translation>Поканен от %1 на %2 в %3</translation>
<translation>Поканен от %1на %2 в %3.</translation>
</message>
<message>
<source>Join</source>
@ -1581,7 +1581,7 @@ Profile does not contain your history.</source>
</message>
<message>
<source>%1 messages</source>
<translation type="unfinished"></translation>
<translation>%1 съобщения</translation>
</message>
</context>
<context>
@ -1803,7 +1803,7 @@ You may want to create one.</source>
</message>
<message>
<source>Contact search input for known friends</source>
<translation type="unfinished"></translation>
<translation>Входно поле за търсене на известни приятели</translation>
</message>
<message>
<source>Sorting and visibility</source>
@ -1961,18 +1961,18 @@ If you are getting spammed with friend requests, change the NoSpam.</source>
</message>
<message>
<source>BlackList</source>
<translation type="unfinished"></translation>
<translation>Черен списък</translation>
</message>
<message>
<source>Filter group message by group member&apos;s public key. Put public key here, one per line.</source>
<translation type="unfinished"></translation>
<translation>Филтриране на групови съобщения по публичен ключ на членовете. Поставете побличните ключове тук, по един на ред.</translation>
</message>
</context>
<context>
<name>Profile</name>
<message>
<source>Failed to derive key from password, the profile won&apos;t use the new password.</source>
<translation type="unfinished"></translation>
<translation>Неуспешно произвеждане на ключ от парола, профилът няма за използва новата парола.</translation>
</message>
<message>
<source>Couldn&apos;t change password on the database, it might be corrupted or use the old password.</source>
@ -2128,7 +2128,7 @@ Please use another image.</source>
</message>
<message>
<source>Couldn&apos;t change password</source>
<translation type="unfinished"></translation>
<translation>Неуспешна смяна на парола</translation>
</message>
<message>
<source>This bunch of characters tells other Tox clients how to contact you.
@ -2139,7 +2139,7 @@ This ID includes the NoSpam code (in blue), and the checksum (in gray).</source>
</message>
<message>
<source>Empty path is unavaliable</source>
<translation type="unfinished"></translation>
<translation>Празен път не е достъпен</translation>
</message>
<message>
<source>Failed to rename</source>
@ -2155,15 +2155,15 @@ This ID includes the NoSpam code (in blue), and the checksum (in gray).</source>
</message>
<message>
<source>Empty name</source>
<translation type="unfinished"></translation>
<translation>Празно име</translation>
</message>
<message>
<source>Empty name is unavaliable</source>
<translation type="unfinished"></translation>
<translation>Празно име не е достъпно</translation>
</message>
<message>
<source>Empty path</source>
<translation type="unfinished"></translation>
<translation>Празен път</translation>
</message>
<message>
<source>Couldn&apos;t change password on the database, it might be corrupted or use the old password.</source>
@ -2409,11 +2409,11 @@ It will be installed when qTox restarts.</source>
</message>
<message>
<source>Reformatting text in progress..</source>
<translation type="unfinished"></translation>
<translation>Изпълнява се преформатиране на текст..</translation>
</message>
<message>
<source>Starts new instance and opens the login screen.</source>
<translation type="unfinished"></translation>
<translation>Стартира ново копие и отваря екрана за вписване.</translation>
</message>
</context>
<context>
@ -2709,7 +2709,7 @@ It will be installed when qTox restarts.</source>
</message>
<message>
<source>Use identicons instead of empty avatars</source>
<translation type="unfinished"></translation>
<translation>Използване на идентикони вместо празни аватари</translation>
</message>
</context>
<context>

94
translations/ko.ts vendored
View File

@ -33,7 +33,7 @@
</message>
<message>
<source>Playback device</source>
<translation type="unfinished"></translation>
<translation>재생 기기</translation>
</message>
<message>
<source>Use slider to set volume of your speakers.</source>
@ -87,7 +87,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Audio quality</source>
<translation type="unfinished"></translation>
<translation>오디오 음질</translation>
</message>
<message>
<source>Transmitted audio quality. Lower this setting if your bandwidth is not high enough or if you want to lower the internet usage.</source>
@ -95,23 +95,23 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>High (64 kbps)</source>
<translation type="unfinished"></translation>
<translation>높음(64kbps)</translation>
</message>
<message>
<source>Medium (32 kbps)</source>
<translation type="unfinished"></translation>
<translation>중간(32kbps)</translation>
</message>
<message>
<source>Low (16 kbps)</source>
<translation type="unfinished"></translation>
<translation>낮음 (16kbps)</translation>
</message>
<message>
<source>Very low (8 kbps)</source>
<translation type="unfinished"></translation>
<translation>아주 낮음 (8kbps)</translation>
</message>
<message>
<source>Threshold</source>
<translation type="unfinished"></translation>
<translation>한계점</translation>
</message>
</context>
<context>
@ -135,7 +135,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>You are using qTox version %1.</source>
<translation>qTox 버젼 %1.</translation>
<translation>qTox 버젼 %1 사용중.</translation>
</message>
<message>
<source>Commit hash: %1</source>
@ -184,7 +184,7 @@ which may lead to problems with video calls.</source>
<name>AboutFriendForm</name>
<message>
<source>Dialog</source>
<translation type="unfinished"></translation>
<translation>대화</translation>
</message>
<message>
<source>username</source>
@ -232,11 +232,11 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Audio</source>
<translation type="unfinished">오디오</translation>
<translation>오디오</translation>
</message>
<message>
<source>Audio + Video</source>
<translation type="unfinished">오디오 + 비디오</translation>
<translation>오디오 + 비디오</translation>
</message>
<message>
<source>Automatically accept group chat invitations from this contact if set.</source>
@ -244,7 +244,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Auto accept group invites</source>
<translation type="unfinished"></translation>
<translation>그룹 초대를 받아드립니다.</translation>
</message>
<message>
<source>Remove history (operation can not be undone!)</source>
@ -264,11 +264,11 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>History removed</source>
<translation type="unfinished">기록 삭제되었습니다</translation>
<translation>기록 삭제</translation>
</message>
<message>
<source>Chat history with %1 removed!</source>
<translation type="unfinished">%1 채팅기록이 삭제되었습니다!</translation>
<translation>%1 채팅기록이 삭제되었습니다!</translation>
</message>
<message>
<source>Choose an auto accept directory</source>
@ -357,7 +357,7 @@ which may lead to problems with video calls.</source>
<message>
<source>You can&apos;t add yourself as a friend!</source>
<extracomment>When trying to add your own Tox ID as friend</extracomment>
<translation type="unfinished">자기 자신을 친구로 추가할 수 없습니다!</translation>
<translation>자신을 친구로 추가할 수 없습니다!</translation>
</message>
<message>
<source>Open contact list</source>
@ -365,7 +365,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Couldn&apos;t open file</source>
<translation type="unfinished"></translation>
<translation>파일을 열 수 없습니다</translation>
</message>
<message>
<source>Couldn&apos;t open the contact file</source>
@ -393,16 +393,16 @@ which may lead to problems with video calls.</source>
<message>
<source>Message</source>
<extracomment>The message you send in friend requests</extracomment>
<translation type="unfinished">메시지</translation>
<translation>메시지</translation>
</message>
<message>
<source>Open</source>
<extracomment>Button to choose a file with a list of contacts to import</extracomment>
<translation type="unfinished"></translation>
<translation>열기</translation>
</message>
<message>
<source>Send friend requests</source>
<translation type="unfinished"></translation>
<translation>친구 요청을 보냄</translation>
</message>
<message>
<source>%1 here! Tox me maybe?</source>
@ -475,7 +475,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Save File</source>
<translation>파일보관</translation>
<translation>파일저장</translation>
</message>
<message>
<source>Logs (*.log)</source>
@ -499,7 +499,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Portable</source>
<translation type="unfinished"></translation>
<translation>이동가능</translation>
</message>
<message>
<source>Connection Settings</source>
@ -584,7 +584,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Bad idea</source>
<translation type="unfinished"></translation>
<translation>좋지않은 의견</translation>
</message>
<message>
<source>%1 calling</source>
@ -606,7 +606,7 @@ which may lead to problems with video calls.</source>
<message>
<source>qTox wasn&apos;t able to save the screenshot</source>
<translatorcomment>laut Duden ist Screenshot schon deutsch</translatorcomment>
<translation type="unfinished"></translation>
<translation>스크린샷을 저장할 수 없었습니다.</translation>
</message>
<message>
<source>Call with %1 ended. %2</source>
@ -646,7 +646,7 @@ which may lead to problems with video calls.</source>
<message>
<source>online</source>
<comment>contact status</comment>
<translation type="unfinished"></translation>
<translation>온라인</translation>
</message>
<message>
<source>%1 is now %2</source>
@ -659,7 +659,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Save chat log</source>
<translation type="unfinished"></translation>
<translation>채팅 기록을 저장</translation>
</message>
<message>
<source>Call with %1 ended unexpectedly. %2</source>
@ -674,7 +674,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Start audio call</source>
<translation type="unfinished"></translation>
<translation>음성통화 시작</translation>
</message>
<message>
<source>End audio call</source>
@ -694,7 +694,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Start video call</source>
<translation type="unfinished"></translation>
<translation>영상통화 시작</translation>
</message>
<message>
<source>End video call</source>
@ -718,7 +718,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Mute call</source>
<translation type="unfinished"></translation>
<translation>음소거 전화</translation>
</message>
<message>
<source>Microphone can be muted only during a call</source>
@ -795,12 +795,12 @@ which may lead to problems with video calls.</source>
<message>
<source>Your message is too long!</source>
<comment>Error while sending friendship request</comment>
<translation type="unfinished">제한된 메시지 길이를 초과했습니다!</translation>
<translation>제한된 메시지 길이를 초과했습니다!</translation>
</message>
<message>
<source>Friend is already added</source>
<comment>Error while sending friendship request</comment>
<translation type="unfinished">이미 추가된 친구입니다</translation>
<translation>이미 추가된 친구입니다</translation>
</message>
</context>
<context>
@ -895,7 +895,7 @@ which may lead to problems with video calls.</source>
<message>
<source>Transferred Files</source>
<comment>&quot;Headline&quot; of the window</comment>
<translation type="unfinished"></translation>
<translation>전송 된 파일</translation>
</message>
<message>
<source>Downloads</source>
@ -910,23 +910,23 @@ which may lead to problems with video calls.</source>
<name>FriendListWidget</name>
<message>
<source>Today</source>
<translation type="unfinished">오늘</translation>
<translation>오늘</translation>
</message>
<message>
<source>Yesterday</source>
<translation type="unfinished">어제</translation>
<translation>어제</translation>
</message>
<message>
<source>Last 7 days</source>
<translation type="unfinished">지난 7일</translation>
<translation>지난 7일</translation>
</message>
<message>
<source>This month</source>
<translation type="unfinished">이달</translation>
<translation> </translation>
</message>
<message>
<source>Older than 6 Months</source>
<translation type="unfinished">6개월 이전</translation>
<translation>6개월 이전</translation>
</message>
<message>
<source>Never</source>
@ -2726,23 +2726,23 @@ It will be installed when qTox restarts.</source>
</message>
<message>
<source>File</source>
<translation type="unfinished"></translation>
<translation>파일</translation>
</message>
<message>
<source>Edit Profile</source>
<translation type="unfinished"></translation>
<translation>프로필 편집</translation>
</message>
<message>
<source>Change Status</source>
<translation type="unfinished"></translation>
<translation>상태 바꾸기</translation>
</message>
<message>
<source>Log out</source>
<translation type="unfinished"></translation>
<translation>로그아웃</translation>
</message>
<message>
<source>Edit</source>
<translation type="unfinished"></translation>
<translation>편집</translation>
</message>
<message>
<source>Logout</source>
@ -2772,7 +2772,7 @@ It will be installed when qTox restarts.</source>
</message>
<message>
<source>Previous Conversation</source>
<translation type="unfinished"></translation>
<translation>이전의 대화</translation>
</message>
<message>
<source>Executable file</source>
@ -2803,7 +2803,7 @@ It will be installed when qTox restarts.</source>
<message>
<source>&lt;Empty&gt;</source>
<comment>Placeholder when someone&apos;s name in a group chat is empty</comment>
<translation type="unfinished"></translation>
<translation>비었습니다</translation>
</message>
<message>
<source>Message failed to send</source>
@ -2874,17 +2874,17 @@ It will be installed when qTox restarts.</source>
<message>
<source>Add friend</source>
<comment>title of the window</comment>
<translation type="unfinished"></translation>
<translation>친구 추가</translation>
</message>
<message>
<source>Group invites</source>
<comment>title of the window</comment>
<translation type="unfinished"></translation>
<translation>그룹 초대</translation>
</message>
<message>
<source>File transfers</source>
<comment>title of the window</comment>
<translation type="unfinished"></translation>
<translation>파일 이동</translation>
</message>
<message>
<source>Settings</source>
@ -2894,7 +2894,7 @@ It will be installed when qTox restarts.</source>
<message>
<source>My profile</source>
<comment>title of the window</comment>
<translation type="unfinished"></translation>
<translation>내 프로필</translation>
</message>
</context>
</TS>

94
translations/sv.ts vendored
View File

@ -113,7 +113,7 @@ vilket kan leda till problem med videosamtal.</translation>
</message>
<message>
<source>Threshold</source>
<translation type="unfinished"></translation>
<translation>Tröskel</translation>
</message>
</context>
<context>
@ -169,7 +169,7 @@ vilket kan leda till problem med videosamtal.</translation>
<message>
<source>bug-tracker</source>
<comment>Replaces `%1` in the `A list of all known…`</comment>
<translation type="unfinished">felbevakare</translation>
<translation>felbevakare</translation>
</message>
<message>
<source>Writing Useful Bug Reports</source>
@ -179,7 +179,7 @@ vilket kan leda till problem med videosamtal.</translation>
<message>
<source>contributors</source>
<comment>Replaces `%1` in `See a full list of…`</comment>
<translation type="unfinished">bidragare</translation>
<translation>bidragsgivare</translation>
</message>
</context>
<context>
@ -190,11 +190,11 @@ vilket kan leda till problem med videosamtal.</translation>
</message>
<message>
<source>username</source>
<translation type="unfinished">användarnamn</translation>
<translation>användarnamn</translation>
</message>
<message>
<source>status message</source>
<translation type="unfinished">statusmeddelande</translation>
<translation>statusmeddelande</translation>
</message>
<message>
<source>Public key:</source>
@ -214,7 +214,7 @@ vilket kan leda till problem med videosamtal.</translation>
</message>
<message>
<source>Auto accept files</source>
<translation type="unfinished">Acceptera automatiskt filer</translation>
<translation>Acceptera filer automatiskt</translation>
</message>
<message>
<source>Default directory to save files:</source>
@ -222,23 +222,23 @@ vilket kan leda till problem med videosamtal.</translation>
</message>
<message>
<source>Auto accept for this contact is disabled</source>
<translation type="unfinished">Acceptera automatiskt för den här kontakten är avaktiverad</translation>
<translation>Acceptera automatiskt för den här kontakten är inaktiverat</translation>
</message>
<message>
<source>Auto accept call:</source>
<translation type="unfinished">Acceptera automatiskt samtal:</translation>
<translation>Acceptera samtal automatiskt:</translation>
</message>
<message>
<source>Manual</source>
<translation type="unfinished">Handbok</translation>
<translation>Handbok</translation>
</message>
<message>
<source>Audio</source>
<translation type="unfinished">Ljud</translation>
<translation>Ljud</translation>
</message>
<message>
<source>Audio + Video</source>
<translation type="unfinished">Ljud + Video</translation>
<translation>Ljud + video</translation>
</message>
<message>
<source>Automatically accept group chat invitations from this contact if set.</source>
@ -246,15 +246,15 @@ vilket kan leda till problem med videosamtal.</translation>
</message>
<message>
<source>Auto accept group invites</source>
<translation type="unfinished">Acceptera automatiskt gruppinbjudningar</translation>
<translation>Acceptera gruppinbjudningar automatiskt</translation>
</message>
<message>
<source>Remove history (operation can not be undone!)</source>
<translation type="unfinished">Ta bort historia (operation kan inte ångras!)</translation>
<translation>Ta bort historik (operation kan inte ångras!)</translation>
</message>
<message>
<source>Notes</source>
<translation type="unfinished">Anteckningar</translation>
<translation>Anteckningar</translation>
</message>
<message>
<source>Input field for notes about the contact</source>
@ -266,11 +266,11 @@ vilket kan leda till problem med videosamtal.</translation>
</message>
<message>
<source>History removed</source>
<translation type="unfinished">Historik raderad</translation>
<translation>Historik borttagen</translation>
</message>
<message>
<source>Chat history with %1 removed!</source>
<translation type="unfinished">Chatthistorik med %1 raderad!</translation>
<translation>Chatthistorik med %1 borttagen!</translation>
</message>
<message>
<source>Choose an auto accept directory</source>
@ -359,7 +359,7 @@ vilket kan leda till problem med videosamtal.</translation>
<message>
<source>You can&apos;t add yourself as a friend!</source>
<extracomment>When trying to add your own Tox ID as friend</extracomment>
<translation type="unfinished">Du kan inte lägga till dig själv som vän!</translation>
<translation>Du kan inte lägga till dig själv som vän!</translation>
</message>
<message>
<source>Open contact list</source>
@ -385,17 +385,17 @@ vilket kan leda till problem med videosamtal.</translation>
<message>
<source>Tox ID</source>
<extracomment>Tox ID of the person you&apos;re sending a friend request to</extracomment>
<translation type="unfinished">Tox-ID</translation>
<translation>Tox-ID</translation>
</message>
<message>
<source>either 76 hexadecimal characters or name@example.com</source>
<extracomment>Tox ID format description</extracomment>
<translation type="unfinished">antingen 76 hexadecimala tecken eller name@example.com</translation>
<translation>antingen 76 hexadecimala tecken eller namn@exempel.se</translation>
</message>
<message>
<source>Message</source>
<extracomment>The message you send in friend requests</extracomment>
<translation type="unfinished">Meddelande</translation>
<translation>Meddelande</translation>
</message>
<message>
<source>Open</source>
@ -418,11 +418,9 @@ vilket kan leda till problem med videosamtal.</translation>
<message numerus="yes">
<source>Ready to import %n contact(s), click send to confirm</source>
<extracomment>Shows the number of contacts we&apos;re about to import from a file (at least one)</extracomment>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<translation><numerusform>Klar för att importera %n kontakt(er), klicka på skicka för att bekräfta</numerusform>
<numerusform>Klar för att importera %n kontakter, klicka på skicka för att bekräfta</numerusform>
</translation></message>
<message>
<source>Import contacts</source>
<translation>Importera kontakter</translation>
@ -680,15 +678,15 @@ vilket kan leda till problem med videosamtal.</translation>
</message>
<message>
<source>End audio call</source>
<translation type="unfinished">Avsluta ljudsamtal</translation>
<translation>Avsluta ljudsamtal</translation>
</message>
<message>
<source>Cancel audio call</source>
<translation type="unfinished">Avbryt ljudsamtal</translation>
<translation>Avbryt ljudsamtal</translation>
</message>
<message>
<source>Accept audio call</source>
<translation type="unfinished">Acceptera ljudsamtal</translation>
<translation>Acceptera ljudsamtal</translation>
</message>
<message>
<source>Can&apos;t start video call</source>
@ -700,23 +698,23 @@ vilket kan leda till problem med videosamtal.</translation>
</message>
<message>
<source>End video call</source>
<translation type="unfinished">Avsluta videosamtal</translation>
<translation>Avsluta videosamtal</translation>
</message>
<message>
<source>Cancel video call</source>
<translation type="unfinished">Avbryt videosamtal</translation>
<translation>Avbryt videosamtal</translation>
</message>
<message>
<source>Accept video call</source>
<translation type="unfinished">Acceptera videosamtal</translation>
<translation>Acceptera videosamtal</translation>
</message>
<message>
<source>Sound can be disabled only during a call</source>
<translation type="unfinished">Ljud kan endast avaktiveras under ett samtal</translation>
<translation>Ljud kan endast inaktiveras under ett samtal</translation>
</message>
<message>
<source>Unmute call</source>
<translation type="unfinished">Slå på samtal</translation>
<translation>Slå på mikrofon</translation>
</message>
<message>
<source>Mute call</source>
@ -1964,7 +1962,7 @@ Om du blir spammad med vänförfrågningar, ändra NoSpam.</translation>
</message>
<message>
<source>Filter group message by group member&apos;s public key. Put public key here, one per line.</source>
<translation type="unfinished"></translation>
<translation>Filtrera gruppmeddelande genom gruppmedlems allmänna nyckel. Ange den offentliga nyckeln här, en per rad.</translation>
</message>
</context>
<context>
@ -2141,39 +2139,39 @@ Detta ID inkluderar NoSpam-koden (i blått) och checksum (i grått).</translatio
</message>
<message>
<source>Empty path is unavaliable</source>
<translation type="unfinished"></translation>
<translation>Tom sökväg är inte tillgänglig</translation>
</message>
<message>
<source>Failed to rename</source>
<translation type="unfinished">Det gick inte att byta namn</translation>
<translation>Det gick inte att byta namn</translation>
</message>
<message>
<source>Profile already exists</source>
<translation type="unfinished">Profil finns redan</translation>
<translation>Profilen finns redan</translation>
</message>
<message>
<source>A profile named &quot;%1&quot; already exists.</source>
<translation type="unfinished">En profil med namnet &quot;%1&quot; finns redan.</translation>
<translation>En profil med namnet &quot;%1&quot; finns redan.</translation>
</message>
<message>
<source>Empty name</source>
<translation type="unfinished"></translation>
<translation>Tomt namn</translation>
</message>
<message>
<source>Empty name is unavaliable</source>
<translation type="unfinished"></translation>
<translation>Tomt namn är inte tillgängligt</translation>
</message>
<message>
<source>Empty path</source>
<translation type="unfinished"></translation>
<translation>Tom sökväg</translation>
</message>
<message>
<source>Couldn&apos;t change password on the database, it might be corrupted or use the old password.</source>
<translation type="unfinished">Kunde inte byta lösenord på databasen, den kan vara trasig eller använda det gamla lösenordet.</translation>
<translation>Det gick inte att byta lösenord på databasen, den kan vara trasig eller använda det gamla lösenordet.</translation>
</message>
<message>
<source>Export profile</source>
<translation type="unfinished">Exportera profil</translation>
<translation>Exportera profil</translation>
</message>
<message>
<source>Tox save file (*.tox)</source>
@ -2183,17 +2181,17 @@ Detta ID inkluderar NoSpam-koden (i blått) och checksum (i grått).</translatio
<message>
<source>The following files could not be deleted:</source>
<extracomment>deletion failed text part 1</extracomment>
<translation type="unfinished">Följande filer kan inte tas bort:</translation>
<translation>Följande filer kunde inte tas bort:</translation>
</message>
<message>
<source>Please manually remove them.</source>
<extracomment>deletion failed text part 2</extracomment>
<translation type="unfinished">Vänligen ta bort dem manuellt.</translation>
<translation>Ta bort dem manuellt.</translation>
</message>
<message>
<source>Are you sure you want to delete your password?</source>
<extracomment>deletion confirmation text</extracomment>
<translation type="unfinished">Är du säker på att du vill ta bort ditt lösenord?</translation>
<translation>Är du säker på att du vill ta bort ditt lösenord?</translation>
</message>
</context>
<context>
@ -2707,11 +2705,11 @@ Den kommer att installeras när qTox startas om.</translation>
<message>
<source>If enabled every contact without an avatar set will have a generated avatar based on their Tox ID instead of a default picture. Requires restart to apply.</source>
<comment>toolTip for show identicons</comment>
<translation type="unfinished"></translation>
<translation>Om den är aktiverad kommer varje kontakt utan en avatar att ha en genererad avatar baserat på deras Tox-ID istället för en standardbild. Kräver omstart att tillämpa.</translation>
</message>
<message>
<source>Use identicons instead of empty avatars</source>
<translation type="unfinished"></translation>
<translation>Använd identicons istället för tomma avatarer</translation>
</message>
</context>
<context>

2
translations/ta.ts vendored
View File

@ -89,7 +89,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Audio quality</source>
<translation type="unfinished"></translation>
<translation>ஒலித்தரம்</translation>
</message>
<message>
<source>Transmitted audio quality. Lower this setting if your bandwidth is not high enough or if you want to lower the internet usage.</source>

70
translations/uk.ts vendored
View File

@ -93,7 +93,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Transmitted audio quality. Lower this setting if your bandwidth is not high enough or if you want to lower the internet usage.</source>
<translation type="unfinished"></translation>
<translation>Якість передаваємого звуку. Зменшіть цей параметр якщо пропускна здатність з&apos;єднання недостатня або ви хочете зменшити використання Інтернет трафіку.</translation>
</message>
<message>
<source>High (64 kbps)</source>
@ -137,7 +137,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>You are using qTox version %1.</source>
<translation type="unfinished">Ви використовуєте qTox версії %1.</translation>
<translation>Ви використовуєте qTox версії %1.</translation>
</message>
<message>
<source>Commit hash: %1</source>
@ -145,11 +145,11 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>toxcore version: %1</source>
<translation type="unfinished">Версія toxcore: %1</translation>
<translation>Версія toxcore: %1</translation>
</message>
<message>
<source>Qt version: %1</source>
<translation type="unfinished">Версія Qt: %1</translation>
<translation>Версія Qt: %1</translation>
</message>
<message>
<source>A list of all known issues may be found at our %1 at Github. If you discover a bug or security vulnerability within qTox, please report it according to the guidelines in our %2 wiki article.</source>
@ -169,7 +169,7 @@ which may lead to problems with video calls.</source>
<message>
<source>bug-tracker</source>
<comment>Replaces `%1` in the `A list of all known…`</comment>
<translation type="unfinished">баг-трекері</translation>
<translation>баг-трекері</translation>
</message>
<message>
<source>Writing Useful Bug Reports</source>
@ -367,40 +367,40 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Couldn&apos;t open file</source>
<translation type="unfinished"></translation>
<translation>Помилка відкриття файлу</translation>
</message>
<message>
<source>Couldn&apos;t open the contact file</source>
<extracomment>Error message when trying to open a contact list file to import</extracomment>
<translation type="unfinished"></translation>
<translation>Помилка відкриття файлу контактів</translation>
</message>
<message>
<source>Invalid file</source>
<translation type="unfinished"></translation>
<translation>Неправильний файл</translation>
</message>
<message>
<source>We couldn&apos;t find any contacts to import in this file!</source>
<translation type="unfinished"></translation>
<translation>Контактів для імпорту не знайдено!</translation>
</message>
<message>
<source>Tox ID</source>
<extracomment>Tox ID of the person you&apos;re sending a friend request to</extracomment>
<translation type="unfinished"></translation>
<translation>Ідентифікатор Tox</translation>
</message>
<message>
<source>either 76 hexadecimal characters or name@example.com</source>
<extracomment>Tox ID format description</extracomment>
<translation type="unfinished">76 шістнадцяткових цифр або name@example.com</translation>
<translation>76 шістнадцяткових символів або name@example.com</translation>
</message>
<message>
<source>Message</source>
<extracomment>The message you send in friend requests</extracomment>
<translation type="unfinished">Повідомлення</translation>
<translation>Повідомлення</translation>
</message>
<message>
<source>Open</source>
<extracomment>Button to choose a file with a list of contacts to import</extracomment>
<translation type="unfinished"></translation>
<translation>Відкрити</translation>
</message>
<message>
<source>Send friend requests</source>
@ -413,7 +413,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Import a list of contacts, one Tox ID per line</source>
<translation type="unfinished"></translation>
<translation>Імпортувати список контактів, один Ідентифікатор Tox на рядок</translation>
</message>
<message numerus="yes">
<source>Ready to import %n contact(s), click send to confirm</source>
@ -426,14 +426,14 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Import contacts</source>
<translation type="unfinished"></translation>
<translation>Імпортувати контакти</translation>
</message>
</context>
<context>
<name>AdvancedForm</name>
<message>
<source>Advanced</source>
<translation>Додатково</translation>
<translation>Більше</translation>
</message>
<message>
<source>Unless you %1 know what you are doing, please do %2 change anything here. Changes made here may lead to problems with qTox, and even to loss of your data, e.g. history.</source>
@ -441,7 +441,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>really</source>
<translation>впевнені</translation>
<translation>дійсно</translation>
</message>
<message>
<source>not</source>
@ -461,11 +461,11 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Yes</source>
<translation type="unfinished">Так</translation>
<translation>Так</translation>
</message>
<message>
<source>No</source>
<translation type="unfinished">Ні</translation>
<translation>Ні</translation>
</message>
<message>
<source>Call active</source>
@ -483,7 +483,7 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Logs (*.log)</source>
<translation type="unfinished"></translation>
<translation>Логи (*.log)</translation>
</message>
</context>
<context>
@ -512,7 +512,7 @@ which may lead to problems with video calls.</source>
<message>
<source>Enable IPv6 (recommended)</source>
<extracomment>Text on a checkbox to enable IPv6</extracomment>
<translation type="unfinished">Дозволити IPv6 (рекомендовано)</translation>
<translation>Увімкнути IPv6 (рекомендовано)</translation>
</message>
<message>
<source>Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary.</source>
@ -522,21 +522,21 @@ which may lead to problems with video calls.</source>
<message>
<source>Enable UDP (recommended)</source>
<extracomment>Text on checkbox to disable UDP</extracomment>
<translation type="unfinished">Дозволити UDP (рекомендовано)</translation>
<translation>Увімкнути UDP (рекомендовано)</translation>
</message>
<message>
<source>Proxy type:</source>
<translation type="unfinished">Тип проксі:</translation>
<translation>Тип проксі:</translation>
</message>
<message>
<source>Address:</source>
<extracomment>Text on proxy addr label</extracomment>
<translation type="unfinished">Адреса проксі:</translation>
<translation>Адреса:</translation>
</message>
<message>
<source>Port:</source>
<extracomment>Text on proxy port label</extracomment>
<translation type="unfinished">Порт:</translation>
<translation>Порт:</translation>
</message>
<message>
<source>None</source>
@ -544,28 +544,28 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>SOCKS5</source>
<translation type="unfinished">SOCKS5</translation>
<translation>SOCKS5</translation>
</message>
<message>
<source>HTTP</source>
<translation type="unfinished">HTTP</translation>
<translation>HTTP</translation>
</message>
<message>
<source>Reconnect</source>
<comment>reconnect button</comment>
<translation type="unfinished">Повторно під&apos;єднатись</translation>
<translation>Перепідключитись</translation>
</message>
<message>
<source>Debug</source>
<translation type="unfinished">Налагодження</translation>
<translation>Відладка</translation>
</message>
<message>
<source>Export Debug Log</source>
<translation type="unfinished">Вивантажити логи налагодження</translation>
<translation>Експортувати логи відладки</translation>
</message>
<message>
<source>Copy Debug Log</source>
<translation type="unfinished">Скопіювати логи налагодження</translation>
<translation>Скопіювати логи відладки</translation>
</message>
</context>
<context>
@ -600,20 +600,20 @@ which may lead to problems with video calls.</source>
</message>
<message>
<source>Failed to send file &quot;%1&quot;</source>
<translation>Не вдалось відправити файл «%1»</translation>
<translation>Не вдалось відправити файл &quot;%1&quot;</translation>
</message>
<message>
<source>Failed to open temporary file</source>
<comment>Temporary file for screenshot</comment>
<translation>Помилка під час відкриття тимчасового файлу</translation>
<translation>Помилка відкриття тимчасового файлу</translation>
</message>
<message>
<source>qTox wasn&apos;t able to save the screenshot</source>
<translation>qTox не може зберегти снімок екрану</translation>
<translation>qTox не зміг зберегти скриншот</translation>
</message>
<message>
<source>Call with %1 ended. %2</source>
<translation>Виклик із %1 завершено. %2</translation>
<translation>Дзвінок з %1 завершено. %2</translation>
</message>
<message>
<source>Call duration: </source>

View File

@ -1,39 +1,42 @@
QLineEdit {
QLineEdit
{
color: @black;
background: white;
border: 0px;
}
#nameLabel {
#nameLabel
{
color: @black;
font: @mediumBold;
font-size:12px;
}
#statusLabel {
#statusLabel
{
color: @mediumGrey;
font: @medium;
font-size:12px;
}
#peersLabel {
#peersLabel
{
color: @mediumGrey;
font: @medium;
font-size:12px;
}
QLabel[peerType="our"] {
QLabel[peerType="our"]
{
color: green;
}
QLabel[peerType="muted"] {
QLabel[peerType="muted"]
{
color: darkred;
}
QLabel[playingAudio="true"] {
QLabel[playingAudio="true"]
{
font-weight: bold;
}
QMenu:disabled {
color: gray;
}

View File

@ -1,64 +1,80 @@
QScrollArea {
QScrollArea
{
background: @themeMedium;
}
QScrollBar:vertical {
QScrollBar:vertical
{
background: @themeMedium;
width: 16px;
padding: 0px 3px 0px 3px;
}
QScrollBar:handle:vertical {
QScrollBar:handle:vertical
{
background: @themeDark;
min-height: 20px;
border-radius: 5px;
margin: 3px 0px 3px 0px;
}
QScrollBar:handle:vertical:hover {
QScrollBar:handle:vertical:hover
{
background: @themeMediumDark;
}
QScrollBar:handle:vertical:pressed {
QScrollBar:handle:vertical:pressed
{
background: @themeDark;
}
QScrollBar:add-line:vertical {height: 0px;subcontrol-position: bottom;subcontrol-origin: margin;}
QScrollBar:add-line:vertical
{
height: 0px;
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar:sub-line:vertical {height: 0px;subcontrol-position: top;subcontrol-origin: margin;}
QScrollBar:sub-line:vertical
{
height: 0px;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar:add-page:vertical, QScrollBar::sub-page:vertical {
QScrollBar:add-page:vertical, QScrollBar::sub-page:vertical
{
background: none;
}
QWidget#circleWidgetContainer > QFrame#line
{
color: white;
color: white;
}
QWidget#circleWidgetContainer
{
background-color: @themeMedium;
background-color: @themeMedium;
}
QWidget#circleWidgetContainer:hover
{
background-color: @themeLight;
background-color: @themeLight;
}
QWidget#circleWidgetContainer QLineEdit
{
background-color: @themeLight;
background-color: @themeLight;
}
QWidget#circleWidgetContainer > QLabel#status
{
font: @small;
color: @lightGrey;
font: @small;
color: @lightGrey;
}
QWidget#circleWidgetContainer > QLabel#name
{
font: @big;
color: @white;
font: @big;
color: @white;
}

View File

@ -1,58 +1,68 @@
#LoginScreen {
background-color: #1c1c1c;
#LoginScreen
{
background-color: #1c1c1c;
}
#line {
color: #757575;
#line
{
color: #757575;
}
#stackedWidget {
background-color: #d6d2Cf;
#stackedWidget
{
background-color: #d6d2Cf;
}
#labelNewProfile,
#labelLoadProfile {
border-style: solid;
border-width: 15px 0 15px 15px;
border-color: #d6d2cf #d6d2cf #d6d2cf #1c1c1c;
#labelLoadProfile
{
border-style: solid;
border-width: 15px 0 15px 15px;
border-color: #d6d2cf #d6d2cf #d6d2cf #1c1c1c;
}
#labelNewProfile {
margin-top:125px;
margin-bottom:45px;
#labelNewProfile
{
margin-top:125px;
margin-bottom:45px;
}
#labelLoadProfile {
margin-top:165px;
margin-bottom:5px;
#labelLoadProfile
{
margin-top:165px;
margin-bottom:5px;
}
LoginScreen > QPushButton,
QStackedWidget QPushButton
{
background: transparent;
border: none;
color: white;
font-size: 9pt;
font-weight: bold;
background: transparent;
border: none;
color: white;
font-size: 9pt;
font-weight: bold;
}
#loginButton,
#importButton,
#createAccountButton {
border-radius:5px;
padding:5px;
background-color:#42a33a;
#createAccountButton
{
border-radius:5px;
padding:5px;
background-color:#42a33a;
}
#loginButton:hover,
#importButton:hover,
#createAccountButton:hover {
background: #0c530d;
#createAccountButton:hover
{
background: #0c530d;
}
QLabel, QCheckBox, QProgressBar {
color: black;
QLabel, QCheckBox, QProgressBar
{
color: black;
}
QCheckBox:disabled {
color: gray;
QCheckBox:disabled
{
color: gray;
}

View File

@ -1,27 +1,33 @@
QTextEdit {
border-color: @lightGrey;
border-style: solid;
border-width: 1px 0 1px 1px;
QTextEdit
{
border-color: @lightGrey;
border-style: solid;
border-width: 1px 0 1px 1px;
background: white;
}
QTextEdit:hover {
border-color: #d7d4d1;
QTextEdit:hover
{
border-color: #d7d4d1;
}
QTextEdit:pressed {
border-color: #4ea6ea;
QTextEdit:pressed
{
border-color: #4ea6ea;
}
QTextEdit#group {
/*border-radius: 0 6px 6px 0; would use to round corners in groupchat, but Qt's implementation seems to be bugged*/
border: 1px solid #c4c1bd;
QTextEdit#group
{
/*border-radius: 0 6px 6px 0; would use to round corners in groupchat, but Qt's implementation seems to be bugged*/
border: 1px solid #c4c1bd;
}
QTextEdit#group:hover {
border-color: #d7d4d1;
QTextEdit#group:hover
{
border-color: #d7d4d1;
}
QTextEdit#group:pressed {
border-color: #4ea6ea;
QTextEdit#group:pressed
{
border-color: #4ea6ea;
}

View File

@ -10,21 +10,22 @@ QPushButton
QPushButton:default
{
background-color: @themeMediumDark;
background-color: @themeMediumDark;
}
/*Bugged in Qt, but it's probably better to leave enabled so that users can tell it's clickable*/
QPushButton:hover
{
background-color: @themeMedium;
background-color: @themeMedium;
}
QPushButton:pressed
{
background-color: @themeMediumDark;
background-color: @themeMediumDark;
}
QPushButton:focus {
QPushButton:focus
{
outline: none;
}
@ -32,9 +33,9 @@ QPushButton::menu-indicator {image: none;}
QPushButton::menu-indicator:pressed, QPushButton::menu-indicator:open
{
image: url(":ui/statusButton/menu_indicator.svg");
subcontrol-origin: padding;
subcontrol-position: bottom center;
position: relative;
image: url(":ui/statusButton/menu_indicator.svg");
subcontrol-origin: padding;
subcontrol-position: bottom center;
position: relative;
bottom: 2px;
}

View File

@ -1,4 +1,5 @@
#selfAvatar:hover {
#selfAvatar:hover
{
border-radius: 6px;
background-color: #ccc;
}

View File

@ -7,7 +7,8 @@ QLineEdit
border-radius: 4px;
}
QToolButton {
QToolButton
{
background: none;
background-color: @themeMedium;
color: white;
@ -15,17 +16,20 @@ QToolButton {
border-radius: 4px;
}
QToolButton:pressed {
QToolButton:pressed
{
background-color: @themeMediumDark;
border-radius: 4px;
color: white;
}
QToolButton::menu-indicator {
QToolButton::menu-indicator
{
image: none
}
QPushButton#green {
QPushButton#green
{
background: none;
background-color: #6bc260;
color: white;
@ -48,12 +52,14 @@ QPushButton#green:pressed
/**
Uncomment this after https://github.com/qTox/qTox/pull/1640
is merged!
QComboBox:down-arrow {
QComboBox:down-arrow
{
image: url(ui/css/down_arrow.png);
}
**/
QListView {
QListView
{
background-color: @themeLight;
border-style: none;
border-radius: 4px;
@ -69,13 +75,15 @@ QListView {
background-color: @themeDark;
}
#statusPanel > #statusHead > #nameLabel {
#statusPanel > #statusHead > #nameLabel
{
background-color: @themeDark;
font: @extraBig;
color: @white;
}
#statusPanel > #statusHead > #statusLabel {
#statusPanel > #statusHead > #statusLabel
{
background-color: @themeDark;
font: @medium;
color: @lightGrey;
@ -102,7 +110,8 @@ QListView {
background-color: @themeMedium;
}
#statusPanel > #statusHead > #statusButton:focus {
#statusPanel > #statusHead > #statusButton:focus
{
outline: none;
}
@ -116,4 +125,3 @@ QListView {
position: relative;
bottom: 2px;
}

View File

@ -35,7 +35,8 @@ QPushButton#minimizeButton
{
border: 0px solid transparent;
background-color: transparent;
image: url(":ui/window/minimizeButton.png");}
image: url(":ui/window/minimizeButton.png");
}
QPushButton#minimizeButton:hover {image: url(":ui/window/minimizeButtonHover.png");}
QPushButton#minimizeButton:pressed {image: url(":ui/window/minimizeButtonPressed.png");}
QPushButton#minimizeButton:focus {outline: none;}
@ -44,7 +45,8 @@ QPushButton#maximizeButton
{
border: 0px solid transparent;
background-color: transparent;
image: url(":ui/window/maximizeButton.png");}
image: url(":ui/window/maximizeButton.png");
}
QPushButton#maximizeButton:hover {image: url(":ui/window/maximizeButtonHover.png");}
QPushButton#maximizeButton:pressed {image: url(":ui/window/maximizeButtonPressed.png");}
QPushButton#maximizeButton:focus {outline: none;}
@ -53,7 +55,8 @@ QPushButton#closeButton
{
border: 0px solid transparent;
background-color: transparent;
image: url(":ui/window/closeButton.png");}
image: url(":ui/window/closeButton.png");
}
QPushButton#closeButton:hover {image: url(":ui/window/closeButtonHover.png");}
QPushButton#closeButton:pressed {image: url(":ui/window/closeButtonPressed.png");}
QPushButton#closeButton:focus {outline: none;}
@ -62,7 +65,8 @@ QPushButton#restoreButton
{
border: 0px solid transparent;
background-color: transparent;
image: url(":ui/window/restoreButton.png");}
image: url(":ui/window/restoreButton.png");
}
QPushButton#restoreButton:hover {image: url(":ui/window/restoreButtonHover.png");}
QPushButton#restoreButton:pressed {image: url(":ui/window/restoreButtonPressed.png");}
QPushButton#restoreButton:focus {outline: none;}

View File

@ -26,10 +26,6 @@
# - Doesn't build qTox updater, because it wasn't ported to cmake yet and
# because it requires static Qt, which means we'd need to build Qt twice, and
# building Qt takes really long time.
#
# - FFmpeg 3.3 doesn't cross-compile correctly, qTox build fails when linking
# against the 3.3 FFmpeg. They have removed `--enable-memalign-hack` switch,
# which might be what causes this. Further research needed.
set -euo pipefail
@ -217,15 +213,16 @@ OPENSSL_PREFIX_DIR="$DEP_DIR/libopenssl"
OPENSSL_VERSION=1.0.2o
# hash from https://www.openssl.org/source/
OPENSSL_HASH="ec3f5c9714ba0fd45cb4e087301eb1336c317e0d20b575a125050470e8089e4d"
OPENSSL_FILENAME="openssl-$OPENSSL_VERSION.tar.gz"
if [ ! -f "$OPENSSL_PREFIX_DIR/done" ]
then
rm -rf "$OPENSSL_PREFIX_DIR"
mkdir -p "$OPENSSL_PREFIX_DIR"
wget https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz
check_sha256 "$OPENSSL_HASH" "openssl-$OPENSSL_VERSION.tar.gz"
bsdtar --no-same-owner --no-same-permissions -xf openssl*.tar.gz
rm openssl*.tar.gz
wget "https://www.openssl.org/source/$OPENSSL_FILENAME"
check_sha256 "$OPENSSL_HASH" "$OPENSSL_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf "$OPENSSL_FILENAME"
rm $OPENSSL_FILENAME
cd openssl*
CONFIGURE_OPTIONS="--prefix=$OPENSSL_PREFIX_DIR shared"
@ -256,19 +253,20 @@ fi
QT_PREFIX_DIR="$DEP_DIR/libqt5"
QT_MAJOR=5
QT_MINOR=9
QT_PATCH=5
QT_PATCH=6
QT_VERSION=$QT_MAJOR.$QT_MINOR.$QT_PATCH
# hash from https://download.qt.io/archive/qt/5.9/5.9.5/single/qt-everywhere-opensource-src-5.9.5.tar.xz.mirrorlist
QT_HASH="a75b87f46240a374fde93fb60038d63e3b570457785268c766c639b5dc18ccf6"
# hash from https://download.qt.io/archive/qt/5.9/5.9.6/single/qt-everywhere-opensource-src-5.9.6.tar.xz.mirrorlist
QT_HASH="dacc995ae3a7cdad80eb9fdf6470299a8fac41f468a9bb941670ece523b62af4"
QT_FILENAME="qt-everywhere-opensource-src-$QT_VERSION.tar.xz"
if [ ! -f "$QT_PREFIX_DIR/done" ]
then
rm -rf "$QT_PREFIX_DIR"
mkdir -p "$QT_PREFIX_DIR"
wget https://download.qt.io/official_releases/qt/$QT_MAJOR.$QT_MINOR/$QT_VERSION/single/qt-everywhere-opensource-src-$QT_VERSION.tar.xz
check_sha256 "$QT_HASH" "qt-everywhere-opensource-src-$QT_VERSION.tar.xz"
bsdtar --no-same-owner --no-same-permissions -xf qt*.tar.xz
rm qt*.tar.xz
wget "https://download.qt.io/official_releases/qt/$QT_MAJOR.$QT_MINOR/$QT_VERSION/single/$QT_FILENAME"
check_sha256 "$QT_HASH" "$QT_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf $QT_FILENAME
rm $QT_FILENAME
cd qt*
export PKG_CONFIG_PATH="$OPENSSL_PREFIX_DIR/lib/pkgconfig"
@ -374,15 +372,16 @@ set -u
SQLCIPHER_PREFIX_DIR="$DEP_DIR/libsqlcipher"
SQLCIPHER_VERSION=v3.4.2
SQLCIPHER_HASH="69897a5167f34e8a84c7069f1b283aba88cdfa8ec183165c4a5da2c816cfaadb"
SQLCIPHER_FILENAME="$SQLCIPHER_VERSION.tar.gz"
if [ ! -f "$SQLCIPHER_PREFIX_DIR/done" ]
then
rm -rf "$SQLCIPHER_PREFIX_DIR"
mkdir -p "$SQLCIPHER_PREFIX_DIR"
wget https://github.com/sqlcipher/sqlcipher/archive/$SQLCIPHER_VERSION.tar.gz -O sqlcipher.tar.gz
check_sha256 "$SQLCIPHER_HASH" "sqlcipher.tar.gz"
bsdtar --no-same-owner --no-same-permissions -xf sqlcipher.tar.gz
rm sqlcipher.tar.gz
wget "https://github.com/sqlcipher/sqlcipher/archive/$SQLCIPHER_FILENAME"
check_sha256 "$SQLCIPHER_HASH" "$SQLCIPHER_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf "$SQLCIPHER_FILENAME"
rm $SQLCIPHER_FILENAME
cd sqlcipher*
sed -i s/'LIBS="-lcrypto $LIBS"'/'LIBS="-lcrypto -lgdi32 $LIBS"'/g configure
@ -397,8 +396,8 @@ then
@@ -1074,7 +1074,7 @@
$(TOP)/ext/fts5/fts5_varint.c \
$(TOP)/ext/fts5/fts5_vocab.c \
-fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon
-fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon
+fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon$(BEXE)
cp $(TOP)/ext/fts5/fts5parse.y .
rm -f fts5parse.h
@ -431,17 +430,18 @@ fi
# FFmpeg
FFMPEG_PREFIX_DIR="$DEP_DIR/libffmpeg"
FFMPEG_VERSION=3.2.10
FFMPEG_HASH="3c1626220c7b68ff6be7312559f77f3c65ff6809daf645d4470ac0189926bdbc"
FFMPEG_VERSION=4.0.1
FFMPEG_HASH="605f5c01c60db35d3b617a79cabb2c7032412be243554602eeed1b628125c0ee"
FFMPEG_FILENAME="ffmpeg-$FFMPEG_VERSION.tar.xz"
if [ ! -f "$FFMPEG_PREFIX_DIR/done" ]
then
rm -rf "$FFMPEG_PREFIX_DIR"
mkdir -p "$FFMPEG_PREFIX_DIR"
wget https://www.ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.xz
check_sha256 "$FFMPEG_HASH" "ffmpeg-$FFMPEG_VERSION.tar.xz"
bsdtar --no-same-owner --no-same-permissions -xf ffmpeg*.tar.xz
rm ffmpeg*.tar.xz
wget "https://www.ffmpeg.org/releases/$FFMPEG_FILENAME"
check_sha256 "$FFMPEG_HASH" "$FFMPEG_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf $FFMPEG_FILENAME
rm $FFMPEG_FILENAME
cd ffmpeg*
if [[ "$ARCH" == "x86_64"* ]]
@ -453,6 +453,7 @@ then
fi
./configure $CONFIGURE_OPTIONS \
--enable-gpl \
--prefix="$FFMPEG_PREFIX_DIR" \
--target-os="mingw32" \
--cross-prefix="$ARCH-w64-mingw32-" \
@ -460,11 +461,12 @@ then
--extra-cflags="-static -O2 -g0" \
--extra-ldflags="-lm -static" \
--pkg-config-flags="--static" \
--disable-debug \
--disable-shared \
--disable-programs \
--disable-protocols \
--disable-doc \
--disable-sdl \
--disable-sdl2 \
--disable-avfilter \
--disable-avresample \
--disable-filters \
@ -492,14 +494,14 @@ then
--disable-decoders \
--disable-demuxers \
--disable-parsers \
--disable-bsfs \
--enable-demuxer=h264 \
--enable-demuxer=mjpeg \
--enable-parser=h264 \
--enable-parser=mjpeg \
--enable-decoder=h264 \
--enable-decoder=mjpeg \
--enable-decoder=rawvideo \
--enable-memalign-hack
--enable-decoder=rawvideo
make
make install
echo -n $FFMPEG_VERSION > $FFMPEG_PREFIX_DIR/done
@ -702,17 +704,18 @@ fi
# QREncode
QRENCODE_PREFIX_DIR="$DEP_DIR/libqrencode"
QRENCODE_VERSION=4.0.0
QRENCODE_HASH="c90035e16921117d4086a7fdee65aab85be32beb4a376f6b664b8a425d327d0b"
QRENCODE_VERSION=4.0.2
QRENCODE_HASH="c9cb278d3b28dcc36b8d09e8cad51c0eca754eb004cb0247d4703cb4472b58b4"
QRENCODE_FILENAME="qrencode-$QRENCODE_VERSION.tar.bz2"
if [ ! -f "$QRENCODE_PREFIX_DIR/done" ]
then
rm -rf "$QRENCODE_PREFIX_DIR"
mkdir -p "$QRENCODE_PREFIX_DIR"
wget https://fukuchi.org/works/qrencode/qrencode-$QRENCODE_VERSION.tar.bz2
check_sha256 "$QRENCODE_HASH" "qrencode-$QRENCODE_VERSION.tar.bz2"
bsdtar --no-same-owner --no-same-permissions -xf qrencode*.tar.bz2
rm qrencode*.tar.bz2
wget https://fukuchi.org/works/qrencode/$QRENCODE_FILENAME
check_sha256 "$QRENCODE_HASH" "$QRENCODE_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf "$QRENCODE_FILENAME"
rm $QRENCODE_FILENAME
cd qrencode*
CFLAGS="-O2 -g0" ./configure --host="$ARCH-w64-mingw32" \
@ -738,15 +741,16 @@ fi
EXIF_PREFIX_DIR="$DEP_DIR/libexif"
EXIF_VERSION=0.6.21
EXIF_HASH="16cdaeb62eb3e6dfab2435f7d7bccd2f37438d21c5218ec4e58efa9157d4d41a"
EXIF_FILENAME=libexif-$EXIF_VERSION.tar.bz2
if [ ! -f "$EXIF_PREFIX_DIR/done" ]
then
rm -rf "$EXIF_PREFIX_DIR"
mkdir -p "$EXIF_PREFIX_DIR"
wget https://sourceforge.net/projects/libexif/files/libexif/0.6.21/libexif-$EXIF_VERSION.tar.bz2
check_sha256 "$EXIF_HASH" "libexif-$EXIF_VERSION.tar.bz2"
bsdtar --no-same-owner --no-same-permissions -xf libexif*.tar.bz2
rm libexif*.tar.bz2
wget https://sourceforge.net/projects/libexif/files/libexif/$EXIF_VERSION/$EXIF_FILENAME
check_sha256 "$EXIF_HASH" "$EXIF_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf $EXIF_FILENAME
rm $EXIF_FILENAME
cd libexif*
CFLAGS="-O2 -g0" ./configure --host="$ARCH-w64-mingw32" \
@ -771,15 +775,16 @@ fi
OPUS_PREFIX_DIR="$DEP_DIR/libopus"
OPUS_VERSION=1.2.1
OPUS_HASH="cfafd339ccd9c5ef8d6ab15d7e1a412c054bf4cb4ecbbbcc78c12ef2def70732"
OPUS_FILENAME="opus-$OPUS_VERSION.tar.gz"
if [ ! -f "$OPUS_PREFIX_DIR/done" ]
then
rm -rf "$OPUS_PREFIX_DIR"
mkdir -p "$OPUS_PREFIX_DIR"
wget https://archive.mozilla.org/pub/opus/opus-$OPUS_VERSION.tar.gz
check_sha256 "$OPUS_HASH" "opus-$OPUS_VERSION.tar.gz"
bsdtar --no-same-owner --no-same-permissions -xf opus*.tar.gz
rm opus*.tar.gz
wget "https://archive.mozilla.org/pub/opus/$OPUS_FILENAME"
check_sha256 "$OPUS_HASH" "$OPUS_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf "$OPUS_FILENAME"
rm $OPUS_FILENAME
cd opus*
CFLAGS="-O2 -g0" ./configure --host="$ARCH-w64-mingw32" \
@ -804,15 +809,16 @@ fi
SODIUM_PREFIX_DIR="$DEP_DIR/libsodium"
SODIUM_VERSION=1.0.16
SODIUM_HASH="eeadc7e1e1bcef09680fb4837d448fbdf57224978f865ac1c16745868fbd0533"
SODIUM_FILENAME="libsodium-$SODIUM_VERSION.tar.gz"
if [ ! -f "$SODIUM_PREFIX_DIR/done" ]
then
rm -rf "$SODIUM_PREFIX_DIR"
mkdir -p "$SODIUM_PREFIX_DIR"
wget https://download.libsodium.org/libsodium/releases/libsodium-$SODIUM_VERSION.tar.gz
check_sha256 "$SODIUM_HASH" "libsodium-$SODIUM_VERSION.tar.gz"
bsdtar --no-same-owner --no-same-permissions -xf libsodium*.tar.gz
rm libsodium*.tar.gz
wget "https://download.libsodium.org/libsodium/releases/$SODIUM_FILENAME"
check_sha256 "$SODIUM_HASH" "$SODIUM_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf "$SODIUM_FILENAME"
rm "$SODIUM_FILENAME"
cd libsodium*
./configure --host="$ARCH-w64-mingw32" \
@ -836,15 +842,16 @@ fi
VPX_PREFIX_DIR="$DEP_DIR/libvpx"
VPX_VERSION=v1.7.0
VPX_HASH="1fec931eb5c94279ad219a5b6e0202358e94a93a90cfb1603578c326abfc1238"
VPX_FILENAME="libvpx-$VPX_VERSION.tar.bz2"
if [ ! -f "$VPX_PREFIX_DIR/done" ]
then
rm -rf "$VPX_PREFIX_DIR"
mkdir -p "$VPX_PREFIX_DIR"
wget https://github.com/webmproject/libvpx/archive/$VPX_VERSION.tar.gz -O libvpx-$VPX_VERSION.tar.gz
check_sha256 "$VPX_HASH" "libvpx-$VPX_VERSION.tar.gz"
bsdtar --no-same-owner --no-same-permissions -xf libvpx-*.tar.gz
rm libvpx*.tar.gz
wget https://github.com/webmproject/libvpx/archive/$VPX_VERSION.tar.gz -O $VPX_FILENAME
check_sha256 "$VPX_HASH" "$VPX_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf "$VPX_FILENAME"
rm $VPX_FILENAME
cd libvpx*
if [[ "$ARCH" == "x86_64" ]]
@ -877,17 +884,18 @@ fi
# Toxcore
TOXCORE_PREFIX_DIR="$DEP_DIR/libtoxcore"
TOXCORE_VERSION=0.2.2
TOXCORE_HASH=a3b25d8bd92b9526b47ba1f60a2893d2154a80bb7ae690f44b5a2dea41c76ea1
TOXCORE_VERSION=0.2.3
TOXCORE_HASH=22c52f286c46d3f802edb6978bcf2a53f8301363e2b745784613427a33ba3a34
TOXCORE_FILENAME="c-toxcore-$TOXCORE_VERSION.tar.gz"
if [ ! -f "$TOXCORE_PREFIX_DIR/done" ]
then
rm -rf "$TOXCORE_PREFIX_DIR"
mkdir -p "$TOXCORE_PREFIX_DIR"
wget https://github.com/TokTok/c-toxcore/archive/v$TOXCORE_VERSION.tar.gz -O c-toxcore-$TOXCORE_VERSION.tar.gz
check_sha256 "$TOXCORE_HASH" "c-toxcore-$TOXCORE_VERSION.tar.gz"
bsdtar --no-same-owner --no-same-permissions -xf c-toxcore*.tar.gz
rm c-toxcore*.tar.gz
wget https://github.com/TokTok/c-toxcore/archive/v$TOXCORE_VERSION.tar.gz -O $TOXCORE_FILENAME
check_sha256 "$TOXCORE_HASH" "$TOXCORE_FILENAME"
bsdtar --no-same-owner --no-same-permissions -xf "$TOXCORE_FILENAME"
rm "$TOXCORE_FILENAME"
cd c-toxcore*
mkdir -p build
@ -1117,15 +1125,18 @@ then
fi
set -u
# Spell check on windows currently not supported, disable
if [[ "$BUILD_TYPE" == "release" ]]
then
cmake -DCMAKE_TOOLCHAIN_FILE=./toolchain.cmake \
-DCMAKE_BUILD_TYPE=Release \
-DSPELL_CHECK=OFF \
..
elif [[ "$BUILD_TYPE" == "debug" ]]
then
cmake -DCMAKE_TOOLCHAIN_FILE=./toolchain.cmake \
-DCMAKE_BUILD_TYPE=Debug \
-DSPELL_CHECK=OFF \
..
fi

View File

@ -276,7 +276,7 @@ Section "Install"
${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}"
${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "Path" "$INSTDIR\bin\"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayName" "qTox"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "1.15.0"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "1.16.3"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "The qTox Project"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\uninstall.exe"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "URLInfoAbout" "https://qtox.github.io"

View File

@ -277,7 +277,7 @@ Section "Install"
${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}"
${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "Path" "$INSTDIR\bin\"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayName" "qTox"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "1.15.0"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "1.16.3"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "The qTox Project"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\uninstall.exe"
${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "URLInfoAbout" "https://qtox.github.io"