summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoseph Donofry <joedonofry@gmail.com>2020-06-09 20:51:36 -0400
committerJoseph Donofry <joedonofry@gmail.com>2020-06-09 20:51:36 -0400
commit6bb73f84a3075abe7a1c1fd2d56a259740070c70 (patch)
treeeecb0bc3ec18a04cd226d1caea74b2b65ef6d851
parentMinor adjustments to emoji picker (diff)
parentSmooth scaling for images (diff)
downloadnheko-6bb73f84a3075abe7a1c1fd2d56a259740070c70.tar.xz
Merge master and fix conflicts
-rwxr-xr-x.ci/install.sh1
-rwxr-xr-x.ci/script.sh21
-rw-r--r--.travis.yml11
-rw-r--r--CMakeLists.txt18
-rw-r--r--io.github.NhekoReborn.Nheko.json6
-rw-r--r--resources/langs/nheko_it.ts1586
-rw-r--r--resources/qml/Avatar.qml4
-rw-r--r--resources/qml/Reactions.qml4
-rw-r--r--resources/qml/TimelineRow.qml8
-rw-r--r--resources/qml/TimelineView.qml17
-rw-r--r--resources/qml/delegates/TextMessage.qml1
-rw-r--r--resources/qml/emoji/EmojiCategory.qml67
-rw-r--r--src/Cache.cpp14
-rw-r--r--src/ChatPage.cpp14
-rw-r--r--src/CommunitiesListItem.h1
-rw-r--r--src/EventAccessors.cpp6
-rw-r--r--src/EventAccessors.h3
-rw-r--r--src/InviteeItem.cpp1
-rw-r--r--src/InviteeItem.h2
-rw-r--r--src/LoginPage.cpp11
-rw-r--r--src/LoginPage.h7
-rw-r--r--src/MainWindow.cpp4
-rw-r--r--src/MxcImageProvider.cpp3
-rw-r--r--src/RegisterPage.cpp9
-rw-r--r--src/RegisterPage.h7
-rw-r--r--src/RoomInfoListItem.cpp2
-rw-r--r--src/RoomList.cpp2
-rw-r--r--src/SideBarActions.cpp2
-rw-r--r--src/TopRoomBar.h1
-rw-r--r--src/UserInfoWidget.cpp3
-rw-r--r--src/UserInfoWidget.h7
-rw-r--r--src/UserSettingsPage.cpp417
-rw-r--r--src/UserSettingsPage.h193
-rw-r--r--src/Utils.cpp16
-rw-r--r--src/Utils.h39
-rw-r--r--src/dialogs/CreateRoom.cpp4
-rw-r--r--src/dialogs/InviteUsers.cpp1
-rw-r--r--src/dialogs/InviteUsers.h4
-rw-r--r--src/dialogs/ReadReceipts.cpp18
-rw-r--r--src/dialogs/ReadReceipts.h14
-rw-r--r--src/dialogs/RoomSettings.cpp34
-rw-r--r--src/dialogs/RoomSettings.h35
-rw-r--r--src/emoji/Category.cpp3
-rw-r--r--src/emoji/Category.h9
-rw-r--r--src/popups/PopupItem.cpp1
-rw-r--r--src/popups/PopupItem.h5
-rw-r--r--src/popups/SuggestionsPopup.h3
-rw-r--r--src/popups/UserMentions.cpp11
-rw-r--r--src/timeline/TimelineModel.cpp24
-rw-r--r--src/timeline/TimelineModel.h1
-rw-r--r--src/timeline/TimelineViewManager.cpp13
-rw-r--r--src/ui/Avatar.cpp1
-rw-r--r--src/ui/InfoMessage.cpp1
-rw-r--r--src/ui/Painter.h3
-rw-r--r--third_party/SingleApplication-3.1.3.1/.gitignore (renamed from third_party/SingleApplication-3.0.19/.gitignore)9
-rw-r--r--third_party/SingleApplication-3.1.3.1/CHANGELOG.md (renamed from third_party/SingleApplication-3.0.19/CHANGELOG.md)34
-rw-r--r--third_party/SingleApplication-3.1.3.1/CMakeLists.txt (renamed from third_party/SingleApplication-3.0.19/CMakeLists.txt)35
-rw-r--r--third_party/SingleApplication-3.1.3.1/LICENSE (renamed from third_party/SingleApplication-3.0.19/LICENSE)2
-rw-r--r--third_party/SingleApplication-3.1.3.1/README.md (renamed from third_party/SingleApplication-3.0.19/README.md)48
-rw-r--r--third_party/SingleApplication-3.1.3.1/SingleApplication1
-rw-r--r--third_party/SingleApplication-3.1.3.1/Windows.md (renamed from third_party/SingleApplication-3.0.19/Windows.md)0
-rw-r--r--third_party/SingleApplication-3.1.3.1/singleapplication.cpp (renamed from third_party/SingleApplication-3.0.19/singleapplication.cpp)18
-rw-r--r--third_party/SingleApplication-3.1.3.1/singleapplication.h (renamed from third_party/SingleApplication-3.0.19/singleapplication.h)18
-rw-r--r--third_party/SingleApplication-3.1.3.1/singleapplication.pri20
-rw-r--r--third_party/SingleApplication-3.1.3.1/singleapplication_p.cpp (renamed from third_party/SingleApplication-3.0.19/singleapplication_p.cpp)100
-rw-r--r--third_party/SingleApplication-3.1.3.1/singleapplication_p.h (renamed from third_party/SingleApplication-3.0.19/singleapplication_p.h)15
66 files changed, 2534 insertions, 459 deletions
diff --git a/.ci/install.sh b/.ci/install.sh

