diff --git a/.travis.yml b/.travis.yml
index 1ae26b336..04cdc4e89 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -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:
diff --git a/.travis/build-ubuntu-14-04.sh b/.travis/build-ubuntu-14-04.sh
index f19b5e97e..d1c1a5e3a 100755
--- a/.travis/build-ubuntu-14-04.sh
+++ b/.travis/build-ubuntu-14-04.sh
@@ -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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2161c33d7..2bb6efed0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,99 @@
+
+## v1.16.3 (2018-07-18)
+
+This point release fixes flatpak build. No feature changes.
+
+
+
+
+## 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))
+
+
+
+
+## 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))
+
+
+
+
+## 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))
+
+
+
## v1.15.0 (2018-04-18)
@@ -45,88 +141,7 @@
-## (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))
-
-
-
-
-## (2018-03-12)
+## v1.14.0 (2018-03-12)
#### Bug Fixes
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f087f2c1a..c9aa7a49a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 236aa89ca..d38c79f13 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -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:/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
```
diff --git a/INSTALL.fa.md b/INSTALL.fa.md
index 6f77201df..0831639cc 100644
--- a/INSTALL.fa.md
+++ b/INSTALL.fa.md
@@ -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)
diff --git a/INSTALL.md b/INSTALL.md
index 0717197f6..f3a43863a 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -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
```
+
+
+#### 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
+```
@@ -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
```
#### 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.
-
-
-
-#### Arch Linux
-
-```bash
-sudo pacman -S --needed opus libvpx libsodium
-```
-
-
-
-#### Debian
-
-```bash
-sudo apt-get install libtool autotools-dev automake checkinstall check \
-libopus-dev libvpx-dev libsodium-dev libavdevice-dev
-```
-
-
-
-#### Fedora
-
-```bash
-sudo dnf install libtool autoconf automake check check-devel libsodium-devel \
-opus-devel libvpx-devel
-```
-
-
-
-#### openSUSE
-
-```bash
-sudo zypper install libsodium-devel libvpx-devel libopus-devel \
-patterns-openSUSE-devel_basis
-```
-
-
-
-#### Slackware
-
-List of all the toxcore dependencies and their SlackBuilds can be found
-here: http://slackbuilds.org/repository/14.2/network/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
diff --git a/MAINTAINING.md b/MAINTAINING.md
index b6d62bf3e..73e66d7e5 100644
--- a/MAINTAINING.md
+++ b/MAINTAINING.md
@@ -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
- ```
-
-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/#/
diff --git a/README.md b/README.md
index b05666920..f7a912792 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/appimage/build-appimage.sh b/appimage/build-appimage.sh
index 6ef5868eb..eb4ce24ca 100755
--- a/appimage/build-appimage.sh
+++ b/appimage/build-appimage.sh
@@ -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
diff --git a/bootstrap.sh b/bootstrap.sh
index a01515b38..c43bf9fdd 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -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
diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake
index f77190ef4..ac3fa5de8 100644
--- a/cmake/Dependencies.cmake
+++ b/cmake/Dependencies.cmake
@@ -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)
diff --git a/doc/TCS_state.md b/doc/TCS_state.md
new file mode 100644
index 000000000..80416dbc2
--- /dev/null
+++ b/doc/TCS_state.md
@@ -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/
diff --git a/docker/Dockerfile.debian b/docker/Dockerfile.debian
new file mode 100644
index 000000000..e1a41b128
--- /dev/null
+++ b/docker/Dockerfile.debian
@@ -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 .
diff --git a/docker/Dockerfile.ubuntu b/docker/Dockerfile.ubuntu
new file mode 100644
index 000000000..3d14ad46e
--- /dev/null
+++ b/docker/Dockerfile.ubuntu
@@ -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 .
diff --git a/docker/build-debian.sh b/docker/build-debian.sh
new file mode 100755
index 000000000..d15c4a476
--- /dev/null
+++ b/docker/build-debian.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+cd "$(dirname "$0")/.."
+docker build . -f docker/Dockerfile.debian -t qtox
+cd -
diff --git a/docker/build-ubuntu.sh b/docker/build-ubuntu.sh
new file mode 100755
index 000000000..87ed7c357
--- /dev/null
+++ b/docker/build-ubuntu.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+cd "$(dirname "$0")/.."
+docker build . -f docker/Dockerfile.ubuntu -t qtox
+cd -
diff --git a/docker/start-qtox.sh b/docker/start-qtox.sh
new file mode 100755
index 000000000..8162f43b3
--- /dev/null
+++ b/docker/start-qtox.sh
@@ -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
diff --git a/flatpak/build-flatpak.sh b/flatpak/build-flatpak.sh
new file mode 100755
index 000000000..b6b74bb66
--- /dev/null
+++ b/flatpak/build-flatpak.sh
@@ -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
diff --git a/flatpak/build.sh b/flatpak/build.sh
new file mode 100755
index 000000000..262bb32ef
--- /dev/null
+++ b/flatpak/build.sh
@@ -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"
diff --git a/flatpak/io.github.qtox.qTox.json b/flatpak/io.github.qtox.qTox.json
new file mode 100644
index 000000000..bc03f4718
--- /dev/null
+++ b/flatpak/io.github.qtox.qTox.json
@@ -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/"
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/osx/info.plist b/osx/info.plist
index 2c17665a8..e91fedaba 100644
--- a/osx/info.plist
+++ b/osx/info.plist
@@ -65,7 +65,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.15.0
+ 1.16.3
CFBundleSignature
toxq
CFBundleURLTypes
@@ -84,7 +84,7 @@
CFBundleVersion
- 1.15.0
+ 1.16.3
NSPrincipalClass
NSApplication
UTImportedTypeDeclarations
diff --git a/osx/qTox-Mac-Deployer-ULTIMATE.sh b/osx/qTox-Mac-Deployer-ULTIMATE.sh
index 132b48985..9ce616656 100755
--- a/osx/qTox-Mac-Deployer-ULTIMATE.sh
+++ b/osx/qTox-Mac-Deployer-ULTIMATE.sh
@@ -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"
diff --git a/simple_make.sh b/simple_make.sh
index 7f8ab359e..c845123cd 100755
--- a/simple_make.sh
+++ b/simple_make.sh
@@ -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[@]}"
diff --git a/src/audio/backend/openal2.cpp b/src/audio/backend/openal2.cpp
index 094a183af..3d9907116 100644
--- a/src/audio/backend/openal2.cpp
+++ b/src/audio/backend/openal2.cpp
@@ -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
diff --git a/src/chatlog/chatlog.cpp b/src/chatlog/chatlog.cpp
index ff26e85f7..f21eecc8f 100644
--- a/src/chatlog/chatlog.cpp
+++ b/src/chatlog/chatlog.cpp
@@ -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
diff --git a/src/chatlog/chatlog.h b/src/chatlog/chatlog.h
index 56d0dda54..d98a86a6e 100644
--- a/src/chatlog/chatlog.h
+++ b/src/chatlog/chatlog.h
@@ -155,6 +155,7 @@ private:
AutoScrollDirection selectionScrollDir = NoDirection;
int clickCount = 0;
QPoint lastClickPos;
+ Qt::MouseButton lastClickButton;
// worker vars
int workerLastIndex = 0;
diff --git a/src/chatlog/chatmessage.cpp b/src/chatlog/chatmessage.cpp
index 136fc0703..81a6e595d 100644
--- a/src/chatlog/chatmessage.cpp
+++ b/src/chatlog/chatmessage.cpp
@@ -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("" + text + "", baseFont, false, ""),
+ msg->addColumn(new Text("" + text + "", 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));
diff --git a/src/core/core.cpp b/src/core/core.cpp
index f82a5d2e2..7751767d7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -22,11 +22,12 @@
#include "corefile.h"
#include "src/core/coreav.h"
#include "src/core/icoresettings.h"
+#include "src/core/toxlogger.h"
+#include "src/core/toxoptions.h"
#include "src/core/toxstring.h"
#include "src/model/groupinvite.h"
#include "src/nexus.h"
#include "src/persistence/profile.h"
-#include "src/widget/gui.h"
#include
#include
@@ -35,343 +36,46 @@
#include
#include
-#include
+#include
const QString Core::TOX_EXT = ".tox";
-QThread* Core::coreThread{nullptr};
-
-static const int MAX_PROXY_ADDRESS_LENGTH = 255;
#define MAX_GROUP_MESSAGE_LEN 1024
-Core::Core(QThread* CoreThread, Profile& profile, const ICoreSettings* const settings)
+#define ASSERT_CORE_THREAD assert(QThread::currentThread() == coreThread.get())
+
+Core::Core(QThread* coreThread)
: tox(nullptr)
, av(nullptr)
- , profile(profile)
- , ready(false)
- , s{settings}
+ , coreLoopLock(new QMutex(QMutex::Recursive))
+ , coreThread(coreThread)
{
- coreThread = CoreThread;
- toxTimer = new QTimer(this);
- toxTimer->setSingleShot(true);
- connect(toxTimer, &QTimer::timeout, this, &Core::process);
- s->connectTo_dhtServerListChanged([=](const QList& servers){
- process();
- });
-}
-
-void Core::deadifyTox()
-{
- if (av) {
- delete av;
- av = nullptr;
- }
-
- if (tox) {
- tox_kill(tox);
- tox = nullptr;
- }
+ toxTimer.setSingleShot(true);
+ connect(&this->toxTimer, &QTimer::timeout, this, &Core::process);
}
Core::~Core()
{
- if (coreThread->isRunning()) {
- if (QThread::currentThread() == coreThread) {
- killTimers(false);
- } else {
- QMetaObject::invokeMethod(this, "killTimers", Qt::BlockingQueuedConnection,
- Q_ARG(bool, false));
- }
+ if (QThread::currentThread() == coreThread.get()) {
+ killTimers();
+ } else {
+ // ensure the timer is stopped, even if not called from this thread
+ QMetaObject::invokeMethod(this, "killTimers", Qt::BlockingQueuedConnection);
}
coreThread->exit(0);
- if (QThread::currentThread() != coreThread) {
- while (coreThread->isRunning()) {
- qApp->processEvents();
- coreThread->wait(500);
- }
- }
- deadifyTox();
+ // need to reset av first, because it uses tox
+ av.reset();
+ tox.reset();
}
/**
- * @brief Returns the global widget's Core instance
+ * @brief Registers all toxcore callbacks
+ * @param tox Tox instance to register the callbacks on
*/
-Core* Core::getInstance()
+void Core::registerCallbacks(Tox* tox)
{
- return Nexus::getCore();
-}
-
-const CoreAV* Core::getAv() const
-{
- return av;
-}
-
-CoreAV* Core::getAv()
-{
- return av;
-}
-
-namespace {
-
-class ToxOptionsDeleter
-{
-public:
- void operator()(Tox_Options* options)
- {
- tox_options_free(options);
- }
-};
-
-using ToxOptionsPtr = std::unique_ptr;
-
-/**
- * @brief Map TOX_LOG_LEVEL to a string
- * @param level log level
- * @return Descriptive string for the log level
- */
-static 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 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:
- 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;
- }
-}
-
-/**
- * @brief Initializes Tox_Options instance
- * @param savedata Previously saved Tox data
- * @return Tox_Options instance needed to create Tox instance
- */
-ToxOptionsPtr initToxOptions(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();
- const 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();
- QByteArray proxyAddrData = proxyAddr.toUtf8();
-
- 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.";
- }
-
- ToxOptionsPtr toxOptions = ToxOptionsPtr(tox_options_new(nullptr));
- // register log first, to get messages as early as possible
- tox_options_set_log_callback(toxOptions.get(), onLogMessage);
-
- tox_options_set_ipv6_enabled(toxOptions.get(), enableIPv6);
- tox_options_set_udp_enabled(toxOptions.get(), !forceTCP);
- tox_options_set_local_discovery_enabled(toxOptions.get(), enableLanDiscovery);
- tox_options_set_start_port(toxOptions.get(), 0);
- tox_options_set_end_port(toxOptions.get(), 0);
-
- // No proxy by default
- tox_options_set_proxy_type(toxOptions.get(), TOX_PROXY_TYPE_NONE);
- tox_options_set_proxy_host(toxOptions.get(), nullptr);
- tox_options_set_proxy_port(toxOptions.get(), 0);
- tox_options_set_savedata_type(toxOptions.get(), !savedata.isNull() ? TOX_SAVEDATA_TYPE_TOX_SAVE : TOX_SAVEDATA_TYPE_NONE);
- tox_options_set_savedata_data(toxOptions.get(), reinterpret_cast(savedata.data()), savedata.size());
-
- 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.get(), TOX_PROXY_TYPE_SOCKS5);
- } else if (proxyType == ICoreSettings::ProxyType::ptHTTP) {
- tox_options_set_proxy_type(toxOptions.get(), TOX_PROXY_TYPE_HTTP);
- }
-
- tox_options_set_proxy_host(toxOptions.get(), proxyAddrData.data());
- tox_options_set_proxy_port(toxOptions.get(), proxyPort);
- }
- }
-
- return toxOptions;
-}
-
-} // namespace
-
-/**
- * @brief Creates Tox instance from previously saved data
- * @param savedata Previously saved Tox data - null, if new profile was created
- */
-void Core::makeTox(QByteArray savedata)
-{
- ToxOptionsPtr toxOptions = initToxOptions(savedata, s);
- if (toxOptions == nullptr) {
- qCritical() << "could not allocate Tox Options data structure";
- emit failedToStart();
- return;
- }
-
- TOX_ERR_NEW tox_err;
- tox = tox_new(toxOptions.get(), &tox_err);
-
- switch (tox_err) {
- case TOX_ERR_NEW_OK:
- break;
-
- case TOX_ERR_NEW_LOAD_BAD_FORMAT:
- qCritical() << "failed to parse Tox save data";
- emit failedToStart();
- return;
-
- case TOX_ERR_NEW_PORT_ALLOC:
- if (s->getEnableIPv6()) {
- tox_options_set_ipv6_enabled(toxOptions.get(), false);
- tox = tox_new(toxOptions.get(), &tox_err);
- if (tox_err == TOX_ERR_NEW_OK) {
- qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery "
- "may not work properly.";
- break;
- }
- }
-
- qCritical() << "can't to bind the port";
- emit failedToStart();
- return;
-
- case TOX_ERR_NEW_PROXY_BAD_HOST:
- case TOX_ERR_NEW_PROXY_BAD_PORT:
- case TOX_ERR_NEW_PROXY_BAD_TYPE:
- qCritical() << "bad proxy, error code:" << tox_err;
- emit badProxy();
- return;
-
- case TOX_ERR_NEW_PROXY_NOT_FOUND:
- qCritical() << "proxy not found";
- emit badProxy();
- return;
-
- case TOX_ERR_NEW_LOAD_ENCRYPTED:
- qCritical() << "attempted to load encrypted Tox save data";
- emit failedToStart();
- return;
-
- case TOX_ERR_NEW_MALLOC:
- qCritical() << "memory allocation failed";
- emit failedToStart();
- return;
-
- case TOX_ERR_NEW_NULL:
- qCritical() << "a parameter was null";
- emit failedToStart();
- return;
-
- default:
- qCritical() << "Tox core failed to start, unknown error code:" << tox_err;
- emit failedToStart();
- return;
- }
-}
-
-/**
- * @brief Initializes the core, must be called before anything else
- */
-void Core::start(const QByteArray& savedata)
-{
- bool isNewProfile = profile.isNewProfile();
- if (isNewProfile) {
- qDebug() << "Creating a new profile";
- makeTox(QByteArray());
- setStatusMessage(tr("Toxing on qTox"));
- setUsername(profile.getName());
- } else {
- qDebug() << "Loading user profile";
- if (savedata.isEmpty()) {
- emit failedToStart();
- return;
- }
-
- makeTox(savedata);
- }
-
- qsrand(time(nullptr));
- if (!tox) {
- ready = true;
- GUI::setEnabled(true);
- return;
- }
-
- // toxcore is successfully created, create toxav
- av = new CoreAV(tox);
- if (!av->getToxAv()) {
- qCritical() << "Toxav failed to start";
- emit failedToStart();
- deadifyTox();
- return;
- }
-
- // set GUI with user and statusmsg
- QString name = getUsername();
- if (!name.isEmpty()) {
- emit usernameSet(name);
- }
-
- QString msg = getStatusMessage();
- if (!msg.isEmpty()) {
- emit statusMessageSet(msg);
- }
-
- ToxId id = getSelfId();
- // TODO: probably useless check, comes basically directly from toxcore
- if (id.isValid()) {
- emit idSet(id);
- }
-
- loadFriends();
-
tox_callback_friend_request(tox, onFriendRequest);
tox_callback_friend_message(tox, onFriendMessage);
tox_callback_friend_name(tox, onFriendNameChange);
@@ -393,22 +97,200 @@ void Core::start(const QByteArray& savedata)
tox_callback_file_recv(tox, CoreFile::onFileReceiveCallback);
tox_callback_file_recv_chunk(tox, CoreFile::onFileRecvChunkCallback);
tox_callback_file_recv_control(tox, CoreFile::onFileControlCallback);
+}
- ready = true;
+/**
+ * @brief Factory method for the Core object
+ * @param savedata empty if new profile or saved data else
+ * @param settings Settings specific to Core
+ * @return nullptr or a Core object ready to start
+ */
+ToxCorePtr Core::makeToxCore(const QByteArray& savedata, const ICoreSettings* const settings,
+ ToxCoreErrors* err)
+{
+ QThread* thread = new QThread();
+ if (thread == nullptr) {
+ qCritical() << "could not allocate Core thread";
+ return {};
+ }
+ thread->setObjectName("qTox Core");
- if (isNewProfile) {
- profile.saveToxSave();
+ auto toxOptions = ToxOptions::makeToxOptions(savedata, settings);
+ if (toxOptions == nullptr) {
+ qCritical() << "could not allocate Tox Options data structure";
+ if (err) {
+ *err = ToxCoreErrors::ERROR_ALLOC;
+ }
+ return {};
}
- if (isReady()) {
- GUI::setEnabled(true);
+ ToxCorePtr core(new Core(thread));
+ if (core == nullptr) {
+ if (err) {
+ *err = ToxCoreErrors::ERROR_ALLOC;
+ }
+ return {};
}
+ TOX_ERR_NEW tox_err;
+ core->tox = ToxPtr(tox_new(*toxOptions, &tox_err));
+
+ switch (tox_err) {
+ case TOX_ERR_NEW_OK:
+ break;
+
+ case TOX_ERR_NEW_LOAD_BAD_FORMAT:
+ qCritical() << "failed to parse Tox save data";
+ if (err) {
+ *err = ToxCoreErrors::BAD_PROXY;
+ }
+ return {};
+
+ case TOX_ERR_NEW_PORT_ALLOC:
+ if (toxOptions->getIPv6Enabled()) {
+ toxOptions->setIPv6Enabled(false);
+ core->tox = ToxPtr(tox_new(*toxOptions, &tox_err));
+ if (tox_err == TOX_ERR_NEW_OK) {
+ qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery "
+ "may not work properly.";
+ break;
+ }
+ }
+
+ qCritical() << "can't to bind the port";
+ if (err) {
+ *err = ToxCoreErrors::FAILED_TO_START;
+ }
+ return {};
+
+ case TOX_ERR_NEW_PROXY_BAD_HOST:
+ case TOX_ERR_NEW_PROXY_BAD_PORT:
+ case TOX_ERR_NEW_PROXY_BAD_TYPE:
+ qCritical() << "bad proxy, error code:" << tox_err;
+ if (err) {
+ *err = ToxCoreErrors::BAD_PROXY;
+ }
+ return {};
+
+ case TOX_ERR_NEW_PROXY_NOT_FOUND:
+ qCritical() << "proxy not found";
+ if (err) {
+ *err = ToxCoreErrors::BAD_PROXY;
+ }
+ return {};
+
+ case TOX_ERR_NEW_LOAD_ENCRYPTED:
+ qCritical() << "attempted to load encrypted Tox save data";
+ if (err) {
+ *err = ToxCoreErrors::INVALID_SAVE;
+ }
+ return {};
+
+ case TOX_ERR_NEW_MALLOC:
+ qCritical() << "memory allocation failed";
+ if (err) {
+ *err = ToxCoreErrors::ERROR_ALLOC;
+ }
+ return {};
+
+ case TOX_ERR_NEW_NULL:
+ qCritical() << "a parameter was null";
+ if (err) {
+ *err = ToxCoreErrors::FAILED_TO_START;
+ }
+ return {};
+
+ default:
+ qCritical() << "Tox core failed to start, unknown error code:" << tox_err;
+ if (err) {
+ *err = ToxCoreErrors::FAILED_TO_START;
+ }
+ return {};
+ }
+
+ // provide a list of bootstrap nodes
+ core->bootstrapNodes = settings->getDhtServerList();
+
+ // tox should be valid by now
+ assert(core->tox != nullptr);
+
+ // toxcore is successfully created, create toxav
+ core->av = std::unique_ptr(new CoreAV(core->tox.get()));
+ if (!core->av || !core->av->getToxAv()) {
+ qCritical() << "Toxav failed to start";
+ if (err) {
+ *err = ToxCoreErrors::FAILED_TO_START;
+ }
+ return {};
+ }
+
+ registerCallbacks(core->tox.get());
+
+ // connect the thread with the Core
+ connect(thread, &QThread::started, core.get(), &Core::onStarted);
+ core->moveToThread(thread);
+ // since this is allocated in the constructor move it to the other thread too
+ core->toxTimer.moveToThread(thread);
+
+ // when leaving this function 'core' should be ready for it's start() action or
+ // a nullptr
+ return core;
+}
+
+void Core::onStarted()
+{
+ ASSERT_CORE_THREAD;
+
+ // One time initialization stuff
+ QString name = getUsername();
+ if (!name.isEmpty()) {
+ emit usernameSet(name);
+ }
+
+ QString msg = getStatusMessage();
+ if (!msg.isEmpty()) {
+ emit statusMessageSet(msg);
+ }
+
+ ToxId id = getSelfId();
+ // Id comes from toxcore, must be valid
+ assert(id.isValid());
+ emit idSet(id);
+
+ loadFriends();
+
process(); // starts its own timer
av->start();
emit avReady();
}
+/**
+ * @brief Starts toxcore and it's event loop, can be called from any thread
+ */
+void Core::start()
+{
+ coreThread->start();
+}
+
+
+/**
+ * @brief Returns the global widget's Core instance
+ */
+Core* Core::getInstance()
+{
+ return Nexus::getCore();
+}
+
+const CoreAV* Core::getAv() const
+{
+ return av.get();
+}
+
+CoreAV* Core::getAv()
+{
+ return av.get();
+}
+
/* Using the now commented out statements in checkConnection(), I watched how
* many ticks disconnects-after-initial-connect lasted. Out of roughly 15 trials,
* 5 disconnected; 4 were DCd for less than 20 ticks, while the 5th was ~50 ticks.
@@ -423,19 +305,23 @@ void Core::start(const QByteArray& savedata)
*/
void Core::process()
{
+ QMutexLocker ml{coreLoopLock.get()};
+
+ ASSERT_CORE_THREAD;
if (!isReady()) {
av->stop();
return;
}
static int tolerance = CORE_DISCONNECT_TOLERANCE;
- tox_iterate(tox, getInstance());
+ tox_iterate(tox.get(), this);
#ifdef DEBUG
// we want to see the debug messages immediately
fflush(stdout);
#endif
+ // TODO(sudden6): recheck if this is still necessary
if (checkConnection()) {
tolerance = CORE_DISCONNECT_TOLERANCE;
} else if (!(--tolerance)) {
@@ -443,14 +329,16 @@ void Core::process()
tolerance = 3 * CORE_DISCONNECT_TOLERANCE;
}
- unsigned sleeptime = qMin(tox_iteration_interval(tox), CoreFile::corefileIterationInterval());
- toxTimer->start(sleeptime);
+ unsigned sleeptime =
+ qMin(tox_iteration_interval(tox.get()), CoreFile::corefileIterationInterval());
+ toxTimer.start(sleeptime);
}
bool Core::checkConnection()
{
+ ASSERT_CORE_THREAD;
static bool isConnected = false;
- bool toxConnected = tox_self_get_connection_status(tox) != TOX_CONNECTION_NONE;
+ bool toxConnected = tox_self_get_connection_status(tox.get()) != TOX_CONNECTION_NONE;
if (toxConnected && !isConnected) {
qDebug() << "Connected to the DHT";
emit connected();
@@ -468,8 +356,8 @@ bool Core::checkConnection()
*/
void Core::bootstrapDht()
{
- QList dhtServerList = s->getDhtServerList();
- int listSize = dhtServerList.size();
+ ASSERT_CORE_THREAD;
+ int listSize = bootstrapNodes.size();
if (!listSize) {
qWarning() << "no bootstrap list?!?";
return;
@@ -479,7 +367,7 @@ void Core::bootstrapDht()
static int j = qrand() % listSize;
// i think the more we bootstrap, the more we jitter because the more we overwrite nodes
while (i < 2) {
- const DhtServer& dhtServer = dhtServerList[j % listSize];
+ const DhtServer& dhtServer = bootstrapNodes[j % listSize];
QString dhtServerAddress = dhtServer.address.toLatin1();
QString port = QString::number(dhtServer.port);
QString name = dhtServer.name;
@@ -491,11 +379,11 @@ void Core::bootstrapDht()
const uint8_t* pkPtr = reinterpret_cast(pk.getBytes());
- if (!tox_bootstrap(tox, address.constData(), dhtServer.port, pkPtr, nullptr)) {
+ if (!tox_bootstrap(tox.get(), address.constData(), dhtServer.port, pkPtr, nullptr)) {
qDebug() << "Error bootstrapping from " + dhtServer.name;
}
- if (!tox_add_tcp_relay(tox, address.constData(), dhtServer.port, pkPtr, nullptr)) {
+ if (!tox_add_tcp_relay(tox.get(), address.constData(), dhtServer.port, pkPtr, nullptr)) {
qDebug() << "Error adding TCP relay from " + dhtServer.name;
}
@@ -570,8 +458,8 @@ void Core::onConnectionStatusChanged(Tox*, uint32_t friendId, TOX_CONNECTION sta
}
}
-void Core::onGroupInvite(Tox* tox, uint32_t friendId, TOX_CONFERENCE_TYPE type, const uint8_t* cookie,
- size_t length, void* vCore)
+void Core::onGroupInvite(Tox* tox, uint32_t friendId, TOX_CONFERENCE_TYPE type,
+ const uint8_t* cookie, size_t length, void* vCore)
{
Core* core = static_cast(vCore);
// static_cast is used twice to replace using unsafe reinterpret_cast
@@ -592,8 +480,7 @@ void Core::onGroupInvite(Tox* tox, uint32_t friendId, TOX_CONFERENCE_TYPE type,
qDebug() << QString("AV group invite by %1").arg(friendId);
if (friendId == UINT32_MAX) {
// Rejoining existing (persistent) AV conference after disconnect and reconnect.
- toxav_join_av_groupchat(tox, friendId, cookie, length,
- CoreAV::groupCallCallback, core);
+ toxav_join_av_groupchat(tox, friendId, cookie, length, CoreAV::groupCallCallback, core);
return;
}
emit core->groupInviteReceived(inviteInfo);
@@ -625,8 +512,8 @@ void Core::onGroupPeerListChange(Tox*, uint32_t groupId, void* core)
emit static_cast(core)->groupPeerlistChanged(groupId);
}
-void Core::onGroupPeerNameChange(Tox*, uint32_t groupId, uint32_t peerId,
- const uint8_t* name, size_t length, void* core)
+void Core::onGroupPeerNameChange(Tox*, uint32_t groupId, uint32_t peerId, const uint8_t* name,
+ size_t length, void* core)
{
const auto newName = ToxString(name, length).getQString();
qDebug() << QString("Group %1, Peer %2, name changed to %3").arg(groupId).arg(peerId).arg(newName);
@@ -639,7 +526,7 @@ void Core::onGroupNamelistChange(Tox*, uint32_t groupId, uint32_t peerId,
TOX_CONFERENCE_STATE_CHANGE change, void* core)
{
CoreAV* coreAv = static_cast(core)->getAv();
- const auto changed = change == TOX_CONFERENCE_STATE_CHANGE_PEER_EXIT;
+ const auto changed = change == TOX_CONFERENCE_STATE_CHANGE_PEER_EXIT;
if (changed && coreAv->isGroupAvEnabled(groupId)) {
CoreAV::invalidateGroupCallPeerSource(groupId, peerId);
}
@@ -664,12 +551,13 @@ void Core::onReadReceiptCallback(Tox*, uint32_t friendId, uint32_t receipt, void
void Core::acceptFriendRequest(const ToxPk& friendPk)
{
+ QMutexLocker ml{coreLoopLock.get()};
// TODO: error handling
- uint32_t friendId = tox_friend_add_norequest(tox, friendPk.getBytes(), nullptr);
+ uint32_t friendId = tox_friend_add_norequest(tox.get(), friendPk.getBytes(), nullptr);
if (friendId == std::numeric_limits::max()) {
emit failedToAddFriend(friendPk);
} else {
- profile.saveToxSave();
+ emit saveRequest();
emit friendAdded(friendId, friendPk);
}
}
@@ -682,6 +570,8 @@ void Core::acceptFriendRequest(const ToxPk& friendPk)
*/
QString Core::getFriendRequestErrorMessage(const ToxId& friendId, const QString& message) const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
if (!friendId.isValid()) {
return tr("Invalid Tox ID", "Error while sending friendship request");
}
@@ -704,17 +594,19 @@ QString Core::getFriendRequestErrorMessage(const ToxId& friendId, const QString&
void Core::requestFriendship(const ToxId& friendId, const QString& message)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
ToxPk friendPk = friendId.getPublicKey();
QString errorMessage = getFriendRequestErrorMessage(friendId, message);
if (!errorMessage.isNull()) {
emit failedToAddFriend(friendPk, errorMessage);
- profile.saveToxSave();
+ emit saveRequest();
return;
}
ToxString cMessage(message);
uint32_t friendNumber =
- tox_friend_add(tox, friendId.getBytes(), cMessage.data(), cMessage.size(), nullptr);
+ tox_friend_add(tox.get(), friendId.getBytes(), cMessage.data(), cMessage.size(), nullptr);
if (friendNumber == std::numeric_limits::max()) {
qDebug() << "Failed to request friendship";
emit failedToAddFriend(friendPk);
@@ -724,32 +616,34 @@ void Core::requestFriendship(const ToxId& friendId, const QString& message)
emit requestSent(friendPk, message);
}
- profile.saveToxSave();
+ emit saveRequest();
}
int Core::sendMessage(uint32_t friendId, const QString& message)
{
- QMutexLocker ml(&messageSendMutex);
+ QMutexLocker ml(coreLoopLock.get());
ToxString cMessage(message);
- int receipt = tox_friend_send_message(tox, friendId, TOX_MESSAGE_TYPE_NORMAL, cMessage.data(),
- cMessage.size(), nullptr);
+ int receipt = tox_friend_send_message(tox.get(), friendId, TOX_MESSAGE_TYPE_NORMAL,
+ cMessage.data(), cMessage.size(), nullptr);
emit messageSentResult(friendId, message, receipt);
return receipt;
}
int Core::sendAction(uint32_t friendId, const QString& action)
{
- QMutexLocker ml(&messageSendMutex);
+ QMutexLocker ml(coreLoopLock.get());
ToxString cMessage(action);
- int receipt = tox_friend_send_message(tox, friendId, TOX_MESSAGE_TYPE_ACTION, cMessage.data(),
- cMessage.size(), nullptr);
+ int receipt = tox_friend_send_message(tox.get(), friendId, TOX_MESSAGE_TYPE_ACTION,
+ cMessage.data(), cMessage.size(), nullptr);
emit messageSentResult(friendId, action, receipt);
return receipt;
}
void Core::sendTyping(uint32_t friendId, bool typing)
{
- if (!tox_self_set_typing(tox, friendId, typing, nullptr)) {
+ QMutexLocker ml{coreLoopLock.get()};
+
+ if (!tox_self_set_typing(tox.get(), friendId, typing, nullptr)) {
emit failedToSetTyping(typing);
}
}
@@ -784,12 +678,15 @@ bool parseConferenceSendMessageError(TOX_ERR_CONFERENCE_SEND_MESSAGE error)
void Core::sendGroupMessageWithType(int groupId, const QString& message, TOX_MESSAGE_TYPE type)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
QStringList cMessages = splitMessage(message, MAX_GROUP_MESSAGE_LEN);
for (auto& part : cMessages) {
ToxString cMsg(part);
TOX_ERR_CONFERENCE_SEND_MESSAGE error;
- bool ok = tox_conference_send_message(tox, groupId, type, cMsg.data(), cMsg.size(), &error);
+ bool ok =
+ tox_conference_send_message(tox.get(), groupId, type, cMsg.data(), cMsg.size(), &error);
if (!ok || !parseConferenceSendMessageError(error)) {
emit groupSentFailed(groupId);
return;
@@ -799,19 +696,25 @@ void Core::sendGroupMessageWithType(int groupId, const QString& message, TOX_MES
void Core::sendGroupMessage(int groupId, const QString& message)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
sendGroupMessageWithType(groupId, message, TOX_MESSAGE_TYPE_NORMAL);
}
void Core::sendGroupAction(int groupId, const QString& message)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
sendGroupMessageWithType(groupId, message, TOX_MESSAGE_TYPE_ACTION);
}
void Core::changeGroupTitle(int groupId, const QString& title)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
ToxString cTitle(title);
TOX_ERR_CONFERENCE_TITLE error;
- bool success = tox_conference_set_title(tox, groupId, cTitle.data(), cTitle.size(), &error);
+ bool success = tox_conference_set_title(tox.get(), groupId, cTitle.data(), cTitle.size(), &error);
if (success && error == TOX_ERR_CONFERENCE_TITLE_OK) {
emit groupTitleChanged(groupId, getUsername(), title);
return;
@@ -838,67 +741,87 @@ void Core::changeGroupTitle(int groupId, const QString& title)
void Core::sendFile(uint32_t friendId, QString filename, QString filePath, long long filesize)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
CoreFile::sendFile(this, friendId, filename, filePath, filesize);
}
void Core::sendAvatarFile(uint32_t friendId, const QByteArray& data)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
CoreFile::sendAvatarFile(this, friendId, data);
}
void Core::pauseResumeFileSend(uint32_t friendId, uint32_t fileNum)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
CoreFile::pauseResumeFileSend(this, friendId, fileNum);
}
void Core::pauseResumeFileRecv(uint32_t friendId, uint32_t fileNum)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
CoreFile::pauseResumeFileRecv(this, friendId, fileNum);
}
void Core::cancelFileSend(uint32_t friendId, uint32_t fileNum)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
CoreFile::cancelFileSend(this, friendId, fileNum);
}
void Core::cancelFileRecv(uint32_t friendId, uint32_t fileNum)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
CoreFile::cancelFileRecv(this, friendId, fileNum);
}
void Core::rejectFileRecvRequest(uint32_t friendId, uint32_t fileNum)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
CoreFile::rejectFileRecvRequest(this, friendId, fileNum);
}
void Core::acceptFileRecvRequest(uint32_t friendId, uint32_t fileNum, QString path)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
CoreFile::acceptFileRecvRequest(this, friendId, fileNum, path);
}
void Core::removeFriend(uint32_t friendId, bool fake)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
if (!isReady() || fake) {
return;
}
- if (!tox_friend_delete(tox, friendId, nullptr)) {
+ if (!tox_friend_delete(tox.get(), friendId, nullptr)) {
emit failedToRemoveFriend(friendId);
return;
}
- profile.saveToxSave();
+ emit saveRequest();
emit friendRemoved(friendId);
}
void Core::removeGroup(int groupId, bool fake)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
if (!isReady() || fake) {
return;
}
TOX_ERR_CONFERENCE_DELETE error;
- bool success = tox_conference_delete(tox, groupId, &error);
+ bool success = tox_conference_delete(tox.get(), groupId, &error);
if (success && error == TOX_ERR_CONFERENCE_DELETE_OK) {
av->leaveGroupCall(groupId);
return;
@@ -920,14 +843,16 @@ void Core::removeGroup(int groupId, bool fake)
*/
QString Core::getUsername() const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
QString sname;
if (!tox) {
return sname;
}
- int size = tox_self_get_name_size(tox);
+ int size = tox_self_get_name_size(tox.get());
uint8_t* name = new uint8_t[size];
- tox_self_get_name(tox, name);
+ tox_self_get_name(tox.get(), name);
sname = ToxString(name, size).getQString();
delete[] name;
return sname;
@@ -935,20 +860,20 @@ QString Core::getUsername() const
void Core::setUsername(const QString& username)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
if (username == getUsername()) {
return;
}
ToxString cUsername(username);
- if (!tox_self_set_name(tox, cUsername.data(), cUsername.size(), nullptr)) {
+ if (!tox_self_set_name(tox.get(), cUsername.data(), cUsername.size(), nullptr)) {
emit failedToSetUsername(username);
return;
}
emit usernameSet(username);
- if (ready) {
- profile.saveToxSave();
- }
+ emit saveRequest();
}
/**
@@ -956,8 +881,10 @@ void Core::setUsername(const QString& username)
*/
ToxId Core::getSelfId() const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
uint8_t friendId[TOX_ADDRESS_SIZE] = {0x00};
- tox_self_get_address(tox, friendId);
+ tox_self_get_address(tox.get(), friendId);
return ToxId(friendId, TOX_ADDRESS_SIZE);
}
@@ -967,8 +894,10 @@ ToxId Core::getSelfId() const
*/
ToxPk Core::getSelfPublicKey() const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
uint8_t friendId[TOX_ADDRESS_SIZE] = {0x00};
- tox_self_get_address(tox, friendId);
+ tox_self_get_address(tox.get(), friendId);
return ToxPk(friendId);
}
@@ -977,6 +906,8 @@ ToxPk Core::getSelfPublicKey() const
*/
QPair Core::getKeypair() const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
QPair keypair;
if (!tox) {
return keypair;
@@ -984,8 +915,8 @@ QPair Core::getKeypair() const
QByteArray pk(TOX_PUBLIC_KEY_SIZE, 0x00);
QByteArray sk(TOX_SECRET_KEY_SIZE, 0x00);
- tox_self_get_public_key(tox, reinterpret_cast(pk.data()));
- tox_self_get_secret_key(tox, reinterpret_cast(sk.data()));
+ tox_self_get_public_key(tox.get(), reinterpret_cast(pk.data()));
+ tox_self_get_secret_key(tox.get(), reinterpret_cast(sk.data()));
keypair.first = pk;
keypair.second = sk;
return keypair;
@@ -996,14 +927,16 @@ QPair Core::getKeypair() const
*/
QString Core::getStatusMessage() const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
QString sname;
if (!tox) {
return sname;
}
- size_t size = tox_self_get_status_message_size(tox);
+ size_t size = tox_self_get_status_message_size(tox.get());
uint8_t* name = new uint8_t[size];
- tox_self_get_status_message(tox, name);
+ tox_self_get_status_message(tox.get(), name);
sname = ToxString(name, size).getQString();
delete[] name;
return sname;
@@ -1014,30 +947,33 @@ QString Core::getStatusMessage() const
*/
Status Core::getStatus() const
{
- return static_cast(tox_self_get_status(tox));
+ QMutexLocker ml{coreLoopLock.get()};
+
+ return static_cast(tox_self_get_status(tox.get()));
}
void Core::setStatusMessage(const QString& message)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
if (message == getStatusMessage()) {
return;
}
ToxString cMessage(message);
- if (!tox_self_set_status_message(tox, cMessage.data(), cMessage.size(), nullptr)) {
+ if (!tox_self_set_status_message(tox.get(), cMessage.data(), cMessage.size(), nullptr)) {
emit failedToSetStatusMessage(message);
return;
}
- if (ready) {
- profile.saveToxSave();
- }
-
+ emit saveRequest();
emit statusMessageSet(message);
}
void Core::setStatus(Status status)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
TOX_USER_STATUS userstatus;
switch (status) {
case Status::Online:
@@ -1057,8 +993,8 @@ void Core::setStatus(Status status)
break;
}
- tox_self_set_status(tox, userstatus);
- profile.saveToxSave();
+ tox_self_set_status(tox.get(), userstatus);
+ emit saveRequest();
emit statusSet(status);
}
@@ -1067,39 +1003,43 @@ void Core::setStatus(Status status)
*/
QByteArray Core::getToxSaveData()
{
- uint32_t fileSize = tox_get_savedata_size(tox);
+ QMutexLocker ml{coreLoopLock.get()};
+
+ uint32_t fileSize = tox_get_savedata_size(tox.get());
QByteArray data;
data.resize(fileSize);
- tox_get_savedata(tox, (uint8_t*)data.data());
+ tox_get_savedata(tox.get(), (uint8_t*)data.data());
return data;
}
// Declared to avoid code duplication
-#define GET_FRIEND_PROPERTY(property, function, checkSize) \
- const size_t property##Size = function##_size(tox, ids[i], nullptr); \
- if ((!checkSize || property##Size) && property##Size != SIZE_MAX) { \
- uint8_t* prop = new uint8_t[property##Size]; \
- if (function(tox, ids[i], prop, nullptr)) { \
- QString propStr = ToxString(prop, property##Size).getQString(); \
- emit friend##property##Changed(ids[i], propStr); \
- } \
- \
- delete[] prop; \
+#define GET_FRIEND_PROPERTY(property, function, checkSize) \
+ const size_t property##Size = function##_size(tox.get(), ids[i], nullptr); \
+ if ((!checkSize || property##Size) && property##Size != SIZE_MAX) { \
+ uint8_t* prop = new uint8_t[property##Size]; \
+ if (function(tox.get(), ids[i], prop, nullptr)) { \
+ QString propStr = ToxString(prop, property##Size).getQString(); \
+ emit friend##property##Changed(ids[i], propStr); \
+ } \
+ \
+ delete[] prop; \
}
void Core::loadFriends()
{
- const uint32_t friendCount = tox_self_get_friend_list_size(tox);
+ QMutexLocker ml{coreLoopLock.get()};
+
+ const uint32_t friendCount = tox_self_get_friend_list_size(tox.get());
if (friendCount == 0) {
return;
}
// assuming there are not that many friends to fill up the whole stack
uint32_t* ids = new uint32_t[friendCount];
- tox_self_get_friend_list(tox, ids);
+ tox_self_get_friend_list(tox.get(), ids);
uint8_t friendPk[TOX_PUBLIC_KEY_SIZE] = {0x00};
for (uint32_t i = 0; i < friendCount; ++i) {
- if (!tox_friend_get_public_key(tox, ids[i], friendPk, nullptr)) {
+ if (!tox_friend_get_public_key(tox.get(), ids[i], friendPk, nullptr)) {
continue;
}
@@ -1113,7 +1053,9 @@ void Core::loadFriends()
void Core::checkLastOnline(uint32_t friendId)
{
- const uint64_t lastOnline = tox_friend_get_last_online(tox, friendId, nullptr);
+ QMutexLocker ml{coreLoopLock.get()};
+
+ const uint64_t lastOnline = tox_friend_get_last_online(tox.get(), friendId, nullptr);
if (lastOnline != std::numeric_limits::max()) {
emit friendLastSeenChanged(friendId, QDateTime::fromTime_t(lastOnline));
}
@@ -1124,9 +1066,11 @@ void Core::checkLastOnline(uint32_t friendId)
*/
QVector Core::getFriendList() const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
QVector friends;
- friends.resize(tox_self_get_friend_list_size(tox));
- tox_self_get_friend_list(tox, friends.data());
+ friends.resize(tox_self_get_friend_list_size(tox.get()));
+ tox_self_get_friend_list(tox.get(), friends.data());
return friends;
}
@@ -1154,6 +1098,7 @@ bool Core::parsePeerQueryError(TOX_ERR_CONFERENCE_PEER_QUERY error) const
return false;
default:
+ qCritical() << "Unknow error code:" << error;
return false;
}
}
@@ -1164,8 +1109,10 @@ bool Core::parsePeerQueryError(TOX_ERR_CONFERENCE_PEER_QUERY error) const
*/
uint32_t Core::getGroupNumberPeers(int groupId) const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
TOX_ERR_CONFERENCE_PEER_QUERY error;
- uint32_t count = tox_conference_peer_count(tox, groupId, &error);
+ uint32_t count = tox_conference_peer_count(tox.get(), groupId, &error);
if (!parsePeerQueryError(error)) {
return std::numeric_limits::max();
}
@@ -1178,15 +1125,17 @@ uint32_t Core::getGroupNumberPeers(int groupId) const
*/
QString Core::getGroupPeerName(int groupId, int peerId) const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
TOX_ERR_CONFERENCE_PEER_QUERY error;
- size_t length = tox_conference_peer_get_name_size(tox, groupId, peerId, &error);
+ size_t length = tox_conference_peer_get_name_size(tox.get(), groupId, peerId, &error);
if (!parsePeerQueryError(error)) {
return QString{};
}
QByteArray name(length, Qt::Uninitialized);
uint8_t* namePtr = static_cast(static_cast(name.data()));
- bool success = tox_conference_peer_get_name(tox, groupId, peerId, namePtr, &error);
+ bool success = tox_conference_peer_get_name(tox.get(), groupId, peerId, namePtr, &error);
if (!parsePeerQueryError(error) || !success) {
qWarning() << "getGroupPeerName: Unknown error";
return QString{};
@@ -1200,9 +1149,11 @@ QString Core::getGroupPeerName(int groupId, int peerId) const
*/
ToxPk Core::getGroupPeerPk(int groupId, int peerId) const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
uint8_t friendPk[TOX_PUBLIC_KEY_SIZE] = {0x00};
TOX_ERR_CONFERENCE_PEER_QUERY error;
- bool success = tox_conference_peer_get_public_key(tox, groupId, peerId, friendPk, &error);
+ bool success = tox_conference_peer_get_public_key(tox.get(), groupId, peerId, friendPk, &error);
if (!parsePeerQueryError(error) || !success) {
qWarning() << "getGroupPeerToxId: Unknown error";
return ToxPk{};
@@ -1216,6 +1167,8 @@ ToxPk Core::getGroupPeerPk(int groupId, int peerId) const
*/
QStringList Core::getGroupPeerNames(int groupId) const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
if (!tox) {
qWarning() << "Can't get group peer names, tox is null";
return {};
@@ -1228,7 +1181,7 @@ QStringList Core::getGroupPeerNames(int groupId) const
}
TOX_ERR_CONFERENCE_PEER_QUERY error;
- uint32_t count = tox_conference_peer_count(tox, groupId, &error);
+ uint32_t count = tox_conference_peer_count(tox.get(), groupId, &error);
if (!parsePeerQueryError(error)) {
return {};
}
@@ -1240,14 +1193,14 @@ QStringList Core::getGroupPeerNames(int groupId) const
QStringList names;
for (uint32_t i = 0; i < nPeers; ++i) {
- size_t length = tox_conference_peer_get_name_size(tox, groupId, i, &error);
+ size_t length = tox_conference_peer_get_name_size(tox.get(), groupId, i, &error);
if (!parsePeerQueryError(error)) {
continue;
}
QByteArray name(length, Qt::Uninitialized);
uint8_t* namePtr = static_cast(static_cast(name.data()));
- bool ok = tox_conference_peer_get_name(tox, groupId, i, namePtr, &error);
+ bool ok = tox_conference_peer_get_name(tox.get(), groupId, i, namePtr, &error);
if (ok && parsePeerQueryError(error)) {
names.append(ToxString(name).getQString());
}
@@ -1292,6 +1245,7 @@ bool Core::parseConferenceJoinError(TOX_ERR_CONFERENCE_JOIN error) const
return false;
default:
+ qCritical() << "Unknow error code:" << error;
return false;
}
}
@@ -1304,6 +1258,8 @@ bool Core::parseConferenceJoinError(TOX_ERR_CONFERENCE_JOIN error) const
*/
uint32_t Core::joinGroupchat(const GroupInvite& inviteInfo) const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
const uint32_t friendId = inviteInfo.getFriendId();
const uint8_t confType = inviteInfo.getType();
const QByteArray invite = inviteInfo.getInvite();
@@ -1313,13 +1269,13 @@ uint32_t Core::joinGroupchat(const GroupInvite& inviteInfo) const
case TOX_CONFERENCE_TYPE_TEXT: {
qDebug() << QString("Trying to join text groupchat invite sent by friend %1").arg(friendId);
TOX_ERR_CONFERENCE_JOIN error;
- uint32_t groupId = tox_conference_join(tox, friendId, cookie, cookieLength, &error);
+ uint32_t groupId = tox_conference_join(tox.get(), friendId, cookie, cookieLength, &error);
return parseConferenceJoinError(error) ? groupId : std::numeric_limits::max();
}
case TOX_CONFERENCE_TYPE_AV: {
qDebug() << QString("Trying to join AV groupchat invite sent by friend %1").arg(friendId);
- return toxav_join_av_groupchat(tox, friendId, cookie, cookieLength,
+ return toxav_join_av_groupchat(tox.get(), friendId, cookie, cookieLength,
CoreAV::groupCallCallback, const_cast(this));
}
@@ -1332,8 +1288,10 @@ uint32_t Core::joinGroupchat(const GroupInvite& inviteInfo) const
void Core::groupInviteFriend(uint32_t friendId, int groupId)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
TOX_ERR_CONFERENCE_INVITE error;
- tox_conference_invite(tox, friendId, groupId, &error);
+ tox_conference_invite(tox.get(), friendId, groupId, &error);
switch (error) {
case TOX_ERR_CONFERENCE_INVITE_OK:
@@ -1354,9 +1312,11 @@ void Core::groupInviteFriend(uint32_t friendId, int groupId)
int Core::createGroup(uint8_t type)
{
+ QMutexLocker ml{coreLoopLock.get()};
+
if (type == TOX_CONFERENCE_TYPE_TEXT) {
TOX_ERR_CONFERENCE_NEW error;
- uint32_t groupId = tox_conference_new(tox, &error);
+ uint32_t groupId = tox_conference_new(tox.get(), &error);
switch (error) {
case TOX_ERR_CONFERENCE_NEW_OK:
@@ -1371,7 +1331,7 @@ int Core::createGroup(uint8_t type)
return std::numeric_limits::max();
}
} else if (type == TOX_CONFERENCE_TYPE_AV) {
- uint32_t groupId = toxav_add_av_groupchat(tox, CoreAV::groupCallCallback, this);
+ uint32_t groupId = toxav_add_av_groupchat(tox.get(), CoreAV::groupCallCallback, this);
emit emptyGroupCreated(groupId);
return groupId;
} else {
@@ -1385,7 +1345,9 @@ int Core::createGroup(uint8_t type)
*/
bool Core::isFriendOnline(uint32_t friendId) const
{
- TOX_CONNECTION connetion = tox_friend_get_connection_status(tox, friendId, nullptr);
+ QMutexLocker ml{coreLoopLock.get()};
+
+ TOX_CONNECTION connetion = tox_friend_get_connection_status(tox.get(), friendId, nullptr);
return connetion != TOX_CONNECTION_NONE;
}
@@ -1394,12 +1356,14 @@ bool Core::isFriendOnline(uint32_t friendId) const
*/
bool Core::hasFriendWithPublicKey(const ToxPk& publicKey) const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
if (publicKey.isEmpty()) {
return false;
}
// TODO: error handling
- uint32_t friendId = tox_friend_by_public_key(tox, publicKey.getBytes(), nullptr);
+ uint32_t friendId = tox_friend_by_public_key(tox.get(), publicKey.getBytes(), nullptr);
return friendId != std::numeric_limits::max();
}
@@ -1408,8 +1372,10 @@ bool Core::hasFriendWithPublicKey(const ToxPk& publicKey) const
*/
ToxPk Core::getFriendPublicKey(uint32_t friendNumber) const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
uint8_t rawid[TOX_PUBLIC_KEY_SIZE];
- if (!tox_friend_get_public_key(tox, friendNumber, rawid, nullptr)) {
+ if (!tox_friend_get_public_key(tox.get(), friendNumber, rawid, nullptr)) {
qWarning() << "getFriendPublicKey: Getting public key failed";
return ToxPk();
}
@@ -1422,14 +1388,16 @@ ToxPk Core::getFriendPublicKey(uint32_t friendNumber) const
*/
QString Core::getFriendUsername(uint32_t friendnumber) const
{
- size_t namesize = tox_friend_get_name_size(tox, friendnumber, nullptr);
+ QMutexLocker ml{coreLoopLock.get()};
+
+ size_t namesize = tox_friend_get_name_size(tox.get(), friendnumber, nullptr);
if (namesize == SIZE_MAX) {
qWarning() << "getFriendUsername: Failed to get name size for friend " << friendnumber;
return QString();
}
uint8_t* name = new uint8_t[namesize];
- tox_friend_get_name(tox, friendnumber, name, nullptr);
+ tox_friend_get_name(tox.get(), friendnumber, name, nullptr);
ToxString sname(name, namesize);
delete[] name;
return sname.getQString();
@@ -1469,20 +1437,22 @@ QStringList Core::splitMessage(const QString& message, int maxLen)
QString Core::getPeerName(const ToxPk& id) const
{
+ QMutexLocker ml{coreLoopLock.get()};
+
QString name;
- uint32_t friendId = tox_friend_by_public_key(tox, id.getBytes(), nullptr);
+ uint32_t friendId = tox_friend_by_public_key(tox.get(), id.getBytes(), nullptr);
if (friendId == std::numeric_limits::max()) {
qWarning() << "getPeerName: No such peer";
return name;
}
- const size_t nameSize = tox_friend_get_name_size(tox, friendId, nullptr);
+ const size_t nameSize = tox_friend_get_name_size(tox.get(), friendId, nullptr);
if (nameSize == SIZE_MAX) {
return name;
}
uint8_t* cname = new uint8_t[nameSize < tox_max_name_length() ? tox_max_name_length() : nameSize];
- if (!tox_friend_get_name(tox, friendId, cname, nullptr)) {
+ if (!tox_friend_get_name(tox.get(), friendId, cname, nullptr)) {
qWarning() << "getPeerName: Can't get name of friend " + QString().setNum(friendId);
delete[] cname;
return name;
@@ -1498,7 +1468,7 @@ QString Core::getPeerName(const ToxPk& id) const
*/
bool Core::isReady() const
{
- return av && av->getToxAv() && tox && ready;
+ return av && av->getToxAv() && tox;
}
/**
@@ -1507,37 +1477,20 @@ bool Core::isReady() const
*/
void Core::setNospam(uint32_t nospam)
{
- tox_self_set_nospam(tox, nospam);
+ QMutexLocker ml{coreLoopLock.get()};
+
+ tox_self_set_nospam(tox.get(), nospam);
emit idSet(getSelfId());
}
/**
- * @brief Returns the unencrypted tox save data
+ * @brief Stops the AV thread and the timer here
*/
-void Core::killTimers(bool onlyStop)
+void Core::killTimers()
{
- assert(QThread::currentThread() == coreThread);
+ ASSERT_CORE_THREAD;
if (av) {
av->stop();
}
- toxTimer->stop();
- if (!onlyStop) {
- delete toxTimer;
- toxTimer = nullptr;
- }
-}
-
-/**
- * @brief Reinitialized the core.
- * @warning Must be called from the Core thread, with the GUI thread ready to process events.
- */
-void Core::reset()
-{
- assert(QThread::currentThread() == coreThread);
- QByteArray toxsave = getToxSaveData();
- ready = false;
- killTimers(true);
- deadifyTox();
- GUI::clearContacts();
- start(toxsave);
+ toxTimer.stop();
}
diff --git a/src/core/core.h b/src/core/core.h
index 626d4bc41..7faf44887 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -24,18 +24,21 @@
#include "toxfile.h"
#include "toxid.h"
+#include "src/core/dhtserver.h"
#include
#include
#include
+#include
+#include
#include
+#include
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;
+
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;
+ ToxPtr tox;
+
+ std::unique_ptr av;
+ QTimer toxTimer;
+ // recursive, since we might call our own functions
+ // pointer so we can circumvent const functions
+ std::unique_ptr coreLoopLock = nullptr;
+
+ std::unique_ptr coreThread = nullptr;
+ QList bootstrapNodes{};
friend class Audio; ///< Audio can access our calls directly to reduce latency
friend class CoreFile; ///< CoreFile can access tox* and emit our signals
diff --git a/src/core/coreav.cpp b/src/core/coreav.cpp
index 6335c4a1c..19813b200 100644
--- a/src/core/coreav.cpp
+++ b/src/core/coreav.cpp
@@ -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(friendNum, ToxFriendCall{friendNum, video, *self}));
+ auto it = self->calls.insert(
+ std::pair(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,
diff --git a/src/core/coreav.h b/src/core/coreav.h
index 48c91b614..048226d9e 100644
--- a/src/core/coreav.h
+++ b/src/core/coreav.h
@@ -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);
diff --git a/src/core/corefile.cpp b/src/core/corefile.cpp
index 768956b2f..bd64221b0 100644
--- a/src/core/corefile.cpp
+++ b/src/core/corefile.cpp
@@ -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
#include
#include
-#include
#include
+#include
#include
/**
@@ -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::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(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(static_cast(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, "", "", 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);
diff --git a/src/core/corefile.h b/src/core/corefile.h
index 759cd2876..6a0395ba8 100644
--- a/src/core/corefile.h
+++ b/src/core/corefile.h
@@ -39,6 +39,10 @@ class CoreFile
{
friend class Core;
+
+public:
+ static void handleAvatarOffer(uint32_t friendId, uint32_t fileId, bool accept);
+
private:
CoreFile() = delete;
diff --git a/src/core/toxcall.cpp b/src/core/toxcall.cpp
index be4131024..74e43cfd0 100644
--- a/src/core/toxcall.cpp
+++ b/src/core/toxcall.cpp
@@ -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 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 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(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& 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];
}
diff --git a/src/core/toxcall.h b/src/core/toxcall.h
index d4b60462c..2524e4e93 100644
--- a/src/core/toxcall.h
+++ b/src/core/toxcall.h
@@ -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 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& getPeers();
+ quint32 getAlSource(int peer);
private:
QMap peers;
diff --git a/src/core/toxlogger.cpp b/src/core/toxlogger.cpp
new file mode 100644
index 000000000..d8a1cf1ff
--- /dev/null
+++ b/src/core/toxlogger.cpp
@@ -0,0 +1,60 @@
+#include "toxlogger.h"
+
+#include
+
+#include
+#include
+#include
+#include
+
+/**
+ * @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;
+ }
+}
+
diff --git a/src/core/toxlogger.h b/src/core/toxlogger.h
new file mode 100644
index 000000000..624bf8c91
--- /dev/null
+++ b/src/core/toxlogger.h
@@ -0,0 +1,13 @@
+#ifndef TOXLOGGER_H
+#define TOXLOGGER_H
+
+#include
+
+#include
+
+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
diff --git a/src/core/toxoptions.cpp b/src/core/toxoptions.cpp
new file mode 100644
index 000000000..7b1e2c907
--- /dev/null
+++ b/src/core/toxoptions.cpp
@@ -0,0 +1,133 @@
+#include "toxoptions.h"
+
+#include "src/core/icoresettings.h"
+#include "src/core/toxlogger.h"
+
+#include
+#include
+
+// 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::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(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(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);
+}
diff --git a/src/core/toxoptions.h b/src/core/toxoptions.h
new file mode 100644
index 000000000..bdaeb9426
--- /dev/null
+++ b/src/core/toxoptions.h
@@ -0,0 +1,31 @@
+#ifndef TOXOPTIONS_H
+#define TOXOPTIONS_H
+
+#include
+
+#include
+
+class ICoreSettings;
+struct Tox_Options;
+
+class ToxOptions
+{
+public:
+ ~ToxOptions();
+ ToxOptions(ToxOptions&& from);
+ operator Tox_Options*();
+ const char* getProxyAddrData() const;
+ static std::unique_ptr 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
diff --git a/src/main.cpp b/src/main.cpp
index ba6166f14..30cb246b3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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 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 ).";
+ 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 ).";
} 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};
diff --git a/src/model/chatroom/chatroom.h b/src/model/chatroom/chatroom.h
new file mode 100644
index 000000000..e0203ff26
--- /dev/null
+++ b/src/model/chatroom/chatroom.h
@@ -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 .
+*/
+
+#ifndef MODEL_CHATROOM_H
+#define MODEL_CHATROOM_H
+
+#include "src/model/contact.h"
+
+class Chatroom
+{
+public:
+ virtual Contact* getContact() = 0;
+};
+
+#endif /* MODEL_CHATROOM_H */
diff --git a/src/model/chatroom/friendchatroom.cpp b/src/model/chatroom/friendchatroom.cpp
new file mode 100644
index 000000000..96a339ce2
--- /dev/null
+++ b/src/model/chatroom/friendchatroom.cpp
@@ -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
+
+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 FriendChatroom::getGroups() const
+{
+ QVector 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 FriendChatroom::getOtherCircles() const
+{
+ QVector 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);
+}
diff --git a/src/model/chatroom/friendchatroom.h b/src/model/chatroom/friendchatroom.h
new file mode 100644
index 000000000..d990c3f54
--- /dev/null
+++ b/src/model/chatroom/friendchatroom.h
@@ -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 .
+*/
+
+#ifndef FRIEND_CHATROOM_H
+#define FRIEND_CHATROOM_H
+
+#include "chatroom.h"
+
+#include
+#include
+#include
+
+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 getGroups() const;
+ QVector getOtherCircles() const;
+
+ void resetEventFlags();
+
+signals:
+ void activeChanged(bool activated);
+
+private:
+ bool active{false};
+ Friend* frnd{nullptr};
+};
+
+#endif // FRIEND_H
diff --git a/src/model/chatroom/groupchatroom.cpp b/src/model/chatroom/groupchatroom.cpp
new file mode 100644
index 000000000..ee03c11ce
--- /dev/null
+++ b/src/model/chatroom/groupchatroom.cpp
@@ -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);
+ }
+}
diff --git a/src/model/chatroom/groupchatroom.h b/src/model/chatroom/groupchatroom.h
new file mode 100644
index 000000000..5905f71a2
--- /dev/null
+++ b/src/model/chatroom/groupchatroom.h
@@ -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 .
+*/
+
+#ifndef GROUP_CHATROOM_H
+#define GROUP_CHATROOM_H
+
+#include "chatroom.h"
+
+#include
+
+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 */
diff --git a/src/model/profile/profileinfo.cpp b/src/model/profile/profileinfo.cpp
index b363e2ecd..738355fb3 100644
--- a/src/model/profile/profileinfo.cpp
+++ b/src/model/profile/profileinfo.cpp
@@ -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
+#include
#include
#include
-#include
/**
* @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();
}
diff --git a/src/nexus.cpp b/src/nexus.cpp
index 446162618..d006e3919 100644
--- a/src/nexus.cpp
+++ b/src/nexus.cpp
@@ -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);
}
/**
diff --git a/src/persistence/profile.cpp b/src/persistence/profile.cpp
index b84d5b711..6686aa230 100644
--- a/src/persistence/profile.cpp
+++ b/src/persistence/profile.cpp
@@ -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 friendList = core->getFriendList();
QVectorIterator i(friendList);
while (i.hasNext()) {
const ToxPk friendPublicKey = core->getFriendPublicKey(i.next());
- saveAvatar(loadAvatarData(friendPublicKey), friendPublicKey);
+ saveAvatar(friendPublicKey, loadAvatarData(friendPublicKey));
}
return error;
}
diff --git a/src/persistence/profile.h b/src/persistence/profile.h
index d0feda86e..377be8dcf 100644
--- a/src/persistence/profile.h
+++ b/src/persistence/profile.h
@@ -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
#include
-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 = nullptr;
QString name;
std::unique_ptr passkey = nullptr;
std::shared_ptr database;
diff --git a/src/persistence/settings.cpp b/src/persistence/settings.cpp
index 15f39577d..70552b4be 100644
--- a/src/persistence/settings.cpp
+++ b/src/persistence/settings.cpp
@@ -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};
diff --git a/src/persistence/settings.h b/src/persistence/settings.h
index 45f31810d..a20555a8a 100644
--- a/src/persistence/settings.h
+++ b/src/persistence/settings.h
@@ -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;
diff --git a/src/video/corevideosource.h b/src/video/corevideosource.h
index eb98686ad..d5f2af965 100644
--- a/src/video/corevideosource.h
+++ b/src/video/corevideosource.h
@@ -50,7 +50,7 @@ private:
std::atomic_bool stopped;
friend class CoreAV;
- friend class ToxCall;
+ friend class ToxFriendCall;
};
#endif // COREVIDEOSOURCE_H
diff --git a/src/video/genericnetcamview.h b/src/video/genericnetcamview.h
index 272afd15e..283d1612e 100644
--- a/src/video/genericnetcamview.h
+++ b/src/video/genericnetcamview.h
@@ -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;
diff --git a/src/video/groupnetcamview.cpp b/src/video/groupnetcamview.cpp
index e2942d2df..d58d09e32 100644
--- a/src/video/groupnetcamview.cpp
+++ b/src/video/groupnetcamview.cpp
@@ -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());
diff --git a/src/video/netcamview.cpp b/src/video/netcamview.cpp
index 10a6f27e3..82bfc82ef 100644
--- a/src/video/netcamview.cpp
+++ b/src/video/netcamview.cpp
@@ -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);
diff --git a/src/widget/about/aboutfriendform.cpp b/src/widget/about/aboutfriendform.cpp
index 0c47a26cb..05d323e31 100644
--- a/src/widget/about/aboutfriendform.cpp
+++ b/src/widget/about/aboutfriendform.cpp
@@ -4,10 +4,10 @@
#include
#include
-AboutFriendForm::AboutFriendForm(QPointer about, QWidget* parent)
+AboutFriendForm::AboutFriendForm(std::unique_ptr _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 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());
diff --git a/src/widget/about/aboutfriendform.h b/src/widget/about/aboutfriendform.h
index c98731fd6..d45b0012c 100644
--- a/src/widget/about/aboutfriendform.h
+++ b/src/widget/about/aboutfriendform.h
@@ -6,6 +6,8 @@
#include
#include
+#include
+
namespace Ui {
class AboutFriendForm;
}
@@ -15,12 +17,12 @@ class AboutFriendForm : public QDialog
Q_OBJECT
public:
- AboutFriendForm(QPointer about, QWidget* parent = 0);
+ AboutFriendForm(std::unique_ptr about, QWidget* parent = 0);
~AboutFriendForm();
private:
Ui::AboutFriendForm* ui;
- QPointer about;
+ const std::unique_ptr about;
private slots:
void onAutoAcceptDirChanged(const QString& path);
diff --git a/src/widget/chatformheader.cpp b/src/widget/chatformheader.cpp
index 70e072253..4852d88dc 100644
--- a/src/widget/chatformheader.cpp
+++ b/src/widget/chatformheader.cpp
@@ -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);
diff --git a/src/widget/chatformheader.h b/src/widget/chatformheader.h
index 6765539dc..bc1b89907 100644
--- a/src/widget/chatformheader.h
+++ b/src/widget/chatformheader.h
@@ -55,6 +55,7 @@ public:
};
ChatFormHeader(QWidget* parent = nullptr);
+ ~ChatFormHeader();
void setName(const QString& newName);
void setMode(Mode mode);
diff --git a/src/widget/contentdialog.cpp b/src/widget/contentdialog.cpp
index 44257c175..e4293b3f0 100644
--- a/src/widget/contentdialog.cpp
+++ b/src/widget/contentdialog.cpp
@@ -27,21 +27,22 @@
#include
#include
-#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 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 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;
diff --git a/src/widget/contentdialog.h b/src/widget/contentdialog.h
index 9ef586883..687d904b7 100644
--- a/src/widget/contentdialog.h
+++ b/src/widget/contentdialog.h
@@ -20,27 +20,30 @@
#ifndef CONTENTDIALOG_H
#define CONTENTDIALOG_H
-#include
-
#include "src/widget/genericchatitemlayout.h"
#include "src/widget/tool/activatedialog.h"
+#include
+#include
+
template
class QHash;
template
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;
@@ -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 chatroom, GenericChatForm* form);
+ GroupWidget* addGroup(std::shared_ptr chatroom, GenericChatForm* form);
void removeFriend(int friendId);
void removeGroup(int groupId);
bool hasFriendWidget(int friendId, const GenericChatroomWidget* chatroomWidget) const;
diff --git a/src/widget/form/chatform.cpp b/src/widget/form/chatform.cpp
index 86f1850d0..1523ecefc 100644
--- a/src/widget/form/chatform.cpp
+++ b/src/widget/form/chatform.cpp
@@ -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
#include
@@ -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 chatLines)
verticalBar->setValue(savedSliderPos);
}
-QDate ChatForm::addDateLineIfNeeded(QList msgs, QDate const& lastDate, History::HistMessage const& newMessage, MessageMetadata const& metadata)
+QDate ChatForm::addDateLineIfNeeded(QList 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();
diff --git a/src/widget/form/chatform.h b/src/widget/form/chatform.h
index e948d2351..89554d343 100644
--- a/src/widget/form/chatform.h
+++ b/src/widget/form/chatform.h
@@ -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 newHistMsgs, bool processUndelivered);
- QDate addDateLineIfNeeded(QList msgs, QDate const& lastDate, History::HistMessage const& newMessage, MessageMetadata const& metadata);
+ QDate addDateLineIfNeeded(QList 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 chatLines);
void updateMuteMicButton();
diff --git a/src/widget/form/genericchatform.cpp b/src/widget/form/genericchatform.cpp
index c4d63f4f8..dcc1f4579 100644
--- a/src/widget/form/genericchatform.cpp
+++ b/src/widget/form/genericchatform.cpp
@@ -46,6 +46,11 @@
#include
#include
#include
+#include
+
+#ifdef SPELL_CHECKING
+#include
+#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(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());
diff --git a/src/widget/form/genericchatform.h b/src/widget/form/genericchatform.h
index 62b36e575..c91fa8314 100644
--- a/src/widget/form/genericchatform.h
+++ b/src/widget/form/genericchatform.h
@@ -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;
diff --git a/src/widget/form/settings/advancedform.cpp b/src/widget/form/settings/advancedform.cpp
index c9d04b010..045a30184 100644
--- a/src/widget/form/settings/advancedform.cpp
+++ b/src/widget/form/settings/advancedform.cpp
@@ -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(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(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);
}
diff --git a/src/widget/form/settings/avform.cpp b/src/widget/form/settings/avform.cpp
index 96794b09c..345aefa6c 100644
--- a/src/widget/form/settings/avform.cpp
+++ b/src/widget/form/settings/avform.cpp
@@ -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();
diff --git a/src/widget/form/settings/generalform.cpp b/src/widget/form/settings/generalform.cpp
index 373502fce..523f9aa9a 100644
--- a/src/widget/form/settings/generalform.cpp
+++ b/src/widget/form/settings/generalform.cpp
@@ -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());
diff --git a/src/widget/form/settings/generalform.h b/src/widget/form/settings/generalform.h
index 79cc6d510..71681457f 100644
--- a/src/widget/form/settings/generalform.h
+++ b/src/widget/form/settings/generalform.h
@@ -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();
diff --git a/src/widget/form/settings/generalsettings.ui b/src/widget/form/settings/generalsettings.ui
index 36a3c9ad7..1da736897 100644
--- a/src/widget/form/settings/generalsettings.ui
+++ b/src/widget/form/settings/generalsettings.ui
@@ -119,6 +119,13 @@
-
+
-
+
+
+ Spell checking
+
+
+
-
diff --git a/src/widget/form/settingswidget.cpp b/src/widget/form/settingswidget.cpp
index e638b28ea..10a78fea3 100644
--- a/src/widget/form/settingswidget.cpp
+++ b/src/widget/form/settingswidget.cpp
@@ -51,7 +51,7 @@ SettingsWidget::SettingsWidget(QWidget* parent)
setAttribute(Qt::WA_DeleteOnClose);
- QVBoxLayout* bodyLayout = new QVBoxLayout();
+ bodyLayout = std::unique_ptr(new QVBoxLayout());
settingsWidgets = std::unique_ptr(new QTabWidget(this));
settingsWidgets->setTabPosition(QTabWidget::North);
diff --git a/src/widget/form/settingswidget.h b/src/widget/form/settingswidget.h
index 21ae5f53c..1d7a7987f 100644
--- a/src/widget/form/settingswidget.h
+++ b/src/widget/form/settingswidget.h
@@ -56,6 +56,7 @@ private:
void retranslateUi();
private:
+ std::unique_ptr bodyLayout;
std::unique_ptr settingsWidgets;
std::array, 6> cfgForms;
int currentIndex;
diff --git a/src/widget/friendwidget.cpp b/src/widget/friendwidget.cpp
index c3b0399a1..98cea27ca 100644
--- a/src/widget/friendwidget.cpp
+++ b/src/widget/friendwidget.cpp
@@ -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
#include
-#include
#include
#include
#include
@@ -49,11 +48,6 @@
#include
-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 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 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 getCircleAndFriendList(
- const Friend* frnd, FriendWidget* fw)
+std::tuple getCircleAndFriendList(const Friend* frnd, FriendWidget* fw)
{
const auto pk = frnd->getPublicKey();
const auto circleId = Settings::getInstance().getFriendCircleID(pk);
@@ -242,10 +209,11 @@ std::tuple 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 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 about = std::unique_ptr(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(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(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;
}
diff --git a/src/widget/friendwidget.h b/src/widget/friendwidget.h
index fd82687b5..537e0400e 100644
--- a/src/widget/friendwidget.h
+++ b/src/widget/friendwidget.h
@@ -19,7 +19,11 @@
#define FRIENDWIDGET_H
#include "genericchatroomwidget.h"
+#include "src/core/toxpk.h"
+#include
+
+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 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 chatroom;
bool isDefaultAvatar;
};
diff --git a/src/widget/genericchatroomwidget.h b/src/widget/genericchatroomwidget.h
index eba28ffb8..f28c062ed 100644
--- a/src/widget/genericchatroomwidget.h
+++ b/src/widget/genericchatroomwidget.h
@@ -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;
diff --git a/src/widget/groupwidget.cpp b/src/widget/groupwidget.cpp
index 118da094d..d10f0d64c 100644
--- a/src/widget/groupwidget.cpp
+++ b/src/widget/groupwidget.cpp
@@ -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 chatroom, bool compact)
: GenericChatroomWidget(compact)
- , groupId{groupId}
+ , groupId{static_cast(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)
diff --git a/src/widget/groupwidget.h b/src/widget/groupwidget.h
index c755a2430..78eb7d41d 100644
--- a/src/widget/groupwidget.h
+++ b/src/widget/groupwidget.h
@@ -22,11 +22,15 @@
#include "genericchatroomwidget.h"
+#include "src/model/chatroom/groupchatroom.h"
+
+#include
+
class GroupWidget final : public GenericChatroomWidget
{
Q_OBJECT
public:
- GroupWidget(int GroupId, const QString& Name, bool compact);
+ GroupWidget(std::shared_ptr 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 chatroom;
};
#endif // GROUPWIDGET_H
diff --git a/src/widget/tool/croppinglabel.h b/src/widget/tool/croppinglabel.h
index 9c9175b15..328f72e77 100644
--- a/src/widget/tool/croppinglabel.h
+++ b/src/widget/tool/croppinglabel.h
@@ -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:
diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp
index 1804ae0a6..75759fc16 100644
--- a/src/widget/widget.cpp
+++ b/src/widget/widget.cpp
@@ -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 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(&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::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 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(&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);
diff --git a/src/widget/widget.h b/src/widget/widget.h
index bd82f551b..0b3fbbb63 100644
--- a/src/widget/widget.h
+++ b/src/widget/widget.h
@@ -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 groupWidgets;
QMap friendWidgets;
+ QMap> friendChatrooms;
QMap chatForms;
+
+ QMap groupWidgets;
+ QMap> groupChatrooms;
QMap groupChatForms;
#ifdef Q_OS_MAC
diff --git a/translations/bg.ts b/translations/bg.ts
index d86140c19..d47760582 100644
--- a/translations/bg.ts
+++ b/translations/bg.ts
@@ -81,39 +81,39 @@ which may lead to problems with video calls.
-
+ Включва експериментален звуков процесор с потискане на ехото, изисква рестарт на qTox.
-
+ Включва експериментален звуков процесор
-
+ Качество на звука
-
+ Качество на предавания звук. Намалете тази настройка ако нямате достатъчно широколентова връзка или ако искате да намалите използването на интернет.
-
+ Високо (64 kбита/с)
-
+ Средно (32 kбита/с)
-
+ Ниско (16 kбита/с)
-
+ Много ниско (8 kбита/с)
-
+ Прагова стойност
@@ -198,7 +198,7 @@ which may lead to problems with video calls.
- Публичен ключ:
+ Публичен ключ:
@@ -242,7 +242,7 @@ which may lead to problems with video calls.
-
+ Автоматично приемане на покани за групов чат от този контакт ако е включено.
@@ -355,7 +355,7 @@ which may lead to problems with video calls.
Toxme error
-
+ %1 Tox ID е невалиден или не съществува
@@ -364,24 +364,24 @@ which may lead to problems with video calls.
-
+ Отваряне на списък с контакти
-
+ Не може да се отвори файла
Error message when trying to open a contact list file to import
-
+ Не може да се отвори файла с контакти
-
+ Невалиден файл
-
+ Не са намерени контакти за зареждане в този файл!
@@ -401,11 +401,11 @@ which may lead to problems with video calls.
Button to choose a file with a list of contacts to import
-
+ Отваряне
-
+ Изпращане на заявка за приятелство
@@ -414,7 +414,7 @@ which may lead to problems with video calls.
-
+ Зареждане на списък с контакти, по един Tox ID на ред
@@ -426,7 +426,7 @@ which may lead to problems with video calls.
-
+ Зареждане на контакти
@@ -658,7 +658,7 @@ which may lead to problems with video calls.
-
+ Изнасяне към файл
@@ -666,7 +666,7 @@ which may lead to problems with video calls.
-
+ Връзката с %1 завърши неочаквано. %2
@@ -929,7 +929,7 @@ which may lead to problems with video calls.
-
+ Никога
@@ -1297,7 +1297,7 @@ instead of system taskbar.
GroupInviteWidget
- Поканен от %1 на %2 в %3
+ Поканен от %1на %2 в %3.
@@ -1581,7 +1581,7 @@ Profile does not contain your history.
-
+ %1 съобщения
@@ -1803,7 +1803,7 @@ You may want to create one.
-
+ Входно поле за търсене на известни приятели
@@ -1961,18 +1961,18 @@ If you are getting spammed with friend requests, change the NoSpam.
-
+ Черен списък
-
+ Филтриране на групови съобщения по публичен ключ на членовете. Поставете побличните ключове тук, по един на ред.
Profile
-
+ Неуспешно произвеждане на ключ от парола, профилът няма за използва новата парола.
@@ -2128,7 +2128,7 @@ Please use another image.
-
+ Неуспешна смяна на парола
-
+ Празен път не е достъпен
@@ -2155,15 +2155,15 @@ This ID includes the NoSpam code (in blue), and the checksum (in gray).
-
+ Празно име
-
+ Празно име не е достъпно
-
+ Празен път
@@ -2409,11 +2409,11 @@ It will be installed when qTox restarts.
-
+ Изпълнява се преформатиране на текст..
-
+ Стартира ново копие и отваря екрана за вписване.
@@ -2709,7 +2709,7 @@ It will be installed when qTox restarts.
-
+ Използване на идентикони вместо празни аватари
diff --git a/translations/ko.ts b/translations/ko.ts
index 44d5b6eaf..cdd6d196a 100644
--- a/translations/ko.ts
+++ b/translations/ko.ts
@@ -33,7 +33,7 @@
-
+ 재생 기기
@@ -87,7 +87,7 @@ which may lead to problems with video calls.
-
+ 오디오 음질
@@ -95,23 +95,23 @@ which may lead to problems with video calls.
-
+ 높음(64kbps)
-
+ 중간(32kbps)
-
+ 낮음 (16kbps)
-
+ 아주 낮음 (8kbps)
-
+ 한계점
@@ -135,7 +135,7 @@ which may lead to problems with video calls.
- qTox 버젼 %1.
+ qTox 버젼 %1 사용중.
@@ -184,7 +184,7 @@ which may lead to problems with video calls.
AboutFriendForm
-
+ 대화
@@ -232,11 +232,11 @@ which may lead to problems with video calls.
- 오디오
+ 오디오
- 오디오 + 비디오
+ 오디오 + 비디오
@@ -244,7 +244,7 @@ which may lead to problems with video calls.
-
+ 그룹 초대를 받아드립니다.
@@ -264,11 +264,11 @@ which may lead to problems with video calls.
- 기록이 삭제되었습니다
+ 기록 삭제
- %1 채팅기록이 삭제되었습니다!
+ %1 채팅기록이 삭제되었습니다!
@@ -357,7 +357,7 @@ which may lead to problems with video calls.
When trying to add your own Tox ID as friend
- 자기 자신을 친구로 추가할 수 없습니다!
+ 자신을 친구로 추가할 수 없습니다!
@@ -365,7 +365,7 @@ which may lead to problems with video calls.
-
+ 파일을 열 수 없습니다
@@ -393,16 +393,16 @@ which may lead to problems with video calls.
The message you send in friend requests
- 메시지
+ 메시지
Button to choose a file with a list of contacts to import
-
+ 열기
-
+ 친구 요청을 보냄
@@ -475,7 +475,7 @@ which may lead to problems with video calls.
- 파일보관
+ 파일저장
@@ -499,7 +499,7 @@ which may lead to problems with video calls.
-
+ 이동가능
@@ -584,7 +584,7 @@ which may lead to problems with video calls.
-
+ 좋지않은 의견
@@ -606,7 +606,7 @@ which may lead to problems with video calls.
laut Duden ist Screenshot schon deutsch
-
+ 스크린샷을 저장할 수 없었습니다.
@@ -646,7 +646,7 @@ which may lead to problems with video calls.
contact status
-
+ 온라인
@@ -659,7 +659,7 @@ which may lead to problems with video calls.
-
+ 채팅 기록을 저장
@@ -674,7 +674,7 @@ which may lead to problems with video calls.
-
+ 음성통화 시작
@@ -694,7 +694,7 @@ which may lead to problems with video calls.
-
+ 영상통화 시작
@@ -718,7 +718,7 @@ which may lead to problems with video calls.
-
+ 음소거 전화
@@ -795,12 +795,12 @@ which may lead to problems with video calls.
Error while sending friendship request
- 제한된 메시지 길이를 초과했습니다!
+ 제한된 메시지 길이를 초과했습니다!
Error while sending friendship request
- 이미 추가된 친구입니다
+ 이미 추가된 친구입니다
@@ -895,7 +895,7 @@ which may lead to problems with video calls.
"Headline" of the window
-
+ 전송 된 파일
@@ -910,23 +910,23 @@ which may lead to problems with video calls.
FriendListWidget
- 오늘
+ 오늘
- 어제
+ 어제
- 지난 7일
+ 지난 7일
- 이달내
+ 이 달
- 6개월 이전
+ 6개월 이전
@@ -2726,23 +2726,23 @@ It will be installed when qTox restarts.
-
+ 파일
-
+ 프로필 편집
-
+ 상태 바꾸기
-
+ 로그아웃
-
+ 편집
@@ -2772,7 +2772,7 @@ It will be installed when qTox restarts.
-
+ 이전의 대화
@@ -2803,7 +2803,7 @@ It will be installed when qTox restarts.
Placeholder when someone's name in a group chat is empty
-
+ 비었습니다
@@ -2874,17 +2874,17 @@ It will be installed when qTox restarts.
title of the window
-
+ 친구 추가
title of the window
-
+ 그룹 초대
title of the window
-
+ 파일 이동
@@ -2894,7 +2894,7 @@ It will be installed when qTox restarts.
title of the window
-
+ 내 프로필
diff --git a/translations/sv.ts b/translations/sv.ts
index 267eb3639..3057d175c 100644
--- a/translations/sv.ts
+++ b/translations/sv.ts
@@ -113,7 +113,7 @@ vilket kan leda till problem med videosamtal.
-
+ Tröskel
@@ -169,7 +169,7 @@ vilket kan leda till problem med videosamtal.
Replaces `%1` in the `A list of all knownâ¦`
- felbevakare
+ felbevakare
@@ -179,7 +179,7 @@ vilket kan leda till problem med videosamtal.
Replaces `%1` in `See a full list ofâ¦`
- bidragare
+ bidragsgivare
@@ -190,11 +190,11 @@ vilket kan leda till problem med videosamtal.
- användarnamn
+ användarnamn
- statusmeddelande
+ statusmeddelande
@@ -214,7 +214,7 @@ vilket kan leda till problem med videosamtal.
- Acceptera automatiskt filer
+ Acceptera filer automatiskt
@@ -222,23 +222,23 @@ vilket kan leda till problem med videosamtal.
- Acceptera automatiskt för den här kontakten är avaktiverad
+ Acceptera automatiskt för den här kontakten är inaktiverat
- Acceptera automatiskt samtal:
+ Acceptera samtal automatiskt:
- Handbok
+ Handbok
- Ljud
+ Ljud
- Ljud + Video
+ Ljud + video
@@ -246,15 +246,15 @@ vilket kan leda till problem med videosamtal.
- Acceptera automatiskt gruppinbjudningar
+ Acceptera gruppinbjudningar automatiskt
- Ta bort historia (operation kan inte ångras!)
+ Ta bort historik (operation kan inte ångras!)
- Anteckningar
+ Anteckningar
@@ -266,11 +266,11 @@ vilket kan leda till problem med videosamtal.
- Historik raderad
+ Historik borttagen
- Chatthistorik med %1 raderad!
+ Chatthistorik med %1 borttagen!
@@ -359,7 +359,7 @@ vilket kan leda till problem med videosamtal.
When trying to add your own Tox ID as friend
- Du kan inte lägga till dig själv som vän!
+ Du kan inte lägga till dig själv som vän!
@@ -385,17 +385,17 @@ vilket kan leda till problem med videosamtal.
Tox ID of the person you're sending a friend request to
- Tox-ID
+ Tox-ID
Tox ID format description
- antingen 76 hexadecimala tecken eller name@example.com
+ antingen 76 hexadecimala tecken eller namn@exempel.se
The message you send in friend requests
- Meddelande
+ Meddelande
@@ -418,11 +418,9 @@ vilket kan leda till problem med videosamtal.
Shows the number of contacts we're about to import from a file (at least one)
-
-
-
-
-
+ Klar för att importera %n kontakt(er), klicka på skicka för att bekräfta
+ Klar för att importera %n kontakter, klicka på skicka för att bekräfta
+
Importera kontakter
@@ -680,15 +678,15 @@ vilket kan leda till problem med videosamtal.
- Avsluta ljudsamtal
+ Avsluta ljudsamtal
- Avbryt ljudsamtal
+ Avbryt ljudsamtal
- Acceptera ljudsamtal
+ Acceptera ljudsamtal
@@ -700,23 +698,23 @@ vilket kan leda till problem med videosamtal.
- Avsluta videosamtal
+ Avsluta videosamtal
- Avbryt videosamtal
+ Avbryt videosamtal
- Acceptera videosamtal
+ Acceptera videosamtal
- Ljud kan endast avaktiveras under ett samtal
+ Ljud kan endast inaktiveras under ett samtal
- Slå på samtal
+ Slå på mikrofon
@@ -1964,7 +1962,7 @@ Om du blir spammad med vänförfrågningar, ändra NoSpam.
-
+ Filtrera gruppmeddelande genom gruppmedlems allmänna nyckel. Ange den offentliga nyckeln här, en per rad.
@@ -2141,39 +2139,39 @@ Detta ID inkluderar NoSpam-koden (i blått) och checksum (i grått).
-
+ Tom sökväg är inte tillgänglig
- Det gick inte att byta namn
+ Det gick inte att byta namn
- Profil finns redan
+ Profilen finns redan
- En profil med namnet "%1" finns redan.
+ En profil med namnet "%1" finns redan.
-
+ Tomt namn
-
+ Tomt namn är inte tillgängligt
-
+ Tom sökväg
- Kunde inte byta lösenord på databasen, den kan vara trasig eller använda det gamla lösenordet.
+ Det gick inte att byta lösenord på databasen, den kan vara trasig eller använda det gamla lösenordet.
- Exportera profil
+ Exportera profil
@@ -2183,17 +2181,17 @@ Detta ID inkluderar NoSpam-koden (i blått) och checksum (i grått).
deletion failed text part 1
- Följande filer kan inte tas bort:
+ Följande filer kunde inte tas bort:
deletion failed text part 2
- Vänligen ta bort dem manuellt.
+ Ta bort dem manuellt.
deletion confirmation text
- Är du säker på att du vill ta bort ditt lösenord?
+ Är du säker på att du vill ta bort ditt lösenord?
@@ -2707,11 +2705,11 @@ Den kommer att installeras när qTox startas om.
toolTip for show identicons
-
+ 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.
-
+ Använd identicons istället för tomma avatarer
diff --git a/translations/ta.ts b/translations/ta.ts
index 733fa48bc..b4e31683d 100644
--- a/translations/ta.ts
+++ b/translations/ta.ts
@@ -89,7 +89,7 @@ which may lead to problems with video calls.
-
+ ஒலித்தரம்
diff --git a/translations/uk.ts b/translations/uk.ts
index 33e362e64..e506fcd26 100644
--- a/translations/uk.ts
+++ b/translations/uk.ts
@@ -93,7 +93,7 @@ which may lead to problems with video calls.
-
+ Якість передаваємого звуку. Зменшіть цей параметр якщо пропускна здатність з'єднання недостатня або ви хочете зменшити використання Інтернет трафіку.
@@ -137,7 +137,7 @@ which may lead to problems with video calls.
- Ви використовуєте qTox версії %1.
+ Ви використовуєте qTox версії %1.
@@ -145,11 +145,11 @@ which may lead to problems with video calls.
- Версія toxcore: %1
+ Версія toxcore: %1
- Версія Qt: %1
+ Версія Qt: %1
@@ -169,7 +169,7 @@ which may lead to problems with video calls.
Replaces `%1` in the `A list of all knownâ¦`
- баг-трекері
+ баг-трекері
@@ -367,40 +367,40 @@ which may lead to problems with video calls.
-
+ Помилка відкриття файлу
Error message when trying to open a contact list file to import
-
+ Помилка відкриття файлу контактів
-
+ Неправильний файл
-
+ Контактів для імпорту не знайдено!
Tox ID of the person you're sending a friend request to
-
+ Ідентифікатор Tox
Tox ID format description
- 76 шістнадцяткових цифр або name@example.com
+ 76 шістнадцяткових символів або name@example.com
The message you send in friend requests
- Повідомлення
+ Повідомлення
Button to choose a file with a list of contacts to import
-
+ Відкрити
@@ -413,7 +413,7 @@ which may lead to problems with video calls.
-
+ Імпортувати список контактів, один Ідентифікатор Tox на рядок
@@ -426,14 +426,14 @@ which may lead to problems with video calls.
-
+ Імпортувати контакти
AdvancedForm
- Додатково
+ Більше
@@ -441,7 +441,7 @@ which may lead to problems with video calls.
- впевнені
+ дійсно
@@ -461,11 +461,11 @@ which may lead to problems with video calls.
- Так
+ Так
- Ні
+ Ні
@@ -483,7 +483,7 @@ which may lead to problems with video calls.
-
+ Логи (*.log)
@@ -512,7 +512,7 @@ which may lead to problems with video calls.
Text on a checkbox to enable IPv6
- Дозволити IPv6 (рекомендовано)
+ Увімкнути IPv6 (рекомендовано)
@@ -522,21 +522,21 @@ which may lead to problems with video calls.
Text on checkbox to disable UDP
- Дозволити UDP (рекомендовано)
+ Увімкнути UDP (рекомендовано)
- Тип проксі:
+ Тип проксі:
Text on proxy addr label
- Адреса проксі:
+ Адреса:
Text on proxy port label
- Порт:
+ Порт:
@@ -544,28 +544,28 @@ which may lead to problems with video calls.
- SOCKS5
+ SOCKS5
- HTTP
+ HTTP
reconnect button
- Повторно під'єднатись
+ Перепідключитись
- Налагодження
+ Відладка
- Вивантажити логи налагодження
+ Експортувати логи відладки
- Скопіювати логи налагодження
+ Скопіювати логи відладки
@@ -600,20 +600,20 @@ which may lead to problems with video calls.
- Не вдалось відправити файл «%1»
+ Не вдалось відправити файл "%1"
Temporary file for screenshot
- Помилка під час відкриття тимчасового файлу
+ Помилка відкриття тимчасового файлу
- qTox не може зберегти снімок екрану
+ qTox не зміг зберегти скриншот
- Виклик із %1 завершено. %2
+ Дзвінок з %1 завершено. %2
diff --git a/ui/chatArea/chatHead.css b/ui/chatArea/chatHead.css
index d7a3846f0..c0f9ee85b 100644
--- a/ui/chatArea/chatHead.css
+++ b/ui/chatArea/chatHead.css
@@ -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;
-}
diff --git a/ui/friendList/friendList.css b/ui/friendList/friendList.css
index 3d7972065..3e18a085e 100644
--- a/ui/friendList/friendList.css
+++ b/ui/friendList/friendList.css
@@ -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;
}
diff --git a/ui/loginScreen/loginScreen.css b/ui/loginScreen/loginScreen.css
index 9bc063f9f..074070f05 100644
--- a/ui/loginScreen/loginScreen.css
+++ b/ui/loginScreen/loginScreen.css
@@ -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;
}
diff --git a/ui/msgEdit/msgEdit.css b/ui/msgEdit/msgEdit.css
index e9845285d..27125a6c1 100644
--- a/ui/msgEdit/msgEdit.css
+++ b/ui/msgEdit/msgEdit.css
@@ -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;
}
diff --git a/ui/statusButton/statusButton.css b/ui/statusButton/statusButton.css
index 0a149ff5a..f97975d2c 100644
--- a/ui/statusButton/statusButton.css
+++ b/ui/statusButton/statusButton.css
@@ -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;
}
diff --git a/ui/window/profile.css b/ui/window/profile.css
index 9c1e97056..daddea876 100644
--- a/ui/window/profile.css
+++ b/ui/window/profile.css
@@ -1,4 +1,5 @@
-#selfAvatar:hover {
+#selfAvatar:hover
+{
border-radius: 6px;
background-color: #ccc;
}
diff --git a/ui/window/statusPanel.css b/ui/window/statusPanel.css
index 5344a79a7..3b94f248e 100644
--- a/ui/window/statusPanel.css
+++ b/ui/window/statusPanel.css
@@ -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;
}
-
diff --git a/ui/window/window.css b/ui/window/window.css
index 84c771a95..49c60da8c 100644
--- a/ui/window/window.css
+++ b/ui/window/window.css
@@ -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;}
diff --git a/windows/cross-compile/build.sh b/windows/cross-compile/build.sh
index 12fdba540..c7947792d 100644
--- a/windows/cross-compile/build.sh
+++ b/windows/cross-compile/build.sh
@@ -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
diff --git a/windows/qtox.nsi b/windows/qtox.nsi
index 349d4529d..8ada7378c 100644
--- a/windows/qtox.nsi
+++ b/windows/qtox.nsi
@@ -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"
diff --git a/windows/qtox64.nsi b/windows/qtox64.nsi
index 7b90b6a09..b943388b9 100755
--- a/windows/qtox64.nsi
+++ b/windows/qtox64.nsi
@@ -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"