New Feature : Pdf viewer

This commit is contained in:
Julien Wadel 2023-02-24 10:35:09 +01:00
parent d1f8072550
commit d77c818005
34 changed files with 2855 additions and 40 deletions

View file

@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- VFS Encryption
- File viewer in chats (Image/Animated Image/Video/Texts) with the option to export the file.
- File viewer in chats (Image/Animated Image/Video/Texts/Pdf) with the option to export the file.
- Accept/decline CLI commands.
- Colored Emojis with its own font family.
- OAuth2 connection to retrieve remote provisioning (Experimental and not usable without configuration).

View file

@ -97,6 +97,7 @@ option(ENABLE_APP_LICENSE "Enable the license in packages." YES)
option(ENABLE_APP_OAUTH2 "Build with OAuth2 support for remote provisioning." OFF) #Experimental.
option(ENABLE_APP_PACKAGING "Enable packaging" NO)
option(ENABLE_APP_PACKAGE_ROOTCA "Embed the rootca file into the package" YES)
option(ENABLE_APP_PDF_VIEWER "Enable Pdf viewer" YES)
option(ENABLE_APP_WEBVIEW "Enable webviews." NO) #Webview is not fully supported because of deployments. Used for subscription.
option(ENABLE_BUILD_APP_PLUGINS "Enable the build of plugins" YES)
option(ENABLE_BUILD_EXAMPLES "Enable the build of examples" NO)
@ -145,6 +146,7 @@ list(APPEND APP_OPTIONS "-DENABLE_APP_LICENSE=${ENABLE_APP_LICENSE}")
list(APPEND APP_OPTIONS "-DENABLE_APP_OAUTH2=${ENABLE_APP_OAUTH2}")
list(APPEND APP_OPTIONS "-DENABLE_APP_PACKAGING=${ENABLE_APP_PACKAGING}")
list(APPEND APP_OPTIONS "-DENABLE_APP_PACKAGE_ROOTCA=${ENABLE_APP_PACKAGE_ROOTCA}")
list(APPEND APP_OPTIONS "-DENABLE_APP_PDF_VIEWER=${ENABLE_APP_PDF_VIEWER}")
list(APPEND APP_OPTIONS "-DENABLE_APP_WEBVIEW=${ENABLE_APP_WEBVIEW}")
list(APPEND APP_OPTIONS "-DENABLE_BUILD_EXAMPLES=${ENABLE_BUILD_EXAMPLES}")
list(APPEND APP_OPTIONS "-DENABLE_BUILD_VERBOSE=${ENABLE_BUILD_VERBOSE}")

View file

@ -215,6 +215,7 @@ Also, more configurations are available in the docker-files folder of linphone-s
| :--- | :---: | ---: |
| ENABLE_APP_LICENSE | Enable the license in packages. | YES |
| ENABLE_APP_PACKAGING | Enable packaging. Package will be deployed in `OUTPUT/packages` | NO |
| ENABLE_APP_PDF_VIEWER | Enable PDF viewer. Need Qt PDF module. | YES |
| ENABLE_APP_WEBVIEW | Enable webview for accounts. The Webview engine must be deployed, it takes a large size. | NO |
| ENABLE_APP_PACKAGE_ROOTCA | Embed the rootca file (concatenation of all root certificates published by mozilla) into the package | YES |
| ENABLE_BUILD_APP_PLUGINS | Enable the build of plugins | YES |

View file

