mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-10-30 07:53:33 +08:00
Compare commits
47 Commits
nightly/20
...
nightly/20
| Author | SHA1 | Date | |
|---|---|---|---|
| abce881b50 | |||
| 666b149033 | |||
| eb10ccbd4a | |||
| f513358236 | |||
| 3c00829afb | |||
| 84a59889e3 | |||
| 6c35561817 | |||
| 6ffdda7b81 | |||
| a51078c900 | |||
| 76a710e2ab | |||
| bc324aa8bb | |||
| 5c772960bc | |||
| da2acefced | |||
| 524c9b0ed4 | |||
| 8677349c4a | |||
| 905b2e858e | |||
| 6fc2e75a33 | |||
| c738caa3a4 | |||
| cb13d4f771 | |||
| 436e11a6a4 | |||
| 869aef8929 | |||
| e0f939318e | |||
| 136dea011f | |||
| af09269d81 | |||
| 5a5ef4711d | |||
| 1dd745d446 | |||
| 3dd2d26d86 | |||
| eb6b6a2b93 | |||
| b15d692a0e | |||
| e76bcbd555 | |||
| b5a979e6b1 | |||
| 898444dd3c | |||
| 99f246016d | |||
| e24f3d91e8 | |||
| 3a7850b398 | |||
| 1e1750024b | |||
| 2ba53a2e40 | |||
| ceec1f95b9 | |||
| 1ac3db4f33 | |||
| 93d3d18c7b | |||
| d4b7891f48 | |||
| d7c294edd0 | |||
| 945cfe176d | |||
| fa3a153896 | |||
| e9106b2bcc | |||
| a967518d45 | |||
| 7ebed53e97 |
2
3rdparty/hunspell
vendored
2
3rdparty/hunspell
vendored
Submodule 3rdparty/hunspell updated: 525f9f2276...749cd84a0b
@ -84,10 +84,8 @@ if(WIN32)
|
||||
if(BETA)
|
||||
message(STATUS "Beta config enabled")
|
||||
add_definitions(-DBETA)
|
||||
set(JAMI_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/x64/Beta)
|
||||
else()
|
||||
set(JAMI_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/x64/Release)
|
||||
endif()
|
||||
set(JAMI_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/x64/${CMAKE_BUILD_TYPE})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@ -246,7 +244,7 @@ set(CMAKE_MODULE_PATH
|
||||
${CMAKE_MODULE_PATH} "${EXTRAS_DIR}/build/cmake/modules")
|
||||
find_package(LibJami REQUIRED)
|
||||
if(LIBJAMI_FOUND)
|
||||
include_directories(${LIBJAMI_INCLUDE_DIRS})
|
||||
include_directories(${LIBJAMI_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
include(FindPython3)
|
||||
@ -266,6 +264,7 @@ add_custom_target(
|
||||
-DAPP_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
|
||||
-DCORE_SOURCE_DIR=${DAEMON_DIR}
|
||||
-DCPP_INT_FILE=${VERSION_INFO_DIR}/version_info.cpp.in
|
||||
-DBUILD_VERSION=${BUILD_VERSION}
|
||||
-P ${CMAKE_SCRIPTS_DIR}/generate_version_info.cmake
|
||||
)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${VERSION_INFO_DIR})
|
||||
@ -347,7 +346,8 @@ set(COMMON_SOURCES
|
||||
${APP_SRC_DIR}/conversationlistmodel.cpp
|
||||
${APP_SRC_DIR}/searchresultslistmodel.cpp
|
||||
${APP_SRC_DIR}/calloverlaymodel.cpp
|
||||
${APP_SRC_DIR}/spellcheckdictionarymanager.cpp
|
||||
${APP_SRC_DIR}/spellcheckdictionarylistmodel.cpp
|
||||
${APP_SRC_DIR}/spellcheckadapter.cpp
|
||||
${APP_SRC_DIR}/filestosendlistmodel.cpp
|
||||
${APP_SRC_DIR}/wizardviewstepmodel.cpp
|
||||
${APP_SRC_DIR}/avatarregistry.cpp
|
||||
@ -378,7 +378,6 @@ set(COMMON_HEADERS
|
||||
${APP_SRC_DIR}/appversionmanager.h
|
||||
${APP_SRC_DIR}/utils.h
|
||||
${APP_SRC_DIR}/bannedlistmodel.h
|
||||
${APP_SRC_DIR}/version.h
|
||||
${APP_SRC_DIR}/accountlistmodel.h
|
||||
${APP_SRC_DIR}/instancemanager.h
|
||||
${APP_SRC_DIR}/connectivitymonitor.h
|
||||
@ -420,7 +419,8 @@ set(COMMON_HEADERS
|
||||
${APP_SRC_DIR}/conversationlistmodel.h
|
||||
${APP_SRC_DIR}/searchresultslistmodel.h
|
||||
${APP_SRC_DIR}/calloverlaymodel.h
|
||||
${APP_SRC_DIR}/spellcheckdictionarymanager.h
|
||||
${APP_SRC_DIR}/spellcheckdictionarylistmodel.h
|
||||
${APP_SRC_DIR}/spellcheckadapter.h
|
||||
${APP_SRC_DIR}/filestosendlistmodel.h
|
||||
${APP_SRC_DIR}/wizardviewstepmodel.h
|
||||
${APP_SRC_DIR}/avatarregistry.h
|
||||
@ -475,13 +475,6 @@ find_package(PkgConfig REQUIRED)
|
||||
|
||||
# hunspell
|
||||
pkg_search_module(hunspell IMPORTED_TARGET hunspell)
|
||||
if(MSVC)
|
||||
elseif (NOT APPLE)
|
||||
set(HUNSPELL_DICT_DIR "/usr/share/hunspell/")
|
||||
else()
|
||||
set(HUNSPELL_DICT_DIR "/Library/Spelling/")
|
||||
endif()
|
||||
|
||||
if(hunspell_FOUND)
|
||||
message(STATUS "hunspell found")
|
||||
set(HUNSPELL_LIBRARIES PkgConfig::hunspell)
|
||||
@ -701,20 +694,34 @@ add_subdirectory(3rdparty/SortFilterProxyModel)
|
||||
set(SFPM_OBJECTS $<TARGET_OBJECTS:SortFilterProxyModel>)
|
||||
|
||||
# md4c
|
||||
set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL "Don't build md2html executable" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Don't build shared md4c library" FORCE)
|
||||
add_subdirectory(3rdparty/md4c EXCLUDE_FROM_ALL)
|
||||
list(APPEND CLIENT_LINK_DIRS ${MD4C_BINARY_DIR}/src)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${MD4C_SOURCE_DIR}/src)
|
||||
list(APPEND CLIENT_LIBS md4c-html)
|
||||
find_package(md4c)
|
||||
if(md4c_FOUND)
|
||||
message(STATUS "Using system-provided md4c-html")
|
||||
list(APPEND CLIENT_LIBS md4c::md4c-html)
|
||||
else()
|
||||
message("Using bundled md4c-html library")
|
||||
set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL "Don't build md2html executable" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Don't build shared md4c library" FORCE)
|
||||
add_subdirectory(3rdparty/md4c EXCLUDE_FROM_ALL)
|
||||
list(APPEND CLIENT_LINK_DIRS ${MD4C_BINARY_DIR}/src)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${MD4C_SOURCE_DIR}/src)
|
||||
list(APPEND CLIENT_LIBS md4c-html)
|
||||
endif()
|
||||
|
||||
# tidy-html5
|
||||
set(BUILD_SHARED_LIB OFF CACHE BOOL "Don't build shared tidy library" FORCE)
|
||||
set(SUPPORT_CONSOLE_APP OFF CACHE BOOL "Don't build tidy console app" FORCE)
|
||||
add_subdirectory(3rdparty/tidy-html5 EXCLUDE_FROM_ALL)
|
||||
list(APPEND CLIENT_LINK_DIRS ${tidy_BINARY_DIR}/Release)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${tidy_SOURCE_DIR}/include)
|
||||
list(APPEND CLIENT_LIBS tidy-static)
|
||||
pkg_check_modules(tidy IMPORTED_TARGET tidy)
|
||||
if(tidy_FOUND)
|
||||
message(STATUS "Using system-provided tidy")
|
||||
list(APPEND CLIENT_LIBS PkgConfig::tidy)
|
||||
else()
|
||||
message("Using bundled tidy library")
|
||||
set(BUILD_SHARED_LIB OFF CACHE BOOL "Don't build shared tidy library" FORCE)
|
||||
set(SUPPORT_CONSOLE_APP OFF CACHE BOOL "Don't build tidy console app" FORCE)
|
||||
add_subdirectory(3rdparty/tidy-html5 EXCLUDE_FROM_ALL)
|
||||
list(APPEND CLIENT_LINK_DIRS ${tidy_BINARY_DIR}/Release)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${tidy_SOURCE_DIR}/include)
|
||||
list(APPEND CLIENT_LIBS tidy-static)
|
||||
endif()
|
||||
|
||||
# ZXing-cpp configuration
|
||||
set(BUILD_EXAMPLES OFF CACHE BOOL "")
|
||||
@ -734,7 +741,8 @@ qt_add_executable(
|
||||
${COMMON_SOURCES}
|
||||
${QML_RESOURCES}
|
||||
${QML_RESOURCES_QML}
|
||||
${SFPM_OBJECTS})
|
||||
${SFPM_OBJECTS}
|
||||
src/app/spellcheckadapter.h src/app/spellcheckadapter.cpp)
|
||||
|
||||
#add_dependencies(${PROJECT_NAME} hunspell)
|
||||
|
||||
@ -839,12 +847,12 @@ elseif (NOT APPLE)
|
||||
|
||||
# Logos
|
||||
install(
|
||||
FILES resources/images/jami.svg
|
||||
FILES resources/images/net.jami.Jami.svg
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps)
|
||||
|
||||
install(
|
||||
FILES resources/images/jami-48px.png
|
||||
FILES resources/images/net.jami.Jami-48px.png
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps
|
||||
RENAME jami.png)
|
||||
|
||||
@ -1022,5 +1030,6 @@ qt_finalize_executable(${PROJECT_NAME})
|
||||
# tests
|
||||
if(BUILD_TESTING)
|
||||
message("Add Jami tests")
|
||||
enable_testing()
|
||||
add_subdirectory(${TESTS_DIR})
|
||||
endif()
|
||||
|
||||
@ -4,10 +4,5 @@
|
||||
<?define ExeName="Jami" ?>
|
||||
<?define AppName="Jami" ?>
|
||||
<?define Manufacturer="Savoir-Faire Linux"?>
|
||||
|
||||
<?if $(var.Configuration) = Release ?>
|
||||
<?define ReleaseDir="..\x64\Release"?>
|
||||
<?else?>
|
||||
<?define ReleaseDir="..\x64\Beta"?>
|
||||
<?endif ?>
|
||||
</Include>
|
||||
|
||||
@ -16,9 +16,9 @@
|
||||
<InstallerPlatform>x64</InstallerPlatform>
|
||||
<DefineSolutionProperties>false</DefineSolutionProperties>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||
<PropertyGroup>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<IntermediateOutputPath>obj\Release\</IntermediateOutputPath>
|
||||
<DefineConstants>AppHarvestPath=..\x64\Release;CrtHarvestPath=$(VC_CRT_Dir)</DefineConstants>
|
||||
<SuppressPdbOutput>True</SuppressPdbOutput>
|
||||
<CompilerAdditionalOptions>
|
||||
@ -26,16 +26,6 @@
|
||||
<WixVariables>
|
||||
</WixVariables>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Beta|x64' ">
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||
<DefineConstants>AppHarvestPath=..\x64\Beta;CrtHarvestPath=$(VC_CRT_Dir)</DefineConstants>
|
||||
<SuppressPdbOutput>True</SuppressPdbOutput>
|
||||
<CompilerAdditionalOptions>
|
||||
</CompilerAdditionalOptions>
|
||||
<WixVariables>
|
||||
</WixVariables>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Product.wxs" />
|
||||
<Compile Include="AppComponents.wxs" />
|
||||
@ -70,7 +60,7 @@
|
||||
<Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
|
||||
</Target>
|
||||
<Target Name="BeforeBuild">
|
||||
<HeatDirectory Directory="..\x64\$(Configuration)"
|
||||
<HeatDirectory Directory="..\x64\Release"
|
||||
PreprocessorVariable="var.AppHarvestPath"
|
||||
OutputFile="AppComponents.wxs"
|
||||
ComponentGroupName="AppHeatGenerated"
|
||||
|
||||
2
daemon
2
daemon
Submodule daemon updated: 80ff0174b8...4a7b5ac5b2
@ -24,6 +24,7 @@ set(CMAKE_CURRENT_BINARY_DIR ${APP_BINARY_DIR})
|
||||
# Generate the version string for the application and core
|
||||
configure_version_string(${APP_SOURCE_DIR} APP_VERSION_STRING)
|
||||
configure_version_string(${CORE_SOURCE_DIR} CORE_VERSION_STRING)
|
||||
set(BUILD_VERSION_STRING ${BUILD_VERSION})
|
||||
|
||||
# Get output file names with the .in extension removed
|
||||
get_filename_component(VERSION_CPP_FILENAME ${CPP_INT_FILE} NAME_WE)
|
||||
|
||||
@ -16,30 +16,19 @@
|
||||
|
||||
# Once done, this find module will set:
|
||||
#
|
||||
# LIBJAMI_INCLUDE_DIRS - libjami include directories
|
||||
# LIBJAMI_INCLUDE_DIR - libjami include directory
|
||||
# LIBJAMI_FOUND - whether it was able to find the include directories
|
||||
# LIBJAMI_LIB - path to libjami or libring library
|
||||
|
||||
set(LIBJAMI_FOUND true)
|
||||
|
||||
if(WITH_DAEMON_SUBMODULE)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${DAEMON_DIR}/src/jami)
|
||||
set(LIBJAMI_INCLUDE_DIR ${DAEMON_DIR}/src/jami)
|
||||
else()
|
||||
if(EXISTS ${LIBJAMI_INCLUDE_DIR}/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${LIBJAMI_INCLUDE_DIR})
|
||||
elseif(EXISTS ${LIBJAMI_BUILD_DIR}/jami/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${LIBJAMI_BUILD_DIR}/jami)
|
||||
elseif(EXISTS ${RING_INCLUDE_DIR}/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${RING_INCLUDE_DIR})
|
||||
elseif(EXISTS ${RING_BUILD_DIR}/jami/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${RING_BUILD_DIR}/jami)
|
||||
elseif(EXISTS ${CMAKE_INSTALL_PREFIX}/include/jami/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include/jami)
|
||||
elseif(EXISTS ${CMAKE_INSTALL_PREFIX}/daemon/include/jami/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/daemon/include/jami)
|
||||
else()
|
||||
find_path(LIBJAMI_INCLUDE_DIR jami.h PATH_SUFFIXES jami)
|
||||
if(NOT LIBJAMI_INCLUDE_DIR)
|
||||
message(STATUS "Jami daemon headers not found!
|
||||
Set -DLIBJAMI_BUILD_DIR or -DCMAKE_INSTALL_PREFIX")
|
||||
To build using the daemon git submodule, set -DWITH_DAEMON_SUBMODULE")
|
||||
set(LIBJAMI_FOUND false)
|
||||
endif()
|
||||
endif()
|
||||
@ -121,5 +110,5 @@ endif()
|
||||
# Restore the original value of CMAKE_FIND_LIBRARY_SUFFIXES.
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_orig})
|
||||
|
||||
message(STATUS "Jami daemon headers are in " ${LIBJAMI_INCLUDE_DIRS})
|
||||
message(STATUS "Jami daemon headers are in " ${LIBJAMI_INCLUDE_DIR})
|
||||
message(STATUS "Jami daemon library is at " ${LIBJAMI_LIB})
|
||||
|
||||
@ -4,11 +4,24 @@ ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV QT_QUICK_BACKEND software
|
||||
ENV QT_QPA_PLATFORM offscreen
|
||||
|
||||
RUN apt-get clean
|
||||
RUN apt-get update && \
|
||||
apt-get install -y devscripts equivs
|
||||
apt-get install -y --no-install-recommends ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Use only the custom Savoir-faire Linux Ubuntu mirror
|
||||
RUN rm -f /etc/apt/sources.list /etc/apt/sources.list.d/* && \
|
||||
echo "deb http://gpl.savoirfairelinux.net/pub/mirrors/ubuntu jammy main restricted universe multiverse" > /etc/apt/sources.list && \
|
||||
echo "deb http://gpl.savoirfairelinux.net/pub/mirrors/ubuntu jammy-updates main restricted universe multiverse" >> /etc/apt/sources.list && \
|
||||
echo "deb http://gpl.savoirfairelinux.net/pub/mirrors/ubuntu jammy-security main restricted universe multiverse" >> /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
devscripts \
|
||||
equivs \
|
||||
gnupg \
|
||||
dirmngr \
|
||||
curl
|
||||
|
||||
RUN apt install gnupg dirmngr ca-certificates curl --no-install-recommends
|
||||
RUN curl -s https://dl.jami.net/public-key.gpg | tee /usr/share/keyrings/jami-archive-keyring.gpg > /dev/null
|
||||
RUN sh -c "echo 'deb [signed-by=/usr/share/keyrings/jami-archive-keyring.gpg] https://dl.jami.net/internal/ubuntu_22.04/ jami main' > /etc/apt/sources.list.d/jami.list"
|
||||
RUN apt-get update && apt-get install libqt-jami -y
|
||||
@ -69,7 +82,8 @@ RUN apt-get install -y pandoc \
|
||||
libcppunit-dev \
|
||||
googletest \
|
||||
libgtest-dev \
|
||||
wget
|
||||
wget && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install a recent version of CMake
|
||||
ADD extras/packaging/gnu-linux/scripts/install-cmake.sh /opt/install-cmake.sh
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
<id>net.jami.daemon</id>
|
||||
</requires>
|
||||
<launchable type="desktop-id">net.jami.Jami.desktop</launchable>
|
||||
<icon type="stock">jami</icon>
|
||||
<icon type="stock">net.jami.Jami</icon>
|
||||
<url type="homepage">https://jami.net/</url>
|
||||
<url type="bugtracker">https://git.jami.net/savoirfairelinux/jami-client-qt/issues</url>
|
||||
<url type="faq">https://docs.jami.net/user/faq.html</url>
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
[Desktop Entry]
|
||||
Name=Jami
|
||||
GenericName=Jami
|
||||
Comment=Privacy-oriented voice, video, chat, and conference platform
|
||||
Comment[hu]=Adatvédelem-orientált hang-, video-, csevegés- és konferenciaplatform
|
||||
Comment[ru]=Jami — приложение для защищённой связи с распределённой архитектурой
|
||||
Comment=Share, freely and privately
|
||||
Comment[hu]=Megosztás, szabadon és bizalmasan
|
||||
Exec=jami %u
|
||||
Icon=jami
|
||||
Icon=net.jami.Jami
|
||||
StartupNotify=true
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;Telephony;
|
||||
Categories=Chat;FileTransfer;InstantMessaging;Network;P2P;Telephony;VideoConference;
|
||||
Keywords=Qt;chat;talk;im;message;voip;
|
||||
MimeType=x-scheme-handler/jami;
|
||||
MimeType=x-scheme-handler/jami;
|
||||
|
||||
@ -2,87 +2,104 @@
|
||||
<!-- Copyright (C) 2015-2025 Savoir-faire Linux Inc. -->
|
||||
<component type="desktop-application">
|
||||
<id>net.jami.Jami</id>
|
||||
<metadata_license>CC-BY-SA-3.0</metadata_license>
|
||||
<project_license>GPL-3.0+</project_license>
|
||||
<name>Jami</name>
|
||||
|
||||
<summary>Privacy-oriented voice, video, chat, and conference platform</summary>
|
||||
<summary xml:lang="hu">Adatvédelem-orientált hang-, video-, csevegés- és konferenciaplatform</summary>
|
||||
<icon type="stock">jami</icon>
|
||||
<summary>Share, freely and privately</summary>
|
||||
<summary xml:lang="hu">Megosztás, szabadon és bizalmasan</summary>
|
||||
|
||||
<icon type="stock">net.jami.Jami</icon>
|
||||
|
||||
<metadata_license>CC-BY-SA-3.0</metadata_license>
|
||||
<project_license>GPL-3.0+</project_license>
|
||||
|
||||
<developer id="net.jami">
|
||||
<name>Savoir-faire Linux Inc.</name>
|
||||
</developer>
|
||||
|
||||
<releases>
|
||||
<release version="20250718.0" date="2025-07-18">
|
||||
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250718</url>
|
||||
</release>
|
||||
<release version="20250613.0" date="2025-06-13">
|
||||
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250613</url>
|
||||
</release>
|
||||
</releases>
|
||||
|
||||
<branding>
|
||||
<color type="primary" scheme_preference="light">#03b9e9</color>
|
||||
<color type="primary" scheme_preference="dark">#005699</color>
|
||||
</branding>
|
||||
|
||||
<!-- https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-description -->
|
||||
<description>
|
||||
<p>Jami, a GNU package, is software for universal and distributed peer-to-peer communication that respects the freedom and privacy of its users.</p>
|
||||
<p xml:lang="hu">A Jami, egy GNU-csomag, egy univerzális és elosztott társ-társ kommunikációra szolgáló szoftver, amely tiszteletben tartja a felhasználók szabadságát és magánéletét.</p>
|
||||
<p>Jami is the simplest and easiest way to connect with people (and devices) with instant messaging, audio and video calls over the Internet and LAN/WAN intranets.</p>
|
||||
<p xml:lang="hu">A Jami a legegyszerűbb és legegyszerűbb módja annak, hogy azonnali üzenetküldéssel, hang- és videohívásokkal kapcsolódjon az emberekhez (és eszközökhöz) az interneten és a LAN/WAN intraneteken keresztül.</p>
|
||||
|
||||
<p><em>Jami</em>, a GNU package, is software for universal and distributed peer-to-peer communication that respects the freedom and privacy of its users.</p>
|
||||
<p xml:lang="hu">A <em>Jami</em>, egy GNU-csomag, egy univerzális és elosztott társ-társ kommunikációt szolgáló szoftver, amely tiszteletben tartja a felhasználók szabadságát és magánéletét.</p>
|
||||
|
||||
<p>Jami is the simplest and easiest way to connect with people (and devices) with instant messaging, audio, and video calls over the Internet and LAN/WAN intranets.</p>
|
||||
<p xml:lang="hu">A Jami a legegyszerűbb és legegyszerűbb módja annak, hogy azonnali üzenetküldéssel, hang- és videohívásokkal kapcsolódjon az emberekhez (és eszközökhöz) az interneten és a LAN/WAN intraneten keresztül.</p>
|
||||
|
||||
<p>Jami is a free/libre, end-to-end encrypted, and private communication platform.</p>
|
||||
<p xml:lang="hu">A Jami egy ingyenes, teljes körűen titkosított és privát kommunikációs platform.</p>
|
||||
<p>Jami – which used to be known as Ring – is also an open-source alternative (to Facebook Messenger, Signal, Skype, Teams, Telegram, TikTok, Viber, WhatsApp, Zoom) that prioritizes the privacy of its users.</p>
|
||||
<p xml:lang="hu">A Jami – amelyet korábban Ring néven ismertek – egy nyílt forráskódú alternatíva is (a Facebook Messenger, a Signal, a Skype, a Teams, a Telegram, a TikTok, a Viber, a WhatsApp, a Zoom számára), amely előtérbe helyezi a felhasználók magánéletét.</p>
|
||||
<p>Jami has a professional-looking design and is available for a wide range of platforms. Unlike the alternatives, calls using Jami are directly between users as it does not use servers to handle calls.</p>
|
||||
<p xml:lang="hu">A Jami professzionális megjelenésű, és platformok széles skálájához elérhető. Az alternatívákkal ellentétben a Jami-t használó hívások közvetlenül a felhasználók között zajlanak, mivel nem használ kiszolgálókat a hívások kezelésére.</p>
|
||||
<p>This gives the greatest privacy as the distributed nature of Jami means your calls are only between participants.</p>
|
||||
|
||||
<p>Jami is open-source software that prioritizes user privacy.</p>
|
||||
<p xml:lang="hu">A Jami nyílt forráskódú szoftver, amely előnyben részesíti a felhasználók adatvédelmét.</p>
|
||||
|
||||
<p>Jami has a professional-looking design and is available for a wide range of platforms. Unlike the alternatives, Jami calls are directly between users, as it does not use servers to handle calls.</p>
|
||||
<p xml:lang="hu">A Jami professzionális megjelenésű, és platformok széles skálájához elérhető. Az alternatívákkal ellentétben a Jami közvetlenül a felhasználók között zajlik, mivel nem használ kiszolgálókat a hívások kezelésére.</p>
|
||||
|
||||
<p>This gives the greatest privacy, as the distributed nature of Jami means your calls are only between participants.</p>
|
||||
<p xml:lang="hu">Ez biztosítja a legnagyobb magánéletet, mivel a Jami elosztott jellege azt jelenti, hogy a hívások csak a résztvevők között zajlanak.</p>
|
||||
<p>One-to-one and group conversations with Jami are enhanced with: instant messaging; audio and video calling; recording and sending audio and video messages; file transfers; screen sharing; and, location sharing.</p>
|
||||
<p xml:lang="hu">A Jamival folytatott személyes és csoportos beszélgetéseket a következők javítják: azonnali üzenetküldés; hang- és videohívások; hang- és videoüzenetek rögzítése és küldése; fájlátvitel; képernyőmegosztás; és helymegosztás.</p>
|
||||
|
||||
<p>One-to-one and group conversations with Jami are enhanced with instant messaging, audio and video calling, recording and sending audio and video messages, file transfers, screen sharing, and location sharing.</p>
|
||||
<p xml:lang="hu">A Jamival folytatott személyes és csoportos beszélgetéseket az azonnali üzenetküldés, hang- és videohívások, hang- és videoüzenetek rögzítése és küldése, fájlátvitel, képernyőmegosztás és helymegosztás továbbfejlesztik.</p>
|
||||
|
||||
<p>Jami can also function as a SIP client.</p>
|
||||
<p xml:lang="hu">A Jami SIP-ügyfélként is működhet.</p>
|
||||
<p>Jami has multiple extensions available: Audio Filter; Auto Answer; Green Screen; Watermark; and, Whisper Transcript.</p>
|
||||
<p xml:lang="hu">A Jami-nek több bővítménye is elérhető: hangszűrő; automatikus válasz; zöld képernyő; vízjel; és, suttogó átirat.</p>
|
||||
<p>Jami can be easily deployed in organizations with the “Jami Account Management Server” (JAMS), allowing users to connect with their corporate credentials or create local accounts. JAMS allows you to manage your own Jami community while taking advantage of Jami’s distributed network architecture.</p>
|
||||
<p xml:lang="hu">A Jami könnyen telepíthető a szervezetekben a JAMS (Jami Account Management Server – Jami fiókkezelő kiszolgáló), amely lehetővé teszi a felhasználók számára, hogy csatlakozzanak vállalati hitelesítő adataikhoz, vagy helyi fiókokat hozzanak létre. A JAMS lehetővé teszi saját Jami közösségének kezelését, miközben kihasználja a Jami elosztott hálózati architektúráját.</p>
|
||||
<p>Jami is available for GNU/Linux, Windows, macOS, iOS, Android, and Android TV, making Jami an interoperable and cross-platform communication framework.</p>
|
||||
<p xml:lang="hu">A Jami elérhető GNU/Linux, Windows, macOS, iOS, Android és Android TV rendszereken, így a Jami egy interoperábilis és platformok közötti kommunikációs keretrendszer.</p>
|
||||
<p>Manage multiple SIP accounts, Jami accounts and JAMS accounts with the Jami client installed on one or multiple devices.</p>
|
||||
<p xml:lang="hu">Kezeljen több SIP-fiókot, Jami-fiókot és JAMS-fiókot az egy vagy több eszközre telepített Jami-ügyféllel.</p>
|
||||
<p>Jami is free, unlimited, private, advertising free, compatible, fast, autonomous, and anonymous.</p>
|
||||
|
||||
<p>Multiple Jami extensions are available: Audio Filter, Auto Answer, Green Screen, Segmentation, Watermark, and Whisper Transcript.</p>
|
||||
<p xml:lang="hu">Több Jami bővítmény érhető el: hangszűrő, automatikus válasz, zöld képernyő, szegmentálás, vízjel és suttogó átirat.</p>
|
||||
|
||||
<p>Jami can be easily deployed in organizations with the <em>JAMS (Jami Account Management Server)</em>, allowing users to connect with their corporate credentials or create local accounts. JAMS allows you to manage your own Jami community while taking advantage of Jami’s distributed network architecture.</p>
|
||||
<p xml:lang="hu">A Jami könnyen telepíthető a szervezetekben a <em>JAMS (Jami Account Management Server — JAMS-kezelő-kiszolgáló)</em> segítségével, amely lehetővé teszi a felhasználók számára, hogy csatlakozzanak vállalati hitelesítő adataikhoz, vagy helyi fiókokat hozzanak létre. A JAMS lehetővé teszi a saját Jami közösség kezelését, miközben kihasználja a Jami elosztott hálózati architektúráját.</p>
|
||||
|
||||
<p>Jami is available for GNU/Linux, Windows, macOS, iOS, Android, Android TV, and web browsers, making Jami an interoperable and cross-platform communication framework.</p>
|
||||
<p xml:lang="hu">A Jami elérhető GNU/Linux, Windows, macOS, iOS, Android, Android TV és webböngészők számára, így a Jami interoperábilis és platformok közötti kommunikációs keretrendszerré válik.</p>
|
||||
|
||||
<p>Manage multiple SIP accounts, Jami accounts, and JAMS accounts with the Jami client installed on one or multiple devices.</p>
|
||||
<p xml:lang="hu">Több SIP-, Jami- és JAMS-fiók kezelése az egy vagy több eszközre telepített a Jami-alkalmazással.</p>
|
||||
|
||||
<p>Jami is free, unlimited, private, advertising‑free, compatible, fast, autonomous, and anonymous.</p>
|
||||
<p xml:lang="hu">A Jami ingyenes, korlátlan, privát, reklámmentes, kompatibilis, gyors, autonóm és névtelen.</p>
|
||||
<p>Learn more about:</p>
|
||||
<p xml:lang="hu">További tájékoztatás:</p>
|
||||
<ul>
|
||||
<li>Jami: https://jami.net/</li>
|
||||
<li xml:lang="hu">Jami: https://jami.net/hu/</li>
|
||||
<li>Jami extensions: https://jami.net/extensions/</li>
|
||||
<li xml:lang="hu">Jami-bővítmények: https://jami.net/hu/extensions/</li>
|
||||
<li>“Jami Account Management Server” (JAMS): https://jami.biz/</li>
|
||||
<li xml:lang="hu">JAMS (Jami Account Management Server – Jami fiókkezelő kiszolgáló): https://jami.biz/</li>
|
||||
<li>Jami documentation: https://docs.jami.net/</li>
|
||||
<li xml:lang="hu">Jami-dokumentáció: https://docs.jami.net/hu/</li>
|
||||
</ul>
|
||||
<p>Follow us for more:</p>
|
||||
<p xml:lang="hu">Kövess minket a továbbiakért:</p>
|
||||
<ul>
|
||||
<li>Mastodon: https://mstdn.io/@Jami</li>
|
||||
<li xml:lang="hu">Mastodon: https://mstdn.io/@Jami</li>
|
||||
<li>X: https://x.com/jami_social</li>
|
||||
<li xml:lang="hu">X: https://x.com/jami_social</li>
|
||||
<li>YouTube: https://www.youtube.com/@jami9311</li>
|
||||
<li xml:lang="hu">YouTube: https://www.youtube.com/@jami9311</li>
|
||||
</ul>
|
||||
<p>We’d love to hear from you! Join the Jami community:</p>
|
||||
<p xml:lang="hu">Szívesen hallanánk felőled! Csatlakozzon a Jami közösséghez:</p>
|
||||
<ul>
|
||||
<li>Contribute: https://jami.net/contribute/</li>
|
||||
<li xml:lang="hu">Közreműködés: https://jami.net/hu/contribute/</li>
|
||||
<li>Forum: https://forum.jami.net/</li>
|
||||
<li xml:lang="hu">Fórum: https://forum.jami.net/</li>
|
||||
</ul>
|
||||
<p>Build with Jami on your IoT project: re-use the universal communications technology of Jami with its portable library on your system of choice.</p>
|
||||
<p xml:lang="hu">Építsen a Jamival IoT-projektjére: használja újra a Jami univerzális kommunikációs technológiáját a hordozható könyvtárával a választott rendszerén.</p>
|
||||
|
||||
<p>Build IoT projects with Jami. Re-use the universal communications technology of Jami with its portable library on your system of choice.</p>
|
||||
<p xml:lang="hu">Építsen IoT-projekteket Jamival. Használja újra a Jami univerzális kommunikációs technológiáját annak hordozható könyvtárával az Ön által választott rendszeren.</p>
|
||||
|
||||
<p>Jami for Android TV is tested on NVIDIA SHIELD TV with Logitech cameras.</p>
|
||||
<p xml:lang="hu">A Jami for Android TV-t Logitech kamerákkal ellátott NVIDIA SHIELD TV-n tesztelték.</p>
|
||||
<p>Jami is published under the GPL license, version 3 or higher.</p>
|
||||
<p xml:lang="hu">A Jami a GPL licenc 3-as vagy újabb verziója alatt jelent meg.</p>
|
||||
<p>Copyright © Savoir-faire Linux Inc.</p>
|
||||
<p xml:lang="hu">Szerzői jog © Savoir-faire Linux Inc.</p>
|
||||
<p xml:lang="hu">A Jami Androidra TV-t Logitech kamerákkal ellátott NVIDIA SHIELD TV-n tesztelték.</p>
|
||||
|
||||
<p>Jami is published under the GPL license, version 3 or higher. Copyright © Savoir-faire Linux Inc.</p>
|
||||
<p xml:lang="hu">A Jami a GPL-licenc 3-as vagy újabb verziója alatt jelent meg. Szerzői jog © Savoir-faire Linux Inc.</p>
|
||||
|
||||
</description>
|
||||
|
||||
<url type="homepage">https://jami.net/</url>
|
||||
<requires>
|
||||
<id>net.jami.daemon</id>
|
||||
</requires>
|
||||
|
||||
<url type="bugtracker">https://git.jami.net/savoirfairelinux/jami-client-qt/issues</url>
|
||||
<url type="faq">https://docs.jami.net/user/faq.html</url>
|
||||
<url type="help">https://forum.jami.net/</url>
|
||||
<url type="homepage">https://jami.net/</url>
|
||||
<url type="donation">https://jami.net/whydonate/</url>
|
||||
<url type="translate">https://www.transifex.com/savoirfairelinux/jami</url>
|
||||
<url type="faq">https://docs.jami.net/user/faq.html</url>
|
||||
<url type="translate">https://explore.transifex.com/savoirfairelinux/jami/</url>
|
||||
<url type="contribute">https://jami.net/contribute/</url>
|
||||
<url type="help">https://docs.jami.net/</url>
|
||||
<url type="vcs-browser">https://git.jami.net/savoirfairelinux</url>
|
||||
|
||||
<provides>
|
||||
<binary>jami</binary>
|
||||
<mediatype>x-scheme-handler/jami</mediatype>
|
||||
</provides>
|
||||
|
||||
<!-- Maximum caption length is 60 characters -->
|
||||
<!-- Officially GIF is not an allowed video format, but it appears to work nonetheless -->
|
||||
@ -121,7 +138,15 @@
|
||||
|
||||
<launchable type="desktop-id">net.jami.Jami.desktop</launchable>
|
||||
|
||||
<provides><binary>jami</binary></provides>
|
||||
<!-- https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-keywords -->
|
||||
<keywords>
|
||||
<keyword translate="no">Qt</keyword>
|
||||
<keyword>chat</keyword>
|
||||
<keyword>talk</keyword>
|
||||
<keyword>im</keyword>
|
||||
<keyword>message</keyword>
|
||||
<keyword translate="no">voip</keyword>
|
||||
</keywords>
|
||||
|
||||
<!-- https://specifications.freedesktop.org/menu-spec/latest/apa.html -->
|
||||
<!-- https://specifications.freedesktop.org/menu-spec/latest/apas02.html -->
|
||||
@ -142,5 +167,4 @@
|
||||
<content_attribute id="social-audio">intense</content_attribute>
|
||||
</content_rating>
|
||||
|
||||
<requires><id>net.jami.daemon</id></requires>
|
||||
</component>
|
||||
|
||||
4
extras/packaging/gnu-linux/Jenkinsfile
vendored
4
extras/packaging/gnu-linux/Jenkinsfile
vendored
@ -85,6 +85,10 @@ pipeline {
|
||||
|
||||
environment {
|
||||
TARBALLS = '/var/cache/jami' // set the cache directory
|
||||
BUILD_VERSION = sh(
|
||||
returnStdout: true,
|
||||
script: 'date +"%Y%m%d%H%M"'
|
||||
).trim()
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
@ -169,12 +169,10 @@ DISTRIBUTIONS := \
|
||||
ubuntu_24.04 \
|
||||
ubuntu_24.10 \
|
||||
ubuntu_25.04 \
|
||||
fedora_39 \
|
||||
fedora_40 \
|
||||
fedora_41 \
|
||||
fedora_42 \
|
||||
alma_9 \
|
||||
opensuse-leap_15.5 \
|
||||
alma_10 \
|
||||
opensuse-leap_15.6 \
|
||||
snap
|
||||
|
||||
@ -194,6 +192,7 @@ $(1)-docker-image-name := jami-packaging-$(1)
|
||||
$(1)-docker-image-file := .docker-image-$$($(1)-docker-image-name)
|
||||
$(1)-docker-run-command := docker run \
|
||||
--rm --privileged --security-opt apparmor=docker-default \
|
||||
-e BUILD_VERSION=${BUILD_VERSION} \
|
||||
-e RELEASE_VERSION="$(RELEASE_VERSION)" \
|
||||
-e RELEASE_DIRNAME="$(RELEASE_DIRNAME)" \
|
||||
-e RELEASE_TARBALL_FILENAME="$(RELEASE_TARBALL_FILENAME)" \
|
||||
@ -253,7 +252,7 @@ define guix-pack-command
|
||||
guix pack -C xz -f $(1) -m $(CURDIR)/extras/packaging/gnu-linux/guix/guix-pack-manifest.scm -v3 \
|
||||
-S /usr/bin/jami=bin/jami \
|
||||
-S /usr/share/applications/net.jami.Jami.desktop=share/applications/net.jami.Jami.desktop \
|
||||
-S /usr/share/icons/hicolor/scalable/apps/jami.svg=share/icons/hicolor/scalable/apps/jami.svg \
|
||||
-S /usr/share/icons/hicolor/scalable/apps/net.jami.Jami.svg=share/icons/hicolor/scalable/apps/net.jami.Jami.svg \
|
||||
-S /usr/share/icons/hicolor/48x48/apps/jami.png=share/icons/hicolor/48x48/apps/jami.png \
|
||||
-S /usr/share/metainfo/net.jami.Jami.metainfo.xml=share/metainfo/net.jami.Jami.metainfo.xml \
|
||||
-S /usr/share/swcatalog/xml/jami.xml=share/swcatalog/xml/jami.xml \
|
||||
|
||||
@ -1,105 +1,93 @@
|
||||
FROM fedora:39
|
||||
|
||||
RUN dnf clean all
|
||||
RUN dnf update -y
|
||||
|
||||
FROM almalinux:10
|
||||
RUN dnf clean all && dnf update -y
|
||||
RUN dnf install -y epel-release
|
||||
RUN dnf install -y 'dnf-command(config-manager)'
|
||||
RUN dnf config-manager --set-enabled crb
|
||||
RUN dnf config-manager --set-enabled appstream
|
||||
RUN dnf install -y dnf-command\(builddep\) rpmdevtools && \
|
||||
dnf install -y mock
|
||||
|
||||
RUN dnf groupinstall -y "X Software Development"
|
||||
|
||||
RUN yum install -y xorg-x11-xauth
|
||||
RUN dnf install -y \
|
||||
git \
|
||||
rpm-build \
|
||||
tar \
|
||||
make \
|
||||
alsa-lib-devel \
|
||||
astyle \
|
||||
autoconf \
|
||||
automake \
|
||||
nasm \
|
||||
speexdsp-devel \
|
||||
pulseaudio-libs-devel \
|
||||
libcanberra-devel \
|
||||
libcurl-devel \
|
||||
libtool \
|
||||
mesa-libgbm-devel \
|
||||
mesa-dri-drivers \
|
||||
bison \
|
||||
check \
|
||||
chrpath \
|
||||
clang15-devel \
|
||||
cmake \
|
||||
cryptopp-devel \
|
||||
cups-devel \
|
||||
dbus-devel \
|
||||
expat-devel \
|
||||
pcre-devel \
|
||||
yaml-cpp-devel \
|
||||
libXext-devel \
|
||||
libXfixes-devel \
|
||||
yasm \
|
||||
speex-devel \
|
||||
gsm-devel \
|
||||
chrpath \
|
||||
check \
|
||||
astyle \
|
||||
uuid-c++-devel \
|
||||
gettext-devel \
|
||||
gcc-c++ \
|
||||
which \
|
||||
alsa-lib-devel \
|
||||
systemd-devel \
|
||||
libuuid-devel \
|
||||
uuid-devel \
|
||||
gnutls-devel \
|
||||
nettle-devel \
|
||||
opus-devel \
|
||||
patch \
|
||||
jsoncpp-devel \
|
||||
libnatpmp-devel \
|
||||
webkitgtk4-devel \
|
||||
cryptopp-devel \
|
||||
libva-devel \
|
||||
libvdpau-devel \
|
||||
msgpack-devel \
|
||||
NetworkManager-libnm-devel \
|
||||
openssl-devel \
|
||||
clutter-devel \
|
||||
clutter-gtk-devel \
|
||||
libappindicator-gtk3-devel \
|
||||
libnotify-devel \
|
||||
libupnp-devel \
|
||||
qrencode-devel \
|
||||
libargon2-devel \
|
||||
libsndfile-devel \
|
||||
libdrm \
|
||||
gperf \
|
||||
bison \
|
||||
clang \
|
||||
clang-devel \
|
||||
llvm-devel \
|
||||
nodejs \
|
||||
flex \
|
||||
fmt-devel \
|
||||
gcc-c++ \
|
||||
gettext-devel \
|
||||
git \
|
||||
gnutls-devel \
|
||||
gperf \
|
||||
gsm-devel \
|
||||
gstreamer1 gstreamer1-devel \
|
||||
gstreamer1-plugins-bad-free-devel \
|
||||
gstreamer1-plugins-base-devel \
|
||||
gstreamer1-plugins-good \
|
||||
gstreamer1-plugins-bad-free-devel \
|
||||
nss-devel \
|
||||
jsoncpp-devel \
|
||||
libX11-devel \
|
||||
libXext-devel \
|
||||
libXfixes-devel \
|
||||
libXrender-devel \
|
||||
libappindicator-gtk3-devel \
|
||||
libargon2-devel \
|
||||
libcanberra-devel \
|
||||
libcurl-devel \
|
||||
libdrm \
|
||||
libnatpmp-devel \
|
||||
libnotify \
|
||||
libnotify-devel \
|
||||
libsndfile-devel \
|
||||
libstdc++-static \
|
||||
libtool \
|
||||
libupnp-devel \
|
||||
libuuid-devel \
|
||||
libva-devel \
|
||||
libvdpau-devel \
|
||||
libxcb* \
|
||||
libxkb* \
|
||||
libX11-devel \
|
||||
vulkan-devel \
|
||||
libXrender-devel \
|
||||
xcb-util-* \
|
||||
xz \
|
||||
xkeyboard-config \
|
||||
libnotify \
|
||||
wget \
|
||||
libstdc++-static \
|
||||
sqlite-devel \
|
||||
perl-generators \
|
||||
perl-English \
|
||||
libxshmfence-devel \
|
||||
llvm15-devel \
|
||||
make \
|
||||
mesa-dri-drivers \
|
||||
mesa-libgbm-devel \
|
||||
msgpack-devel \
|
||||
nasm \
|
||||
nettle-devel \
|
||||
NetworkManager-libnm-devel \
|
||||
ninja-build \
|
||||
clang \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3.10 \
|
||||
cups-devel \
|
||||
pipewire-devel
|
||||
|
||||
nodejs \
|
||||
nss-devel \
|
||||
openssl-devel \
|
||||
opus-devel \
|
||||
pcre2-devel \
|
||||
perl-English \
|
||||
perl-generators \
|
||||
pipewire-devel \
|
||||
pulseaudio-libs-devel \
|
||||
python3-html5lib \
|
||||
qrencode-devel \
|
||||
speex-devel \
|
||||
speexdsp-devel \
|
||||
sqlite-devel \
|
||||
systemd-devel \
|
||||
uuid-devel \
|
||||
vulkan-devel \
|
||||
webkitgtk6.0-devel \
|
||||
wget \
|
||||
which \
|
||||
xcb-util-* \
|
||||
xkeyboard-config \
|
||||
yaml-cpp-devel \
|
||||
yasm
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
@ -70,9 +70,8 @@ RUN dnf install -y \
|
||||
libdrm \
|
||||
gperf \
|
||||
bison \
|
||||
clang \
|
||||
clang-devel \
|
||||
llvm-devel \
|
||||
clang16-devel \
|
||||
llvm16-devel \
|
||||
nodejs \
|
||||
flex \
|
||||
gstreamer1 gstreamer1-devel \
|
||||
@ -96,7 +95,6 @@ RUN dnf install -y \
|
||||
perl-English \
|
||||
libxshmfence-devel \
|
||||
ninja-build \
|
||||
clang \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3-html5lib \
|
||||
|
||||
@ -1,105 +0,0 @@
|
||||
FROM fedora:40
|
||||
|
||||
RUN dnf clean all
|
||||
RUN dnf update -y
|
||||
|
||||
RUN dnf install -y dnf-command\(builddep\) rpmdevtools && \
|
||||
dnf install -y mock
|
||||
|
||||
RUN dnf groupinstall -y "X Software Development"
|
||||
|
||||
RUN dnf install -y \
|
||||
git \
|
||||
rpm-build \
|
||||
tar \
|
||||
make \
|
||||
autoconf \
|
||||
automake \
|
||||
nasm \
|
||||
speexdsp-devel \
|
||||
pulseaudio-libs-devel \
|
||||
libcanberra-devel \
|
||||
libcurl-devel \
|
||||
libtool \
|
||||
mesa-libgbm-devel \
|
||||
mesa-dri-drivers \
|
||||
dbus-devel \
|
||||
expat-devel \
|
||||
pcre-devel \
|
||||
yaml-cpp-devel \
|
||||
libXext-devel \
|
||||
libXfixes-devel \
|
||||
yasm \
|
||||
speex-devel \
|
||||
gsm-devel \
|
||||
chrpath \
|
||||
check \
|
||||
astyle \
|
||||
uuid-c++-devel \
|
||||
gettext-devel \
|
||||
gcc-c++ \
|
||||
which \
|
||||
alsa-lib-devel \
|
||||
systemd-devel \
|
||||
libuuid-devel \
|
||||
uuid-devel \
|
||||
gnutls-devel \
|
||||
nettle-devel \
|
||||
opus-devel \
|
||||
patch \
|
||||
jsoncpp-devel \
|
||||
libnatpmp-devel \
|
||||
webkitgtk4-devel \
|
||||
cryptopp-devel \
|
||||
libva-devel \
|
||||
libvdpau-devel \
|
||||
msgpack-devel \
|
||||
NetworkManager-libnm-devel \
|
||||
openssl-devel \
|
||||
clutter-devel \
|
||||
clutter-gtk-devel \
|
||||
libappindicator-gtk3-devel \
|
||||
libnotify-devel \
|
||||
libupnp-devel \
|
||||
qrencode-devel \
|
||||
libargon2-devel \
|
||||
libsndfile-devel \
|
||||
libdrm \
|
||||
gperf \
|
||||
bison \
|
||||
clang \
|
||||
clang-devel \
|
||||
llvm-devel \
|
||||
nodejs \
|
||||
flex \
|
||||
gstreamer1 gstreamer1-devel \
|
||||
gstreamer1-plugins-base-devel \
|
||||
gstreamer1-plugins-good \
|
||||
gstreamer1-plugins-bad-free-devel \
|
||||
nss-devel \
|
||||
libxcb* \
|
||||
libxkb* \
|
||||
libX11-devel \
|
||||
vulkan-devel \
|
||||
libXrender-devel \
|
||||
xcb-util-* \
|
||||
xz \
|
||||
xkeyboard-config \
|
||||
libnotify \
|
||||
wget \
|
||||
libstdc++-static \
|
||||
sqlite-devel \
|
||||
perl-generators \
|
||||
perl-English \
|
||||
libxshmfence-devel \
|
||||
ninja-build \
|
||||
clang \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3.10 \
|
||||
cups-devel \
|
||||
pipewire-devel
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
@ -1,117 +0,0 @@
|
||||
FROM opensuse/leap:15.5
|
||||
|
||||
RUN zypper --gpg-auto-import-keys refresh
|
||||
|
||||
RUN zypper --non-interactive install -y \
|
||||
dnf \
|
||||
dnf-command\(builddep\) \
|
||||
rpmdevtools \
|
||||
Mesa-dri-devel Mesa-dri \
|
||||
git \
|
||||
gcc10 \
|
||||
gcc10-c++ \
|
||||
rpm-build \
|
||||
tar \
|
||||
make \
|
||||
autoconf \
|
||||
automake \
|
||||
nasm \
|
||||
speexdsp-devel \
|
||||
libpulse-devel \
|
||||
libcanberra-devel \
|
||||
libcurl-devel \
|
||||
libtool \
|
||||
pcre-devel \
|
||||
yaml-cpp-devel \
|
||||
libXext-devel \
|
||||
libXfixes-devel \
|
||||
yasm \
|
||||
speex-devel \
|
||||
libgsm-devel \
|
||||
chrpath \
|
||||
check \
|
||||
astyle \
|
||||
gettext-devel \
|
||||
which \
|
||||
alsa-lib-devel \
|
||||
systemd-devel \
|
||||
libuuid-devel \
|
||||
uuid-devel \
|
||||
libopus-devel \
|
||||
patch \
|
||||
jsoncpp-devel \
|
||||
webkit2gtk3-devel \
|
||||
libcryptopp-devel \
|
||||
libva-devel \
|
||||
libvdpau-devel \
|
||||
msgpack-c-devel \
|
||||
msgpack-cxx-devel \
|
||||
clutter-devel \
|
||||
openssl-devel \
|
||||
clutter-gtk-devel \
|
||||
libnma-devel \
|
||||
libcryptopp-devel \
|
||||
libexpat-devel \
|
||||
gnome-icon-theme-symbolic \
|
||||
libgsm-devel \
|
||||
gtk3-devel \
|
||||
libappindicator-devel \
|
||||
sqlite-devel \
|
||||
ffmpeg-4-libavutil-devel \
|
||||
gtk3-devel\
|
||||
qrencode-devel \
|
||||
python310 \
|
||||
python3-python-dateutil \
|
||||
python3-html5lib \
|
||||
libsndfile-devel \
|
||||
libdrm \
|
||||
gperf \
|
||||
bison \
|
||||
flex \
|
||||
ffmpeg ffmpeg-devel \
|
||||
nodejs20 \
|
||||
mozilla-nss-devel \
|
||||
python-xml \
|
||||
python3-six \
|
||||
python3-importlib-metadata \
|
||||
libxcb* \
|
||||
libxkb* \
|
||||
libX11-devel \
|
||||
libXrender-devel \
|
||||
libfreetype6 \
|
||||
xcb-util-image-devel \
|
||||
xcb-util-keysyms-devel \
|
||||
xcb-util-renderutil-devel \
|
||||
xcb-util-wm-devel \
|
||||
xorg-x11-devel \
|
||||
xz \
|
||||
xkeyboard-config \
|
||||
libnotify \
|
||||
argon2-devel \
|
||||
libxshmfence-devel \
|
||||
xproto-devel \
|
||||
xcb-proto-devel \
|
||||
xcb-* \
|
||||
xorg-* \
|
||||
vulkan-devel \
|
||||
ninja \
|
||||
gstreamer-devel \
|
||||
gstreamer-plugins-good \
|
||||
gstreamer-plugins-bad-devel \
|
||||
gstreamer-plugins-base-devel \
|
||||
cmake \
|
||||
wget \
|
||||
pipewire-devel
|
||||
|
||||
# openSUSE Leap 15.5 comes with Python 3.6 by default,
|
||||
# but we need at least 3.7 to compile Qt 6.6.1
|
||||
RUN rm /usr/bin/python3 && ln -s /usr/bin/python3.10 /usr/bin/python3
|
||||
|
||||
RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 50
|
||||
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 50
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
ENV CC=gcc
|
||||
ENV CXX=g++
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
@ -36,12 +36,6 @@ RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/sna
|
||||
RUN mkdir -p /snap/snapcraft
|
||||
RUN unsquashfs -d /snap/snapcraft/current snapcraft.snap
|
||||
|
||||
# Fix Python3 installation: Make sure we use the interpreter from
|
||||
# the snapcraft snap:
|
||||
RUN unlink /snap/snapcraft/current/usr/bin/python3
|
||||
RUN ln -s /snap/snapcraft/current/usr/bin/python3.* /snap/snapcraft/current/usr/bin/python3
|
||||
RUN echo /snap/snapcraft/current/lib/python3.*/site-packages >> /snap/snapcraft/current/usr/lib/python3/dist-packages/site-packages.pth
|
||||
|
||||
# Create a snapcraft runner
|
||||
RUN mkdir -p /snap/bin
|
||||
RUN echo "#!/bin/sh" > /snap/bin/snapcraft
|
||||
|
||||
@ -24,7 +24,10 @@
|
||||
(list
|
||||
;; Minimal requirements of the daemon contrib build system.
|
||||
"coreutils"
|
||||
"gcc-toolchain"
|
||||
;; When using GCC 15, Jami fails to link with errors like:
|
||||
;; ld: CMakeFiles/jami.dir/src/app/main.cpp.o:(.rodata+0x0):
|
||||
;; multiple definition of `QtPrivate::IsFloatType_v<_Float16>'
|
||||
"gcc-toolchain@14"
|
||||
"git-minimal"
|
||||
"grep"
|
||||
"gzip"
|
||||
@ -42,18 +45,12 @@
|
||||
"alsa-lib"
|
||||
"autoconf"
|
||||
"automake"
|
||||
"asio"
|
||||
"bash"
|
||||
"bzip2"
|
||||
"cmake"
|
||||
"dbus"
|
||||
;; Bundled because broken with GCC 7 upstream (unmaintained). When
|
||||
;; attempting to use it, it would cause confusing errors such as
|
||||
;; "ld: ../src/.libs/libring.a(libupnpcontrol_la-upnp_context.o): in
|
||||
;; function `jami::upnp::UPnPContext::updateMappingList(bool)':
|
||||
;; upnp_context.cpp:(.text+0xa4be): undefined reference to
|
||||
;; `std::__cxx11::basic_ostringstream<char, std::char_traits<char>,
|
||||
;; std::allocator<char> >::basic_ostringstream()'
|
||||
;;"dbus-c++" ;for dbusxx-xml2cpp
|
||||
;;"dhtnet" ;bundled because tightly coupled
|
||||
"diffutils"
|
||||
"doxygen"
|
||||
"eudev" ;udev library
|
||||
@ -67,6 +64,7 @@
|
||||
"gsm"
|
||||
"gtk-doc"
|
||||
"http-parser"
|
||||
"jack@0"
|
||||
"jsoncpp"
|
||||
"libarchive"
|
||||
"libgit2"
|
||||
@ -84,16 +82,24 @@
|
||||
"patch"
|
||||
"pcre"
|
||||
"perl"
|
||||
"pipewire"
|
||||
;;"pjproject" ;bundled because patched
|
||||
"pulseaudio"
|
||||
"sdbus-c++@1"
|
||||
"speex"
|
||||
"speexdsp"
|
||||
"webrtc-audio-processing@0"
|
||||
"which"
|
||||
"yaml-cpp"
|
||||
"yasm"
|
||||
|
||||
;; For the Qt client.
|
||||
"glib"
|
||||
"hunspell"
|
||||
"libnotify"
|
||||
"libxcb"
|
||||
"libxkbcommon"
|
||||
"md4c"
|
||||
"network-manager" ;libnm
|
||||
"qrencode"
|
||||
"qtbase"
|
||||
@ -103,10 +109,13 @@
|
||||
"qtnetworkauth"
|
||||
"qtpositioning"
|
||||
"qtsvg"
|
||||
"qwindowkit"
|
||||
"qttools"
|
||||
"qtwebchannel"
|
||||
"qtwebengine"
|
||||
"tidy-html"
|
||||
"vulkan-headers"
|
||||
"zxing-cpp"
|
||||
|
||||
;; For tests and debugging.
|
||||
"file"
|
||||
|
||||
@ -91,6 +91,7 @@ override_dh_auto_build:
|
||||
mkdir build && \
|
||||
cd build && \
|
||||
cmake \
|
||||
-DBUILD_VERSION=$(BUILD_VERSION) \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DLIBJAMI_BUILD_DIR=$(CURDIR)/daemon/src \
|
||||
-DENABLE_LIBWRAP=true \
|
||||
|
||||
@ -49,7 +49,7 @@ BuildRequires: libXfixes-devel
|
||||
BuildRequires: libuuid-devel
|
||||
BuildRequires: libva-devel
|
||||
BuildRequires: libvdpau-devel
|
||||
BuildRequires: pcre-devel
|
||||
BuildRequires: (pcre-devel or pcre2-devel)
|
||||
BuildRequires: pipewire-devel
|
||||
BuildRequires: uuid-devel
|
||||
BuildRequires: yaml-cpp-devel
|
||||
|
||||
@ -31,6 +31,7 @@ URL: https://jami.net/
|
||||
Source: jami-libqt-%{version}.tar.xz
|
||||
Patch0: 0001-fix-gcc14.patch
|
||||
Patch1: 0002-qtwebengine-add-missing-chromium-dependencies.patch
|
||||
Patch2: 0003-fix-embree-linking-errors.patch
|
||||
|
||||
%global gst 0.10
|
||||
%if 0%{?fedora} || 0%{?rhel} > 7
|
||||
@ -68,6 +69,7 @@ This package contains Qt libraries for Jami.
|
||||
%setup -n qt-everywhere-src-%{version}
|
||||
%patch -P 0 -p1
|
||||
%patch -P 1 -p1
|
||||
%patch -P 2 -p1
|
||||
|
||||
%build
|
||||
echo "Building Qt using %{job_count} parallel jobs"
|
||||
|
||||
@ -1,16 +1,7 @@
|
||||
%define name jami
|
||||
%define version RELEASE_VERSION
|
||||
%define release 0
|
||||
|
||||
# The AppStream 1.0 spec says that the catalog file must be put in /usr/share/swcatalog/xml
|
||||
# (see https://www.freedesktop.org/software/appstream/docs/chap-CatalogData.html).
|
||||
#
|
||||
# However, openSUSE Leap still uses the legacy path /usr/share/app-info/xmls as of version 15.5.
|
||||
%if 0%{?sle_version} && 0%{?sle_version} <= 150500
|
||||
%define appstream_catalog_dir /share/app-info/xmls
|
||||
%else
|
||||
%define appstream_catalog_dir /share/swcatalog/xml
|
||||
%endif
|
||||
|
||||
# Exclude vendored Qt6 from dependency generator
|
||||
%define __requires_exclude ^libQt6.*$
|
||||
@ -82,6 +73,7 @@ cd %{_builddir}/jami-%{version} && \
|
||||
-DAPPSTREAM_CATALOG_DIR=%{appstream_catalog_dir} \
|
||||
-DWITH_DAEMON_SUBMODULE=true \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_VERSION=${BUILD_VERSION} \
|
||||
..
|
||||
make -C %{_builddir}/jami-%{version}/build %{_smp_mflags} V=2
|
||||
|
||||
@ -93,7 +85,7 @@ DESTDIR=%{buildroot} make -C %{_builddir}/jami-%{version}/build install V=2
|
||||
%{_bindir}/jami
|
||||
%{_datadir}/applications/net.jami.Jami.desktop
|
||||
%{_datadir}/jami/net.jami.Jami.desktop
|
||||
%{_datadir}/icons/hicolor/scalable/apps/jami.svg
|
||||
%{_datadir}/icons/hicolor/scalable/apps/net.jami.Jami.svg
|
||||
%{_datadir}/icons/hicolor/48x48/apps/jami.png
|
||||
%{_datadir}/pixmaps/jami.xpm
|
||||
%{_datadir}/metainfo/net.jami.Jami.metainfo.xml
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
From 709d0b0cf45b920f63960a70725138dbaf7ec721 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
|
||||
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
|
||||
Date: Wed, 18 Jun 2025 15:53:55 -0400
|
||||
Subject: [PATCH] Fix embree linking errors
|
||||
|
||||
Patch taken from:
|
||||
https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=37bd373cd33c36f8dd44e71be25fa6ea24cf4588
|
||||
---
|
||||
qtquick3d/src/3rdparty/embree/CMakeLists.txt | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/qtquick3d/src/3rdparty/embree/CMakeLists.txt b/qtquick3d/src/3rdparty/embree/CMakeLists.txt
|
||||
index cf27196de2..332bbd17ca 100644
|
||||
--- a/qtquick3d/src/3rdparty/embree/CMakeLists.txt
|
||||
+++ b/qtquick3d/src/3rdparty/embree/CMakeLists.txt
|
||||
@@ -62,6 +62,9 @@ if (IOS)
|
||||
endif()
|
||||
|
||||
# Use SSE2 only, ignore AVX/SSE4.2 for now
|
||||
+if (TEST_architecture_arch STREQUAL x86_64)
|
||||
+ qt_internal_extend_target(BundledEmbree COMPILE_OPTIONS -mno-avx -mno-sse4.2)
|
||||
+endif()
|
||||
qt_internal_extend_target(BundledEmbree DEFINES
|
||||
EMBREE_TARGET_SSE2
|
||||
__SSE2__
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@ -270,7 +270,7 @@ parts:
|
||||
ls
|
||||
snapcraftctl pull
|
||||
sed -i -E 's|(tmpName) << (PACKAGE_NAME << "_shm_")|\1 << "snap.jami." << \2|' ./daemon/src/media/video/sinkclient.cpp
|
||||
sed -i -E 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/scalable/apps/jami.svg|' extras/data/net.jami.Jami.desktop
|
||||
sed -i -E 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/scalable/apps/net.jami.Jami.svg|' extras/data/net.jami.Jami.desktop
|
||||
override-build: |
|
||||
$SNAPCRAFT_PART_BUILD/extras/packaging/gnu-linux/scripts/install-pipewire-from-source.sh
|
||||
|
||||
@ -293,7 +293,8 @@ parts:
|
||||
cmake .. -DENABLE_LIBWRAP=true \
|
||||
-DLIBJAMI_BUILD_DIR=$SNAPCRAFT_PART_BUILD/daemon/src \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_VERSION=BUILD_VERSION_PLACEHOLDER
|
||||
make -j$SNAPCRAFT_PARALLEL_BUILD_COUNT
|
||||
DESTDIR=$SNAPCRAFT_PART_INSTALL make install
|
||||
build-packages:
|
||||
|
||||
@ -101,16 +101,14 @@ if [ ! -f "${RPM_PATH}" ]; then
|
||||
# Cache the built Qt RPM package.
|
||||
if [[ "${DISTRIBUTION:0:4}" == "rhel" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el8.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_39" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc39.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_40" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc40.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_41" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc41.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_42" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc42.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "alma_9" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el9.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "alma_10" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el10.x86_64.rpm "${RPM_PATH}"
|
||||
else
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-*.rpm "${RPM_PATH}"
|
||||
fi
|
||||
@ -134,7 +132,7 @@ rpmbuild --define "debug_package %{nil}" -ba jami-libclient.spec
|
||||
rpmbuild --define "debug_package %{nil}" -ba jami-qt.spec
|
||||
|
||||
# Build the Qt client.
|
||||
rpmbuild --define "debug_package %{nil}" -ba jami.spec
|
||||
rpmbuild --define "debug_package %{nil}" --define "BUILD_VERSION ${BUILD_VERSION}" -ba jami.spec
|
||||
|
||||
# Move the built packages to the output directory.
|
||||
mv /root/rpmbuild/RPMS/*/* /opt/output
|
||||
|
||||
@ -29,6 +29,9 @@ cp -r extras/packaging/gnu-linux/rules/snap/${SNAP_PKG_NAME}/snapcraft.yaml .
|
||||
# set the version and tarball filename
|
||||
sed -i "s/RELEASE_VERSION/${RELEASE_VERSION}/g" snapcraft.yaml
|
||||
|
||||
# set the build version of the app
|
||||
sed -i "s/BUILD_VERSION_PLACEHOLDER/${BUILD_VERSION}/g" snapcraft.yaml
|
||||
|
||||
snapcraft # requires snapcraft >= 4.8
|
||||
|
||||
# move the built snap to output
|
||||
|
||||
@ -263,7 +263,7 @@ def cmake_build(config_str, env_vars, cmake_build_dir):
|
||||
return True
|
||||
|
||||
|
||||
def build(config_str, qt_dir, tests, enable_crash_reports, crash_report_url=None):
|
||||
def build(config_str, qt_dir, tests, build_version, enable_crash_reports, crash_report_url=None):
|
||||
"""Use cmake to build the project."""
|
||||
print("Building with Qt at " + qt_dir)
|
||||
|
||||
@ -294,6 +294,9 @@ def build(config_str, qt_dir, tests, enable_crash_reports, crash_report_url=None
|
||||
else:
|
||||
cmake_options.append("-DENABLE_CRASHREPORTS=OFF")
|
||||
|
||||
if build_version:
|
||||
cmake_options.append("-DBUILD_VERSION=" + build_version)
|
||||
|
||||
# Make sure the build directory exists.
|
||||
if not os.path.exists(build_dir):
|
||||
os.makedirs(build_dir)
|
||||
@ -307,11 +310,11 @@ def build(config_str, qt_dir, tests, enable_crash_reports, crash_report_url=None
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def deploy_runtimes(config_str, qt_dir):
|
||||
def deploy_runtimes(qt_dir):
|
||||
"""Deploy the dependencies to the runtime directory."""
|
||||
print("Deploying runtime dependencies")
|
||||
|
||||
runtime_dir = os.path.join(repo_root_dir, "x64", config_str)
|
||||
runtime_dir = os.path.join(repo_root_dir, "x64", "Release")
|
||||
stamp_file = os.path.join(runtime_dir, ".deploy.stamp")
|
||||
if os.path.exists(stamp_file):
|
||||
return
|
||||
@ -470,6 +473,8 @@ def parse_args():
|
||||
help='Sets the Qt root path')
|
||||
parser.add_argument(
|
||||
"-a", "--arch", default="x64", help="Sets the build architecture")
|
||||
parser.add_argument(
|
||||
"--build-version", help="Sets the build version string used for defining app build version")
|
||||
parser.add_argument(
|
||||
"-t", "--tests", action="store_true", help="Build and run tests")
|
||||
parser.add_argument(
|
||||
@ -552,10 +557,11 @@ def main():
|
||||
def do_build(do_tests):
|
||||
if not parsed_args.skip_build:
|
||||
build(config_str, parsed_args.qt, do_tests,
|
||||
parsed_args.build_version,
|
||||
parsed_args.enable_crash_reports,
|
||||
parsed_args.crash_report_url)
|
||||
if not parsed_args.skip_deploy:
|
||||
deploy_runtimes(config_str, parsed_args.qt)
|
||||
deploy_runtimes(parsed_args.qt)
|
||||
|
||||
if parsed_args.subcommand == "pack":
|
||||
do_build(False)
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
382
resources/misc/available_dictionaries.json
Normal file
382
resources/misc/available_dictionaries.json
Normal file
@ -0,0 +1,382 @@
|
||||
{
|
||||
"af_ZA": {
|
||||
"nativeName": "Afrikaans (Suid-Afrika)",
|
||||
"path": "af_ZA/af_ZA"
|
||||
},
|
||||
"an_ES": {
|
||||
"nativeName": "aragonés (España)",
|
||||
"path": "an_ES/an_ES"
|
||||
},
|
||||
"ar": {
|
||||
"nativeName": "العربية",
|
||||
"path": "ar/ar"
|
||||
},
|
||||
"as_IN": {
|
||||
"nativeName": "অসমীয়া (भारत)",
|
||||
"path": "as_IN/as_IN"
|
||||
},
|
||||
"be-official": {
|
||||
"nativeName": "беларуская",
|
||||
"path": "be_BY/be-official"
|
||||
},
|
||||
"bg_BG": {
|
||||
"nativeName": "български",
|
||||
"path": "bg_BG/bg_BG"
|
||||
},
|
||||
"bn_BD": {
|
||||
"nativeName": "বাঙ্গালি (বাংলাদেশ)",
|
||||
"path": "bn_BD/bn_BD"
|
||||
},
|
||||
"bn_IN": {
|
||||
"nativeName": "বাঙ্গালি (ভারত)",
|
||||
"path": "bn_BD/bn_IN"
|
||||
},
|
||||
"bo": {
|
||||
"nativeName": "བོད་སྐད་",
|
||||
"path": "bo/bo"
|
||||
},
|
||||
"br_FR": {
|
||||
"nativeName": "breton (France)",
|
||||
"path": "br_FR/br_FR"
|
||||
},
|
||||
"bs_BA": {
|
||||
"nativeName": "bosanski",
|
||||
"path": "bs_BA/bs_BA"
|
||||
},
|
||||
"cs_CZ": {
|
||||
"nativeName": "čeština",
|
||||
"path": "cs_CZ/cs_CZ"
|
||||
},
|
||||
"da_DK": {
|
||||
"nativeName": "dansk",
|
||||
"path": "da_DK/da_DK"
|
||||
},
|
||||
"de_AT_frami": {
|
||||
"nativeName": "Deutsch (Österreich)",
|
||||
"path": "de/de_AT_frami"
|
||||
},
|
||||
"de_CH_frami": {
|
||||
"nativeName": "Deutsch (Schweiz)",
|
||||
"path": "de/de_CH_frami"
|
||||
},
|
||||
"de_DE_frami": {
|
||||
"nativeName": "Deutsch (Deutschland)",
|
||||
"path": "de/de_DE_frami"
|
||||
},
|
||||
"el_GR": {
|
||||
"nativeName": "Ελληνικά",
|
||||
"path": "el_GR/el_GR"
|
||||
},
|
||||
"en_AU": {
|
||||
"nativeName": "English (Australia)",
|
||||
"path": "en/en_AU"
|
||||
},
|
||||
"en_CA": {
|
||||
"nativeName": "English (Canada)",
|
||||
"path": "en/en_CA"
|
||||
},
|
||||
"en_GB": {
|
||||
"nativeName": "English (British)",
|
||||
"path": "en/en_GB"
|
||||
},
|
||||
"en_US": {
|
||||
"nativeName": "English (American)",
|
||||
"path": "en/en_US"
|
||||
},
|
||||
"en_ZA": {
|
||||
"nativeName": "English (Suid-Afrika)",
|
||||
"path": "en/en_ZA"
|
||||
},
|
||||
"eo": {
|
||||
"nativeName": "esperanto",
|
||||
"path": "eo/eo"
|
||||
},
|
||||
"es_AR": {
|
||||
"nativeName": "español (Argentina)",
|
||||
"path": "es/es_AR"
|
||||
},
|
||||
"es_BO": {
|
||||
"nativeName": "español (Bolivia)",
|
||||
"path": "es/es_BO"
|
||||
},
|
||||
"es_CL": {
|
||||
"nativeName": "español (Chile)",
|
||||
"path": "es/es_CL"
|
||||
},
|
||||
"es_CO": {
|
||||
"nativeName": "español (Colombia)",
|
||||
"path": "es/es_CO"
|
||||
},
|
||||
"es_CR": {
|
||||
"nativeName": "español (Costa Rica)",
|
||||
"path": "es/es_CR"
|
||||
},
|
||||
"es_CU": {
|
||||
"nativeName": "español (Cuba)",
|
||||
"path": "es/es_CU"
|
||||
},
|
||||
"es_DO": {
|
||||
"nativeName": "español (República Dominicana)",
|
||||
"path": "es/es_DO"
|
||||
},
|
||||
"es_EC": {
|
||||
"nativeName": "español (Ecuador)",
|
||||
"path": "es/es_EC"
|
||||
},
|
||||
"es_ES": {
|
||||
"nativeName": "español (España)",
|
||||
"path": "es/es_ES"
|
||||
},
|
||||
"es_GQ": {
|
||||
"nativeName": "español (Guinea Ecuatorial)",
|
||||
"path": "es/es_GQ"
|
||||
},
|
||||
"es_GT": {
|
||||
"nativeName": "español (Guatemala)",
|
||||
"path": "es/es_GT"
|
||||
},
|
||||
"es_HN": {
|
||||
"nativeName": "español (Honduras)",
|
||||
"path": "es/es_HN"
|
||||
},
|
||||
"es_MX": {
|
||||
"nativeName": "español (México)",
|
||||
"path": "es/es_MX"
|
||||
},
|
||||
"es_NI": {
|
||||
"nativeName": "español (Nicaragua)",
|
||||
"path": "es/es_NI"
|
||||
},
|
||||
"es_PA": {
|
||||
"nativeName": "español (Panamá)",
|
||||
"path": "es/es_PA"
|
||||
},
|
||||
"es_PE": {
|
||||
"nativeName": "español (Perú)",
|
||||
"path": "es/es_PE"
|
||||
},
|
||||
"es_PH": {
|
||||
"nativeName": "español (Pilipinas)",
|
||||
"path": "es/es_PH"
|
||||
},
|
||||
"es_PR": {
|
||||
"nativeName": "español (Puerto Rico)",
|
||||
"path": "es/es_PR"
|
||||
},
|
||||
"es_PY": {
|
||||
"nativeName": "español (Paraguai)",
|
||||
"path": "es/es_PY"
|
||||
},
|
||||
"es_SV": {
|
||||
"nativeName": "español (El Salvador)",
|
||||
"path": "es/es_SV"
|
||||
},
|
||||
"es_US": {
|
||||
"nativeName": "español (United States)",
|
||||
"path": "es/es_US"
|
||||
},
|
||||
"es_UY": {
|
||||
"nativeName": "español (Uruguay)",
|
||||
"path": "es/es_UY"
|
||||
},
|
||||
"es_VE": {
|
||||
"nativeName": "español (Venezuela)",
|
||||
"path": "es/es_VE"
|
||||
},
|
||||
"et_EE": {
|
||||
"nativeName": "eesti",
|
||||
"path": "et_EE/et_EE"
|
||||
},
|
||||
"fa-IR": {
|
||||
"nativeName": "فارسی",
|
||||
"path": "fa_IR/fa-IR"
|
||||
},
|
||||
"fr": {
|
||||
"nativeName": "français",
|
||||
"path": "fr_FR/fr"
|
||||
},
|
||||
"gd_GB": {
|
||||
"nativeName": "Gàidhlig",
|
||||
"path": "gd_GB/gd_GB"
|
||||
},
|
||||
"gl_ES": {
|
||||
"nativeName": "galego",
|
||||
"path": "gl/gl_ES"
|
||||
},
|
||||
"gu_IN": {
|
||||
"nativeName": "ગુજરાતી",
|
||||
"path": "gu_IN/gu_IN"
|
||||
},
|
||||
"he_IL": {
|
||||
"nativeName": "עברית",
|
||||
"path": "he_IL/he_IL"
|
||||
},
|
||||
"hi_IN": {
|
||||
"nativeName": "हिन्दी",
|
||||
"path": "hi_IN/hi_IN"
|
||||
},
|
||||
"hr_HR": {
|
||||
"nativeName": "hrvatski",
|
||||
"path": "hr_HR/hr_HR"
|
||||
},
|
||||
"hu_HU": {
|
||||
"nativeName": "magyar",
|
||||
"path": "hu_HU/hu_HU"
|
||||
},
|
||||
"id_ID": {
|
||||
"nativeName": "Indonesian",
|
||||
"path": "id/id_ID"
|
||||
},
|
||||
"is": {
|
||||
"nativeName": "íslenska",
|
||||
"path": "is/is"
|
||||
},
|
||||
"it_IT": {
|
||||
"nativeName": "italiano",
|
||||
"path": "it_IT/it_IT"
|
||||
},
|
||||
"kmr_Latn": {
|
||||
"nativeName": "Northern Kurdish",
|
||||
"path": "kmr_Latn/kmr_Latn"
|
||||
},
|
||||
"kn_IN": {
|
||||
"nativeName": "ಕನ್ನಡ",
|
||||
"path": "kn_IN/kn_IN"
|
||||
},
|
||||
"ko_KR": {
|
||||
"nativeName": "한국어",
|
||||
"path": "ko_KR/ko_KR"
|
||||
},
|
||||
"lo_LA": {
|
||||
"nativeName": "ລາວ",
|
||||
"path": "lo_LA/lo_LA"
|
||||
},
|
||||
"lt": {
|
||||
"nativeName": "lietuvių",
|
||||
"path": "lt_LT/lt"
|
||||
},
|
||||
"lv_LV": {
|
||||
"nativeName": "latviešu",
|
||||
"path": "lv_LV/lv_LV"
|
||||
},
|
||||
"mn_MN": {
|
||||
"nativeName": "монгол",
|
||||
"path": "mn_MN/mn_MN"
|
||||
},
|
||||
"mr_IN": {
|
||||
"nativeName": "मराठी",
|
||||
"path": "mr_IN/mr_IN"
|
||||
},
|
||||
"nb_NO": {
|
||||
"nativeName": "norsk bokmål",
|
||||
"path": "no/nb_NO"
|
||||
},
|
||||
"ne_NP": {
|
||||
"nativeName": "नेपाली",
|
||||
"path": "ne_NP/ne_NP"
|
||||
},
|
||||
"nl_NL": {
|
||||
"nativeName": "Nederlands",
|
||||
"path": "nl_NL/nl_NL"
|
||||
},
|
||||
"nn_NO": {
|
||||
"nativeName": "norsk nynorsk",
|
||||
"path": "no/nn_NO"
|
||||
},
|
||||
"oc_FR": {
|
||||
"nativeName": "occitan",
|
||||
"path": "oc_FR/oc_FR"
|
||||
},
|
||||
"or_IN": {
|
||||
"nativeName": "ଓଡ଼ିଆ",
|
||||
"path": "or_IN/or_IN"
|
||||
},
|
||||
"pa_IN": {
|
||||
"nativeName": "ਪੰਜਾਬੀ",
|
||||
"path": "pa_IN/pa_IN"
|
||||
},
|
||||
"pl_PL": {
|
||||
"nativeName": "polski",
|
||||
"path": "pl_PL/pl_PL"
|
||||
},
|
||||
"pt_BR": {
|
||||
"nativeName": "português (Brasil)",
|
||||
"path": "pt_BR/pt_BR"
|
||||
},
|
||||
"pt_PT": {
|
||||
"nativeName": "português europeu (Portugal)",
|
||||
"path": "pt_PT/pt_PT"
|
||||
},
|
||||
"ro_RO": {
|
||||
"nativeName": "română",
|
||||
"path": "ro/ro_RO"
|
||||
},
|
||||
"ru_RU": {
|
||||
"nativeName": "русский",
|
||||
"path": "ru_RU/ru_RU"
|
||||
},
|
||||
"sa_IN": {
|
||||
"nativeName": "संस्कृत भाषा",
|
||||
"path": "sa_IN/sa_IN"
|
||||
},
|
||||
"si_LK": {
|
||||
"nativeName": "සිංහල",
|
||||
"path": "si_LK/si_LK"
|
||||
},
|
||||
"sk_SK": {
|
||||
"nativeName": "slovenčina",
|
||||
"path": "sk_SK/sk_SK"
|
||||
},
|
||||
"sl_SI": {
|
||||
"nativeName": "slovenščina",
|
||||
"path": "sl_SI/sl_SI"
|
||||
},
|
||||
"sq_AL": {
|
||||
"nativeName": "shqip",
|
||||
"path": "sq_AL/sq_AL"
|
||||
},
|
||||
"sr": {
|
||||
"nativeName": "српски",
|
||||
"path": "sr/sr"
|
||||
},
|
||||
"sr-Latn": {
|
||||
"nativeName": "srpski",
|
||||
"path": "sr/sr-Latn"
|
||||
},
|
||||
"sv_FI": {
|
||||
"nativeName": "svenska (Finland)",
|
||||
"path": "sv_SE/sv_FI"
|
||||
},
|
||||
"sv_SE": {
|
||||
"nativeName": "svenska (Sverige)",
|
||||
"path": "sv_SE/sv_SE"
|
||||
},
|
||||
"sw_TZ": {
|
||||
"nativeName": "Kiswahili",
|
||||
"path": "sw_TZ/sw_TZ"
|
||||
},
|
||||
"ta_IN": {
|
||||
"nativeName": "தமிழ்",
|
||||
"path": "ta_IN/ta_IN"
|
||||
},
|
||||
"te_IN": {
|
||||
"nativeName": "తెలుగు",
|
||||
"path": "te_IN/te_IN"
|
||||
},
|
||||
"th_TH": {
|
||||
"nativeName": "ไทย",
|
||||
"path": "th_TH/th_TH"
|
||||
},
|
||||
"tr_TR": {
|
||||
"nativeName": "Türkçe",
|
||||
"path": "tr_TR/tr_TR"
|
||||
},
|
||||
"uk_UA": {
|
||||
"nativeName": "українська",
|
||||
"path": "uk_UA/uk_UA"
|
||||
},
|
||||
"vi_VN": {
|
||||
"nativeName": "Tiếng Việt",
|
||||
"path": "vi/vi_VN"
|
||||
}
|
||||
}
|
||||
@ -17,7 +17,6 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtWebEngine
|
||||
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Enums 1.1
|
||||
@ -253,9 +252,15 @@ QtObject {
|
||||
isCallFullscreen = fullScreenItems
|
||||
.filter(o => o.item.objectName === "callViewLoader")
|
||||
.length
|
||||
isWebFullscreen = fullScreenItems
|
||||
.filter(o => o.item instanceof WebEngineView)
|
||||
.length
|
||||
isWebFullscreen = WITH_WEBENGINE ? fullScreenItems
|
||||
.filter(o => o.item && (
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.mediaPreview ||
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.videoPreview ||
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.map ||
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.general ||
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.emojiPicker
|
||||
))
|
||||
.length : 0
|
||||
}
|
||||
|
||||
// Listen for a hangup combined with a fullscreen call state and
|
||||
|
||||
@ -41,15 +41,20 @@ ApplicationWindow {
|
||||
|
||||
onActiveFocusItemChanged: {
|
||||
focusOverlay.margin = -5;
|
||||
if (activeFocusItem && ((activeFocusItem.focusReason === Qt.TabFocusReason) || (activeFocusItem.focusReason === Qt.BacktabFocusReason))) {
|
||||
if (activeFocusItem.focusOnChild) {
|
||||
focusOverlay.parent = activeFocusItem.parent;
|
||||
} else if (activeFocusItem.dontShowFocusState) {
|
||||
focusOverlay.parent = null;
|
||||
if (activeFocusItem) {
|
||||
const goodReasonToChangeFocus = activeFocusItem instanceof ItemDelegate || ((activeFocusItem.focusReason === Qt.TabFocusReason) || (activeFocusItem.focusReason === Qt.BacktabFocusReason));
|
||||
if (goodReasonToChangeFocus) {
|
||||
if (activeFocusItem.focusOnChild) {
|
||||
focusOverlay.parent = activeFocusItem.parent;
|
||||
} else if (activeFocusItem.dontShowFocusState) {
|
||||
focusOverlay.parent = null;
|
||||
} else {
|
||||
if (activeFocusItem.showFocusMargin)
|
||||
focusOverlay.margin = 0;
|
||||
focusOverlay.parent = activeFocusItem;
|
||||
}
|
||||
} else {
|
||||
if (activeFocusItem.showFocusMargin)
|
||||
focusOverlay.margin = 0;
|
||||
focusOverlay.parent = activeFocusItem;
|
||||
focusOverlay.parent = null;
|
||||
}
|
||||
} else {
|
||||
focusOverlay.parent = null;
|
||||
@ -292,9 +297,9 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
Connections {
|
||||
target: UtilsAdapter
|
||||
function onRaiseWhenCalled() {
|
||||
function onRaiseWhenCalledChanged() {
|
||||
raiseWhenCalled = AppSettingsManager.getValue(Settings.RaiseWhenCalled);
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ extern const QString defaultDownloadPath;
|
||||
X(WindowState, QWindow::AutomaticVisibility) \
|
||||
X(EnableExperimentalSwarm, false) \
|
||||
X(LANG, "SYSTEM") \
|
||||
X(SpellLang, "NONE") \
|
||||
X(SpellLang, {}) \
|
||||
X(EnableSpellCheck, true) \
|
||||
X(PluginStoreEndpoint, "https://plugins.jami.net") \
|
||||
X(PositionShareDuration, 15) \
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
#include "appversionmanager.h"
|
||||
|
||||
#include "lrcinstance.h"
|
||||
#include "version.h"
|
||||
#include "version_info.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
@ -73,7 +73,7 @@ struct AppVersionManager::Impl : public QObject
|
||||
Q_EMIT parent_.updateCheckReplyReceived(false);
|
||||
return;
|
||||
}
|
||||
auto currentVersion = QString(VERSION_STRING).toULongLong();
|
||||
auto currentVersion = BUILD_VERSION_STRING.toULongLong();
|
||||
auto latestVersion = latestVersionString.toULongLong();
|
||||
const QString channelStr = isBeta ? "beta" : "stable";
|
||||
const auto newVersionFound = latestVersion > currentVersion;
|
||||
|
||||
@ -249,7 +249,7 @@ CallAdapter::onCallEnded(const QString& callId)
|
||||
}
|
||||
|
||||
void
|
||||
CallAdapter::onCallStatusChanged(const QString& callId, int code)
|
||||
CallAdapter::onCallStatusChanged(const QString& accountId, const QString& callId, int code)
|
||||
{
|
||||
Q_UNUSED(code)
|
||||
|
||||
@ -468,11 +468,14 @@ CallAdapter::onShowIncomingCallView(const QString& accountId, const QString& con
|
||||
showNotification(accountId, convInfo.uid);
|
||||
return;
|
||||
}
|
||||
if (!accountProperties.denySecondCall) {
|
||||
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
||||
}
|
||||
} else {
|
||||
// finally, in this case, the conversation isn't selected yet
|
||||
// and there are no other special conditions, so just select the conversation
|
||||
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
||||
}
|
||||
|
||||
// finally, in this case, the conversation isn't selected yet
|
||||
// and there are no other special conditions, so just select the conversation
|
||||
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
||||
}
|
||||
|
||||
void
|
||||
@ -563,7 +566,7 @@ CallAdapter::connectCallModel(const QString& accountId)
|
||||
connect(accInfo.callModel.get(),
|
||||
&CallModel::callStatusChanged,
|
||||
this,
|
||||
QOverload<const QString&, int>::of(&CallAdapter::onCallStatusChanged),
|
||||
QOverload<const QString&, const QString&, int>::of(&CallAdapter::onCallStatusChanged),
|
||||
Qt::UniqueConnection);
|
||||
|
||||
connect(accInfo.callModel.get(),
|
||||
|
||||
@ -118,7 +118,7 @@ public Q_SLOTS:
|
||||
void onShowCallView(const QString& accountId, const QString& convUid);
|
||||
void onAccountChanged();
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId);
|
||||
void onCallStatusChanged(const QString& callId, int code);
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId, int code);
|
||||
void onCallAddedToConference(const QString& callId, const QString& conversationId, const QString& confId);
|
||||
void onCallStarted(const QString& callId);
|
||||
void onCallEnded(const QString& callId);
|
||||
|
||||
@ -135,7 +135,7 @@ PendingConferenceesListModel::connectSignals()
|
||||
callsStatusChanged_ = connect(currentCallModel,
|
||||
&CallModel::callStatusChanged,
|
||||
this,
|
||||
[this](const QString&, int) {
|
||||
[this](const QString&, const QString&, int) {
|
||||
Q_EMIT dataChanged(index(0, 0),
|
||||
index(rowCount() - 1),
|
||||
{Role::CallStatus});
|
||||
@ -401,6 +401,16 @@ CallOverlayModel::eventFilter(QObject* object, QEvent* event)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tab or BackTab key events should trigger a signal that we can use to
|
||||
// prevent the overlay from fading and to allow the user to navigate
|
||||
// through the controls.
|
||||
if (event->type() == QEvent::KeyPress && (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Tab)
|
||||
|| (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Backtab)) {
|
||||
Q_EMIT focusKeyPressed();
|
||||
// Don't absorb the event so that the focus can be changed
|
||||
// to the next or previous control.
|
||||
return false;
|
||||
}
|
||||
#ifndef HAVE_GLOBAL_PTT
|
||||
else if (event->type() == QEvent::KeyPress && listener_->getPttState()) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
|
||||
@ -140,6 +140,7 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void mouseMoved(QQuickItem* item);
|
||||
void focusKeyPressed();
|
||||
void pttKeyPressed();
|
||||
void pttKeyReleased();
|
||||
|
||||
|
||||
@ -78,8 +78,10 @@ Popup {
|
||||
contentItem: ColumnLayout {
|
||||
id: contentLayout
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: closeButton
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: JamiStrings.close
|
||||
|
||||
visible: closeButtonVisible
|
||||
|
||||
@ -117,7 +119,7 @@ Popup {
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredHeight: Math.min(contentHeight, root.height)
|
||||
Layout.preferredWidth: contentItem.childrenRect.width
|
||||
Layout.preferredWidth: contentItem.childrenRect.width + ScrollBar.vertical.width
|
||||
Layout.leftMargin: popupMargins
|
||||
Layout.rightMargin: popupMargins
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
@ -23,12 +23,12 @@ import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
SBSMessageBase {
|
||||
id: root
|
||||
id: rootDelegate
|
||||
|
||||
property var confId: ConfId
|
||||
property var currentCallId: CurrentCall.id
|
||||
component JoinCallButton: MaterialButton {
|
||||
visible: root.isActive && root.currentCallId !== root.confId
|
||||
visible: rootDelegate.isActive && rootDelegate.currentCallId !== rootDelegate.confId
|
||||
toolTipText: JamiStrings.joinCall
|
||||
color: JamiTheme.blackColor
|
||||
background.opacity: hovered ? 0.2 : 0.1
|
||||
@ -40,6 +40,20 @@ SBSMessageBase {
|
||||
textRightPadding: 9
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.StaticText
|
||||
Accessible.name: {
|
||||
let name = isOutgoing ? JamiStrings.inReplyToYou : UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return name + ": " + callLabel.text + " " + formattedDay;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (bubble.isEdited)
|
||||
status += JamiStrings.edited + " ";
|
||||
return status + (readers.length > 0 ? JamiStrings.readBy + " " + readers.map(function (uri) {
|
||||
return UtilsAdapter.getBestNameForUri(CurrentAccount.id, uri);
|
||||
}).join(", ") : "");
|
||||
}
|
||||
|
||||
property bool isRemoteImage
|
||||
|
||||
isOutgoing: Author === CurrentAccount.uri
|
||||
@ -48,17 +62,17 @@ SBSMessageBase {
|
||||
formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
|
||||
|
||||
bubble.border.color: CurrentConversation.color
|
||||
bubble.border.width: root.isActive ? 1.5 : 0
|
||||
bubble.border.width: rootDelegate.isActive ? 1.5 : 0
|
||||
bubble.color: JamiTheme.messageInBgColor
|
||||
bubble.opacity: 1
|
||||
|
||||
Connections {
|
||||
target: CurrentConversation
|
||||
enabled: root.isActive
|
||||
enabled: rootDelegate.isActive
|
||||
|
||||
function onActiveCallsChanged() {
|
||||
root.isActive = LRCInstance.indexOfActiveCall(root.confId, ActionUri, DeviceId) !== -1;
|
||||
if (root.isActive) {
|
||||
rootDelegate.isActive = LRCInstance.indexOfActiveCall(rootDelegate.confId, ActionUri, DeviceId) !== -1;
|
||||
if (rootDelegate.isActive) {
|
||||
bubble.mask.border.color = CurrentConversation.color;
|
||||
bubble.mask.border.width = 1.5;
|
||||
bubble.mask.z = -2;
|
||||
@ -66,8 +80,8 @@ SBSMessageBase {
|
||||
}
|
||||
}
|
||||
|
||||
property bool isActive: LRCInstance.indexOfActiveCall(root.confId, ActionUri, DeviceId) !== -1
|
||||
visible: isActive || root.confId === "" || Duration > 0
|
||||
property bool isActive: LRCInstance.indexOfActiveCall(rootDelegate.confId, ActionUri, DeviceId) !== -1
|
||||
visible: isActive || rootDelegate.confId === "" || Duration > 0
|
||||
|
||||
property var baseColor: JamiTheme.messageInBgColor
|
||||
|
||||
@ -76,7 +90,7 @@ SBSMessageBase {
|
||||
id: msg
|
||||
anchors.right: isOutgoing ? parent.right : undefined
|
||||
spacing: 10
|
||||
visible: root.visible
|
||||
visible: rootDelegate.visible
|
||||
|
||||
Image {
|
||||
id: statusIcon
|
||||
@ -84,10 +98,10 @@ SBSMessageBase {
|
||||
width: 10
|
||||
height: 10
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
visible: !root.isActive
|
||||
visible: !rootDelegate.isActive
|
||||
|
||||
source: {
|
||||
if (root.isOutgoing) {
|
||||
if (rootDelegate.isOutgoing) {
|
||||
if (Duration > 0)
|
||||
return "qrc:/icons/outgoing-call.svg";
|
||||
else
|
||||
@ -104,12 +118,11 @@ SBSMessageBase {
|
||||
effect: ColorOverlay {
|
||||
color: {
|
||||
if (Duration > 0)
|
||||
return UtilsAdapter.luma(root.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
|
||||
return JamiTheme.redColor
|
||||
return UtilsAdapter.luma(rootDelegate.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
|
||||
return JamiTheme.redColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
@ -120,11 +133,11 @@ SBSMessageBase {
|
||||
bottomPadding: 8
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: root.isActive && root.currentCallId !== root.confId ? 0 : root.timeWidth + 16
|
||||
Layout.leftMargin: root.isActive ? 10 : -5 /* spacing is 10 and we want 5px with icon */
|
||||
Layout.rightMargin: rootDelegate.isActive && rootDelegate.currentCallId !== rootDelegate.confId ? 0 : rootDelegate.timeWidth + 16
|
||||
Layout.leftMargin: rootDelegate.isActive ? 10 : -5 /* spacing is 10 and we want 5px with icon */
|
||||
|
||||
text: {
|
||||
if (root.isActive)
|
||||
if (rootDelegate.isActive)
|
||||
return JamiStrings.startedACall;
|
||||
return Body;
|
||||
}
|
||||
@ -136,7 +149,7 @@ SBSMessageBase {
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: Text.MarkdownText
|
||||
|
||||
color: UtilsAdapter.luma(root.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
|
||||
color: UtilsAdapter.luma(rootDelegate.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
|
||||
}
|
||||
|
||||
JoinCallButton {
|
||||
@ -146,7 +159,7 @@ SBSMessageBase {
|
||||
Layout.bottomMargin: 4
|
||||
|
||||
text: JamiStrings.joinWithAudio
|
||||
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, root.confId, true)
|
||||
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, rootDelegate.confId, true)
|
||||
}
|
||||
|
||||
JoinCallButton {
|
||||
@ -156,20 +169,20 @@ SBSMessageBase {
|
||||
Layout.topMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
|
||||
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, root.confId)
|
||||
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, rootDelegate.confId)
|
||||
Layout.rightMargin: 4
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
opacity: 0
|
||||
Behavior on opacity {
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
bubble.timestampItem.visible = !root.isActive || root.currentCallId === root.confId;
|
||||
bubble.timestampItem.visible = !rootDelegate.isActive || rootDelegate.currentCallId === rootDelegate.confId;
|
||||
opacity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,8 +21,8 @@ import net.jami.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
Column {
|
||||
id: root
|
||||
Control {
|
||||
id: rootDelegate
|
||||
|
||||
property bool showTime: false
|
||||
property bool showDay: false
|
||||
@ -36,21 +36,41 @@ Column {
|
||||
height: timestampItem.height + textLabel.height
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Accessible.name: {
|
||||
let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return name + ": " + Body + " " + formattedTime + " " + formattedDay;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (IsLastSent)
|
||||
status += JamiStrings.sent + " ";
|
||||
return status;
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: focusIndicator
|
||||
visible: rootDelegate.activeFocus
|
||||
border.color: JamiTheme.tintedBlue
|
||||
border.width: 2
|
||||
radius: 10
|
||||
color: "transparent"
|
||||
z: 1
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: timestampItem.height + textLabel.height
|
||||
|
||||
TimestampInfo {
|
||||
id: timestampItem
|
||||
|
||||
showDay: root.showDay
|
||||
showTime: root.showTime
|
||||
formattedTime: root.formattedTime
|
||||
formattedDay: root.formattedDay
|
||||
showDay: rootDelegate.showDay
|
||||
showTime: rootDelegate.showTime
|
||||
formattedTime: rootDelegate.formattedTime
|
||||
formattedDay: rootDelegate.formattedDay
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
|
||||
}
|
||||
|
||||
Label {
|
||||
@ -67,7 +87,7 @@ Column {
|
||||
}
|
||||
}
|
||||
opacity: 0
|
||||
Behavior on opacity {
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
|
||||
@ -24,20 +24,32 @@ import net.jami.Constants 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
id: rootDelegate
|
||||
|
||||
property var mediaInfo
|
||||
property bool showTime
|
||||
property bool showDay
|
||||
property int timestamp: Timestamp
|
||||
property string formattedTime: MessagesAdapter.getFormattedTime(root.timestamp)
|
||||
property string formattedDay: MessagesAdapter.getFormattedDay(root.timestamp)
|
||||
property string formattedTime: MessagesAdapter.getFormattedTime(rootDelegate.timestamp)
|
||||
property string formattedDay: MessagesAdapter.getFormattedDay(rootDelegate.timestamp)
|
||||
|
||||
property int seq: MsgSeq.single
|
||||
property string author: Author
|
||||
property string body: Body
|
||||
property var tid: TID
|
||||
property int transferStatus: TransferStatus
|
||||
|
||||
Accessible.name: {
|
||||
let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return JamiStrings.dataTransfer + name + ": " + JamiStrings.status + TransferStatus + Body + " " + formattedTime + " " + formattedDay;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (IsLastSent)
|
||||
status += JamiStrings.sent + " ";
|
||||
return status;
|
||||
}
|
||||
|
||||
onTidChanged: {
|
||||
if (tid === "") {
|
||||
sourceComponent = deletedMsgComp;
|
||||
@ -48,7 +60,7 @@ Loader {
|
||||
sourceComponent = deletedMsgComp;
|
||||
return;
|
||||
} else if (transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED) {
|
||||
mediaInfo = MessagesAdapter.getMediaInfo(root.body);
|
||||
mediaInfo = MessagesAdapter.getMediaInfo(rootDelegate.body);
|
||||
if (Object.keys(mediaInfo).length !== 0 && WITH_WEBENGINE) {
|
||||
sourceComponent = localMediaMsgComp;
|
||||
return;
|
||||
@ -74,13 +86,13 @@ Loader {
|
||||
id: deletedItem
|
||||
|
||||
isOutgoing: Author === CurrentAccount.uri
|
||||
showTime: root.showTime
|
||||
seq: root.seq
|
||||
showTime: rootDelegate.showTime
|
||||
seq: rootDelegate.seq
|
||||
author: Author
|
||||
readers: Readers
|
||||
timestamp: root.timestamp
|
||||
formattedTime: root.formattedTime
|
||||
formattedDay: root.formattedTime
|
||||
timestamp: rootDelegate.timestamp
|
||||
formattedTime: rootDelegate.formattedTime
|
||||
formattedDay: rootDelegate.formattedTime
|
||||
extraHeight: 0
|
||||
textContentWidth: textEditId.width
|
||||
textContentHeight: textEditId.height
|
||||
@ -122,34 +134,34 @@ Loader {
|
||||
id: dataTransferItem
|
||||
|
||||
transferId: Id
|
||||
property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus)
|
||||
property bool canOpen: root.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing
|
||||
property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth - buttonsLoader.width - 24 - 6 - 24
|
||||
property var transferStats: MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus)
|
||||
property bool canOpen: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing
|
||||
property real maxMsgWidth: rootDelegate.width - senderMargin - 2 * hPadding - avatarBlockWidth - buttonsLoader.width - 24 - 6 - 24
|
||||
|
||||
// Timer to update the translation bar
|
||||
Loader {
|
||||
id: timerLoader
|
||||
active: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
|
||||
active: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
|
||||
sourceComponent: Timer {
|
||||
interval: 1000 // Update every second
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
transferStats = MessagesAdapter.getTransferStats(transferId, root.transferStatus);
|
||||
transferStats = MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isOutgoing: Author === CurrentAccount.uri
|
||||
showTime: root.showTime
|
||||
seq: root.seq
|
||||
showTime: rootDelegate.showTime
|
||||
seq: rootDelegate.seq
|
||||
author: Author
|
||||
location: Body
|
||||
transferName: TransferName
|
||||
readers: Readers
|
||||
timestamp: root.timestamp
|
||||
formattedTime: root.formattedTime
|
||||
formattedDay: root.formattedTime
|
||||
timestamp: rootDelegate.timestamp
|
||||
formattedTime: rootDelegate.formattedTime
|
||||
formattedDay: rootDelegate.formattedTime
|
||||
extraHeight: progressBar.visible ? 25 : 0
|
||||
|
||||
innerContent.children: [
|
||||
@ -178,7 +190,7 @@ Loader {
|
||||
Layout.margins: 8
|
||||
|
||||
sourceComponent: {
|
||||
switch (root.transferStatus) {
|
||||
switch (rootDelegate.transferStatus) {
|
||||
case Interaction.TransferStatus.TRANSFER_CREATED:
|
||||
case Interaction.TransferStatus.TRANSFER_FINISHED:
|
||||
iconSource = JamiResources.link_black_24dp_svg;
|
||||
@ -225,7 +237,7 @@ Loader {
|
||||
normalColor: JamiTheme.chatviewBgColor
|
||||
imageColor: JamiTheme.chatviewButtonColor
|
||||
onClicked: {
|
||||
if (root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) {
|
||||
if (rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) {
|
||||
MessagesAdapter.cancelFile(transferId);
|
||||
} else {
|
||||
buttonsLoader.iconSource = JamiResources.connecting_black_24dp_svg;
|
||||
@ -287,7 +299,7 @@ Loader {
|
||||
ProgressBar {
|
||||
id: progressBar
|
||||
|
||||
visible: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
|
||||
visible: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
|
||||
height: visible * implicitHeight
|
||||
value: transferStats.progress / transferStats.totalSize
|
||||
width: transferItem.width
|
||||
@ -305,15 +317,15 @@ Loader {
|
||||
|
||||
isOutgoing: Author === CurrentAccount.uri
|
||||
transferId: Id
|
||||
property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus)
|
||||
showTime: root.showTime
|
||||
seq: root.seq
|
||||
property var transferStats: MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus)
|
||||
showTime: rootDelegate.showTime
|
||||
seq: rootDelegate.seq
|
||||
author: Author
|
||||
location: Body
|
||||
transferName: TransferName
|
||||
readers: Readers
|
||||
formattedTime: MessagesAdapter.getFormattedTime(root.timestamp)
|
||||
formattedDay: MessagesAdapter.getFormattedDay(root.timestamp)
|
||||
formattedTime: MessagesAdapter.getFormattedTime(rootDelegate.timestamp)
|
||||
formattedDay: MessagesAdapter.getFormattedDay(rootDelegate.timestamp)
|
||||
|
||||
property real contentWidth
|
||||
|
||||
|
||||
370
src/app/commoncomponents/DictionaryInstallView.qml
Normal file
370
src/app/commoncomponents/DictionaryInstallView.qml
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import net.jami.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import "../mainview/components"
|
||||
import "../settingsview/components"
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
// Search bar for filtering dictionaries
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 0
|
||||
property int checkBoxWidth: 24
|
||||
|
||||
Component.onCompleted: Qt.callLater(dictionarySearchBar.setTextAreaFocus)
|
||||
|
||||
RowLayout {
|
||||
id: headerLayout
|
||||
width: parent.width
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
|
||||
// Header title
|
||||
Searchbar {
|
||||
id: dictionarySearchBar
|
||||
|
||||
focus: true
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 55
|
||||
|
||||
placeHolderText: JamiStrings.searchTextLanguages
|
||||
Accessible.name: JamiStrings.searchTextLanguages
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.description: JamiStrings.searchAvailableTextLanguages
|
||||
|
||||
onSearchBarTextChanged: function (text) {
|
||||
dictionaryProxyModel.combinedFilterPattern = text;
|
||||
dictionaryProxyModel.invalidate();
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: JamiStrings.showInstalledDictionaries
|
||||
color: JamiTheme.faddedLastInteractionFontColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
|
||||
Layout.rightMargin: 0
|
||||
Layout.preferredHeight: 16
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
// Checkbox to filter installed dictionaries
|
||||
CheckBox {
|
||||
id: showInstalledOnlyCheckbox
|
||||
Accessible.name: JamiStrings.showInstalledDictionaries
|
||||
Accessible.role: Accessible.CheckBox
|
||||
Accessible.description: JamiStrings.showInstalledDictionariesDescription
|
||||
checked: false
|
||||
indicator: Image {
|
||||
anchors.centerIn: parent
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: JamiTheme.tintedBlue
|
||||
}
|
||||
mipmap: false
|
||||
smooth: true
|
||||
}
|
||||
width: checkBoxWidth
|
||||
height: checkBoxWidth
|
||||
source: showInstalledOnlyCheckbox.checked ? JamiResources.check_box_24dp_svg : JamiResources.check_box_outline_blank_24dp_svg
|
||||
}
|
||||
|
||||
Layout.preferredWidth: 55
|
||||
Layout.preferredHeight: 55
|
||||
Layout.rightMargin: 0
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to listen for download failure and pop a simple dialog to inform the user
|
||||
Connections {
|
||||
target: SpellCheckAdapter
|
||||
function onDownloadFailed(locale) {
|
||||
viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
|
||||
"title": JamiStrings.error,
|
||||
"infoText": JamiStrings.spellCheckDownloadFailed.arg(locale),
|
||||
"buttonTitles": [JamiStrings.optionOk],
|
||||
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue],
|
||||
"buttonRoles": [DialogButtonBox.AcceptRole]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
JamiListView {
|
||||
id: spellCheckDictionaryListView
|
||||
|
||||
width: parent.width
|
||||
Layout.fillHeight: true
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: dictionaryProxyModel
|
||||
sourceModel: SpellCheckAdapter.getDictionaryListModel()
|
||||
|
||||
property string combinedFilterPattern
|
||||
|
||||
filters: AllOf {
|
||||
AnyOf {
|
||||
// Filter by dictionary name
|
||||
RegExpFilter {
|
||||
roleName: "Locale"
|
||||
pattern: dictionaryProxyModel.combinedFilterPattern
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
// Filter by native name
|
||||
RegExpFilter {
|
||||
roleName: "NativeName"
|
||||
pattern: dictionaryProxyModel.combinedFilterPattern
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
}
|
||||
ValueFilter {
|
||||
roleName: "Installed"
|
||||
value: true
|
||||
enabled: showInstalledOnlyCheckbox.checked
|
||||
}
|
||||
}
|
||||
|
||||
sorters: [
|
||||
// Sort by locale alphabetically
|
||||
RoleSorter {
|
||||
roleName: "Locale"
|
||||
sortOrder: Qt.AscendingOrder
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
readonly property int itemMargins: 20
|
||||
topMargin: itemMargins / 2
|
||||
bottomMargin: itemMargins / 2
|
||||
|
||||
spacing: 8
|
||||
clip: true
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: dictionaryDelegate
|
||||
width: spellCheckDictionaryListView.width
|
||||
height: Math.max(JamiTheme.preferredFieldHeight, contentLayout.implicitHeight + 32)
|
||||
|
||||
background: Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width - spellCheckDictionaryListView.itemMargins
|
||||
height: parent.height
|
||||
color: JamiTheme.backgroundColor
|
||||
radius: JamiTheme.primaryRadius
|
||||
border.color: "transparent"
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 16
|
||||
|
||||
// Dictionary info
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: 16
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
id: dictionaryName
|
||||
Layout.fillWidth: true
|
||||
text: model.NativeName || ""
|
||||
color: JamiTheme.textColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
id: dictionaryLocale
|
||||
Layout.fillWidth: true
|
||||
text: model.Locale || ""
|
||||
color: JamiTheme.faddedLastInteractionFontColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize - 2
|
||||
elide: Text.ElideRight
|
||||
visible: text !== ""
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
// Installation status and action
|
||||
Item {
|
||||
Layout.preferredWidth: 100
|
||||
Layout.preferredHeight: 32
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.rightMargin: 16
|
||||
|
||||
// Install button for available dictionaries
|
||||
MaterialButton {
|
||||
id: installButton
|
||||
anchors.centerIn: parent
|
||||
width: 100
|
||||
height: 32
|
||||
Accessible.name: dictionaryName.text + " " + JamiStrings.install
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
text: JamiStrings.install
|
||||
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize - 1
|
||||
font.weight: Font.Medium
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
KeyNavigation.tab: {
|
||||
try {
|
||||
if (model.index < dictionaryProxyModel.count - 1) {
|
||||
var nextItem = spellCheckDictionaryListView.itemAtIndex(model.index + 1);
|
||||
if (nextItem) {
|
||||
var nextButton = nextItem.findChild("installButton") || nextItem.findChild("uninstallButton");
|
||||
return nextButton || null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug("KeyNavigation error handled:", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
spellCheckDictionaryListView.positionViewAtIndex(model.index, ListView.Contain);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (model.Locale) {
|
||||
SpellCheckAdapter.installDictionary(model.Locale);
|
||||
}
|
||||
}
|
||||
|
||||
visible: !model.Downloading && !model.Installed && model.Locale !== undefined && model.Locale !== ""
|
||||
}
|
||||
|
||||
// Uninstall button for installed dictionaries (not system dictionaries)
|
||||
MaterialButton {
|
||||
id: uninstallButton
|
||||
anchors.centerIn: parent
|
||||
width: 100
|
||||
height: 32
|
||||
|
||||
Accessible.name: dictionaryName.text + " " + JamiStrings.uninstall
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
text: JamiStrings.uninstall
|
||||
color: "#ff6666"
|
||||
hoveredColor: "#ff9999"
|
||||
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize - 1
|
||||
font.weight: Font.Medium
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
KeyNavigation.tab: {
|
||||
try {
|
||||
if (model.index < dictionaryProxyModel.count - 1) {
|
||||
var nextItem = spellCheckDictionaryListView.itemAtIndex(model.index + 1);
|
||||
if (nextItem) {
|
||||
var nextButton = nextItem.findChild("installButton") || nextItem.findChild("uninstallButton");
|
||||
return nextButton || null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug("KeyNavigation error handled:", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
spellCheckDictionaryListView.positionViewAtIndex(model.index, ListView.Contain);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (model.Locale) {
|
||||
SpellCheckAdapter.uninstallDictionary(model.Locale);
|
||||
}
|
||||
}
|
||||
|
||||
visible: !model.Downloading && model.Installed && !model.IsSystem && model.Locale !== undefined && model.Locale !== ""
|
||||
}
|
||||
|
||||
// System dictionary indicator
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: JamiStrings.systemDictionary
|
||||
color: JamiTheme.faddedLastInteractionFontColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize - 2
|
||||
visible: model.IsSystem
|
||||
}
|
||||
|
||||
// Downloading status indicator
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
visible: model.Downloading
|
||||
running: model.Downloading
|
||||
width: 24
|
||||
height: 24
|
||||
|
||||
// Use a custom animation for better UX
|
||||
Behavior on running {
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Empty state for when no dictionaries are found
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
visible: dictionaryProxyModel.count === 0
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 16
|
||||
width: parent.width * 0.8
|
||||
|
||||
// Big books emoji
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "📚"
|
||||
font.pixelSize: 48
|
||||
opacity: 0.3
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: dictionarySearchBar.textContent.length > 0 ? JamiStrings.noDictionariesFoundFor.arg(dictionarySearchBar.textContent) : JamiStrings.noDictionariesAvailable
|
||||
color: JamiTheme.faddedLastInteractionFontColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,7 @@ import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
Column {
|
||||
id: root
|
||||
id: rootDelegate
|
||||
|
||||
property bool showTime: false
|
||||
property bool showDay: false
|
||||
@ -34,6 +34,18 @@ Column {
|
||||
spacing: 2
|
||||
topPadding: 12
|
||||
bottomPadding: 12
|
||||
|
||||
Accessible.name: {
|
||||
let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return name + ": " + Body + " " + formattedTime + " " + formattedDay;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (IsLastSent)
|
||||
status += JamiStrings.sent + " ";
|
||||
return status;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
width: parent.width
|
||||
@ -42,10 +54,10 @@ Column {
|
||||
TimestampInfo {
|
||||
id: timestampItem
|
||||
|
||||
showDay: root.showDay
|
||||
showTime: root.showTime
|
||||
formattedTime: root.formattedTime
|
||||
formattedDay: root.formattedDay
|
||||
showDay: rootDelegate.showDay
|
||||
showTime: rootDelegate.showTime
|
||||
formattedTime: rootDelegate.formattedTime
|
||||
formattedDay: rootDelegate.formattedDay
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
@ -60,7 +72,7 @@ Column {
|
||||
}
|
||||
|
||||
opacity: 0
|
||||
Behavior on opacity {
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
|
||||
@ -28,6 +28,10 @@ Control {
|
||||
property string title: ""
|
||||
property string description: ""
|
||||
|
||||
Accessible.role: Accessible.StaticText
|
||||
Accessible.name: title
|
||||
Accessible.description: description
|
||||
|
||||
width: 190
|
||||
height: infos.implicitHeight
|
||||
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
import QtQuick
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import net.jami.Enums 1.1
|
||||
import net.jami.Models 1.1
|
||||
import "contextmenu"
|
||||
import "../mainview"
|
||||
import "../mainview/components"
|
||||
@ -30,15 +32,16 @@ ContextMenuAutoLoader {
|
||||
property var selectionEnd
|
||||
property bool customizePaste: false
|
||||
property bool selectOnly: false
|
||||
property bool checkSpell: false
|
||||
property bool spellCheckEnabled: false
|
||||
property var suggestionList
|
||||
property var menuItemsLength
|
||||
property var language
|
||||
|
||||
signal contextMenuRequirePaste
|
||||
|
||||
SpellLanguageContextMenu {
|
||||
id: spellLanguageContextMenu
|
||||
active: checkSpell
|
||||
active: spellCheckEnabled
|
||||
}
|
||||
|
||||
property list<GeneralMenuItem> menuItems: [
|
||||
@ -49,8 +52,7 @@ ContextMenuAutoLoader {
|
||||
isActif: lineEditObj.selectedText.length && !selectOnly
|
||||
itemName: JamiStrings.cut
|
||||
hasIcon: false
|
||||
onClicked:
|
||||
lineEditObj.cut();
|
||||
onClicked: lineEditObj.cut()
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: copy
|
||||
@ -59,8 +61,7 @@ ContextMenuAutoLoader {
|
||||
isActif: lineEditObj.selectedText.length
|
||||
itemName: JamiStrings.copy
|
||||
hasIcon: false
|
||||
onClicked:
|
||||
lineEditObj.copy();
|
||||
onClicked: lineEditObj.copy()
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: paste
|
||||
@ -76,30 +77,39 @@ ContextMenuAutoLoader {
|
||||
}
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: language
|
||||
visible: checkSpell
|
||||
canTrigger: checkSpell
|
||||
itemName: JamiStrings.language
|
||||
id: textLanguage
|
||||
canTrigger: spellCheckEnabled && SpellCheckAdapter.installedDictionaryCount > 0
|
||||
itemName: JamiStrings.textLanguage
|
||||
hasIcon: false
|
||||
onClicked: {
|
||||
spellLanguageContextMenu.openMenu();
|
||||
}
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: manageLanguages
|
||||
itemName: JamiStrings.manageDictionaries
|
||||
canTrigger: spellCheckEnabled
|
||||
hasIcon: false
|
||||
onClicked: {
|
||||
viewCoordinator.presentDialog(appWindow, "commoncomponents/ManageDictionariesDialog.qml");
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
ListView {
|
||||
model: ListModel {
|
||||
id: dynamicModel
|
||||
id: suggestionListModel
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
model: dynamicModel
|
||||
model: suggestionListModel
|
||||
delegate: GeneralMenuItem {
|
||||
id: suggestion
|
||||
|
||||
canTrigger: true
|
||||
isActif: true
|
||||
itemName: model.name
|
||||
bold: true
|
||||
hasIcon: false
|
||||
onClicked: {
|
||||
replaceWord(model.name);
|
||||
@ -117,7 +127,7 @@ ContextMenuAutoLoader {
|
||||
}
|
||||
|
||||
function removeItems() {
|
||||
dynamicModel.remove(0, suggestionList.length);
|
||||
suggestionListModel.clear();
|
||||
suggestionList.length = 0;
|
||||
}
|
||||
|
||||
@ -125,7 +135,7 @@ ContextMenuAutoLoader {
|
||||
menuItemsLength = menuItems.length; // Keep initial number of items for easier removal
|
||||
suggestionList = wordList;
|
||||
for (var i = 0; i < suggestionList.length; ++i) {
|
||||
dynamicModel.append({
|
||||
suggestionListModel.append({
|
||||
"name": suggestionList[i]
|
||||
});
|
||||
}
|
||||
@ -154,7 +164,7 @@ ContextMenuAutoLoader {
|
||||
lineEditObj.select(selectionStart, selectionEnd);
|
||||
}
|
||||
function onClosed() {
|
||||
if (!suggestionList || suggestionList.length == 0) {
|
||||
if (!suggestionList || suggestionList.length === 0) {
|
||||
return;
|
||||
}
|
||||
removeItems();
|
||||
|
||||
@ -16,19 +16,21 @@
|
||||
*/
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import net.jami.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import net.jami.Helpers 1.1
|
||||
import net.jami.Models 1.1
|
||||
import "../../commoncomponents"
|
||||
import "../commoncomponents/contextmenu"
|
||||
|
||||
Item {
|
||||
id: cachedFile
|
||||
property string dictionaryPath: SpellCheckDictionaryManager.getDictionariesPath()
|
||||
BaseModalDialog {
|
||||
id: root
|
||||
objectName: "manageDictionariesDialog"
|
||||
|
||||
function updateDictionnary(languagePath) {
|
||||
var file = dictionaryPath + languagePath;
|
||||
MessagesAdapter.updateDictionnary(file);
|
||||
title: JamiStrings.manageDictionaries
|
||||
|
||||
popupContent: DictionaryInstallView {
|
||||
Accessible.name: JamiStrings.manageDictionaries
|
||||
Accessible.role: Accessible.PopupMenu
|
||||
width: 400
|
||||
height: 500
|
||||
}
|
||||
}
|
||||
@ -86,6 +86,7 @@ BaseModalDialog {
|
||||
|
||||
JamiPushButton {
|
||||
id: takePhotoButton
|
||||
Accessible.name: objectName
|
||||
|
||||
objectName: "takePhotoButton"
|
||||
|
||||
@ -136,6 +137,8 @@ BaseModalDialog {
|
||||
height: buttonSize
|
||||
width: buttonSize
|
||||
|
||||
Accessible.name: objectName
|
||||
|
||||
normalColor: "transparent"
|
||||
source: JamiResources.add_photo_alternate_black_24dp_svg
|
||||
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
|
||||
@ -37,12 +37,16 @@ Row {
|
||||
|
||||
SystemButton {
|
||||
id: minButton
|
||||
Accessible.name: JamiStrings.minimize
|
||||
Accessible.role: Accessible.Button
|
||||
source: JamiResources.window_bar_minimize_svg
|
||||
onClicked: appWindow.showMinimized()
|
||||
}
|
||||
|
||||
SystemButton {
|
||||
id: maxButton
|
||||
Accessible.name: JamiStrings.maximize
|
||||
Accessible.role: Accessible.Button
|
||||
source: appWindow.visibility === Window.Maximized ?
|
||||
JamiResources.window_bar_restore_svg :
|
||||
JamiResources.window_bar_maximize_svg
|
||||
@ -53,6 +57,8 @@ Row {
|
||||
|
||||
SystemButton {
|
||||
id: closeButton
|
||||
Accessible.name: JamiStrings.closeApplication
|
||||
Accessible.role: Accessible.Button
|
||||
source: JamiResources.window_bar_close_svg
|
||||
baseColor: "#e81123"
|
||||
onClicked: appWindow.close()
|
||||
|
||||
@ -24,6 +24,7 @@ import net.jami.Constants 1.1
|
||||
|
||||
Control {
|
||||
id: root
|
||||
Accessible.role: Accessible.StaticText
|
||||
|
||||
property alias avatarBlockWidth: avatarBlock.width
|
||||
property alias innerContent: innerContent
|
||||
@ -64,6 +65,7 @@ Control {
|
||||
property bool bigMsg
|
||||
property bool timeUnderBubble: false
|
||||
property var type: Type
|
||||
property var shouldBeVisible: msgRowlayout.msgHovered || root.activeFocus || reply.activeFocus || more.activeFocus || share.activeFocus
|
||||
|
||||
// If the ListView attached properties are not available,
|
||||
// then the root delegate is likely a Loader.
|
||||
@ -81,6 +83,16 @@ Control {
|
||||
rightPadding: hPadding
|
||||
leftPadding: hPadding
|
||||
|
||||
background: Rectangle {
|
||||
id: focusIndicator
|
||||
visible: rootDelegate.activeFocus
|
||||
radius: 4
|
||||
border.color: JamiTheme.tintedBlue
|
||||
border.width: 2
|
||||
color: "transparent"
|
||||
z: 1
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: mainColumnLayout
|
||||
|
||||
@ -142,6 +154,7 @@ Control {
|
||||
RowLayout {
|
||||
id: replyToLayout
|
||||
|
||||
spacing: replyItem.isSelf ? 2 : 4
|
||||
Layout.alignment: isOutgoing ? Qt.AlignRight : Qt.AlignLeft
|
||||
property var replyUserName: UtilsAdapter.getBestNameForUri(CurrentAccount.id, ReplyToAuthor)
|
||||
|
||||
@ -185,7 +198,7 @@ Control {
|
||||
text: textMetricsUsername2.elidedText
|
||||
TextMetrics {
|
||||
id: textMetricsUsername2
|
||||
text: replyItem.isSelf ? JamiStrings.inReplyToMe : replyToLayout.replyUserName
|
||||
text: replyItem.isSelf ? JamiStrings.inReplyToYou : replyToLayout.replyUserName
|
||||
elideWidth: 200
|
||||
elide: Qt.ElideMiddle
|
||||
}
|
||||
@ -300,7 +313,7 @@ Control {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: isOutgoing ? optionButtonItem.right : undefined
|
||||
anchors.left: !isOutgoing ? optionButtonItem.left : undefined
|
||||
visible: msgRowlayout.msgHovered
|
||||
visible: shouldBeVisible
|
||||
source: JamiResources.more_vert_24dp_svg
|
||||
width: optionButtonItem.width / 4
|
||||
height: optionButtonItem.height
|
||||
@ -310,7 +323,7 @@ Control {
|
||||
|
||||
function setBindings() {
|
||||
more.isOpen = false;
|
||||
visible = Qt.binding(() => msgRowlayout.msgHovered);
|
||||
visible = Qt.binding(() => shouldBeVisible);
|
||||
imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor);
|
||||
normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor);
|
||||
}
|
||||
@ -355,7 +368,7 @@ Control {
|
||||
anchors.rightMargin: 5
|
||||
anchors.right: isOutgoing ? more.left : undefined
|
||||
anchors.left: !isOutgoing ? more.right : undefined
|
||||
visible: msgRowlayout.msgHovered
|
||||
visible: shouldBeVisible
|
||||
|
||||
onClicked: {
|
||||
MessagesAdapter.editId = "";
|
||||
@ -379,13 +392,14 @@ Control {
|
||||
anchors.rightMargin: 5
|
||||
anchors.right: isOutgoing ? reply.left : undefined
|
||||
anchors.left: !isOutgoing ? reply.right : undefined
|
||||
visible: msgRowlayout.msgHovered
|
||||
|
||||
visible: shouldBeVisible
|
||||
property bool isOpen: false
|
||||
property var obj: undefined
|
||||
|
||||
function setBindings() { // when the popup is closed, setBindings is called to reset the icon's visual settings
|
||||
share.isOpen = false;
|
||||
visible = Qt.binding(() => msgRowlayout.msgHovered);
|
||||
visible = Qt.binding(() => shouldBeVisible);
|
||||
imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor);
|
||||
normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor);
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import QtQuick.Layouts
|
||||
Rectangle {
|
||||
property alias name: label.text
|
||||
property bool stretchParent: false
|
||||
property string tag: parent.toString()
|
||||
property string tag: parent.toString() + " (w:" + width + ", h: " + height + ")"
|
||||
signal moveX(real dx)
|
||||
signal moveY(real dy)
|
||||
property real ox: 0
|
||||
|
||||
@ -50,11 +50,14 @@ ComboBox {
|
||||
|
||||
contentItem: Text {
|
||||
text: {
|
||||
if (index < 0)
|
||||
if (index < 0 || !model)
|
||||
return '';
|
||||
var currentItem = root.delegateModel.items.get(index);
|
||||
const value = currentItem.model[root.textRole];
|
||||
return value === undefined ? '' : value.toString();
|
||||
|
||||
if (root.textRole && model[root.textRole] !== undefined) {
|
||||
return model[root.textRole].toString();
|
||||
}
|
||||
|
||||
return model.display !== undefined ? model.display.toString() : '';
|
||||
}
|
||||
|
||||
color: hovered ? JamiTheme.comboboxTextColorHovered : JamiTheme.textColor
|
||||
@ -80,7 +83,7 @@ ComboBox {
|
||||
|
||||
source: popup.visible ? JamiResources.expand_less_24dp_svg : JamiResources.expand_more_24dp_svg
|
||||
|
||||
color: JamiTheme.comboboxIconColor
|
||||
color: root.enabled ? JamiTheme.comboboxIconColor : "grey"
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
@ -92,7 +95,7 @@ ComboBox {
|
||||
anchors.rightMargin: root.indicator.width * 2
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
|
||||
text: root.displayText
|
||||
color: JamiTheme.comboboxTextColor
|
||||
color: root.enabled ? JamiTheme.comboboxTextColor : "grey"
|
||||
font.weight: Font.Medium
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@ -104,7 +107,11 @@ ComboBox {
|
||||
color: JamiTheme.transparentColor
|
||||
implicitWidth: 120
|
||||
implicitHeight: contentItem.implicitHeight + JamiTheme.buttontextHeightMargin
|
||||
border.color: popup.visible ? JamiTheme.comboboxBorderColorActive : JamiTheme.comboboxBorderColor
|
||||
border.color: root.enabled ?
|
||||
(popup.visible ?
|
||||
JamiTheme.comboboxBorderColorActive :
|
||||
JamiTheme.comboboxBorderColor) :
|
||||
"grey"
|
||||
border.width: root.visualFocus ? 2 : 1
|
||||
radius: 5
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import "contextmenu"
|
||||
|
||||
BaseContextMenu {
|
||||
id: root
|
||||
|
||||
property var modelList
|
||||
signal audioRecordMessageButtonClicked
|
||||
signal videoRecordMessageButtonClicked
|
||||
@ -31,35 +32,63 @@ BaseContextMenu {
|
||||
GeneralMenuItem {
|
||||
id: audioMessage
|
||||
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: itemName
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Keys.onReturnPressed: clicked()
|
||||
|
||||
canTrigger: true
|
||||
iconSource: JamiResources.message_audio_black_24dp_svg
|
||||
itemName: JamiStrings.leaveAudioMessage
|
||||
onClicked: {
|
||||
root.audioRecordMessageButtonClicked();
|
||||
root.close()
|
||||
}
|
||||
|
||||
KeyNavigation.tab: videoMessage
|
||||
KeyNavigation.backtab: shareLocation
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: videoMessage
|
||||
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: itemName
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Keys.onReturnPressed: clicked()
|
||||
|
||||
canTrigger: true
|
||||
iconSource: JamiResources.message_video_black_24dp_svg
|
||||
itemName: JamiStrings.leaveVideoMessage
|
||||
|
||||
isActif: VideoDevices.listSize !== 0
|
||||
|
||||
onClicked: {
|
||||
root.videoRecordMessageButtonClicked();
|
||||
root.close()
|
||||
}
|
||||
|
||||
KeyNavigation.tab: shareLocation
|
||||
KeyNavigation.backtab: audioMessage
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: shareLocation
|
||||
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: itemName
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Keys.onReturnPressed: clicked()
|
||||
|
||||
canTrigger: true
|
||||
iconSource: JamiResources.localisation_sharing_send_pin_svg
|
||||
itemName: JamiStrings.shareLocation
|
||||
onClicked: {
|
||||
root.showMapClicked();
|
||||
root.close()
|
||||
}
|
||||
|
||||
KeyNavigation.tab: audioMessage
|
||||
KeyNavigation.backtab: videoMessage
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@ -22,15 +22,12 @@ import net.jami.Enums 1.1
|
||||
import "contextmenu"
|
||||
import "../mainview"
|
||||
import "../mainview/components"
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
ContextMenuAutoLoader {
|
||||
id: root
|
||||
|
||||
signal languageChanged()
|
||||
|
||||
CachedFile {
|
||||
id: cachedFile
|
||||
}
|
||||
signal languageChanged
|
||||
|
||||
function openMenuAt(mouseEvent) {
|
||||
x = mouseEvent.x;
|
||||
@ -46,9 +43,11 @@ ContextMenuAutoLoader {
|
||||
function generateMenuItems() {
|
||||
var menuItems = [];
|
||||
// Create new menu items
|
||||
var dictionaries = SpellCheckDictionaryManager.installedDictionaries();
|
||||
var dictionaries = SpellCheckAdapter.getInstalledDictionaries();
|
||||
var keys = Object.keys(dictionaries);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
const locale = keys[i];
|
||||
const nativeName = dictionaries[keys[i]];
|
||||
var menuItem = Qt.createComponent("qrc:/commoncomponents/contextmenu/GeneralMenuItem.qml", Component.PreferSynchronous);
|
||||
if (menuItem.status !== Component.Ready) {
|
||||
console.error("Error loading component:", menuItem.errorString());
|
||||
@ -58,17 +57,19 @@ ContextMenuAutoLoader {
|
||||
"parent": root,
|
||||
"canTrigger": true,
|
||||
"isActif": true,
|
||||
"itemName": dictionaries[keys[i]],
|
||||
"itemName": nativeName,
|
||||
"hasIcon": false,
|
||||
"content": keys[i],
|
||||
"content": locale,
|
||||
"bold": UtilsAdapter.getAppValue(Settings.SpellLang) === locale
|
||||
});
|
||||
if (menuItemObject === null) {
|
||||
console.error("Error creating menu item:", menuItem.errorString());
|
||||
continue;
|
||||
}
|
||||
menuItemObject.clicked.connect(function () {
|
||||
UtilsAdapter.setAppValue(Settings.Key.SpellLang, menuItemObject.content);
|
||||
});
|
||||
const locale = menuItemObject.content;
|
||||
SpellCheckAdapter.setDictionary(locale);
|
||||
});
|
||||
// Log the object pointer
|
||||
menuItems.push(menuItemObject);
|
||||
}
|
||||
|
||||
@ -24,7 +24,21 @@ import net.jami.Constants 1.1
|
||||
import net.jami.Enums 1.1
|
||||
|
||||
SBSMessageBase {
|
||||
id: root
|
||||
id: rootDelegate
|
||||
|
||||
Accessible.role: Accessible.StaticText
|
||||
Accessible.name: {
|
||||
let name = isOutgoing ? JamiStrings.inReplyToYou : UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return name + ": " + Body + " " + formattedTime;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (bubble.isEdited)
|
||||
status += JamiStrings.edited + " ";
|
||||
return status + (readers.length > 0 ? JamiStrings.readBy + " " + readers.map(function (uri) {
|
||||
return UtilsAdapter.getBestNameForUri(CurrentAccount.id, uri);
|
||||
}).join(", ") : "");
|
||||
}
|
||||
|
||||
property bool isRemoteImage
|
||||
property bool isEmojiOnly: IsEmojiOnly
|
||||
@ -34,11 +48,11 @@ SBSMessageBase {
|
||||
Connections {
|
||||
target: bubble
|
||||
function onColorChanged(color) {
|
||||
root.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark;
|
||||
root.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
|
||||
rootDelegate.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark;
|
||||
rootDelegate.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
|
||||
// Update parsed body with correct colors
|
||||
if (Body !== "")
|
||||
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color);
|
||||
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), rootDelegate.colorUrl, bubble.color);
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +67,7 @@ SBSMessageBase {
|
||||
textContentWidth: textEditId.width
|
||||
textContentHeight: textEditId.height
|
||||
|
||||
bigMsg: textContentWidth >= (2 / 3) * root.maxMsgWidth || extraContent.active
|
||||
bigMsg: textContentWidth >= (2 / 3) * rootDelegate.maxMsgWidth || extraContent.active
|
||||
|
||||
innerContent.children: [
|
||||
TextEdit {
|
||||
@ -63,10 +77,10 @@ SBSMessageBase {
|
||||
topPadding: bubble.isDeleted ? 6 : 10
|
||||
bottomPadding: bubble.isDeleted ? 6 : 10
|
||||
anchors.right: isOutgoing ? parent.right : undefined
|
||||
anchors.rightMargin: isOutgoing && !isEmojiOnly && !bigMsg ? root.timeWidth + root.editedWidth : 0
|
||||
anchors.rightMargin: isOutgoing && !isEmojiOnly && !bigMsg ? rootDelegate.timeWidth + rootDelegate.editedWidth : 0
|
||||
text: {
|
||||
if (Body !== "" && ParsedBody.length === 0) {
|
||||
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color);
|
||||
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), rootDelegate.colorUrl, bubble.color);
|
||||
return "";
|
||||
}
|
||||
if (ParsedBody !== "")
|
||||
@ -82,11 +96,11 @@ SBSMessageBase {
|
||||
|
||||
width: {
|
||||
if (extraContent.active)
|
||||
Math.max(extraContent.width, Math.min((2 / 3) * root.maxMsgWidth, implicitWidth - avatarBlockWidth, extraContent.minSize) - senderMargin);
|
||||
Math.max(extraContent.width, Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth - avatarBlockWidth, extraContent.minSize) - senderMargin);
|
||||
else if (isEmojiOnly)
|
||||
Math.min((2 / 3) * root.maxMsgWidth, implicitWidth, innerContent.width - senderMargin - (innerContent.width - senderMargin) % (JamiTheme.chatviewEmojiSize + 2));
|
||||
Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth, innerContent.width - senderMargin - (innerContent.width - senderMargin) % (JamiTheme.chatviewEmojiSize + 2));
|
||||
else
|
||||
Math.min((2 / 3) * root.maxMsgWidth, implicitWidth + 5, innerContent.width - senderMargin + 5);
|
||||
Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth + 5, innerContent.width - senderMargin + 5);
|
||||
}
|
||||
|
||||
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
|
||||
@ -96,7 +110,7 @@ SBSMessageBase {
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: Text.RichText
|
||||
clip: true
|
||||
onLinkHovered: root.hoveredLink = hoveredLink
|
||||
onLinkHovered: rootDelegate.hoveredLink = hoveredLink
|
||||
onLinkActivated: Qt.openUrlExternally(new URL(hoveredLink))
|
||||
readOnly: true
|
||||
color: (ParsedBody !== "") ? getBaseColor() : (UtilsAdapter.luma(bubble.color) ? "white" : "dark")
|
||||
@ -150,7 +164,7 @@ SBSMessageBase {
|
||||
HoverHandler {
|
||||
target: previewContent
|
||||
onHoveredChanged: {
|
||||
root.hoveredLink = hovered ? LinkPreviewInfo.url : "";
|
||||
rootDelegate.hoveredLink = hovered ? LinkPreviewInfo.url : "";
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
@ -204,7 +218,7 @@ SBSMessageBase {
|
||||
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: TextEdit.RichText
|
||||
color: root.colorText
|
||||
color: rootDelegate.colorText
|
||||
visible: LinkPreviewInfo.title.length > 0
|
||||
text: LinkPreviewInfo.title
|
||||
lineHeight: 1.3
|
||||
@ -217,9 +231,9 @@ SBSMessageBase {
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: TextEdit.RichText
|
||||
visible: LinkPreviewInfo.description.length > 0
|
||||
font.underline: root.hoveredLink
|
||||
font.underline: rootDelegate.hoveredLink
|
||||
text: LinkPreviewInfo.description
|
||||
color: root.colorUrl
|
||||
color: rootDelegate.colorUrl
|
||||
lineHeight: 1.3
|
||||
}
|
||||
Label {
|
||||
@ -229,7 +243,7 @@ SBSMessageBase {
|
||||
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: TextEdit.RichText
|
||||
color: root.colorText
|
||||
color: rootDelegate.colorText
|
||||
text: LinkPreviewInfo.domain
|
||||
lineHeight: 1.3
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ MenuItem {
|
||||
id: menuItem
|
||||
|
||||
property string itemName: ""
|
||||
property bool bold: false
|
||||
property string content: ""
|
||||
property alias iconSource: contextMenuItemImage.source
|
||||
property string iconColor: ""
|
||||
@ -99,6 +100,7 @@ MenuItem {
|
||||
anchors.left: parent.left
|
||||
height: parent.height
|
||||
text: itemName
|
||||
font.bold: bold
|
||||
color: dangerous ? JamiTheme.redColor : isActif ? JamiTheme.textColor : JamiTheme.chatViewFooterImgColor
|
||||
font.pointSize: JamiTheme.textFontSize
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "version.h"
|
||||
#include "version_info.h"
|
||||
|
||||
#include <QVariantMap>
|
||||
@ -108,7 +107,7 @@ protected:
|
||||
{"platform", QSysInfo::prettyProductName() + "_" + QSysInfo::currentCpuArchitecture()},
|
||||
{"client_sha", APP_VERSION_STRING},
|
||||
{"jamicore_sha", CORE_VERSION_STRING},
|
||||
{"build_id", QString(VERSION_STRING)},
|
||||
{"build_id", BUILD_VERSION_STRING},
|
||||
#if defined(Q_OS_WIN) && defined(BETA)
|
||||
{"build_variant", "beta"},
|
||||
#endif
|
||||
|
||||
@ -147,6 +147,7 @@ CurrentAccount::updateData()
|
||||
set_isRendezVous(accConfig.isRendezVous, true);
|
||||
set_dhtPort(accConfig.dhtPort, true);
|
||||
set_autoAnswer(accConfig.autoAnswer, true);
|
||||
set_denySecondCall(accConfig.denySecondCall, true);
|
||||
set_proxyEnabled(accConfig.proxyEnabled, true);
|
||||
set_upnpEnabled(accConfig.upnpEnabled, true);
|
||||
set_publishedSameAsLocal(accConfig.publishedSameAsLocal, true);
|
||||
|
||||
@ -121,6 +121,7 @@ class CurrentAccount final : public QObject
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, sendComposing)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, isRendezVous)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, autoAnswer)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, denySecondCall)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, proxyEnabled)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, upnpEnabled)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, publishedSameAsLocal)
|
||||
|
||||
@ -356,7 +356,7 @@ CurrentCall::onCurrentAccountIdChanged()
|
||||
}
|
||||
|
||||
void
|
||||
CurrentCall::onCallStatusChanged(const QString& callId, int code)
|
||||
CurrentCall::onCallStatusChanged(const QString& accountId, const QString& callId, int code)
|
||||
{
|
||||
Q_UNUSED(code)
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ private:
|
||||
private Q_SLOTS:
|
||||
void onCurrentConvIdChanged();
|
||||
void onCurrentAccountIdChanged();
|
||||
void onCallStatusChanged(const QString& callId, int code);
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId, int code);
|
||||
void onCallInfosChanged(const QString& accountId, const QString& callId);
|
||||
void onCurrentCallChanged(const QString& callId);
|
||||
void onParticipantsChanged(const QString& callId);
|
||||
|
||||
@ -371,7 +371,7 @@ CurrentConversation::updateActiveCalls(const QString&, const QString& convId)
|
||||
}
|
||||
|
||||
void
|
||||
CurrentConversation::onCallStatusChanged(const QString& callId, int)
|
||||
CurrentConversation::onCallStatusChanged(const QString& accountId, const QString& callId, int)
|
||||
{
|
||||
if (callId != callId_) {
|
||||
return;
|
||||
|
||||
@ -90,7 +90,7 @@ private Q_SLOTS:
|
||||
void updateErrors(const QString& convId);
|
||||
void updateConversationPreferences(const QString& convId);
|
||||
void updateActiveCalls(const QString&, const QString& convId);
|
||||
void onCallStatusChanged(const QString& callId, int code);
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId, int code);
|
||||
void onShowIncomingCallView(const QString& accountId, const QString& convUid);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
#include "mainapplication.h"
|
||||
#include "instancemanager.h"
|
||||
#include "version.h"
|
||||
#include "version_info.h"
|
||||
#if defined(Q_OS_MACOS)
|
||||
#include <os/macos/macutils.h>
|
||||
#endif
|
||||
@ -66,7 +66,7 @@ main(int argc, char* argv[])
|
||||
QApplication::setApplicationName(QStringLiteral("Jami"));
|
||||
QApplication::setOrganizationDomain(QStringLiteral("jami.net"));
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
QCoreApplication::setApplicationVersion(QString(VERSION_STRING));
|
||||
QCoreApplication::setApplicationVersion(BUILD_VERSION_STRING);
|
||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
|
||||
QApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
#include "global.h"
|
||||
#include "qmlregister.h"
|
||||
#include "appsettingsmanager.h"
|
||||
#include "spellcheckdictionarymanager.h"
|
||||
#include "connectivitymonitor.h"
|
||||
#include "systemtray.h"
|
||||
#include "previewengine.h"
|
||||
@ -160,6 +159,7 @@ MainApplication::MainApplication(int& argc, char** argv)
|
||||
"qml.debug=false\n"
|
||||
"default.debug=false\n"
|
||||
"client.debug=false\n"
|
||||
"spellcheck.debug=false\n"
|
||||
"\n");
|
||||
// These can be set in the environment as well.
|
||||
// e.g. QT_LOGGING_RULES="*.debug=false;qml.debug=true"
|
||||
@ -191,7 +191,6 @@ MainApplication::init()
|
||||
// to any other initialization. This won't do anything if crashpad isn't
|
||||
// enabled.
|
||||
settingsManager_ = new AppSettingsManager(this);
|
||||
spellCheckDictionaryManager_ = new SpellCheckDictionaryManager(settingsManager_, this);
|
||||
crashReporter_ = new CrashReporter(settingsManager_, this);
|
||||
|
||||
// This 2-phase initialisation prevents ephemeral instances from
|
||||
@ -349,8 +348,8 @@ MainApplication::parseArguments()
|
||||
parser_.addOption(muteDaemonOption);
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
// In debug mode, add an option to test a specific QML component via its name.
|
||||
// e.g. ./jami --test AccountComboBox
|
||||
// In debug mode, add an option to test a specific QML component via its name.
|
||||
// e.g. ./jami --test AccountComboBox
|
||||
parser_.addOption(QCommandLineOption("test", "Test a QML component via its name.", "uri"));
|
||||
// We may need to force the test window dimensions in the case that the component to test
|
||||
// does not specify its own dimensions and is dependent on parent/sibling dimensions.
|
||||
@ -425,7 +424,6 @@ MainApplication::initQmlLayer()
|
||||
lrcInstance_.get(),
|
||||
systemTray_,
|
||||
settingsManager_,
|
||||
spellCheckDictionaryManager_,
|
||||
connectivityMonitor_,
|
||||
previewEngine_,
|
||||
&screenInfo_,
|
||||
@ -466,7 +464,7 @@ MainApplication::initQmlLayer()
|
||||
void
|
||||
MainApplication::initSystray()
|
||||
{
|
||||
systemTray_->setIcon(QIcon(":/images/jami.svg"));
|
||||
systemTray_->setIcon(QIcon(":/images/net.jami.Jami.svg"));
|
||||
|
||||
QMenu* menu {nullptr};
|
||||
// If there was a previous menu, reuse it, otherwise create a new one.
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
class ConnectivityMonitor;
|
||||
class SystemTray;
|
||||
class AppSettingsManager;
|
||||
class SpellCheckDictionaryManager;
|
||||
class CrashReporter;
|
||||
class PreviewEngine;
|
||||
|
||||
@ -119,7 +118,6 @@ private:
|
||||
ConnectivityMonitor* connectivityMonitor_;
|
||||
SystemTray* systemTray_;
|
||||
AppSettingsManager* settingsManager_;
|
||||
SpellCheckDictionaryManager* spellCheckDictionaryManager_;
|
||||
PreviewEngine* previewEngine_;
|
||||
CrashReporter* crashReporter_;
|
||||
|
||||
|
||||
@ -85,7 +85,6 @@ Popup {
|
||||
anchors.rightMargin: 15
|
||||
spacing: 10
|
||||
|
||||
|
||||
Avatar {
|
||||
id: avatar
|
||||
objectName: "accountComboBoxPopupAvatar"
|
||||
@ -164,8 +163,13 @@ Popup {
|
||||
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
hoveredColor: JamiTheme.hoverColor
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: toolTipText
|
||||
Accessible.description: JamiStrings.qrCodeExplanation
|
||||
|
||||
onClicked: {
|
||||
viewCoordinator.presentDialog(appWindow, "mainview/components/WelcomePageQrDialog.qml");
|
||||
listView.currentIndex = -1;
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
@ -184,34 +188,40 @@ Popup {
|
||||
|
||||
toolTipText: !inSettings ? JamiStrings.openSettings : JamiStrings.closeSettings
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: toolTipText
|
||||
KeyNavigation.backtab: shareButton
|
||||
|
||||
onClicked: {
|
||||
!inSettings ? viewCoordinator.present("SettingsView") : viewCoordinator.dismiss("SettingsView");
|
||||
root.close();
|
||||
}
|
||||
|
||||
KeyNavigation.tab: addAccountItem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 15
|
||||
Layout.rightMargin: 15
|
||||
color: JamiTheme.smartListHoveredColor
|
||||
}
|
||||
|
||||
|
||||
JamiListView {
|
||||
ListView {
|
||||
id: listView
|
||||
objectName: "accountList"
|
||||
Accessible.name: JamiStrings.accountList
|
||||
Accessible.role: Accessible.List
|
||||
Accessible.description: JamiStrings.accountListDescription
|
||||
|
||||
layer.mipmap: false
|
||||
clip: true
|
||||
maximumFlickVelocity: 1024
|
||||
|
||||
// HACK: remove after migration to Qt 6.7+
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: parent.width
|
||||
|
||||
activeFocusOnTab: true
|
||||
focus: true
|
||||
currentIndex: -1 // Set to -1 to avoid initial highlighting
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
sourceModel: AccountListModel
|
||||
filters: ValueFilter {
|
||||
@ -221,10 +231,35 @@ Popup {
|
||||
}
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
color: "transparent"
|
||||
border.color: JamiTheme.primaryBackgroundColor
|
||||
border.width: 2
|
||||
radius: 5
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: JamiTheme.hoverColor
|
||||
radius: 5
|
||||
opacity: 0.3
|
||||
}
|
||||
}
|
||||
|
||||
delegate: AccountItemDelegate {
|
||||
height: JamiTheme.accountListItemHeight
|
||||
width: root.width
|
||||
|
||||
Accessible.role: Accessible.ListItem
|
||||
Accessible.name: Alias || Username
|
||||
Accessible.description: JamiStrings.switchToAccount
|
||||
|
||||
// Update the background to show focus state
|
||||
background: Rectangle {
|
||||
color: parent.activeFocus || parent.hovered ? JamiTheme.hoverColor : "transparent"
|
||||
opacity: parent.activeFocus ? 0.3 : 1
|
||||
radius: 5
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.close();
|
||||
// This is a workaround for the synchronicity issue
|
||||
@ -235,7 +270,7 @@ Popup {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 1
|
||||
Layout.fillWidth: true
|
||||
@ -248,19 +283,24 @@ Popup {
|
||||
id: addAccountItem
|
||||
|
||||
Layout.preferredHeight: 45
|
||||
Layout.preferredWidth: parent.width -10
|
||||
Layout.preferredWidth: parent.width - 10
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.leftMargin: 5
|
||||
|
||||
Accessible.name: JamiStrings.addAccount
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Accessible.name: addAccountText.text
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
KeyNavigation.tab: manageAccountItem
|
||||
KeyNavigation.up: listView
|
||||
KeyNavigation.down: manageAccountItem
|
||||
|
||||
background: Rectangle {
|
||||
color: addAccountItem.hovered ? JamiTheme.hoverColor : JamiTheme.accountComboBoxBackgroundColor
|
||||
radius: 5
|
||||
}
|
||||
|
||||
RowLayout{
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 18
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@ -274,6 +314,7 @@ Popup {
|
||||
}
|
||||
|
||||
Text {
|
||||
id: addAccountText
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: JamiStrings.addAccount
|
||||
textFormat: TextEdit.PlainText
|
||||
@ -285,18 +326,21 @@ Popup {
|
||||
root.close();
|
||||
viewCoordinator.present("WizardView");
|
||||
}
|
||||
|
||||
KeyNavigation.tab: manageAccountItem
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
id: manageAccountItem
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: JamiStrings.manageAccount
|
||||
Accessible.name: manageAccountText.text
|
||||
|
||||
KeyNavigation.backtab: addAccountItem
|
||||
KeyNavigation.tab: shareButton
|
||||
KeyNavigation.up: addAccountItem
|
||||
|
||||
Layout.preferredHeight: 45
|
||||
Layout.preferredWidth: parent.width-10
|
||||
Layout.preferredWidth: parent.width - 10
|
||||
Layout.leftMargin: 5
|
||||
Layout.bottomMargin: 5
|
||||
|
||||
@ -305,7 +349,7 @@ Popup {
|
||||
radius: 5
|
||||
}
|
||||
|
||||
RowLayout{
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 18
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@ -319,8 +363,8 @@ Popup {
|
||||
color: manageAccountItem.hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
}
|
||||
Text {
|
||||
id: manageAccountText
|
||||
text: JamiStrings.manageAccount
|
||||
|
||||
textFormat: TextEdit.PlainText
|
||||
color: JamiTheme.textColor
|
||||
font.pointSize: JamiTheme.textFontSize
|
||||
@ -328,7 +372,7 @@ Popup {
|
||||
}
|
||||
onClicked: {
|
||||
root.close();
|
||||
viewCoordinator.present("SettingsView")
|
||||
viewCoordinator.present("SettingsView");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,17 +28,24 @@ ItemDelegate {
|
||||
height: JamiTheme.accountListItemHeight
|
||||
|
||||
background: Rectangle {
|
||||
width: root.width - 10
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors {
|
||||
left: parent ? parent.left : undefined
|
||||
right: parent ? parent.right : undefined
|
||||
leftMargin: 5
|
||||
rightMargin: 5
|
||||
}
|
||||
radius: 5
|
||||
|
||||
Rectangle{
|
||||
Rectangle {
|
||||
id: separationLine
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
leftMargin: 10
|
||||
rightMargin: 10
|
||||
}
|
||||
height: 1
|
||||
width: parent.width - 20
|
||||
color: JamiTheme.hoverColor
|
||||
visible: index !== 0
|
||||
}
|
||||
|
||||
@ -25,6 +25,13 @@ import "../../commoncomponents"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string tipTitle: title.text
|
||||
property string tipDescription: opened ? description.text : JamiStrings.whyBackupAccount
|
||||
|
||||
Accessible.name: tipTitle
|
||||
Accessible.description: tipDescription
|
||||
|
||||
width: parent.width
|
||||
height: backupLayout.height
|
||||
|
||||
|
||||
@ -55,15 +55,30 @@ Control {
|
||||
signal fullScreenClicked
|
||||
signal swarmDetailsClicked
|
||||
|
||||
// For Keyboard naviguation
|
||||
property bool isInternalNavigation: false
|
||||
|
||||
function exitBarNavigation() {
|
||||
isInternalNavigation = false;
|
||||
// Let the parent control take over focus handling
|
||||
parent.forceActiveFocus();
|
||||
}
|
||||
|
||||
Component {
|
||||
id: buttonDelegate
|
||||
|
||||
CallButtonDelegate {
|
||||
id: delegateItem
|
||||
width: root.height
|
||||
height: width
|
||||
barWidth: root.width
|
||||
onSubMenuVisibleChanged: subMenuOpen = subMenuVisible
|
||||
onHoveredChanged: root.barHovered = hovered
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
focus: false
|
||||
|
||||
Keys.onEscapePressed: root.exitBarNavigation()
|
||||
}
|
||||
}
|
||||
|
||||
@ -567,6 +582,34 @@ Control {
|
||||
ComboBox {
|
||||
id: overflowButton
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: JamiStrings.more
|
||||
Accessible.description: JamiStrings.moreOptions
|
||||
|
||||
KeyNavigation.tab: {
|
||||
if (popup.opened) {
|
||||
return popup.contentItem.itemAtIndex(0);
|
||||
}
|
||||
// Exit bar navigation if we've reached the end
|
||||
root.exitBarNavigation();
|
||||
return null;
|
||||
}
|
||||
|
||||
KeyNavigation.backtab: {
|
||||
if (overflowItemListView.count > 0) {
|
||||
return overflowItemListView.itemAtIndex(overflowItemListView.count - 1);
|
||||
}
|
||||
return itemListView.itemAtIndex(itemListView.count - 1);
|
||||
}
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
if (popup.opened) {
|
||||
popup.close();
|
||||
} else {
|
||||
root.exitBarNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
visible: CallOverlayModel.overflowIndex < overflowItemCount - 2
|
||||
width: root.height
|
||||
height: width
|
||||
|
||||
@ -42,8 +42,18 @@ ItemDelegate {
|
||||
text: ""
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: text
|
||||
Accessible.description: text
|
||||
Accessible.name: ItemAction.text
|
||||
Accessible.description: {
|
||||
if (!ItemAction?.text)
|
||||
return "";
|
||||
if (ItemAction.checkable) {
|
||||
return JamiStrings.pressToToggle.arg(ItemAction.text).arg(ItemAction.checked ? JamiStrings.active : JamiStrings.inactive);
|
||||
}
|
||||
return JamiStrings.pressToAction.arg(ItemAction.text);
|
||||
}
|
||||
Accessible.pressed: pressed
|
||||
Accessible.checkable: ItemAction ? ItemAction.checkable : false
|
||||
Accessible.checked: ItemAction ? ItemAction.checked : false
|
||||
|
||||
z: index
|
||||
|
||||
@ -77,7 +87,7 @@ ItemDelegate {
|
||||
return HalfPill.None;
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: JamiTheme.shortFadeDuration
|
||||
}
|
||||
@ -94,7 +104,7 @@ ItemDelegate {
|
||||
radius: isLast ? 5 : width / 2
|
||||
type: isLast ? HalfPill.Right : HalfPill.None
|
||||
|
||||
Behavior on color {
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: JamiTheme.shortFadeDuration
|
||||
}
|
||||
@ -113,7 +123,7 @@ ItemDelegate {
|
||||
source: ItemAction ? ItemAction.icon.source : ""
|
||||
color: ItemAction ? (ItemAction.enabled ? ItemAction.icon.color : Qt.lighter(ItemAction.icon.color)) : null
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
running: ItemAction !== undefined && ItemAction.blinksWhenChecked !== undefined && ItemAction.blinksWhenChecked && checked
|
||||
onStopped: icon.opacity = 1
|
||||
@ -188,7 +198,7 @@ ItemDelegate {
|
||||
radius: 4
|
||||
}
|
||||
|
||||
onActivated: index => menuAction.accept(index);
|
||||
onActivated: index => menuAction.accept(index)
|
||||
model: visible ? menuAction.listModel : null
|
||||
delegate: ItemDelegate {
|
||||
id: menuItem
|
||||
@ -313,7 +323,6 @@ ItemDelegate {
|
||||
// it fits within the overlay, with an extra leftward margin of 24 pixels.
|
||||
return diff > 0 ? xValue - diff - 24 : xValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
|
||||
@ -26,6 +26,12 @@ ColumnLayout {
|
||||
id: column
|
||||
width: parent.width
|
||||
|
||||
property string tipTitle: JamiStrings.customize
|
||||
property string tipDescription: JamiStrings.customizeText
|
||||
|
||||
Accessible.name: tipTitle
|
||||
Accessible.description: tipDescription
|
||||
|
||||
property var iconSize: 26
|
||||
property var margin: 5
|
||||
property var prefWidth: 170
|
||||
|
||||
@ -24,6 +24,13 @@ import "../../commoncomponents"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string tipTitle: title.text
|
||||
property string tipDescription: content.text
|
||||
|
||||
Accessible.name: tipTitle
|
||||
Accessible.description: tipDescription
|
||||
|
||||
focus: true
|
||||
width: parent.width
|
||||
height: backupLayout.height
|
||||
|
||||
@ -43,6 +43,9 @@ TabButton {
|
||||
hoverEnabled: true
|
||||
onClicked: selected()
|
||||
|
||||
Accessible.name: root.labelText
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
Rectangle {
|
||||
id: contentRect
|
||||
|
||||
|
||||
@ -26,6 +26,12 @@ ColumnLayout {
|
||||
id: column
|
||||
width: parent.width
|
||||
|
||||
property alias tipTitle: title.text
|
||||
property alias tipDescription: description.text
|
||||
|
||||
Accessible.name: tipTitle
|
||||
Accessible.description: tipDescription
|
||||
|
||||
property real maxHeight: 250
|
||||
|
||||
property var iconSize: 26
|
||||
|
||||
@ -25,4 +25,6 @@ PushButton {
|
||||
|
||||
normalColor: JamiTheme.chatviewBgColor
|
||||
imageColor: hovered ? JamiTheme.chatviewButtonColor : JamiTheme.chatViewFooterImgColor
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.description: toolTipText
|
||||
}
|
||||
|
||||
@ -228,6 +228,11 @@ Window {
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
|
||||
// make a list view of keyboardShortcutsModelList[selectionBar.currentIndex]
|
||||
JamiListView {
|
||||
id: keyboardShortcutsListView
|
||||
@ -262,6 +267,7 @@ Window {
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 20
|
||||
text: description
|
||||
color: JamiTheme.textColor
|
||||
background: Rectangle {
|
||||
width: parent.width + 16
|
||||
height: parent.height + 16
|
||||
@ -269,6 +275,7 @@ Window {
|
||||
border.width: 2
|
||||
radius: 5
|
||||
anchors.centerIn: parent
|
||||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
}
|
||||
Label {
|
||||
@ -277,6 +284,7 @@ Window {
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 20
|
||||
text: shortcut
|
||||
color: JamiTheme.textColor
|
||||
background: Rectangle {
|
||||
width: parent.width + 16
|
||||
height: parent.height + 16
|
||||
@ -284,6 +292,7 @@ Window {
|
||||
border.width: 2
|
||||
radius: 5
|
||||
anchors.centerIn: parent
|
||||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -300,6 +309,10 @@ Window {
|
||||
|
||||
focus: true
|
||||
|
||||
background: Rectangle {
|
||||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [JamiStrings.generalSettingsTitle, JamiStrings.conversationKeyboardShortcuts, JamiStrings.callKeyboardShortcuts, JamiStrings.markdownKeyboardShortcuts, JamiStrings.settings]
|
||||
|
||||
@ -339,9 +352,16 @@ Window {
|
||||
footer: Item {
|
||||
height: JamiTheme.keyboardShortcutTabBarSize
|
||||
PageIndicator {
|
||||
id: pageIndicator
|
||||
anchors.centerIn: parent
|
||||
count: selectionBar.count
|
||||
currentIndex: selectionBar.currentIndex
|
||||
delegate: Rectangle {
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
color: index === pageIndicator.currentIndex ? JamiTheme.textColor : JamiTheme.textColorHoveredHighContrast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,17 @@ Item {
|
||||
|
||||
property string timeText: "00:00"
|
||||
property string remoteRecordingLabel
|
||||
property bool isKeyboardSelectionActive: {
|
||||
if (!appWindow || !appWindow.activeFocusItem)
|
||||
return false;
|
||||
let parent = appWindow.activeFocusItem.parent;
|
||||
while (parent && parent !== appWindow && parent !== root && parent !== null) {
|
||||
if (parent.objectName === "callActionBar")
|
||||
return true;
|
||||
parent = parent.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: CurrentCall
|
||||
@ -42,7 +53,11 @@ Item {
|
||||
|
||||
property alias callActionBar: __callActionBar
|
||||
|
||||
property bool frozen: callActionBar.overflowOpen || callActionBar.barHovered || callActionBar.subMenuOpen || participantCallInStatusView.visible
|
||||
property bool frozen: callActionBar.overflowOpen ||
|
||||
callActionBar.barHovered ||
|
||||
callActionBar.subMenuOpen ||
|
||||
participantCallInStatusView.visible ||
|
||||
isKeyboardSelectionActive
|
||||
|
||||
property string muteAlertMessage: ""
|
||||
property bool muteAlertActive: false
|
||||
@ -59,15 +74,27 @@ Item {
|
||||
Component.onDestruction: CallOverlayModel.setEventFilterActive(appWindow, this, false)
|
||||
onVisibleChanged: CallOverlayModel.setEventFilterActive(appWindow, this, visible)
|
||||
|
||||
function kickOverlay() {
|
||||
root.opacity = 1;
|
||||
fadeOutTimer.restart();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: CallOverlayModel
|
||||
|
||||
function onMouseMoved(item) {
|
||||
if (item === root) {
|
||||
root.opacity = 1;
|
||||
fadeOutTimer.restart();
|
||||
kickOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
// This is part of a mechanism used to show the overlay when a focus key is pressed
|
||||
// and keep it open in the case that the user is navigating with the keyboard over
|
||||
// the call action bar.
|
||||
function onFocusKeyPressed() {
|
||||
// Always show the overlay when a focus key (Tab/BackTab) is pressed
|
||||
kickOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
@ -76,8 +103,7 @@ Item {
|
||||
context: Qt.ApplicationShortcut
|
||||
onActivated: {
|
||||
CallAdapter.muteAudioToggle();
|
||||
root.opacity = 1;
|
||||
fadeOutTimer.restart();
|
||||
kickOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,8 +113,7 @@ Item {
|
||||
context: Qt.ApplicationShortcut
|
||||
onActivated: {
|
||||
CallAdapter.muteCameraToggle();
|
||||
root.opacity = 1;
|
||||
fadeOutTimer.restart();
|
||||
kickOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,6 @@ JamiFlickable {
|
||||
property bool showPreview: false
|
||||
property bool isShowTypo: UtilsAdapter.getAppValue(Settings.Key.ShowMardownOption)
|
||||
property int textWidth: textArea.contentWidth
|
||||
property var spellCheckActive: AppSettingsManager.getValue(Settings.EnableSpellCheck)
|
||||
property var language: AppSettingsManager.getValue(Settings.SpellLang)
|
||||
|
||||
// Used to cache the editable text when showing the preview message
|
||||
@ -73,7 +72,7 @@ JamiFlickable {
|
||||
|
||||
lineEditObj: textArea
|
||||
customizePaste: true
|
||||
checkSpell: (Qt.platform.os.toString() === "linux") ? true : false
|
||||
spellCheckEnabled: root.spellCheckEnabled
|
||||
|
||||
onContextMenuRequirePaste: {
|
||||
// Intercept paste event to use C++ QMimeData
|
||||
@ -114,73 +113,41 @@ JamiFlickable {
|
||||
}
|
||||
}
|
||||
|
||||
property bool spellCheckEnabled: AppSettingsManager.getValue(Settings.EnableSpellCheck) && AppSettingsManager.getValue(Settings.SpellLang) !== ""
|
||||
|
||||
// Spell check is active under the following conditions:
|
||||
// 1. Spell check is enabled in settings
|
||||
// 2. The selected spell language is not ""
|
||||
// 3. We are not in preview mode
|
||||
readonly property bool spellCheckActive: spellCheckEnabled && !showPreview
|
||||
|
||||
onSpellCheckActiveChanged: textArea.updateSpellCorrection()
|
||||
|
||||
TextArea.flickable: TextArea {
|
||||
id: textArea
|
||||
|
||||
CachedFile {
|
||||
id: cachedFile
|
||||
Connections {
|
||||
target: SpellCheckAdapter
|
||||
|
||||
function onDictionaryChanged() {
|
||||
textArea.updateSpellCorrection();
|
||||
}
|
||||
}
|
||||
|
||||
function updateCorrection(language) {
|
||||
cachedFile.updateDictionnary(language);
|
||||
textArea.updateUnderlineText();
|
||||
}
|
||||
|
||||
// Listen to settings changes and apply it to this widget
|
||||
// Listen to settings changes to apply it to the text area
|
||||
Connections {
|
||||
target: UtilsAdapter
|
||||
|
||||
function onChangeLanguage() {
|
||||
textArea.updateUnderlineText();
|
||||
textArea.updateSpellCorrection();
|
||||
}
|
||||
|
||||
function onChangeFontSize() {
|
||||
textArea.updateUnderlineText();
|
||||
}
|
||||
|
||||
function onSpellLanguageChanged() {
|
||||
root.language = SpellCheckDictionaryManager.getSpellLanguage();
|
||||
if ((Qt.platform.os.toString() !== "linux") || (AppSettingsManager.getValue(Settings.SpellLang) === "NONE")) {
|
||||
spellCheckActive = false;
|
||||
} else {
|
||||
spellCheckActive = AppSettingsManager.getValue(Settings.EnableSpellCheck);
|
||||
}
|
||||
if (spellCheckActive === true) {
|
||||
root.language = SpellCheckDictionaryManager.getSpellLanguage();
|
||||
textArea.updateCorrection(root.language);
|
||||
} else {
|
||||
textArea.clearUnderlines();
|
||||
}
|
||||
textArea.updateSpellCorrection();
|
||||
}
|
||||
|
||||
function onEnableSpellCheckChanged() {
|
||||
// Disable spell check on non-linux platforms yet
|
||||
if ((Qt.platform.os.toString() !== "linux") || (AppSettingsManager.getValue(Settings.SpellLang) === "NONE")) {
|
||||
spellCheckActive = false;
|
||||
} else {
|
||||
spellCheckActive = AppSettingsManager.getValue(Settings.EnableSpellCheck);
|
||||
}
|
||||
if (spellCheckActive === true) {
|
||||
root.language = SpellCheckDictionaryManager.getSpellLanguage();
|
||||
textArea.updateCorrection(root.language);
|
||||
} else {
|
||||
textArea.clearUnderlines();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the settings if the component wasn't loaded when changing settings
|
||||
Component.onCompleted: {
|
||||
if ((Qt.platform.os.toString() !== "linux") || (AppSettingsManager.getValue(Settings.SpellLang) === "NONE")) {
|
||||
spellCheckActive = false;
|
||||
} else {
|
||||
spellCheckActive = AppSettingsManager.getValue(Settings.EnableSpellCheck);
|
||||
}
|
||||
if (spellCheckActive === true) {
|
||||
root.language = SpellCheckDictionaryManager.getSpellLanguage();
|
||||
textArea.updateCorrection(root.language);
|
||||
} else {
|
||||
textArea.clearUnderlines();
|
||||
textArea.updateSpellCorrection();
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,13 +190,15 @@ JamiFlickable {
|
||||
|
||||
onReleased: function (event) {
|
||||
if (event.button === Qt.RightButton) {
|
||||
var position = textArea.positionAt(event.x, event.y);
|
||||
textArea.moveCursorSelection(position, TextInput.SelectWords);
|
||||
textArea.selectWord();
|
||||
if (!MessagesAdapter.spell(textArea.selectedText)) {
|
||||
var wordList = MessagesAdapter.spellSuggestionsRequest(textArea.selectedText);
|
||||
if (wordList.length !== 0) {
|
||||
textAreaContextMenu.addMenuItem(wordList);
|
||||
if (spellCheckActive && SpellCheckAdapter.hasLoadedDictionary) {
|
||||
var position = textArea.positionAt(event.x, event.y);
|
||||
textArea.moveCursorSelection(position, TextInput.SelectWords);
|
||||
textArea.selectWord();
|
||||
if (!SpellCheckAdapter.spell(textArea.selectedText)) {
|
||||
var wordList = SpellCheckAdapter.spellSuggestionsRequest(textArea.selectedText);
|
||||
if (wordList.length !== 0) {
|
||||
textAreaContextMenu.addMenuItem(wordList);
|
||||
}
|
||||
}
|
||||
}
|
||||
textAreaContextMenu.openMenuAt(event);
|
||||
@ -237,7 +206,7 @@ JamiFlickable {
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
updateUnderlineText();
|
||||
updateSpellCorrection();
|
||||
if (text !== debounceText && !showPreview) {
|
||||
debounceText = text;
|
||||
MessagesAdapter.userIsComposing(text ? true : false);
|
||||
@ -250,7 +219,7 @@ JamiFlickable {
|
||||
// Shift + Enter -> Next Line
|
||||
Keys.onPressed: function (keyEvent) {
|
||||
// Update underline on each input to take into account deleted text and sent ones
|
||||
updateUnderlineText();
|
||||
updateSpellCorrection();
|
||||
if (keyEvent.matches(StandardKey.Paste)) {
|
||||
MessagesAdapter.onPaste();
|
||||
keyEvent.accepted = true;
|
||||
@ -280,17 +249,17 @@ JamiFlickable {
|
||||
}
|
||||
}
|
||||
|
||||
function updateUnderlineText() {
|
||||
function updateSpellCorrection() {
|
||||
clearUnderlines();
|
||||
// We iterate over the whole text to find words to check and underline them if needed
|
||||
if (spellCheckActive) {
|
||||
if (spellCheckActive && SpellCheckAdapter.hasLoadedDictionary) {
|
||||
var text = textArea.text;
|
||||
var words = MessagesAdapter.findWords(text);
|
||||
var words = SpellCheckAdapter.findWords(text);
|
||||
if (!words)
|
||||
return;
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
var wordInfo = words[i];
|
||||
if (wordInfo && wordInfo.word && !MessagesAdapter.spell(wordInfo.word)) {
|
||||
if (wordInfo && wordInfo.word && !SpellCheckAdapter.spell(wordInfo.word)) {
|
||||
textMetrics.text = wordInfo.word;
|
||||
var xPos = textArea.positionToRectangle(wordInfo.position).x;
|
||||
var yPos = textArea.positionToRectangle(wordInfo.position).y + textArea.positionToRectangle(wordInfo.position).height;
|
||||
|
||||
@ -479,6 +479,9 @@ Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
enabled: !showPreview
|
||||
hoverEnabled: !showPreview
|
||||
Accessible.name: JamiStrings.showMoreMessagingOptions
|
||||
Accessible.role: Accessible.ComboBox
|
||||
Accessible.description: JamiStrings.showMoreMessagingOptionsDescription
|
||||
|
||||
// Used to choose the correct color for the button.
|
||||
readonly property bool highlight: down || hovered
|
||||
|
||||
@ -24,8 +24,26 @@ import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import "../../commoncomponents"
|
||||
|
||||
JamiListView {
|
||||
ListView {
|
||||
id: root
|
||||
property alias verticalScrollBar: verticalScrollBar
|
||||
layer.mipmap: false
|
||||
clip: true
|
||||
|
||||
ScrollBar.vertical: JamiScrollBar {
|
||||
id: verticalScrollBar
|
||||
|
||||
attachedFlickableMoving: root.moving
|
||||
}
|
||||
|
||||
keyNavigationEnabled: true
|
||||
keyNavigationWraps: false
|
||||
|
||||
focus: true
|
||||
activeFocusOnTab: true
|
||||
|
||||
Accessible.role: Accessible.List
|
||||
Accessible.name: JamiStrings.conversationMessages
|
||||
|
||||
function getDistanceToBottom() {
|
||||
const scrollDiff = ScrollBar.vertical.position - (1.0 - ScrollBar.vertical.size);
|
||||
@ -139,7 +157,10 @@ JamiListView {
|
||||
}
|
||||
|
||||
// fade-in mechanism
|
||||
Component.onCompleted: fadeAnimation.start()
|
||||
Component.onCompleted: {
|
||||
positionViewAtBeginning();
|
||||
fadeAnimation.start();
|
||||
}
|
||||
Rectangle {
|
||||
id: overlay
|
||||
anchors.fill: parent
|
||||
@ -194,13 +215,19 @@ JamiListView {
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
currentIndex: -1
|
||||
|
||||
Connections {
|
||||
target: CurrentConversation
|
||||
function onIdChanged() {
|
||||
currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
model: MessagesAdapter.messageListModel
|
||||
delegate: DelegateChooser {
|
||||
id: delegateChooser
|
||||
role: "Type"
|
||||
|
||||
DelegateChoice {
|
||||
id: delegateChoice
|
||||
roleValue: Interaction.Type.TEXT
|
||||
|
||||
TextMessageDelegate {
|
||||
|
||||
@ -220,7 +220,6 @@ Popup {
|
||||
id: cancelBtn
|
||||
objectName: "cancelBtn"
|
||||
z: 1
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.preferredHeight: 20
|
||||
Layout.preferredWidth: 20
|
||||
@ -323,6 +322,9 @@ Popup {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
preferredSize: btnSize
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: toolTipText
|
||||
|
||||
source: JamiResources.stop_rectangle_24dp_svg
|
||||
|
||||
imageColor: JamiTheme.whiteColor
|
||||
|
||||
@ -36,6 +36,36 @@ FocusScope {
|
||||
property color textColor: JamiTheme.textColor
|
||||
property color iconColor: JamiTheme.tintedBlue
|
||||
|
||||
Accessible.role: Accessible.Paragraph
|
||||
Accessible.name: {
|
||||
switch (type) {
|
||||
case "donation":
|
||||
return JamiStrings.donation
|
||||
case "backup":
|
||||
return JamiStrings.backupAccountBtn
|
||||
case "customize":
|
||||
return JamiStrings.customize
|
||||
case "tip":
|
||||
return title
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
Accessible.description: {
|
||||
switch (type) {
|
||||
case "donation":
|
||||
return JamiStrings.donationTipBoxText
|
||||
case "backup":
|
||||
return JamiStrings.whyBackupAccount
|
||||
case "customize":
|
||||
return JamiStrings.customizeText
|
||||
case "tip":
|
||||
return description
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
property string customizeTip: "CustomizeTipBox {}"
|
||||
|
||||
property string backupTip: "BackupTipBox {" + " onIgnore: {" + " root.ignoreClicked()" + " }" + "}"
|
||||
@ -75,6 +105,9 @@ FocusScope {
|
||||
active: type === "donation"
|
||||
focus: true
|
||||
sourceComponent: DonationTipBox {
|
||||
Accessible.name: JamiStrings.donation
|
||||
Accessible.description: JamiStrings.donationTipBoxText
|
||||
Accessible.role: Accessible.Link
|
||||
maxHeight: root.maximumHeight
|
||||
textColor: root.textColor
|
||||
iconColor: root.iconColor
|
||||
@ -86,6 +119,9 @@ FocusScope {
|
||||
id: loader_backupTip
|
||||
active: type === "backup"
|
||||
sourceComponent: BackupTipBox {
|
||||
Accessible.name: JamiStrings.backupAccountBtn
|
||||
Accessible.description: JamiStrings.whyBackupAccount
|
||||
Accessible.role: Accessible.Link
|
||||
onIgnore: {
|
||||
root.ignoreClicked();
|
||||
}
|
||||
@ -95,20 +131,24 @@ FocusScope {
|
||||
}
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader_customizeTip
|
||||
active: type === "customize"
|
||||
sourceComponent: CustomizeTipBox {
|
||||
Accessible.role: Accessible.Link
|
||||
textColor: root.textColor
|
||||
iconColor: root.iconColor
|
||||
}
|
||||
width: parent.width
|
||||
focus: true
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader_infoTip
|
||||
active: type === "tip"
|
||||
sourceComponent: InformativeTipBox {
|
||||
Accessible.role: Accessible.Link
|
||||
maxHeight: root.maximumHeight
|
||||
textColor: root.textColor
|
||||
iconColor: root.iconColor
|
||||
@ -159,6 +199,8 @@ FocusScope {
|
||||
id: component_btnClose
|
||||
PushButton {
|
||||
id: btnClose
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: JamiStrings.dismissTip
|
||||
|
||||
width: 20
|
||||
height: 20
|
||||
|
||||
@ -18,7 +18,6 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import net.jami.Enums 1.1
|
||||
@ -39,6 +38,10 @@ ListSelectionView {
|
||||
onPresented: LRCInstance.deselectConversation()
|
||||
leftPaneItem: viewCoordinator.getView("SidePanel", true)
|
||||
|
||||
Accessible.role: Accessible.Pane
|
||||
Accessible.name: title
|
||||
Accessible.description: JamiStrings.description
|
||||
|
||||
property variant uiCustomization: CurrentAccount.uiCustomization
|
||||
|
||||
onUiCustomizationChanged: {
|
||||
@ -277,6 +280,9 @@ ListSelectionView {
|
||||
preferredWidth: textSize.width + 2 * JamiTheme.preferredMarginSize
|
||||
text: JamiStrings.aboutJami
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: text
|
||||
|
||||
onClicked: viewCoordinator.presentDialog(appWindow, "mainview/components/AboutPopUp.qml")
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,6 @@
|
||||
#include "qtutils.h"
|
||||
#include "messageparser.h"
|
||||
#include "previewengine.h"
|
||||
#include "spellchecker.h"
|
||||
|
||||
#include <api/datatransfermodel.h>
|
||||
#include <api/contact.h>
|
||||
@ -40,25 +39,17 @@
|
||||
#include <QtMath>
|
||||
#include <QRegExp>
|
||||
|
||||
#define SUGGESTIONS_MAX_SIZE 3 // limit the number of spelling suggestions
|
||||
|
||||
MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager,
|
||||
PreviewEngine* previewEngine,
|
||||
SpellCheckDictionaryManager* spellCheckDictionaryManager,
|
||||
LRCInstance* instance,
|
||||
QObject* parent)
|
||||
: QmlAdapterBase(instance, parent)
|
||||
, settingsManager_(settingsManager)
|
||||
, spellCheckDictionaryManager_(spellCheckDictionaryManager)
|
||||
, messageParser_(new MessageParser(previewEngine, this))
|
||||
, filteredMsgListModel_(new FilteredMsgListModel(this))
|
||||
, mediaInteractions_(std::make_unique<MessageListModel>(nullptr))
|
||||
, timestampTimer_(new QTimer(this))
|
||||
{
|
||||
#if defined(Q_OS_LINUX)
|
||||
// Initialize with make_shared
|
||||
spellChecker_ = std::make_shared<SpellChecker>(spellCheckDictionaryManager_->getDictionaryPath());
|
||||
#endif
|
||||
setObjectName(typeid(*this).name());
|
||||
|
||||
set_messageListModel(QVariant::fromValue(filteredMsgListModel_));
|
||||
@ -736,53 +727,3 @@ MessagesAdapter::getMsgListSourceModel() const
|
||||
// However it may be a nullptr if not yet set.
|
||||
return static_cast<MessageListModel*>(filteredMsgListModel_->sourceModel());
|
||||
}
|
||||
|
||||
bool
|
||||
MessagesAdapter::spell(const QString& word)
|
||||
{
|
||||
return spellChecker_->spell(word);
|
||||
}
|
||||
|
||||
QVariantList
|
||||
MessagesAdapter::spellSuggestionsRequest(const QString& word)
|
||||
{
|
||||
QStringList suggestionsList;
|
||||
QVariantList variantList;
|
||||
if (spellChecker_ == nullptr || spellChecker_->spell(word)) {
|
||||
return variantList;
|
||||
}
|
||||
|
||||
suggestionsList = spellChecker_->suggest(word);
|
||||
for (const auto& suggestion : suggestionsList) {
|
||||
if (variantList.size() >= SUGGESTIONS_MAX_SIZE) {
|
||||
break;
|
||||
}
|
||||
variantList.append(QVariant(suggestion));
|
||||
}
|
||||
|
||||
return variantList;
|
||||
}
|
||||
|
||||
QVariantList
|
||||
MessagesAdapter::findWords(const QString& text)
|
||||
{
|
||||
QVariantList result;
|
||||
if (!spellChecker_)
|
||||
return result;
|
||||
|
||||
auto words = spellChecker_->findWords(text);
|
||||
for (const auto& word : words) {
|
||||
QVariantMap wordInfo;
|
||||
wordInfo["word"] = word.word;
|
||||
wordInfo["position"] = word.position;
|
||||
wordInfo["length"] = word.length;
|
||||
result.append(wordInfo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
MessagesAdapter::updateDictionnary(const QString& path)
|
||||
{
|
||||
return spellChecker_->replaceDictionary(path);
|
||||
}
|
||||
|
||||
@ -23,8 +23,6 @@
|
||||
#include "previewengine.h"
|
||||
#include "messageparser.h"
|
||||
#include "appsettingsmanager.h"
|
||||
#include "spellchecker.h"
|
||||
#include "spellcheckdictionarymanager.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
@ -102,14 +100,11 @@ public:
|
||||
{
|
||||
return new MessagesAdapter(qApp->property("AppSettingsManager").value<AppSettingsManager*>(),
|
||||
qApp->property("PreviewEngine").value<PreviewEngine*>(),
|
||||
qApp->property("SpellCheckDictionaryManager")
|
||||
.value<SpellCheckDictionaryManager*>(),
|
||||
qApp->property("LRCInstance").value<LRCInstance*>());
|
||||
}
|
||||
|
||||
explicit MessagesAdapter(AppSettingsManager* settingsManager,
|
||||
PreviewEngine* previewEngine,
|
||||
SpellCheckDictionaryManager* spellCheckDictionaryManager,
|
||||
LRCInstance* instance,
|
||||
QObject* parent = nullptr);
|
||||
~MessagesAdapter() = default;
|
||||
@ -168,10 +163,6 @@ public:
|
||||
Q_INVOKABLE QVariant dataForInteraction(const QString& interactionId,
|
||||
int role = Qt::DisplayRole) const;
|
||||
Q_INVOKABLE void startSearch(const QString& text, bool isMedia);
|
||||
Q_INVOKABLE QVariantList spellSuggestionsRequest(const QString& word);
|
||||
Q_INVOKABLE bool spell(const QString& word);
|
||||
Q_INVOKABLE void updateDictionnary(const QString& path);
|
||||
Q_INVOKABLE QVariantList findWords(const QString& text);
|
||||
|
||||
// Run corrsponding js functions, c++ to qml.
|
||||
void setMessagesImageContent(const QString& path, bool isBased64 = false);
|
||||
@ -206,12 +197,10 @@ private:
|
||||
QList<QString> conversationTypersUrlToName(const QSet<QString>& typersSet);
|
||||
|
||||
AppSettingsManager* settingsManager_;
|
||||
SpellCheckDictionaryManager* spellCheckDictionaryManager_;
|
||||
MessageParser* messageParser_;
|
||||
FilteredMsgListModel* filteredMsgListModel_;
|
||||
std::unique_ptr<MessageListModel> mediaInteractions_;
|
||||
QTimer* timestampTimer_;
|
||||
std::shared_ptr<SpellChecker> spellChecker_;
|
||||
static constexpr const int loadChunkSize_ {20};
|
||||
static constexpr const int timestampUpdateIntervalMs_ {1000};
|
||||
};
|
||||
|
||||
@ -25,6 +25,14 @@ Item {
|
||||
property string qmlFilePrefix: "file:/"
|
||||
readonly property string base64StringTitle: "data:image/png;base64,"
|
||||
|
||||
readonly property var webEngineNames: {
|
||||
"mediaPreview": "mediaPreviewWebEngine",
|
||||
"videoPreview": "videoPreviewWebEngine",
|
||||
"map": "mapWebEngine",
|
||||
"general": "generalWebEngine",
|
||||
"emojiPicker": "emojiPickerWebEngine"
|
||||
}
|
||||
|
||||
property var accountCreationInputParaObject: ({})
|
||||
|
||||
function setUpAccountCreationInputPara(inputPara) {
|
||||
|
||||
@ -111,6 +111,7 @@ Item {
|
||||
property string keyboardShortcut: qsTr("Keyboard shortcut")
|
||||
property string changeKeyboardShortcut: qsTr("Change keyboard shortcut")
|
||||
property string raiseWhenCalled: qsTr("Bring the application to the front for incoming calls")
|
||||
property string denySecondCall: qsTr("Decline incoming calls when already in a call")
|
||||
|
||||
// ChangePttKeyPopup
|
||||
property string changeShortcut: qsTr("Change shortcut")
|
||||
@ -272,9 +273,9 @@ Item {
|
||||
property string hideSpectators: qsTr("Hide spectators")
|
||||
|
||||
// LineEditContextMenu
|
||||
property string copy: qsTr("Copy")
|
||||
property string share: qsTr("Share")
|
||||
property string cut: qsTr("Cut")
|
||||
property string copy: qsTr("Copy")
|
||||
property string paste: qsTr("Paste")
|
||||
property string language: qsTr("Language")
|
||||
|
||||
@ -788,7 +789,7 @@ Item {
|
||||
property string replyTo: qsTr("Reply to")
|
||||
property string inReplyTo: qsTr("In reply to")
|
||||
property string repliedTo: qsTr("%1 replied to")
|
||||
property string inReplyToMe: qsTr("Me")
|
||||
property string inReplyToYou: qsTr("you")
|
||||
property string reply: qsTr("Reply")
|
||||
property string writeTo: qsTr("Write to %1")
|
||||
property string writeToNewContact: qsTr("Send a message to %1 in order to add them as a contact")
|
||||
@ -912,9 +913,42 @@ Item {
|
||||
|
||||
// Spell checker
|
||||
property string checkSpelling: qsTr("Check spelling while typing")
|
||||
property string systemDictionary: qsTr("System")
|
||||
property string textLanguage: qsTr("Text language")
|
||||
property string textLanguageDescription: qsTr("To install new dictionaries, use the system package manager.")
|
||||
property string spellChecker: qsTr("Spell checker")
|
||||
property string refresh: qsTr("Refresh")
|
||||
property string refreshInstalledDictionaries: qsTr("Refresh installed dictionaries")
|
||||
property string searchTextLanguages: qsTr("Search text languages")
|
||||
property string searchAvailableTextLanguages: qsTr("Search for available text languages")
|
||||
property string noDictionariesFoundFor: qsTr("No dictionaries found for '%1'")
|
||||
property string noDictionariesAvailable: qsTr("No dictionaries available")
|
||||
property string manageDictionaries: qsTr("Manage Dictionaries")
|
||||
property string spellCheckDownloadFailed: qsTr("Download failed for dictionary '%1'")
|
||||
property string showInstalledDictionaries: qsTr("Show installed")
|
||||
property string showInstalledDictionariesDescription: qsTr("Only show dictionaries that are currently installed")
|
||||
|
||||
// Accessibility
|
||||
property string switchToAccount: qsTr("Press enter to switch to this account")
|
||||
property string qrCodeExplanation: qsTr("Display your QR code to allow other users to scan it and add you as a contact")
|
||||
property string accountList: qsTr("Account list")
|
||||
property string accountListDescription: qsTr("Use arrows to switch between available account")
|
||||
property string languageComboBoxExplanation: qsTr("Select the user interface language")
|
||||
property string backButtonExplanation: qsTr("Go back to the previous page")
|
||||
property string adviceBox: qsTr("Advice Box")
|
||||
property string backButton: qsTr("Back button")
|
||||
property string adviceBoxExplanation: qsTr("Open the advice popup that contains information about Jami")
|
||||
property string more: qsTr("More")
|
||||
property string pressToAction: qsTr("Press to %1")
|
||||
property string pressToToggle: qsTr("Press to toggle %1 (%2)")
|
||||
property string active: qsTr("active")
|
||||
property string inactive: qsTr("inactive")
|
||||
property string minimize: qsTr("Minimize application")
|
||||
property string maximize: qsTr("Maximize application")
|
||||
property string closeApplication: qsTr("Close application")
|
||||
property string dismissTip: qsTr("Dismiss this tip")
|
||||
property string tipDescription: qsTr("Tips to help you use Jami more effectively")
|
||||
property string showMoreMessagingOptions: qsTr("Show more messaging options")
|
||||
property string showMoreMessagingOptionsDescription: qsTr("Open a menu that allows you to send voice and video messages as well as sharing your location")
|
||||
property string conversationMessages: qsTr("Conversation messages list. Use arrow keys to navigate through messages.")
|
||||
property string dataTransfer: qsTr("Data transfer")
|
||||
property string status: qsTr("Status")
|
||||
property string readBy: qsTr("Read by")
|
||||
}
|
||||
|
||||
@ -83,6 +83,7 @@ Item {
|
||||
property color editBackgroundColor: darkTheme ? "#373737" : lightGrey_
|
||||
property color textColor: primaryForegroundColor
|
||||
property color textColorHovered: darkTheme ? "#cccccc" : "#333333"
|
||||
property color textColorHoveredHighContrast: darkTheme ? "#6a6a6a" : "#a7a7a7"
|
||||
property color tabbarBorderColor: darkTheme ? blackColor : "#e3e3e3"
|
||||
property color popupOverlayColor: darkTheme ? Qt.rgba(255, 255, 255, 0.22) : Qt.rgba(0, 0, 0, 0.33)
|
||||
property real formsRadius: 30
|
||||
@ -103,7 +104,7 @@ Item {
|
||||
property color pressedButtonColor: darkTheme ? pressColor : "#a0a0a0"
|
||||
property color hoveredButtonColor: darkTheme ? "#4d4d4d" : "#dedede"
|
||||
property color hoveredButtonColorWizard: darkTheme ? "#4d4d4d" : "#dedede"
|
||||
property color normalButtonColor: darkTheme ? backgroundColor : "#e0e0e0"
|
||||
property color normalButtonColor: darkTheme ? "#bfbfbf" : "#e0e0e0"
|
||||
|
||||
property color invertedPressedButtonColor: Qt.rgba(0, 0, 0, 0.5)
|
||||
property color invertedHoveredButtonColor: Qt.rgba(0, 0, 0, 0.6)
|
||||
|
||||
@ -36,7 +36,8 @@
|
||||
#include "currentaccounttomigrate.h"
|
||||
#include "pttlistener.h"
|
||||
#include "calloverlaymodel.h"
|
||||
#include "spellcheckdictionarymanager.h"
|
||||
#include "spellcheckdictionarylistmodel.h"
|
||||
#include "spellcheckadapter.h"
|
||||
#include "accountlistmodel.h"
|
||||
#include "mediacodeclistmodel.h"
|
||||
#include "audiodevicemodel.h"
|
||||
@ -65,7 +66,6 @@
|
||||
#include "wizardviewstepmodel.h"
|
||||
#include "linkdevicemodel.h"
|
||||
#include "qrcodescannermodel.h"
|
||||
#include "spellchecker.h"
|
||||
|
||||
#include "api/peerdiscoverymodel.h"
|
||||
#include "api/codecmodel.h"
|
||||
@ -119,7 +119,6 @@ registerTypes(QQmlEngine* engine,
|
||||
LRCInstance* lrcInstance,
|
||||
SystemTray* systemTray,
|
||||
AppSettingsManager* settingsManager,
|
||||
SpellCheckDictionaryManager* spellCheckDictionaryManager,
|
||||
ConnectivityMonitor* connectivityMonitor,
|
||||
PreviewEngine* previewEngine,
|
||||
ScreenInfo* screenInfo,
|
||||
@ -196,6 +195,10 @@ registerTypes(QQmlEngine* engine,
|
||||
QQmlEngine::setObjectOwnership(linkdevicemodel, QQmlEngine::CppOwnership);
|
||||
REG_QML_SINGLETON<LinkDeviceModel>(REG_MODEL, "LinkDeviceModel", CREATE(linkdevicemodel));
|
||||
|
||||
// SpellCheckDictionaryListModel (available through SpellCheckAdapter)
|
||||
auto spellCheckDictionaryListModel = new SpellCheckDictionaryListModel(settingsManager, connectivityMonitor, app);
|
||||
qApp->setProperty("SpellCheckDictionaryListModel", QVariant::fromValue(spellCheckDictionaryListModel));
|
||||
|
||||
// Register app-level objects that are used by QML created objects.
|
||||
// These MUST be set prior to loading the initial QML file, in order to
|
||||
// be available to the QML adapter class factory creation methods.
|
||||
@ -204,7 +207,6 @@ registerTypes(QQmlEngine* engine,
|
||||
qApp->setProperty("AppSettingsManager", QVariant::fromValue(settingsManager));
|
||||
qApp->setProperty("ConnectivityMonitor", QVariant::fromValue(connectivityMonitor));
|
||||
qApp->setProperty("PreviewEngine", QVariant::fromValue(previewEngine));
|
||||
qApp->setProperty("SpellCheckDictionaryManager", QVariant::fromValue(spellCheckDictionaryManager));
|
||||
|
||||
// qml adapter registration
|
||||
QML_REGISTERSINGLETON_TYPE(NS_HELPERS, QRCodeScannerModel);
|
||||
@ -225,6 +227,7 @@ registerTypes(QQmlEngine* engine,
|
||||
QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, VideoDevices);
|
||||
QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CurrentAccountToMigrate);
|
||||
QML_REGISTERSINGLETON_TYPE(NS_HELPERS, FileDownloader);
|
||||
QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, SpellCheckAdapter);
|
||||
|
||||
// TODO: remove these
|
||||
QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, AVModel, &lrcInstance->avModel())
|
||||
@ -241,7 +244,6 @@ registerTypes(QQmlEngine* engine,
|
||||
QML_REGISTERTYPE(NS_MODELS, PluginListPreferenceModel);
|
||||
QML_REGISTERTYPE(NS_MODELS, FilesToSendListModel);
|
||||
QML_REGISTERTYPE(NS_MODELS, CallInformationListModel);
|
||||
QML_REGISTERTYPE(NS_MODELS, SpellChecker);
|
||||
|
||||
// Roles & type enums for models
|
||||
QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList");
|
||||
@ -250,12 +252,12 @@ registerTypes(QQmlEngine* engine,
|
||||
QML_REGISTERNAMESPACE(NS_MODELS, FilesToSend::staticMetaObject, "FilesToSend");
|
||||
QML_REGISTERNAMESPACE(NS_MODELS, MessageList::staticMetaObject, "MessageList");
|
||||
QML_REGISTERNAMESPACE(NS_MODELS, PluginStatus::staticMetaObject, "PluginStatus");
|
||||
// QML_REGISTERNAMESPACE(NS_MODELS, SpellCheckDictionaryList::staticMetaObject, "SpellCheckDictionaryList");
|
||||
|
||||
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, app, "MainApplication")
|
||||
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, screenInfo, "CurrentScreenInfo")
|
||||
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, lrcInstance, "LRCInstance")
|
||||
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, settingsManager, "AppSettingsManager")
|
||||
QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, spellCheckDictionaryManager, "SpellCheckDictionaryManager")
|
||||
|
||||
// Lrc namespaces, models, and singletons
|
||||
QML_REGISTERNAMESPACE(NS_MODELS, lrc::api::staticMetaObject, "Lrc");
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
class SystemTray;
|
||||
class LRCInstance;
|
||||
class AppSettingsManager;
|
||||
class SpellCheckDictionaryManager;
|
||||
class PreviewEngine;
|
||||
class ScreenInfo;
|
||||
class MainApplication;
|
||||
@ -62,7 +61,6 @@ void registerTypes(QQmlEngine* engine,
|
||||
LRCInstance* lrcInstance,
|
||||
SystemTray* systemTray,
|
||||
AppSettingsManager* appSettingsManager,
|
||||
SpellCheckDictionaryManager* spellCheckDictionaryManager,
|
||||
ConnectivityMonitor* connectivityMonitor,
|
||||
PreviewEngine* previewEngine,
|
||||
ScreenInfo* screenInfo,
|
||||
|
||||
@ -265,11 +265,6 @@ SidePanelBase {
|
||||
|
||||
header: AccountComboBox { QWKSetParentHitTestVisible {}
|
||||
id: accountComboBox
|
||||
Shortcut {
|
||||
sequence: "Ctrl+J"
|
||||
context: Qt.ApplicationShortcut
|
||||
onActivated: accountComboBox.togglePopup()
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
|
||||
@ -27,7 +27,6 @@ import "../../commoncomponents"
|
||||
import "../../mainview/components"
|
||||
import "../../mainview/js/contactpickercreation.js" as ContactPickerCreation
|
||||
|
||||
|
||||
SettingsPageBase {
|
||||
id: root
|
||||
|
||||
@ -103,6 +102,14 @@ SettingsPageBase {
|
||||
checked: UtilsAdapter.getAppValue(Settings.RaiseWhenCalled)
|
||||
onSwitchToggled: UtilsAdapter.setAppValue(Settings.Key.RaiseWhenCalled, checked)
|
||||
}
|
||||
|
||||
ToggleSwitch {
|
||||
id: checkBoxDenySecondCall
|
||||
|
||||
labelText: JamiStrings.denySecondCall
|
||||
checked: CurrentAccount.denySecondCall
|
||||
onSwitchToggled: CurrentAccount.denySecondCall = checked
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@ -383,7 +390,7 @@ SettingsPageBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
ColumnLayout{
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 9
|
||||
Text {
|
||||
@ -400,7 +407,7 @@ SettingsPageBase {
|
||||
labelText: JamiStrings.enablePTT
|
||||
checked: UtilsAdapter.getAppValue(Settings.EnablePtt)
|
||||
onSwitchToggled: {
|
||||
UtilsAdapter.setAppValue(Settings.Key.EnablePtt, checked)
|
||||
UtilsAdapter.setAppValue(Settings.Key.EnablePtt, checked);
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
@ -426,13 +433,13 @@ SettingsPageBase {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
anchors.centerIn: parent
|
||||
width: keyLabel.width + 2 * JamiTheme.preferredMarginSize
|
||||
height: keyLabel.height + JamiTheme.preferredMarginSize
|
||||
color: JamiTheme.lightGrey_
|
||||
border.color: JamiTheme.darkGreyColor
|
||||
radius: 4
|
||||
id: backgroundRect
|
||||
anchors.centerIn: parent
|
||||
width: keyLabel.width + 2 * JamiTheme.preferredMarginSize
|
||||
height: keyLabel.height + JamiTheme.preferredMarginSize
|
||||
color: JamiTheme.lightGrey_
|
||||
border.color: JamiTheme.darkGreyColor
|
||||
radius: 4
|
||||
}
|
||||
}
|
||||
MaterialButton {
|
||||
@ -444,8 +451,8 @@ SettingsPageBase {
|
||||
onClicked: {
|
||||
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ChangePttKeyPopup.qml");
|
||||
dlg.choiceMade.connect(function (chosenKey) {
|
||||
keyLabel.text = PTTListener.keyToString(chosenKey);
|
||||
});
|
||||
keyLabel.text = PTTListener.keyToString(chosenKey);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,11 +17,13 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import net.jami.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Enums 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import net.jami.Helpers 1.1
|
||||
import SortFilterProxyModel 0.2
|
||||
import "../../commoncomponents"
|
||||
import "../../mainview/components"
|
||||
import "../../mainview/js/contactpickercreation.js" as ContactPickerCreation
|
||||
@ -41,6 +43,63 @@ SettingsPageBase {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: JamiTheme.preferredSettingsMarginSize
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
width: parent.width
|
||||
spacing: JamiTheme.settingsCategorySpacing
|
||||
|
||||
Text {
|
||||
id: spellCheckerTitle
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.preferredWidth: parent.width
|
||||
|
||||
text: JamiStrings.spellChecker
|
||||
color: JamiTheme.textColor
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
font.pixelSize: JamiTheme.settingsTitlePixelSize
|
||||
font.kerning: true
|
||||
}
|
||||
|
||||
ToggleSwitch {
|
||||
id: enableSpellCheckToggleSwitch
|
||||
Layout.fillWidth: true
|
||||
visible: true
|
||||
|
||||
checked: UtilsAdapter.getAppValue(Settings.Key.EnableSpellCheck)
|
||||
labelText: JamiStrings.checkSpelling
|
||||
tooltipText: JamiStrings.checkSpelling
|
||||
onSwitchToggled: {
|
||||
UtilsAdapter.setAppValue(Settings.Key.EnableSpellCheck, checked);
|
||||
}
|
||||
}
|
||||
|
||||
SpellCheckLanguageComboBox {
|
||||
id: spellCheckLangComboBoxSetting
|
||||
Layout.fillWidth: true
|
||||
widthOfComboBox: itemWidth
|
||||
}
|
||||
|
||||
// A button to open the dictionary install view as a popup
|
||||
MaterialButton {
|
||||
id: dictionaryInstallButton
|
||||
|
||||
secondary: true
|
||||
|
||||
preferredWidth: itemWidth
|
||||
height: spellCheckLangComboBoxSetting.comboBox.height
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
text: JamiStrings.manageDictionaries
|
||||
onClicked: {
|
||||
viewCoordinator.presentDialog(appWindow, "commoncomponents/ManageDictionariesDialog.qml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: generalSettings
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ SettingsPageBase {
|
||||
flickableContent: ColumnLayout {
|
||||
id: manageAccountColumnLayout
|
||||
|
||||
width: contentFlickableWidth
|
||||
spacing: JamiTheme.settingsBlockSpacing
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: JamiTheme.preferredSettingsMarginSize
|
||||
|
||||
@ -32,6 +32,7 @@ RowLayout {
|
||||
property alias fontPointSize: comboBoxOfLayout.font.pointSize
|
||||
property alias modelIndex: comboBoxOfLayout.currentIndex
|
||||
property alias modelSize: comboBoxOfLayout.count
|
||||
property alias comboBox: comboBoxOfLayout
|
||||
|
||||
property int widthOfComboBox: 50
|
||||
|
||||
@ -53,6 +54,7 @@ RowLayout {
|
||||
SettingParaCombobox {
|
||||
id: comboBoxOfLayout
|
||||
|
||||
enabled: root.enabled
|
||||
Layout.preferredWidth: widthOfComboBox
|
||||
|
||||
font.pointSize: JamiTheme.buttonFontSize
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import net.jami.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Enums 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import net.jami.Helpers 1.1
|
||||
import SortFilterProxyModel 0.2
|
||||
import "../../commoncomponents"
|
||||
import "../../mainview/components"
|
||||
|
||||
SettingsComboBox {
|
||||
id: root
|
||||
height: JamiTheme.preferredFieldHeight
|
||||
labelText: JamiStrings.textLanguage
|
||||
tipText: JamiStrings.textLanguage
|
||||
comboModel: SortFilterProxyModel {
|
||||
id: installedDictionariesModel
|
||||
sourceModel: SpellCheckAdapter.getDictionaryListModel()
|
||||
|
||||
// Filter to show only installed dictionaries
|
||||
filters: ValueFilter {
|
||||
roleName: "Installed"
|
||||
value: true
|
||||
}
|
||||
|
||||
// Sort alphabetically by native name
|
||||
sorters: RoleSorter {
|
||||
roleName: "NativeName"
|
||||
sortOrder: Qt.AscendingOrder
|
||||
}
|
||||
Component.onCompleted: {
|
||||
// Ensure the model is updated with the latest dictionaries
|
||||
root.enabled = Qt.binding(function () {
|
||||
return installedDictionariesModel.count > 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
role: "NativeName"
|
||||
|
||||
// Show placeholder when disabled
|
||||
placeholderText: JamiStrings.none
|
||||
|
||||
function getCurrentLocaleIndex() {
|
||||
var currentLang = UtilsAdapter.getAppValue(Settings.Key.SpellLang);
|
||||
for (var i = 0; i < comboModel.count; i++) {
|
||||
var item = comboModel.get(i);
|
||||
if (item.Locale === currentLang)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set initial selection based on current spell language setting
|
||||
Component.onCompleted: modelIndex = getCurrentLocaleIndex()
|
||||
|
||||
property string locale
|
||||
function setForIndex(index) {
|
||||
var selectedDict = comboModel.get(index);
|
||||
if (selectedDict && selectedDict.Locale && selectedDict.Installed) {
|
||||
locale = selectedDict.Locale;
|
||||
}
|
||||
}
|
||||
onLocaleChanged: SpellCheckAdapter.setDictionary(locale)
|
||||
|
||||
// When the count changes, we might need to update the model index
|
||||
readonly property int count: installedDictionariesModel.count
|
||||
onCountChanged: {
|
||||
modelIndex = getCurrentLocaleIndex();
|
||||
// If the new index is -1 and we still have dictionaries, use the first one
|
||||
if (modelIndex === -1 && installedDictionariesModel.count > 0) {
|
||||
modelIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If the model index changes programmatically, we need to update the dictionary path
|
||||
onModelIndexChanged: setForIndex(modelIndex)
|
||||
}
|
||||
@ -185,165 +185,6 @@ SettingsPageBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
|
||||
width: parent.width
|
||||
spacing: JamiTheme.settingsCategorySpacing
|
||||
visible: (Qt.platform.os.toString() !== "linux") ? false : true
|
||||
|
||||
Text {
|
||||
id: spellCheckerTitle
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.preferredWidth: parent.width
|
||||
|
||||
text: JamiStrings.spellChecker
|
||||
color: JamiTheme.textColor
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
font.pixelSize: JamiTheme.settingsTitlePixelSize
|
||||
font.kerning: true
|
||||
}
|
||||
|
||||
ToggleSwitch {
|
||||
id: enableSpellCheckToggleSwitch
|
||||
Layout.fillWidth: true
|
||||
visible: true
|
||||
|
||||
checked: UtilsAdapter.getAppValue(Settings.Key.EnableSpellCheck)
|
||||
labelText: JamiStrings.checkSpelling
|
||||
descText: JamiStrings.textLanguageDescription
|
||||
tooltipText: JamiStrings.checkSpelling
|
||||
onSwitchToggled: {
|
||||
UtilsAdapter.setAppValue(Settings.Key.EnableSpellCheck, checked);
|
||||
}
|
||||
}
|
||||
|
||||
SettingsComboBox {
|
||||
id: spellCheckLangComboBoxSetting
|
||||
|
||||
Layout.fillWidth: true
|
||||
height: JamiTheme.preferredFieldHeight
|
||||
|
||||
labelText: JamiStrings.textLanguage
|
||||
tipText: JamiStrings.textLanguage
|
||||
comboModel: ListModel {
|
||||
id: installedSpellCheckLangModel
|
||||
Component.onCompleted: {
|
||||
var supported = SpellCheckDictionaryManager.installedDictionaries();
|
||||
var keys = Object.keys(supported);
|
||||
var currentKey = UtilsAdapter.getAppValue(Settings.Key.SpellLang);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
append({
|
||||
"textDisplay": supported[keys[i]],
|
||||
"id": keys[i]
|
||||
});
|
||||
if (keys[i] === currentKey)
|
||||
spellCheckLangComboBoxSetting.modelIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
widthOfComboBox: itemWidth
|
||||
role: "textDisplay"
|
||||
|
||||
onActivated: {
|
||||
UtilsAdapter.setAppValue(Settings.Key.SpellLang, comboModel.get(modelIndex).id);
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: JamiTheme.preferredFieldHeight
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.rightMargin: JamiTheme.preferredMarginSize
|
||||
|
||||
color: JamiTheme.textColor
|
||||
wrapMode: Text.WordWrap
|
||||
text: JamiStrings.refreshInstalledDictionaries
|
||||
font.pointSize: JamiTheme.settingsFontSize
|
||||
font.kerning: true
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
MaterialButton {
|
||||
id: refreshInstalledDictionariesPushButton
|
||||
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
preferredWidth: textSizeRefresh.width + 2 * JamiTheme.buttontextWizzardPadding
|
||||
buttontextHeightMargin: JamiTheme.buttontextHeightMargin
|
||||
|
||||
primary: true
|
||||
toolTipText: JamiStrings.refresh
|
||||
|
||||
text: JamiStrings.refresh
|
||||
|
||||
onClicked: {
|
||||
SpellCheckDictionaryManager.refreshDictionaries();
|
||||
var langIdx = spellCheckLangComboBoxSetting.modelIndex;
|
||||
installedSpellCheckLangModel.clear();
|
||||
var supported = SpellCheckDictionaryManager.installedDictionaries();
|
||||
var keys = Object.keys(supported);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
installedSpellCheckLangModel.append({
|
||||
"textDisplay": supported[keys[i]],
|
||||
"id": keys[i]
|
||||
});
|
||||
}
|
||||
spellCheckLangComboBoxSetting.modelIndex = langIdx;
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: textSizeRefresh
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize
|
||||
font.capitalization: Font.AllUppercase
|
||||
text: refreshInstalledDictionariesPushButton.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: UtilsAdapter
|
||||
|
||||
function onChangeLanguage() {
|
||||
var langIdx = langComboBoxSetting.modelIndex;
|
||||
langModel.clear();
|
||||
var supported = UtilsAdapter.supportedLang();
|
||||
var keys = Object.keys(supported);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
langModel.append({
|
||||
"textDisplay": supported[keys[i]],
|
||||
"id": keys[i]
|
||||
});
|
||||
}
|
||||
langComboBoxSetting.modelIndex = langIdx;
|
||||
}
|
||||
|
||||
// Repopulate the spell check language list
|
||||
function onSpellLanguageChanged() {
|
||||
var langIdx = spellCheckLangComboBoxSetting.modelIndex;
|
||||
installedSpellCheckLangModel.clear();
|
||||
var supported = SpellCheckDictionaryManager.installedDictionaries();
|
||||
var keys = Object.keys(supported);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
installedSpellCheckLangModel.append({
|
||||
"textDisplay": supported[keys[i]],
|
||||
"id": keys[i]
|
||||
});
|
||||
}
|
||||
spellCheckLangComboBoxSetting.modelIndex = langIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
|
||||
142
src/app/spellcheckadapter.cpp
Normal file
142
src/app/spellcheckadapter.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "spellcheckadapter.h"
|
||||
|
||||
#include "spellcheckdictionarylistmodel.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#define SUGGESTIONS_MAX_SIZE 10 // limit the number of spelling suggestions
|
||||
|
||||
SpellCheckAdapter::SpellCheckAdapter(SpellCheckDictionaryListModel* dictionaryListModel,
|
||||
AppSettingsManager* settingsManager,
|
||||
QObject* parent)
|
||||
: QObject(parent)
|
||||
, dictionaryListModel_(dictionaryListModel)
|
||||
, settingsManager_(settingsManager)
|
||||
{
|
||||
// Connect to update the selected dictionary if no dictionary is set on start
|
||||
connect(dictionaryListModel_,
|
||||
&SpellCheckDictionaryListModel::newDictionaryAvailable,
|
||||
this,
|
||||
&SpellCheckAdapter::setDictionary);
|
||||
|
||||
// Load the current dictionary if available
|
||||
auto currentLocale = settingsManager_->getValue(Settings::Key::SpellLang).toString();
|
||||
if (!currentLocale.isEmpty()) {
|
||||
setDictionary(currentLocale);
|
||||
}
|
||||
|
||||
// Listen for data changes to the dictionaryListModel_ to determine
|
||||
// our installedDictionaryCount
|
||||
connect(dictionaryListModel_,
|
||||
&QAbstractItemModel::dataChanged,
|
||||
this,
|
||||
[this](const QModelIndex&, const QModelIndex&, const QList<int>& roles) {
|
||||
if (roles.contains(SpellCheckDictionaryList::Installed)) {
|
||||
set_installedDictionaryCount(
|
||||
dictionaryListModel_->getInstalledDictionaries().size());
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize the installedDictionaryCount
|
||||
set_installedDictionaryCount(dictionaryListModel_->getInstalledDictionaries().size());
|
||||
|
||||
// Listen for download failure
|
||||
connect(dictionaryListModel_,
|
||||
&SpellCheckDictionaryListModel::downloadFailed,
|
||||
this,
|
||||
&SpellCheckAdapter::downloadFailed);
|
||||
}
|
||||
|
||||
void
|
||||
SpellCheckAdapter::installDictionary(const QString& locale)
|
||||
{
|
||||
dictionaryListModel_->installDictionary(locale);
|
||||
}
|
||||
|
||||
void
|
||||
SpellCheckAdapter::uninstallDictionary(const QString& locale)
|
||||
{
|
||||
dictionaryListModel_->uninstallDictionary(locale);
|
||||
}
|
||||
|
||||
QVariant
|
||||
SpellCheckAdapter::getDictionaryListModel() const
|
||||
{
|
||||
return QVariant::fromValue(dictionaryListModel_);
|
||||
}
|
||||
|
||||
QVariantMap
|
||||
SpellCheckAdapter::getInstalledDictionaries() const
|
||||
{
|
||||
return dictionaryListModel_->getInstalledDictionaries();
|
||||
}
|
||||
|
||||
bool
|
||||
SpellCheckAdapter::spell(const QString& word)
|
||||
{
|
||||
return spellChecker_.spell(word);
|
||||
}
|
||||
|
||||
QVariantList
|
||||
SpellCheckAdapter::spellSuggestionsRequest(const QString& word)
|
||||
{
|
||||
QStringList suggestionsList;
|
||||
QVariantList variantList;
|
||||
if (spellChecker_.spell(word)) {
|
||||
return variantList;
|
||||
}
|
||||
|
||||
suggestionsList = spellChecker_.suggest(word);
|
||||
for (const auto& suggestion : suggestionsList) {
|
||||
if (variantList.size() >= SUGGESTIONS_MAX_SIZE) {
|
||||
break;
|
||||
}
|
||||
variantList.append(QVariant(suggestion));
|
||||
}
|
||||
return variantList;
|
||||
}
|
||||
|
||||
QVariantList
|
||||
SpellCheckAdapter::findWords(const QString& text)
|
||||
{
|
||||
QVariantList result;
|
||||
auto words = spellChecker_.findWords(text);
|
||||
for (const auto& word : words) {
|
||||
QVariantMap wordInfo;
|
||||
wordInfo["word"] = word.word;
|
||||
wordInfo["position"] = word.position;
|
||||
wordInfo["length"] = word.length;
|
||||
result.append(wordInfo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
SpellCheckAdapter::setDictionary(const QString& locale)
|
||||
{
|
||||
auto localPath = dictionaryListModel_->pathForLocale(locale);
|
||||
if (spellChecker_.replaceDictionary(localPath)) {
|
||||
settingsManager_->setValue(Settings::Key::SpellLang, locale);
|
||||
set_hasLoadedDictionary(true);
|
||||
Q_EMIT dictionaryChanged();
|
||||
} else {
|
||||
qWarning() << "Failed to set dictionary for locale:" << locale;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user