index d1bff54b..8d27d301 100755 --- a/.ci/install.sh +++ b/.ci/install.sh
@@ -3,6 +3,7 @@ set -ex if [ "$FLATPAK" ]; then + sudo apt-get -y install flatpak flatpak-builder elfutils flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo flatpak --noninteractive install --user flathub org.kde.Platform//5.14 flatpak --noninteractive install --user flathub org.kde.Sdk//5.14 diff --git a/.ci/script.sh b/.ci/script.sh
index 15e7f8cf..63e7f135 100755 --- a/.ci/script.sh +++ b/.ci/script.sh
@@ -6,7 +6,26 @@ if [ "$FLATPAK" ]; then mkdir -p build-flatpak cd build-flatpak - flatpak-builder --ccache --repo=repo --subject="Build of Nheko ${VERSION} `date`" app ../io.github.NhekoReborn.Nheko.json + jobsarg="" + if [ "$ARCH" = "arm64" ]; then + jobsarg="--jobs=2" + fi + + flatpak-builder --ccache --repo=repo --subject="Build of Nheko ${VERSION} $jobsarg `date`" app ../io.github.NhekoReborn.Nheko.json & + + # to prevent flatpak builder from timing out on arm, run it in the background and print something every minute for up to 30 minutes. + minutes=0 + limit=40 + while kill -0 $! >/dev/null 2>&1; do + if [ $minutes == $limit ]; then + break; + fi + + minutes=$((minutes+1)) + + sleep 60 + done + flatpak build-bundle repo nheko-${VERSION}-${ARCH}.flatpak io.github.NhekoReborn.Nheko master mkdir ../artifacts diff --git a/.travis.yml b/.travis.yml
index 443836cf..eec32290 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -113,10 +113,6 @@ matrix: apt: sources: - sourceline: 'ppa:alexlarsson/flatpak' - packages: - - flatpak - - flatpak-builder - - elfutils - os: linux arch: arm64 env: @@ -128,20 +124,17 @@ matrix: sources: - sourceline: 'ppa:alexlarsson/flatpak' packages: - - flatpak - - flatpak-builder - - elfutils - librsvg2-bin before_install: # Use TRAVIS_TAG if defined, or the short commit SHA otherwise - export VERSION=${TRAVIS_TAG:-$(git rev-parse --short HEAD)} install: - - travis_wait ./.ci/install.sh + - ./.ci/install.sh - export PATH=/usr/local/bin:${PATH} script: - - travis_wait ./.ci/script.sh + - ./.ci/script.sh - sed -i -e "s/VERSION_NAME_VALUE/${VERSION}/g" ./.ci/bintray-release.json || true - cp ./.ci/bintray-release.json . deploy: diff --git a/CMakeLists.txt b/CMakeLists.txt
index 36403890..c1def924 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -10,7 +10,9 @@ set( CACHE FILEPATH "Default toolchain" ) - +set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard") +set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Require C++ standard to be supported") +set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "compile as PIC by default") option(HUNTER_ENABLED "Enable Hunter package manager" OFF) include("cmake/HunterGate.cmake") @@ -333,7 +335,7 @@ if(USE_BUNDLED_MTXCLIENT) FetchContent_Declare( MatrixClient GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git - GIT_TAG f5c78f4331b62a1e25a2d839cb38b07bb9bd7829 + GIT_TAG 795b6a82d4f10c629ce0eb99803cc677c413f940 ) FetchContent_MakeAvailable(MatrixClient) else() @@ -421,7 +423,7 @@ endif() # single instance functionality set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication") -add_subdirectory(third_party/SingleApplication-3.0.19/) +add_subdirectory(third_party/SingleApplication-3.1.3.1/) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) @@ -515,6 +517,9 @@ set(TRANSLATION_DEPS ${LANG_QRC} ${QRC} ${QM_SRC}) if (APPLE) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa") set(SRC_FILES ${SRC_FILES} src/notifications/ManagerMac.mm src/emoji/MacHelper.mm) + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0") + set_source_files_properties( src/notifications/ManagerMac.mm src/emoji/MacHelper.mm PROPERTIES SKIP_PRECOMPILE_HEADERS ON) + endif() elseif (WIN32) file(DOWNLOAD "https://raw.githubusercontent.com/mohabouje/WinToast/41ed1c58d5dce0ee9c01dbdeac05be45358d4f57/src/wintoastlib.cpp" @@ -578,6 +583,13 @@ target_link_libraries(nheko PRIVATE tweeny SingleApplication::SingleApplication) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0") +target_precompile_headers(nheko + PRIVATE + <string> + ) +endif() + if(MSVC) target_link_libraries(nheko PRIVATE ntdll) endif() diff --git a/io.github.NhekoReborn.Nheko.json b/io.github.NhekoReborn.Nheko.json
index 50fa3b33..7ffb1995 100644 --- a/io.github.NhekoReborn.Nheko.json +++ b/io.github.NhekoReborn.Nheko.json
@@ -131,7 +131,7 @@ { "sha256": "59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722", "type": "archive", - "url": "https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.bz2" + "url": "https://sourceforge.net/projects/boost/files/boost/1.72.0/boost_1_72_0.tar.bz2" } ] }, @@ -146,9 +146,9 @@ "name": "mtxclient", "sources": [ { - "sha256": "16203a92b03c488178b31bedca9d9015b1d406443f7e5363a2e09171e50f8dfc", + "sha256": "7ba85bb477c9e17e2389faf2333034aa9ceae4b1c65d6bf5f7067f2799e9b770", "type": "archive", - "url": "https://github.com/Nheko-Reborn/mtxclient/archive/f5c78f4331b62a1e25a2d839cb38b07bb9bd7829.tar.gz" + "url": "https://github.com/Nheko-Reborn/mtxclient/archive/795b6a82d4f10c629ce0eb99803cc677c413f940.tar.gz" } ] }, diff --git a/resources/langs/nheko_it.ts b/resources/langs/nheko_it.ts new file mode 100644
index 00000000..38566eca --- /dev/null +++ b/resources/langs/nheko_it.ts
@@ -0,0 +1,1586 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="it"> +<context> + <name>Cache</name> + <message> + <location filename="../../src/Cache.cpp" line="+1341"/> + <source>You joined this room.</source> + <translation>Sei entrato in questa stanza.</translation> + </message> +</context> +<context> + <name>ChatPage</name> + <message> + <location filename="../../src/ChatPage.cpp" line="+226"/> + <source>Failed to invite user: %1</source> + <translation>Impossibile invitare l&apos;utente: %1</translation> + </message> + <message> + <location line="+4"/> + <location line="+892"/> + <source>Invited user: %1</source> + <translation>Invitato utente: %1</translation> + </message> + <message> + <location line="-463"/> + <source>Migrating the cache to the current version failed. This can have different reasons. Please open an issue and try to use an older version in the mean time. Alternatively you can try deleting the cache manually.</source> + <translation>Migrazione della cache alla versione corrente fallita. Questo può avere diverse cause. Per favore apri una issue e nel frattempo prova ad usare una versione più vecchia. In alternativa puoi provare a cancellare la cache manualmente.</translation> + </message> + <message> + <location line="+428"/> + <source>Room %1 created.</source> + <translation>Stanza %1 creata.</translation> + </message> + <message> + <location line="+30"/> + <source>Failed to invite %1 to %2: %3</source> + <translation>Impossibile invitare %1 a %2: %3</translation> + </message> + <message> + <location line="+19"/> + <source>Failed to kick %1 to %2: %3</source> + <translation>Impossibile scacciare %1 a %2: %3</translation> + </message> + <message> + <location line="+5"/> + <source>Kicked user: %1</source> + <translation>Scacciato utente: %1</translation> + </message> + <message> + <location line="+14"/> + <source>Failed to ban %1 in %2: %3</source> + <translation>Impossibile bannare %1 in %2: %3</translation> + </message> + <message> + <location line="+5"/> + <source>Banned user: %1</source> + <translation>Utente bannato: %1</translation> + </message> + <message> + <location line="+14"/> + <source>Failed to unban %1 in %2: %3</source> + <translation>Impossibile rimuovere il ban di %1 in %2: %3</translation> + </message> + <message> + <location line="+5"/> + <source>Unbanned user: %1</source> + <translation>Rimosso il ban dall&apos;utente: %1</translation> + </message> + <message> + <location line="-817"/> + <source>Failed to upload media. Please try again.</source> + <translation>Impossibile inviare il file multimediale. Per favore riprova.</translation> + </message> + <message> + <location line="+296"/> + <source>Cache migration failed!</source> + <translation>Migrazione della cache fallita!</translation> + </message> + <message> + <location line="+13"/> + <source>Incompatible cache version</source> + <translation>Versione della cache incompatibile</translation> + </message> + <message> + <location line="+1"/> + <source>The cache on your disk is newer than this version of Nheko supports. Please update or clear your cache.</source> + <translation>La cache sul tuo disco è più nuova di quella supportata da questa versione di Nheko. Per favore aggiorna o pulisci la tua cache.</translation> + </message> + <message> + <location line="+111"/> + <source>Failed to restore OLM account. Please login again.</source> + <translation>Impossibile ripristinare l&apos;account OLM. Per favore accedi nuovamente.</translation> + </message> + <message> + <location line="+5"/> + <source>Failed to restore save data. Please login again.</source> + <translation>Impossibile ripristinare i dati salvati. Per favore accedi nuovamente.</translation> + </message> + <message> + <location line="+148"/> + <source>Failed to setup encryption keys. Server response: %1 %2. Please try again later.</source> + <translation>Impossibile configurare le chiavi crittografiche. Risposta del server: %1 %2. Per favore riprova in seguito.</translation> + </message> + <message> + <location line="+51"/> + <location line="+219"/> + <source>Please try to login again: %1</source> + <translation>Per favore prova ad accedere nuovamente: %1</translation> + </message> + <message> + <location line="-154"/> + <source>Failed to join room: %1</source> + <translation>Impossibile accedere alla stanza: %1</translation> + </message> + <message> + <location line="+5"/> + <source>You joined the room</source> + <translation>Sei entrato nella stanza</translation> + </message> + <message> + <location line="+6"/> + <source>Failed to remove invite: %1</source> + <translation>Impossibile rimuovere l&apos;invito: %1</translation> + </message> + <message> + <location line="+19"/> + <source>Room creation failed: %1</source> + <translation>Creazione della stanza fallita: %1</translation> + </message> + <message> + <location line="+16"/> + <source>Failed to leave room: %1</source> + <translation>Impossibile lasciare la stanza: %1</translation> + </message> +</context> +<context> + <name>CommunitiesListItem</name> + <message> + <location filename="../../src/CommunitiesListItem.cpp" line="+133"/> + <source>All rooms</source> + <translation>Tutte le stanze</translation> + </message> + <message> + <location line="+4"/> + <source>Favourite rooms</source> + <translation>Stanze preferite</translation> + </message> + <message> + <location line="+2"/> + <source>Low priority rooms</source> + <translation>Stanze a bassa priorità</translation> + </message> + <message> + <location line="+2"/> + <location line="+2"/> + <source> (tag)</source> + <translation> (tag)</translation> + </message> + <message> + <location line="+3"/> + <source> (community)</source> + <translation> (comunità)</translation> + </message> +</context> +<context> + <name>EditModal</name> + <message> + <location filename="../../src/dialogs/RoomSettings.cpp" line="+60"/> + <source>Apply</source> + <translation>Applica</translation> + </message> + <message> + <location line="+1"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location line="+10"/> + <source>Name</source> + <translation>Nome</translation> + </message> + <message> + <location line="+2"/> + <source>Topic</source> + <translation>Argomento</translation> + </message> +</context> +<context> + <name>EncryptionIndicator</name> + <message> + <location filename="../qml/EncryptionIndicator.qml" line="+36"/> + <source>Encrypted</source> + <translation>Criptato</translation> + </message> + <message> + <location line="+2"/> + <source>This message is not encrypted!</source> + <translation>Questo messaggio è in chiaro!</translation> + </message> +</context> +<context> + <name>InviteeItem</name> + <message> + <location filename="../../src/InviteeItem.cpp" line="+17"/> + <source>Remove</source> + <translation>Rimuovi</translation> + </message> +</context> +<context> + <name>LoginPage</name> + <message> + <location filename="../../src/LoginPage.cpp" line="+89"/> + <source>Matrix ID</source> + <translation>ID Matrix</translation> + </message> + <message> + <location line="+1"/> + <source>e.g @joe:matrix.org</source> + <translation>es. @joe:matrix.org</translation> + </message> + <message> + <location line="+2"/> + <source>Your login name. A mxid should start with @ followed by the user id. After the user id you need to include your server name after a :. +You can also put your homeserver address there, if your server doesn&apos;t support .well-known lookup. +Example: @user:server.my +If Nheko fails to discover your homeserver, it will show you a field to enter the server manually.</source> + <translation>Il tuo nome utente. Un mxid dovrebbe iniziare con @ seguita dall&apos;user id. Dopo l&apos;user id hai bisogno di includere il nome del tuo server dopo un :. +Puoi anche inserire qui l&apos;indirizzo del tuo homeserver, se il tuo server non supporta la ricerca con .well-known. +Esempio: @utente:server.mio +Se Nheko non conclude la ricerca del tuo homeserver, ti mostrerà un campo in cui inserire il server manualmente.</translation> + </message> + <message> + <location line="+21"/> + <source>Password</source> + <translation>Password</translation> + </message> + <message> + <location line="+5"/> + <source>Device name</source> + <translation>Nome del dispositivo</translation> + </message> + <message> + <location line="+2"/> + <source>A name for this device, which will be shown to others, when verifying your devices. If none is provided, a random string is used for privacy purposes.</source> + <translation>Un nome per questo dispositivo, che sarà mostrato agli altri mentre si verificano i tuoi dispositivi. Se non ne fornisci uno, verrà usata una stringa casuale per ragioni di privacy. </translation> + </message> + <message> + <location line="+6"/> + <source>The address that can be used to contact you homeservers client API. +Example: https://server.my:8787</source> + <translation>L&apos;indirizzo che può essere usato per contattare le API client del tuo homeserver. +Esempio: https://server.mio:8787</translation> + </message> + <message> + <location line="+16"/> + <location line="+185"/> + <source>LOGIN</source> + <translation>ACCEDI</translation> + </message> + <message> + <location line="-100"/> + <source>Autodiscovery failed. Received malformed response.</source> + <translation>Ricerca automatica fallita. Ricevuta risposta malformata.</translation> + </message> + <message> + <location line="+6"/> + <source>Autodiscovery failed. Unknown error when requesting .well-known.</source> + <translation>Ricerca automatica fallita. Errore ignoto durante la richiesta di .well-known.</translation> + </message> + <message> + <location line="+25"/> + <source>The required endpoints were not found. Possibly not a Matrix server.</source> + <translation>Gli endpoint richiesti non sono stati trovati. Forse non è un server Matrix.</translation> + </message> + <message> + <location line="+6"/> + <source>Received malformed response. Make sure the homeserver domain is valid.</source> + <translation>Ricevuta risposta malformata. Assicurati che il dominio dell&apos;homeserver sia valido.</translation> + </message> + <message> + <location line="+5"/> + <source>An unknown error occured. Make sure the homeserver domain is valid.</source> + <translation>Avvenuto un errore sconosciuto. Assicurati che il dominio dell&apos;homeserver sia valido.</translation> + </message> + <message> + <location line="+55"/> + <source>SSO LOGIN</source> + <translation>ACCESSO SSO</translation> + </message> + <message> + <location line="+25"/> + <source>Empty password</source> + <translation>Password vuota</translation> + </message> + <message> + <location line="+52"/> + <source>SSO login failed</source> + <translation>Accesso SSO fallito</translation> + </message> +</context> +<context> + <name>MemberList</name> + <message> + <location filename="../../src/dialogs/MemberList.cpp" line="+90"/> + <source>Room members</source> + <translation>Membri della stanza</translation> + </message> + <message> + <location line="+4"/> + <source>OK</source> + <translation>OK</translation> + </message> +</context> +<context> + <name>MessageDelegate</name> + <message> + <location filename="../qml/delegates/MessageDelegate.qml" line="+66"/> + <source>redacted</source> + <translation>oscurato</translation> + </message> + <message> + <location line="+6"/> + <source>Encryption enabled</source> + <translation>Crittografia abilitata</translation> + </message> + <message> + <location line="+6"/> + <source>room name changed to: %1</source> + <translation>nome della stanza cambiato in: %1</translation> + </message> + <message> + <location line="+0"/> + <source>removed room name</source> + <translation>nome della stanza rimosso</translation> + </message> + <message> + <location line="+6"/> + <source>topic changed to: %1</source> + <translation>argomento cambiato in: %1</translation> + </message> + <message> + <location line="+0"/> + <source>removed topic</source> + <translation>argomento rimosso</translation> + </message> + <message> + <location line="+6"/> + <source>%1 created and configured room: %2</source> + <translation>%1 creato e configurata stanza: %2</translation> + </message> +</context> +<context> + <name>Placeholder</name> + <message> + <location filename="../qml/delegates/Placeholder.qml" line="+4"/> + <source>unimplemented event: </source> + <translation> event non implementato:</translation> + </message> +</context> +<context> + <name>QuickSwitcher</name> + <message> + <location filename="../../src/QuickSwitcher.cpp" line="+74"/> + <source>Search for a room...</source> + <translation>Cerca una stanza…</translation> + </message> +</context> +<context> + <name>RegisterPage</name> + <message> + <location filename="../../src/RegisterPage.cpp" line="+87"/> + <source>Username</source> + <translation>Nome utente</translation> + </message> + <message> + <location line="+3"/> + <source>The username must not be empty, and must contain only the characters a-z, 0-9, ., _, =, -, and /.</source> + <translation>Il nome utente non deve essere vuoto e deve contenere solo i caratteri a-z, 0-9, ., _, =, -, e /.</translation> + </message> + <message> + <location line="+4"/> + <source>Password</source> + <translation>Password</translation> + </message> + <message> + <location line="+2"/> + <source>Please choose a secure password. The exact requirements for password strength may depend on your server.</source> + <translation>Per favore scegli una password sicura. I requisiti di robustezza della password potrebbero dipendere dal server.</translation> + </message> + <message> + <location line="+4"/> + <source>Password confirmation</source> + <translation>Conferma della password</translation> + </message> + <message> + <location line="+4"/> + <source>Homeserver</source> + <translation>Homeserver</translation> + </message> + <message> + <location line="+2"/> + <source>A server that allows registration. Since matrix is decentralized, you need to first find a server you can register on or host your own.</source> + <translation>Un server che consente la registrazione. Siccome matrix è decentralizzata, devi prima trovare un server su cui registrarti o ospitarne uno tuo.</translation> + </message> + <message> + <location line="+17"/> + <source>REGISTER</source> + <translation>REGISTRATI</translation> + </message> + <message> + <location line="+55"/> + <source>No supported registration flows!</source> + <translation>Non ci sono processi di registrazione supportati!</translation> + </message> + <message> + <location line="+135"/> + <source>Invalid username</source> + <translation>Nome utente non valido</translation> + </message> + <message> + <location line="+2"/> + <source>Password is not long enough (min 8 chars)</source> + <translation>La password non è abbastanza lunga (minimo 8 caratteri)</translation> + </message> + <message> + <location line="+2"/> + <source>Passwords don&apos;t match</source> + <translation>Le password non corrispondono</translation> + </message> + <message> + <location line="+2"/> + <source>Invalid server name</source> + <translation>Nome del server non valido</translation> + </message> +</context> +<context> + <name>RoomInfo</name> + <message> + <location filename="../../src/Cache.cpp" line="+939"/> + <source>no version stored</source> + <translation>nessuna versione memorizzata</translation> + </message> +</context> +<context> + <name>RoomInfoListItem</name> + <message> + <location filename="../../src/RoomInfoListItem.cpp" line="+98"/> + <source>Leave room</source> + <translation>Lascia la stanza</translation> + </message> + <message> + <location line="+160"/> + <source>Accept</source> + <translation>Accetta</translation> + </message> + <message> + <location line="+3"/> + <source>Decline</source> + <translation>Rifiuta</translation> + </message> +</context> +<context> + <name>SideBarActions</name> + <message> + <location filename="../../src/SideBarActions.cpp" line="+38"/> + <source>User settings</source> + <translation>Impostazioni utente</translation> + </message> + <message> + <location line="+7"/> + <source>Create new room</source> + <translation>Crea una nuova stanza</translation> + </message> + <message> + <location line="+1"/> + <source>Join a room</source> + <translation>Entra in una stanza</translation> + </message> + <message> + <location line="+16"/> + <source>Start a new chat</source> + <translation>Inizia una nuova discussione</translation> + </message> + <message> + <location line="+15"/> + <source>Room directory</source> + <translation>Elenco delle stanze</translation> + </message> +</context> +<context> + <name>StatusIndicator</name> + <message> + <location filename="../qml/StatusIndicator.qml" line="+14"/> + <source>Failed</source> + <translation>Fallito</translation> + </message> + <message> + <location line="+1"/> + <source>Sent</source> + <translation>Inviato</translation> + </message> + <message> + <location line="+1"/> + <source>Received</source> + <translation>Ricevuto</translation> + </message> + <message> + <location line="+1"/> + <source>Read</source> + <translation>Letto</translation> + </message> +</context> +<context> + <name>TextInputWidget</name> + <message> + <location filename="../../src/TextInputWidget.cpp" line="+459"/> + <source>Send a file</source> + <translation>Invia un file</translation> + </message> + <message> + <location line="+13"/> + <location filename="../../src/TextInputWidget.h" line="+145"/> + <source>Write a message...</source> + <translation>Scrivi un messaggio…</translation> + </message> + <message> + <location line="+31"/> + <source>Send a message</source> + <translation>Invia un messaggio</translation> + </message> + <message> + <location line="+8"/> + <source>Emoji</source> + <translation>Emoji</translation> + </message> + <message> + <location line="+84"/> + <source>Select a file</source> + <translation>Seleziona un file</translation> + </message> + <message> + <location line="+0"/> + <source>All Files (*)</source> + <translation>Tutti i file (*)</translation> + </message> + <message> + <location filename="../../src/TextInputWidget.h" line="-5"/> + <source>Connection lost. Nheko is trying to re-connect...</source> + <translation>Connessione interrotta. Nheko sta provando a riconnettersi…</translation> + </message> +</context> +<context> + <name>TimelineModel</name> + <message> + <location filename="../../src/timeline/TimelineModel.cpp" line="+853"/> + <source>-- Decryption Error (failed to communicate with DB) --</source> + <comment>Placeholder, when the message can&apos;t be decrypted, because the DB access failed when trying to lookup the session.</comment> + <translation>-- Errore di Decriptazione (impossibile comunicare con il DB) --</translation> + </message> + <message> + <location line="+20"/> + <source>-- Decryption Error (failed to retrieve megolm keys from db) --</source> + <comment>Placeholder, when the message can&apos;t be decrypted, because the DB access failed.</comment> + <translation>-- Errore di Decrittazione (impossibile recuperare le chiavi megolm dal DB) --</translation> + </message> + <message> + <location line="+13"/> + <source>-- Decryption Error (%1) --</source> + <comment>Placeholder, when the message can&apos;t be decrypted. In this case, the Olm decrytion returned an error, which is passed ad %1.</comment> + <translation>-- Errore di Decrittazione (%1) --</translation> + </message> + <message> + <location line="+99"/> + <source>Message redaction failed: %1</source> + <translation>Oscuramento del messaggio fallito: %1</translation> + </message> + <message> + <location line="+455"/> + <source>Save image</source> + <translation>Salva immagine</translation> + </message> + <message> + <location line="+2"/> + <source>Save video</source> + <translation>Salva video</translation> + </message> + <message> + <location line="+2"/> + <source>Save audio</source> + <translation>Salva audio</translation> + </message> + <message> + <location line="+2"/> + <source>Save file</source> + <translation>Salva file</translation> + </message> + <message> + <location line="-610"/> + <source>-- Encrypted Event (No keys found for decryption) --</source> + <comment>Placeholder, when the message was not decrypted yet or can&apos;t be decrypted.</comment> + <translation>-- Evento Criptato (Chiavi per la decriptazione non trovate) --</translation> + </message> + <message> + <location line="+82"/> + <source>-- Encrypted Event (Unknown event type) --</source> + <comment>Placeholder, when the message was decrypted, but we couldn&apos;t parse it, because Nheko/mtxclient don&apos;t support that event type yet.</comment> + <translation>-- Evento Criptato (Tipo di evento ignoto) --</translation> + </message> + <message numerus="yes"> + <location line="+660"/> + <source>%1 and %2 are typing.</source> + <comment>Multiple users are typing. First argument is a comma separated list of potentially multiple users. Second argument is the last user of that list. (If only one user is typing, %1 is empty. You should still use it in your string though to silence Qt warnings.)</comment> + <translation> + <numerusform>%1%2 sta scrivendo.</numerusform> + <numerusform>%1 e %2 stanno scrivendo.</numerusform> + </translation> + </message> + <message> + <location line="+68"/> + <source>%1 opened the room to the public.</source> + <translation>%1 ha aperto la stanza al pubblico.</translation> + </message> + <message> + <location line="+2"/> + <source>%1 made this room require and invitation to join.</source> + <translation>%1 ha configurato questa stanza per richiedere un invito per entrare.</translation> + </message> + <message> + <location line="+23"/> + <source>%1 made the room open to guests.</source> + <translation>%1 ha configurato questa stanza affinché sia aperta ai visitatori.</translation> + </message> + <message> + <location line="+2"/> + <source>%1 has closed the room to guest access.</source> + <translation>%1 ha chiuso la stanza ai visitatori.</translation> + </message> + <message> + <location line="+23"/> + <source>%1 made the room history world readable. Events may be now read by non-joined people.</source> + <translation>%1 ha reso la cronologia della stanza leggibile da tutti. Gli eventi adesso possono essere letti da persone esterne. </translation> + </message> + <message> + <location line="+4"/> + <source>%1 set the room history visible to members from this point on.</source> + <translation>%1 ha reso la cronologia della stanza visibile ai membri da questo momento in poi. </translation> + </message> + <message> + <location line="+3"/> + <source>%1 set the room history visible to members since they were invited.</source> + <translation>%1 ha reso la cronologia della stanza visibile ai membri dal momento in cui sono stati invitati.</translation> + </message> + <message> + <location line="+3"/> + <source>%1 set the room history visible to members since they joined the room.</source> + <translation>%1 ha reso la cronologia della stanza visibile ai membri dal momento in cui sono entrati nella stanza.</translation> + </message> + <message> + <location line="+22"/> + <source>%1 has changed the room&apos;s permissions.</source> + <translation>%1 ha cambiato i permessi della stanza.</translation> + </message> + <message> + <location line="+48"/> + <source>%1 was invited.</source> + <translation>%1 è stato invitato.</translation> + </message> + <message> + <location line="+11"/> + <source>%1 changed their display name and avatar.</source> + <translation>%1 ha cambiato il suo nome visualizzato e l&apos;avatar.</translation> + </message> + <message> + <location line="+2"/> + <source>%1 changed their display name.</source> + <translation>%1 ha cambiato il suo nome visualizzato.</translation> + </message> + <message> + <location line="+2"/> + <source>%1 changed their avatar.</source> + <translation>%1 ha cambiato il suo avatar.</translation> + </message> + <message> + <location line="+4"/> + <source>%1 joined.</source> + <translation>%1 è entrato.</translation> + </message> + <message> + <location line="+9"/> + <source>%1 rejected their invite.</source> + <translation>%1 ha rifiutato il suo invito.</translation> + </message> + <message> + <location line="+2"/> + <source>Revoked the invite to %1.</source> + <translation>Revocato l&apos;invito a %1.</translation> + </message> + <message> + <location line="+3"/> + <source>%1 left the room.</source> + <translation>%1 ha lasciato la stanza.</translation> + </message> + <message> + <location line="+2"/> + <source>Kicked %1.</source> + <translation>Scacciato %1.</translation> + </message> + <message> + <location line="+2"/> + <source>Unbanned %1.</source> + <translation>Rimosso ban da %1.</translation> + </message> + <message> + <location line="+14"/> + <source>%1 was banned.</source> + <translation>%1 è stato bannato.</translation> + </message> + <message> + <location line="-11"/> + <source>%1 redacted their knock.</source> + <translation>%1 ha oscurato la sua bussata.</translation> + </message> + <message> + <location line="-1238"/> + <source>You joined this room.</source> + <translation>Sei entrato in questa stanza.</translation> + </message> + <message> + <location line="+1240"/> + <source>Rejected the knock from %1.</source> + <translation>Rifiutata la bussata di %1.</translation> + </message> + <message> + <location line="+2"/> + <source>%1 left after having already left!</source> + <comment>This is a leave event after the user already left and shouldn&apos;t happen apart from state resets</comment> + <translation>%1 è uscito dopo essere già uscito!</translation> + </message> + <message> + <location line="+15"/> + <source> Reason: %1</source> + <translation>Motivazione: %1</translation> + </message> + <message> + <location line="-5"/> + <source>%1 knocked.</source> + <translation>%1 ha bussato.</translation> + </message> +</context> +<context> + <name>TimelineRow</name> + <message> + <location filename="../qml/TimelineRow.qml" line="+88"/> + <source>Reply</source> + <translation>Rispondi</translation> + </message> + <message> + <location line="+15"/> + <source>Options</source> + <translation>Opzioni</translation> + </message> +</context> +<context> + <name>TimelineView</name> + <message> + <location filename="../qml/TimelineView.qml" line="+53"/> + <source>Reply</source> + <translation>Risposta</translation> + </message> + <message> + <location line="+4"/> + <source>Read receipts</source> + <translation>Leggi le ricevute</translation> + </message> + <message> + <location line="+4"/> + <source>Mark as read</source> + <translation>Segna come letto</translation> + </message> + <message> + <location line="+3"/> + <source>View raw message</source> + <translation>Mostra il messaggio grezzo</translation> + </message> + <message> + <location line="+6"/> + <source>View decrypted raw message</source> + <translation>Mostra il messaggio grezzo decriptato</translation> + </message> + <message> + <location line="+4"/> + <source>Redact message</source> + <translation>Oscura messaggio</translation> + </message> + <message> + <location line="+6"/> + <source>Save as</source> + <translation>Salva come</translation> + </message> + <message> + <location line="+14"/> + <source>No room open</source> + <translation>Nessuna stanza aperta</translation> + </message> + <message> + <location line="+256"/> + <source>Close</source> + <translation>Chiudi</translation> + </message> +</context> +<context> + <name>TopRoomBar</name> + <message> + <location filename="../../src/TopRoomBar.cpp" line="+86"/> + <source>Room options</source> + <translation>Opzioni della stanza</translation> + </message> + <message> + <location line="+5"/> + <source>Mentions</source> + <translation>Menzioni</translation> + </message> + <message> + <location line="+34"/> + <source>Invite users</source> + <translation>Invita utenti</translation> + </message> + <message> + <location line="+6"/> + <source>Members</source> + <translation>Membri</translation> + </message> + <message> + <location line="+5"/> + <source>Leave room</source> + <translation>Lascia la stanza</translation> + </message> + <message> + <location line="+5"/> + <source>Settings</source> + <translation>Impostazioni</translation> + </message> +</context> +<context> + <name>TrayIcon</name> + <message> + <location filename="../../src/TrayIcon.cpp" line="+122"/> + <source>Show</source> + <translation>Rivela</translation> + </message> + <message> + <location line="+1"/> + <source>Quit</source> + <translation>Esci</translation> + </message> +</context> +<context> + <name>UserInfoWidget</name> + <message> + <location filename="../../src/UserInfoWidget.cpp" line="+88"/> + <source>Logout</source> + <translation>Disconnettiti</translation> + </message> +</context> +<context> + <name>UserSettingsPage</name> + <message> + <location filename="../../src/UserSettingsPage.cpp" line="+335"/> + <source>Minimize to tray</source> + <translation>Minimizza nella tray</translation> + </message> + <message> + <location line="+1"/> + <source>Start in tray</source> + <translation>Avvia nella tray</translation> + </message> + <message> + <location line="+3"/> + <source>Group&apos;s sidebar</source> + <translation>Barra laterale dei gruppi</translation> + </message> + <message> + <location line="-1"/> + <source>Circular Avatars</source> + <translation>Avatar Circolari</translation> + </message> + <message> + <location line="+2"/> + <source>Decrypt messages in sidebar</source> + <translation>Decripta messaggi nella barra laterale</translation> + </message> + <message> + <location line="+1"/> + <source>Show buttons in timeline</source> + <translation>Mostra pulsanti nella timeline</translation> + </message> + <message> + <location line="+1"/> + <source>Typing notifications</source> + <translation>Notifiche di scrittura</translation> + </message> + <message> + <location line="+1"/> + <source>Sort rooms by unreads</source> + <translation>Ordina stanze per non letti</translation> + </message> + <message> + <location line="+2"/> + <source>Read receipts</source> + <translation>Ricevute di lettura</translation> + </message> + <message> + <location line="+1"/> + <source>Send messages as Markdown</source> + <translation>Invia messaggi come Markdown</translation> + </message> + <message> + <location line="+1"/> + <source>Desktop notifications</source> + <translation>Notifiche desktop</translation> + </message> + <message> + <location line="+5"/> + <source>Scale factor</source> + <translation>Fattore di scala</translation> + </message> + <message> + <location line="+4"/> + <source>Font size</source> + <translation>Dimensione dei caratteri</translation> + </message> + <message> + <location line="+1"/> + <source>Font Family</source> + <translation>Famiglia dei caratteri</translation> + </message> + <message> + <location line="+8"/> + <source>Theme</source> + <translation>Tema</translation> + </message> + <message> + <location line="+3"/> + <source>Device ID</source> + <translation>ID Dispositivo</translation> + </message> + <message> + <location line="+1"/> + <source>Device Fingerprint</source> + <translation>Impronta digitale del dispositivo</translation> + </message> + <message> + <location line="-59"/> + <source>Session Keys</source> + <translation>Chiavi di Sessione</translation> + </message> + <message> + <location line="+4"/> + <source>IMPORT</source> + <translation>IMPORTA</translation> + </message> + <message> + <location line="+1"/> + <source>EXPORT</source> + <translation>ESPORTA</translation> + </message> + <message> + <location line="-25"/> + <source>ENCRYPTION</source> + <translation>CRITTOGRAFIA</translation> + </message> + <message> + <location line="-63"/> + <source>GENERAL</source> + <translation>GENERALE</translation> + </message> + <message> + <location line="+25"/> + <source>INTERFACE</source> + <translation>INTERFACCIA</translation> + </message> + <message> + <location line="+108"/> + <source>Emoji Font Family</source> + <translation>Famiglia dei caratteri delle Emoji</translation> + </message> + <message> + <location line="+158"/> + <source>Open Sessions File</source> + <translation>Apri File delle Sessioni</translation> + </message> + <message> + <location line="+4"/> + <location line="+18"/> + <location line="+9"/> + <location line="+2"/> + <location line="+2"/> + <location line="+19"/> + <location line="+11"/> + <location line="+18"/> + <location line="+2"/> + <location line="+2"/> + <source>Error</source> + <translation>Errore</translation> + </message> + <message> + <location line="-74"/> + <location line="+32"/> + <source>File Password</source> + <translation>Password del File</translation> + </message> + <message> + <location line="-31"/> + <source>Enter the passphrase to decrypt the file:</source> + <translation>Inserisci la passphrase per decriptare il file:</translation> + </message> + <message> + <location line="+8"/> + <location line="+32"/> + <source>The password cannot be empty</source> + <translation>La password non può essere vuota</translation> + </message> + <message> + <location line="-8"/> + <source>Enter passphrase to encrypt your session keys:</source> + <translation>Inserisci la passphrase per criptare le tue chiavi di sessione:</translation> + </message> + <message> + <location line="+15"/> + <source>File to save the exported session keys</source> + <translation>File ove salvare le chiavi di sessione esportate</translation> + </message> +</context> +<context> + <name>WelcomePage</name> + <message> + <location filename="../../src/WelcomePage.cpp" line="+47"/> + <source>Welcome to nheko! The desktop client for the Matrix protocol.</source> + <translation>Benvenuto su nheko! Il client desktop per il protocollo Matrix.</translation> + </message> + <message> + <location line="+1"/> + <source>Enjoy your stay!</source> + <translation>Goditi la permanenza!</translation> + </message> + <message> + <location line="+23"/> + <source>REGISTER</source> + <translation>REGISTRATI</translation> + </message> + <message> + <location line="+5"/> + <source>LOGIN</source> + <translation>ACCEDI</translation> + </message> +</context> +<context> + <name>descriptiveTime</name> + <message> + <location filename="../../src/Utils.cpp" line="+141"/> + <source>Yesterday</source> + <translation>Ieri</translation> + </message> +</context> +<context> + <name>dialogs::CreateRoom</name> + <message> + <location filename="../../src/dialogs/CreateRoom.cpp" line="+36"/> + <source>Create room</source> + <translation>Crea stanza</translation> + </message> + <message> + <location line="+2"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location line="+10"/> + <source>Name</source> + <translation>Nome</translation> + </message> + <message> + <location line="+3"/> + <source>Topic</source> + <translation>Argomento</translation> + </message> + <message> + <location line="+3"/> + <source>Alias</source> + <translation>Alias</translation> + </message> + <message> + <location line="+8"/> + <source>Room Visibility</source> + <translation>Visibilità Stanza</translation> + </message> + <message> + <location line="+8"/> + <source>Room Preset</source> + <translation>Preset Stanza</translation> + </message> + <message> + <location line="+9"/> + <source>Direct Chat</source> + <translation>Chat Diretta</translation> + </message> +</context> +<context> + <name>dialogs::FallbackAuth</name> + <message> + <location filename="../../src/dialogs/FallbackAuth.cpp" line="+30"/> + <source>Open Fallback in Browser</source> + <translation>Apertura di Ripiego nel Browser </translation> + </message> + <message> + <location line="+1"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location line="+1"/> + <source>Confirm</source> + <translation>Conferma</translation> + </message> + <message> + <location line="+12"/> + <source>Open the fallback, follow the steps and confirm after completing them.</source> + <translation>Apri il ripiego, segui i passaggi e conferma dopo averli completati.</translation> + </message> +</context> +<context> + <name>dialogs::InviteUsers</name> + <message> + <location filename="../../src/dialogs/InviteUsers.cpp" line="+41"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location line="+8"/> + <source>User ID to invite</source> + <translation>ID utente da invitare</translation> + </message> +</context> +<context> + <name>dialogs::JoinRoom</name> + <message> + <location filename="../../src/dialogs/JoinRoom.cpp" line="+30"/> + <source>Join</source> + <translation>Entra</translation> + </message> + <message> + <location line="+2"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location line="+7"/> + <source>Room ID or alias</source> + <translation>ID della stanza o alias</translation> + </message> +</context> +<context> + <name>dialogs::LeaveRoom</name> + <message> + <location filename="../../src/dialogs/LeaveRoom.cpp" line="+31"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location line="+8"/> + <source>Are you sure you want to leave?</source> + <translation>Sei sicuro di voler uscire?</translation> + </message> +</context> +<context> + <name>dialogs::Logout</name> + <message> + <location filename="../../src/dialogs/Logout.cpp" line="+47"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location line="+8"/> + <source>Logout. Are you sure?</source> + <translation>Uscita. Ne sei certo?</translation> + </message> +</context> +<context> + <name>dialogs::PreviewUploadOverlay</name> + <message> + <location filename="../../src/dialogs/PreviewUploadOverlay.cpp" line="+41"/> + <source>Upload</source> + <translation>Upload</translation> + </message> + <message> + <location line="+1"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location line="+84"/> + <source>Media type: %1 +Media size: %2 +</source> + <translation>Tipo media: %1 +Peso media: %2 +</translation> + </message> +</context> +<context> + <name>dialogs::ReCaptcha</name> + <message> + <location filename="../../src/dialogs/ReCaptcha.cpp" line="+31"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location line="+1"/> + <source>Confirm</source> + <translation>Conferma</translation> + </message> + <message> + <location line="+11"/> + <source>Solve the reCAPTCHA and press the confirm button</source> + <translation>Risolvi il reCAPTCHA e premi il pulsante di conferma</translation> + </message> +</context> +<context> + <name>dialogs::ReadReceipts</name> + <message> + <location filename="../../src/dialogs/ReadReceipts.cpp" line="+117"/> + <source>Read receipts</source> + <translation>Ricevute di lettura</translation> + </message> + <message> + <location line="+4"/> + <source>Close</source> + <translation>Chiudi</translation> + </message> +</context> +<context> + <name>dialogs::ReceiptItem</name> + <message> + <location line="-44"/> + <source>Today %1</source> + <translation>Oggi %1</translation> + </message> + <message> + <location line="+2"/> + <source>Yesterday %1</source> + <translation>Ieri %1</translation> + </message> +</context> +<context> + <name>dialogs::RoomSettings</name> + <message> + <location filename="../../src/dialogs/RoomSettings.cpp" line="+113"/> + <source>Settings</source> + <translation>Impostazioni</translation> + </message> + <message> + <location line="+3"/> + <source>Info</source> + <translation>Informazioni</translation> + </message> + <message> + <location line="+11"/> + <source>Internal ID</source> + <translation>ID interno</translation> + </message> + <message> + <location line="+10"/> + <source>Room Version</source> + <translation>Versione Stanza</translation> + </message> + <message> + <location line="+4"/> + <source>Notifications</source> + <translation>Notifiche</translation> + </message> + <message> + <location line="+2"/> + <source>Muted</source> + <translation>Silenziata</translation> + </message> + <message> + <location line="+2"/> + <source>Mentions only</source> + <translation>Solo menzioni</translation> + </message> + <message> + <location line="+1"/> + <source>All messages</source> + <translation>Tutti i messaggi</translation> + </message> + <message> + <location line="+97"/> + <source>Room access</source> + <translation>Accesso stanza</translation> + </message> + <message> + <location line="+2"/> + <source>Anyone and guests</source> + <translation>Chiunque ed ospiti</translation> + </message> + <message> + <location line="+1"/> + <source>Anyone</source> + <translation>Chiunque conosca il link della stanza (no ospiti)</translation> + </message> + <message> + <location line="+1"/> + <source>Invited users</source> + <translation>Utenti invitati</translation> + </message> + <message> + <location line="+50"/> + <source>Encryption</source> + <translation>Crittografia</translation> + </message> + <message> + <location line="+8"/> + <source>End-to-End Encryption</source> + <translation>Crittografia End-to-End</translation> + </message> + <message> + <location line="+1"/> + <source>Encryption is currently experimental and things might break unexpectedly. &lt;br&gt;Please take note that it can&apos;t be disabled afterwards.</source> + <translation>La crittografia è ancora sperimentale e le cose potrebbero rompersi inaspettatamente. &lt;br&gt;Per favore prendi nota che in seguito non potrà essere disabilitata.</translation> + </message> + <message> + <location line="+27"/> + <source>Respond to key requests</source> + <translation>Rispondi alle richieste di chiavi</translation> + </message> + <message> + <location line="+3"/> + <source>Whether or not the client should respond automatically with the session keys + upon request. Use with caution, this is a temporary measure to test the + E2E implementation until device verification is completed.</source> + <translation>Indica se il client deve rispondere automaticamente con le chiavi di sessione + su richiesta. Usa con cautela, questa è una misura temporanea per testare + l&apos;implementazione di E2E fino al completamento della verifica dei dispositivi.</translation> + </message> + <message numerus="yes"> + <location line="+51"/> + <source>%n member(s)</source> + <translation> + <numerusform>%n membro</numerusform> + <numerusform>%n membri</numerusform> + </translation> + </message> + <message> + <location line="+140"/> + <source>Failed to enable encryption: %1</source> + <translation>Impossibile abilitare la crittografia: %1</translation> + </message> + <message> + <location line="+147"/> + <source>Select an avatar</source> + <translation>Scegli un avatar</translation> + </message> + <message> + <location line="+0"/> + <source>All Files (*)</source> + <translation>Tutti i File (*)</translation> + </message> + <message> + <location line="+12"/> + <source>The selected file is not an image</source> + <translation>Il file selezionato non è un&apos;immagine</translation> + </message> + <message> + <location line="+5"/> + <source>Error while reading file: %1</source> + <translation>Errore durante la lettura del file: %1</translation> + </message> + <message> + <location line="+35"/> + <location line="+20"/> + <source>Failed to upload image: %s</source> + <translation>Impossibile fare l&apos;upload dell&apos;immagine: %s</translation> + </message> +</context> +<context> + <name>dialogs::UserProfile</name> + <message> + <location filename="../../src/dialogs/UserProfile.cpp" line="+63"/> + <source>Ban the user from the room</source> + <translation>Banna l&apos;utente dalla stanza</translation> + </message> + <message> + <location line="+8"/> + <source>Ignore messages from this user</source> + <translation>Ignora i messaggi da questo utente</translation> + </message> + <message> + <location line="+9"/> + <source>Kick the user from the room</source> + <translation>Scaccia l&apos;utente dalla stanza</translation> + </message> + <message> + <location line="+8"/> + <source>Start a conversation</source> + <translation>Inizia una conversazione</translation> + </message> + <message> + <location line="+61"/> + <source>Devices</source> + <translation>Dispositivi</translation> + </message> +</context> +<context> + <name>emoji::Panel</name> + <message> + <location filename="../../src/emoji/Panel.cpp" line="+122"/> + <source>Smileys &amp; People</source> + <translation>Faccine &amp; Persone</translation> + </message> + <message> + <location line="+4"/> + <source>Animals &amp; Nature</source> + <translation>Animali &amp; Natura</translation> + </message> + <message> + <location line="+3"/> + <source>Food &amp; Drink</source> + <translation>Cibi &amp; Bevande</translation> + </message> + <message> + <location line="+3"/> + <source>Activity</source> + <translation>Attività</translation> + </message> + <message> + <location line="+4"/> + <source>Travel &amp; Places</source> + <translation>Viaggi &amp; Luoghi</translation> + </message> + <message> + <location line="+3"/> + <source>Objects</source> + <translation>Oggetti</translation> + </message> + <message> + <location line="+3"/> + <source>Symbols</source> + <translation>Simboli</translation> + </message> + <message> + <location line="+3"/> + <source>Flags</source> + <translation>Bandiere</translation> + </message> +</context> +<context> + <name>message-description sent:</name> + <message> + <location filename="../../src/Utils.h" line="+100"/> + <source>You sent an audio clip</source> + <translation>Hai inviato una clip audio</translation> + </message> + <message> + <location line="+3"/> + <source>%1 sent an audio clip</source> + <translation>%1 ha inviato una clip audio</translation> + </message> + <message> + <location line="+5"/> + <source>You sent an image</source> + <translation>Hai inviato un&apos;immagine</translation> + </message> + <message> + <location line="+3"/> + <source>%1 sent an image</source> + <translation>%1 ha inviato un&apos;immagine</translation> + </message> + <message> + <location line="+5"/> + <source>You sent a file</source> + <translation>Hai inviato un file</translation> + </message> + <message> + <location line="+3"/> + <source>%1 sent a file</source> + <translation>%1 ha inviato un file</translation> + </message> + <message> + <location line="+5"/> + <source>You sent a video</source> + <translation>Hai inviato un video</translation> + </message> + <message> + <location line="+3"/> + <source>%1 sent a video</source> + <translation>%1 ha inviato un video</translation> + </message> + <message> + <location line="+5"/> + <source>You sent a sticker</source> + <translation>Hai inviato uno sticker</translation> + </message> + <message> + <location line="+3"/> + <source>%1 sent a sticker</source> + <translation>%1 ha inviato uno sticker</translation> + </message> + <message> + <location line="+5"/> + <source>You sent a notification</source> + <translation>Hai inviato una notifica</translation> + </message> + <message> + <location line="+3"/> + <source>%1 sent a notification</source> + <translation>%1 ha inviato una notifica</translation> + </message> + <message> + <location line="+5"/> + <source>You: %1</source> + <translation>Tu: %1</translation> + </message> + <message> + <location line="+3"/> + <source>%1: %2</source> + <translation>%1: %2</translation> + </message> + <message> + <location line="+7"/> + <source>You sent an encrypted message</source> + <translation>Hai inviato un messaggio criptato</translation> + </message> + <message> + <location line="+3"/> + <source>%1 sent an encrypted message</source> + <translation>%1 ha inviato un messaggio criptato</translation> + </message> +</context> +<context> + <name>popups::UserMentions</name> + <message> + <location filename="../../src/popups/UserMentions.cpp" line="+64"/> + <source>This Room</source> + <translation>Questa Stanza</translation> + </message> + <message> + <location line="+1"/> + <source>All Rooms</source> + <translation>Tutte le Stanze</translation> + </message> +</context> +<context> + <name>utils</name> + <message> + <location filename="../../src/Utils.h" line="+4"/> + <source>Unknown Message Type</source> + <translation>Tipo di Messaggio sconosciuto</translation> + </message> +</context> +<context> + <name></name> + <message> + <location line="+7"/> + <source>Tag room as:</source> + <translation>stanza come:</translation> + </message> + <message> + <location line="+7"/> + <source>Favourite</source> + <comment>Standard matrix tag for favourites</comment> + <translation>Tag matrix standard per i preferiti</translation> + </message> + <message> + <location line="+13"/> + <source>Adds or removes the specified tag.</source> + <comment>WhatsThis hint for tag menu actions</comment> + <translation>Aggiungi o rimuovi il tag specificato.</translation> + </message> + <message> + <location line="+1"/> + <source>Highlight message on hover</source> + <translation>Evidenzia il messaggio al passaggio del mouse</translation> + </message> +</context> +</TS> diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index ed065270..465a8e1c 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml
@@ -6,7 +6,7 @@ Rectangle { id: avatar width: 48 height: 48 - radius: settings.avatar_circles ? height/2 : 3 + radius: settings.avatarCircles ? height/2 : 3 property alias url: img.source property string displayName @@ -39,7 +39,7 @@ Rectangle { anchors.fill: parent width: avatar.width height: avatar.height - radius: settings.avatar_circles ? height/2 : 3 + radius: settings.avatarCircles ? height/2 : 3 } } } diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml
index b272e2e6..848e4974 100644 --- a/resources/qml/Reactions.qml +++ b/resources/qml/Reactions.qml
@@ -57,7 +57,7 @@ Flow { Text { anchors.baseline: reactionCounter.baseline id: reactionText - text: textMetrics.elidedText + (textMetrics.elidedText == model.key ? "" : "…") + text: textMetrics.elidedText + (textMetrics.elidedText == textMetrics.text ? "" : "…") font.family: settings.emoji_font_family color: reaction.hovered ? colors.highlight : colors.text maximumLineCount: 1 @@ -65,7 +65,7 @@ Flow { Rectangle { id: divider - height: reactionCounter.implicitHeight * 1.4 + height: Math.floor(reactionCounter.implicitHeight * 1.4) width: 1 color: (reaction.hovered || model.selfReactedEvent !== '') ? colors.highlight : colors.text } diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml
index 9961d15f..cbec6d94 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml
@@ -26,13 +26,13 @@ MouseArea { messageContextMenu.show(model.id, model.type, model.isEncrypted, row) } Rectangle { - color: (timelineSettings.message_hover_highlight && parent.containsMouse) ? colors.base : "transparent" + color: (settings.messageHoverHighlight && parent.containsMouse) ? colors.base : "transparent" anchors.fill: row } RowLayout { id: row - anchors.leftMargin: avatarSize + 4 + anchors.leftMargin: avatarSize + 16 anchors.left: parent.left anchors.right: parent.right @@ -97,7 +97,7 @@ MouseArea { event_id: model.id } ImageButton { - visible: timelineSettings.buttons + visible: settings.buttonsInTimeline Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.preferredHeight: 16 width: 16 @@ -113,7 +113,7 @@ MouseArea { onClicked: chat.model.replyAction(model.id) } ImageButton { - visible: timelineSettings.buttons + visible: settings.buttonsInTimeline Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.preferredHeight: 16 width: 16 diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index e5359910..12c8108a 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import QtQuick.Window 2.2 -import Qt.labs.settings 1.0 import im.nheko 1.0 import im.nheko.EmojiModel 1.0 @@ -122,7 +121,7 @@ Page { BusyIndicator { visible: running anchors.centerIn: parent - running: timelineManager.isInitialSync + running: timelineManager.isInitialSync height: 200 width: 200 z: 3 @@ -133,12 +132,12 @@ Page { visible: timelineManager.timeline != null - cacheBuffer: 500 + cacheBuffer: 400 - anchors.left: parent.left - anchors.right: parent.right + anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.bottom: chatFooter.top + width: parent.width anchors.leftMargin: 4 anchors.rightMargin: scrollbar.width @@ -180,7 +179,7 @@ Page { id: scrollbar parent: chat.parent anchors.top: chat.top - anchors.left: chat.right + anchors.right: chat.right anchors.bottom: chat.bottom } @@ -195,7 +194,8 @@ Page { id: wrapper property Item section - width: chat.width + anchors.horizontalCenter: parent.horizontalCenter + width: (settings.timelineMaxWidth > 100 && (parent.width - settings.timelineMaxWidth) > 32) ? settings.timelineMaxWidth : (parent.width - 32) height: section ? section.height + timelinerow.height : timelinerow.height color: "transparent" @@ -265,7 +265,8 @@ Page { } Row { height: userName.height - spacing: 4 + spacing: 8 + Avatar { width: avatarSize height: avatarSize diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index bef4f76d..b3c45c36 100644 --- a/resources/qml/delegates/TextMessage.qml +++ b/resources/qml/delegates/TextMessage.qml
@@ -6,4 +6,5 @@ MatrixText { width: parent ? parent.width : undefined height: isReply ? Math.min(chat.height / 8, implicitHeight) : undefined clip: true + font.pointSize: (settings.enlargeEmojiOnlyMessages && model.data.isOnlyEmoji > 0 && model.data.isOnlyEmoji < 4) ? settings.fontSize * 3 : settings.fontSize } diff --git a/resources/qml/emoji/EmojiCategory.qml b/resources/qml/emoji/EmojiCategory.qml new file mode 100644
index 00000000..1cd42d0b --- /dev/null +++ b/resources/qml/emoji/EmojiCategory.qml
@@ -0,0 +1,67 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.9 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.9 + +import im.nheko 1.0 +import im.nheko.EmojiModel 1.0 + +GridView { + id: root + property var category + property var emojiPopup + property EmojiProxyModel model: EmojiProxyModel { + sourceModel: EmojiModel { + viewCategory: category + } + } + + interactive: false + + cellWidth: 52 + cellHeight: 52 + height: 52 * ( model.count / 7 + 1 ) + + clip: true + + // Individual emoji + delegate: AbstractButton { + width: 48 + height: 48 + contentItem: Text { + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.family: settings.emoji_font_family + + font.pixelSize: 36 + text: model.unicode + } + + background: Rectangle { + anchors.fill: parent + color: hovered ? colors.highlight : 'transparent' + radius: 5 + } + + hoverEnabled: true + ToolTip.text: model.shortName + ToolTip.visible: hovered + + // give the emoji a little oomf + DropShadow { + width: parent.width; + height: parent.height; + horizontalOffset: 3 + verticalOffset: 3 + radius: 8.0 + samples: 17 + color: "#80000000" + source: parent.contentItem + } + // TODO: maybe add favorites at some point? + onClicked: { + console.debug("Picked " + model.unicode + "in response to " + emojiPopup.event_id + " in room " + emojiPopup.room_id) + emojiPopup.picked(emojiPopup.room_id, emojiPopup.event_id, model.unicode) + } + } +} \ No newline at end of file diff --git a/src/Cache.cpp b/src/Cache.cpp
index 1061e60e..009cbabc 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp
@@ -31,6 +31,7 @@ #include "Cache.h" #include "Cache_p.h" +#include "EventAccessors.h" #include "Logging.h" #include "Utils.h" @@ -1947,13 +1948,14 @@ Cache::saveTimelineMessages(lmdb::txn &txn, json obj = json::object(); - obj["event"] = utils::serialize_event(e); + obj["event"] = mtx::accessors::serialize_event(e); obj["token"] = res.prev_batch; - lmdb::dbi_put(txn, - db, - lmdb::val(std::to_string(utils::event_timestamp(e))), - lmdb::val(obj.dump())); + lmdb::dbi_put( + txn, + db, + lmdb::val(std::to_string(obj["event"]["origin_server_ts"].get<uint64_t>())), + lmdb::val(obj.dump())); } } @@ -2026,7 +2028,7 @@ Cache::saveTimelineMentions(lmdb::txn &txn, using namespace mtx::events::state; for (const auto &notif : res) { - const auto event_id = utils::event_id(notif.event); + const auto event_id = mtx::accessors::event_id(notif.event); // double check that we have the correct room_id... if (room_id.compare(notif.room_id) != 0) { diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 1e06da5d..2340b146 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp
@@ -26,6 +26,7 @@ #include "Cache.h" #include "Cache_p.h" #include "ChatPage.h" +#include "EventAccessors.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" @@ -255,7 +256,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) text_input_, &TextInputWidget::startedTyping, this, &ChatPage::sendTypingNotifications); connect(typingRefresher_, &QTimer::timeout, this, &ChatPage::sendTypingNotifications); connect(text_input_, &TextInputWidget::stoppedTyping, this, [this]() { - if (!userSettings_->isTypingNotificationsEnabled()) + if (!userSettings_->typingNotifications()) return; typingRefresher_->stop(); @@ -482,7 +483,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) activateWindow(); }); - setGroupViewState(userSettings_->isGroupViewEnabled()); + setGroupViewState(userSettings_->groupView()); connect(userSettings_.data(), &UserSettings::groupViewStateChanged, @@ -891,7 +892,7 @@ void ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) { for (const auto &item : res.notifications) { - const auto event_id = utils::event_id(item.event); + const auto event_id = mtx::accessors::event_id(item.event); try { if (item.read) { @@ -901,7 +902,8 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) if (!cache::isNotificationSent(event_id)) { const auto room_id = QString::fromStdString(item.room_id); - const auto user_id = utils::event_sender(item.event); + const auto user_id = + QString::fromStdString(mtx::accessors::sender(item.event)); // We should only sent one notification per event. cache::markSentNotification(event_id); @@ -1213,7 +1215,7 @@ ChatPage::unbanUser(QString userid, QString reason) void ChatPage::sendTypingNotifications() { - if (!userSettings_->isTypingNotificationsEnabled()) + if (!userSettings_->typingNotifications()) return; http::client()->start_typing( @@ -1349,7 +1351,7 @@ ChatPage::hideSideBars() void ChatPage::showSideBars() { - if (userSettings_->isGroupViewEnabled()) + if (userSettings_->groupView()) communitiesList_->show(); sideBar_->show(); diff --git a/src/CommunitiesListItem.h b/src/CommunitiesListItem.h
index 0cc5d60c..535a6ec0 100644 --- a/src/CommunitiesListItem.h +++ b/src/CommunitiesListItem.h
@@ -7,7 +7,6 @@ #include "ui/Theme.h" class RippleOverlay; -class QPainter; class QMouseEvent; class CommunitiesListItem : public QWidget diff --git a/src/EventAccessors.cpp b/src/EventAccessors.cpp
index 7f28eb46..a2d8adbb 100644 --- a/src/EventAccessors.cpp +++ b/src/EventAccessors.cpp
@@ -400,3 +400,9 @@ mtx::accessors::media_width(const mtx::events::collections::TimelineEvents &even { return std::visit(EventMediaWidth{}, event); } + +nlohmann::json +mtx::accessors::serialize_event(const mtx::events::collections::TimelineEvents &event) +{ + return std::visit([](const auto &e) { return nlohmann::json(e); }, event); +} diff --git a/src/EventAccessors.h b/src/EventAccessors.h
index c9ac4d00..a7577d86 100644 --- a/src/EventAccessors.h +++ b/src/EventAccessors.h
@@ -63,4 +63,7 @@ media_height(const mtx::events::collections::TimelineEvents &event); uint64_t media_width(const mtx::events::collections::TimelineEvents &event); + +nlohmann::json +serialize_event(const mtx::events::collections::TimelineEvents &event); } diff --git a/src/InviteeItem.cpp b/src/InviteeItem.cpp
index 906a3bfe..a6b471dc 100644 --- a/src/InviteeItem.cpp +++ b/src/InviteeItem.cpp
@@ -1,4 +1,5 @@ #include <QHBoxLayout> +#include <QLabel> #include <QPushButton> #include "InviteeItem.h" diff --git a/src/InviteeItem.h b/src/InviteeItem.h
index 582904b4..54c61938 100644 --- a/src/InviteeItem.h +++ b/src/InviteeItem.h
@@ -1,11 +1,11 @@ #pragma once -#include <QLabel> #include <QWidget> #include <mtx/identifiers.hpp> class QPushButton; +class QLabel; class InviteeItem : public QWidget { diff --git a/src/LoginPage.cpp b/src/LoginPage.cpp
index bb329699..9a920d1d 100644 --- a/src/LoginPage.cpp +++ b/src/LoginPage.cpp
@@ -16,6 +16,7 @@ */ #include <QDesktopServices> +#include <QLabel> #include <QPainter> #include <QStyleOption> @@ -118,7 +119,7 @@ LoginPage::LoginPage(QWidget *parent) deviceName_->setLabel(tr("Device name")); deviceName_->setToolTip( tr("A name for this device, which will be shown to others, when verifying your devices. " - "If none is provided, a random string is used for privacy purposes.")); + "If none is provided a default is used.")); serverInput_ = new TextField(this); serverInput_->setLabel("Homeserver address"); @@ -132,7 +133,7 @@ LoginPage::LoginPage(QWidget *parent) form_layout_->addLayout(matrixidLayout_); form_layout_->addWidget(password_input_); - form_layout_->addWidget(deviceName_, Qt::AlignHCenter, nullptr); + form_layout_->addWidget(deviceName_, Qt::AlignHCenter); form_layout_->addLayout(serverLayout_); button_layout_ = new QHBoxLayout(); @@ -180,6 +181,12 @@ LoginPage::LoginPage(QWidget *parent) } void +LoginPage::loginError(const QString &msg) +{ + error_label_->setText(msg); +} + +void LoginPage::onMatrixIdEntered() { error_label_->setText(""); diff --git a/src/LoginPage.h b/src/LoginPage.h
index 8a402aea..c9220297 100644 --- a/src/LoginPage.h +++ b/src/LoginPage.h
@@ -17,8 +17,6 @@ #pragma once -#include <QLabel> -#include <QLayout> #include <QWidget> class FlatButton; @@ -26,6 +24,9 @@ class LoadingIndicator; class OverlayModal; class RaisedButton; class TextField; +class QLabel; +class QVBoxLayout; +class QHBoxLayout; namespace mtx { namespace responses { @@ -65,7 +66,7 @@ protected: public slots: // Displays errors produced during the login. - void loginError(const QString &msg) { error_label_->setText(msg); } + void loginError(const QString &msg); private slots: // Callback for the back button. diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index c6abdca2..cc1d868b 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp
@@ -148,7 +148,7 @@ MainWindow::MainWindow(QWidget *parent) QSettings settings; - trayIcon_->setVisible(userSettings_->isTrayEnabled()); + trayIcon_->setVisible(userSettings_->tray()); if (hasActiveUser()) { QString token = settings.value("auth/access_token").toString(); @@ -286,7 +286,7 @@ void MainWindow::closeEvent(QCloseEvent *event) { if (!qApp->isSavingSession() && isVisible() && pageSupportsTray() && - userSettings_->isTrayEnabled()) { + userSettings_->tray()) { event->ignore(); hide(); } diff --git a/src/MxcImageProvider.cpp b/src/MxcImageProvider.cpp
index 8e67375d..a197e4aa 100644 --- a/src/MxcImageProvider.cpp +++ b/src/MxcImageProvider.cpp
@@ -17,7 +17,8 @@ MxcImageResponse::run() auto data = cache::image(fileName); if (!data.isNull()) { m_image = utils::readImage(&data); - m_image = m_image.scaled(m_requestedSize, Qt::KeepAspectRatio); + m_image = m_image.scaled( + m_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); m_image.setText("mxc url", "mxc://" + m_id); if (!m_image.isNull()) { diff --git a/src/RegisterPage.cpp b/src/RegisterPage.cpp
index 03e9ab34..b8fe93b5 100644 --- a/src/RegisterPage.cpp +++ b/src/RegisterPage.cpp
@@ -15,6 +15,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QLabel> #include <QMetaType> #include <QPainter> #include <QStyleOption> @@ -106,10 +107,10 @@ RegisterPage::RegisterPage(QWidget *parent) tr("A server that allows registration. Since matrix is decentralized, you need to first " "find a server you can register on or host your own.")); - form_layout_->addWidget(username_input_, Qt::AlignHCenter, nullptr); - form_layout_->addWidget(password_input_, Qt::AlignHCenter, nullptr); - form_layout_->addWidget(password_confirmation_, Qt::AlignHCenter, nullptr); - form_layout_->addWidget(server_input_, Qt::AlignHCenter, nullptr); + form_layout_->addWidget(username_input_, Qt::AlignHCenter); + form_layout_->addWidget(password_input_, Qt::AlignHCenter); + form_layout_->addWidget(password_confirmation_, Qt::AlignHCenter); + form_layout_->addWidget(server_input_, Qt::AlignHCenter); button_layout_ = new QHBoxLayout(); button_layout_->setSpacing(0); diff --git a/src/RegisterPage.h b/src/RegisterPage.h
index ebc24bb1..59ba3d1d 100644 --- a/src/RegisterPage.h +++ b/src/RegisterPage.h
@@ -17,8 +17,8 @@ #pragma once -#include <QLabel> -#include <QLayout> +#include <QWidget> + #include <memory> #include <mtx/user_interactive.hpp> @@ -26,6 +26,9 @@ class FlatButton; class RaisedButton; class TextField; +class QLabel; +class QVBoxLayout; +class QHBoxLayout; class RegisterPage : public QWidget { diff --git a/src/RoomInfoListItem.cpp b/src/RoomInfoListItem.cpp
index ad774360..f234b59b 100644 --- a/src/RoomInfoListItem.cpp +++ b/src/RoomInfoListItem.cpp
@@ -451,7 +451,7 @@ RoomInfoListItem::calculateImportance() const // returns ImportanceDisabled or Invite if (isInvite()) { return Invite; - } else if (!settings->isSortByImportanceEnabled()) { + } else if (!settings->sortByImportance()) { return ImportanceDisabled; } else if (unreadHighlightedMsgCount_) { return NewMentions; diff --git a/src/RoomList.cpp b/src/RoomList.cpp
index 85a22026..b4c507b5 100644 --- a/src/RoomList.cpp +++ b/src/RoomList.cpp
@@ -21,6 +21,8 @@ #include <QObject> #include <QPainter> #include <QScroller> +#include <QStyle> +#include <QStyleOption> #include <QTimer> #include "Logging.h" diff --git a/src/SideBarActions.cpp b/src/SideBarActions.cpp
index 4934ec05..5af01cc2 100644 --- a/src/SideBarActions.cpp +++ b/src/SideBarActions.cpp
@@ -1,6 +1,8 @@ #include <QIcon> #include <QPainter> #include <QResizeEvent> +#include <QStyle> +#include <QStyleOption> #include <mtx/requests.hpp> diff --git a/src/TopRoomBar.h b/src/TopRoomBar.h
index 1aa5934b..0c33c1e0 100644 --- a/src/TopRoomBar.h +++ b/src/TopRoomBar.h
@@ -27,7 +27,6 @@ class Menu; class TextLabel; class OverlayModal; -class QPainter; class QLabel; class QHBoxLayout; class QVBoxLayout; diff --git a/src/UserInfoWidget.cpp b/src/UserInfoWidget.cpp
index 2e21d41f..e11aa6aa 100644 --- a/src/UserInfoWidget.cpp +++ b/src/UserInfoWidget.cpp
@@ -16,7 +16,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QLabel> #include <QPainter> +#include <QStyle> +#include <QStyleOption> #include <QTimer> #include <iostream> diff --git a/src/UserInfoWidget.h b/src/UserInfoWidget.h
index e1a925a4..575ade52 100644 --- a/src/UserInfoWidget.h +++ b/src/UserInfoWidget.h
@@ -17,13 +17,16 @@ #pragma once -#include <QLabel> -#include <QLayout> +#include <QWidget> class Avatar; class FlatButton; class OverlayModal; +class QLabel; +class QHBoxLayout; +class QVBoxLayout; + class UserInfoWidget : public QWidget { Q_OBJECT diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index 6af08e12..dfd99069 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp
@@ -30,6 +30,7 @@ #include <QScrollArea> #include <QScroller> #include <QSettings> +#include <QSpinBox> #include <QStandardPaths> #include <QString> #include <QTextStream> @@ -51,54 +52,206 @@ void UserSettings::load() { QSettings settings; - isTrayEnabled_ = settings.value("user/window/tray", false).toBool(); - hasDesktopNotifications_ = settings.value("user/desktop_notifications", true).toBool(); - isStartInTrayEnabled_ = settings.value("user/window/start_in_tray", false).toBool(); - isGroupViewEnabled_ = settings.value("user/group_view", true).toBool(); - isButtonsInTimelineEnabled_ = settings.value("user/timeline/buttons", true).toBool(); - isMessageHoverHighlightEnabled_ = + tray_ = settings.value("user/window/tray", false).toBool(); + hasDesktopNotifications_ = settings.value("user/desktop_notifications", true).toBool(); + startInTray_ = settings.value("user/window/start_in_tray", false).toBool(); + groupView_ = settings.value("user/group_view", true).toBool(); + buttonsInTimeline_ = settings.value("user/timeline/buttons", true).toBool(); + timelineMaxWidth_ = settings.value("user/timeline/max_width", 0).toInt(); + messageHoverHighlight_ = settings.value("user/timeline/message_hover_highlight", false).toBool(); - isMarkdownEnabled_ = settings.value("user/markdown_enabled", true).toBool(); - isTypingNotificationsEnabled_ = settings.value("user/typing_notifications", true).toBool(); - sortByImportance_ = settings.value("user/sort_by_unread", true).toBool(); - isReadReceiptsEnabled_ = settings.value("user/read_receipts", true).toBool(); - theme_ = settings.value("user/theme", defaultTheme_).toString(); - font_ = settings.value("user/font_family", "default").toString(); - avatarCircles_ = settings.value("user/avatar_circles", true).toBool(); - decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool(); - emojiFont_ = settings.value("user/emoji_font_family", "default").toString(); - baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble(); + enlargeEmojiOnlyMessages_ = + settings.value("user/timeline/enlarge_emoji_only_msg", false).toBool(); + markdown_ = settings.value("user/markdown_enabled", true).toBool(); + typingNotifications_ = settings.value("user/typing_notifications", true).toBool(); + sortByImportance_ = settings.value("user/sort_by_unread", true).toBool(); + readReceipts_ = settings.value("user/read_receipts", true).toBool(); + theme_ = settings.value("user/theme", defaultTheme_).toString(); + font_ = settings.value("user/font_family", "default").toString(); + avatarCircles_ = settings.value("user/avatar_circles", true).toBool(); + decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool(); + emojiFont_ = settings.value("user/emoji_font_family", "default").toString(); + baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble(); applyTheme(); } +void +UserSettings::setMessageHoverHighlight(bool state) +{ + if (state == messageHoverHighlight_) + return; + messageHoverHighlight_ = state; + emit messageHoverHighlightChanged(state); + save(); +} +void +UserSettings::setEnlargeEmojiOnlyMessages(bool state) +{ + if (state == enlargeEmojiOnlyMessages_) + return; + enlargeEmojiOnlyMessages_ = state; + emit enlargeEmojiOnlyMessagesChanged(state); + save(); +} +void +UserSettings::setTray(bool state) +{ + if (state == tray_) + return; + tray_ = state; + emit trayChanged(state); + save(); +} + +void +UserSettings::setStartInTray(bool state) +{ + if (state == startInTray_) + return; + startInTray_ = state; + emit startInTrayChanged(state); + save(); +} + +void +UserSettings::setGroupView(bool state) +{ + if (groupView_ != state) + emit groupViewStateChanged(state); + + groupView_ = state; + save(); +} + +void +UserSettings::setMarkdown(bool state) +{ + if (state == markdown_) + return; + markdown_ = state; + emit markdownChanged(state); + save(); +} + +void +UserSettings::setReadReceipts(bool state) +{ + if (state == readReceipts_) + return; + readReceipts_ = state; + emit readReceiptsChanged(state); + save(); +} + +void +UserSettings::setTypingNotifications(bool state) +{ + if (state == typingNotifications_) + return; + typingNotifications_ = state; + emit typingNotificationsChanged(state); + save(); +} + +void +UserSettings::setSortByImportance(bool state) +{ + if (state == sortByImportance_) + return; + sortByImportance_ = state; + emit roomSortingChanged(state); + save(); +} + +void +UserSettings::setButtonsInTimeline(bool state) +{ + if (state == buttonsInTimeline_) + return; + buttonsInTimeline_ = state; + emit buttonInTimelineChanged(state); + save(); +} + +void +UserSettings::setTimelineMaxWidth(int state) +{ + if (state == timelineMaxWidth_) + return; + timelineMaxWidth_ = state; + emit timelineMaxWidthChanged(state); + save(); +} + +void +UserSettings::setDesktopNotifications(bool state) +{ + if (state == hasDesktopNotifications_) + return; + hasDesktopNotifications_ = state; + emit desktopNotificationsChanged(state); + save(); +} + +void +UserSettings::setAvatarCircles(bool state) +{ + if (state == avatarCircles_) + return; + avatarCircles_ = state; + emit avatarCirclesChanged(state); + save(); +} + +void +UserSettings::setDecryptSidebar(bool state) +{ + if (state == decryptSidebar_) + return; + decryptSidebar_ = state; + emit decryptSidebarChanged(state); + save(); +} void UserSettings::setFontSize(double size) { + if (size == baseFontSize_) + return; baseFontSize_ = size; + emit fontSizeChanged(size); save(); } void UserSettings::setFontFamily(QString family) { + if (family == font_) + return; font_ = family; + emit fontChanged(family); save(); } void UserSettings::setEmojiFontFamily(QString family) { + if (family == emojiFont_) + return; emojiFont_ = family; + emit emojiFontChanged(family); save(); } void UserSettings::setTheme(QString theme) { + if (theme == theme) + return; theme_ = theme; save(); applyTheme(); + emit themeChanged(theme); } void @@ -161,29 +314,33 @@ UserSettings::save() settings.beginGroup("user"); settings.beginGroup("window"); - settings.setValue("tray", isTrayEnabled_); - settings.setValue("start_in_tray", isStartInTrayEnabled_); + settings.setValue("tray", tray_); + settings.setValue("start_in_tray", startInTray_); settings.endGroup(); settings.beginGroup("timeline"); - settings.setValue("buttons", isButtonsInTimelineEnabled_); - settings.setValue("message_hover_highlight", isMessageHoverHighlightEnabled_); + settings.setValue("buttons", buttonsInTimeline_); + settings.setValue("message_hover_highlight", messageHoverHighlight_); + settings.setValue("enlarge_emoji_only_msg", enlargeEmojiOnlyMessages_); + settings.setValue("max_width", timelineMaxWidth_); settings.endGroup(); settings.setValue("avatar_circles", avatarCircles_); settings.setValue("decrypt_sidebar", decryptSidebar_); settings.setValue("font_size", baseFontSize_); - settings.setValue("typing_notifications", isTypingNotificationsEnabled_); + settings.setValue("typing_notifications", typingNotifications_); settings.setValue("minor_events", sortByImportance_); - settings.setValue("read_receipts", isReadReceiptsEnabled_); - settings.setValue("group_view", isGroupViewEnabled_); - settings.setValue("markdown_enabled", isMarkdownEnabled_); + settings.setValue("read_receipts", readReceipts_); + settings.setValue("group_view", groupView_); + settings.setValue("markdown_enabled", markdown_); settings.setValue("desktop_notifications", hasDesktopNotifications_); settings.setValue("theme", theme()); settings.setValue("font_family", font_); settings.setValue("emoji_font_family", emojiFont_); settings.endGroup(); + + settings.sync(); } HorizontalLine::HorizontalLine(QWidget *parent) @@ -231,24 +388,26 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge general_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); general_->setFont(font); - trayToggle_ = new Toggle{this}; - startInTrayToggle_ = new Toggle{this}; - avatarCircles_ = new Toggle{this}; - decryptSidebar_ = new Toggle(this); - groupViewToggle_ = new Toggle{this}; - timelineButtonsToggle_ = new Toggle{this}; - typingNotifications_ = new Toggle{this}; - messageHoverHighlight_ = new Toggle{this}; - sortByImportance_ = new Toggle{this}; - readReceipts_ = new Toggle{this}; - markdownEnabled_ = new Toggle{this}; - desktopNotifications_ = new Toggle{this}; - scaleFactorCombo_ = new QComboBox{this}; - fontSizeCombo_ = new QComboBox{this}; - fontSelectionCombo_ = new QComboBox{this}; - emojiFontSelectionCombo_ = new QComboBox{this}; + trayToggle_ = new Toggle{this}; + startInTrayToggle_ = new Toggle{this}; + avatarCircles_ = new Toggle{this}; + decryptSidebar_ = new Toggle(this); + groupViewToggle_ = new Toggle{this}; + timelineButtonsToggle_ = new Toggle{this}; + typingNotifications_ = new Toggle{this}; + messageHoverHighlight_ = new Toggle{this}; + enlargeEmojiOnlyMessages_ = new Toggle{this}; + sortByImportance_ = new Toggle{this}; + readReceipts_ = new Toggle{this}; + markdown_ = new Toggle{this}; + desktopNotifications_ = new Toggle{this}; + scaleFactorCombo_ = new QComboBox{this}; + fontSizeCombo_ = new QComboBox{this}; + fontSelectionCombo_ = new QComboBox{this}; + emojiFontSelectionCombo_ = new QComboBox{this}; + timelineMaxWidthSpin_ = new QSpinBox{this}; - if (!settings_->isTrayEnabled()) + if (!settings_->tray()) startInTrayToggle_->setDisabled(true); avatarCircles_->setFixedSize(64, 48); @@ -291,6 +450,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge int themeIndex = themeCombo_->findText(themeStr); themeCombo_->setCurrentIndex(themeIndex); + timelineMaxWidthSpin_->setMinimum(0); + timelineMaxWidthSpin_->setMaximum(100'000'000); + timelineMaxWidthSpin_->setSingleStep(10); + auto encryptionLabel_ = new QLabel{tr("ENCRYPTION"), this}; encryptionLabel_->setFixedHeight(encryptionLabel_->minimumHeight() + LayoutTopMargin); encryptionLabel_->setAlignment(Qt::AlignBottom); @@ -323,11 +486,15 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge sessionKeysLayout->addWidget(sessionKeysExportBtn, 0, Qt::AlignRight); sessionKeysLayout->addWidget(sessionKeysImportBtn, 0, Qt::AlignRight); - auto boxWrap = [this, &font](QString labelText, QWidget *field) { + auto boxWrap = [this, &font](QString labelText, QWidget *field, QString tooltipText = "") { auto label = new QLabel{labelText, this}; label->setFont(font); label->setMargin(OptionMargin); + if (!tooltipText.isEmpty()) { + label->setToolTip(tooltipText); + } + auto layout = new QHBoxLayout; layout->addWidget(field, 0, Qt::AlignRight); @@ -336,25 +503,70 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge formLayout_->addRow(general_); formLayout_->addRow(new HorizontalLine{this}); - boxWrap(tr("Minimize to tray"), trayToggle_); - boxWrap(tr("Start in tray"), startInTrayToggle_); + boxWrap( + tr("Minimize to tray"), + trayToggle_, + tr("Keep the application running in the background after closing the client window.")); + boxWrap(tr("Start in tray"), + startInTrayToggle_, + tr("Start the application in the background without showing the client window.")); formLayout_->addRow(new HorizontalLine{this}); - boxWrap(tr("Circular Avatars"), avatarCircles_); - boxWrap(tr("Group's sidebar"), groupViewToggle_); - boxWrap(tr("Decrypt messages in sidebar"), decryptSidebar_); - boxWrap(tr("Show buttons in timeline"), timelineButtonsToggle_); - boxWrap(tr("Typing notifications"), typingNotifications_); - boxWrap(tr("Sort rooms by unreads"), sortByImportance_); + boxWrap(tr("Circular Avatars"), + avatarCircles_, + tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle.")); + boxWrap(tr("Group's sidebar"), + groupViewToggle_, + tr("Show a column containing groups and tags next to the room list.")); + boxWrap(tr("Decrypt messages in sidebar"), + decryptSidebar_, + tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in " + "encrypted chats.")); + boxWrap(tr("Show buttons in timeline"), + timelineButtonsToggle_, + tr("Show buttons to quickly reply, react or access additional options next to each " + "message.")); + boxWrap(tr("Limit width of timeline"), + timelineMaxWidthSpin_, + tr("Set the max width of messages in the timeline (in pixels). This can help " + "readability on wide screen, when Nheko is maximised")); + boxWrap(tr("Typing notifications"), + typingNotifications_, + tr("Show who is typing in a room.\nThis will also enable or disable sending typing " + "notifications to others.")); + boxWrap( + tr("Sort rooms by unreads"), + sortByImportance_, + tr( + "Display rooms with new messages first.\nIf this is off, the list of rooms will only " + "be sorted by the timestamp of the last message in a room.\nIf this is on, rooms which " + "have active notifications (the small circle with a number in it) will be sorted on " + "top. Rooms, that you have muted, will still be sorted by timestamp, since you don't " + "seem to consider them as important as the other rooms.")); formLayout_->addRow(new HorizontalLine{this}); - boxWrap(tr("Read receipts"), readReceipts_); - boxWrap(tr("Send messages as Markdown"), markdownEnabled_); - boxWrap(tr("Desktop notifications"), desktopNotifications_); - boxWrap(tr("Highlight message on hover"), messageHoverHighlight_); + boxWrap(tr("Read receipts"), + readReceipts_, + tr("Show if your message was read.\nStatus is displayed next to timestamps.")); + boxWrap( + tr("Send messages as Markdown"), + markdown_, + tr("Allow using markdown in messages.\nWhen disabled, all messages are sent as a plain " + "text.")); + boxWrap(tr("Desktop notifications"), + desktopNotifications_, + tr("Notify about received message when the client is not currently focused.")); + boxWrap(tr("Highlight message on hover"), + messageHoverHighlight_, + tr("Change the background color of messages when you hover over them.")); + boxWrap(tr("Large Emoji in timeline"), + enlargeEmojiOnlyMessages_, + tr("Make font size larger if messages with only a few emojis are displayed.")); formLayout_->addRow(uiLabel_); formLayout_->addRow(new HorizontalLine{this}); #if !defined(Q_OS_MAC) - boxWrap(tr("Scale factor"), scaleFactorCombo_); + boxWrap(tr("Scale factor"), + scaleFactorCombo_, + tr("Change the scale factor of the whole user interface.")); #else scaleFactorCombo_->hide(); #endif @@ -400,78 +612,87 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge topLayout_->addWidget(versionInfo); connect(themeCombo_, - static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated), + static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), [this](const QString &text) { settings_->setTheme(text.toLower()); emit themeChanged(); }); connect(scaleFactorCombo_, - static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated), + static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), [](const QString &factor) { utils::setScaleFactor(factor.toFloat()); }); connect(fontSizeCombo_, - static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated), + static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), [this](const QString &size) { settings_->setFontSize(size.trimmed().toDouble()); }); connect(fontSelectionCombo_, - static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated), + static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), [this](const QString &family) { settings_->setFontFamily(family.trimmed()); }); connect(emojiFontSelectionCombo_, - static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated), + static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), [this](const QString &family) { settings_->setEmojiFontFamily(family.trimmed()); }); - connect(trayToggle_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setTray(!isDisabled); - if (isDisabled) { + connect(trayToggle_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setTray(!disabled); + if (disabled) { startInTrayToggle_->setDisabled(true); } else { startInTrayToggle_->setEnabled(true); } - emit trayOptionChanged(!isDisabled); + emit trayOptionChanged(!disabled); }); - connect(startInTrayToggle_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setStartInTray(!isDisabled); + connect(startInTrayToggle_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setStartInTray(!disabled); }); - connect(groupViewToggle_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setGroupView(!isDisabled); + connect(groupViewToggle_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setGroupView(!disabled); }); - connect(decryptSidebar_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setDecryptSidebar(!isDisabled); + connect(decryptSidebar_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setDecryptSidebar(!disabled); emit decryptSidebarChanged(); }); - connect(avatarCircles_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setAvatarCircles(!isDisabled); + connect(avatarCircles_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setAvatarCircles(!disabled); }); - connect(markdownEnabled_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setMarkdownEnabled(!isDisabled); + connect(markdown_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setMarkdown(!disabled); }); - connect(typingNotifications_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setTypingNotifications(!isDisabled); + connect(typingNotifications_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setTypingNotifications(!disabled); }); - connect(sortByImportance_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setSortByImportance(!isDisabled); + connect(sortByImportance_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setSortByImportance(!disabled); }); - connect(timelineButtonsToggle_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setButtonsInTimeline(!isDisabled); + connect(timelineButtonsToggle_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setButtonsInTimeline(!disabled); }); - connect(readReceipts_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setReadReceipts(!isDisabled); + connect(readReceipts_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setReadReceipts(!disabled); }); - connect(desktopNotifications_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setDesktopNotifications(!isDisabled); + connect(desktopNotifications_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setDesktopNotifications(!disabled); }); - connect(messageHoverHighlight_, &Toggle::toggled, this, [this](bool isDisabled) { - settings_->setMessageHoverHighlight(!isDisabled); + connect(messageHoverHighlight_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setMessageHoverHighlight(!disabled); }); + connect(enlargeEmojiOnlyMessages_, &Toggle::toggled, this, [this](bool disabled) { + settings_->setEnlargeEmojiOnlyMessages(!disabled); + }); + + connect(timelineMaxWidthSpin_, + qOverload<int>(&QSpinBox::valueChanged), + this, + [this](int newValue) { settings_->setTimelineMaxWidth(newValue); }); + connect( sessionKeysImportBtn, &QPushButton::clicked, this, &UserSettingsPage::importSessionKeys); @@ -493,19 +714,21 @@ UserSettingsPage::showEvent(QShowEvent *) utils::restoreCombobox(themeCombo_, settings_->theme()); // FIXME: Toggle treats true as "off" - trayToggle_->setState(!settings_->isTrayEnabled()); - startInTrayToggle_->setState(!settings_->isStartInTrayEnabled()); - groupViewToggle_->setState(!settings_->isGroupViewEnabled()); - decryptSidebar_->setState(!settings_->isDecryptSidebarEnabled()); - avatarCircles_->setState(!settings_->isAvatarCirclesEnabled()); - typingNotifications_->setState(!settings_->isTypingNotificationsEnabled()); - sortByImportance_->setState(!settings_->isSortByImportanceEnabled()); - timelineButtonsToggle_->setState(!settings_->isButtonsInTimelineEnabled()); - readReceipts_->setState(!settings_->isReadReceiptsEnabled()); - markdownEnabled_->setState(!settings_->isMarkdownEnabled()); + trayToggle_->setState(!settings_->tray()); + startInTrayToggle_->setState(!settings_->startInTray()); + groupViewToggle_->setState(!settings_->groupView()); + decryptSidebar_->setState(!settings_->decryptSidebar()); + avatarCircles_->setState(!settings_->avatarCircles()); + typingNotifications_->setState(!settings_->typingNotifications()); + sortByImportance_->setState(!settings_->sortByImportance()); + timelineButtonsToggle_->setState(!settings_->buttonsInTimeline()); + readReceipts_->setState(!settings_->readReceipts()); + markdown_->setState(!settings_->markdown()); desktopNotifications_->setState(!settings_->hasDesktopNotifications()); - messageHoverHighlight_->setState(!settings_->isMessageHoverHighlightEnabled()); + messageHoverHighlight_->setState(!settings_->messageHoverHighlight()); + enlargeEmojiOnlyMessages_->setState(!settings_->enlargeEmojiOnlyMessages()); deviceIdValue_->setText(QString::fromStdString(http::client()->device_id())); + timelineMaxWidthSpin_->setValue(settings_->timelineMaxWidth()); deviceFingerprintValue_->setText( utils::humanReadableFingerprint(olm::client()->identity_keys().ed25519)); diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index 088bbfb5..fb807067 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h
@@ -17,17 +17,19 @@ #pragma once -#include <QComboBox> #include <QFontDatabase> -#include <QFormLayout> #include <QFrame> -#include <QLabel> -#include <QLayout> #include <QProcessEnvironment> #include <QSharedPointer> #include <QWidget> class Toggle; +class QLabel; +class QFormLayout; +class QComboBox; +class QSpinBox; +class QHBoxLayout; +class QVBoxLayout; constexpr int OptionMargin = 6; constexpr int LayoutTopMargin = 50; @@ -37,6 +39,36 @@ class UserSettings : public QObject { Q_OBJECT + Q_PROPERTY(QString theme READ theme WRITE setTheme NOTIFY themeChanged) + Q_PROPERTY(bool messageHoverHighlight READ messageHoverHighlight WRITE + setMessageHoverHighlight NOTIFY messageHoverHighlightChanged) + Q_PROPERTY(bool enlargeEmojiOnlyMessages READ enlargeEmojiOnlyMessages WRITE + setEnlargeEmojiOnlyMessages NOTIFY enlargeEmojiOnlyMessagesChanged) + Q_PROPERTY(bool tray READ tray WRITE setTray NOTIFY trayChanged) + Q_PROPERTY(bool startInTray READ startInTray WRITE setStartInTray NOTIFY startInTrayChanged) + Q_PROPERTY(bool groupView READ groupView WRITE setGroupView NOTIFY groupViewStateChanged) + Q_PROPERTY(bool markdown READ markdown WRITE setMarkdown NOTIFY markdownChanged) + Q_PROPERTY(bool typingNotifications READ typingNotifications WRITE setTypingNotifications + NOTIFY typingNotificationsChanged) + Q_PROPERTY(bool sortByImportance READ sortByImportance WRITE setSortByImportance NOTIFY + roomSortingChanged) + Q_PROPERTY(bool buttonsInTimeline READ buttonsInTimeline WRITE setButtonsInTimeline NOTIFY + buttonInTimelineChanged) + Q_PROPERTY( + bool readReceipts READ readReceipts WRITE setReadReceipts NOTIFY readReceiptsChanged) + Q_PROPERTY(bool desktopNotifications READ hasDesktopNotifications WRITE + setDesktopNotifications NOTIFY desktopNotificationsChanged) + Q_PROPERTY( + bool avatarCircles READ avatarCircles WRITE setAvatarCircles NOTIFY avatarCirclesChanged) + Q_PROPERTY(bool decryptSidebar READ decryptSidebar WRITE setDecryptSidebar NOTIFY + decryptSidebarChanged) + Q_PROPERTY(int timelineMaxWidth READ timelineMaxWidth WRITE setTimelineMaxWidth NOTIFY + timelineMaxWidthChanged) + Q_PROPERTY(double fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged) + Q_PROPERTY(QString font READ font WRITE setFontFamily NOTIFY fontChanged) + Q_PROPERTY( + QString emojiFont READ emojiFont WRITE setEmojiFontFamily NOTIFY emojiFontChanged) + public: UserSettings(); @@ -44,104 +76,62 @@ public: void load(); void applyTheme(); void setTheme(QString theme); - void setMessageHoverHighlight(bool state) - { - isMessageHoverHighlightEnabled_ = state; - save(); - } - void setTray(bool state) - { - isTrayEnabled_ = state; - save(); - } - - void setStartInTray(bool state) - { - isStartInTrayEnabled_ = state; - save(); - } - + void setMessageHoverHighlight(bool state); + void setEnlargeEmojiOnlyMessages(bool state); + void setTray(bool state); + void setStartInTray(bool state); void setFontSize(double size); void setFontFamily(QString family); void setEmojiFontFamily(QString family); - - void setGroupView(bool state) - { - if (isGroupViewEnabled_ != state) - emit groupViewStateChanged(state); - - isGroupViewEnabled_ = state; - save(); - } - - void setMarkdownEnabled(bool state) - { - isMarkdownEnabled_ = state; - save(); - } - - void setReadReceipts(bool state) - { - isReadReceiptsEnabled_ = state; - save(); - } - - void setTypingNotifications(bool state) - { - isTypingNotificationsEnabled_ = state; - save(); - } - - void setSortByImportance(bool state) - { - sortByImportance_ = state; - emit roomSortingChanged(); - } - - void setButtonsInTimeline(bool state) - { - isButtonsInTimelineEnabled_ = state; - save(); - } - - void setDesktopNotifications(bool state) - { - hasDesktopNotifications_ = state; - save(); - } - - void setAvatarCircles(bool state) - { - avatarCircles_ = state; - save(); - } - - void setDecryptSidebar(bool state) - { - decryptSidebar_ = state; - save(); - } + void setGroupView(bool state); + void setMarkdown(bool state); + void setReadReceipts(bool state); + void setTypingNotifications(bool state); + void setSortByImportance(bool state); + void setButtonsInTimeline(bool state); + void setTimelineMaxWidth(int state); + void setDesktopNotifications(bool state); + void setAvatarCircles(bool state); + void setDecryptSidebar(bool state); QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; } - bool isMessageHoverHighlightEnabled() const { return isMessageHoverHighlightEnabled_; } - bool isTrayEnabled() const { return isTrayEnabled_; } - bool isStartInTrayEnabled() const { return isStartInTrayEnabled_; } - bool isGroupViewEnabled() const { return isGroupViewEnabled_; } - bool isAvatarCirclesEnabled() const { return avatarCircles_; } - bool isDecryptSidebarEnabled() const { return decryptSidebar_; } - bool isMarkdownEnabled() const { return isMarkdownEnabled_; } - bool isTypingNotificationsEnabled() const { return isTypingNotificationsEnabled_; } - bool isSortByImportanceEnabled() const { return sortByImportance_; } - bool isButtonsInTimelineEnabled() const { return isButtonsInTimelineEnabled_; } - bool isReadReceiptsEnabled() const { return isReadReceiptsEnabled_; } + bool messageHoverHighlight() const { return messageHoverHighlight_; } + bool enlargeEmojiOnlyMessages() const { return enlargeEmojiOnlyMessages_; } + bool tray() const { return tray_; } + bool startInTray() const { return startInTray_; } + bool groupView() const { return groupView_; } + bool avatarCircles() const { return avatarCircles_; } + bool decryptSidebar() const { return decryptSidebar_; } + bool markdown() const { return markdown_; } + bool typingNotifications() const { return typingNotifications_; } + bool sortByImportance() const { return sortByImportance_; } + bool buttonsInTimeline() const { return buttonsInTimeline_; } + bool readReceipts() const { return readReceipts_; } bool hasDesktopNotifications() const { return hasDesktopNotifications_; } + int timelineMaxWidth() const { return timelineMaxWidth_; } double fontSize() const { return baseFontSize_; } QString font() const { return font_; } QString emojiFont() const { return emojiFont_; } signals: void groupViewStateChanged(bool state); - void roomSortingChanged(); + void roomSortingChanged(bool state); + void themeChanged(QString state); + void messageHoverHighlightChanged(bool state); + void enlargeEmojiOnlyMessagesChanged(bool state); + void trayChanged(bool state); + void startInTrayChanged(bool state); + void markdownChanged(bool state); + void typingNotificationsChanged(bool state); + void buttonInTimelineChanged(bool state); + void readReceiptsChanged(bool state); + void desktopNotificationsChanged(bool state); + void avatarCirclesChanged(bool state); + void decryptSidebarChanged(bool state); + void timelineMaxWidthChanged(int state); + void fontSizeChanged(double state); + void fontChanged(QString state); + void emojiFontChanged(QString state); private: // Default to system theme if QT_QPA_PLATFORMTHEME var is set. @@ -150,18 +140,20 @@ private: ? "light" : "system"; QString theme_; - bool isMessageHoverHighlightEnabled_; - bool isTrayEnabled_; - bool isStartInTrayEnabled_; - bool isGroupViewEnabled_; - bool isMarkdownEnabled_; - bool isTypingNotificationsEnabled_; + bool messageHoverHighlight_; + bool enlargeEmojiOnlyMessages_; + bool tray_; + bool startInTray_; + bool groupView_; + bool markdown_; + bool typingNotifications_; bool sortByImportance_; - bool isButtonsInTimelineEnabled_; - bool isReadReceiptsEnabled_; + bool buttonsInTimeline_; + bool readReceipts_; bool hasDesktopNotifications_; bool avatarCircles_; bool decryptSidebar_; + int timelineMaxWidth_; double baseFontSize_; QString font_; QString emojiFont_; @@ -211,9 +203,10 @@ private: Toggle *timelineButtonsToggle_; Toggle *typingNotifications_; Toggle *messageHoverHighlight_; + Toggle *enlargeEmojiOnlyMessages_; Toggle *sortByImportance_; Toggle *readReceipts_; - Toggle *markdownEnabled_; + Toggle *markdown_; Toggle *desktopNotifications_; Toggle *avatarCircles_; Toggle *decryptSidebar_; @@ -226,5 +219,7 @@ private: QComboBox *fontSelectionCombo_; QComboBox *emojiFontSelectionCombo_; + QSpinBox *timelineMaxWidthSpin_; + int sideMargin_ = 0; }; diff --git a/src/Utils.cpp b/src/Utils.cpp
index 7f11a8cd..26ea124c 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp
@@ -51,6 +51,14 @@ utils::localUser() return QString::fromStdString(http::client()->user_id().to_string()); } +bool +utils::codepointIsEmoji(uint code) +{ + // TODO: Be more precise here. + return (code >= 0x2600 && code <= 0x27bf) || (code >= 0x1f300 && code <= 0x1f3ff) || + (code >= 0x1f000 && code <= 0x1faff); +} + QString utils::replaceEmoji(const QString &body) { @@ -63,9 +71,7 @@ utils::replaceEmoji(const QString &body) bool insideFontBlock = false; for (auto &code : utf32_string) { - // TODO: Be more precise here. - if ((code >= 0x2600 && code <= 0x27bf) || (code >= 0x1f300 && code <= 0x1f3ff) || - (code >= 0x1f000 && code <= 0x1faff)) { + if (utils::codepointIsEmoji(code)) { if (!insideFontBlock) { fmtBody += QString("<font face=\"" + userFontFamily + "\">"); insideFontBlock = true; @@ -136,13 +142,13 @@ utils::descriptiveTime(const QDateTime &then) const auto days = then.daysTo(now); if (days == 0) - return then.time().toString(Qt::DefaultLocaleShortDate); + return QLocale::system().toString(then.time(), QLocale::ShortFormat); else if (days < 2) return QString(QCoreApplication::translate("descriptiveTime", "Yesterday")); else if (days < 7) return then.toString("dddd"); - return then.date().toString(Qt::DefaultLocaleShortDate); + return QLocale::system().toString(then.date(), QLocale::ShortFormat); } DescInfo diff --git a/src/Utils.h b/src/Utils.h
index d3f66246..07a4a648 100644 --- a/src/Utils.h +++ b/src/Utils.h
@@ -36,6 +36,9 @@ namespace utils { using TimelineEvent = mtx::events::collections::TimelineEvents; +bool +codepointIsEmoji(uint code); + QString replaceEmoji(const QString &body); @@ -183,42 +186,6 @@ erase_if(ContainerT &items, const PredicateT &predicate) } } -inline uint64_t -event_timestamp(const mtx::events::collections::TimelineEvents &event) -{ - return std::visit([](auto msg) { return msg.origin_server_ts; }, event); -} - -inline nlohmann::json -serialize_event(const mtx::events::collections::TimelineEvents &event) -{ - return std::visit([](auto msg) { return json(msg); }, event); -} - -inline mtx::events::EventType -event_type(const mtx::events::collections::TimelineEvents &event) -{ - return std::visit([](auto msg) { return msg.type; }, event); -} - -inline std::string -event_id(const mtx::events::collections::TimelineEvents &event) -{ - return std::visit([](auto msg) { return msg.event_id; }, event); -} - -inline QString -eventId(const mtx::events::collections::TimelineEvents &event) -{ - return QString::fromStdString(event_id(event)); -} - -inline QString -event_sender(const mtx::events::collections::TimelineEvents &event) -{ - return std::visit([](auto msg) { return QString::fromStdString(msg.sender); }, event); -} - template<class T> QString message_body(const mtx::events::collections::TimelineEvents &event) diff --git a/src/dialogs/CreateRoom.cpp b/src/dialogs/CreateRoom.cpp
index 06676d3d..be5b4638 100644 --- a/src/dialogs/CreateRoom.cpp +++ b/src/dialogs/CreateRoom.cpp
@@ -112,7 +112,7 @@ CreateRoom::CreateRoom(QWidget *parent) }); connect(visibilityCombo_, - static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated), + static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), [this](const QString &text) { if (text == "Private") { request_.visibility = mtx::requests::Visibility::Private; @@ -122,7 +122,7 @@ CreateRoom::CreateRoom(QWidget *parent) }); connect(presetCombo_, - static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated), + static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), [this](const QString &text) { if (text == "Private Chat") { request_.preset = mtx::requests::Preset::PrivateChat; diff --git a/src/dialogs/InviteUsers.cpp b/src/dialogs/InviteUsers.cpp
index 691035ce..f85adb8f 100644 --- a/src/dialogs/InviteUsers.cpp +++ b/src/dialogs/InviteUsers.cpp
@@ -1,5 +1,6 @@ #include <QDebug> #include <QIcon> +#include <QLabel> #include <QListWidget> #include <QListWidgetItem> #include <QPushButton> diff --git a/src/dialogs/InviteUsers.h b/src/dialogs/InviteUsers.h
index 952c97a5..684f60b4 100644 --- a/src/dialogs/InviteUsers.h +++ b/src/dialogs/InviteUsers.h
@@ -1,13 +1,13 @@ #pragma once #include <QFrame> -#include <QLabel> -#include <QListWidgetItem> #include <QStringList> class QPushButton; +class QLabel; class TextField; class QListWidget; +class QListWidgetItem; namespace dialogs { diff --git a/src/dialogs/ReadReceipts.cpp b/src/dialogs/ReadReceipts.cpp
index 0edd1ebf..7dcffc28 100644 --- a/src/dialogs/ReadReceipts.cpp +++ b/src/dialogs/ReadReceipts.cpp
@@ -1,5 +1,6 @@ #include <QDebug> #include <QIcon> +#include <QLabel> #include <QListWidgetItem> #include <QPainter> #include <QPushButton> @@ -74,15 +75,17 @@ ReceiptItem::dateFormat(const QDateTime &then) const auto days = then.daysTo(now); if (days == 0) - return tr("Today %1").arg(then.time().toString(Qt::DefaultLocaleShortDate)); + return tr("Today %1") + .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat)); else if (days < 2) - return tr("Yesterday %1").arg(then.time().toString(Qt::DefaultLocaleShortDate)); + return tr("Yesterday %1") + .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat)); else if (days < 7) return QString("%1 %2") .arg(then.toString("dddd")) - .arg(then.time().toString(Qt::DefaultLocaleShortDate)); + .arg(QLocale::system().toString(then.time(), QLocale::ShortFormat)); - return then.toString(Qt::DefaultLocaleShortDate); + return QLocale::system().toString(then.time(), QLocale::ShortFormat); } ReadReceipts::ReadReceipts(QWidget *parent) @@ -163,3 +166,10 @@ ReadReceipts::paintEvent(QPaintEvent *) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } + +void +ReadReceipts::hideEvent(QHideEvent *event) +{ + userList_->clear(); + QFrame::hideEvent(event); +} diff --git a/src/dialogs/ReadReceipts.h b/src/dialogs/ReadReceipts.h
index e298af0a..2e7a0217 100644 --- a/src/dialogs/ReadReceipts.h +++ b/src/dialogs/ReadReceipts.h
@@ -2,12 +2,12 @@ #include <QDateTime> #include <QFrame> -#include <QHBoxLayout> -#include <QLabel> -#include <QListWidget> -#include <QVBoxLayout> class Avatar; +class QLabel; +class QListWidget; +class QHBoxLayout; +class QVBoxLayout; namespace dialogs { @@ -47,11 +47,7 @@ public slots: protected: void paintEvent(QPaintEvent *event) override; - void hideEvent(QHideEvent *event) override - { - userList_->clear(); - QFrame::hideEvent(event); - } + void hideEvent(QHideEvent *event) override; private: QLabel *topLabel_; diff --git a/src/dialogs/RoomSettings.cpp b/src/dialogs/RoomSettings.cpp
index cc10ac91..26aece32 100644 --- a/src/dialogs/RoomSettings.cpp +++ b/src/dialogs/RoomSettings.cpp
@@ -1,5 +1,6 @@ #include <QApplication> #include <QComboBox> +#include <QEvent> #include <QFileDialog> #include <QFontDatabase> #include <QImageReader> @@ -41,6 +42,17 @@ constexpr int WIDGET_SPACING = 15; constexpr int TEXT_SPACING = 4; constexpr int BUTTON_SPACING = 2 * TEXT_SPACING; +bool +ClickableFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::MouseButtonRelease) { + emit clicked(); + return true; + } + + return QObject::eventFilter(obj, event); +} + EditModal::EditModal(const QString &roomId, QWidget *parent) : QWidget(parent) , roomId_{roomId} @@ -94,6 +106,28 @@ EditModal::EditModal(const QString &roomId, QWidget *parent) } void +EditModal::topicEventSent() +{ + errorField_->hide(); + close(); +} + +void +EditModal::nameEventSent(const QString &name) +{ + errorField_->hide(); + emit nameChanged(name); + close(); +} + +void +EditModal::error(const QString &msg) +{ + errorField_->setText(msg); + errorField_->show(); +} + +void EditModal::applyClicked() { // Check if the values are changed from the originals. diff --git a/src/dialogs/RoomSettings.h b/src/dialogs/RoomSettings.h
index e41c866c..e0918afd 100644 --- a/src/dialogs/RoomSettings.h +++ b/src/dialogs/RoomSettings.h
@@ -1,9 +1,7 @@ #pragma once -#include <QEvent> #include <QFrame> #include <QImage> -#include <QLabel> #include <mtx/events/guest_access.hpp> @@ -21,6 +19,8 @@ class QPixmap; class TextField; class TextField; class Toggle; +class QLabel; +class QEvent; class ClickableFilter : public QObject { @@ -35,15 +35,7 @@ signals: void clicked(); protected: - bool eventFilter(QObject *obj, QEvent *event) override - { - if (event->type() == QEvent::MouseButtonRelease) { - emit clicked(); - return true; - } - - return QObject::eventFilter(obj, event); - } + bool eventFilter(QObject *obj, QEvent *event) override; }; /// Convenience class which connects events emmited from threads @@ -72,24 +64,9 @@ signals: void nameChanged(const QString &roomName); private slots: - void topicEventSent() - { - errorField_->hide(); - close(); - } - - void nameEventSent(const QString &name) - { - errorField_->hide(); - emit nameChanged(name); - close(); - } - - void error(const QString &msg) - { - errorField_->setText(msg); - errorField_->show(); - } + void topicEventSent(); + void nameEventSent(const QString &name); + void error(const QString &msg); void applyClicked(); diff --git a/src/emoji/Category.cpp b/src/emoji/Category.cpp
index 7393b7bc..1e8df082 100644 --- a/src/emoji/Category.cpp +++ b/src/emoji/Category.cpp
@@ -15,9 +15,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QLabel> +#include <QListView> #include <QPainter> #include <QScrollBar> #include <QStyleOption> +#include <QVBoxLayout> #include "Config.h" diff --git a/src/emoji/Category.h b/src/emoji/Category.h
index 2f39d621..79e616ee 100644 --- a/src/emoji/Category.h +++ b/src/emoji/Category.h
@@ -18,13 +18,14 @@ #pragma once #include <QColor> -#include <QLabel> -#include <QLayout> -#include <QListView> -#include <QStandardItemModel> #include "ItemDelegate.h" +class QLabel; +class QListView; +class QStandardItemModel; +class QVBoxLayout; + namespace emoji { class Category : public QWidget diff --git a/src/popups/PopupItem.cpp b/src/popups/PopupItem.cpp
index 5513f942..b3784843 100644 --- a/src/popups/PopupItem.cpp +++ b/src/popups/PopupItem.cpp
@@ -1,3 +1,4 @@ +#include <QLabel> #include <QPaintEvent> #include <QPainter> #include <QStyleOption> diff --git a/src/popups/PopupItem.h b/src/popups/PopupItem.h
index 7a710fdb..17a4f6bc 100644 --- a/src/popups/PopupItem.h +++ b/src/popups/PopupItem.h
@@ -1,8 +1,5 @@ #pragma once -#include <QHBoxLayout> -#include <QLabel> -#include <QPoint> #include <QWidget> #include "../AvatarProvider.h" @@ -10,6 +7,8 @@ class Avatar; struct SearchResult; +class QLabel; +class QHBoxLayout; class PopupItem : public QWidget { diff --git a/src/popups/SuggestionsPopup.h b/src/popups/SuggestionsPopup.h
index 63c44538..73bfe6f7 100644 --- a/src/popups/SuggestionsPopup.h +++ b/src/popups/SuggestionsPopup.h
@@ -1,8 +1,5 @@ #pragma once -#include <QHBoxLayout> -#include <QLabel> -#include <QPoint> #include <QWidget> #include "CacheStructs.h" diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp
index 2e70dbd3..23a679f1 100644 --- a/src/popups/UserMentions.cpp +++ b/src/popups/UserMentions.cpp
@@ -8,9 +8,9 @@ #include "Cache.h" #include "ChatPage.h" +#include "EventAccessors.h" #include "Logging.h" #include "UserMentions.h" -//#include "timeline/TimelineItem.h" using namespace popups; @@ -75,12 +75,15 @@ UserMentions::initializeMentions(const QMap<QString, mtx::responses::Notificatio for (const auto &item : notifs) { for (const auto &notif : item.notifications) { - const auto event_id = QString::fromStdString(utils::event_id(notif.event)); + const auto event_id = + QString::fromStdString(mtx::accessors::event_id(notif.event)); try { const auto room_id = QString::fromStdString(notif.room_id); - const auto user_id = utils::event_sender(notif.event); - const auto body = utils::event_body(notif.event); + const auto user_id = + QString::fromStdString(mtx::accessors::sender(notif.event)); + const auto body = + QString::fromStdString(mtx::accessors::body(notif.event)); pushItem(event_id, user_id, diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 371e9f58..95997eb8 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp
@@ -219,6 +219,7 @@ TimelineModel::roleNames() const {Section, "section"}, {Type, "type"}, {TypeString, "typeString"}, + {IsOnlyEmoji, "isOnlyEmoji"}, {Body, "body"}, {FormattedBody, "formattedBody"}, {UserId, "userId"}, @@ -284,6 +285,22 @@ TimelineModel::data(const QString &id, int role) const return QVariant(toRoomEventType(event)); case TypeString: return QVariant(toRoomEventTypeString(event)); + case IsOnlyEmoji: { + QString qBody = QString::fromStdString(body(event)); + + QVector<uint> utf32_string = qBody.toUcs4(); + int emojiCount = 0; + + for (auto &code : utf32_string) { + if (utils::codepointIsEmoji(code)) { + emojiCount++; + } else { + return QVariant(0); + } + } + + return QVariant(emojiCount); + } case Body: return QVariant(utils::replaceEmoji(QString::fromStdString(body(event)))); case FormattedBody: { @@ -386,6 +403,7 @@ TimelineModel::data(const QString &id, int role) const // m.insert(names[Section], data(id, static_cast<int>(Section))); m.insert(names[Type], data(id, static_cast<int>(Type))); m.insert(names[TypeString], data(id, static_cast<int>(TypeString))); + m.insert(names[IsOnlyEmoji], data(id, static_cast<int>(IsOnlyEmoji))); m.insert(names[Body], data(id, static_cast<int>(Body))); m.insert(names[FormattedBody], data(id, static_cast<int>(FormattedBody))); m.insert(names[UserId], data(id, static_cast<int>(UserId))); @@ -810,7 +828,7 @@ TimelineModel::escapeEmoji(QString str) const void TimelineModel::viewRawMessage(QString id) const { - std::string ev = utils::serialize_event(events.value(id)).dump(4); + std::string ev = mtx::accessors::serialize_event(events.value(id)).dump(4); auto dialog = new dialogs::RawMessage(QString::fromStdString(ev)); Q_UNUSED(dialog); } @@ -824,7 +842,7 @@ TimelineModel::viewDecryptedRawMessage(QString id) const event = decryptEvent(*e).event; } - std::string ev = utils::serialize_event(event).dump(4); + std::string ev = mtx::accessors::serialize_event(event).dump(4); auto dialog = new dialogs::RawMessage(QString::fromStdString(ev)); Q_UNUSED(dialog); } @@ -1849,6 +1867,8 @@ TimelineModel::formatMemberEvent(QString id) rendered = tr("%1 changed their display name.").arg(name); else if (avatarChanged) rendered = tr("%1 changed their avatar.").arg(name); + else + rendered = tr("%1 changed some profile info.").arg(name); // the case of nothing changed but join follows join shouldn't happen, so // just show it as join } else { diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index ea7eaffd..3b01781f 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h
@@ -142,6 +142,7 @@ public: Section, Type, TypeString, + IsOnlyEmoji, Body, FormattedBody, UserId, diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 0375ccc8..bf671cb0 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -21,7 +21,7 @@ Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) void TimelineViewManager::updateEncryptedDescriptions() { - auto decrypt = settings->isDecryptSidebarEnabled(); + auto decrypt = settings->decryptSidebar(); QHash<QString, QSharedPointer<TimelineModel>>::iterator i; for (i = models.begin(); i != models.end(); ++i) { auto ptr = i.value(); @@ -96,6 +96,7 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettin #endif container->setMinimumSize(200, 200); view->rootContext()->setContextProperty("timelineManager", this); + view->rootContext()->setContextProperty("settings", settings.data()); updateColorPalette(); view->engine()->addImageProvider("MxcImage", imgProvider); view->engine()->addImageProvider("colorimage", colorImgProvider); @@ -121,7 +122,7 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms) const auto &room_model = models.value(QString::fromStdString(room_id)); room_model->addEvents(room.timeline); - if (ChatPage::instance()->userSettings()->isTypingNotificationsEnabled()) { + if (ChatPage::instance()->userSettings()->typingNotifications()) { std::vector<QString> typing; typing.reserve(room.ephemeral.typing.size()); for (const auto &user : room.ephemeral.typing) { @@ -141,7 +142,7 @@ TimelineViewManager::addRoom(const QString &room_id) { if (!models.contains(room_id)) { QSharedPointer<TimelineModel> newRoom(new TimelineModel(this, room_id)); - newRoom->setDecryptDescription(settings->isDecryptSidebarEnabled()); + newRoom->setDecryptDescription(settings->decryptSidebar()); connect(newRoom.data(), &TimelineModel::newEncryptedImage, @@ -225,7 +226,7 @@ TimelineViewManager::queueTextMessage(const QString &msg) mtx::events::msg::Text text = {}; text.body = msg.trimmed().toStdString(); - if (settings->isMarkdownEnabled()) { + if (settings->markdown()) { text.formatted_body = utils::markdownToHtml(msg).toStdString(); // Don't send formatted_body, when we don't need to @@ -253,7 +254,7 @@ TimelineViewManager::queueTextMessage(const QString &msg) // NOTE(Nico): rich replies always need a formatted_body! text.format = "org.matrix.custom.html"; - if (settings->isMarkdownEnabled()) + if (settings->markdown()) text.formatted_body = utils::getFormattedQuoteBody(related, utils::markdownToHtml(msg)) .toStdString(); @@ -276,7 +277,7 @@ TimelineViewManager::queueEmoteMessage(const QString &msg) mtx::events::msg::Emote emote; emote.body = msg.trimmed().toStdString(); - if (html != msg.trimmed().toHtmlEscaped() && settings->isMarkdownEnabled()) { + if (html != msg.trimmed().toHtmlEscaped() && settings->markdown()) { emote.formatted_body = html.toStdString(); emote.format = "org.matrix.custom.html"; } diff --git a/src/ui/Avatar.cpp b/src/ui/Avatar.cpp
index cb77d1a8..70ebfcf2 100644 --- a/src/ui/Avatar.cpp +++ b/src/ui/Avatar.cpp
@@ -1,4 +1,5 @@ #include <QPainter> +#include <QPainterPath> #include <QSettings> #include "AvatarProvider.h" diff --git a/src/ui/InfoMessage.cpp b/src/ui/InfoMessage.cpp
index 27bc0a5f..0b69564d 100644 --- a/src/ui/InfoMessage.cpp +++ b/src/ui/InfoMessage.cpp
@@ -4,6 +4,7 @@ #include <QDateTime> #include <QLocale> #include <QPainter> +#include <QPainterPath> #include <QPen> #include <QtGlobal> diff --git a/src/ui/Painter.h b/src/ui/Painter.h
index 4d227a5a..2bb0981b 100644 --- a/src/ui/Painter.h +++ b/src/ui/Painter.h
@@ -3,6 +3,7 @@ #include <QFontMetrics> #include <QPaintDevice> #include <QPainter> +#include <QPainterPath> #include <QtGlobal> class Painter : public QPainter @@ -163,5 +164,5 @@ public: private: Painter &_painter; - QPainter::RenderHints hints_ = 0; + QPainter::RenderHints hints_ = {}; }; diff --git a/third_party/SingleApplication-3.0.19/.gitignore b/third_party/SingleApplication-3.1.3.1/.gitignore
index ad390758..35533fe8 100644 --- a/third_party/SingleApplication-3.0.19/.gitignore +++ b/third_party/SingleApplication-3.1.3.1/.gitignore
@@ -6,4 +6,11 @@ /examples/basic/basic /examples/calculator/calculator /examples/sending_arguments/sending_arguments -CMakeLists.txt.user +/**/CMakeLists.txt.user +/**/CMakeCache.txt +/**/CMakeCache/* +/**/CMakeFiles/* +/**/Makefile +/**/cmake_install.cmake +/**/*_autogen/ +libSingleApplication.a diff --git a/third_party/SingleApplication-3.0.19/CHANGELOG.md b/third_party/SingleApplication-3.1.3.1/CHANGELOG.md
index 36f1e261..3662b0b2 100644 --- a/third_party/SingleApplication-3.0.19/CHANGELOG.md +++ b/third_party/SingleApplication-3.1.3.1/CHANGELOG.md
@@ -1,6 +1,40 @@ Changelog ========= +If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it. + +__3.1.3.1__ +--------- +* CMake build system improvements +* Fixed Clang Tidy warnings + + _Hennadii Chernyshchyk_ + +__3.1.3__ +--------- +* Improved `CMakeLists.txt` + + _Hennadii Chernyshchyk_ + +__3.1.2__ +--------- + +* Fix a crash when exiting an application on Android and iOS + + _Emeric Grange_ + +__3.1.1a__ +---------- + +* Added currentUser() method that returns the user the current instance is running as. + + _Leander Schulten_ + +__3.1.0a__ +---------- + +* Added primaryUser() method that returns the user the primary instance is running as. + __3.0.19__ ---------- diff --git a/third_party/SingleApplication-3.0.19/CMakeLists.txt b/third_party/SingleApplication-3.1.3.1/CMakeLists.txt
index 076d514d..85dba84c 100644 --- a/third_party/SingleApplication-3.0.19/CMakeLists.txt +++ b/third_party/SingleApplication-3.1.3.1/CMakeLists.txt
@@ -1,45 +1,34 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.7.0) -project(SingleApplication) +project(SingleApplication LANGUAGES CXX) -set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) -# SingleApplication base class -set(QAPPLICATION_CLASS QCoreApplication CACHE STRING "Inheritance class for SingleApplication") -set_property(CACHE QAPPLICATION_CLASS PROPERTY STRINGS QApplication QGuiApplication QCoreApplication) - -# Libary target add_library(${PROJECT_NAME} STATIC singleapplication.cpp singleapplication_p.cpp - ) +) +add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) # Find dependencies -find_package(Qt5Network) +find_package(Qt5 COMPONENTS Network REQUIRED) +target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Network) + if(QAPPLICATION_CLASS STREQUAL QApplication) find_package(Qt5 COMPONENTS Widgets REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Widgets) elseif(QAPPLICATION_CLASS STREQUAL QGuiApplication) find_package(Qt5 COMPONENTS Gui REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Gui) else() + set(QAPPLICATION_CLASS QCoreApplication) find_package(Qt5 COMPONENTS Core REQUIRED) -endif() -target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS}) - -# Link dependencies -target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Network) -if(QAPPLICATION_CLASS STREQUAL QApplication) - target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets) -elseif(QAPPLICATION_CLASS STREQUAL QGuiApplication) - target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Gui) -else() - target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core) + target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Core) endif() if(WIN32) target_link_libraries(${PROJECT_NAME} PRIVATE advapi32) endif() +target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) diff --git a/third_party/SingleApplication-3.0.19/LICENSE b/third_party/SingleApplication-3.1.3.1/LICENSE
index 85b2a149..a82e5a68 100644 --- a/third_party/SingleApplication-3.0.19/LICENSE +++ b/third_party/SingleApplication-3.1.3.1/LICENSE
@@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) Itay Grudev 2015 - 2016 +Copyright (c) Itay Grudev 2015 - 2020 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/third_party/SingleApplication-3.0.19/README.md b/third_party/SingleApplication-3.1.3.1/README.md
index 5d609865..3c36b557 100644 --- a/third_party/SingleApplication-3.0.19/README.md +++ b/third_party/SingleApplication-3.1.3.1/README.md
@@ -1,5 +1,6 @@ SingleApplication ================= +[![CI](https://github.com/itay-grudev/SingleApplication/workflows/CI:%20Build%20Test/badge.svg)](https://github.com/itay-grudev/SingleApplication/actions) This is a replacement of the QtSingleApplication for `Qt5`. @@ -15,18 +16,6 @@ class you specify via the `QAPPLICATION_CLASS` macro (`QCoreApplication` is the default). Further usage is similar to the use of the `Q[Core|Gui]Application` classes. -The library sets up a `QLocalServer` and a `QSharedMemory` block. The first -instance of your Application is your Primary Instance. It would check if the -shared memory block exists and if not it will start a `QLocalServer` and listen -for connections. Each subsequent instance of your application would check if the -shared memory block exists and if it does, it will connect to the QLocalServer -to notify the primary instance that a new instance had been started, after which -it would terminate with status code `0`. In the Primary Instance -`SingleApplication` would emit the `instanceStarted()` signal upon detecting -that a new instance had been started. - -The library uses `stdlib` to terminate the program with the `exit()` function. - You can use the library as if you use any other `QCoreApplication` derived class: @@ -43,8 +32,7 @@ int main( int argc, char* argv[] ) ``` To include the library files I would recommend that you add it as a git -submodule to your project and include it's contents with a `.pri` file. Here is -how: +submodule to your project. Here is how: ```bash git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication @@ -66,13 +54,27 @@ Then include the subdirectory in your `CMakeLists.txt` project file. ```cmake set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication") add_subdirectory(src/third-party/singleapplication) +target_link_libraries(${PROJECT_NAME} SingleApplication::SingleApplication) ``` + +The library sets up a `QLocalServer` and a `QSharedMemory` block. The first +instance of your Application is your Primary Instance. It would check if the +shared memory block exists and if not it will start a `QLocalServer` and listen +for connections. Each subsequent instance of your application would check if the +shared memory block exists and if it does, it will connect to the QLocalServer +to notify the primary instance that a new instance had been started, after which +it would terminate with status code `0`. In the Primary Instance +`SingleApplication` would emit the `instanceStarted()` signal upon detecting +that a new instance had been started. + +The library uses `stdlib` to terminate the program with the `exit()` function. + Also don't forget to specify which `QCoreApplication` class your app is using if it is not `QCoreApplication` as in examples above. The `Instance Started` signal ------------------------- +----------------------------- The SingleApplication class implements a `instanceStarted()` signal. You can bind to that signal to raise your application's window when a new instance had @@ -204,6 +206,22 @@ qint64 SingleApplication::primaryPid() Returns the process ID (PID) of the primary instance. +--- + +```cpp +QString SingleApplication::primaryUser() +``` + +Returns the username the primary instance is running as. + +--- + +```cpp +QString SingleApplication::currentUser() +``` + +Returns the username the current instance is running as. + ### Signals ```cpp diff --git a/third_party/SingleApplication-3.1.3.1/SingleApplication b/third_party/SingleApplication-3.1.3.1/SingleApplication new file mode 100644
index 00000000..8ead1a42 --- /dev/null +++ b/third_party/SingleApplication-3.1.3.1/SingleApplication
@@ -0,0 +1 @@ +#include "singleapplication.h" diff --git a/third_party/SingleApplication-3.0.19/Windows.md b/third_party/SingleApplication-3.1.3.1/Windows.md
index 13c52da0..13c52da0 100644 --- a/third_party/SingleApplication-3.0.19/Windows.md +++ b/third_party/SingleApplication-3.1.3.1/Windows.md
diff --git a/third_party/SingleApplication-3.0.19/singleapplication.cpp b/third_party/SingleApplication-3.1.3.1/singleapplication.cpp
index 8ff8747a..9af38804 100644 --- a/third_party/SingleApplication-3.0.19/singleapplication.cpp +++ b/third_party/SingleApplication-3.1.3.1/singleapplication.cpp
@@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) Itay Grudev 2015 - 2018 +// Copyright (c) Itay Grudev 2015 - 2020 // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -85,7 +85,7 @@ SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSeconda } } - InstancesInfo* inst = static_cast<InstancesInfo*>( d->memory->data() ); + auto *inst = static_cast<InstancesInfo*>( d->memory->data() ); QElapsedTimer time; time.start(); @@ -172,7 +172,19 @@ qint64 SingleApplication::primaryPid() return d->primaryPid(); } -bool SingleApplication::sendMessage( QByteArray message, int timeout ) +QString SingleApplication::primaryUser() +{ + Q_D(SingleApplication); + return d->primaryUser(); +} + +QString SingleApplication::currentUser() +{ + Q_D(SingleApplication); + return d->getUsername(); +} + +bool SingleApplication::sendMessage( const QByteArray &message, int timeout ) { Q_D(SingleApplication); diff --git a/third_party/SingleApplication-3.0.19/singleapplication.h b/third_party/SingleApplication-3.1.3.1/singleapplication.h
index cb505971..fd806a3d 100644 --- a/third_party/SingleApplication-3.0.19/singleapplication.h +++ b/third_party/SingleApplication-3.1.3.1/singleapplication.h
@@ -43,7 +43,7 @@ class SingleApplication : public QAPPLICATION_CLASS { Q_OBJECT - typedef QAPPLICATION_CLASS app_t; + using app_t = QAPPLICATION_CLASS; public: /** @@ -86,7 +86,7 @@ public: * @see See the corresponding QAPPLICATION_CLASS constructor for reference */ explicit SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 1000 ); - ~SingleApplication(); + ~SingleApplication() override; /** * @brief Returns if the instance is the primary instance @@ -113,13 +113,25 @@ public: qint64 primaryPid(); /** + * @brief Returns the username of the user running the primary instance + * @returns {QString} + */ + QString primaryUser(); + + /** + * @brief Returns the username of the current user + * @returns {QString} + */ + QString currentUser(); + + /** * @brief Sends a message to the primary instance. Returns true on success. * @param {int} timeout - Timeout for connecting * @returns {bool} * @note sendMessage() will return false if invoked from the primary * instance. */ - bool sendMessage( QByteArray message, int timeout = 100 ); + bool sendMessage( const QByteArray &message, int timeout = 100 ); Q_SIGNALS: void instanceStarted(); diff --git a/third_party/SingleApplication-3.1.3.1/singleapplication.pri b/third_party/SingleApplication-3.1.3.1/singleapplication.pri new file mode 100644
index 00000000..ae81f599 --- /dev/null +++ b/third_party/SingleApplication-3.1.3.1/singleapplication.pri
@@ -0,0 +1,20 @@ +QT += core network +CONFIG += c++11 + +HEADERS += $$PWD/SingleApplication \ + $$PWD/singleapplication.h \ + $$PWD/singleapplication_p.h +SOURCES += $$PWD/singleapplication.cpp \ + $$PWD/singleapplication_p.cpp + +INCLUDEPATH += $$PWD + +win32 { + msvc:LIBS += Advapi32.lib + gcc:LIBS += -ladvapi32 +} + +DISTFILES += \ + $$PWD/README.md \ + $$PWD/CHANGELOG.md \ + $$PWD/Windows.md diff --git a/third_party/SingleApplication-3.0.19/singleapplication_p.cpp b/third_party/SingleApplication-3.1.3.1/singleapplication_p.cpp
index 884fe631..705609f2 100644 --- a/third_party/SingleApplication-3.0.19/singleapplication_p.cpp +++ b/third_party/SingleApplication-3.1.3.1/singleapplication_p.cpp
@@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) Itay Grudev 2015 - 2018 +// Copyright (c) Itay Grudev 2015 - 2020 // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -69,18 +69,52 @@ SingleApplicationPrivate::~SingleApplicationPrivate() delete socket; } - memory->lock(); - InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data()); - if( server != nullptr ) { - server->close(); - delete server; - inst->primary = false; - inst->primaryPid = -1; - inst->checksum = blockChecksum(); + if( memory != nullptr ) { + memory->lock(); + auto *inst = static_cast<InstancesInfo*>(memory->data()); + if( server != nullptr ) { + server->close(); + delete server; + inst->primary = false; + inst->primaryPid = -1; + inst->primaryUser[0] = '\0'; + inst->checksum = blockChecksum(); + } + memory->unlock(); + + delete memory; } - memory->unlock(); +} - delete memory; +QString SingleApplicationPrivate::getUsername() +{ +#ifdef Q_OS_WIN + wchar_t username[UNLEN + 1]; + // Specifies size of the buffer on input + DWORD usernameLength = UNLEN + 1; + if( GetUserNameW( username, &usernameLength ) ) + return QString::fromWCharArray( username ); +#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) + return QString::fromLocal8Bit( qgetenv( "USERNAME" ) ); +#else + return qEnvironmentVariable( "USERNAME" ); +#endif +#endif +#ifdef Q_OS_UNIX + QString username; + uid_t uid = geteuid(); + struct passwd *pw = getpwuid( uid ); + if( pw ) + username = QString::fromLocal8Bit( pw->pw_name ); + if ( username.isEmpty() ) { +#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) + username = QString::fromLocal8Bit( qgetenv( "USER" ) ); +#else + username = qEnvironmentVariable( "USER" ); +#endif + } + return username; +#endif } void SingleApplicationPrivate::genBlockServerName() @@ -105,28 +139,7 @@ void SingleApplicationPrivate::genBlockServerName() // User level block requires a user specific data in the hash if( options & SingleApplication::Mode::User ) { -#ifdef Q_OS_WIN - wchar_t username [ UNLEN + 1 ]; - // Specifies size of the buffer on input - DWORD usernameLength = UNLEN + 1; - if( GetUserNameW( username, &usernameLength ) ) { - appData.addData( QString::fromWCharArray(username).toUtf8() ); - } else { - appData.addData( qgetenv("USERNAME") ); - } -#endif -#ifdef Q_OS_UNIX - QByteArray username; - uid_t uid = geteuid(); - struct passwd *pw = getpwuid(uid); - if( pw ) { - username = pw->pw_name; - } - if( username.isEmpty() ) { - username = qgetenv("USER"); - } - appData.addData(username); -#endif + appData.addData( getUsername().toUtf8() ); } // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with @@ -136,10 +149,11 @@ void SingleApplicationPrivate::genBlockServerName() void SingleApplicationPrivate::initializeMemoryBlock() { - InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() ); + auto *inst = static_cast<InstancesInfo*>( memory->data() ); inst->primary = false; inst->secondary = 0; inst->primaryPid = -1; + inst->primaryUser[0] = '\0'; inst->checksum = blockChecksum(); } @@ -169,10 +183,12 @@ void SingleApplicationPrivate::startPrimary() ); // Reset the number of connections - InstancesInfo* inst = static_cast <InstancesInfo*>( memory->data() ); + auto *inst = static_cast <InstancesInfo*>( memory->data() ); inst->primary = true; inst->primaryPid = q->applicationPid(); + strncpy( inst->primaryUser, getUsername().toUtf8().data(), 127 ); + inst->primaryUser[127] = '\0'; inst->checksum = blockChecksum(); instanceNumber = 0; @@ -250,13 +266,25 @@ qint64 SingleApplicationPrivate::primaryPid() qint64 pid; memory->lock(); - InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() ); + auto *inst = static_cast<InstancesInfo*>( memory->data() ); pid = inst->primaryPid; memory->unlock(); return pid; } +QString SingleApplicationPrivate::primaryUser() +{ + QByteArray username; + + memory->lock(); + auto *inst = static_cast<InstancesInfo*>( memory->data() ); + username = inst->primaryUser; + memory->unlock(); + + return QString::fromUtf8( username ); +} + /** * @brief Executed when a connection has been made to the LocalServer */ diff --git a/third_party/SingleApplication-3.0.19/singleapplication_p.h b/third_party/SingleApplication-3.1.3.1/singleapplication_p.h
index e2c361fb..29ba346b 100644 --- a/third_party/SingleApplication-3.0.19/singleapplication_p.h +++ b/third_party/SingleApplication-3.1.3.1/singleapplication_p.h
@@ -1,6 +1,6 @@ // The MIT License (MIT) // -// Copyright (c) Itay Grudev 2015 - 2016 +// Copyright (c) Itay Grudev 2015 - 2020 // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,14 +42,13 @@ struct InstancesInfo { quint32 secondary; qint64 primaryPid; quint16 checksum; + char primaryUser[128]; }; struct ConnectionInfo { - explicit ConnectionInfo() : - msgLen(0), instanceId(0), stage(0) {} - qint64 msgLen; - quint32 instanceId; - quint8 stage; + qint64 msgLen = 0; + quint32 instanceId = 0; + quint8 stage = 0; }; class SingleApplicationPrivate : public QObject { @@ -69,8 +68,9 @@ public: Q_DECLARE_PUBLIC(SingleApplication) SingleApplicationPrivate( SingleApplication *q_ptr ); - ~SingleApplicationPrivate(); + ~SingleApplicationPrivate() override; + QString getUsername(); void genBlockServerName(); void initializeMemoryBlock(); void startPrimary(); @@ -78,6 +78,7 @@ public: void connectToPrimary(int msecs, ConnectionType connectionType ); quint16 blockChecksum(); qint64 primaryPid(); + QString primaryUser(); void readInitMessageHeader(QLocalSocket *socket); void readInitMessageBody(QLocalSocket *socket);