@ -153,12 +153,30 @@ if (UNIX AND NOT APPLE)
list(APPEND QT5_PACKAGES DBus)
endif ()
set(QT5_PACKAGES_OPTIONAL TextToSpeech QmlModels)
if(ENABLE_APP_PDF_VIEWER)
list(APPEND QT5_PACKAGES_OPTIONAL Pdf PdfWidgets)
endif()
set(CMAKE_AUTOMOC ON)
SET(CMAKE_AUTOUIC ON)
find_package(Qt5 COMPONENTS ${QT5_PACKAGES} REQUIRED)
find_package(Qt5 COMPONENTS ${QT5_PACKAGES_OPTIONAL} QUIET)
set(LIBRARIES)
set(INCLUDED_DIRECTORIES)
foreach (package ${QT5_PACKAGES_OPTIONAL})
if ("${Qt5${package}_FOUND}")
message("Optional package ${package} found.")
list(APPEND INCLUDED_DIRECTORIES "${Qt5${package}_INCLUDE_DIRS}")
list(APPEND LIBRARIES ${Qt5${package}_LIBRARIES})
string(TOUPPER "${package}" INCLUDE_NAME)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${INCLUDE_NAME}_ENABLED")
else ()
message("Optional package ${package} not found.")
endif ()
endforeach ()
bc_git_version(${TARGET_NAME} ${PROJECT_VERSION})
#-------------------------------------------------
@ -439,6 +457,8 @@ set(PLUGIN_HEADERS
include/LinphoneApp/PluginNetworkHelper.hpp
include/LinphoneApp/LinphonePlugin.hpp)
set( UIS)
list(APPEND SOURCES include/LinphoneApp/PluginExample.json)
set(MAIN_FILE src/app/main.cpp)
@ -494,6 +514,11 @@ if(ENABLE_APP_OAUTH2)
list(APPEND SOURCES src/components/authentication/OAuth2Model.cpp)
endif()
if(Qt5Pdf_FOUND)
list(APPEND HEADERS src/components/pdf/PdfWidget.hpp)
list(APPEND SOURCES src/components/pdf/PdfWidget.cpp)
endif()
set(QRC_RESOURCES resources.qrc)
if(ENABLE_APP_WEBVIEW)
@ -524,6 +549,10 @@ PREPEND(HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/")
PREPEND(PLUGIN_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/")
PREPEND(PLUGIN_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/")
if(Qt5Pdf_FOUND)
qt5_wrap_ui(SOURCES src/components/pdf/PdfWidget.ui)
endif()
# ------------------------------------------------------------------------------
# Compute QML files list.
# ------------------------------------------------------------------------------
@ -653,7 +682,7 @@ endif()
target_compile_definitions(${APP_PLUGIN} PUBLIC "-DENABLE_APP_EXPORT_PLUGIN")
set_target_properties(${APP_PLUGIN} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(INCLUDED_DIRECTORIES "${LINPHONECXX_INCLUDE_DIRS}" "${MEDIASTREAMER2_INCLUDE_DIRS}")
list(APPEND INCLUDED_DIRECTORIES "${LINPHONECXX_INCLUDE_DIRS}" "${MEDIASTREAMER2_INCLUDE_DIRS}")
list(APPEND INCLUDED_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/include")
set(LIBRARIES_LIST ${BCTOOLBOX_CORE_LIBRARIES} ${BELCARD_LIBRARIES} ${LINPHONE_LIBRARIES} ${LINPHONECXX_LIBRARIES} ${MEDIASTREAMER2_LIBRARIES} ${ORTP_LIBRARIES} ${OPUS_LIBRARIES})
@ -664,7 +693,6 @@ set(LIBRARIES_LIST ${BCTOOLBOX_CORE_LIBRARIES} ${BELCARD_LIBRARIES} ${LINPHONE_
#endif()
if(WIN32)
set(LIBRARIES)
foreach(LIBRARY ${LIBRARIES_LIST})# Search for lib full path
find_library(FIND_LIBRARY_ITEM_${LIBRARY} NAMES ${LIBRARY} lib${LIBRARY} REQUIRED)#find_library need a specific variable name each time
if(FIND_LIBRARY_ITEM_${LIBRARY})
@ -674,7 +702,7 @@ if(WIN32)
endif()
endforeach()
else()
set(LIBRARIES ${LIBRARIES_LIST})
list(APPEND LIBRARIES ${LIBRARIES_LIST})
endif()
if(ENABLE_BUILD_VERBOSE)
@ -692,18 +720,7 @@ foreach (package ${QT5_PACKAGES})
endif ()
endforeach ()
foreach (package ${QT5_PACKAGES_OPTIONAL})
if ("${Qt5${package}_FOUND}")
message("Optional package ${package} found.")
list(APPEND INCLUDED_DIRECTORIES "${Qt5${package}_INCLUDE_DIRS}")
list(APPEND LIBRARIES ${Qt5${package}_LIBRARIES})
string(TOUPPER "${package}" INCLUDE_NAME)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${INCLUDE_NAME}_ENABLED")
else ()
message("Optional package ${package} not found.")
endif ()
endforeach ()
if(ENABLE_QT_KEYCHAIN)
target_link_libraries(${APP_LIBRARY} ${QTKEYCHAIN_TARGET_NAME})

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="80"
height="80"
viewBox="0 0 80 80"
fill="none"
version="1.1"
id="svg11"
sodipodi:docname="rotation_custom.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview13"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="5.7847222"
inkscape:cx="8.2977191"
inkscape:cy="44.773109"
inkscape:window-width="1920"
inkscape:window-height="1163"
inkscape:window-x="1920"
inkscape:window-y="500"
inkscape:window-maximized="1"
inkscape:current-layer="svg11" />
<g
clip-path="url(#clip0_722_38049)"
id="g4"
transform="matrix(4.2443681,0,0,4.2443681,-1.7346453,1.9652308)">
<path
d="M 11.0829,15.7054 C 9.69666,16.1404 8.20997,16.1372 6.82565,15.6961 5.44133,15.2551 4.22678,14.3977 3.3477,13.2409 2.46863,12.0841 1.96782,10.6843 1.91361,9.23245 1.8594,7.78057 2.25442,6.34732 3.04479,5.12822 3.83516,3.90912 4.98238,2.96353 6.32995,2.42046 7.67753,1.87739 9.15984,1.76328 10.5747,2.09369 c 1.4148,0.33042 2.6932,1.08927 3.6609,2.17303 0.9676,1.08377 1.5773,2.43967 1.746,3.88274 m 0,0 -4.4435,-1.92611 m 4.4435,1.92611 1.7756,-4.70436"
stroke="#000000"
stroke-width="3"
stroke-linecap="round"
id="path2" />
</g>
<defs
id="defs9">
<clipPath
id="clip0_722_38049">
<rect
width="20"
height="18"
fill="#ffffff"
id="rect6"
x="0"
y="0" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="80"
height="80"
viewBox="0 0 80 80"
fill="none"
version="1.1"
id="svg6"
sodipodi:docname="zoom_in_custom.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs10" />
<sodipodi:namedview
id="namedview8"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="5.7847222"
inkscape:cx="3.8895558"
inkscape:cy="51.687875"
inkscape:window-width="1920"
inkscape:window-height="1163"
inkscape:window-x="1920"
inkscape:window-y="500"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 21.334123,30.348539 c 0.889432,-2.091349 2.091349,-3.894212 3.605775,-5.408638 1.514427,-1.514427 3.31729,-2.716344 5.408639,-3.605775 C 32.439886,20.444718 34.631372,20 36.923044,20 c 2.291672,0 4.483158,0.444718 6.574459,1.334126 2.0913,0.889431 3.894212,2.091348 5.408735,3.605775 1.514281,1.514426 2.716223,3.317289 3.605824,5.408639 0.889358,2.091348 1.334038,4.282834 1.334038,6.574507 0,3.525645 -0.993634,6.72262 -2.98066,9.591263 l 8.244959,8.2452 C 59.703386,55.352496 60,56.073709 60,56.922907 c 0,0.833472 -0.304598,1.554685 -0.913553,2.16364 -0.608954,0.608954 -1.330168,0.913311 -2.163397,0.913311 -0.865408,0 -1.586621,-0.304357 -2.163398,-0.913311 l -8.2452,-8.221249 c -2.868643,1.987267 -6.065763,2.980901 -9.591408,2.980901 -2.291673,0 -4.483159,-0.444922 -6.574507,-1.334281 -2.091349,-0.889358 -3.894212,-2.0913 -5.408639,-3.605823 C 23.425472,47.391814 22.223555,45.588902 21.334123,43.497601 20.444716,41.406204 20,39.214718 20,36.923047 c 0,-2.291673 0.444716,-4.483159 1.334123,-6.574508 z m 4.819716,6.574507 c 0,2.964764 1.053683,5.500841 3.161048,7.608109 2.107389,2.10751 4.643417,3.161144 7.608157,3.161144 2.964764,0 5.500743,-1.053634 7.608253,-3.161144 2.107269,-2.107268 3.160903,-4.643345 3.160903,-7.608109 0,-2.96474 -1.053635,-5.500768 -3.160903,-7.608157 -2.10751,-2.107388 -4.643489,-3.161047 -7.608253,-3.161047 -2.96474,0 -5.500768,1.053658 -7.608157,3.161047 -2.107365,2.107389 -3.161048,4.643417 -3.161048,7.608157 z"
fill="#000000"
id="path2"
style="stroke-width:2.41936" />
<path
d="m 43.167259,36.555472 v 2.965151 H 31.377542 v -2.965151 z m -4.276447,-4.666256 v 12.52218 h -3.22504 v -12.52218 z"
fill="#000000"
id="path4"
style="stroke-width:2.41936" />
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="80"
height="80"
viewBox="0 0 80 80"
fill="none"
version="1.1"
id="svg6"
sodipodi:docname="zoom_out_custom.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs10" />
<sodipodi:namedview
id="namedview8"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="4.0904163"
inkscape:cx="8.4343493"
inkscape:cy="46.694514"
inkscape:window-width="1920"
inkscape:window-height="1163"
inkscape:window-x="1920"
inkscape:window-y="500"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<path
d="m 40.891833,35.498167 v 3.203771 h -8.009427 v -3.203771 z"
fill="#000000"
id="path2"
style="stroke-width:2.41936" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 21.334125,30.348525 c 0.889431,-2.091347 2.091347,-3.894209 3.605772,-5.408635 1.514426,-1.514425 3.317288,-2.716342 5.408635,-3.605748 C 32.43988,20.444708 34.631365,20 36.923035,20 c 2.29167,0 4.483155,0.444708 6.574478,1.334142 2.091299,0.889406 3.89421,2.091323 5.408732,3.605748 1.51428,1.514426 2.716221,3.317288 3.605579,5.408635 0.8896,2.091348 1.33428,4.282832 1.33428,6.574503 0,3.525668 -0.993633,6.72264 -2.9809,9.591281 l 8.245195,8.245195 C 59.703386,55.35249 60,56.073703 60,56.922899 c 0,0.833472 -0.304598,1.554685 -0.913552,2.163638 -0.608954,0.608954 -1.330167,0.913311 -2.163396,0.913311 -0.865407,0 -1.58662,-0.304356 -2.163638,-0.913311 l -8.244953,-8.221243 c -2.868641,1.987266 -6.065783,2.980899 -9.591426,2.980899 -2.291671,0 -4.483155,-0.444921 -6.574503,-1.334279 -2.091347,-0.889359 -3.894209,-2.091299 -5.408635,-3.605822 -1.514425,-1.51428 -2.716341,-3.31719 -3.605772,-5.408489 C 20.444694,41.406183 20,39.214698 20,36.923028 c 0,-2.291671 0.444694,-4.483155 1.334125,-6.574503 z m 4.819713,6.574503 c 0,2.964762 1.053657,5.500861 3.161045,7.608128 2.107364,2.107508 4.64339,3.161142 7.608152,3.161142 2.964762,0 5.500764,-1.053634 7.608273,-3.161142 2.107266,-2.107267 3.1609,-4.643366 3.1609,-7.608128 0,-2.964738 -1.053634,-5.500764 -3.1609,-7.608152 -2.107509,-2.107364 -4.643511,-3.161045 -7.608273,-3.161045 -2.964762,0 -5.500788,1.053681 -7.608152,3.161045 -2.107388,2.107388 -3.161045,4.643414 -3.161045,7.608152 z"
fill="#000000"
id="path4"
style="stroke-width:2.41936" />
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -2286,6 +2286,113 @@ Klik her: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2286,6 +2286,113 @@ Klicken Sie hier: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2286,6 +2286,114 @@ Click here: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>Me</translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translatorcomment>Title for the PDF viewer</translatorcomment>
<translation>PDF Viewer</translation>
</message>
<message>
<source>View</source>
<translation>View</translation>
</message>
<message>
<source>Zoom In</source>
<translation>Zoom In</translation>
</message>
<message>
<source>Ctrl+=</source>
<translation>Ctr+=</translation>
</message>
<message>
<source>Zoom Out</source>
<translation>Zoom Out</translation>
</message>
<message>
<source>Ctrl+-</source>
<translation>Ctrl+-</translation>
</message>
<message>
<source>Previous Page</source>
<translation>Previous Page</translation>
</message>
<message>
<source>PgUp</source>
<translation>PgUp</translation>
</message>
<message>
<source>Next Page</source>
<translation>Next Page</translation>
</message>
<message>
<source>PgDown</source>
<translation>PgDown</translation>
</message>
<message>
<source>Continuous</source>
<translation>Continuous</translation>
</message>
<message>
<source>Zoom...</source>
<translation>Zoom</translation>
</message>
<message>
<source>Fit Width</source>
<translation>Fit Width</translation>
</message>
<message>
<source>Fit Page</source>
<translation>Fit Page</translation>
</message>
<message>
<source>25%</source>
<translation>25%</translation>
</message>
<message>
<source>50%</source>
<translation>50%</translation>
</message>
<message>
<source>70%</source>
<translation>70%</translation>
</message>
<message>
<source>85%</source>
<translation>85%</translation>
</message>
<message>
<source>100%</source>
<translation>100%</translation>
</message>
<message>
<source>125%</source>
<translation>125%</translation>
</message>
<message>
<source>150%</source>
<translation>150%</translation>
</message>
<message>
<source>175%</source>
<translation>175%</translation>
</message>
<message>
<source>200%</source>
<translation>200%</translation>
</message>
<message>
<source>300%</source>
<translation>300%</translation>
</message>
<message>
<source>400%</source>
<translation>400%</translation>
</message>
<message>
<source>Export as...</source>
<translation>Export as</translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2286,6 +2286,113 @@ Haga clic aquí: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -173,7 +173,7 @@
</message>
<message>
<source>usernameStatusInvalidCharacters</source>
<translation>Caractères invalides détectés (regex: `%1`).</translation>
<translation>Caractères invalides détectés (regex&#x202f;: `%1`).</translation>
</message>
<message>
<source>usernameStatusInvalid</source>
@ -189,7 +189,7 @@
</message>
<message>
<source>passwordStatusInvalidCharacters</source>
<translation>Caractères invalides détectés (regex: `%1`).</translation>
<translation>Caractères invalides détectés (regex&#x202f;: `%1`).</translation>
</message>
<message>
<source>passwordStatusMissingCharacters</source>
@ -1249,7 +1249,7 @@ URL du serveur non configurée.</translation>
<message>
<source>ephemeralNotInConference!</source>
<extracomment>&apos;Ephemeral message is only supported in conference based chat room!&apos;</extracomment>
<translation>Les messages éphémères ne sont disponibles que pour une conversation définie en mode conférence!</translation>
<translation>Les messages éphémères ne sont disponibles que pour une conversation définie en mode conférence&#x202f;!</translation>
<extra-Context>Warning about not being in conference based chat room.</extra-Context>
</message>
<message>
@ -2056,7 +2056,7 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<message>
<source>newConferenceScheduleTitle</source>
<extracomment>&apos;Would you like to schedule your meeting?&apos; : Ask about setting the meeting as scheduled.</extracomment>
<translation>Voulez-vous programmer cette réunion?</translation>
<translation>Voulez-vous programmer cette réunion&#x202f;?</translation>
</message>
<message>
<source>newConferenceDate</source>
@ -2286,6 +2286,113 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>Moi</translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>
@ -2418,12 +2525,12 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<message>
<source>vfsDeactivation</source>
<extracomment>&apos;Are you sure to deactivate the encryption? The application will exit and all your data will be lost. You must delete them before using the application.&apos; : Explanation to deactivate the VFS encryption.</extracomment>
<translation>Êtes-vous certain de désactiver le chiffrement de l&apos;application ? Elle redémarrera et toutes vos données seront perdues. Vous devez les supprimer avant d&apos;utiliser l&apos;application.</translation>
<translation>Êtes-vous certain de désactiver le chiffrement de l&apos;application&#xa0;? Elle redémarrera et toutes vos données seront perdues. Vous devez les supprimer avant d&apos;utiliser l&apos;application.</translation>
</message>
<message>
<source>vfsActivation</source>
<extracomment>&apos;Are you sure to activate the encryption? You cannot revert without deleting ALL your data&apos; : Explanation to activate the VFS encryption.</extracomment>
<translation>Êtes-vous certain d&apos;activer le chiffrement ? Cette action est irréversible sans perdre TOUTES vos données.</translation>
<translation>Êtes-vous certain d&apos;activer le chiffrement&#xa0;? Cette action est irréversible sans perdre TOUTES vos données.</translation>
</message>
<message>
<source>cancel</source>
@ -2692,7 +2799,7 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
</message>
<message>
<source>serverTooltip</source>
<translation>Serveur LDAP. ie: ldap:// pour un serveur local ou ldap://ldap.example.org/</translation>
<translation>Serveur LDAP. ie&#x202f;: ldap:// pour un serveur local ou ldap://ldap.example.org/</translation>
</message>
<message>
<source>bindDNLabel</source>
@ -3638,7 +3745,7 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<message>
<source>deleteTimeline</source>
<extracomment>&apos;Are you sure you want to delete and leave this timeline?&apos;</extracomment>
<translation>Êtes-vous certain de vouloir tout effacer et de quitter cette conversation?</translation>
<translation>Êtes-vous certain de vouloir tout effacer et de quitter cette conversation&#x202f;?</translation>
</message>
<message>
<source>deleteTimelineTooltip</source>
@ -3760,15 +3867,15 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<name>VfsUtils</name>
<message>
<source>Delete key failed: %1</source>
<translation>Échec lors de la suppression de la clef : %1</translation>
<translation>Échec lors de la suppression de la clef&#xa0;: %1</translation>
</message>
<message>
<source>Read key failed: %1</source>
<translation>Échec lors de la lecture de la clef : %1</translation>
<translation>Échec lors de la lecture de la clef&#xa0;: %1</translation>
</message>
<message>
<source>Write key failed: %1</source>
<translation>Échec lors de l&apos;écriture de la clef : %1</translation>
<translation>Échec lors de l&apos;écriture de la clef&#xa0;: %1</translation>
</message>
</context>
<context>

View file

@ -2273,6 +2273,113 @@ Kattintson ide: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2286,6 +2286,113 @@ Clicca: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>Me</translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -579,7 +579,7 @@
<message>
<source>icsDescription</source>
<extracomment>&apos;Description&apos; : Title for the meeting description.</extracomment>
<translation type="unfinished"></translation>
<translation type="unfinished"></translation>
</message>
<message>
<source>icsJoinButton</source>
@ -1801,7 +1801,7 @@
<message>
<source>checkForUpdates</source>
<extracomment>&apos;Check for updates&apos; : Item menu for checking updates</extracomment>
<translation type="unfinished"></translation>
<translation type="unfinished"></translation>
</message>
<message>
<source>recordings</source>
@ -1826,7 +1826,7 @@
<message>
<source>checkForUpdates</source>
<extracomment>&apos;Check for updates&apos; : Item menu for checking updates</extracomment>
<translation type="unfinished"></translation>
<translation type="unfinished"></translation>
</message>
<message>
<source>recordings</source>
@ -2273,6 +2273,113 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2299,6 +2299,113 @@ Spustelėkite čia: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2286,6 +2286,113 @@ Clique aqui: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2299,6 +2299,113 @@
<translation>Я</translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2286,6 +2286,113 @@ Klicka här: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2273,6 +2273,113 @@ Buraya tıklayın: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2299,6 +2299,113 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -2273,6 +2273,113 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PdfWidget</name>
<message>
<source>PDF Viewer</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom In</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+=</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom Out</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Ctrl+-</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Previous Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgUp</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Next Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PgDown</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continuous</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Zoom...</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Width</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Fit Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>25%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>50%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>70%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>85%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>100%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>125%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>150%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>175%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>200%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>300%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>400%</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Export as...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Presence</name>
<message>

View file

@ -136,6 +136,7 @@
<file>assets/images/recording_sign.svg</file>
<file>assets/images/record_custom.svg</file>
<file>assets/images/recordings_custom.svg</file>
<file>assets/images/rotation_custom.svg</file>
<file>assets/images/remove_participant_custom.svg</file>
<file>assets/images/screen_sharing_custom.svg</file>
<file>assets/images/screenshot_custom.svg</file>
@ -169,6 +170,8 @@
<file>assets/images/video_call_accept_custom.svg</file>
<file>assets/images/video_call_custom.svg</file>
<file>assets/images/warning.svg</file>
<file>assets/images/zoom_in_custom.svg</file>
<file>assets/images/zoom_out_custom.svg</file>
<file>ui/modules/Common/Animations/BusyIndicator.qml</file>
<file>ui/modules/Common/Constants/Constants.qml</file>
<file>ui/modules/Common/Dialog/ConfirmDialog.qml</file>

View file

@ -439,6 +439,7 @@ void App::initContentApp () {
registerSharedTypes();
registerToolTypes();
registerSharedToolTypes();
registerUninstalledModules();
// Enable notifications.
mNotifier = new Notifier(mEngine);
@ -623,8 +624,8 @@ static inline void registerType (const char *name) {
}
template<typename T>
static inline void registerToolType (const char *name) {
qmlRegisterSingletonType<T>(name, 1, 0, name, [](QQmlEngine *engine, QJSEngine *) -> QObject *{
static inline void registerToolType (const char *name, const int &major_version = 1 , const int &minor_version = 0) {
qmlRegisterSingletonType<T>(name, major_version, minor_version, name, [](QQmlEngine *engine, QJSEngine *) -> QObject *{
return new T(engine);
});
}
@ -774,6 +775,11 @@ void App::registerSharedToolTypes () {
registerSharedToolType<ColorListModel,App, &App::getColorListModel>("ColorsList");
}
void App::registerUninstalledModules() {
if(!isPdfAvailable())
qmlRegisterModule("QtQuick.Pdf",5,15);
}
// -----------------------------------------------------------------------------
void App::setTrayIcon () {
@ -1117,3 +1123,11 @@ void App::checkForUpdates(bool force) {
Utils::appStringToCoreString(applicationVersion())
);
}
bool App::isPdfAvailable(){
#ifdef PDF_ENABLED
return true;
#else
return false;
#endif
}

View file

@ -118,6 +118,9 @@ public:
Q_INVOKABLE static void smartShowWindow (QQuickWindow *window);
Q_INVOKABLE static void checkForUpdates(bool force = false);
// Check module availability when no dependencies are needed (else use SettingsModel)
Q_INVOKABLE static bool isPdfAvailable();
public slots:
void stateChanged(Qt::ApplicationState);
@ -136,6 +139,7 @@ private:
void registerSharedTypes ();
void registerToolTypes ();
void registerSharedToolTypes ();
void registerUninstalledModules ();
void setTrayIcon ();
void createNotifier ();

View file

@ -244,7 +244,7 @@ ImageProvider::ImageProvider () : QQuickImageProvider(
// -----------------------------------------------------------------------------
QImage ImageProvider::requestImage (const QString &id, QSize *size, const QSize &requestedSize) {
QImage ImageProvider::computeImage (const QString &id, QSize *size, const QSize &requestedSize) {
ImageModel * model = App::getInstance()->getImageListModel()->getImageModel(id);
if(!model)
return QImage();
@ -313,6 +313,13 @@ QImage ImageProvider::requestImage (const QString &id, QSize *size, const QSize
return image;
}
QPixmap ImageProvider::requestPixmap (const QString &id, QSize *size, const QSize &requestedSize) {
return QPixmap::fromImage(requestImage(id, size, requestedSize));
QPixmap ImageProvider::computePixmap (const QString &id, QSize *size, const QSize &requestedSize) {
return QPixmap::fromImage(computeImage(id, size, requestedSize));
}
QPixmap ImageProvider::requestPixmap (const QString &id, QSize *size, const QSize &requestedSize){
return computePixmap(id, size, requestedSize);
}
QImage ImageProvider::requestImage (const QString &id, QSize *size, const QSize &requestedSize){
return computeImage(id, size, requestedSize);
}

View file

@ -29,9 +29,10 @@ class ImageProvider : public QQuickImageProvider {
public:
ImageProvider ();
QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override;
QPixmap requestPixmap (const QString &id, QSize *size, const QSize &requestedSize) override;
virtual QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override;
virtual QPixmap requestPixmap (const QString &id, QSize *size, const QSize &requestedSize) override;
static QPixmap computePixmap (const QString &id, QSize *size, const QSize &requestedSize = QSize());
static QImage computeImage (const QString &id, QSize *size, const QSize &requestedSize = QSize());
static const QString ProviderId;
};

View file

@ -0,0 +1,297 @@
/*
* Copyright (c) 2010-2023 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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 "PdfWidget.hpp"
#include "ui_PdfWidget.h"
#include <QDebug>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPdfDocument>
#include <QPdfPageNavigation>
#include <QtMath>
#include "components/content/ContentModel.hpp"
#include "components/core/CoreManager.hpp"
#include "components/settings/SettingsModel.hpp"
#include "utils/Constants.hpp"
#include "utils/Utils.hpp"
const qreal zoomMultiplier = qSqrt(2.0);
PageSelector::PageSelector(QWidget *parent)
: QWidget(parent)
, mPageNavigation(nullptr){
QHBoxLayout *layout = new QHBoxLayout(this);
mPageNumberEdit = new QLineEdit(this);
mPageNumberEdit->setAlignment(Qt::AlignRight);
mPageNumberEdit->setStyleSheet("QLineEdit { border-radius: 5px; }");
mPageCountLabel = new QLabel(this);
mPageCountLabel->setText("0");
layout->addWidget(mPageNumberEdit);
layout->addWidget(mPageCountLabel);
}
void PageSelector::setPageNavigation(QPdfPageNavigation *pageNavigation){
mPageNavigation = pageNavigation;
connect(mPageNavigation, &QPdfPageNavigation::currentPageChanged, this, &PageSelector::onCurrentPageChanged);
connect(mPageNavigation, &QPdfPageNavigation::pageCountChanged, this, [this](int pageCount){
if(mPageNavigation->currentPage() == 0)
mPageNumberEdit->setText("1");
mPageCountLabel->setText(QString::fromLatin1("/ %1").arg(pageCount));
});
connect(mPageNumberEdit, &QLineEdit::editingFinished, this, &PageSelector::pageNumberEdited);
onCurrentPageChanged(mPageNavigation->currentPage());
}
void PageSelector::onCurrentPageChanged(int page) {
if (mPageNavigation->pageCount() == 0)
mPageNumberEdit->setText(QString::number(0));
else
mPageNumberEdit->setText(QString::number(page + 1));
}
void PageSelector::pageNumberEdited() {
if (!mPageNavigation)
return;
bool ok = false;
const int pageNumber = mPageNumberEdit->text().toInt(&ok);
if (!ok)
onCurrentPageChanged(mPageNavigation->currentPage());
else
mPageNavigation->setCurrentPage(qBound(0, pageNumber - 1, mPageNavigation->pageCount() - 1));
}
void PageSelector::setFamilyFont(const QString& family){
Utils::setFamilyFont(this, family);
Utils::setFamilyFont(mPageNumberEdit, family);
Utils::setFamilyFont(mPageCountLabel, family);
}
//------------------------------------------------------------------------------------------------------------------
PdfWidget::PdfWidget(ContentModel * contentModel, QWidget *parent)
: QMainWindow(parent)
, mContentModel(contentModel)
, mUi(new Ui::PdfWidget)
, mPageSelector(new PageSelector(this))
, mDocument(new QPdfDocument(this)) {
QSize size;
mUi->setupUi(this);
// Icons
mUi->downloadToolButton->setIcon(Utils::getMaskedPixmap("download_custom", "#CBCBCB"));
mUi->fullscreenToolButton->setIcon(Utils::getMaskedPixmap("fullscreen_custom", "#CBCBCB"));
mUi->rotationToolButton->setIcon(Utils::getMaskedPixmap("rotation_custom", "#CBCBCB"));
mUi->zoomInToolButton->setIcon(Utils::getMaskedPixmap("zoom_in_custom", "#CBCBCB"));
mUi->zoomOutToolButton->setIcon(Utils::getMaskedPixmap("zoom_out_custom", "#CBCBCB"));
mUi->rotationToolButton->setVisible(false);// Rotation is not available.
// Fonts
QString family;
if(CoreManager::getInstance() && CoreManager::getInstance()->getSettingsModel())
family = CoreManager::getInstance()->getSettingsModel()->getTextMessageFont().family();
else
family = Constants::DefaultFont;
Utils::setFamilyFont(mUi->menuBar, family);
Utils::setFamilyFont(mUi->menuView, family);
Utils::setFamilyFont(mUi->menuZoom, family);
Utils::setFamilyFont(mUi->actionZoom_In, family);
Utils::setFamilyFont(mUi->actionZoom_Out, family);
Utils::setFamilyFont(mUi->actionZoomFit_Width, family);
Utils::setFamilyFont(mUi->actionZoomFit_Page, family);
Utils::setFamilyFont(mUi->actionZoom25, family);
Utils::setFamilyFont(mUi->actionZoom50, family);
Utils::setFamilyFont(mUi->actionZoom70, family);
Utils::setFamilyFont(mUi->actionZoom85, family);
Utils::setFamilyFont(mUi->actionZoom100, family);
Utils::setFamilyFont(mUi->actionZoom125, family);
Utils::setFamilyFont(mUi->actionZoom150, family);
Utils::setFamilyFont(mUi->actionZoom175, family);
Utils::setFamilyFont(mUi->actionZoom200, family);
Utils::setFamilyFont(mUi->actionZoom300, family);
Utils::setFamilyFont(mUi->actionZoom400, family);
Utils::setFamilyFont(mUi->actionPrevious_Page, family);
Utils::setFamilyFont(mUi->actionNext_Page, family);
Utils::setFamilyFont(mUi->actionContinuous, family);
mPageSelector->setFamilyFont(family);
mPageSelector->setMaximumWidth(150);
mUi->statusBar->addPermanentWidget(mPageSelector);
mPageSelector->setPageNavigation(mUi->pdfView->pageNavigation());
//-------
// SEARCH
// QWidget * searchBox = new QWidget(this);
// QHBoxLayout * hbox = new QHBoxLayout(searchBox);
// QLineEdit *search = new QLineEdit(this);
// search->setPlaceholderText("Search...");
// search->setFrame(false);
// connect(search, &QLineEdit::textChanged, [](const QString &text){
//
// });
// hbox->addWidget(search);
// QLabel * searchIcon = new QLabel(this);
//
// searchIcon->setScaledContents(true);
// searchIcon->setPixmap(QPixmap("://assets/images/search_custom.svg"));
// hbox->addWidget(searchIcon);
// hbox->setSpacing(0);
// hbox->setContentsMargins(0,0,0,0);
// searchBox->setStyleSheet("QWidget { background-color : white}");
// searchBox->setContentsMargins(0,0,0,0);
//
// mUi->mainToolBar->addWidget(searchBox);
// SEARCH
// searchBox->setMaximumHeight(fullscreen->height());
// searchIcon->setMaximumHeight(search->height());
// searchIcon->setMaximumWidth(searchIcon->height());
mUi->pdfView->setDocument(mDocument);
mUi->pdfView->setZoomMode(QPdfView::FitInView);
}
PdfWidget::PdfWidget(QWidget *parent) : PdfWidget(nullptr, parent){
}
PdfWidget::~PdfWidget(){
delete mUi;
}
void PdfWidget::open(const QString &filePath) {
mDocument->load(filePath);
const auto documentTitle = mDocument->metaData(QPdfDocument::Title).toString();
setWindowTitle(!documentTitle.isEmpty() ? documentTitle : tr("PDF Viewer"));
}
// Tools
void PdfWidget::on_fullscreenToolButton_toggled(bool checked){
if(checked)
this->showFullScreen();
else
this->showNormal();
}
void PdfWidget::on_downloadToolButton_clicked(){
if(mContentModel){
auto fileName = QFileDialog::getSaveFileName(this, tr("Export as..."));
if(!fileName.isEmpty()){
mContentModel->saveAs(fileName);
}
}
}
// Page
void PdfWidget::on_actionPrevious_Page_triggered(){
mUi->pdfView->pageNavigation()->goToPreviousPage();
}
void PdfWidget::on_actionNext_Page_triggered(){
mUi->pdfView->pageNavigation()->goToNextPage();
}
void PdfWidget::on_actionContinuous_triggered(){
mUi->pdfView->setPageMode(mUi->actionContinuous->isChecked() ? QPdfView::MultiPage : QPdfView::SinglePage);
}
// Zoom
void PdfWidget::on_actionZoom_In_triggered(){
mUi->pdfView->setZoomMode(QPdfView::CustomZoom);
mUi->pdfView->setZoomFactor(mUi->pdfView->zoomFactor() * zoomMultiplier);
}
void PdfWidget::on_actionZoom_Out_triggered(){
mUi->pdfView->setZoomMode(QPdfView::CustomZoom);
mUi->pdfView->setZoomFactor(mUi->pdfView->zoomFactor() / zoomMultiplier);
}
void PdfWidget::setZoom(const double& percent){
mUi->actionZoomFit_Width->setChecked(false);
mUi->actionZoomFit_Page->setChecked(false);
mUi->pdfView->setZoomMode(QPdfView::CustomZoom);
mUi->pdfView->setZoomFactor(percent/100.0);
}
void PdfWidget::on_actionZoomFit_Width_triggered(){
mUi->actionZoomFit_Page->setChecked(false);
mUi->pdfView->setZoomMode(QPdfView::FitToWidth);
}
void PdfWidget::on_actionZoomFit_Page_triggered(){
mUi->actionZoomFit_Width->setChecked(false);
mUi->pdfView->setZoomMode(QPdfView::FitInView);
}
void PdfWidget::on_actionZoom25_triggered(){
setZoom(25);
}
void PdfWidget::on_actionZoom50_triggered(){
setZoom(50);
}
void PdfWidget::on_actionZoom70_triggered(){
setZoom(70);
}
void PdfWidget::on_actionZoom85_triggered(){
setZoom(85);
}
void PdfWidget::on_actionZoom100_triggered(){
setZoom(100);
}
void PdfWidget::on_actionZoom125_triggered(){
setZoom(125);
}
void PdfWidget::on_actionZoom150_triggered(){
setZoom(150);
}
void PdfWidget::on_actionZoom175_triggered(){
setZoom(175);
}
void PdfWidget::on_actionZoom200_triggered(){
setZoom(200);
}
void PdfWidget::on_actionZoom300_triggered(){
setZoom(300);
}
void PdfWidget::on_actionZoom400_triggered(){
setZoom(400);
}

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2010-2023 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
// Open a Widnow to display a PDF.
#ifndef PDF_WIDGET_H_
#define PDF_WIDGET_H_
#include <QMainWindow>
#include <QWidget>
namespace Ui {
class PdfWidget;
}
class ContentModel;
class PageSelector;
class QLabel;
class QLineEdit;
class QPdfDocument;
class QPdfPageNavigation;
class PdfWidget : public QMainWindow{
Q_OBJECT
public:
explicit PdfWidget(QWidget *parent = nullptr);
explicit PdfWidget(ContentModel * contentModel, QWidget *parent = nullptr);
~PdfWidget();
public slots:
void open(const QString &filePath);
void setZoom(const double& percent);
private slots:
// Tools
void on_downloadToolButton_clicked();
void on_fullscreenToolButton_toggled(bool checked);
// Page
void on_actionPrevious_Page_triggered();
void on_actionNext_Page_triggered();
void on_actionContinuous_triggered();
// Zoom
void on_actionZoom_In_triggered();
void on_actionZoom_Out_triggered();
void on_actionZoomFit_Width_triggered();
void on_actionZoomFit_Page_triggered();
void on_actionZoom25_triggered();
void on_actionZoom50_triggered();
void on_actionZoom70_triggered();
void on_actionZoom85_triggered();
void on_actionZoom100_triggered();
void on_actionZoom125_triggered();
void on_actionZoom150_triggered();
void on_actionZoom175_triggered();
void on_actionZoom200_triggered();
void on_actionZoom300_triggered();
void on_actionZoom400_triggered();
private:
Ui::PdfWidget *mUi;
PageSelector *mPageSelector;
QPdfDocument *mDocument;
ContentModel * mContentModel;
};
class PageSelector : public QWidget {
Q_OBJECT
public:
explicit PageSelector(QWidget *parent = nullptr);
void setPageNavigation(QPdfPageNavigation *pageNavigation);
void setFamilyFont(const QString& family);
private slots:
void onCurrentPageChanged(int page);
void pageNumberEdited();
private:
QLabel *mPageCountLabel;
QPdfPageNavigation *mPageNavigation;
QLineEdit *mPageNumberEdit;
};
#endif

View file

@ -0,0 +1,540 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PdfWidget</class>
<widget class="QMainWindow" name="PdfWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>PDF Viewer</string>
</property>
<property name="unifiedTitleAndToolBarOnMac">
<bool>true</bool>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="downloadToolButton">
<property name="minimumSize">
<size>
<width>33</width>
<height>27</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>33</width>
<height>27</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">background-color:white;
border-radius:5px;
border-width:1px</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="document-save">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="iconSize">
<size>
<width>27</width>
<height>27</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>304</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="widget_4" native="true">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">QLabel{background-color:white}</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="QToolButton" name="fullscreenToolButton">
<property name="minimumSize">
<size>
<width>33</width>
<height>27</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>33</width>
<height>27</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">background-color:white;
border-radius:5px;
border-width:1px</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="view-fullscreen">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="iconSize">
<size>
<width>27</width>
<height>27</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>376</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="rotationToolButton">
<property name="minimumSize">
<size>
<width>33</width>
<height>33</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>33</width>
<height>33</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:white;
border-radius:5px;
border-width:1px</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="object-rotate-right">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="iconSize">
<size>
<width>33</width>
<height>33</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="zoomInToolButton">
<property name="minimumSize">
<size>
<width>33</width>
<height>33</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>33</width>
<height>33</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">background-color:white;
border-radius:5px;
border-width:1px</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="zoom-in">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="iconSize">
<size>
<width>33</width>
<height>33</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="zoomOutToolButton">
<property name="minimumSize">
<size>
<width>33</width>
<height>33</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>33</width>
<height>33</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="styleSheet">
<string notr="true">background-color:white;
border-radius:5px;
border-width:1px</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="zoom-out">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="iconSize">
<size>
<width>33</width>
<height>33</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPdfView" name="pdfView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
<widget class="QMenu" name="menuZoom">
<property name="title">
<string>Zoom...</string>
</property>
<addaction name="actionZoom_In"/>
<addaction name="actionZoom_Out"/>
<addaction name="separator"/>
<addaction name="actionZoomFit_Width"/>
<addaction name="actionZoomFit_Page"/>
<addaction name="actionZoom25"/>
<addaction name="actionZoom50"/>
<addaction name="actionZoom70"/>
<addaction name="actionZoom85"/>
<addaction name="actionZoom100"/>
<addaction name="actionZoom125"/>
<addaction name="actionZoom150"/>
<addaction name="actionZoom175"/>
<addaction name="actionZoom200"/>
<addaction name="actionZoom300"/>
<addaction name="actionZoom400"/>
</widget>
<addaction name="menuZoom"/>
<addaction name="actionPrevious_Page"/>
<addaction name="actionNext_Page"/>
<addaction name="separator"/>
<addaction name="actionContinuous"/>
</widget>
<addaction name="menuView"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionZoom_In">
<property name="icon">
<iconset>
<normaloff>:/icons/images/zoom-in-24.png</normaloff>:/icons/images/zoom-in-24.png</iconset>
</property>
<property name="text">
<string>Zoom In</string>
</property>
<property name="shortcut">
<string>Ctrl+=</string>
</property>
</action>
<action name="actionZoom_Out">
<property name="icon">
<iconset>
<normaloff>:/icons/images/zoom-out-24.png</normaloff>:/icons/images/zoom-out-24.png</iconset>
</property>
<property name="text">
<string>Zoom Out</string>
</property>
<property name="shortcut">
<string>Ctrl+-</string>
</property>
</action>
<action name="actionPrevious_Page">
<property name="text">
<string>Previous Page</string>
</property>
<property name="shortcut">
<string>PgUp</string>
</property>
</action>
<action name="actionNext_Page">
<property name="text">
<string>Next Page</string>
</property>
<property name="shortcut">
<string>PgDown</string>
</property>
</action>
<action name="actionContinuous">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Continuous</string>
</property>
</action>
<action name="actionZoomFit_Width">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Fit Width</string>
</property>
</action>
<action name="actionZoomFit_Page">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Fit Page</string>
</property>
</action>
<action name="actionZoom25">
<property name="text">
<string>25%</string>
</property>
</action>
<action name="actionZoom50">
<property name="text">
<string>50%</string>
</property>
</action>
<action name="actionZoom70">
<property name="text">
<string>70%</string>
</property>
</action>
<action name="actionZoom85">
<property name="text">
<string>85%</string>
</property>
</action>
<action name="actionZoom100">
<property name="text">
<string>100%</string>
</property>
</action>
<action name="actionZoom125">
<property name="text">
<string>125%</string>
</property>
</action>
<action name="actionZoom150">
<property name="text">
<string>150%</string>
</property>
</action>
<action name="actionZoom175">
<property name="text">
<string>175%</string>
</property>
</action>
<action name="actionZoom200">
<property name="text">
<string>200%</string>
</property>
</action>
<action name="actionZoom300">
<property name="text">
<string>300%</string>
</property>
</action>
<action name="actionZoom400">
<property name="text">
<string>400%</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>QPdfView</class>
<extends>QWidget</extends>
<header location="global">qpdfview.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>zoomInToolButton</sender>
<signal>clicked()</signal>
<receiver>actionZoom_In</receiver>
<slot>trigger()</slot>
<hints>
<hint type="sourcelabel">
<x>27</x>
<y>519</y>
</hint>
<hint type="destinationlabel">
<x>-1</x>
<y>-1</y>
</hint>
</hints>
</connection>
<connection>
<sender>zoomOutToolButton</sender>
<signal>clicked()</signal>
<receiver>actionZoom_Out</receiver>
<slot>trigger()</slot>
<hints>
<hint type="sourcelabel">
<x>27</x>
<y>552</y>
</hint>
<hint type="destinationlabel">
<x>-1</x>
<y>-1</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -18,6 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QAction>
#include <QBitmap>
#include <QFileInfo>
#include <QCoreApplication>
#include <QCryptographicHash>
@ -30,17 +32,25 @@
#include <QTimeZone>
#include <QUrl>
#ifdef PDF_ENABLED
#include <QtPdf/QPdfDocument>
#include <QtPdfWidgets/QPdfView>
#include "components/pdf/PdfWidget.hpp"
#endif
#include "config.h"
#include "Utils.hpp"
#include "UriTools.hpp"
#include "app/App.hpp"
#include "app/paths/Paths.hpp"
#include "app/providers/ImageProvider.hpp"
#include "components/core/CoreManager.hpp"
#include "components/contacts/ContactsListModel.hpp"
#include "components/contact/ContactModel.hpp"
#include "components/contact/VcardModel.hpp"
#include "components/settings/AccountSettingsModel.hpp"
#include "components/settings/SettingsModel.hpp"
#include "app/paths/Paths.hpp"
#ifdef _WIN32
#include <time.h>
@ -616,6 +626,10 @@ bool Utils::isVideo(const QString& path){
return QMimeDatabase().mimeTypeForFile(path).name().contains("video");
}
bool Utils::isPdf(const QString& path){
return QMimeDatabase().mimeTypeForFile(path).name().contains("application/pdf");
}
bool Utils::isSupportedForDisplay(const QString& path){
return !QMimeDatabase().mimeTypeForFile(path).name().contains("application");// "for pdf : "application/pdf". Note : Make an exception when supported.
}
@ -769,4 +783,39 @@ void Utils::deleteAllUserDataOffline(){
QDir dir(gDbPaths[i]);
qWarning() << "Deleting " << gDbPaths[i] << " : " << (dir.removeRecursively() ? "Successfully" : "Failed");
}
}
//-------------------------------------------------------------------------------------------------------
// WIDGETS
//-------------------------------------------------------------------------------------------------------
bool Utils::openWithPdfViewer(ContentModel * contentModel, const QString& filePath, const int& width, const int& height) {
#ifdef PDF_ENABLED
PdfWidget *view = new PdfWidget(contentModel);
view->setMinimumSize(QSize(width, height));
view->show();
view->open(filePath);
return true;
#else
return false;
#endif
}
void Utils::setFamilyFont(QAction * dest, const QString& family){
QFont font(dest->font());
font.setFamily(family);
dest->setFont(font);
}
void Utils::setFamilyFont(QWidget * dest, const QString& family){
QFont font(dest->font());
font.setFamily(family);
dest->setFont(font);
}
QPixmap Utils::getMaskedPixmap(const QString& name, const QColor& color){
QSize size;
QPixmap img = ImageProvider::computePixmap(name, &size);
QPixmap pxr( img.size() );
pxr.fill( color );
pxr.setMask( img.createMaskFromColor( Qt::transparent ) );
return pxr;
}

View file

@ -32,6 +32,9 @@
#include "LinphoneEnums.hpp"
#include "Constants.hpp"
class QAction;
class QWidget;
// =============================================================================
/*
@ -46,6 +49,7 @@
#endif // if defined(__GNUC__) && __GNUC__ >= 7
#endif // ifndef UTILS_NO_BREAK
class ContentModel;
class Utils : public QObject{
Q_OBJECT
@ -65,6 +69,7 @@ public:
Q_INVOKABLE static bool isAnimatedImage(const QString& path);
Q_INVOKABLE static bool isImage(const QString& path);
Q_INVOKABLE static bool isVideo(const QString& path);
Q_INVOKABLE static bool isPdf(const QString& path);
Q_INVOKABLE static bool isSupportedForDisplay(const QString& path);
Q_INVOKABLE static bool isPhoneNumber(const QString& txt);
Q_INVOKABLE QSize getImageSize(const QString& url);
@ -76,6 +81,8 @@ public:
Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString& text, const QVariantMap& options = QVariantMap());
Q_INVOKABLE static QString getFileContent(const QString& filePath);
Q_INVOKABLE static bool openWithPdfViewer(ContentModel *contentModel, const QString& filePath, const int& width, const int& height); // return true if PDF is enabled
//----------------------------------------------------------------------------------
static inline QString coreStringToAppString (const std::string &str) {
@ -159,6 +166,9 @@ public:
static void deleteAllUserData();
static void deleteAllUserDataOffline();// When we are out of all events and core is not running (aka in main())
static void setFamilyFont(QAction * dest, const QString& family);
static void setFamilyFont(QWidget * dest, const QString& family);
static QPixmap getMaskedPixmap(const QString& name, const QColor& color);
};
#endif // UTILS_H_

View file

@ -25,6 +25,7 @@ DialogPlus{
property bool isAnimatedImage : filePath && UtilsCpp.isAnimatedImage(filePath)
property bool isVideo: filePath && UtilsCpp.isVideo(filePath)
property bool isImage: filePath && UtilsCpp.isImage(filePath)
property bool isPdf: filePath && UtilsCpp.isPdf(filePath)
property bool isSupportedForDisplay: filePath && tempFile.isReadable && UtilsCpp.isSupportedForDisplay(filePath)
showCloseCross: true
@ -61,6 +62,12 @@ DialogPlus{
tempFile.createFileFromContentModel(contentModel, false);
}
onExitStatus: if(loader.sourceComponent == videoComponent) loader.item.stop();
Component.onCompleted:{
if(mainItem.isPdf){
if(UtilsCpp.openWithPdfViewer(contentModel, mainItem.filePath, 800,800))
Qt.callLater(mainItem.exit)
}
}
TemporaryFile {
id: tempFile

View file

@ -40,7 +40,7 @@ TextEdit {
readOnly: true
selectByMouse: true
font.family: customFont.family
font.pointSize: Units.dp * customFont.pointSize * (UtilsCpp.isOnlyEmojis(contentModel.text) ? 4 : 1 )
font.pointSize: Units.dp * customFont.pointSize * (contentModel && UtilsCpp.isOnlyEmojis(contentModel.text) ? 4 : 1 )
text: visible ? UtilsCpp.encodeTextToQmlRichFormat(contentModel.text, {
imagesHeight: ChatStyle.entry.message.images.height,