Merge branch 'master' of https://invent.kde.org/graphics/okular into work/hires_wheel_page_scroll_rework

This commit is contained in:
Zack Mak 2024-05-02 20:50:30 +08:00
commit 4a6d28ddb8
1624 changed files with 706442 additions and 18434 deletions

View File

@ -1,5 +1,5 @@
---
Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,performance-*,bugprone-*,readability-inconsistent-declaration-parameter-name,readability-string-compare,readability-braces-around-statements,modernize-redundant-void-arg,modernize-use-bool-literals,modernize-make-unique,modernize-make-shared,modernize-use-override,modernize-use-equals-delete,modernize-use-emplace,modernize-loop-convert,modernize-use-nullptr,google-explicit-constructor,-bugprone-macro-parentheses,-bugprone-narrowing-conversions,-bugprone-branch-clone,-bugprone-incorrect-roundings,-bugprone-suspicious-include,-bugprone-reserved-identifier,-performance-no-automatic-move,-bugprone-suspicious-enum-usage,-bugprone-easily-swappable-parameters,-performance-no-int-to-ptr,-bugprone-implicit-widening-of-multiplication-result'
Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,performance-*,bugprone-*,readability-inconsistent-declaration-parameter-name,readability-string-compare,readability-braces-around-statements,modernize-redundant-void-arg,modernize-use-bool-literals,modernize-make-unique,modernize-make-shared,modernize-use-override,modernize-use-equals-delete,modernize-use-emplace,modernize-loop-convert,modernize-use-nullptr,google-explicit-constructor,-bugprone-assignment-in-if-condition,-bugprone-switch-missing-default-case,-bugprone-macro-parentheses,-bugprone-narrowing-conversions,-bugprone-branch-clone,-bugprone-incorrect-roundings,-bugprone-suspicious-include,-bugprone-reserved-identifier,-performance-no-automatic-move,-bugprone-suspicious-enum-usage,-bugprone-easily-swappable-parameters,-performance-no-int-to-ptr,-bugprone-implicit-widening-of-multiplication-result,-performance-unnecessary-copy-initialization'
WarningsAsErrors: '*'
HeaderFilterRegex: '.*/okular/.*'
AnalyzeTemporaryDtors: false

5
.craft.ini Normal file
View File

@ -0,0 +1,5 @@
; SPDX-FileCopyrightText: None
; SPDX-License-Identifier: CC0-1.0
[BlueprintSettings]
kde/applications/okular.packageAppx = True

View File

@ -2,57 +2,75 @@ variables:
DEBIAN_FRONTEND: "noninteractive"
include:
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml
- project: sysadmin/ci-utilities
file:
- /gitlab-templates/android-qt6.yml
- /gitlab-templates/linux-qt6.yml
- /gitlab-templates/freebsd-qt6.yml
- /gitlab-templates/windows-qt6.yml
- /gitlab-templates/cppcheck.yml
- /gitlab-templates/xml-lint.yml
- /gitlab-templates/craft-windows-x86-64-qt6.yml
# - /gitlab-templates/craft-android-qt6-apks.yml
- /gitlab-templates/craft-macos-arm64-qt6.yml
- /gitlab-templates/craft-macos-x86-64-qt6.yml
build_ubuntu_20_04:
stage: build
image: ubuntu:focal
tags:
- Linux
only:
- merge_requests
before_script:
- sed -i -e 's/# deb-src/deb-src/g' /etc/apt/sources.list
- apt-get update
- apt-get install --yes eatmydata
- eatmydata apt-get build-dep --yes --no-install-recommends okular
- eatmydata apt-get install --yes --no-install-recommends ninja-build qtbase5-private-dev
script:
- mkdir -p build && cd build
- cmake -DOKULAR_UI=desktop -G Ninja ..
- ninja
#build_ubuntu_22_04:
# stage: build
# image: ubuntu:22.04
# tags:
# - Linux
# only:
# - merge_requests
# before_script:
# - sed -i -e 's/# deb-src/deb-src/g' /etc/apt/sources.list
# - apt-get update
# - apt-get install --yes eatmydata
# - eatmydata apt-get build-dep --yes --no-install-recommends okular
# - eatmydata apt-get install --yes --no-install-recommends ninja-build libqt5x11extras5-dev
# script:
# - mkdir -p build && cd build
# - cmake -DOKULAR_UI=desktop -G Ninja ..
# - ninja
build_clazy_clang_tidy:
stage: build
image: debian:unstable
image: invent-registry.kde.org/sysadmin/ci-images/suse-qt66:latest
tags:
- Linux
interruptible: true
before_script:
- git clone https://invent.kde.org/sysadmin/ci-utilities.git --depth=1
- git clone https://invent.kde.org/sysadmin/repo-metadata.git ci-utilities/repo-metadata/ --depth=1
only:
- merge_requests
before_script:
- echo 'deb-src http://deb.debian.org/debian unstable main' >> /etc/apt/sources.list
- apt-get update
- apt-get install --yes eatmydata
- eatmydata apt-get build-dep --yes --no-install-recommends okular
- eatmydata apt-get install --yes --no-install-recommends ninja-build clazy clang clang-tidy qtbase5-private-dev libkf5crash-dev libkf5purpose-dev kirigami2-dev libegl-dev jq
variables:
KDECI_CC_CACHE: /mnt/caches/suse-qt6.6/
KDECI_CACHE_PATH: /mnt/artifacts/suse-qq6.6/
KDECI_GITLAB_SERVER: https://invent.kde.org/
KDECI_PACKAGE_PROJECT: teams/ci-artifacts/suse-qt6.6
CLAZY_CHECKS: level0,level1,level2,no-ctor-missing-parent-argument,isempty-vs-count,qhash-with-char-pointer-key,raw-environment-function,qproperty-type-mismatch
CXXFLAGS: -Werror -Wno-deprecated-declarations
CC: clang
CXX: clazy
script:
- srcdir=`pwd` && mkdir -p /tmp/okular_build && cd /tmp/okular_build && CC=clang CXX=clazy CXXFLAGS="-Werror -Wno-deprecated-declarations" cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja $srcdir && cat compile_commands.json | jq '[.[] | select(.file | contains("'"$srcdir"'"))]' > compile_commands.aux.json && cat compile_commands.aux.json | jq '[.[] | select(.file | contains("/synctex/")| not)]' > compile_commands.json && cp "$srcdir/.clang-tidy" .
- CLAZY_IGNORE_DIRS="settings_core.[cpp|h]|settings.[cpp.h]|pdfsettings.h|gssettings.h" CLAZY_CHECKS="level0,level1,level2,no-ctor-missing-parent-argument,isempty-vs-count,qhash-with-char-pointer-key,raw-environment-function,qproperty-type-mismatch" ninja
- run-clang-tidy-14
- rm -rf *
- echo "Now compiling the mobile UI"
- cd "$CI_PROJECT_DIR"
- srcdir=`pwd` && mkdir -p /tmp/okular_build && cd /tmp/okular_build && CC=clang CXX=clazy CXXFLAGS="-Werror -Wno-deprecated-declarations" cmake -DOKULAR_UI=mobile -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja $srcdir && cat compile_commands.json | jq '[.[] | select(.file | contains("'"$srcdir"'"))]' > compile_commands.aux.json && cat compile_commands.aux.json | jq '[.[] | select(.file | contains("/synctex/")| not)]' > compile_commands.json && cp "$srcdir/.clang-tidy" .
- CLAZY_IGNORE_DIRS="settings_mobile.[cpp|h]|settings.[cpp.h]|pdfsettings.h|gssettings.h" CLAZY_CHECKS="level0,level1,level2,no-ctor-missing-parent-argument,isempty-vs-count,qhash-with-char-pointer-key,raw-environment-function,qproperty-type-mismatch" ninja
- run-clang-tidy-14
- git config --global --add safe.directory $CI_PROJECT_DIR
- python3 -u ci-utilities/run-ci-build.py --project $CI_PROJECT_NAME --branch $CI_COMMIT_REF_NAME --platform Linux/Qt5/Shared --only-setup-environment
- mkdir -p /tmp/okular_build
- cd /tmp/okular_build
- cmake -DOKULAR_UI=both -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja -DCMAKE_INSTALL_PREFIX=$CI_PROJECT_DIR/_install -DFORCE_NOT_REQUIRED_DEPENDENCIES=KF6DocTools -DKF6DocTools_FOUND=false $CI_PROJECT_DIR
- cat compile_commands.json | jq '[.[] | select(.file | contains("'"$CI_PROJECT_DIR"'"))]' > compile_commands.aux.json
- cat compile_commands.aux.json | jq '[.[] | select(.file | contains("/synctex/")| not)]' > compile_commands.json
- cp "$CI_PROJECT_DIR/.clang-tidy" .
- CLAZY_IGNORE_DIRS="settings_core.[cpp|h]|settings.[cpp.h]|pdfsettings.h|gssettings.h|ui_selectcertificatedialog.h" ninja -k 0
- run-clang-tidy
clang_format:
stage: build
image: debian:testing
image: debian:stable
tags:
- Linux
only:
- merge_requests
- master

View File

@ -4,26 +4,43 @@
Dependencies:
- 'on': ['@all']
'require':
'frameworks/kbookmarks': '@stable'
'frameworks/threadweaver': '@stable'
'frameworks/ki18n': '@stable'
'frameworks/kio': '@stable'
'frameworks/karchive': '@stable'
'libraries/phonon': '@stable'
'frameworks/kbookmarks': '@latest-kf6'
'frameworks/threadweaver': '@latest-kf6'
'frameworks/kcolorscheme': '@latest-kf6'
'frameworks/ki18n': '@latest-kf6'
'frameworks/kio': '@latest-kf6'
'frameworks/kitemviews': '@latest-kf6'
'frameworks/karchive': '@latest-kf6'
'frameworks/kparts': '@latest-kf6'
'frameworks/ktextwidgets': '@latest-kf6'
'frameworks/kwidgetsaddons': '@latest-kf6'
'frameworks/kxmlgui': '@latest-kf6'
'libraries/phonon': '@latest-kf6'
'graphics/kdegraphics-mobipocket': '@same'
- 'on': ['Linux', 'FreeBSD', 'Windows']
'require':
'frameworks/khtml': '@stable'
'frameworks/kjs': '@stable'
'frameworks/purpose': '@stable'
'frameworks/purpose': '@latest-kf6'
'frameworks/breeze-icons': '@latest-kf6'
'graphics/libkexiv2': '@same'
- 'on': ['Linux', 'FreeBSD']
'require':
'frameworks/kactivities': '@stable'
'frameworks/kpty': '@stable'
'plasma/plasma-activities': '@latest-kf6'
'frameworks/kpty': '@latest-kf6'
- 'on': ['Android']
'require':
'frameworks/kirigami': '@stable'
'frameworks/kirigami': '@latest-kf6'
Options:
require-passing-tests-on: [ 'Linux', 'FreeBSD' ]
per-test-timeout: 300
cppcheck-arguments: '--enable=warning,style,performance -DOKULAR_EXPORT_PLUGIN --suppress-xml=cppcheck-suppressions.xml'
cppcheck-ignore-files:
- autotests
- core/synctex/synctex_parser.c
xml-validate-include:
- shell/shell.rc
- part/part-viewermode.rc
- part/part.rc

View File

@ -1,22 +1,39 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.22)
# KDE Application Version, managed by release script
set (RELEASE_SERVICE_VERSION_MAJOR "22")
set (RELEASE_SERVICE_VERSION_MINOR "11")
set (RELEASE_SERVICE_VERSION_MAJOR "24")
set (RELEASE_SERVICE_VERSION_MINOR "07")
set (RELEASE_SERVICE_VERSION_MICRO "70")
set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
project(okular VERSION ${RELEASE_SERVICE_VERSION})
set(QT_REQUIRED_VERSION "5.12.0") # Remember to update the QT_DEPRECATED_WARNINGS_SINCE below
set(KF5_REQUIRED_VERSION "5.68.0") # Remember to update the KF_DEPRECATED_WARNINGS_SINCE below
set(QT_REQUIRED_VERSION "6.6.0") # Remember to update the QT_DEPRECATED_WARNINGS_SINCE below
set(KF_REQUIRED_VERSION "5.240.0") # Remember to update the KF_DEPRECATED_WARNINGS_SINCE below
set(OKULAR_UI "" CACHE STRING "Which Okular user interface to build. Possible values: desktop, mobile, both. Default: desktop (except on Android, where it is 'mobile')")
option(FORCE_NOT_REQUIRED_DEPENDENCIES "List (semicolon-separated) of dependencies that will be downgraded from REQUIRED to RECOMMENDED")
set(FORCE_NOT_REQUIRED_DEPENDENCIES "" CACHE STRING "List (semicolon-separated) of dependencies that will be downgraded from REQUIRED to RECOMMENDED")
if (ANDROID AND (NOT FORCE_NOT_REQUIRED_DEPENDENCIES) AND (NOT FORCE_NOT_REQUIRED_DEPENDENCIES STREQUAL ""))
set(FORCE_NOT_REQUIRED_DEPENDENCIES "KF5Wallet;KF5DocTools;KF5JS;TIFF;JPEG;LibSpectre;KF5KExiv2;CHM;KF5KHtml;LibZip;DjVuLibre;EPub;Discount;")
find_package(ECM ${KF_REQUIRED_VERSION} CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
include(ECMInstallIcons)
include(ECMSetupVersion)
include(ECMOptionalAddSubdirectory)
include(GenerateExportHeader)
include(FeatureSummary)
include(ECMAddAppIcon)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(ECMAddTests)
include(ECMAddAppIcon)
include(CMakePackageConfigHelpers)
include(ECMSetupQtPluginMacroNames)
if (ANDROID AND (FORCE_NOT_REQUIRED_DEPENDENCIES STREQUAL ""))
set(FORCE_NOT_REQUIRED_DEPENDENCIES "KF6Wallet;KF6DocTools;Qt6Qml;TIFF;LibSpectre;KExiv2Qt6;CHM;LibZip;DjVuLibre;EPub;Discount;")
endif()
function(set_okular_optional_package_properties _name _props)
@ -65,27 +82,6 @@ else()
set(BUILD_MOBILE OFF)
endif()
if (BUILD_MOBILE)
set(QT_REQUIRED_VERSION "5.15.2")
endif()
find_package(ECM ${KF5_REQUIRED_VERSION} CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
include(ECMInstallIcons)
include(ECMSetupVersion)
include(ECMOptionalAddSubdirectory)
include(GenerateExportHeader)
include(FeatureSummary)
include(ECMAddAppIcon)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(ECMAddTests)
include(ECMAddAppIcon)
include(CMakePackageConfigHelpers)
include(ECMSetupQtPluginMacroNames)
# append the plugins from the install tree folder to the QT_PLUGIN_PATH
if(UNIX)
set(ENV{QT_PLUGIN_PATH} "$ENV{QT_PLUGIN_PATH}:${KDE_INSTALL_FULL_QTPLUGINDIR}")
@ -93,20 +89,16 @@ elseif(WIN32)
set(ENV{QT_PLUGIN_PATH} "$ENV{QT_PLUGIN_PATH};${KDE_INSTALL_FULL_QTPLUGINDIR}")
endif()
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX OKULAR
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/core/version.h"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/Okular5ConfigVersion.cmake")
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/Okular6ConfigVersion.cmake")
find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED COMPONENTS Core Test Widgets PrintSupport Svg Qml Quick)
if(ANDROID)
find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED COMPONENTS AndroidExtras)
endif()
find_package(Qt6 ${QT_REQUIRED_VERSION} CONFIG REQUIRED COMPONENTS Core Test Widgets PrintSupport Svg Xml)
ecm_setup_qtplugin_macro_names(
JSON_ARG2
@ -115,7 +107,7 @@ ecm_setup_qtplugin_macro_names(
PACKAGE_SETUP_AUTOMOC_VARIABLES
)
find_package(KF5 ${KF5_REQUIRED_VERSION} REQUIRED COMPONENTS
find_package(KF6 ${KF_REQUIRED_VERSION} REQUIRED COMPONENTS
Archive
Bookmarks
Completion
@ -126,73 +118,106 @@ find_package(KF5 ${KF5_REQUIRED_VERSION} REQUIRED COMPONENTS
KIO
ThreadWeaver
WindowSystem
XmlGui
)
find_package(KF5Wallet ${KF5_REQUIRED_VERSION})
set_okular_optional_package_properties(KF5Wallet PROPERTIES
if (NOT WIN32)
find_package(KF6Wallet ${KF_REQUIRED_VERSION})
set_okular_optional_package_properties(KF6Wallet PROPERTIES
PURPOSE "Required for document storing passwords in secure wallets.")
if(KF5Wallet_FOUND)
add_definitions(-DWITH_KWALLET=1)
endif()
if(KF6Wallet_FOUND)
set(HAVE_KWALLET 1)
else()
set(HAVE_KWALLET 0)
endif()
find_package(KF5DocTools ${KF5_REQUIRED_VERSION})
set_okular_optional_package_properties(KF5DocTools PROPERTIES
find_package(KF6DocTools ${KF_REQUIRED_VERSION})
set_okular_optional_package_properties(KF6DocTools PROPERTIES
PURPOSE "Required for compiling and installing the user documentation.")
find_package(KF5JS ${KF5_REQUIRED_VERSION})
set_okular_optional_package_properties(KF5JS PROPERTIES
find_package(Qt6Qml)
set_okular_optional_package_properties(Qt6Qml PROPERTIES
PURPOSE "Required for supporting JavaScript in PDF documents")
if(KF5JS_FOUND)
add_definitions(-DWITH_KJS=1)
if(TARGET Qt6::Qml)
set(HAVE_JS 1)
else()
set(HAVE_JS 0)
endif()
if (BUILD_DESKTOP)
find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED COMPONENTS DBus)
find_package(KF5 ${KF5_REQUIRED_VERSION} REQUIRED COMPONENTS Parts Crash IconThemes TextWidgets)
find_package(Qt6DBus)
set_okular_optional_package_properties(Qt6DBus PROPERTIES
PURPOSE "Required for interprocess communication, external open in same instance and various presentation related bits.")
if(TARGET Qt6::DBus)
set(HAVE_DBUS 1)
else()
set(HAVE_DBUS 0)
endif()
find_package(KF6 ${KF_REQUIRED_VERSION} REQUIRED COMPONENTS Parts ColorScheme Crash IconThemes ItemViews TextWidgets WidgetsAddons)
find_package(KF5Purpose)
set_okular_optional_package_properties(KF5Purpose PROPERTIES
find_package(KF6Purpose)
set_okular_optional_package_properties(KF6Purpose PROPERTIES
DESCRIPTION "A framework for services and actions integration"
PURPOSE "Required for enabling the share menu.")
if (KF5Purpose_FOUND)
set(PURPOSE_FOUND 1)
if (KF6Purpose_FOUND)
set(HAVE_PURPOSE 1)
else()
set(PURPOSE_FOUND 0)
set(HAVE_PURPOSE 0)
endif()
find_package(Qt5TextToSpeech ${QT_REQUIRED_VERSION} CONFIG)
set_okular_optional_package_properties(Qt5TextToSpeech PROPERTIES
find_package(Qt6TextToSpeech ${QT_REQUIRED_VERSION} CONFIG)
set_okular_optional_package_properties(Qt6TextToSpeech PROPERTIES
PURPOSE "Enables speech features.")
if (Qt5TextToSpeech_FOUND)
add_definitions(-DHAVE_SPEECH)
if (Qt6TextToSpeech_FOUND)
set(HAVE_SPEECH 1)
else()
set(HAVE_SPEECH 0)
endif()
endif()
if (BUILD_MOBILE)
find_package(KF5Kirigami2)
set_package_properties(KF5Kirigami2 PROPERTIES
find_package(Qt6 ${QT_REQUIRED_VERSION} CONFIG REQUIRED COMPONENTS Qml Quick)
find_package(KF6Kirigami2)
set_package_properties(KF6Kirigami2 PROPERTIES
DESCRIPTION "A QtQuick based components set"
PURPOSE "Required at runtime by the mobile app"
TYPE RUNTIME
)
find_package(KF6KirigamiAddons)
set_package_properties(KF6KirigamiAddons PROPERTIES
DESCRIPTION "A set of widgets for Kirigami along with code to support them"
PURPOSE "Required at runtime by the mobile app"
TYPE RUNTIME
)
endif()
if(NOT WIN32 AND NOT ANDROID AND NOT APPLE)
find_package(KF5 ${KF5_REQUIRED_VERSION} REQUIRED COMPONENTS
Activities
)
set_package_properties("KF5Activities" PROPERTIES
find_package(Qt6Gui REQUIRED COMPONENTS Private)
find_package(PlasmaActivities REQUIRED)
set_package_properties("PlasmaActivities" PROPERTIES
DESCRIPTION "Activities interface library"
URL "https://api.kde.org/frameworks/kactivities/html/"
PURPOSE "Required for Activities integration.")
URL "https://invent.kde.org/plasma/plasma-activities"
PURPOSE "Required for Activities integration."
)
set(HAVE_X11 TRUE)
else()
set(HAVE_X11 FALSE)
endif()
find_package(Phonon4Qt6 CONFIG)
set_okular_optional_package_properties(Phonon4Qt6 PROPERTIES
PURPOSE "Required for documents with audio or video elements")
if (Phonon4Qt6_FOUND)
set(HAVE_PHONON 1)
else()
set(HAVE_PHONON 0)
endif()
find_package(Phonon4Qt5 CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules)
find_package(ZLIB REQUIRED)
find_package(Poppler "0.86.0" COMPONENTS Qt5)
find_package(Poppler "22.02.0" COMPONENTS Qt6)
set_okular_optional_package_properties(Poppler PROPERTIES
PURPOSE "Support for PDF files in okular.")
@ -208,12 +233,6 @@ set_okular_optional_package_properties(TIFF PROPERTIES
URL "http://www.libtiff.org"
PURPOSE "Support for TIFF files in okular.")
find_package(JPEG)
set_okular_optional_package_properties(JPEG PROPERTIES
DESCRIPTION "A library for reading and writing JPEG image files."
URL "https://www.ijg.org"
PURPOSE "Support for PalmDB documents in okular.")
set(LIBSPECTRE_MINIMUM_VERSION "0.2")
find_package(LibSpectre "${LIBSPECTRE_MINIMUM_VERSION}")
set_okular_optional_package_properties(LibSpectre PROPERTIES
@ -221,22 +240,20 @@ set_okular_optional_package_properties(LibSpectre PROPERTIES
URL "https://libspectre.freedesktop.org"
PURPOSE "Support for PS files in okular.")
find_package(KF5KExiv2 CONFIG)
set_okular_optional_package_properties(KF5KExiv2 PROPERTIES
find_package(KExiv2Qt6 CONFIG)
set_okular_optional_package_properties(KExiv2Qt6 PROPERTIES
DESCRIPTION "Wrapper around Exiv2 library"
URL "https://commits.kde.org/libkexiv2"
PURPOSE "Support for exif rotation in image files.")
find_package(CHM)
set_okular_optional_package_properties(CHM PROPERTIES
DESCRIPTION "A library for dealing with Microsoft ITSS/CHM format files"
URL "http://www.jedrea.com/chmlib"
PURPOSE "Support CHM files in okular.")
if (FALSE ) # Investigate porting at some point
find_package(CHM)
set_okular_optional_package_properties(CHM PROPERTIES
DESCRIPTION "A library for dealing with Microsoft ITSS/CHM format files"
URL "http://www.jedrea.com/chmlib"
PURPOSE "Support CHM files in okular.")
find_package(KF5KHtml CONFIG)
set_okular_optional_package_properties(KF5KHtml PROPERTIES
DESCRIPTION "HTML rendering library"
PURPOSE "Support CHM files in okular.")
endif()
find_package(LibZip)
set_okular_optional_package_properties(LibZip PROPERTIES
@ -244,7 +261,7 @@ set_okular_optional_package_properties(LibZip PROPERTIES
URL "https://libzip.org/"
PURPOSE "Support CHM files in okular.")
find_package(DjVuLibre "3.5.17")
find_package(DjVuLibre "3.5.28")
set_okular_optional_package_properties(DjVuLibre PROPERTIES
DESCRIPTION "A library for dealing with DjVu formatted files"
URL "https://djvulibre.djvuzone.org"
@ -256,8 +273,8 @@ set_okular_optional_package_properties(EPub PROPERTIES
URL "http://sourceforge.net/projects/ebook-tools"
PURPOSE "Support for EPub documents in Okular.")
find_package(QMobipocket "2" CONFIG)
set_okular_optional_package_properties(QMobipocket PROPERTIES
find_package(QMobipocket6 "2" CONFIG)
set_okular_optional_package_properties(QMobiPocket6 PROPERTIES
DESCRIPTION "A library for reading Mobipocket documents"
URL "https://commits.kde.org/kdegraphics-mobipocket"
PURPOSE "Support for Mobipocket documents in Okular.")
@ -268,23 +285,12 @@ set_okular_optional_package_properties(Discount PROPERTIES
URL "https://www.pell.portland.or.us/~orc/Code/discount/"
PURPOSE "Support for Markdown documents in Okular.")
add_definitions(-DQT_USE_QSTRINGBUILDER)
add_definitions(-DTRANSLATION_DOMAIN="okular")
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
add_definitions(-DQT_NO_CAST_FROM_ASCII)
add_definitions(-DQT_NO_CAST_FROM_BYTEARRAY)
add_definitions(-DQT_NO_CAST_TO_ASCII)
add_definitions(-DQT_DEPRECATED_WARNINGS_SINCE=0x050C00)
add_definitions(-DKF_DEPRECATED_WARNINGS_SINCE=0x054400)
add_definitions(-DQT_NO_KEYWORDS)
add_definitions(-DQT_DEPRECATED_WARNINGS_SINCE=0x060600)
add_definitions(-DKF_DEPRECATED_WARNINGS_SINCE=0x05F000)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${PHONON_INCLUDES} core/synctex ${CMAKE_BINARY_DIR}/core)
if(BUILD_MOBILE AND KF5Kirigami2_VERSION VERSION_LESS "5.85")
message(FATAL_ERROR "Disable mobile build. This requires Kirigami 5.85 or greater.")
set(BUILD_MOBILE OFF)
endif()
option(BUILD_COVERAGE "Build the project with gcov support" OFF)
if(BUILD_COVERAGE)
@ -307,7 +313,7 @@ if(BUILD_TESTING)
add_subdirectory( autotests )
endif()
if(KF5DocTools_FOUND)
if(KF6DocTools_FOUND)
add_subdirectory(doc)
endif()
@ -359,7 +365,7 @@ set(okularcore_SRCS
core/synctex/synctex_parser.c
core/synctex/synctex_parser_utils.c
)
qt5_add_resources(okularcore_SRCS
qt_add_resources(okularcore_SRCS
core/script/builtin.qrc
)
@ -411,6 +417,7 @@ kconfig_add_kcfg_files(okularcore_SRCS GENERATE_MOC conf/settings_core.kcfgc)
add_library(okularcore SHARED ${okularcore_SRCS})
generate_export_header(okularcore BASE_NAME okularcore EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/core/okularcore_export.h")
target_include_directories(okularcore PUBLIC "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}>")
if (ANDROID)
set(fileName ${CMAKE_BINARY_DIR}/Okular5Core-android-dependencies.xml)
@ -435,55 +442,56 @@ target_link_libraries(okularcore
PRIVATE
${OKULAR_IOKIT}
${SHLWAPI}
KF5::Archive
KF5::KIOCore
KF5::KIOWidgets
KF5::I18n
KF5::ThreadWeaver
KF5::Bookmarks
Phonon::phonon4qt5
Qt5::Svg
KF6::Archive
KF6::KIOCore
KF6::KIOWidgets
KF6::I18n
KF6::ThreadWeaver
KF6::BookmarksWidgets
KF6::Bookmarks
Qt6::Svg
${MATH_LIB}
ZLIB::ZLIB
PUBLIC # these are included from the installed headers
KF5::CoreAddons
KF5::XmlGui
KF5::ConfigGui
Qt5::PrintSupport
Qt5::Widgets
Qt5::CorePrivate
KF6::CoreAddons
KF6::XmlGui
KF6::ConfigGui
Qt6::PrintSupport
Qt6::Widgets
)
if (KF5Wallet_FOUND)
target_link_libraries(okularcore PRIVATE KF5::Wallet)
if (Phonon4Qt6_FOUND)
target_link_libraries(okularcore PRIVATE Phonon::phonon4qt6)
endif()
if (KF5JS_FOUND)
if (KF6Wallet_FOUND)
target_link_libraries(okularcore PRIVATE KF6::Wallet)
endif()
if (TARGET Qt6::Qml)
target_sources(okularcore PRIVATE
core/script/executor_kjs.cpp
core/script/kjs_app.cpp
core/script/kjs_console.cpp
core/script/kjs_data.cpp
core/script/kjs_display.cpp
core/script/kjs_document.cpp
core/script/kjs_field.cpp
core/script/kjs_fullscreen.cpp
core/script/kjs_field.cpp
core/script/kjs_spell.cpp
core/script/kjs_util.cpp
core/script/kjs_event.cpp
core/script/kjs_ocg.cpp
core/script/executor_js.cpp
core/script/js_app.cpp
core/script/js_console.cpp
core/script/js_data.cpp
core/script/js_display.cpp
core/script/js_document.cpp
core/script/js_field.cpp
core/script/js_fullscreen.cpp
core/script/js_field.cpp
core/script/js_spell.cpp
core/script/js_util.cpp
core/script/js_event.cpp
core/script/js_ocg.cpp
)
target_link_libraries(okularcore PRIVATE KF5::JS KF5::JSApi)
target_link_libraries(okularcore PRIVATE Qt6::Qml)
endif()
set_target_properties(okularcore PROPERTIES VERSION 10.0.0 SOVERSION 10 OUTPUT_NAME Okular5Core EXPORT_NAME Core)
set_target_properties(okularcore PROPERTIES VERSION 1.0.0 SOVERSION 1 OUTPUT_NAME Okular6Core EXPORT_NAME Core)
install(TARGETS okularcore EXPORT Okular5Targets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(TARGETS okularcore EXPORT Okular6Targets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES conf/okular.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
install(FILES conf/okular_core.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
install(FILES core/okularGenerator.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR})
if(BUILD_DESKTOP)
# okularpart
@ -509,7 +517,6 @@ if(BUILD_DESKTOP)
part/widgetconfigurationtoolsbase.cpp
part/widgetdrawingtools.cpp
part/part.cpp
part/xmlgui_helper.cpp
part/extensions.cpp
part/embeddedfilesdialog.cpp
part/actionbar.cpp
@ -557,9 +564,12 @@ if(BUILD_DESKTOP)
part/signaturepartutils.cpp
part/signaturepropertiesdialog.cpp
part/signaturepanel.cpp
)
if (Qt5TextToSpeech_FOUND)
part/part.qrc
)
ki18n_wrap_ui(okularpart_SRCS part/selectcertificatedialog.ui)
if (Qt6TextToSpeech_FOUND)
set(okularpart_SRCS ${okularpart_SRCS}
part/tts.cpp)
endif()
@ -571,75 +581,74 @@ generate_export_header(okularpart BASE_NAME okularpart)
target_link_libraries(okularpart okularcore
${MATH_LIB}
Qt5::Svg
Phonon::phonon4qt5
KF5::Archive
KF5::Bookmarks
KF5::I18n
KF5::IconThemes
KF5::ItemViews
KF5::KIOCore
KF5::KIOFileWidgets
KF5::KIOWidgets
KF5::Parts
KF5::Solid
KF5::WindowSystem
KF5::TextWidgets
Qt6::Svg
KF6::Archive
KF6::Bookmarks
KF6::I18n
KF6::IconThemes
KF6::ItemViews
KF6::KIOCore
KF6::KIOWidgets
KF6::Parts
KF6::WindowSystem
KF6::TextWidgets
)
if(KF5Wallet_FOUND)
target_link_libraries(okularpart KF5::Wallet)
if (TARGET Qt6::DBus)
target_link_libraries(okularpart Qt6::DBus)
endif()
if (Phonon4Qt6_FOUND)
target_link_libraries(okularpart Phonon::phonon4qt6)
endif()
if (KF5Purpose_FOUND)
target_link_libraries(okularpart KF5::PurposeWidgets)
if(KF6Wallet_FOUND)
target_link_libraries(okularpart KF6::Wallet)
endif()
if (KF6Purpose_FOUND)
target_link_libraries(okularpart KF6::PurposeWidgets)
endif()
set_target_properties(okularpart PROPERTIES PREFIX "")
set_target_properties(okularpart PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/kf6/parts)
if (Qt5TextToSpeech_FOUND)
target_link_libraries(okularpart Qt5::TextToSpeech)
endif()
install(TARGETS okularpart DESTINATION ${KDE_INSTALL_PLUGINDIR})
if (Qt6TextToSpeech_FOUND)
target_link_libraries(okularpart Qt6::TextToSpeech)
endif()
#instead install the part to this path so it can be found
install(TARGETS okularpart DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf6/parts/ )
endif()# End of BUILD_DESKTOP if
########### install files ###############
install(FILES okular.upd DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR})
install( FILES okular_part.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} )
install( FILES part/part.rc part/part-viewermode.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/okular )
if (${ECM_VERSION} STRGREATER "5.58.0")
install(FILES okular.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR})
else()
install(FILES okular.categories DESTINATION ${KDE_INSTALL_CONFDIR})
endif()
install(FILES okular.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR})
ki18n_install(po)
if(KF5DocTools_FOUND)
if(KF6DocTools_FOUND)
kdoctools_install(po)
endif()
########### cmake files #################
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/Okular5")
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/Okular6")
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/Okular5Config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/Okular5Config.cmake"
"${CMAKE_CURRENT_SOURCE_DIR}/Okular6Config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/Okular6Config.cmake"
INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
PATH_VARS INCLUDE_INSTALL_DIR CMAKE_INSTALL_PREFIX
PATH_VARS KDE_INSTALL_INCLUDEDIR CMAKE_INSTALL_PREFIX
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/Okular5Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/Okular5ConfigVersion.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/Okular6Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/Okular6ConfigVersion.cmake"
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
COMPONENT Devel
)
install(EXPORT Okular5Targets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE Okular5Targets.cmake NAMESPACE Okular::)
install(EXPORT Okular6Targets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE Okular6Targets.cmake NAMESPACE Okular::)
install(FILES
core/stamps.svg

View File

@ -411,13 +411,13 @@ add_definitions(-DTRANSLATION_DOMAIN="okular_magic")
macro_optional_find_package(Okular)
include_directories( ${OKULAR_INCLUDE_DIR} ${KF5_INCLUDE_DIR} ${QT_INCLUDES} )
include_directories( ${OKULAR_INCLUDE_DIR} ${KF6_INCLUDE_DIR} ${QT_INCLUDES} )
########### next target ###############
set( okularGenerator_magic_PART_SRCS generator_magic.cpp )
target_link_libraries( okularGenerator_magic PRIVATE okularcore KF5::I18n KF5::KIOCore )
target_link_libraries( okularGenerator_magic PRIVATE okularcore KF6::I18n KF6::KIOCore )
install( TARGETS okularGenerator_magic DESTINATION ${PLUGIN_INSTALL_DIR} )

View File

@ -1,14 +0,0 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(Qt5Core @QT_REQUIRED_VERSION@)
find_dependency(Qt5PrintSupport @QT_REQUIRED_VERSION@)
find_dependency(Qt5Widgets @QT_REQUIRED_VERSION@)
find_dependency(KF5CoreAddons @KF5_REQUIRED_VERSION@)
find_dependency(KF5Config @KF5_REQUIRED_VERSION@)
find_dependency(KF5XmlGui @KF5_REQUIRED_VERSION@)
@PACKAGE_SETUP_AUTOMOC_VARIABLES@
include("${CMAKE_CURRENT_LIST_DIR}/Okular5Targets.cmake")

14
Okular6Config.cmake.in Normal file
View File

@ -0,0 +1,14 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(Qt6Core @QT_REQUIRED_VERSION@)
find_dependency(Qt6PrintSupport @QT_REQUIRED_VERSION@)
find_dependency(Qt6Widgets @QT_REQUIRED_VERSION@)
find_dependency(KF6CoreAddons @KF6_REQUIRED_VERSION@)
find_dependency(KF6Config @KF6_REQUIRED_VERSION@)
find_dependency(KF6XmlGui @KF6_REQUIRED_VERSION@)
@PACKAGE_SETUP_AUTOMOC_VARIABLES@
include("${CMAKE_CURRENT_LIST_DIR}/Okular6Targets.cmake")

View File

@ -9,12 +9,12 @@ macro(add_conf_unittest _source)
add_test( NAME ${_name} COMMAND ${_name} )
ecm_mark_as_test(${_name})
target_link_libraries( ${_name}
Qt5::Test
Qt5::Widgets
Qt5::Xml
KF5::I18n
KF5::Completion
KF5::WidgetsAddons
Qt6::Test
Qt6::Widgets
Qt6::Xml
KF6::I18n
KF6::Completion
KF6::WidgetsAddons
)
endmacro ()
@ -22,117 +22,134 @@ add_conf_unittest(editdrawingtooldialogtest.cpp)
ecm_add_test(shelltest.cpp ../shell/shellutils.cpp
TEST_NAME "shelltest"
LINK_LIBRARIES Qt5::Test okularcore
LINK_LIBRARIES Qt6::Test okularcore
)
if(Poppler_Qt5_FOUND)
if(Poppler_Qt6_FOUND)
if (BUILD_DESKTOP)
ecm_add_test(parttest.cpp closedialoghelper.cpp
TEST_NAME "parttest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test Qt5::Xml okularcore okularpart
LINK_LIBRARIES Qt6::Widgets Qt6::Test Qt6::Xml okularcore okularpart
)
endif()
ecm_add_test(visibilitytest.cpp
TEST_NAME "visibilitytest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test okularcore
)
ecm_add_test(kjsfunctionstest.cpp
TEST_NAME "kjsfunctionstest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore
ecm_add_test(jsfunctionstest.cpp
TEST_NAME "jsfunctionstest"
LINK_LIBRARIES Qt6::Widgets Qt6::Test okularcore
)
ecm_add_test(formattest.cpp
TEST_NAME "formattest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test okularcore
)
ecm_add_test(keystroketest.cpp
TEST_NAME "keystroketest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test okularcore
)
ecm_add_test(signunsignedfieldtest.cpp
TEST_NAME "signunsignedfieldtest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test okularcore
)
endif()
ecm_add_test(suggestedfilenametest.cpp
TEST_NAME "suggestedfilenametest"
LINK_LIBRARIES Qt6::Test okularcore KF6::I18n
)
ecm_add_test(documenttest.cpp
TEST_NAME "documenttest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test Qt5::Xml okularcore KF5::ThreadWeaver
LINK_LIBRARIES Qt6::Widgets Qt6::Test Qt6::Xml okularcore KF6::ThreadWeaver
)
ecm_add_test(searchtest.cpp
TEST_NAME "searchtest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test Qt5::Xml okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test Qt6::Xml okularcore
)
ecm_add_test(annotationstest.cpp
TEST_NAME "annotationstest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test Qt5::Xml okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test Qt6::Xml okularcore
)
ecm_add_test(urldetecttest.cpp
TEST_NAME "urldetecttest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test Qt5::Xml KF5::CoreAddons
LINK_LIBRARIES Qt6::Widgets Qt6::Test Qt6::Xml KF6::CoreAddons
)
ecm_add_test(editannotationcontentstest.cpp testingutils.cpp
TEST_NAME "editannotationcontentstest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test Qt5::Xml okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test Qt6::Xml okularcore
)
ecm_add_test(addremoveannotationtest.cpp testingutils.cpp
TEST_NAME "addremoveannotationtest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test Qt5::Xml okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test Qt6::Xml okularcore
)
ecm_add_test(translateannotationtest.cpp testingutils.cpp
TEST_NAME "translateannotationtest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test Qt5::Xml okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test Qt6::Xml okularcore
)
ecm_add_test(modifyannotationpropertiestest.cpp testingutils.cpp
TEST_NAME "modifyannotationpropertiestest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test okularcore
)
ecm_add_test(editformstest.cpp
TEST_NAME "editformstest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test okularcore
)
ecm_add_test(calculatetexttest.cpp
TEST_NAME "calculatetexttest"
LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore
LINK_LIBRARIES Qt6::Widgets Qt6::Test okularcore
)
if(KF5Activities_FOUND AND BUILD_DESKTOP)
ecm_add_test(mainshelltest.cpp ../shell/okular_main.cpp ../shell/shellutils.cpp ../shell/shell.cpp ../shell/welcomescreen.cpp ../shell/recentitemsmodel.cpp closedialoghelper.cpp
TEST_NAME "mainshelltest"
LINK_LIBRARIES Qt5::Test KF5::Activities okularpart okularcore
)
target_compile_definitions(mainshelltest PRIVATE OKULAR_BINARY="$<TARGET_FILE:okular>")
ecm_add_test(check_distinguished_name_parser.cpp
TEST_NAME "distinguishednameparser"
LINK_LIBRARIES Qt6::Test)
if(PlasmaActivities_FOUND AND BUILD_DESKTOP)
ecm_add_test(mainshelltest.cpp ../shell/okular_main.cpp ../shell/shellutils.cpp ../shell/shell.cpp ../shell/welcomescreen.cpp ../shell/recentitemsmodel.cpp closedialoghelper.cpp
TEST_NAME "mainshelltest"
LINK_LIBRARIES Qt6::Test Plasma::Activities okularpart okularcore
)
target_compile_definitions(mainshelltest PRIVATE OKULAR_BINARY="$<TARGET_FILE:okular>")
if (HAVE_X11)
target_link_libraries(mainshelltest Qt6::GuiPrivate)
endif()
endif()
if(BUILD_DESKTOP)
ecm_add_test(annotationtoolbartest.cpp ../shell/okular_main.cpp ../shell/shellutils.cpp ../shell/shell.cpp ../shell/welcomescreen.cpp ../shell/recentitemsmodel.cpp closedialoghelper.cpp ../shell/welcomescreen.ui
TEST_NAME "annotationtoolbartest"
LINK_LIBRARIES Qt5::Test okularpart
LINK_LIBRARIES Qt6::Test okularpart
)
if (HAVE_X11)
target_link_libraries(annotationtoolbartest Qt6::GuiPrivate)
endif()
endif()
ecm_add_test(generatorstest.cpp
TEST_NAME "generatorstest"
LINK_LIBRARIES Qt5::Test KF5::CoreAddons okularcore
LINK_LIBRARIES Qt6::Test KF6::CoreAddons okularcore
)
target_compile_definitions(generatorstest PRIVATE GENERATORS_BUILD_DIR="${CMAKE_BINARY_DIR}/generators")
ecm_add_test(signatureformtest.cpp
TEST_NAME "signatureformtest"
LINK_LIBRARIES Qt5::Test okularcore
LINK_LIBRARIES Qt6::Test okularcore
)
find_package(Discount)
@ -140,11 +157,11 @@ find_package(Discount)
if(Discount_FOUND)
ecm_add_test(markdowntest.cpp ../generators/markdown/converter.cpp
TEST_NAME "markdowntest"
LINK_LIBRARIES Qt5::Test okularcore KF5::I18n PkgConfig::Discount
LINK_LIBRARIES Qt6::Test okularcore KF6::I18n PkgConfig::Discount
)
endif()
ecm_add_test(toggleactionmenutest.cpp ../part/toggleactionmenu.cpp
TEST_NAME "toggleactionmenutest"
LINK_LIBRARIES Qt5::Test KF5::WidgetsAddons
LINK_LIBRARIES Qt6::Test KF6::WidgetsAddons
)

View File

@ -94,11 +94,11 @@ void AnnotationTest::testDistance_data()
square->setGeometricalInnerColor(QColor(0, 0, 0));
}
m_document->addPageAnnotation(0, square);
QTest::newRow("Square: Base point 1") << (Okular::Annotation *)square << 0.1 << 0.1 << 0;
QTest::newRow("Square: On edge 1") << (Okular::Annotation *)square << 0.1 << 0.2 << 0;
QTest::newRow("Square: On edge 2") << (Okular::Annotation *)square << 0.2 << 0.1 << 0;
QTest::newRow("Square: Inside") << (Okular::Annotation *)square << 0.2 << 0.2 << ((i == 0) ? qRound(pow(0.1 * documentX, 2) - 1 /* stroke width */) : 0);
QTest::newRow("Square: Outside") << (Okular::Annotation *)square << 0.0 << 0.0 << qRound(pow(0.1 * documentX, 2) + pow(0.1 * documentY, 2));
QTest::addRow("Square: Base point 1 %d", i) << (Okular::Annotation *)square << 0.1 << 0.1 << 0;
QTest::addRow("Square: On edge 1 %d", i) << (Okular::Annotation *)square << 0.1 << 0.2 << 0;
QTest::addRow("Square: On edge 2 %d", i) << (Okular::Annotation *)square << 0.2 << 0.1 << 0;
QTest::addRow("Square: Inside %d", i) << (Okular::Annotation *)square << 0.2 << 0.2 << ((i == 0) ? qRound(pow(0.1 * documentX, 2) - 1 /* stroke width */) : 0);
QTest::addRow("Square: Outside %d", i) << (Okular::Annotation *)square << 0.0 << 0.0 << qRound(pow(0.1 * documentX, 2) + pow(0.1 * documentY, 2));
}
// ellipsis
@ -110,9 +110,9 @@ void AnnotationTest::testDistance_data()
ellipse->setGeometricalInnerColor(QColor(0, 0, 0));
}
m_document->addPageAnnotation(0, ellipse);
QTest::newRow("Ellipse: Base point 1") << (Okular::Annotation *)ellipse << 0.1 << 0.3 << 0;
QTest::newRow("Ellipse: Inside") << (Okular::Annotation *)ellipse << 0.2 << 0.3 << qRound((i == 0) ? pow(documentX * 0.1, 2) - 1 /* pen */ : 0);
QTest::newRow("Ellipse: Outside") << (Okular::Annotation *)ellipse << 0.0 << 0.3 << qRound(pow(documentX * 0.1, 2));
QTest::addRow("Ellipse: Base point 1 %d", i) << (Okular::Annotation *)ellipse << 0.1 << 0.3 << 0;
QTest::addRow("Ellipse: Inside %d", i) << (Okular::Annotation *)ellipse << 0.2 << 0.3 << qRound((i == 0) ? pow(documentX * 0.1, 2) - 1 /* pen */ : 0);
QTest::addRow("Ellipse: Outside %d", i) << (Okular::Annotation *)ellipse << 0.0 << 0.3 << qRound(pow(documentX * 0.1, 2));
}
// highlight

View File

@ -90,8 +90,8 @@ void AnnotationToolBarTest::initMain()
QByteArray homePath = QFile::encodeName(homeDir.path());
qputenv("USERPROFILE", homePath);
qputenv("HOME", homePath);
qputenv("XDG_DATA_HOME", homePath + "/.local");
qputenv("XDG_CONFIG_HOME", homePath + "/.kde-unit-test/xdg/config");
qputenv("XDG_DATA_HOME", QByteArray(homePath + "/.local"));
qputenv("XDG_CONFIG_HOME", QByteArray(homePath + "/.kde-unit-test/xdg/config"));
}
void AnnotationToolBarTest::initTestCase()

View File

@ -0,0 +1,166 @@
/*
SPDX-FileCopyrightText: 2023 g10 Code GmbH
SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>
SPDX-License-Identifier: GPL-2.0-or-later
Copied from poppler (pdf library)
*/
#include "../part/DistinguishedNameParser.h"
#include <QTest>
class TestDistinguishedNameParser : public QObject
{
Q_OBJECT
public:
explicit TestDistinguishedNameParser(QObject *parent = nullptr)
: QObject(parent)
{
}
private Q_SLOTS:
// The big set of input/output. Several of the helper functions can be tested independently
void testParser();
void testParser_data();
void testRemoveLeadingSpaces();
void testRemoveLeadingSpaces_data();
void testRemoveTrailingSpaces();
void testRemoveTrailingSpaces_data();
void testParseHexString();
void testParseHexString_data();
};
Q_DECLARE_METATYPE(DN::Result);
Q_DECLARE_METATYPE(std::string);
Q_DECLARE_METATYPE(std::optional<std::string>);
void TestDistinguishedNameParser::testParser()
{
QFETCH(std::string, inputData);
QFETCH(DN::Result, expectedResult);
auto result = DN::parseString(inputData);
QCOMPARE(result, expectedResult);
}
void TestDistinguishedNameParser::testParser_data()
{
QTest::addColumn<std::string>("inputData");
QTest::addColumn<DN::Result>("expectedResult");
QTest::newRow("empty") << std::string {} << DN::Result {};
QTest::newRow("CN=Simple") << std::string {"CN=Simple"} << DN::Result {{"CN", "Simple"}};
QTest::newRow("CN=Name with spaces") << std::string {"CN=Name with spaces"} << DN::Result {{"CN", "Name with spaces"}};
QTest::newRow("CN=Simple,O=Silly") << std::string {"CN=Simple,O=Silly"} << DN::Result {{"CN", "Simple"}, {"O", "Silly"}};
QTest::newRow("CN=Steve Kille,O=Isode Limited,C=GB") << std::string {"CN=Steve Kille,O=Isode Limited,C=GB"} << DN::Result {{"CN", "Steve Kille"}, {"O", "Isode Limited"}, {"C", "GB"}};
QTest::newRow("CN=some.user@example.com, O=MyCompany, L=San Diego,ST=California, C=US")
<< std::string {"CN=some.user@example.com, O=MyCompany, L=San Diego,ST=California, C=US"} << DN::Result {{"CN", "some.user@example.com"}, {"O", "MyCompany"}, {"L", "San Diego"}, {"ST", "California"}, {"C", "US"}};
QTest::newRow("Multi valued") << std::string {"OU=Sales+CN=J. Smith,O=Widget Inc.,C=US"}
<< DN::Result {{"OU", "Sales"}, {"CN", "J. Smith"}, {"O", "Widget Inc."}, {"C", "US"}}; // This is technically wrong, but probably good enough for now
QTest::newRow("Escaping comma") << std::string {"CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB"} << DN::Result {{"CN", "L. Eagle"}, {"O", "Sue, Grabbit and Runn"}, {"C", "GB"}};
QTest::newRow("Escaped trailing space") << std::string {"CN=Trailing space\\ "} << DN::Result {{"CN", "Trailing space "}};
QTest::newRow("Escaped quote") << std::string {"CN=Quotation \\\" Mark"} << DN::Result {{"CN", "Quotation \" Mark"}};
QTest::newRow("CN=Simple with escaping") << std::string {"CN=S\\69mpl\\65\\7A"} << DN::Result {{"CN", "Simplez"}};
QTest::newRow("SN=Lu\\C4\\8Di\\C4\\87") << std::string {"SN=Lu\\C4\\8Di\\C4\\87"} << DN::Result {{"SN", "Lučić"}};
QTest::newRow("CN=\"Quoted name\"") << std::string {"CN=\"Quoted name\""} << DN::Result {{"CN", "Quoted name"}};
QTest::newRow("CN=\" Leading and trailing spacees \"") << std::string {"CN=\" Leading and trailing spaces \""} << DN::Result {{"CN", " Leading and trailing spaces "}};
QTest::newRow("Comma in quotes") << std::string {"CN=\"Comma, inside\""} << DN::Result {{"CN", "Comma, inside"}};
QTest::newRow("forbidden chars in quotes") << std::string {"CN=\"Forbidden !@#$%&*()<>[]{},.?/\\| chars\""} << DN::Result {{"CN", "Forbidden !@#$%&*()<>[]{},.?/\\| chars"}};
QTest::newRow("Quoted quotation") << std::string {"CN=\"Quotation \\\" Mark\""} << DN::Result {{"CN", "Quotation \" Mark"}};
QTest::newRow("Quoted quotation") << std::string {"CN=\"Quotation \\\" Mark\\\" Multiples\""} << DN::Result {{"CN", "Quotation \" Mark\" Multiples"}};
QTest::newRow("frompdf1") << std::string {"2.5.4.97=#5553742D49644E722E20444520313233343735323233,CN=TeleSec PKS eIDAS QES CA 5,O=Deutsche Telekom AG,C=DE"}
<< DN::Result {{"2.5.4.97", "USt-IdNr. DE 123475223"}, {"CN", "TeleSec PKS eIDAS QES CA 5"}, {"O", "Deutsche Telekom AG"}, {"C", "DE"}};
QTest::newRow("frompdf2") << std::string {"2.5.4.5=#34,CN=Koch\\, Werner,2.5.4.42=#5765726E6572,2.5.4.4=#4B6F6368,C=DE"} << DN::Result {{"SerialNumber", "4"}, {"CN", "Koch, Werner"}, {"GN", "Werner"}, {"SN", "Koch"}, {"C", "DE"}};
QTest::newRow("frompdf2a") << std::string {"2.5.4.5=#34,CN=Koch\\, Werner,oid.2.5.4.42=#5765726E6572,OID.2.5.4.4=#4B6F6368,C=DE"}
<< DN::Result {{"SerialNumber", "4"}, {"CN", "Koch, Werner"}, {"GN", "Werner"}, {"SN", "Koch"}, {"C", "DE"}};
// weird spacing
QTest::newRow("CN =Simple") << std::string {"CN =Simple"} << DN::Result {{"CN", "Simple"}};
QTest::newRow("CN= Simple") << std::string {"CN= Simple"} << DN::Result {{"CN", "Simple"}};
QTest::newRow("CN=Simple ") << std::string {"CN=Simple "} << DN::Result {{"CN", "Simple"}};
QTest::newRow("CN=Simple,") << std::string {"CN=Simple,"} << DN::Result {{"CN", "Simple"}};
QTest::newRow("CN=Simple, O=Silly") << std::string {"CN=Simple, O=Silly"} << DN::Result {{"CN", "Simple"}, {"O", "Silly"}};
// various malformed
QTest::newRow("CN=Simple\\") << std::string {"CN=Simple\\"} << DN::Result {};
QTest::newRow("CN=") << std::string {"CN="} << DN::Result {};
QTest::newRow("CN=Simple\\X") << std::string {"CN=Simple\\X"} << DN::Result {};
QTest::newRow("CN=Simple, O") << std::string {"CN=Simple, O"} << DN::Result {};
QTest::newRow("CN=Sim\"ple") << std::string {"CN=Sim\"ple, O"} << DN::Result {};
QTest::newRow("CN=Simple\\a") << std::string {"CN=Simple\\a"} << DN::Result {};
QTest::newRow("=Simple") << std::string {"=Simple"} << DN::Result {};
QTest::newRow("CN=\"Simple") << std::string {"CN=\"Simple"} << DN::Result {};
QTest::newRow("CN=\"Simple") << std::string {"CN=\"Simple\\"} << DN::Result {};
QTest::newRow("unquoted quotation in quotation") << std::string {"CN=\"Quotation \" Mark\""} << DN::Result {};
}
void TestDistinguishedNameParser::testRemoveLeadingSpaces()
{
QFETCH(std::string, input);
QFETCH(std::string, expectedOutput);
auto result = DN::detail::removeLeadingSpaces(input);
QCOMPARE(result, expectedOutput);
}
void TestDistinguishedNameParser::testRemoveLeadingSpaces_data()
{
QTest::addColumn<std::string>("input");
QTest::addColumn<std::string>("expectedOutput");
QTest::newRow("Empty") << std::string {} << std::string {};
QTest::newRow("No leading spaces") << std::string {"horse"} << std::string {"horse"};
QTest::newRow("Some spaces") << std::string {" horse"} << std::string {"horse"};
QTest::newRow("Some leading and trailing") << std::string {" horse "} << std::string {"horse "};
}
void TestDistinguishedNameParser::testRemoveTrailingSpaces()
{
QFETCH(std::string, input);
QFETCH(std::string, expectedOutput);
auto result = DN::detail::removeTrailingSpaces(input);
QCOMPARE(result, expectedOutput);
}
void TestDistinguishedNameParser::testRemoveTrailingSpaces_data()
{
QTest::addColumn<std::string>("input");
QTest::addColumn<std::string>("expectedOutput");
QTest::newRow("Empty") << std::string {} << std::string {};
QTest::newRow("No leading spaces") << std::string {"horse"} << std::string {"horse"};
QTest::newRow("Some spaces") << std::string {"horse "} << std::string {"horse"};
QTest::newRow("Some leading and trailing") << std::string {" horse "} << std::string {" horse"};
}
void TestDistinguishedNameParser::testParseHexString()
{
QFETCH(std::string, input);
QFETCH(std::optional<std::string>, expectedOutput);
auto result = DN::detail::parseHexString(input);
QCOMPARE(result, expectedOutput);
}
void TestDistinguishedNameParser::testParseHexString_data()
{
QTest::addColumn<std::string>("input");
QTest::addColumn<std::optional<std::string>>("expectedOutput");
QTest::newRow("4") << std::string {"34"} << std::optional<std::string>("4");
QTest::newRow("Koch") << std::string {"4B6F6368"} << std::optional<std::string>("Koch");
QTest::newRow("USt-IdNr. DE 123475223") << std::string {"5553742D49644E722E20444520313233343735323233"} << std::optional<std::string>("USt-IdNr. DE 123475223");
// various baddies
QTest::newRow("empty") << std::string {} << std::optional<std::string> {};
QTest::newRow("FFF") << std::string {"FFF"} << std::optional<std::string> {};
QTest::newRow("F") << std::string {"F"} << std::optional<std::string> {};
QTest::newRow("XX") << std::string {"XX"} << std::optional<std::string> {};
}
QTEST_GUILESS_MAIN(TestDistinguishedNameParser);
#include "check_distinguished_name_parser.moc"

View File

@ -4,6 +4,7 @@
#include <QDialog>
#include <QPushButton>
#include <QTest>
#include <QTimer>
namespace TestingUtils
{
@ -39,7 +40,7 @@ CloseDialogHelper::~CloseDialogHelper()
void CloseDialogHelper::closeDialog()
{
QWidget *dialog = (m_widget) ? m_widget->findChild<QDialog *>() : qApp->activeModalWidget();
if (!dialog) {
if (!dialog || !QTest::qWaitForWindowExposed(dialog)) {
QTimer::singleShot(0, this, &CloseDialogHelper::closeDialog);
return;
}

Binary file not shown.

View File

@ -0,0 +1,9 @@
# Test for strikethrough tag workaround
Line without strikethrough
Line ~~with~~ strikethrough
```
~~Strikethrough~~ should be <del>ignored</del> in a <s>code block</s>
```

Binary file not shown.

Binary file not shown.

View File

@ -164,6 +164,12 @@ void DocumentTest::testDiff_data()
QTest::addRow("unicode") << "☮🤌"
<< "☮🤌❤️"
<< "❤️";
QTest::addRow("unicode2") << ""
<< "☮🤌❤️"
<< "🤌❤️";
QTest::addRow("unicode3") << "🤍"
<< "🤌"
<< "🤌";
}
void DocumentTest::testDiff()

View File

@ -4,7 +4,7 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QtTest>
#include <QTest>
#include "../settings_core.h"
#include <QLocale>
@ -30,6 +30,8 @@ private Q_SLOTS:
void testFocusAction_data();
void testValidateAction();
void testValidateAction_data();
void testNumberFormat();
void testNumberFormat_data();
private:
Okular::Document *m_document;
@ -168,6 +170,36 @@ void FormatTest::testValidateAction_data()
QTest::newRow("invalid text was set") << QStringLiteral("abc") << QStringLiteral("invalid");
}
void FormatTest::testNumberFormat()
{
m_formattedText = QString();
QFETCH(QString, fieldName);
QFETCH(QString, text);
QFETCH(QString, result);
Okular::FormFieldText *fft = reinterpret_cast<Okular::FormFieldText *>(m_fields[fieldName]);
fft->setText(text);
m_document->processFormatAction(fft->additionalAction(Okular::FormField::FormatField), fft);
QCOMPARE(m_formattedText, result);
}
void FormatTest::testNumberFormat_data()
{
QTest::addColumn<QString>("fieldName");
QTest::addColumn<QString>("text");
QTest::addColumn<QString>("result");
QTest::newRow("EUR on left") << QStringLiteral("number1") << QStringLiteral("1.20") << QStringLiteral("€ 1.20");
QTest::newRow("EUR on left with comma") << QStringLiteral("number1") << QStringLiteral("1234.20") << QStringLiteral("€ 1,234.20");
QTest::newRow("EUR on right") << QStringLiteral("number2") << QStringLiteral("1.20") << QStringLiteral("1.20 €");
QTest::newRow("EUR on right without comma") << QStringLiteral("number2") << QStringLiteral("1234.20") << QStringLiteral("1234.20 €");
QTest::newRow("EUR on left using comma sep") << QStringLiteral("number3") << QStringLiteral("1,20") << QStringLiteral("€ 1,20");
QTest::newRow("EUR on left using comma sep and thousands with dot") << QStringLiteral("number3") << QStringLiteral("1234,20") << QStringLiteral("€ 1.234,20");
QTest::newRow("EUR on right with comma") << QStringLiteral("number4") << QStringLiteral("1,20") << /*true <<*/ QStringLiteral("1,20 €");
QTest::newRow("EUR on right with dot sep without thousands sep") << QStringLiteral("number4") << QStringLiteral("1234,20") << QStringLiteral("1234,20 €");
}
void FormatTest::cleanupTestCase()
{
m_document->closeDocument();

View File

@ -5,9 +5,9 @@
*/
#include <KPluginFactory>
#include <KPluginLoader>
#include <QDebug>
#include <QDirIterator>
#include <QLibrary>
#include <QStringList>
#include <QTest>
@ -40,11 +40,8 @@ void GeneratorsTest::testLoadsCorrectly()
}
int failures = 0;
int successful = 0;
for (const QString &lib : qAsConst(generatorLibs)) {
KPluginLoader loader(lib);
QVERIFY2(!loader.fileName().isEmpty(), qPrintable(lib));
qDebug() << loader.fileName();
auto factory = loader.factory();
for (const QString &lib : std::as_const(generatorLibs)) {
auto factory = KPluginFactory::loadFactory(KPluginMetaData(lib)).plugin;
if (!factory) {
qWarning() << "Could not get KPluginFactory for" << lib;
failures++;

View File

@ -4,7 +4,12 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QtTest>
#include <QAction>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMenu>
#include <QTest>
#include <QTimer>
#include "../settings_core.h"
#include "core/action.h"
@ -74,7 +79,7 @@ private:
bool m_checkBox;
};
class KJSFunctionsTest : public QObject
class JSFunctionsTest : public QObject
{
Q_OBJECT
@ -87,6 +92,8 @@ private Q_SLOTS:
void testGetOCGs();
void cleanupTestCase();
void testAlert();
void testPopUpMenu();
void testPopUpMenuEx();
void testPrintD();
void testPrintD_data();
@ -95,9 +102,9 @@ private:
QMap<QString, Okular::FormField *> m_fields;
};
void KJSFunctionsTest::initTestCase()
void JSFunctionsTest::initTestCase()
{
Okular::SettingsCore::instance(QStringLiteral("kjsfunctionstest"));
Okular::SettingsCore::instance(QStringLiteral("jsfunctionstest"));
m_document = new Okular::Document(nullptr);
const QString testFile = QStringLiteral(KDESRCDIR "data/kjsfunctionstest.pdf");
@ -112,7 +119,7 @@ void KJSFunctionsTest::initTestCase()
}
}
void KJSFunctionsTest::testNthFieldName()
void JSFunctionsTest::testNthFieldName()
{
for (int i = 0; i < 21; ++i) {
Okular::ScriptAction *action = new Okular::ScriptAction(Okular::JavaScript,
@ -126,7 +133,7 @@ void KJSFunctionsTest::testNthFieldName()
}
}
void KJSFunctionsTest::testDisplay()
void JSFunctionsTest::testDisplay()
{
Okular::ScriptAction *action = new Okular::ScriptAction(Okular::JavaScript, QStringLiteral("field = Doc.getField(\"0.0\");field.display=display.hidden;\
field = Doc.getField(\"0.10\");field.display=display.visible;"));
@ -173,7 +180,7 @@ void delay()
}
}
void KJSFunctionsTest::testSetClearInterval()
void JSFunctionsTest::testSetClearInterval()
{
Okular::ScriptAction *action = new Okular::ScriptAction(Okular::JavaScript, QStringLiteral("obj = new Object();obj.idx=0;\
obj.inc=function(){field = Doc.getField(Doc.getNthFieldName(obj.idx));\
@ -192,7 +199,7 @@ void KJSFunctionsTest::testSetClearInterval()
delete action;
}
void KJSFunctionsTest::testSetClearTimeOut()
void JSFunctionsTest::testSetClearTimeOut()
{
Okular::ScriptAction *action = new Okular::ScriptAction(Okular::JavaScript, QStringLiteral("intv = app.setTimeOut('obj.inc()', 1);obj.idx;"));
m_document->processAction(action);
@ -216,7 +223,7 @@ void KJSFunctionsTest::testSetClearTimeOut()
delete action;
}
void KJSFunctionsTest::testGetOCGs()
void JSFunctionsTest::testGetOCGs()
{
QAbstractItemModel *model = m_document->layersModel();
@ -262,7 +269,7 @@ void KJSFunctionsTest::testGetOCGs()
delete action;
}
void KJSFunctionsTest::testAlert()
void JSFunctionsTest::testAlert()
{
Okular::ScriptAction *action = new Okular::ScriptAction(Okular::JavaScript, QStringLiteral("ret = app.alert( \"Random Message\" );"));
QScopedPointer<MessageBoxHelper> messageBoxHelper;
@ -292,6 +299,146 @@ void KJSFunctionsTest::testAlert()
delete action;
}
class PopupMenuHelper : public QObject
{
Q_OBJECT
public:
PopupMenuHelper()
: m_menuFound(false)
{
QTimer::singleShot(0, this, &PopupMenuHelper::closeMenu);
}
~PopupMenuHelper() override
{
}
bool menuFound()
{
return m_menuFound;
}
const QJsonArray &menuTree()
{
return m_menuTree;
}
private Q_SLOTS:
void closeMenu()
{
const QWidgetList allToplevelWidgets = QApplication::topLevelWidgets();
QMenu *menu = nullptr;
for (QWidget *w : allToplevelWidgets) {
if (w->objectName() == QStringLiteral("popUpMenuEx") && w->inherits("QMenu")) {
menu = qobject_cast<QMenu *>(w);
// Generate an tree of string with all the menu trees
processQMenuToJS(menu, m_menuTree);
menu->close();
m_menuFound = true;
break;
}
}
if (!menu) {
QTimer::singleShot(0, this, &PopupMenuHelper::closeMenu);
}
}
private:
static void processQMenuToJS(QMenu *menu, QJsonArray &array)
{
const QList<QAction *> actions = menu->actions();
for (QAction *action : actions) {
QMenu *itMenu = action->menu();
if (itMenu != nullptr) {
QJsonArray subMenus;
subMenus.append(action->text());
processQMenuToJS(itMenu, subMenus);
array.append(subMenus);
} else {
array.append(action->text());
}
}
}
private:
bool m_menuFound;
QJsonArray m_menuTree;
};
void JSFunctionsTest::testPopUpMenu()
{
Okular::ScriptAction *action = new Okular::ScriptAction(Okular::JavaScript, QStringLiteral("ret = app.popUpMenu( [\"Fruits\",\"Apples\",\"Oranges\"], \"-\",\"Beans\",\"Corn\" );"));
QScopedPointer<PopupMenuHelper> popupMenuHelper;
popupMenuHelper.reset(new PopupMenuHelper());
m_document->processAction(action);
QJsonArray expected = {
QJsonArray {
QJsonValue {QStringLiteral("Fruits")},
QJsonValue {QStringLiteral("Apples")},
QJsonValue {QStringLiteral("Oranges")},
},
QJsonValue {QStringLiteral("-")},
QJsonValue {QStringLiteral("Beans")},
QJsonValue {QStringLiteral("Corn")},
};
QString expectedString = QString::fromUtf8(QJsonDocument(expected).toJson());
QString resultString = QString::fromUtf8(QJsonDocument(popupMenuHelper->menuTree()).toJson());
QString description = QStringLiteral("Expected:\n") + expectedString + QStringLiteral("But got:\n") + resultString;
QVERIFY2(expected == popupMenuHelper->menuTree(), description.toUtf8().constData());
delete action;
}
void JSFunctionsTest::testPopUpMenuEx()
{
QScopedPointer<PopupMenuHelper> popupMenuHelper;
Okular::ScriptAction *action = new Okular::ScriptAction(
Okular::JavaScript,
QStringLiteral("ret = app.popUpMenuEx( {cName:\"Fruits\", oSubMenu:[{cName:\"Apples\", bMarked:false},{cName:\"Oranges\", bMarked:true}]}, {cName:\"-\"},{cName:\"Beans\", bEnabled:false},{cName:\"Corn\", bEnabled:true} )"));
popupMenuHelper.reset(new PopupMenuHelper());
m_document->processAction(action);
QVERIFY(popupMenuHelper->menuFound());
QJsonArray expected = {
QJsonArray {
QJsonValue {QStringLiteral("Fruits")},
QJsonValue {QStringLiteral("Apples")},
QJsonValue {QStringLiteral("Oranges")},
},
QJsonValue {QStringLiteral("-")},
QJsonValue {QStringLiteral("Beans")},
QJsonValue {QStringLiteral("Corn")},
};
QString expectedString = QString::fromUtf8(QJsonDocument(expected).toJson());
QString resultString = QString::fromUtf8(QJsonDocument(popupMenuHelper->menuTree()).toJson());
QString description = QStringLiteral("Expected:\n") + expectedString + QStringLiteral("But got:\n") + resultString;
QVERIFY2(expected == popupMenuHelper->menuTree(), description.toUtf8().constData());
delete action;
// Test infinite recursion
action = new Okular::ScriptAction(Okular::JavaScript, QStringLiteral("\
var recursiveMenu = {\"cName\": \"Devil menu\"};\n\
recursiveMenu.oSubMenu = [ recursiveMenu ];\n\
ret = app.popUpMenuEx( recursiveMenu );"));
popupMenuHelper.reset(new PopupMenuHelper());
m_document->processAction(action);
QVERIFY(popupMenuHelper->menuFound());
// Must not crash
delete action;
}
/** @brief Checks a single JS action against an expected result
*
* Runs an action with the given @p script and checks that it
@ -312,7 +459,7 @@ private:
QScopedPointer<MessageBoxHelper> box;
};
void KJSFunctionsTest::testPrintD_data()
void JSFunctionsTest::testPrintD_data()
{
// Force consistent locale
QLocale locale(QStringLiteral("en_US"));
@ -333,10 +480,10 @@ void KJSFunctionsTest::testPrintD_data()
QTest::newRow("1") << QStringLiteral("ret = app.alert( util.printd( 1, date ) );") << QStringLiteral("2010.01.05 11:10:32");
QDate date(2010, 1, 5);
QTest::newRow("2") << QStringLiteral("ret = app.alert( util.printd( 2, date ) );") << QString(date.toString(locale.dateFormat(QLocale::ShortFormat)) + QStringLiteral(" 11:10:32 AM"));
QTest::newRow("2") << QStringLiteral("ret = app.alert( util.printd( 2, date ) );") << QString(date.toString(locale.dateFormat(QLocale::ShortFormat)) + QStringLiteral(" 11:10:32\u202FAM"));
}
void KJSFunctionsTest::testPrintD()
void JSFunctionsTest::testPrintD()
{
QFETCH(QString, script);
QFETCH(QString, result);
@ -345,11 +492,11 @@ void KJSFunctionsTest::testPrintD()
PrintDHelper test(m_document, script, result);
}
void KJSFunctionsTest::cleanupTestCase()
void JSFunctionsTest::cleanupTestCase()
{
m_document->closeDocument();
delete m_document;
}
QTEST_MAIN(KJSFunctionsTest)
#include "kjsfunctionstest.moc"
QTEST_MAIN(JSFunctionsTest)
#include "jsfunctionstest.moc"

View File

@ -4,7 +4,7 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QtTest>
#include <QTest>
#include "../settings_core.h"
#include <QLocale>

View File

@ -7,14 +7,22 @@
// clazy:excludeall=qstring-allocations
#include <QTest>
#include <config-okular.h>
#include <KConfigGroup>
#include <KLineEdit>
#include <KRecentFilesAction>
#if HAVE_DBUS
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#endif // HAVE_DBUS
#include <QPrintDialog>
#include <QStandardPaths>
#include <QTabBar>
#include <QTabWidget>
#include <QTemporaryFile>
#include <QTimer>
#include <qwidget.h>
#include "../core/document_p.h"
@ -137,16 +145,17 @@ void MainShellTest::initTestCase()
Okular::Settings::instance(QStringLiteral("mainshelltest"));
// Register in bus as okular
#if HAVE_DBUS
QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface();
const QString myPid = QString::number(getpid());
const QString serviceName = QStringLiteral("org.kde.okular-") + myPid;
QVERIFY(bus->registerService(serviceName) == QDBusConnectionInterface::ServiceRegistered);
#endif
// Tell the presentationWidget and queryClose to not be annoying
KSharedConfigPtr c = KSharedConfig::openConfig();
KConfigGroup cg = c->group("Notification Messages");
cg.writeEntry("presentationInfo", false);
cg.writeEntry("ShowTabWarning", false);
KSharedConfigPtr c = KSharedConfig::openConfig(QStringLiteral("mainshelltest.kmessagebox"));
KConfigGroup cg = c->group(QStringLiteral("General"));
cg.writeEntry("presentationInfo", 4);
cg.writeEntry("ShowTabWarning", 4);
}
void MainShellTest::cleanupTestCase()
@ -211,11 +220,14 @@ void MainShellTest::testShell_data()
QTest::newRow("two files no tabs") << file1AndToc << QString() << false << QString() << 0u << false << false << false << 0u << false << false << QString();
QTest::newRow("two files with tabs") << file1AndToc << QString() << true << QString() << 0u << false << false << false << 0u << false << false << QString();
QTest::newRow("two files sequence no tabs") << file1 << QString() << false << tocReload << 0u << false << false << false << 0u << false << false << QString();
#if HAVE_DBUS
QTest::newRow("two files sequence with tabs") << file1 << QString() << true << tocReload << 0u << false << false << false << 0u << false << false << QString();
#endif // HAVE_DBUS
QTest::newRow("open file page number") << contentsEpub << optionsPage2 << false << QString() << 1u << false << false << false << 0u << false << false << QString();
QTest::newRow("open file page number and presentation") << contentsEpub << optionsPage2Presentation << false << QString() << 1u << true << false << false << 0u << false << false << QString();
QTest::newRow("open file find") << file1 << optionsFind << false << QString() << 0u << false << false << false << 0u << false << false << QStringLiteral("si:next-testing parameters!");
QTest::newRow("open file print") << file1 << optionsPrint << false << QString() << 0u << false << true << false << 0u << false << false << QString();
#if HAVE_DBUS
QTest::newRow("open two files unique") << file1 << optionsUnique << false << tocReload << 0u << false << false << true << 0u << false << false << QString();
QTest::newRow("open two files unique tabs") << file1 << optionsUnique << true << tocReload << 0u << false << false << true << 0u << false << false << QString();
QTest::newRow("page number attach tabs") << file1 << QString() << true << contentsEpub[0] << 0u << false << false << false << 2u << false << false << QString();
@ -227,6 +239,7 @@ void MainShellTest::testShell_data()
QTest::newRow("page number attach unique tabs") << file1 << optionsUnique << true << contentsEpub[0] << 0u << false << false << true << 3u << false << false << QString();
QTest::newRow("presentation attach unique tabs") << file1 << optionsUnique << true << contentsEpub[0] << 0u << false << false << true << 2u << true << false << QString();
QTest::newRow("print attach unique tabs") << file1 << optionsUnique << true << contentsEpub[0] << 0u << false << false << true << 2u << false << true << QString();
#endif // HAVE_DBUS
}
void MainShellTest::testShell()
@ -300,7 +313,7 @@ void MainShellTest::testShell()
QCOMPARE(partDocument(part2)->currentPage(), expectedPage);
openUrls << part2->url().url();
for (const QString &path : qAsConst(paths)) {
for (const QString &path : std::as_const(paths)) {
QVERIFY(openUrls.contains(QStringLiteral("file://%1").arg(path)));
}
}
@ -469,8 +482,10 @@ void MainShellTest::test2FilesError_data()
QTest::newRow("startInPresentation") << ShellUtils::serializeOptions(true, false, false, false, false, QString(), QString(), QString());
QTest::newRow("showPrintDialog") << ShellUtils::serializeOptions(false, true, false, false, false, QString(), QString(), QString());
#if HAVE_DBUS
QTest::newRow("unique") << ShellUtils::serializeOptions(false, false, false, true, false, QString(), QString(), QString());
QTest::newRow("pageNumber") << ShellUtils::serializeOptions(false, false, false, false, false, QStringLiteral("3"), QString(), QString());
#endif // HAVE_DBUS
QTest::newRow("find") << ShellUtils::serializeOptions(false, false, false, false, false, QString(), QStringLiteral("silly"), QString());
}
@ -524,7 +539,7 @@ void MainShellTest::testSessionRestore()
QList<Shell *> shells = getShells();
QVERIFY(!shells.isEmpty());
int numDocs = 0;
for (Shell *shell : qAsConst(shells)) {
for (Shell *shell : std::as_const(shells)) {
QVERIFY(QTest::qWaitForWindowExposed(shell));
numDocs += shell->m_tabs.size();
}
@ -544,7 +559,7 @@ void MainShellTest::testSessionRestore()
int numWindows = 0;
{ // Scope for config so that we can reconstruct from file
KConfig config(configFile.fileName(), KConfig::SimpleConfig);
for (Shell *shell : qAsConst(shells)) {
for (Shell *shell : std::as_const(shells)) {
shell->savePropertiesInternal(&config, ++numWindows);
// Windows aren't necessarily closed on shutdown, but we'll use
// this as a way to trigger the destructor code, which is normally
@ -558,8 +573,10 @@ void MainShellTest::testSessionRestore()
QEventLoop eventLoop;
QTimer::singleShot(100, &eventLoop, &QEventLoop::quit);
eventLoop.exec(QEventLoop::AllEvents);
// Sometimes the event loop is not enough, so try a bit more to get deferred delete happen
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
shells = getShells();
QVERIFY(shells.isEmpty());
QCOMPARE(shells.size(), 0);
Okular::Settings::self()->setShellOpenFileInTabs(useTabsRestore);
@ -577,7 +594,7 @@ void MainShellTest::testSessionRestore()
shells = getShells();
QVERIFY(!shells.isEmpty());
numDocs = 0;
for (Shell *shell : qAsConst(shells)) {
for (Shell *shell : std::as_const(shells)) {
QVERIFY(QTest::qWaitForWindowExposed(shell));
numDocs += shell->m_tabs.size();
}

View File

@ -12,6 +12,7 @@
#include <QMimeDatabase>
#include <QMimeType>
#include <QTextDocument>
#include <memory>
class MarkdownTest : public QObject
{
@ -24,6 +25,8 @@ private Q_SLOTS:
void testFancyPantsDisabled();
void testImageSizes();
void testSpecialCharsInImageFileName();
void testStrikeThrough();
void testHtmlTagFixup();
private:
void findImages(QTextFrame *parent, QVector<QTextImageFormat> &images);
@ -39,7 +42,7 @@ void MarkdownTest::testFancyPantsEnabled()
{
Markdown::Converter converter;
converter.setFancyPantsEnabled(true);
QTextDocument *document = converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md"));
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md")));
QTextFrame::iterator secondFrame = ++(document->rootFrame()->begin());
QVERIFY(secondFrame.currentBlock().text().startsWith(QStringLiteral("©")));
@ -49,7 +52,7 @@ void MarkdownTest::testFancyPantsDisabled()
{
Markdown::Converter converter;
converter.setFancyPantsEnabled(false);
QTextDocument *document = converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md"));
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md")));
QTextFrame::iterator secondFrame = ++(document->rootFrame()->begin());
QVERIFY(secondFrame.currentBlock().text().startsWith(QStringLiteral("(c)")));
@ -58,7 +61,7 @@ void MarkdownTest::testFancyPantsDisabled()
void MarkdownTest::testImageSizes()
{
Markdown::Converter converter;
QTextDocument *document = converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md"));
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md")));
QTextFrame *parent = document->rootFrame();
@ -125,7 +128,7 @@ void MarkdownTest::findImages(const QTextBlock &parent, QVector<QTextImageFormat
void MarkdownTest::testSpecialCharsInImageFileName()
{
Markdown::Converter converter;
QTextDocument *document = converter.convert(QStringLiteral(KDESRCDIR "data/imageUrlsWithSpecialChars.md"));
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/imageUrlsWithSpecialChars.md")));
QTextFrame *parent = document->rootFrame();
@ -137,5 +140,87 @@ void MarkdownTest::testSpecialCharsInImageFileName()
QVERIFY(!images[0].name().contains(QStringLiteral("kart%C3%B6ffelchen.jpg")));
}
void MarkdownTest::testStrikeThrough()
{
Markdown::Converter converter;
converter.setFancyPantsEnabled(true);
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/strikethrough.md")));
const QTextFrame *rootFrame = document->rootFrame();
auto frameIter = rootFrame->begin();
// Header line.
QCOMPARE_NE(frameIter, rootFrame->end());
QCOMPARE(frameIter.currentBlock().text(), QStringLiteral("Test for strikethrough tag workaround"));
// Ordinary line.
{
++frameIter;
QCOMPARE_NE(frameIter, rootFrame->end());
auto block = frameIter.currentBlock();
QCOMPARE(block.text(), QStringLiteral("Line without strikethrough"));
// Single format for the entire line.
auto formats = block.textFormats();
QCOMPARE(formats.size(), 1);
QVERIFY(!formats[0].format.fontStrikeOut());
}
// Part of the line has a strikethrough.
{
++frameIter;
QCOMPARE_NE(frameIter, rootFrame->end());
auto block = frameIter.currentBlock();
QCOMPARE(block.text(), QStringLiteral("Line with strikethrough"));
// The "with" should be the only thing striked out.
auto formats = block.textFormats();
QCOMPARE(formats.size(), 3);
QCOMPARE(block.text().sliced(formats[0].start, formats[0].length), QStringLiteral("Line "));
QVERIFY(!formats[0].format.fontStrikeOut());
QCOMPARE(block.text().sliced(formats[1].start, formats[1].length), QStringLiteral("with"));
QVERIFY(formats[1].format.fontStrikeOut());
QCOMPARE(block.text().sliced(formats[2].start, formats[2].length), QStringLiteral(" strikethrough"));
QVERIFY(!formats[2].format.fontStrikeOut());
}
// Code block shouldn't have leading spaces, or be modified by our fixup.
{
++frameIter;
QCOMPARE_NE(frameIter, rootFrame->end());
auto block = frameIter.currentBlock();
QCOMPARE(block.text(), QStringLiteral("~~Strikethrough~~ should be <del>ignored</del> in a <s>code block</s>"));
}
}
void MarkdownTest::testHtmlTagFixup()
{
const QString wrapperTag = QStringLiteral("ignored_by_qt");
const QString wrapperTagBegin = QStringLiteral("<ignored_by_qt>");
const QString wrapperTagEnd = QStringLiteral("</ignored_by_qt>");
// These should passthrough unchanged.
const QString testCases[] = {
QStringLiteral("basic test"),
QStringLiteral("<p>test with tag</p>"),
QStringLiteral("line with <em>combined <strong>tags</strong></em>"),
};
for (const QString &inputHtml : testCases) {
const QString outputHtml = Markdown::detail::fixupHtmlTags(QString(inputHtml));
QCOMPARE(outputHtml, wrapperTagBegin + inputHtml + wrapperTagEnd);
}
// <del> should become <s>.
{
const QString outputHtml = Markdown::detail::fixupHtmlTags(QStringLiteral("<del>test</del>"));
QCOMPARE(outputHtml, wrapperTagBegin + QStringLiteral("<s>test</s>") + wrapperTagEnd);
}
// Check that the wrapper is ignored by Qt.
{
QTextDocument dom;
dom.setHtml(wrapperTagBegin + QStringLiteral("basic test") + wrapperTagEnd);
QVERIFY(!dom.toHtml().contains(wrapperTag));
}
}
QTEST_MAIN(MarkdownTest)
#include "markdowntest.moc"

View File

@ -9,7 +9,8 @@
// clazy:excludeall=qstring-allocations
#include <QtTest>
#include <QSignalSpy>
#include <QTest>
#include "../core/annotations.h"
#include "../core/document_p.h"
@ -31,13 +32,16 @@
#include <QApplication>
#include <QClipboard>
#include <QDesktopServices>
#include <QLineEdit>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QScrollBar>
#include <QTabletEvent>
#include <QTemporaryDir>
#include <QTemporaryFile>
#include <QTextEdit>
#include <QTimer>
#include <QToolBar>
#include <QTreeView>
#include <QUrl>
@ -64,6 +68,7 @@ private Q_SLOTS:
void testForwardPDF_data();
void testGeneratorPreferences();
void testSelectText();
void testSelectTextMultiline();
void testClickInternalLink();
void testScrollBarAndMouseWheel();
void testOpenUrlArguments();
@ -103,6 +108,7 @@ private Q_SLOTS:
void testFullScreenRequest();
void testZoomInFacingPages();
void testLinkWithCrop();
void testFieldFormatting();
private:
void simulateMouseSelection(double startX, double startY, double endX, double endY, QWidget *target);
@ -112,8 +118,8 @@ class PartThatHijacksQueryClose : public Okular::Part
{
Q_OBJECT
public:
PartThatHijacksQueryClose(QWidget *parentWidget, QObject *parent, const QVariantList &args)
: Okular::Part(parentWidget, parent, args)
PartThatHijacksQueryClose(QObject *parent, const QVariantList &args)
: Okular::Part(parent, args)
, behavior(PassThru)
{
}
@ -168,7 +174,7 @@ void PartTest::init()
void PartTest::testReload()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file1.pdf")));
part.reload();
qApp->processEvents();
@ -178,7 +184,7 @@ void PartTest::testReload()
void PartTest::testCanceledReload()
{
QVariantList dummyArgs;
PartThatHijacksQueryClose part(nullptr, nullptr, dummyArgs);
PartThatHijacksQueryClose part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file1.pdf")));
// When queryClose() returns false, the reload operation is canceled (as if
@ -193,7 +199,7 @@ void PartTest::testCanceledReload()
void PartTest::testTOCReload()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/tocreload.pdf")));
QCOMPARE(part.m_toc->expandedNodes().count(), 0);
part.m_toc->m_treeView->expandAll();
@ -208,42 +214,17 @@ void PartTest::testForwardPDF()
QFETCH(QString, dir);
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
// Create temp dir named like this: ${system temp dir}/${random string}/${dir}
const QTemporaryDir tempDir;
const QDir workDir(QDir(tempDir.path()).filePath(dir));
workDir.mkpath(QStringLiteral("."));
QFile f(QStringLiteral(KDESRCDIR "data/synctextest.tex"));
const QString texDestination = workDir.path() + QStringLiteral("/synctextest.tex");
QVERIFY(f.copy(texDestination));
QProcess process;
process.setWorkingDirectory(workDir.path());
const QString pdflatexPath(QStandardPaths::findExecutable(QStringLiteral("pdflatex")));
if (pdflatexPath.isEmpty()) {
QFAIL("pdflatex executable not found, but needed for the test. Try installing the respective TeXLive packages.");
}
process.start(pdflatexPath, QStringList() << QStringLiteral("-synctex=1") << QStringLiteral("-interaction=nonstopmode") << texDestination);
bool started = process.waitForStarted();
if (!started) {
qDebug() << "start error:" << process.error();
qDebug() << "start stdout:" << process.readAllStandardOutput();
qDebug() << "start stderr:" << process.readAllStandardError();
}
QVERIFY(started);
process.waitForFinished();
if (process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) {
qDebug() << "exit error:" << process.error() << "status" << process.exitStatus() << "code" << process.exitCode();
qDebug() << "exit stdout:" << process.readAllStandardOutput();
qDebug() << "exit stderr:" << process.readAllStandardError();
}
const QString pdfResult = workDir.path() + QStringLiteral("/synctextest.pdf");
QVERIFY(QFile::exists(pdfResult));
QVERIFY(QFile::copy(QStringLiteral(KDESRCDIR "data/synctextest.pdf"), pdfResult));
const QString gzDestination = workDir.path() + QStringLiteral("/synctextest.synctex.gz");
QVERIFY(QFile::copy(QStringLiteral(KDESRCDIR "data/synctextest.synctex.gz"), gzDestination));
QVERIFY(openDocument(&part, pdfResult));
part.m_document->setViewportPage(0);
@ -251,7 +232,8 @@ void PartTest::testForwardPDF()
part.closeUrl();
QUrl u(QUrl::fromLocalFile(pdfResult));
u.setFragment(QStringLiteral("src:100") + texDestination);
// Update this if you regenerate the synctextest.pdf somewhere else
u.setFragment(QStringLiteral("src:100/home/tsdgeos/devel/kde/okular/autotests/data/synctextest.tex"));
part.openUrl(u);
QCOMPARE(part.m_document->currentPage(), 1u);
}
@ -269,7 +251,7 @@ void PartTest::testGeneratorPreferences()
{
KConfigDialog *dialog;
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
// Test that we don't crash while opening the dialog
dialog = part.slotGeneratorPreferences();
@ -286,7 +268,7 @@ void PartTest::testGeneratorPreferences()
void PartTest::testSelectText()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file2.pdf")));
part.widget()->show();
QVERIFY(QTest::qWaitForWindowExposed(part.widget()));
@ -313,10 +295,71 @@ void PartTest::testSelectText()
QCOMPARE(QApplication::clipboard()->text(), QStringLiteral("Hola que tal"));
}
void PartTest::testSelectTextMultiline()
{
// This test tests a specific variation of multiline selection
// Select from middle to end of line, then continue to select next line
// then move selection back on next line past the point of the first line
// https://bugs.kde.org/show_bug.cgi?id=482249 has a nice animation.
QVariantList dummyArgs;
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file2.pdf")));
part.widget()->show();
QVERIFY(QTest::qWaitForWindowExposed(part.widget()));
part.m_document->setViewportPage(1);
// wait for pixmap
QTRY_VERIFY(part.m_document->page(0)->hasPixmap(part.m_pageView));
const int width = part.m_pageView->horizontalScrollBar()->maximum() + part.m_pageView->viewport()->width();
const int height = part.m_pageView->verticalScrollBar()->maximum() + part.m_pageView->viewport()->height();
QVERIFY(QMetaObject::invokeMethod(part.m_pageView, "slotSetMouseTextSelect"));
const int startY = height * 0.052;
const int startX = width * 0.22;
const int endY = height * 0.072;
const int endX = width * 0.6;
const int steps = 5;
const double diffX = endX - startX;
const double diffXStep = diffX / steps;
QTestEventList events;
events.addMouseMove(QPoint(startX, startY));
events.addMousePress(Qt::LeftButton, Qt::NoModifier, QPoint(startX, startY));
for (int i = 0; i < steps - 1; ++i) {
events.addMouseMove(QPoint(startX + i * diffXStep, startY));
events.addDelay(100);
}
events.addMouseMove(QPoint(endX, startY));
events.addDelay(100);
events.addMouseMove(QPoint(endX, endY));
events.addDelay(100);
for (int i = 0; i < (steps); i++) {
events.addMouseMove(QPoint(endX - (i * diffXStep), endY));
events.addDelay(100);
}
events.addMouseMove(QPoint(endX - (diffXStep * (steps)), endY));
events.addDelay(100);
events.addMouseMove(QPoint(endX - (diffXStep * (steps + 0.5)), endY));
events.addDelay(100);
events.addMouseRelease(Qt::LeftButton, Qt::NoModifier, QPoint(endX - (diffXStep * (steps + 0.5)), endY));
events.simulate(part.m_pageView->viewport());
QApplication::clipboard()->clear();
QVERIFY(QMetaObject::invokeMethod(part.m_pageView, "copyTextSelection"));
QCOMPARE(QApplication::clipboard()->text(), QStringLiteral("cks!\nOf c"));
}
void PartTest::testClickInternalLink()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file2.pdf")));
part.widget()->show();
QVERIFY(QTest::qWaitForWindowExposed(part.widget()));
@ -329,7 +372,7 @@ void PartTest::testClickInternalLink()
const int width = part.m_pageView->horizontalScrollBar()->maximum() + part.m_pageView->viewport()->width();
const int height = part.m_pageView->verticalScrollBar()->maximum() + part.m_pageView->viewport()->height();
QMetaObject::invokeMethod(part.m_pageView, "slotMouseNormalToggled", Q_ARG(bool, true));
QVERIFY(QMetaObject::invokeMethod(part.m_pageView, "slotSetMouseNormal"));
QCOMPARE(part.m_document->currentPage(), 0u);
QTest::mouseMove(part.m_pageView->viewport(), QPoint(width * 0.17, height * 0.05));
@ -346,7 +389,7 @@ void PartTest::testClickInternalLink()
void PartTest::testScrollBarAndMouseWheel()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/simple-multipage.pdf")));
part.widget()->show();
QVERIFY(QTest::qWaitForWindowExposed(part.widget()));
@ -384,7 +427,7 @@ void PartTest::testScrollBarAndMouseWheel()
void PartTest::testMouseMoveOverLinkWhileInSelectionMode()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_links.pdf")));
// resize window to avoid problem with selection areas
part.widget()->resize(800, 600);
@ -413,7 +456,7 @@ void PartTest::testMouseMoveOverLinkWhileInSelectionMode()
void PartTest::testClickUrlLinkWhileInSelectionMode()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_links.pdf")));
// resize window to avoid problem with selection areas
part.widget()->resize(800, 600);
@ -463,7 +506,7 @@ void PartTest::testeTextSelectionOverAndAcrossLinks_data()
void PartTest::testeTextSelectionOverAndAcrossLinks()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_links.pdf")));
// resize window to avoid problem with selection areas
part.widget()->resize(800, 600);
@ -501,7 +544,7 @@ void PartTest::testeTextSelectionOverAndAcrossLinks()
void PartTest::testClickUrlLinkWhileLinkTextIsSelected()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_links.pdf")));
// resize window to avoid problem with selection areas
part.widget()->resize(800, 600);
@ -546,7 +589,7 @@ void PartTest::testClickUrlLinkWhileLinkTextIsSelected()
void PartTest::testRClickWhileLinkTextIsSelected()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_links.pdf")));
// resize window to avoid problem with selection areas
part.widget()->resize(800, 600);
@ -612,7 +655,7 @@ void PartTest::testRClickWhileLinkTextIsSelected()
void PartTest::testRClickOverLinkWhileLinkTextIsSelected()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_links.pdf")));
// resize window to avoid problem with selection areas
part.widget()->resize(800, 600);
@ -673,7 +716,7 @@ void PartTest::testRClickOverLinkWhileLinkTextIsSelected()
void PartTest::testRClickOnSelectionModeShoulShowFollowTheLinkMenu()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_links.pdf")));
// resize window to avoid problem with selection areas
part.widget()->resize(800, 600);
@ -728,7 +771,7 @@ void PartTest::testRClickOnSelectionModeShoulShowFollowTheLinkMenu()
void PartTest::testClickAnywhereAfterSelectionShouldUnselect()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_links.pdf")));
// resize window to avoid problem with selection areas
part.widget()->resize(800, 600);
@ -768,7 +811,7 @@ void PartTest::testClickAnywhereAfterSelectionShouldUnselect()
void PartTest::testeRectSelectionStartingOnLinks()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_links.pdf")));
// hide info messages as they interfere with selection area
Okular::Settings::self()->setShowEmbeddedContentMessages(false);
@ -843,7 +886,7 @@ void PartTest::simulateMouseSelection(double startX, double startY, double endX,
void PartTest::testSaveAsToNonExistingPath()
{
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(QStringLiteral(KDESRCDIR "data/file1.pdf"));
QString saveFilePath;
@ -864,7 +907,7 @@ void PartTest::testSaveAsToNonExistingPath()
void PartTest::testSaveAsToSymlink()
{
#ifdef Q_OS_UNIX
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(QStringLiteral(KDESRCDIR "data/file1.pdf"));
QTemporaryFile newFile(QStringLiteral("%1/okrXXXXXX.pdf").arg(QDir::tempPath()));
@ -893,7 +936,7 @@ void PartTest::testSaveAsToSymlink()
void PartTest::testSaveIsSymlink()
{
#ifdef Q_OS_UNIX
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
QString newFilePath;
{
@ -949,7 +992,7 @@ void PartTest::testSaveAs()
qDebug() << "Open file, add annotation and save both natively and to .okular";
{
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(file);
part.m_document->documentInfo();
@ -989,7 +1032,7 @@ void PartTest::testSaveAs()
qDebug() << "Open the .okular, check that the annotation is present and save to native";
{
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(archiveSave.fileName());
part.m_document->documentInfo();
@ -1015,7 +1058,7 @@ void PartTest::testSaveAs()
qDebug() << "Open the native file saved directly, and check that the annot"
<< "is there iff we expect it";
{
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(nativeDirectSave.fileName());
QCOMPARE(part.m_document->page(0)->annotations().size(), nativelySupportsAnnotations ? 1 : 0);
@ -1029,7 +1072,7 @@ void PartTest::testSaveAs()
qDebug() << "Open the native file saved from the .okular, and check that the annot"
<< "is there iff we expect it";
{
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(nativeFromArchiveFile.fileName());
QCOMPARE(part.m_document->page(0)->annotations().size(), nativelySupportsAnnotations ? 1 : 0);
@ -1060,8 +1103,7 @@ void PartTest::testSaveAs_data()
void PartTest::testSidebarItemAfterSaving()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, {});
QWidget *currentSidebarItem = part.m_sidebar->currentItem(); // thumbnails
openDocument(&part, QStringLiteral(KDESRCDIR "data/tocreload.pdf"));
// since it has TOC it changes to TOC
@ -1077,8 +1119,7 @@ void PartTest::testSidebarItemAfterSaving()
void PartTest::testViewModeSavingPerFile()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, {});
// Open some file
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file1.pdf")));
@ -1128,7 +1169,7 @@ void PartTest::testSaveAsUndoStackAnnotations()
QVERIFY(saveFile2.open());
saveFile2.close();
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(file);
QCOMPARE(part.m_document->canSwapBackingFile(), canSwapBackingFile);
@ -1312,7 +1353,7 @@ void PartTest::testSaveAsUndoStackForms()
QVERIFY(saveFile.open());
saveFile.close();
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(file);
const QList<Okular::FormField *> pageFormFields = part.m_document->page(0)->formFields();
@ -1389,8 +1430,7 @@ void PartTest::testSaveAsUndoStackForms_data()
void PartTest::testOpenUrlArguments()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, {});
KParts::OpenUrlArguments args;
args.setMimeType(QStringLiteral("text/rtf"));
@ -1404,14 +1444,14 @@ void PartTest::testOpenUrlArguments()
void PartTest::test388288()
{
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openUrl(QUrl::fromLocalFile(QStringLiteral(KDESRCDIR "data/file1.pdf")));
part.widget()->show();
QVERIFY(QTest::qWaitForWindowExposed(part.widget()));
QMetaObject::invokeMethod(part.m_pageView, "slotMouseNormalToggled", Q_ARG(bool, true));
QVERIFY(QMetaObject::invokeMethod(part.m_pageView, "slotSetMouseNormal"));
auto annot = new Okular::HighlightAnnotation();
annot->setHighlightType(Okular::HighlightAnnotation::Highlight);
@ -1456,7 +1496,7 @@ void PartTest::test388288()
void PartTest::testCheckBoxReadOnly()
{
const QString testFile = QStringLiteral(KDESRCDIR "data/checkbox_ro.pdf");
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(testFile);
// The test document uses the activation action of checkboxes
@ -1569,7 +1609,7 @@ void PartTest::testCheckBoxReadOnly()
void PartTest::testCrashTextEditDestroy()
{
const QString testFile = QStringLiteral(KDESRCDIR "data/formSamples.pdf");
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, {});
part.openDocument(testFile);
part.widget()->show();
QVERIFY(QTest::qWaitForWindowExposed(part.widget()));
@ -1580,8 +1620,7 @@ void PartTest::testCrashTextEditDestroy()
void PartTest::testAnnotWindow()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, {});
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file1.pdf")));
part.widget()->show();
part.widget()->resize(800, 600);
@ -1589,7 +1628,7 @@ void PartTest::testAnnotWindow()
part.m_document->setViewportPage(0);
QMetaObject::invokeMethod(part.m_pageView, "slotMouseNormalToggled", Q_ARG(bool, true));
QVERIFY(QMetaObject::invokeMethod(part.m_pageView, "slotSetMouseNormal"));
QCOMPARE(part.m_document->currentPage(), 0u);
@ -1682,7 +1721,7 @@ static void verifyTargetStates(const QString &triggerName, const QMap<QString, O
void PartTest::testAdditionalActionTriggers()
{
const QString testFile = QStringLiteral(KDESRCDIR "data/additionalFormActions.pdf");
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, QVariantList());
part.openDocument(testFile);
part.widget()->resize(800, 600);
@ -1793,7 +1832,7 @@ void PartTest::testAdditionalActionTriggers()
void PartTest::testTypewriterAnnotTool()
{
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, QVariantList());
part.openUrl(QUrl::fromLocalFile(QStringLiteral(KDESRCDIR "data/file1.pdf")));
@ -1830,7 +1869,7 @@ void PartTest::testJumpToPage()
{
const QString testFile = QStringLiteral(KDESRCDIR "data/simple-multipage.pdf");
const int targetPage = 25;
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, QVariantList());
part.openDocument(testFile);
part.widget()->resize(800, 600);
part.widget()->show();
@ -1860,7 +1899,7 @@ void PartTest::testOpenAtPage()
{
const QString testFile = QStringLiteral(KDESRCDIR "data/simple-multipage.pdf");
QUrl url = QUrl::fromLocalFile(testFile);
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, QVariantList());
const uint targetPageNumA = 25;
const uint expectedPageA = targetPageNumA - 1;
@ -1879,7 +1918,7 @@ void PartTest::testOpenAtPage()
void PartTest::testForwardBackwardNavigation()
{
const QString testFile = QStringLiteral(KDESRCDIR "data/simple-multipage.pdf");
Okular::Part part(nullptr, nullptr, QVariantList());
Okular::Part part(nullptr, QVariantList());
part.openDocument(testFile);
part.widget()->resize(800, 600);
part.widget()->show();
@ -1908,7 +1947,7 @@ void PartTest::testForwardBackwardNavigation()
void PartTest::testTabletProximityBehavior()
{
QVariantList dummyArgs;
Okular::Part part {nullptr, nullptr, dummyArgs};
Okular::Part part {nullptr, dummyArgs};
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file1.pdf")));
part.slotShowPresentation();
PresentationWidget *w = part.m_presentationWidget;
@ -1918,8 +1957,9 @@ void PartTest::testTabletProximityBehavior()
// close the KMessageBox "There are two ways of exiting[...]"
TestingUtils::CloseDialogHelper closeDialogHelper(w, QDialogButtonBox::Ok); // confirm the "To leave, press ESC"
QTabletEvent enterProximityEvent {QEvent::TabletEnterProximity, QPoint(10, 10), QPoint(10, 10), QTabletEvent::Stylus, QTabletEvent::Pen, 1., 0, 0, 1., 1., 0, Qt::NoModifier, 0, Qt::NoButton, Qt::NoButton};
QTabletEvent leaveProximityEvent {QEvent::TabletLeaveProximity, QPoint(10, 10), QPoint(10, 10), QTabletEvent::Stylus, QTabletEvent::Pen, 1., 0, 0, 1., 1., 0, Qt::NoModifier, 0, Qt::NoButton, Qt::NoButton};
auto pointingDevice = new QPointingDevice(QStringLiteral("test"), 42, QInputDevice::DeviceType::Stylus, QPointingDevice::PointerType::Pen, QInputDevice::Capability::All, 3, 3);
QTabletEvent enterProximityEvent {QEvent::TabletEnterProximity, pointingDevice, QPointF(10, 10), QPointF(10, 10), 1., 0, 0, 1., 1., 0, Qt::NoModifier, Qt::NoButton, Qt::NoButton};
QTabletEvent leaveProximityEvent {QEvent::TabletLeaveProximity, pointingDevice, QPointF(10, 10), QPointF(10, 10), 1., 0, 0, 1., 1., 0, Qt::NoModifier, Qt::NoButton, Qt::NoButton};
// Test with the Okular::Settings::EnumSlidesCursor::Visible setting
Okular::Settings::self()->setSlidesCursor(Okular::Settings::EnumSlidesCursor::Visible);
@ -1968,7 +2008,7 @@ void PartTest::testTabletProximityBehavior()
void PartTest::testOpenPrintPreview()
{
QVariantList dummyArgs;
Okular::Part part {nullptr, nullptr, dummyArgs};
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file1.pdf")));
part.widget()->show();
QVERIFY(QTest::qWaitForWindowExposed(part.widget()));
@ -1979,7 +2019,7 @@ void PartTest::testOpenPrintPreview()
void PartTest::testMouseModeMenu()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file1.pdf")));
QMetaObject::invokeMethod(part.m_pageView, "slotSetMouseNormal");
@ -2010,7 +2050,7 @@ void PartTest::testMouseModeMenu()
void PartTest::testFullScreenRequest()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
// Open file. For this particular file, a dialog has to appear asking whether
// one wants to comply with the wish to go to presentation mode directly.
@ -2037,7 +2077,7 @@ void PartTest::testFullScreenRequest()
void PartTest::testZoomInFacingPages()
{
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file2.pdf")));
QAction *facingAction = part.m_pageView->findChild<QAction *>(QStringLiteral("view_render_mode_facing"));
KSelectAction *zoomSelectAction = part.m_pageView->findChild<KSelectAction *>(QStringLiteral("zoom_to"));
@ -2065,7 +2105,7 @@ void PartTest::testZoomWithCrop()
// We test that all zoom levels can be achieved with cropped pages, bug 342003
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/file2.pdf")));
KActionMenu *cropMenu = part.m_pageView->findChild<KActionMenu *>(QStringLiteral("view_trim_mode"));
@ -2130,7 +2170,7 @@ void PartTest::testLinkWithCrop()
// We test that link targets are correct with cropping, related to bug 198427
QVariantList dummyArgs;
Okular::Part part(nullptr, nullptr, dummyArgs);
Okular::Part part(nullptr, dummyArgs);
QVERIFY(openDocument(&part, QStringLiteral(KDESRCDIR "data/pdf_with_internal_links.pdf")));
KActionMenu *cropMenu = part.m_pageView->findChild<KActionMenu *>(QStringLiteral("view_trim_mode"));
@ -2166,6 +2206,10 @@ void PartTest::testLinkWithCrop()
// Trim the page
simulateMouseSelection(mouseStartX, mouseStartY, mouseEndX, mouseEndY, part.m_pageView->viewport());
// We seem to have a trimmed view where we just by sheer luck ends up with mouse over a link at least sometimes
// So move the mouse
QTest::mouseMove(part.m_pageView->viewport(), QPoint(width * 0.1, width * 0.1));
// The cursor should be normal again
QTRY_COMPARE(part.m_pageView->cursor().shape(), Qt::CursorShape(Qt::OpenHandCursor));
@ -2180,6 +2224,122 @@ void PartTest::testLinkWithCrop()
cropAction->trigger();
}
void PartTest::testFieldFormatting()
{
// Test field formatting. This has to be a parttest so that we
// can properly test focus in / out which triggers formatting.
const QString testFile = QStringLiteral(KDESRCDIR "data/fieldFormat.pdf");
Okular::Part part(nullptr, QVariantList());
part.openDocument(testFile);
part.widget()->resize(800, 600);
part.widget()->show();
QVERIFY(QTest::qWaitForWindowExposed(part.widget()));
// Field names in test document are:
//
// us_currency_fmt for formatting like "$ 1,234.56"
// de_currency_fmt for formatting like "1.234,56 €"
// de_simple_sum for calculation test and formatting like "1.234,56€"
// date_mm_dd_yyyy for dates like "18/06/2018"
// date_dd_mm_yyyy for dates like "06/18/2018"
// percent_fmt for percent format like "100,00%" if you enter 1
// time_HH_MM_fmt for times like "23:12"
// time_HH_MM_ss_fmt for times like "23:12:34"
// special_phone_number for an example of a special format selectable in Acrobat.
QMap<QString, Okular::FormField *> fields;
const Okular::Page *page = part.m_document->page(0);
const auto formFields = page->formFields();
for (Okular::FormField *ff : formFields) {
fields.insert(ff->name(), static_cast<Okular::FormField *>(ff));
}
const int width = part.m_pageView->horizontalScrollBar()->maximum() + part.m_pageView->viewport()->width();
const int height = part.m_pageView->verticalScrollBar()->maximum() + part.m_pageView->viewport()->height();
part.m_document->setViewportPage(0);
// wait for pixmap
QTRY_VERIFY(part.m_document->page(0)->hasPixmap(part.m_pageView));
part.actionCollection()->action(QStringLiteral("view_toggle_forms"))->trigger();
// Note as of version 1.5:
// The test document is prepared for future extensions to formatting for dates etc.
// Currently we only have the number format to test.
const auto ff_us = dynamic_cast<Okular::FormFieldText *>(fields.value(QStringLiteral("us_currency_fmt")));
const auto ff_de = dynamic_cast<Okular::FormFieldText *>(fields.value(QStringLiteral("de_currency_fmt")));
const auto ff_sum = dynamic_cast<Okular::FormFieldText *>(fields.value(QStringLiteral("de_simple_sum")));
const QPoint usPos(width * 0.25, height * 0.025);
const QPoint dePos(width * 0.25, height * 0.05);
const QPoint deSumPos(width * 0.25, height * 0.075);
const auto viewport = part.m_pageView->viewport();
QVERIFY(viewport);
auto usCurrencyWidget = dynamic_cast<QLineEdit *>(viewport->childAt(usPos));
auto deCurrencyWidget = dynamic_cast<QLineEdit *>(viewport->childAt(dePos));
auto sumCurrencyWidget = dynamic_cast<QLineEdit *>(viewport->childAt(deSumPos));
// Check that the widgets were found at the right position
QVERIFY(usCurrencyWidget);
QVERIFY(deCurrencyWidget);
QVERIFY(sumCurrencyWidget);
QTest::mousePress(usCurrencyWidget, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
QTRY_VERIFY(usCurrencyWidget->hasFocus());
// locale is en_US for this test. Enter a value and check it.
usCurrencyWidget->setText(QStringLiteral("1234.56"));
// Check that the internal text matches
QCOMPARE(ff_us->text(), QStringLiteral("1234.56"));
// Now move the focus to trigger formatting.
QTest::mousePress(deCurrencyWidget, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
QTRY_VERIFY(deCurrencyWidget->hasFocus());
QCOMPARE(usCurrencyWidget->text(), QStringLiteral("$ 1,234.56"));
QCOMPARE(ff_us->text(), QStringLiteral("1234.56"));
// And again with an invalid number
QTest::mousePress(usCurrencyWidget, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
QTRY_VERIFY(usCurrencyWidget->hasFocus());
usCurrencyWidget->setText(QStringLiteral("131.234,567"));
QTest::mousePress(deCurrencyWidget, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
QTRY_VERIFY(deCurrencyWidget->hasFocus());
// Check that the internal text still contains it.
QCOMPARE(ff_us->text(), QStringLiteral("131.234,567"));
// Just check that the text does not match the internal text.
// We don't check for a concrete value to keep NaN handling flexible
QVERIFY(ff_us->text() != usCurrencyWidget->text());
// Move the focus back and modify it a bit more
QTest::mousePress(usCurrencyWidget, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
QTRY_VERIFY(usCurrencyWidget->hasFocus());
usCurrencyWidget->setText(QStringLiteral("1,234.567"));
QTest::mousePress(deCurrencyWidget, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
QTRY_VERIFY(deCurrencyWidget->hasFocus());
QCOMPARE(usCurrencyWidget->text(), QStringLiteral("$ 1,234.57"));
// Sum should already match
QCOMPARE(sumCurrencyWidget->text(), QStringLiteral("1.234,57€"));
// Set a text in the de field
deCurrencyWidget->setText(QStringLiteral("1,123,234.567"));
QTest::mousePress(usCurrencyWidget, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
QTRY_VERIFY(usCurrencyWidget->hasFocus());
QCOMPARE(deCurrencyWidget->text(), QStringLiteral("1.123.234,57 €"));
QCOMPARE(ff_de->text(), QStringLiteral("1,123,234.567"));
QCOMPARE(sumCurrencyWidget->text(), QStringLiteral("1.124.469,13€"));
QCOMPARE(ff_sum->text(), QStringLiteral("1,124,469.1340000000782310962677002"));
}
} // namespace Okular
int main(int argc, char *argv[])
@ -2200,8 +2360,8 @@ int main(int argc, char *argv[])
qDebug() << homePath;
qputenv("USERPROFILE", homePath);
qputenv("HOME", homePath);
qputenv("XDG_DATA_HOME", homePath + "/.local");
qputenv("XDG_CONFIG_HOME", homePath + "/.kde-unit-test/xdg/config");
qputenv("XDG_DATA_HOME", QByteArray(homePath + "/.local"));
qputenv("XDG_CONFIG_HOME", QByteArray(homePath + "/.kde-unit-test/xdg/config"));
// Disable fancy debug output
qunsetenv("QT_MESSAGE_PATTERN");

View File

@ -62,7 +62,7 @@ static void createTextPage(const QVector<QString> &text, const QVector<Okular::N
{
tp = new Okular::TextPage();
for (int i = 0; i < text.size(); i++) {
tp->append(text[i], new Okular::NormalizedRect(rect[i]));
tp->append(text[i], rect[i]);
}
// The Page::setTextPage method invokes the layout analysis algorithms tested by some tests here

View File

@ -88,35 +88,32 @@ void SignUnsignedFieldTest::testSignUnsignedField()
QCOMPARE(forms.count(), 1);
Okular::FormFieldSignature *ffs = dynamic_cast<Okular::FormFieldSignature *>(forms.first());
// This is a hacky way of doing ifdef HAVE_POPPLER_22_02
if (m_document->metaData(QStringLiteral("CanSignDocumentWithPassword")).toString() == QLatin1String("yes")) {
QCOMPARE(ffs->signatureType(), Okular::FormFieldSignature::UnsignedSignature);
QCOMPARE(ffs->signatureType(), Okular::FormFieldSignature::UnsignedSignature);
const Okular::CertificateStore *certStore = m_document->certificateStore();
bool userCancelled, nonDateValidCerts;
{
EnterPasswordDialogHelper helper;
const QList<Okular::CertificateInfo *> &certs = certStore->signingCertificatesForNow(&userCancelled, &nonDateValidCerts);
QCOMPARE(certs.count(), 1);
}
Okular::NewSignatureData data;
data.setCertNickname(QStringLiteral("fake-okular"));
QTemporaryFile f;
f.open();
QVERIFY(ffs->sign(data, f.fileName()));
m_document->closeDocument();
QMimeDatabase db;
const QMimeType mime = db.mimeTypeForFile(f.fileName());
QCOMPARE(m_document->openDocument(f.fileName(), QUrl(), mime), Okular::Document::OpenSuccess);
const QList<Okular::FormField *> newForms = m_document->page(0)->formFields();
QCOMPARE(newForms.count(), 1);
ffs = dynamic_cast<Okular::FormFieldSignature *>(newForms.first());
QCOMPARE(ffs->signatureType(), Okular::FormFieldSignature::AdbePkcs7detached);
QCOMPARE(ffs->signatureInfo().signerName(), QStringLiteral("FakeOkular"));
const Okular::CertificateStore *certStore = m_document->certificateStore();
bool userCancelled, nonDateValidCerts;
{
EnterPasswordDialogHelper helper;
const QList<Okular::CertificateInfo> &certs = certStore->signingCertificatesForNow(&userCancelled, &nonDateValidCerts);
QCOMPARE(certs.count(), 1);
}
Okular::NewSignatureData data;
data.setCertNickname(QStringLiteral("fake-okular"));
QTemporaryFile f;
f.open();
QVERIFY(ffs->sign(data, f.fileName()));
m_document->closeDocument();
QMimeDatabase db;
const QMimeType mime = db.mimeTypeForFile(f.fileName());
QCOMPARE(m_document->openDocument(f.fileName(), QUrl(), mime), Okular::Document::OpenSuccess);
const QList<Okular::FormField *> newForms = m_document->page(0)->formFields();
QCOMPARE(newForms.count(), 1);
ffs = dynamic_cast<Okular::FormFieldSignature *>(newForms.first());
QCOMPARE(ffs->signatureType(), Okular::FormFieldSignature::AdbePkcs7detached);
QCOMPARE(ffs->signatureInfo().signerName(), QStringLiteral("FakeOkular"));
}
QTEST_MAIN(SignUnsignedFieldTest)

View File

@ -0,0 +1,53 @@
/*
SPDX-FileCopyrightText: 2023 g10 Code GmbH
SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QTest>
#include "../part/signaturepartutils.h"
class SuggestedFileNameTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testSuggestedSignedDocumentName();
void testSuggestedSignedDocumentName_data();
};
void SuggestedFileNameTest::initTestCase()
{
qputenv("LC_ALL", "en_US.UTF-8");
}
void SuggestedFileNameTest::testSuggestedSignedDocumentName()
{
QFETCH(QString, input);
QFETCH(QString, preferredSuffix);
QFETCH(QString, expected);
auto output = SignaturePartUtils::getSuggestedFileNameForSignedFile(input, preferredSuffix);
QCOMPARE(output, expected);
}
void SuggestedFileNameTest::testSuggestedSignedDocumentName_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("preferredSuffix"); // normally derived from mimetype of document
QTest::addColumn<QString>("expected");
QTest::newRow("simple") << QStringLiteral("foo.pdf") << QStringLiteral("pdf") << QStringLiteral("foo_signed.pdf");
QTest::newRow("double extensions") << QStringLiteral("foo.pdf.gz") << QStringLiteral("pdf") << QStringLiteral("foo_signed.pdf"); // while we might read compressed files, we don't write them out
QTest::newRow("versioning") << QStringLiteral("foo-1.2.3.pdf") << QStringLiteral("pdf") << QStringLiteral("foo-1.2.3_signed.pdf");
QTest::newRow("versioned and double extensions") << QStringLiteral("foo-1.2.3.pdf.gz") << QStringLiteral("pdf") << QStringLiteral("foo-1.2.3_signed.pdf");
QTest::newRow("gif") << QStringLiteral("foo.gif") << QStringLiteral("pdf") << QStringLiteral("foo_signed.pdf");
QTest::newRow("version gif") << QStringLiteral("foo-1.2.3.gif") << QStringLiteral("pdf") << QStringLiteral("foo-1.2.3_signed.pdf");
QTest::newRow("no extension") << QStringLiteral("foo") << QStringLiteral("pdf") << QStringLiteral("foo_signed.pdf");
QTest::newRow("no extension with versions") << QStringLiteral("foo-1.2.3") << QStringLiteral("pdf") << QStringLiteral("foo-1.2_signed.pdf"); // This is not as such expected behavior but more a documentation of implementation.
}
QTEST_GUILESS_MAIN(SuggestedFileNameTest)
#include "suggestedfilenametest.moc"

View File

@ -7,6 +7,8 @@
#include "testingutils.h"
#include "core/annotations.h"
#include <QIODevice>
namespace TestingUtils
{
QString getAnnotationXml(const Okular::Annotation *annotation)

View File

@ -4,7 +4,7 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QtTest>
#include <QTest>
#include "../part/toggleactionmenu.h"
#include <QToolBar>

View File

@ -4,13 +4,14 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QtTest>
#include <QTest>
#include "../settings_core.h"
#include "core/document.h"
#include <QMap>
#include <QMimeDatabase>
#include <QMimeType>
#include <QTemporaryFile>
#include <core/form.h>
#include <core/page.h>

View File

@ -325,6 +325,9 @@
<default>true</default>
<emit signal="viewContinuousChanged" />
</entry>
<entry key="DragBeyondScreenEdges" type="Bool" >
<default>true</default>
</entry>
<entry key="PrimaryAnnotationToolBar" type="Enum" >
<default>FullAnnotationToolBar</default>
<choices>

View File

@ -2,7 +2,25 @@
#define OKULAR_FORCE_DRM ${_OKULAR_FORCE_DRM}
/* Defines if the purpose framework is available */
#define PURPOSE_FOUND ${PURPOSE_FOUND}
#cmakedefine01 HAVE_PURPOSE
/* Defines if qtspeech is available */
#cmakedefine01 HAVE_SPEECH
/* Defines if kwallet is available */
#cmakedefine01 HAVE_KWALLET
/* Defines if QJSEngine is available*/
#cmakedefine01 HAVE_JS
/* Defines whether the malloc_trim method from malloc.h is available */
#cmakedefine01 HAVE_MALLOC_TRIM
/* Defines whether we are building with X11 support */
#cmakedefine01 HAVE_X11
/* Defines wheter we are building with Phonon support */
#cmakedefine01 HAVE_PHONON
/* Defines whether we are building with dbus enabled */
#cmakedefine01 HAVE_DBUS

View File

@ -10,6 +10,7 @@
// qt/kde includes
#include <QApplication>
#include <QColor>
#include <QFile>
#include <QIcon>
#include <QPainter>
#include <QStandardPaths>
@ -46,12 +47,11 @@ static bool isLeftOfVector(const NormalizedPoint &a, const NormalizedPoint &b, c
static double distanceSqr(double x, double y, double xScale, double yScale, const QList<NormalizedPoint> &path)
{
double distance = DBL_MAX;
double thisDistance;
QList<NormalizedPoint>::const_iterator i = path.constBegin();
NormalizedPoint lastPoint = *i;
for (++i; i != path.constEnd(); ++i) {
thisDistance = NormalizedPoint::distanceSqr(x, y, xScale, yScale, lastPoint, (*i));
double thisDistance = NormalizedPoint::distanceSqr(x, y, xScale, yScale, lastPoint, (*i));
if (thisDistance < distance) {
distance = thisDistance;
@ -154,7 +154,7 @@ QDomElement AnnotationUtils::findChildElement(const QDomNode &parentNode, const
QRect AnnotationUtils::annotationGeometry(const Annotation *annotation, double scaleX, double scaleY)
{
const QRect rect = annotation->transformedBoundingRectangle().geometry((int)scaleX, (int)scaleY);
if (annotation->subType() == Annotation::AText && (((TextAnnotation *)annotation)->textType() == TextAnnotation::Linked)) {
if (annotation->subType() == Annotation::AText && (static_cast<const TextAnnotation *>(annotation)->textType() == TextAnnotation::Linked)) {
// To be honest i have no clue of why the 24,24 is here, maybe to make sure it's not too small?
// But why only for linked text?
const QRect rect24 = QRect((int)(annotation->transformedBoundingRectangle().left * scaleX), (int)(annotation->transformedBoundingRectangle().top * scaleY), 24, 24);
@ -192,11 +192,16 @@ QPixmap AnnotationUtils::loadStamp(const QString &nameOrPath, int size, bool kee
}
// _name is a path (do this before loading as icon name to avoid some rare weirdness )
QPixmap pixmap;
pixmap.load(nameOrPath);
if (!pixmap.isNull()) {
pixmap = pixmap.scaled(size, size, keepAspectRatio ? Qt::KeepAspectRatioByExpanding : Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
return pixmap;
// Check that it exists up front. While pixmap.load() fails, if it is
// actually an icon from theme, the loader will try all supported
// extensions in current workdir before failing
if (QFile::exists(nameOrPath)) {
QPixmap pixmap;
pixmap.load(nameOrPath);
if (!pixmap.isNull()) {
pixmap = pixmap.scaled(size, size, keepAspectRatio ? Qt::KeepAspectRatioByExpanding : Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
return pixmap;
}
}
// _name is an icon name
@ -554,7 +559,7 @@ AnnotationPrivate::~AnnotationPrivate()
return;
}
for (const Annotation::Revision &revision : qAsConst(m_revisions)) {
for (const Annotation::Revision &revision : std::as_const(m_revisions)) {
delete revision.annotation();
}
}
@ -869,7 +874,7 @@ void Annotation::store(QDomNode &annNode, QDomDocument &document) const
}
// add all revisions as children of revisions element
for (const Revision &revision : qAsConst(d->m_revisions)) {
for (const Revision &revision : std::as_const(d->m_revisions)) {
// create revision element
QDomElement r = document.createElement(QStringLiteral("revision"));
annNode.appendChild(r);
@ -1602,7 +1607,7 @@ void LineAnnotation::store(QDomNode &node, QDomDocument &document) const
lineElement.appendChild(pElement);
pElement.setAttribute(QStringLiteral("x"), QString::number(p.x));
pElement.setAttribute(QStringLiteral("y"), QString::number(p.y));
it++; // to avoid loop
++it; // to avoid loop
}
}
}
@ -1723,7 +1728,7 @@ double LineAnnotationPrivate::distanceSqr(double x, double y, double xScale, dou
if (m_lineInnerColor.isValid()) {
QPolygonF polygon;
for (const NormalizedPoint &p : qAsConst(transformedLinePoints)) {
for (const NormalizedPoint &p : std::as_const(transformedLinePoints)) {
polygon.append(QPointF(p.x, p.y));
}
@ -1930,6 +1935,9 @@ class HighlightAnnotation::Quad::Private
{
public:
Private()
: m_capStart(false)
, m_capEnd(false)
, m_feather(0.0)
{
}

View File

@ -243,6 +243,13 @@ QRect NormalizedRect::roundedGeometry(int xScale, int yScale) const
return QRect(l, t, r - l + 1, b - t + 1);
}
QRectF NormalizedRect::geometryF(float xScale, float yScale) const
{
float l = (left * xScale), t = (top * yScale), r = (right * xScale), b = (bottom * yScale);
return QRectF(l, t, r - l, b - t);
}
void NormalizedRect::transform(const QTransform &matrix)
{
QRectF rect(left, top, right - left, bottom - top);
@ -254,9 +261,9 @@ void NormalizedRect::transform(const QTransform &matrix)
bottom = rect.bottom();
}
uint Okular::qHash(const NormalizedRect &r, uint seed)
size_t Okular::qHash(const NormalizedRect &r, size_t seed)
{
return ::qHash(r.bottom, ::qHash(r.right, ::qHash(r.top, ::qHash(r.left, seed))));
return ::qHashMulti(seed, r.bottom, r.right, r.top, r.left);
}
QDebug operator<<(QDebug str, const Okular::NormalizedRect &r)

View File

@ -282,6 +282,11 @@ public:
*/
QRect roundedGeometry(int xScale, int yScale) const;
/**
* Same functionality as geometry, but nothing is converted into int.
*/
QRectF geometryF(float xScale, float yScale) const;
/**
* Returns the normalized bounding rectangle of the normalized rectangle
* combined with the @p other normalized rectangle.
@ -962,7 +967,7 @@ public:
QColor color;
};
uint qHash(const Okular::NormalizedRect &r, uint seed = 0);
size_t qHash(const Okular::NormalizedRect &r, size_t seed = 0);
}
#ifndef QT_NO_DEBUG_STREAM

View File

@ -5,25 +5,60 @@
*/
#include "audioplayer.h"
#include "audioplayer_p.h"
// qt/kde includes
#include <KRandom>
#include <KLocalizedString>
#include <QBuffer>
#include <QDebug>
#include <QDir>
#include <QRandomGenerator>
#include "config-okular.h"
#if HAVE_PHONON
#include <phonon/abstractmediastream.h>
#include <phonon/audiooutput.h>
#include <phonon/mediaobject.h>
#include <phonon/path.h>
#endif
// local includes
#include "action.h"
#include "debug_p.h"
#include "document.h"
#include "sound.h"
#include <stdlib.h>
using namespace Okular;
#if HAVE_PHONON
class PlayData;
class SoundInfo;
namespace Okular
{
class AudioPlayerPrivate
{
public:
explicit AudioPlayerPrivate(AudioPlayer *qq);
~AudioPlayerPrivate();
int newId() const;
bool play(const SoundInfo &si);
void stopPlayings();
void finished(int);
AudioPlayer *q;
QHash<int, PlayData *> m_playing;
QUrl m_currentDocument;
AudioPlayer::State m_state;
};
}
// helper class used to store info about a sound to be played
class SoundInfo
{
@ -98,11 +133,12 @@ AudioPlayerPrivate::~AudioPlayerPrivate()
int AudioPlayerPrivate::newId() const
{
auto random = QRandomGenerator::global();
int newid = 0;
QHash<int, PlayData *>::const_iterator it;
QHash<int, PlayData *>::const_iterator itEnd = m_playing.constEnd();
do {
newid = KRandom::random();
newid = random->bounded(RAND_MAX);
it = m_playing.constFind(newid);
} while (it != itEnd);
return newid;
@ -126,13 +162,7 @@ bool AudioPlayerPrivate::play(const SoundInfo &si)
if (!url.isEmpty()) {
int newid = newId();
QObject::connect(data->m_mediaobject, &Phonon::MediaObject::finished, q, [this, newid]() { finished(newid); });
QUrl newurl;
if (QUrl::fromUserInput(url).isRelative()) {
newurl = m_currentDocument.adjusted(QUrl::RemoveFilename);
newurl.setPath(newurl.path() + url);
} else {
newurl = QUrl::fromLocalFile(url);
}
const QUrl newurl = QUrl::fromUserInput(url, m_currentDocument.adjusted(QUrl::RemoveFilename).toLocalFile());
data->m_mediaobject->setCurrentSource(newurl);
m_playing.insert(newid, data);
valid = true;
@ -245,4 +275,70 @@ AudioPlayer::State AudioPlayer::state() const
return d->m_state;
}
void AudioPlayer::resetDocument()
{
d->m_currentDocument = {};
}
void AudioPlayer::setDocument(const QUrl &url, Okular::Document *document)
{
Q_UNUSED(document);
d->m_currentDocument = url;
}
#else
namespace Okular
{
class AudioPlayerPrivate
{
public:
Document *document;
};
}
AudioPlayer::AudioPlayer()
: d(new AudioPlayerPrivate())
{
}
AudioPlayer *AudioPlayer::instance()
{
static AudioPlayer ap;
return &ap;
}
void AudioPlayer::playSound(const Sound *sound, const SoundAction *linksound)
{
Q_UNUSED(sound);
Q_UNUSED(linksound);
Q_EMIT d->document->warning(i18n("This Okular is built without audio support"), 2000);
}
AudioPlayer::State Okular::AudioPlayer::state() const
{
return State::StoppedState;
}
void AudioPlayer::stopPlaybacks()
{
}
AudioPlayer::~AudioPlayer() noexcept
{
}
void AudioPlayer::resetDocument()
{
d->document = nullptr;
}
void AudioPlayer::setDocument(const QUrl &url, Okular::Document *document)
{
Q_UNUSED(url);
d->document = document;
}
#endif
#include "moc_audioplayer.cpp"

View File

@ -70,6 +70,8 @@ public:
private:
AudioPlayer();
void resetDocument();
void setDocument(const QUrl &url, Document *document);
friend class AudioPlayerPrivate;
AudioPlayerPrivate *const d;

View File

@ -1,43 +0,0 @@
/*
SPDX-FileCopyrightText: 2007 Pino Toscano <pino@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef _OKULAR_AUDIOPLAYER_P_H_
#define _OKULAR_AUDIOPLAYER_P_H_
// qt/kde includes
#include <QHash>
#include <QUrl>
class PlayData;
class SoundInfo;
namespace Okular
{
class AudioPlayer;
class AudioPlayerPrivate
{
public:
explicit AudioPlayerPrivate(AudioPlayer *qq);
~AudioPlayerPrivate();
int newId() const;
bool play(const SoundInfo &si);
void stopPlayings();
void finished(int);
AudioPlayer *q;
QHash<int, PlayData *> m_playing;
QUrl m_currentDocument;
AudioPlayer::State m_state;
};
}
#endif

View File

@ -10,6 +10,7 @@
#include <KBookmarkAction>
#include <KBookmarkManager>
#include <KBookmarkMenu>
#include <KBookmarkOwner>
#include <QDebug>
#include <QFileInfo>
#include <QGuiApplication>
@ -117,7 +118,8 @@ public:
: KBookmarkOwner()
, q(qq)
, document(nullptr)
, manager(nullptr)
, file(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/okular/bookmarks.xml"))
, manager(KBookmarkManager(file))
{
}
@ -139,14 +141,14 @@ public:
QHash<QUrl, QString>::iterator bookmarkFind(const QUrl &url, bool doCreate, KBookmarkGroup *result = nullptr);
// slots
void _o_changed(const QString &groupAddress, const QString &caller);
void _o_changed(const QString &groupAddress);
BookmarkManager *q;
QUrl url;
QHash<int, int> urlBookmarks;
DocumentPrivate *document;
QString file;
KBookmarkManager *manager;
KBookmarkManager manager;
QHash<QUrl, QString> knownFiles;
};
@ -167,12 +169,7 @@ BookmarkManager::BookmarkManager(DocumentPrivate *document)
d->document = document;
d->file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/okular/bookmarks.xml");
d->manager = KBookmarkManager::managerForFile(d->file, QStringLiteral("okular"));
d->manager->setEditorOptions(QGuiApplication::applicationDisplayName(), false);
d->manager->setUpdate(true);
connect(d->manager, &KBookmarkManager::changed, this, [this](const QString &groupAddress, const QString &caller) { d->_o_changed(groupAddress, caller); });
connect(&d->manager, &KBookmarkManager::changed, this, [this](const QString &groupAddress) { d->_o_changed(groupAddress); });
}
BookmarkManager::~BookmarkManager()
@ -203,9 +200,8 @@ void BookmarkManager::Private::openBookmark(const KBookmark &bm, Qt::MouseButton
}
// END Reimplementations from KBookmarkOwner
void BookmarkManager::Private::_o_changed(const QString &groupAddress, const QString &caller)
void BookmarkManager::Private::_o_changed(const QString &groupAddress)
{
Q_UNUSED(caller);
if (groupAddress.isEmpty()) {
return;
}
@ -221,7 +217,7 @@ void BookmarkManager::Private::_o_changed(const QString &groupAddress, const QSt
}
}
if (!referurl.isValid()) {
const KBookmark bm = manager->findByAddress(groupAddress);
const KBookmark bm = manager.findByAddress(groupAddress);
// better be safe than sorry
if (bm.isNull()) {
return;
@ -257,7 +253,7 @@ void BookmarkManager::Private::_o_changed(const QString &groupAddress, const QSt
QList<QUrl> BookmarkManager::files() const
{
QList<QUrl> ret;
KBookmarkGroup group = d->manager->root();
KBookmarkGroup group = d->manager.root();
for (KBookmark bm = group.first(); !bm.isNull(); bm = group.next(bm)) {
if (bm.isSeparator() || !bm.isGroup()) {
continue;
@ -272,7 +268,7 @@ KBookmark::List BookmarkManager::bookmarks(const QUrl &documentUrl) const
{
const QUrl url = mostCanonicalUrl(documentUrl);
KBookmark::List ret;
KBookmarkGroup group = d->manager->root();
KBookmarkGroup group = d->manager.root();
for (KBookmark bm = group.first(); !bm.isNull(); bm = group.next(bm)) {
if (!bm.isGroup() || urlForGroup(bm) != url) {
continue;
@ -351,7 +347,7 @@ KBookmark BookmarkManager::bookmark(const DocumentViewport &viewport) const
void BookmarkManager::save() const
{
d->manager->emitChanged();
d->manager.emitChanged();
Q_EMIT const_cast<BookmarkManager *>(this)->saved();
}
@ -363,7 +359,7 @@ QHash<QUrl, QString>::iterator BookmarkManager::Private::bookmarkFind(const QUrl
// known files, then first try to find the file among the top-level
// "folder" names
bool found = false;
KBookmarkGroup root = manager->root();
KBookmarkGroup root = manager.root();
for (KBookmark bm = root.first(); !found && !bm.isNull(); bm = root.next(bm)) {
if (bm.isSeparator() || !bm.isGroup()) {
continue;
@ -393,7 +389,7 @@ QHash<QUrl, QString>::iterator BookmarkManager::Private::bookmarkFind(const QUrl
}
}
} else if (result) {
const KBookmark bm = manager->findByAddress(it.value());
const KBookmark bm = manager.findByAddress(it.value());
Q_ASSERT(bm.isGroup());
*result = bm.toGroup();
}
@ -473,7 +469,7 @@ bool BookmarkManager::addBookmark(const QUrl &documentUrl, const Okular::Documen
d->urlBookmarks[vp.pageNumber]++;
foreachObserver(notifyPageChanged(vp.pageNumber, DocumentObserver::Bookmark));
}
d->manager->emitChanged(thebg);
d->manager.emitChanged(thebg);
return true;
}
@ -503,7 +499,7 @@ void BookmarkManager::renameBookmark(KBookmark *bm, const QString &newName)
}
bm->setFullText(newName);
d->manager->emitChanged(thebg);
d->manager.emitChanged(thebg);
}
void BookmarkManager::renameBookmark(const QUrl &documentUrl, const QString &newName)
@ -522,7 +518,7 @@ void BookmarkManager::renameBookmark(const QUrl &documentUrl, const QString &new
}
thebg.setFullText(newName);
d->manager->emitChanged(thebg);
d->manager.emitChanged(thebg);
}
QString BookmarkManager::titleForUrl(const QUrl &documentUrl) const
@ -559,7 +555,7 @@ int BookmarkManager::removeBookmark(const QUrl &documentUrl, const KBookmark &bm
d->urlBookmarks[vp.pageNumber]--;
foreachObserver(notifyPageChanged(vp.pageNumber, DocumentObserver::Bookmark));
}
d->manager->emitChanged(thebg);
d->manager.emitChanged(thebg);
return vp.pageNumber;
}
@ -605,7 +601,7 @@ void BookmarkManager::removeBookmarks(const QUrl &documentUrl, const KBookmark::
}
}
if (deletedAny) {
d->manager->emitChanged(thebg);
d->manager.emitChanged(thebg);
}
}
@ -613,7 +609,7 @@ QList<QAction *> BookmarkManager::actionsForUrl(const QUrl &documentUrl) const
{
const QUrl url = mostCanonicalUrl(documentUrl);
QList<QAction *> ret;
KBookmarkGroup group = d->manager->root();
KBookmarkGroup group = d->manager.root();
for (KBookmark bm = group.first(); !bm.isNull(); bm = group.next(bm)) {
if (!bm.isGroup() || urlForGroup(bm) != url) {
continue;
@ -681,7 +677,7 @@ bool BookmarkManager::setPageBookmark(int page)
newurl.setFragment(vp.toString(), QUrl::DecodedMode);
thebg.addBookmark(QLatin1String("#") + QString::number(vp.pageNumber + 1), newurl, QString());
added = true;
d->manager->emitChanged(thebg);
d->manager.emitChanged(thebg);
}
return added;
}
@ -705,7 +701,7 @@ bool BookmarkManager::removePageBookmark(int page)
found = true;
thebg.deleteBookmark(bm);
d->urlBookmarks[page]--;
d->manager->emitChanged(thebg);
d->manager.emitChanged(thebg);
}
}
return found;
@ -729,7 +725,7 @@ KBookmark BookmarkManager::nextBookmark(const DocumentViewport &viewport) const
std::sort(bmarks.begin(), bmarks.end(), bookmarkLessThan);
KBookmark bookmark;
for (const KBookmark &bm : qAsConst(bmarks)) {
for (const KBookmark &bm : std::as_const(bmarks)) {
DocumentViewport vp(bm.url().fragment(QUrl::FullyDecoded));
if (viewport < vp) {
bookmark = bm;

View File

@ -47,20 +47,21 @@
#include <QUndoCommand>
#include <QWindow>
#include <QtAlgorithms>
#include <private/qstringiterator_p.h>
#include <KApplicationTrader>
#include <KAuthorized>
#include <KConfigDialog>
#include <KFormat>
#include <KIO/Global>
#include <KIO/JobUiDelegate>
#include <KIO/JobUiDelegateFactory>
#include <KIO/OpenUrlJob>
#include <KLocalizedString>
#include <KMacroExpander>
#include <KPluginMetaData>
#include <KProcess>
#include <KRun>
#include <KShell>
#include <Kdelibs4Migration>
#include <kio_version.h>
#include <kzip.h>
// local includes
@ -68,7 +69,6 @@
#include "annotations.h"
#include "annotations_p.h"
#include "audioplayer.h"
#include "audioplayer_p.h"
#include "bookmarkmanager.h"
#include "chooseenginedialog_p.h"
#include "debug_p.h"
@ -164,7 +164,10 @@ struct RunningSearch {
#define OKULAR_HISTORY_SAVEDSTEPS 10
// how often to run slotTimedMemoryCheck
const int kMemCheckTime = 2000; // in msec
constexpr int kMemCheckTime = 2000; // in msec
// getFreeMemory is called every two seconds when checking to see if the system is low on memory. If this timeout was left at kMemCheckTime, half of these checks are useless (when okular is idle) since the cache is used when the cache is
// <=2 seconds old. This means that after the system is out of memory, up to 4 seconds (instead of 2) could go by before okular starts to free memory.
constexpr int kFreeMemCacheTimeout = kMemCheckTime - 100;
/***** Document ******/
@ -213,14 +216,14 @@ QString DocumentPrivate::pagesSizeString() const
QString DocumentPrivate::namePaperSize(double inchesWidth, double inchesHeight) const
{
const QPrinter::Orientation orientation = inchesWidth > inchesHeight ? QPrinter::Landscape : QPrinter::Portrait;
const QPageLayout::Orientation orientation = inchesWidth > inchesHeight ? QPageLayout::Landscape : QPageLayout::Portrait;
const QSize pointsSize(inchesWidth * 72.0, inchesHeight * 72.0);
const QPageSize::PageSizeId paperSize = QPageSize::id(pointsSize, QPageSize::FuzzyOrientationMatch);
const QString paperName = QPageSize::name(paperSize);
if (orientation == QPrinter::Portrait) {
if (orientation == QPageLayout::Portrait) {
return i18nc("paper type and orientation (eg: Portrait A4)", "Portrait %1", paperName);
} else {
return i18nc("paper type and orientation (eg: Portrait A4)", "Landscape %1", paperName);
@ -350,7 +353,7 @@ void DocumentPrivate::cleanupPixmapMemory(qulonglong memoryToFree)
std::list<AllocatedPixmap *> pixmapsToKeep;
while (memoryToFree > 0) {
int clean_hits = 0;
for (DocumentObserver *observer : qAsConst(m_observers)) {
for (DocumentObserver *observer : std::as_const(m_observers)) {
AllocatedPixmap *p = searchLowestPriorityPixmap(false, true, observer);
if (!p) { // No pixmap to remove
continue;
@ -390,6 +393,7 @@ void DocumentPrivate::cleanupPixmapMemory(qulonglong memoryToFree)
}
m_allocatedPixmaps.splice(m_allocatedPixmaps.end(), pixmapsToKeep);
Q_UNUSED(pagesFreed);
// p--rintf("freeMemory A:[%d -%d = %d] \n", m_allocatedPixmaps.count() + pagesFreed, pagesFreed, m_allocatedPixmaps.count() );
}
@ -474,11 +478,11 @@ qulonglong DocumentPrivate::getTotalMemory()
qulonglong DocumentPrivate::getFreeMemory(qulonglong *freeSwap)
{
static QTime lastUpdate = QTime::currentTime().addSecs(-3);
static QDeadlineTimer cacheTimer(0);
static qulonglong cachedValue = 0;
static qulonglong cachedFreeSwap = 0;
if (qAbs(lastUpdate.msecsTo(QTime::currentTime())) <= kMemCheckTime - 100) {
if (!cacheTimer.hasExpired()) {
if (freeSwap) {
*freeSwap = cachedFreeSwap;
}
@ -537,7 +541,7 @@ qulonglong DocumentPrivate::getFreeMemory(qulonglong *freeSwap)
return 0;
}
lastUpdate = QTime::currentTime();
cacheTimer.setRemainingTime(kFreeMemCacheTimeout);
if (freeSwap) {
*freeSwap = (cachedFreeSwap = (Q_UINT64_C(1024) * values[3]));
@ -553,7 +557,7 @@ qulonglong DocumentPrivate::getFreeMemory(qulonglong *freeSwap)
// sum up inactive, cached and free memory
if (sysctlbyname("vm.stats.vm.v_cache_count", &cache, &cachelen, NULL, 0) == 0 && sysctlbyname("vm.stats.vm.v_inactive_count", &inact, &inactlen, NULL, 0) == 0 &&
sysctlbyname("vm.stats.vm.v_free_count", &free, &freelen, NULL, 0) == 0 && sysctlbyname("vm.stats.vm.v_page_size", &psize, &psizelen, NULL, 0) == 0) {
lastUpdate = QTime::currentTime();
cacheTimer.setRemainingTime(kFreeMemCacheTimeout);
return (cachedValue = (cache + inact + free) * psize);
} else {
return 0;
@ -563,7 +567,7 @@ qulonglong DocumentPrivate::getFreeMemory(qulonglong *freeSwap)
stat.dwLength = sizeof(stat);
GlobalMemoryStatusEx(&stat);
lastUpdate = QTime::currentTime();
cacheTimer.setRemainingTime(kFreeMemCacheTimeout);
if (freeSwap)
*freeSwap = (cachedFreeSwap = stat.ullAvailPageFile);
@ -675,7 +679,7 @@ bool DocumentPrivate::loadDocumentInfo(QFile &infoFile, LoadDocumentInfoFlags lo
QDomElement viewElement = viewNode.toElement();
if (viewElement.tagName() == QLatin1String("view")) {
const QString viewName = viewElement.attribute(QStringLiteral("name"));
for (View *view : qAsConst(m_views)) {
for (View *view : std::as_const(m_views)) {
if (view->name() == viewName) {
loadViewsInfo(view, viewElement);
loadedAnything = true;
@ -809,19 +813,16 @@ bool DocumentPrivate::openRelativeFile(const QString &fileName)
Generator *DocumentPrivate::loadGeneratorLibrary(const KPluginMetaData &service)
{
KPluginLoader loader(service.fileName());
qCDebug(OkularCoreDebug) << service.fileName();
KPluginFactory *factory = loader.factory();
if (!factory) {
qCWarning(OkularCoreDebug).nospace() << "Invalid plugin factory for " << service.fileName() << ":" << loader.errorString();
const auto result = KPluginFactory::instantiatePlugin<Okular::Generator>(service);
if (!result) {
qCWarning(OkularCoreDebug).nospace() << "Failed to load plugin " << service.fileName() << ": " << result.errorText;
return nullptr;
}
Generator *plugin = factory->create<Okular::Generator>();
GeneratorInfo info(plugin, service);
GeneratorInfo info(result.plugin, service);
m_loadedGenerators.insert(service.pluginId(), info);
return plugin;
return result.plugin;
}
void DocumentPrivate::loadAllGeneratorLibraries()
@ -1008,8 +1009,9 @@ bool DocumentPrivate::savePageDocumentInfo(QTemporaryFile *infoFile, int what) c
// 3. Save DOM to XML file
QString xml = doc.toString();
QTextStream os(infoFile);
os.setCodec("UTF-8");
os.setEncoding(QStringConverter::Utf8);
os << xml;
return true;
}
@ -1302,7 +1304,7 @@ void DocumentPrivate::saveDocumentInfo() const
// create views root node
QDomElement viewsNode = doc.createElement(QStringLiteral("views"));
generalInfo.appendChild(viewsNode);
for (View *view : qAsConst(m_views)) {
for (View *view : std::as_const(m_views)) {
QDomElement viewEntry = doc.createElement(QStringLiteral("view"));
viewEntry.setAttribute(QStringLiteral("name"), view->name());
viewsNode.appendChild(viewEntry);
@ -1312,7 +1314,7 @@ void DocumentPrivate::saveDocumentInfo() const
// 3. Save DOM to XML file
QString xml = doc.toString();
QTextStream os(&infoFile);
os.setCodec("UTF-8");
os.setEncoding(QStringConverter::Utf8);
os << xml;
infoFile.close();
}
@ -1366,6 +1368,9 @@ void DocumentPrivate::sendGeneratorPixmapRequest()
}
const long screenSize = screen->devicePixelRatio() * screen->size().width() * screen->devicePixelRatio() * screen->size().height();
// Make sure the page is the right size to receive the pixmap
r->page()->setPageSize(r->observer(), r->width(), r->height());
// If it's a preload but the generator is not threaded no point in trying to preload
if (r->preload() && !m_generator->hasFeature(Generator::Threaded)) {
m_pixmapRequestsStack.pop_back();
@ -1386,7 +1391,7 @@ void DocumentPrivate::sendGeneratorPixmapRequest()
delete r;
}
// If the requested area is above 4*screenSize pixels, and we're not rendering most of the page, switch on the tile manager
else if (!tilesManager && m_generator->hasFeature(Generator::TiledRendering) && (long)r->width() * (long)r->height() > 4L * screenSize && normalizedArea < 0.75 && normalizedArea != 0) {
else if (!tilesManager && m_generator->hasFeature(Generator::TiledRendering) && (long)r->width() * (long)r->height() > 4L * screenSize && normalizedArea < 0.75) {
// if the image is too big. start using tiles
qCDebug(OkularCoreDebug).nospace() << "Start using tiles on page " << r->pageNumber() << " (" << r->width() << "x" << r->height() << " px);";
@ -1518,7 +1523,7 @@ void DocumentPrivate::rotationFinished(int page, Okular::Page *okularPage)
return;
}
for (DocumentObserver *o : qAsConst(m_observers)) {
for (DocumentObserver *o : std::as_const(m_observers)) {
o->notifyPageChanged(page, DocumentObserver::Pixmap | DocumentObserver::Annotations);
}
}
@ -1602,13 +1607,13 @@ void DocumentPrivate::refreshPixmaps(int pageNumber)
// Need to do this ↑↓ in two steps since requestPixmaps can end up calling cancelRenderingBecauseOf
// which changes m_pixmaps and thus breaks the loop above
for (PixmapRequest *pr : qAsConst(pixmapsToRequest)) {
for (PixmapRequest *pr : std::as_const(pixmapsToRequest)) {
QList<Okular::PixmapRequest *> requestedPixmaps;
requestedPixmaps.push_back(pr);
m_parent->requestPixmaps(requestedPixmaps, Okular::Document::NoOption);
}
for (DocumentObserver *observer : qAsConst(m_observers)) {
for (DocumentObserver *observer : std::as_const(m_observers)) {
QList<Okular::PixmapRequest *> requestedPixmaps;
TilesManager *tilesManager = page->d->tilesManager(observer);
@ -1755,8 +1760,8 @@ void DocumentPrivate::doProcessSearchMatch(RegularAreaRect *match, RunningSearch
}
// notify observers about highlights changes
for (int pageNumber : qAsConst(*pagesToNotify)) {
for (DocumentObserver *observer : qAsConst(m_observers)) {
for (int pageNumber : std::as_const(*pagesToNotify)) {
for (DocumentObserver *observer : std::as_const(m_observers)) {
observer->notifyPageChanged(pageNumber, DocumentObserver::Highlights);
}
}
@ -1786,7 +1791,7 @@ void DocumentPrivate::doContinueAllDocumentSearch(void *pagesToNotifySet, void *
}
Q_EMIT m_parent->searchFinished(searchID, Document::SearchCancelled);
for (const MatchesVector &mv : qAsConst(*pageMatches)) {
for (const MatchesVector &mv : std::as_const(*pageMatches)) {
qDeleteAll(mv);
}
delete pageMatches;
@ -1841,13 +1846,13 @@ void DocumentPrivate::doContinueAllDocumentSearch(void *pagesToNotifySet, void *
pagesToNotify->insert(it.key()->number());
}
for (DocumentObserver *observer : qAsConst(m_observers)) {
for (DocumentObserver *observer : std::as_const(m_observers)) {
observer->notifySetup(m_pagesVector, 0);
}
// notify observers about highlights changes
for (int pageNumber : qAsConst(*pagesToNotify)) {
for (DocumentObserver *observer : qAsConst(m_observers)) {
for (int pageNumber : std::as_const(*pagesToNotify)) {
for (DocumentObserver *observer : std::as_const(m_observers)) {
observer->notifyPageChanged(pageNumber, DocumentObserver::Highlights);
}
}
@ -1881,7 +1886,7 @@ void DocumentPrivate::doContinueGooglesDocumentSearch(void *pagesToNotifySet, vo
Q_EMIT m_parent->searchFinished(searchID, Document::SearchCancelled);
for (const MatchesVector &mv : qAsConst(*pageMatches)) {
for (const MatchesVector &mv : std::as_const(*pageMatches)) {
for (const MatchColor &mc : mv) {
delete mc.first;
}
@ -1967,13 +1972,13 @@ void DocumentPrivate::doContinueGooglesDocumentSearch(void *pagesToNotifySet, vo
}
// send page lists to update observers (since some filter on bookmarks)
for (DocumentObserver *observer : qAsConst(m_observers)) {
for (DocumentObserver *observer : std::as_const(m_observers)) {
observer->notifySetup(m_pagesVector, 0);
}
// notify observers about highlights changes
for (int pageNumber : qAsConst(*pagesToNotify)) {
for (DocumentObserver *observer : qAsConst(m_observers)) {
for (int pageNumber : std::as_const(*pagesToNotify)) {
for (DocumentObserver *observer : std::as_const(m_observers)) {
observer->notifyPageChanged(pageNumber, DocumentObserver::Highlights);
}
}
@ -2077,7 +2082,7 @@ void DocumentPrivate::loadSyncFile(const QString &filePath)
const QString versionstr = ts.readLine();
// anchor the pattern with \A and \z to match the entire subject string
// TODO: with Qt 5.12 QRegularExpression::anchoredPattern() can be used instead
QRegularExpression versionre(QStringLiteral("\\AVersion \\d+\\z"), QRegularExpression::CaseInsensitiveOption);
static QRegularExpression versionre(QStringLiteral("\\AVersion \\d+\\z"), QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch match = versionre.match(versionstr);
if (!match.hasMatch()) {
return;
@ -2096,7 +2101,7 @@ void DocumentPrivate::loadSyncFile(const QString &filePath)
QString line;
while (!ts.atEnd()) {
line = ts.readLine();
const QStringList tokens = line.split(spaceChar, QString::SkipEmptyParts);
const QStringList tokens = line.split(spaceChar, Qt::SkipEmptyParts);
const int tokenSize = tokens.count();
if (tokenSize < 1) {
continue;
@ -2147,7 +2152,7 @@ void DocumentPrivate::loadSyncFile(const QString &filePath)
}
QVector<QList<Okular::SourceRefObjectRect *>> refRects(m_pagesVector.size());
for (const pdfsyncpoint &pt : qAsConst(points)) {
for (const pdfsyncpoint &pt : std::as_const(points)) {
// drop pdfsync points not completely valid
if (pt.page < 0 || pt.page >= m_pagesVector.size()) {
continue;
@ -2184,7 +2189,7 @@ void DocumentPrivate::clearAndWaitForRequests()
startEventLoop = !m_executingPixmapRequests.empty();
if (m_generator->hasFeature(Generator::SupportsCancelling)) {
for (PixmapRequest *executingRequest : qAsConst(m_executingPixmapRequests)) {
for (PixmapRequest *executingRequest : std::as_const(m_executingPixmapRequests)) {
executingRequest->d->mShouldAbortRender = 1;
}
@ -2281,21 +2286,7 @@ QString DocumentPrivate::docDataFileName(const QUrl &url, qint64 document_size)
QDir().mkpath(docdataDir);
}
QString newokularfile = docdataDir + QLatin1Char('/') + fn;
// we don't want to accidentally migrate old files when running unit tests
if (!QFile::exists(newokularfile) && !QStandardPaths::isTestModeEnabled()) {
// see if an KDE4 file still exists
static Kdelibs4Migration k4migration;
QString oldfile = k4migration.locateLocal("data", QStringLiteral("okular/docdata/") + fn);
if (oldfile.isEmpty()) {
oldfile = k4migration.locateLocal("data", QStringLiteral("kpdf/") + fn);
}
if (!oldfile.isEmpty() && QFile::exists(oldfile)) {
// ### copy or move?
if (!QFile::copy(oldfile, newokularfile)) {
return QString();
}
}
}
return newokularfile;
}
@ -2303,7 +2294,7 @@ QVector<KPluginMetaData> DocumentPrivate::availableGenerators()
{
static QVector<KPluginMetaData> result;
if (result.isEmpty()) {
result = KPluginLoader::findPlugins(QStringLiteral("okular/generators"));
result = KPluginMetaData::findPlugins(QStringLiteral("okular_generators"));
}
return result;
}
@ -2351,7 +2342,7 @@ KPluginMetaData DocumentPrivate::generatorForMimeType(const QMimeType &type, QWi
// sort the offers: the offers with an higher priority come before
auto cmp = [](const KPluginMetaData &s1, const KPluginMetaData &s2) {
const QString property = QStringLiteral("X-KDE-Priority");
return s1.rawData()[property].toInt() > s2.rawData()[property].toInt();
return s1.rawData().value(property).toInt() > s2.rawData().value(property).toInt();
};
std::stable_sort(offers.begin(), offers.end(), cmp);
@ -2381,7 +2372,7 @@ Document::OpenResult Document::openDocument(const QString &docFile, const QUrl &
int fd = -1;
if (url.scheme() == QLatin1String("fd")) {
bool ok;
fd = url.path().midRef(1).toInt(&ok);
fd = QStringView {url.path()}.mid(1).toInt(&ok);
if (!ok) {
return OpenError;
}
@ -2508,11 +2499,10 @@ Document::OpenResult Document::openDocument(const QString &docFile, const QUrl &
d->m_pageController = new PageController();
connect(d->m_pageController, &PageController::rotationFinished, this, [this](int p, Okular::Page *op) { d->rotationFinished(p, op); });
for (Page *p : qAsConst(d->m_pagesVector)) {
for (Page *p : std::as_const(d->m_pagesVector)) {
p->d->m_doc = d;
}
d->m_metadataLoadingCompleted = false;
d->m_docdataMigrationNeeded = false;
// 2. load Additional Data (bookmarks, local annotations and metadata) about the document
@ -2528,7 +2518,6 @@ Document::OpenResult Document::openDocument(const QString &docFile, const QUrl &
d->loadDocumentInfo(LoadGeneralInfo);
}
d->m_metadataLoadingCompleted = true;
d->m_bookmarkManager->setUrl(d->m_url);
// 3. setup observers internal lists and data
@ -2567,7 +2556,7 @@ Document::OpenResult Document::openDocument(const QString &docFile, const QUrl &
d->m_nextDocumentDestination = QString();
}
AudioPlayer::instance()->d->m_currentDocument = fromFileDescriptor ? QUrl() : d->m_url;
AudioPlayer::instance()->setDocument(fromFileDescriptor ? QUrl() : d->m_url, this);
const QStringList docScripts = d->m_generator->metaData(QStringLiteral("DocumentScripts"), QStringLiteral("JavaScript")).toStringList();
if (!docScripts.isEmpty()) {
@ -2652,7 +2641,7 @@ void Document::closeDocument()
// ideally we would just do that in the BackendOpaqueAction destructor
// but that's too late in the cleanup process, i.e. the generator has already closed its document
// and the document generator is nullptr
for (Page *p : qAsConst(d->m_pagesVector)) {
for (Page *p : std::as_const(d->m_pagesVector)) {
const QList<ObjectRect *> &oRects = p->objectRects();
for (ObjectRect *oRect : oRects) {
if (oRect->objectType() == ObjectRect::Action) {
@ -2754,7 +2743,7 @@ void Document::closeDocument()
// reset internal variables
d->m_viewportHistory.clear();
d->m_viewportHistory.emplace_back(DocumentViewport());
d->m_viewportHistory.emplace_back();
d->m_viewportIterator = d->m_viewportHistory.begin();
d->m_allocatedPixmapsTotalMemory = 0;
d->m_allocatedTextPagesFifo.clear();
@ -2764,7 +2753,7 @@ void Document::closeDocument()
d->m_documentInfo = DocumentInfo();
d->m_documentInfoAskedKeys.clear();
AudioPlayer::instance()->d->m_currentDocument = QUrl();
AudioPlayer::instance()->resetDocument();
d->m_undoStack->clear();
d->m_docdataMigrationNeeded = false;
@ -2812,7 +2801,7 @@ void Document::removeObserver(DocumentObserver *pObserver)
}
}
for (PixmapRequest *executingRequest : qAsConst(d->m_executingPixmapRequests)) {
for (PixmapRequest *executingRequest : std::as_const(d->m_executingPixmapRequests)) {
if (executingRequest->observer() == pObserver) {
d->cancelRenderingBecauseOf(executingRequest, nullptr);
}
@ -2931,10 +2920,10 @@ DocumentInfo Document::documentInfo(const QSet<DocumentInfo::Key> &keys) const
info.set(DocumentInfo::Pages, QString::number(this->pages()));
}
d->m_documentInfo.d->values.unite(info.d->values);
d->m_documentInfo.d->titles.unite(info.d->titles);
result.d->values.unite(info.d->values);
result.d->titles.unite(info.d->titles);
d->m_documentInfo.d->values.insert(info.d->values);
d->m_documentInfo.d->titles.insert(info.d->titles);
result.d->values.insert(info.d->values);
result.d->titles.insert(info.d->titles);
}
d->m_documentInfoAskedKeys += keys;
@ -3022,7 +3011,7 @@ void Document::setVisiblePageRects(const QVector<VisiblePageRect *> &visiblePage
}
d->m_pageRects = visiblePageRects;
// notify change to all other (different from id) observers
for (DocumentObserver *o : qAsConst(d->m_observers)) {
for (DocumentObserver *o : std::as_const(d->m_observers)) {
if (o != excludeObserver) {
o->notifyVisibleRectsChanged();
}
@ -3403,7 +3392,7 @@ void Document::requestPixmaps(const QList<PixmapRequest *> &requests, PixmapRequ
// 1.C [CANCEL REQUESTS] cancel those requests that are running and should be cancelled because of the new requests coming in
if (d->m_generator->hasFeature(Generator::SupportsCancelling)) {
for (PixmapRequest *executingRequest : qAsConst(d->m_executingPixmapRequests)) {
for (PixmapRequest *executingRequest : std::as_const(d->m_executingPixmapRequests)) {
bool newRequestsContainExecutingRequestPage = false;
bool requestCancelled = false;
for (PixmapRequest *newRequest : requests) {
@ -3452,7 +3441,7 @@ void Document::requestPixmaps(const QList<PixmapRequest *> &requests, PixmapRequ
// if ( generator->canRequestPixmap() )
d->sendGeneratorPixmapRequest();
for (DocumentObserver *o : qAsConst(observersPixmapCleared)) {
for (DocumentObserver *o : std::as_const(observersPixmapCleared)) {
o->notifyContentsCleared(Okular::DocumentObserver::Pixmap);
}
}
@ -3633,7 +3622,7 @@ bool DocumentPrivate::canRemoveExternalAnnotations() const
return false;
}
void Document::setPageTextSelection(int page, RegularAreaRect *rect, const QColor &color)
void Document::setPageTextSelection(int page, std::unique_ptr<RegularAreaRect> &&rect, const QColor &color)
{
Page *kp = d->m_pagesVector[page];
if (!d->m_generator || !kp) {
@ -3642,7 +3631,7 @@ void Document::setPageTextSelection(int page, RegularAreaRect *rect, const QColo
// add or remove the selection basing whether rect is null or not
if (rect) {
kp->d->setTextSelections(rect, color);
kp->d->setTextSelections(*rect, color);
} else {
kp->d->deleteTextSelections();
}
@ -3677,7 +3666,7 @@ void Document::setPrevPage()
}
*/
void Document::setViewportWithHistory(const DocumentViewport &viewport, DocumentObserver *excludeObserver, bool smoothMove, bool updateHistory)
void Document::setViewport(const DocumentViewport &viewport, DocumentObserver *excludeObserver, bool smoothMove, bool updateHistory)
{
if (!viewport.isValid()) {
qCDebug(OkularCoreDebug) << "invalid viewport:" << viewport.toString();
@ -3718,7 +3707,7 @@ void Document::setViewportWithHistory(const DocumentViewport &viewport, Document
const bool currentPageChanged = (oldPageNumber != currentViewportPage);
// notify change to all other (different from id) observers
for (DocumentObserver *o : qAsConst(d->m_observers)) {
for (DocumentObserver *o : std::as_const(d->m_observers)) {
if (o != excludeObserver) {
o->notifyViewportChanged(smoothMove);
}
@ -3742,16 +3731,10 @@ void Document::setViewportPage(int page, DocumentObserver *excludeObserver, bool
setViewport(DocumentViewport(page), excludeObserver, smoothMove);
}
void Document::setViewport(const DocumentViewport &viewport, DocumentObserver *excludeObserver, bool smoothMove)
{
// set viewport, updating history
setViewportWithHistory(viewport, excludeObserver, smoothMove, true);
}
void Document::setZoom(int factor, DocumentObserver *excludeObserver)
{
// notify change to all other (different from id) observers
for (DocumentObserver *o : qAsConst(d->m_observers)) {
for (DocumentObserver *o : std::as_const(d->m_observers)) {
if (o != excludeObserver) {
o->notifyZoom(factor);
}
@ -3835,7 +3818,7 @@ void Document::searchText(int searchID, const QString &text, bool fromStart, Qt:
// remove highlights from pages and queue them for notifying changes
*pagesToNotify += s->highlightedPages;
for (const int pageNumber : qAsConst(s->highlightedPages)) {
for (const int pageNumber : std::as_const(s->highlightedPages)) {
d->m_pagesVector.at(pageNumber)->d->deleteHighlights(searchID);
}
s->highlightedPages.clear();
@ -3892,7 +3875,7 @@ void Document::searchText(int searchID, const QString &text, bool fromStart, Qt:
// 4. GOOGLE* - process all document marking pages
else if (type == GoogleAll || type == GoogleAny) {
QMap<Page *, QVector<QPair<RegularAreaRect *, QColor>>> *pageMatches = new QMap<Page *, QVector<QPair<RegularAreaRect *, QColor>>>;
const QStringList words = text.split(QLatin1Char(' '), QString::SkipEmptyParts);
const QStringList words = text.split(QLatin1Char(' '), Qt::SkipEmptyParts);
// search and highlight every word in 'text' on all pages
QTimer::singleShot(0, this, [this, pagesToNotify, pageMatches, searchID, words] { d->doContinueGooglesDocumentSearch(pagesToNotify, pageMatches, 0, searchID, words); });
@ -3948,7 +3931,7 @@ void Document::resetSearch(int searchID)
RunningSearch *s = *searchIt;
// unhighlight pages and inform observers about that
for (const int pageNumber : qAsConst(s->highlightedPages)) {
for (const int pageNumber : std::as_const(s->highlightedPages)) {
d->m_pagesVector.at(pageNumber)->d->deleteHighlights(searchID);
foreachObserver(notifyPageChanged(pageNumber, DocumentObserver::Highlights));
}
@ -4085,9 +4068,35 @@ QString Document::bookmarkedPageRange() const
return range;
}
struct ExecuteNextActionsHelper : public QObject {
struct ExecuteNextActionsHelper : public QObject, private DocumentObserver {
Q_OBJECT
public:
explicit ExecuteNextActionsHelper(Document *doc)
: m_doc(doc)
{
doc->addObserver(this);
connect(doc, &Document::aboutToClose, this, [this] { b = false; });
}
~ExecuteNextActionsHelper() override
{
m_doc->removeObserver(this);
}
void notifySetup(const QVector<Okular::Page *> & /*pages*/, int setupFlags) override
{
if (setupFlags == DocumentChanged || setupFlags == UrlChanged) {
b = false;
}
}
bool shouldExecuteNextAction() const
{
return b;
}
private:
Document *const m_doc;
bool b = true;
};
@ -4098,8 +4107,7 @@ void Document::processAction(const Action *action)
}
// Don't execute next actions if the action itself caused the closing of the document
ExecuteNextActionsHelper executeNextActions;
connect(this, &Document::aboutToClose, &executeNextActions, [&executeNextActions] { executeNextActions.b = false; });
const ExecuteNextActionsHelper executeNextActionsHelper(this);
switch (action->actionType()) {
case Action::Goto: {
@ -4148,13 +4156,13 @@ void Document::processAction(const Action *action)
QMimeDatabase db;
QMimeType mime = db.mimeTypeForUrl(url);
// Check executables
if (KRun::isExecutableFile(url, mime.name())) {
if (KIO::OpenUrlJob::isExecutableFile(url, mime.name())) {
// Don't have any pdf that uses this code path, just a guess on how it should work
if (!exe->parameters().isEmpty()) {
url = d->giveAbsoluteUrl(exe->parameters());
mime = db.mimeTypeForUrl(url);
if (KRun::isExecutableFile(url, mime.name())) {
if (KIO::OpenUrlJob::isExecutableFile(url, mime.name())) {
// this case is a link pointing to an executable with a parameter
// that also is an executable, possibly a hand-crafted pdf
Q_EMIT error(i18n("The document is trying to execute an external application and, for your safety, Okular does not allow that."), -1);
@ -4168,14 +4176,14 @@ void Document::processAction(const Action *action)
}
}
KService::Ptr ptr = KApplicationTrader::preferredService(mime.name());
if (ptr) {
QList<QUrl> lst;
lst.append(url);
KRun::runService(*ptr, lst, nullptr);
} else {
Q_EMIT error(i18n("No application found for opening file of mimetype %1.", mime.name()), -1);
}
KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url, mime.name());
job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, d->m_widget));
job->start();
connect(job, &KIO::OpenUrlJob::result, this, [this, mime](KJob *job) {
if (job->error()) {
Q_EMIT error(i18n("No application found for opening file of mimetype %1.", mime.name()), -1);
}
});
} break;
case Action::DocAction: {
@ -4250,11 +4258,16 @@ void Document::processAction(const Action *action)
}
// handle documents with relative path
QUrl realUrl;
if (d->m_url.isValid()) {
const QUrl realUrl = KIO::upUrl(d->m_url).resolved(url);
// KRun autodeletes
KRun *r = new KRun(realUrl, d->m_widget);
r->setRunExecutables(false);
realUrl = KIO::upUrl(d->m_url).resolved(url);
} else if (!url.isRelative()) {
realUrl = url;
}
if (realUrl.isValid()) {
auto *job = new KIO::OpenUrlJob(realUrl);
job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, d->m_widget.data()));
job->start();
}
}
} break;
@ -4291,7 +4304,7 @@ void Document::processAction(const Action *action)
} break;
}
if (executeNextActions.b) {
if (executeNextActionsHelper.shouldExecuteNextAction()) {
const QVector<Action *> nextActions = action->nextActions();
for (const Action *a : nextActions) {
processAction(a);
@ -4345,25 +4358,25 @@ void Document::processFormatAction(const Action *action, Okular::FormFieldText *
QString DocumentPrivate::diff(const QString &oldVal, const QString &newVal)
{
QString diff;
// We need to consider unicode surrogate pairs and others so working
// with QString directly, even with the private QStringIterator is
// not that simple to get right
// so let's just convert to ucs4
// also, given that toUcs4 is either a QList or a QVector depending on
// qt version, let's try keep it very auto-typed to ease Qt6 porting
QStringIterator oldIt(oldVal);
QStringIterator newIt(newVal);
auto oldUcs4 = oldVal.toStdU32String();
auto newUcs4 = newVal.toStdU32String();
while (oldIt.hasNext() && newIt.hasNext()) {
QChar oldToken = oldIt.next();
QChar newToken = newIt.next();
if (oldToken != newToken) {
diff += newToken;
break;
for (size_t i = 0; i < std::min(oldUcs4.size(), newUcs4.size()); i++) {
if (oldUcs4.at(i) != newUcs4.at(i)) {
return QString::fromUcs4(std::u32string_view {newUcs4}.substr(i).data(), newUcs4.size() - i);
}
}
while (newIt.hasNext()) {
diff += newIt.next();
if (oldUcs4.size() < newUcs4.size()) {
return QString::fromUcs4(std::u32string_view {newUcs4}.substr(oldUcs4.size()).data(), newUcs4.size() - oldUcs4.size());
}
return diff;
return {};
}
void Document::processKeystrokeAction(const Action *action, Okular::FormFieldText *fft, const QVariant &newValue)
@ -4466,6 +4479,27 @@ void Document::processValidateAction(const Action *action, Okular::FormFieldText
returnCode = event->returnCode();
}
void Document::processFormMouseUpScripAction(const Action *action, Okular::FormField *ff)
{
if (!action || action->actionType() != Action::Script) {
return;
}
// Lookup the page of the FormFieldText
int foundPage = d->findFieldPageNumber(ff);
if (foundPage == -1) {
qCDebug(OkularCoreDebug) << "Could not find page for formfield!";
return;
}
std::shared_ptr<Event> event = Event::createFieldMouseUpEvent(ff, d->m_pagesVector[foundPage]);
const ScriptAction *linkscript = static_cast<const ScriptAction *>(action);
d->executeScriptEvent(event, linkscript);
}
void Document::processSourceReference(const SourceReference *ref)
{
if (!ref) {
@ -4687,7 +4721,7 @@ QVector<KPluginMetaData> DocumentPrivate::configurableGenerators()
const QVector<KPluginMetaData> available = availableGenerators();
QVector<KPluginMetaData> result;
for (const KPluginMetaData &md : available) {
if (md.rawData()[QStringLiteral("X-KDE-okularHasInternalSettings")].toBool()) {
if (md.rawData().value(QStringLiteral("X-KDE-okularHasInternalSettings")).toBool()) {
result << md;
}
}
@ -4723,7 +4757,7 @@ QStringList Document::supportedMimeTypes() const
// Remove duplicate mimetypes represented by different names
QMimeDatabase mimeDatabase;
QSet<QMimeType> uniqueMimetypes;
for (const QString &mimeName : qAsConst(result)) {
for (const QString &mimeName : std::as_const(result)) {
uniqueMimetypes.insert(mimeDatabase.mimeTypeForName(mimeName));
}
result.clear();
@ -5212,7 +5246,7 @@ bool Document::extractArchivedFile(const QString &destFileName)
return d->m_archiveData->document.copy(destFileName);
}
QPrinter::Orientation Document::orientation() const
QPageLayout::Orientation Document::orientation() const
{
double width, height;
int landscape, portrait;
@ -5227,7 +5261,7 @@ QPrinter::Orientation Document::orientation() const
width = currentPage->width();
height = currentPage->height();
if (currentPage->orientation() == Okular::Rotation90 || currentPage->orientation() == Okular::Rotation270) {
qSwap(width, height);
std::swap(width, height);
}
if (width > height) {
landscape++;
@ -5235,7 +5269,7 @@ QPrinter::Orientation Document::orientation() const
portrait++;
}
}
return (landscape > portrait) ? QPrinter::Landscape : QPrinter::Portrait;
return (landscape > portrait) ? QPageLayout::Landscape : QPageLayout::Portrait;
}
void Document::setAnnotationEditingEnabled(bool enable)
@ -5890,7 +5924,10 @@ struct Okular::NewSignatureDataPrivate {
QString certSubjectCommonName;
QString password;
QString documentPassword;
int page;
QString location;
QString reason;
QString backgroundImagePath;
int page = -1;
NormalizedRect boundingRectangle;
};
@ -5964,6 +6001,36 @@ void NewSignatureData::setDocumentPassword(const QString &password)
d->documentPassword = password;
}
QString NewSignatureData::location() const
{
return d->location;
}
void NewSignatureData::setLocation(const QString &location)
{
d->location = location;
}
QString NewSignatureData::reason() const
{
return d->reason;
}
void NewSignatureData::setReason(const QString &reason)
{
d->reason = reason;
}
QString Okular::NewSignatureData::backgroundImagePath() const
{
return d->backgroundImagePath;
}
void Okular::NewSignatureData::setBackgroundImagePath(const QString &path)
{
d->backgroundImagePath = path;
}
#undef foreachObserver
#undef foreachObserverD

View File

@ -24,6 +24,7 @@
#include <QMimeType>
#include <QUrl>
#include <QVariant>
class KConfigDialog;
class KPluginMetaData;
@ -433,19 +434,9 @@ public:
* @param viewport The document viewport.
* @param excludeObserver The observer which shouldn't be effected by this change.
* @param smoothMove Whether the move shall be animated smoothly.
*/
void setViewport(const DocumentViewport &viewport, DocumentObserver *excludeObserver = nullptr, bool smoothMove = false);
/**
* Sets the current document viewport to the given @p viewport.
* BCI TODO: merge with setViewport, adding a parameter "bool updateHistory = true"
*
* @param viewport The document viewport.
* @param excludeObserver The observer which shouldn't be effected by this change.
* @param smoothMove Whether the move shall be animated smoothly.
* @param updateHistory Whether to consider the change of viewport for the history navigation
*/
void setViewportWithHistory(const DocumentViewport &viewport, DocumentObserver *excludeObserver = nullptr, bool smoothMove = false, bool updateHistory = true);
void setViewport(const DocumentViewport &viewport, DocumentObserver *excludeObserver = nullptr, bool smoothMove = false, bool updateHistory = true);
/**
* Sets the current document viewport to the next viewport in the
@ -601,7 +592,7 @@ public:
* @param rect The rectangle of the selection.
* @param color The color of the selection.
*/
void setPageTextSelection(int page, RegularAreaRect *rect, const QColor &color);
void setPageTextSelection(int page, std::unique_ptr<RegularAreaRect> &&rect, const QColor &color);
/**
* Returns true if there is an undo command available; otherwise returns false.
@ -712,6 +703,13 @@ public:
*/
void processValidateAction(const Action *action, Okular::FormFieldText *fft, bool &returnCode);
/**
* Processes the mouse up @p action on @p ff.
*
* @since 23.12
*/
void processFormMouseUpScripAction(const Action *action, Okular::FormField *ff);
/**
* Returns a list of the bookmarked.pages
*/
@ -962,7 +960,7 @@ public:
*
* @since 0.14 (KDE 4.8)
*/
QPrinter::Orientation orientation() const;
QPageLayout::Orientation orientation() const;
/**
* Control annotation editing (creation, modification and removal),
@ -1565,6 +1563,24 @@ public:
/// @since 22.04
void setDocumentPassword(const QString &password);
/// @since 23.08
QString reason() const;
/// @since 23.08
void setReason(const QString &reason);
/// @since 23.08
QString location() const;
/// @since 23.08
void setLocation(const QString &location);
/// @since 23.08
QString backgroundImagePath() const;
/// @since 23.08
void setBackgroundImagePath(const QString &path);
private:
NewSignatureDataPrivate *const d;
};

View File

@ -106,6 +106,7 @@ class DocumentPrivate
public:
explicit DocumentPrivate(Document *parent)
: m_parent(parent)
, m_searchCancelled(false)
, m_tempFile(nullptr)
, m_docSize(-1)
, m_allocatedPixmapsTotalMemory(0)
@ -126,6 +127,7 @@ public:
, m_fontsCached(false)
, m_annotationEditingEnabled(true)
, m_annotationBeingModified(false)
, m_undoStack(nullptr)
, m_docdataMigrationNeeded(false)
, m_synctex_scanner(nullptr)
{
@ -326,7 +328,6 @@ public:
bool m_annotationEditingEnabled;
bool m_annotationBeingModified; // is an annotation currently being moved or resized?
bool m_metadataLoadingCompleted;
QUndoStack *m_undoStack;
QDomNode m_prevPropsOfAnnotBeingModified;

View File

@ -337,14 +337,16 @@ EditTextCommand::EditTextCommand(const QString &newContents, int newCursorPos, c
if (m_prevCursorPos != m_prevAnchorPos) {
qCDebug(OkularCoreDebug) << "OtherEdit, selection";
m_editType = OtherEdit;
} else if (newContentsRightOfCursor() == oldContentsRightOfCursor() && newContentsLeftOfCursor() == oldContentsLeftOfCursor().left(oldContentsLeftOfCursor().length() - 1) && oldContentsLeftOfCursor().rightRef(1) != QLatin1Char('\n')) {
} else if (newContentsRightOfCursor() == oldContentsRightOfCursor() && newContentsLeftOfCursor() == oldContentsLeftOfCursor().left(oldContentsLeftOfCursor().length() - 1) &&
QStringView {oldContentsLeftOfCursor()}.right(1) != QLatin1Char('\n')) {
qCDebug(OkularCoreDebug) << "CharBackspace";
m_editType = CharBackspace;
} else if (newContentsLeftOfCursor() == oldContentsLeftOfCursor() && newContentsRightOfCursor() == oldContentsRightOfCursor().right(oldContentsRightOfCursor().length() - 1) &&
oldContentsRightOfCursor().leftRef(1) != QLatin1Char('\n')) {
QStringView {oldContentsRightOfCursor()}.left(1) != QLatin1Char('\n')) {
qCDebug(OkularCoreDebug) << "CharDelete";
m_editType = CharDelete;
} else if (newContentsRightOfCursor() == oldContentsRightOfCursor() && newContentsLeftOfCursor().left(newContentsLeftOfCursor().length() - 1) == oldContentsLeftOfCursor() && newContentsLeftOfCursor().rightRef(1) != QLatin1Char('\n')) {
} else if (newContentsRightOfCursor() == oldContentsRightOfCursor() && newContentsLeftOfCursor().left(newContentsLeftOfCursor().length() - 1) == oldContentsLeftOfCursor() &&
QStringView {newContentsLeftOfCursor()}.right(1) != QLatin1Char('\n')) {
qCDebug(OkularCoreDebug) << "CharInsert";
m_editType = CharInsert;
} else {
@ -598,34 +600,23 @@ EditFormButtonsCommand::EditFormButtonsCommand(Okular::DocumentPrivate *docPriv,
, m_prevButtonStates(QList<bool>())
{
setText(i18nc("Edit the state of a group of form buttons", "edit form button states"));
for (const FormFieldButton *formButton : qAsConst(m_formButtons)) {
for (const FormFieldButton *formButton : std::as_const(m_formButtons)) {
m_prevButtonStates.append(formButton->state());
m_pageNumbers.append(formButton->page()->number());
}
}
void EditFormButtonsCommand::undo()
{
clearFormButtonStates();
QSet<int> extraPages;
for (int i = 0; i < m_formButtons.size(); i++) {
bool checked = m_prevButtonStates.at(i);
if (checked) {
m_formButtons.at(i)->setState(checked);
}
}
Okular::NormalizedRect boundingRect = buildBoundingRectangleForButtons(m_formButtons);
moveViewportIfBoundingRectNotFullyVisible(boundingRect, m_docPriv, m_pageNumber);
Q_EMIT m_docPriv->m_parent->formButtonsChangedByUndoRedo(m_pageNumber, m_formButtons);
m_docPriv->notifyFormChanges(m_pageNumber);
}
void EditFormButtonsCommand::redo()
{
clearFormButtonStates();
for (int i = 0; i < m_formButtons.size(); i++) {
bool checked = m_newButtonStates.at(i);
if (checked) {
m_formButtons.at(i)->setState(checked);
if (m_pageNumbers.at(i) != m_pageNumber) {
extraPages << m_pageNumbers.at(i);
}
}
@ -633,14 +624,40 @@ void EditFormButtonsCommand::redo()
moveViewportIfBoundingRectNotFullyVisible(boundingRect, m_docPriv, m_pageNumber);
Q_EMIT m_docPriv->m_parent->formButtonsChangedByUndoRedo(m_pageNumber, m_formButtons);
m_docPriv->notifyFormChanges(m_pageNumber);
for (auto page : std::as_const(extraPages)) {
m_docPriv->notifyFormChanges(page);
}
}
void EditFormButtonsCommand::redo()
{
clearFormButtonStates();
QSet<int> extraPages;
for (int i = 0; i < m_formButtons.size(); i++) {
bool checked = m_newButtonStates.at(i);
if (checked) {
m_formButtons.at(i)->setState(checked);
}
if (m_pageNumbers.at(i) != m_pageNumber) {
extraPages << m_pageNumbers.at(i);
}
}
Okular::NormalizedRect boundingRect = buildBoundingRectangleForButtons(m_formButtons);
moveViewportIfBoundingRectNotFullyVisible(boundingRect, m_docPriv, m_pageNumber);
Q_EMIT m_docPriv->m_parent->formButtonsChangedByUndoRedo(m_pageNumber, m_formButtons);
m_docPriv->notifyFormChanges(m_pageNumber);
for (auto page : std::as_const(extraPages)) {
m_docPriv->notifyFormChanges(page);
}
}
bool EditFormButtonsCommand::refreshInternalPageReferences(const QVector<Okular::Page *> &newPagesVector)
{
const QList<FormFieldButton *> oldFormButtons = m_formButtons;
m_formButtons.clear();
for (FormFieldButton *oldFormButton : oldFormButtons) {
FormFieldButton *button = dynamic_cast<FormFieldButton *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumber], oldFormButton));
for (int i = 0; i < oldFormButtons.size(); i++) {
FormFieldButton *button = dynamic_cast<FormFieldButton *>(Okular::PagePrivate::findEquivalentForm(newPagesVector[m_pageNumbers[i]], oldFormButtons[i]));
if (!button) {
return false;
}
@ -652,7 +669,7 @@ bool EditFormButtonsCommand::refreshInternalPageReferences(const QVector<Okular:
void EditFormButtonsCommand::clearFormButtonStates()
{
for (FormFieldButton *formButton : qAsConst(m_formButtons)) {
for (FormFieldButton *formButton : std::as_const(m_formButtons)) {
formButton->setState(false);
}
}

View File

@ -250,6 +250,7 @@ private:
Okular::DocumentPrivate *m_docPriv;
int m_pageNumber;
QList<FormFieldButton *> m_formButtons;
QList<int> m_pageNumbers;
QList<bool> m_newButtonStates;
QList<bool> m_prevButtonStates;
};

View File

@ -28,7 +28,7 @@
using namespace Okular;
Document::PrintError
FilePrinter::printFile(QPrinter &printer, const QString &file, QPrinter::Orientation documentOrientation, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, ScaleMode scaleMode)
FilePrinter::printFile(QPrinter &printer, const QString &file, QPageLayout::Orientation documentOrientation, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, ScaleMode scaleMode)
{
FilePrinter fp;
return fp.doPrintFiles(printer, QStringList(file), fileDeletePolicy, pageSelectPolicy, pageRange, documentOrientation, scaleMode);
@ -51,7 +51,7 @@ static Document::PrintError doKProcessExecute(const QString &exe, const QStringL
}
Document::PrintError
FilePrinter::doPrintFiles(QPrinter &printer, const QStringList &fileList, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, QPrinter::Orientation documentOrientation, ScaleMode scaleMode)
FilePrinter::doPrintFiles(QPrinter &printer, const QStringList &fileList, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, QPageLayout::Orientation documentOrientation, ScaleMode scaleMode)
{
if (fileList.size() < 1) {
return Document::NoFileToPrintError;
@ -144,11 +144,6 @@ FilePrinter::doPrintFiles(QPrinter &printer, const QStringList &fileList, FileDe
return ret;
}
QList<int> FilePrinter::pageList(QPrinter &printer, int lastPage, const QList<int> &selectedPageList)
{
return pageList(printer, lastPage, 0, selectedPageList);
}
QList<int> FilePrinter::pageList(QPrinter &printer, int lastPage, int currentPage, const QList<int> &selectedPageList)
{
if (printer.printRange() == QPrinter::Selection) {
@ -176,50 +171,6 @@ QList<int> FilePrinter::pageList(QPrinter &printer, int lastPage, int currentPag
return list;
}
QString FilePrinter::pageRange(QPrinter &printer, int lastPage, const QList<int> &selectedPageList)
{
if (printer.printRange() == QPrinter::Selection) {
return pageListToPageRange(selectedPageList);
}
if (printer.printRange() == QPrinter::PageRange) {
return QStringLiteral("%1-%2").arg(printer.fromPage()).arg(printer.toPage());
}
return QStringLiteral("1-%2").arg(lastPage);
}
QString FilePrinter::pageListToPageRange(const QList<int> &pageList)
{
QString pageRange;
int count = pageList.count();
int i = 0;
int seqStart = i;
int seqEnd;
while (i != count) {
if (i + 1 == count || pageList[i] + 1 != pageList[i + 1]) {
seqEnd = i;
if (!pageRange.isEmpty()) {
pageRange.append(QLatin1Char(','));
}
if (seqStart == seqEnd) {
pageRange.append(pageList[i]);
} else {
pageRange.append(QStringLiteral("%1-%2").arg(seqStart).arg(seqEnd));
}
seqStart = i + 1;
}
i++;
}
return pageRange;
}
bool FilePrinter::ps2pdfAvailable()
{
return (!QStandardPaths::findExecutable(QStringLiteral("ps2pdf")).isEmpty());
@ -235,68 +186,23 @@ bool FilePrinter::cupsAvailable()
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
// Ideally we would have access to the private Qt method
// QCUPSSupport::cupsAvailable() to do this as it is very complex routine.
// However, if CUPS is available then QPrinter::numCopies() will always return 1
// whereas if CUPS is not available it will return the real number of copies.
// However, if CUPS is available then QPrinter::supportsMultipleCopies() will always return true
// whereas if CUPS is not available it will return false.
// This behaviour is guaranteed never to change, so we can use it as a reliable substitute.
QPrinter testPrinter;
testPrinter.setNumCopies(2);
return (testPrinter.numCopies() == 1);
return testPrinter.supportsMultipleCopies();
#else
return false;
#endif
}
bool FilePrinter::detectCupsService()
{
QTcpSocket qsock;
qsock.connectToHost(QStringLiteral("localhost"), 631);
bool rtn = qsock.waitForConnected() && qsock.isValid();
qsock.abort();
return rtn;
}
bool FilePrinter::detectCupsConfig()
{
if (QFile::exists(QStringLiteral("/etc/cups/cupsd.conf"))) {
return true;
}
if (QFile::exists(QStringLiteral("/usr/etc/cups/cupsd.conf"))) {
return true;
}
if (QFile::exists(QStringLiteral("/usr/local/etc/cups/cupsd.conf"))) {
return true;
}
if (QFile::exists(QStringLiteral("/opt/etc/cups/cupsd.conf"))) {
return true;
}
if (QFile::exists(QStringLiteral("/opt/local/etc/cups/cupsd.conf"))) {
return true;
}
return false;
}
QSize FilePrinter::psPaperSize(QPrinter &printer)
{
QSize size = printer.pageLayout().pageSize().sizePoints();
if (printer.pageSize() == QPrinter::Custom) {
return QSize((int)printer.widthMM() * (25.4 / 72), (int)printer.heightMM() * (25.4 / 72));
}
if (printer.orientation() == QPrinter::Landscape) {
size.transpose();
}
return size;
}
QStringList FilePrinter::printArguments(QPrinter &printer,
FileDeletePolicy fileDeletePolicy,
PageSelectPolicy pageSelectPolicy,
bool useCupsOptions,
const QString &pageRange,
const QString &version,
QPrinter::Orientation documentOrientation,
QPageLayout::Orientation documentOrientation,
ScaleMode scaleMode)
{
QStringList argList;
@ -347,7 +253,7 @@ QStringList FilePrinter::destination(QPrinter &printer, const QString &version)
QStringList FilePrinter::copies(QPrinter &printer, const QString &version)
{
int cp = printer.actualNumCopies();
int cp = printer.copyCount();
if (version == QLatin1String("lp")) {
return QStringList(QStringLiteral("-n")) << QStringLiteral("%1").arg(cp);
@ -412,7 +318,7 @@ QStringList FilePrinter::pages(QPrinter &printer, PageSelectPolicy pageSelectPol
return QStringList(); // AllPages
}
QStringList FilePrinter::cupsOptions(QPrinter &printer, QPrinter::Orientation documentOrientation, ScaleMode scaleMode)
QStringList FilePrinter::cupsOptions(QPrinter &printer, QPageLayout::Orientation documentOrientation, ScaleMode scaleMode)
{
QStringList optionList;
@ -464,68 +370,68 @@ QStringList FilePrinter::optionMedia(QPrinter &printer)
QString FilePrinter::mediaPageSize(QPrinter &printer)
{
switch (printer.pageSize()) {
case QPrinter::A0:
switch (printer.pageLayout().pageSize().id()) {
case QPageSize::A0:
return QStringLiteral("A0");
case QPrinter::A1:
case QPageSize::A1:
return QStringLiteral("A1");
case QPrinter::A2:
case QPageSize::A2:
return QStringLiteral("A2");
case QPrinter::A3:
case QPageSize::A3:
return QStringLiteral("A3");
case QPrinter::A4:
case QPageSize::A4:
return QStringLiteral("A4");
case QPrinter::A5:
case QPageSize::A5:
return QStringLiteral("A5");
case QPrinter::A6:
case QPageSize::A6:
return QStringLiteral("A6");
case QPrinter::A7:
case QPageSize::A7:
return QStringLiteral("A7");
case QPrinter::A8:
case QPageSize::A8:
return QStringLiteral("A8");
case QPrinter::A9:
case QPageSize::A9:
return QStringLiteral("A9");
case QPrinter::B0:
case QPageSize::B0:
return QStringLiteral("B0");
case QPrinter::B1:
case QPageSize::B1:
return QStringLiteral("B1");
case QPrinter::B10:
case QPageSize::B10:
return QStringLiteral("B10");
case QPrinter::B2:
case QPageSize::B2:
return QStringLiteral("B2");
case QPrinter::B3:
case QPageSize::B3:
return QStringLiteral("B3");
case QPrinter::B4:
case QPageSize::B4:
return QStringLiteral("B4");
case QPrinter::B5:
case QPageSize::B5:
return QStringLiteral("B5");
case QPrinter::B6:
case QPageSize::B6:
return QStringLiteral("B6");
case QPrinter::B7:
case QPageSize::B7:
return QStringLiteral("B7");
case QPrinter::B8:
case QPageSize::B8:
return QStringLiteral("B8");
case QPrinter::B9:
case QPageSize::B9:
return QStringLiteral("B9");
case QPrinter::C5E:
case QPageSize::C5E:
return QStringLiteral("C5"); // Correct Translation?
case QPrinter::Comm10E:
case QPageSize::Comm10E:
return QStringLiteral("Comm10"); // Correct Translation?
case QPrinter::DLE:
case QPageSize::DLE:
return QStringLiteral("DL"); // Correct Translation?
case QPrinter::Executive:
case QPageSize::Executive:
return QStringLiteral("Executive");
case QPrinter::Folio:
case QPageSize::Folio:
return QStringLiteral("Folio");
case QPrinter::Ledger:
case QPageSize::Ledger:
return QStringLiteral("Ledger");
case QPrinter::Legal:
case QPageSize::Legal:
return QStringLiteral("Legal");
case QPrinter::Letter:
case QPageSize::Letter:
return QStringLiteral("Letter");
case QPrinter::Tabloid:
case QPageSize::Tabloid:
return QStringLiteral("Tabloid");
case QPrinter::Custom:
case QPageSize::Custom:
return QStringLiteral("Custom.%1x%2mm").arg(printer.widthMM()).arg(printer.heightMM());
default:
return QString();
@ -569,12 +475,12 @@ QString FilePrinter::mediaPaperSource(QPrinter &printer)
}
}
QStringList FilePrinter::optionOrientation(QPrinter &printer, QPrinter::Orientation documentOrientation)
QStringList FilePrinter::optionOrientation(QPrinter &printer, QPageLayout::Orientation documentOrientation)
{
// portrait and landscape options rotate the document according to the document orientation
// If we want to print a landscape document as one would expect it, we have to pass the
// portrait option so that the document is not rotated additionally
if (printer.orientation() == documentOrientation) {
if (printer.pageLayout().orientation() == documentOrientation) {
// the user wants the document printed as is
return QStringList(QStringLiteral("-o")) << QStringLiteral("portrait");
} else {
@ -589,7 +495,7 @@ QStringList FilePrinter::optionDoubleSidedPrinting(QPrinter &printer)
case QPrinter::DuplexNone:
return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=one-sided");
case QPrinter::DuplexAuto:
if (printer.orientation() == QPrinter::Landscape) {
if (printer.pageLayout().orientation() == QPageLayout::Landscape) {
return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-short-edge");
} else {
return QStringList(QStringLiteral("-o")) << QStringLiteral("sides=two-sided-long-edge");
@ -626,7 +532,11 @@ QStringList FilePrinter::optionPageMargins(QPrinter &printer, ScaleMode scaleMod
} else {
qreal l(0), t(0), r(0), b(0);
if (!printer.fullPage()) {
printer.getPageMargins(&l, &t, &r, &b, QPrinter::Point);
auto marginsf = printer.pageLayout().margins(QPageLayout::Point);
l = marginsf.left();
t = marginsf.top();
r = marginsf.right();
b = marginsf.bottom();
}
QStringList marginOptions;
marginOptions << (QStringLiteral("-o")) << QStringLiteral("page-left=%1").arg(l) << QStringLiteral("-o") << QStringLiteral("page-top=%1").arg(t) << QStringLiteral("-o") << QStringLiteral("page-right=%1").arg(r)

View File

@ -84,7 +84,7 @@ public:
*/
static Document::PrintError printFile(QPrinter &printer,
const QString &file,
QPrinter::Orientation documentOrientation,
QPageLayout::Orientation documentOrientation,
FileDeletePolicy fileDeletePolicy = FilePrinter::ApplicationDeletesFiles,
PageSelectPolicy pageSelectPolicy = FilePrinter::ApplicationSelectsPages,
const QString &pageRange = QString(),
@ -100,31 +100,6 @@ public:
*/
static QList<int> pageList(QPrinter &printer, int lastPage, int currentPage, const QList<int> &selectedPageList);
/** Return the list of pages selected by the user in the Print Dialog
*
* @param printer the print settings to use
* @param lastPage the last page number, needed if AllPages option is selected
* @param selectedPageList list of pages to use if Selection option is selected
* @returns Returns list of pages to print
*/
static QList<int> pageList(QPrinter &printer, int lastPage, const QList<int> &selectedPageList);
/** Return the range of pages selected by the user in the Print Dialog
*
* @param printer the print settings to use
* @param lastPage the last page number, needed if AllPages option is selected
* @param selectedPageList list of pages to use if Selection option is selected
* @returns Returns range of pages to print
*/
static QString pageRange(QPrinter &printer, int lastPage, const QList<int> &selectedPageList);
/** convert a Page List into a Page Range
*
* @param pageList list of pages to convert
* @returns Returns equivalent page range
*/
static QString pageListToPageRange(const QList<int> &pageList);
/** Return if Ghostscript ps2pdf is available on this system
*
* @returns Returns true if Ghostscript ps2pdf available
@ -137,24 +112,15 @@ public:
*/
static bool pdf2psAvailable();
private:
/** Return if CUPS Print System is available on this system
*
* @returns Returns true if CUPS available
*/
static bool cupsAvailable();
/** Returns the postscript standard page size
*
* @returns Returns paper size in ps points
*/
static QSize psPaperSize(QPrinter &printer);
protected:
bool detectCupsService();
bool detectCupsConfig();
Document::PrintError
doPrintFiles(QPrinter &printer, const QStringList &fileList, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, QPrinter::Orientation documentOrientation, ScaleMode scaleMode);
doPrintFiles(QPrinter &printer, const QStringList &fileList, FileDeletePolicy fileDeletePolicy, PageSelectPolicy pageSelectPolicy, const QString &pageRange, QPageLayout::Orientation documentOrientation, ScaleMode scaleMode);
/// @since 1.8
QStringList printArguments(QPrinter &printer,
@ -163,7 +129,7 @@ protected:
bool useCupsOptions,
const QString &pageRange,
const QString &version,
QPrinter::Orientation documentOrientation,
QPageLayout::Orientation documentOrientation,
ScaleMode scaleMode);
QStringList destination(QPrinter &printer, const QString &version);
@ -173,11 +139,11 @@ protected:
QStringList pages(QPrinter &printer, PageSelectPolicy pageSelectPolicy, const QString &pageRange, bool useCupsOptions, const QString &version);
/// @since 1.8
QStringList cupsOptions(QPrinter &printer, QPrinter::Orientation documentOrientation, ScaleMode scaleMode);
QStringList cupsOptions(QPrinter &printer, QPageLayout::Orientation documentOrientation, ScaleMode scaleMode);
QStringList optionMedia(QPrinter &printer);
QString mediaPageSize(QPrinter &printer);
QString mediaPaperSource(QPrinter &printer);
QStringList optionOrientation(QPrinter &printer, QPrinter::Orientation documentOrientation);
QStringList optionOrientation(QPrinter &printer, QPageLayout::Orientation documentOrientation);
QStringList optionDoubleSidedPrinting(QPrinter &printer);
QStringList optionPageOrder(QPrinter &printer);
QStringList optionCollateCopies(QPrinter &printer);

View File

@ -17,6 +17,7 @@ using namespace Okular;
FormFieldPrivate::FormFieldPrivate(FormField::FieldType type)
: m_type(type)
, m_activateAction(nullptr)
, q_ptr(nullptr)
{
}
@ -242,7 +243,7 @@ public:
void setValue(const QString &v) override
{
Q_Q(FormFieldChoice);
const QStringList choices = v.split(QLatin1Char(';'), QString::SkipEmptyParts);
const QStringList choices = v.split(QLatin1Char(';'), Qt::SkipEmptyParts);
QList<int> newchoices;
for (const QString &str : choices) {
bool ok = true;
@ -262,7 +263,7 @@ public:
QList<int> choices = q->currentChoices();
std::sort(choices.begin(), choices.end());
QStringList list;
for (const int c : qAsConst(choices)) {
for (const int c : std::as_const(choices)) {
list.append(QString::number(c));
}
return list.join(QStringLiteral(";"));

View File

@ -474,8 +474,9 @@ public:
/**
* The signature info
* @since 23.08
*/
virtual const SignatureInfo &signatureInfo() const = 0;
virtual SignatureInfo signatureInfo() const = 0;
/**
Signs a field of UnsignedSignature type.

View File

@ -8,6 +8,8 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config-okular.h"
#include "generator.h"
#include "generator_p.h"
#include "observer.h"
@ -22,7 +24,7 @@
#include <QMimeDatabase>
#include <QTimer>
#ifdef WITH_KWALLET
#if HAVE_KWALLET
#include <KWallet>
#endif
@ -35,7 +37,8 @@
using namespace Okular;
GeneratorPrivate::GeneratorPrivate()
: m_document(nullptr)
: q_ptr(nullptr)
, m_document(nullptr)
, mPixmapGenerationThread(nullptr)
, mTextPageGenerationThread(nullptr)
, mPixmapReady(true)
@ -414,7 +417,7 @@ bool Generator::exportTo(const QString &, const ExportFormat &)
void Generator::walletDataForFile(const QString &fileName, QString *walletName, QString *walletFolder, QString *walletKey) const
{
#ifdef WITH_KWALLET
#if HAVE_KWALLET
*walletKey = fileName.section(QLatin1Char('/'), -1, -1);
*walletName = KWallet::Wallet::NetworkWallet();
*walletFolder = QStringLiteral("KPdf");
@ -559,11 +562,6 @@ TextRequestPrivate *TextRequestPrivate::get(const TextRequest *req)
return req->d;
}
PixmapRequest::PixmapRequest(DocumentObserver *observer, int pageNumber, int width, int height, int priority, PixmapRequestFeatures features)
: PixmapRequest(observer, pageNumber, width, height, qApp->devicePixelRatio(), priority, features)
{
}
PixmapRequest::PixmapRequest(DocumentObserver *observer, int pageNumber, int width, int height, qreal dpr, int priority, PixmapRequestFeatures features)
: d(new PixmapRequestPrivate)
{
@ -676,7 +674,7 @@ PixmapRequestPrivate *PixmapRequestPrivate::get(const PixmapRequest *req)
void PixmapRequestPrivate::swap()
{
qSwap(mWidth, mHeight);
std::swap(mWidth, mHeight);
}
class Okular::ExportFormatPrivate : public QSharedData

View File

@ -616,18 +616,6 @@ public:
enum PixmapRequestFeature { NoFeature = 0, Asynchronous = 1, Preload = 2 };
Q_DECLARE_FLAGS(PixmapRequestFeatures, PixmapRequestFeature)
/**
* Creates a new pixmap request.
*
* @param observer The observer.
* @param pageNumber The page number.
* @param width The width of the page in logical pixels.
* @param height The height of the page in logical pixels.
* @param priority The priority of the request.
* @param features The features of generation.
*/
[[deprecated("This PixmapRequest constructor is deprecated, use the one including the device pixel ratio")]] PixmapRequest(DocumentObserver *observer, int pageNumber, int width, int height, int priority, PixmapRequestFeatures features);
/**
* Creates a new pixmap request.
*

View File

@ -7,6 +7,7 @@
#ifndef OKULAR_GLOBAL_H
#define OKULAR_GLOBAL_H
#include <QFlags>
#include <QGlobalStatic>
/**

View File

@ -16,12 +16,11 @@ class TextSelection::Private
{
public:
int direction;
int it[2];
NormalizedPoint cur[2];
};
TextSelection::TextSelection(const NormalizedPoint &start, const NormalizedPoint &end)
: d(new Private)
: d(std::make_unique<Private>())
{
if (end.y - start.y < 0 || (end.y - start.y == 0 && end.x - start.x < 0)) {
d->direction = 1;
@ -31,41 +30,9 @@ TextSelection::TextSelection(const NormalizedPoint &start, const NormalizedPoint
d->cur[0] = start;
d->cur[1] = end;
d->it[d->direction % 2] = -1;
d->it[(d->direction + 1) % 2] = -1;
}
TextSelection::~TextSelection()
{
delete d;
}
void TextSelection::end(const NormalizedPoint &p)
{
// changing direction as in 2b , assuming the bool->int conversion is correct
int dir1 = d->direction;
d->direction = (p.y - d->cur[0].y < 0 || (p.y - d->cur[0].y == 0 && p.x - d->cur[0].x < 0));
if (d->direction != dir1) {
qCDebug(OkularCoreDebug) << "changing direction in selection";
}
d->cur[1] = p;
}
void TextSelection::itE(int p)
{
d->it[(d->direction + 1) % 2] = p;
}
void TextSelection::itB(int p)
{
d->it[(d->direction) % 2] = p;
}
int TextSelection::direction() const
{
return d->direction;
}
TextSelection::~TextSelection() = default;
NormalizedPoint TextSelection::start() const
{
@ -76,13 +43,3 @@ NormalizedPoint TextSelection::end() const
{
return d->cur[(d->direction + 1) % 2];
}
int TextSelection::itB() const
{
return d->it[d->direction % 2];
}
int TextSelection::itE() const
{
return d->it[(d->direction + 1) % 2];
}

View File

@ -14,21 +14,6 @@ namespace Okular
{
/**
@short Wrapper around the information needed to generate the selection area
There are two assumptions inside this class:
1. the start never changes, one instance of this class is used for one selection,
therefore the start of the selection will not change, only end and direction of
the selection will change.
By direction we mean the direction in which the end moves in relation to the start,
forward selection is when end is after the start, backward when its before.
2. The following changes might appear during selection:
a. the end moves without changing the direction (it can move up and down but not past the start):
only itE will be updated
b. the end moves with changing the direction then itB becomes itE if the previous direction was forward
or itE becomes itB
3. Internally it that is related to the start cursor is always at it[0] while it related to end is it[1],
transition between meanings (itB/itE) is done with dir modifier;
*/
class OKULARCORE_EXPORT TextSelection
{
@ -46,19 +31,6 @@ public:
TextSelection(const TextSelection &) = delete;
TextSelection &operator=(const TextSelection &) = delete;
/**
* Changes the end point of the selection to the given @p point.
*/
void end(const NormalizedPoint &point);
void itE(int pos);
void itB(int pos);
/**
* Returns the direction of the selection.
*/
int direction() const;
/**
* Returns the start point of the selection.
*/
@ -69,12 +41,9 @@ public:
*/
NormalizedPoint end() const;
int itB() const;
int itE() const;
private:
class Private;
Private *const d;
std::unique_ptr<Private> d;
};
}

View File

@ -30,6 +30,7 @@ public:
, m_tmp(nullptr)
, m_showControls(false)
, m_autoPlay(false)
, m_startPaused(false)
, m_showPosterImage(false)
{
}
@ -43,6 +44,7 @@ public:
QImage m_posterImage;
bool m_showControls : 1;
bool m_autoPlay : 1;
bool m_startPaused : 1;
bool m_showPosterImage : 1;
};
@ -145,6 +147,16 @@ bool Movie::autoPlay() const
return d->m_autoPlay;
}
void Movie::setStartPaused(bool startPaused)
{
d->m_startPaused = startPaused;
}
bool Movie::startPaused() const
{
return d->m_startPaused;
}
void Movie::setShowPosterImage(bool show)
{
d->m_showPosterImage = show;

View File

@ -117,6 +117,16 @@ public:
*/
bool autoPlay() const;
/**
* Sets whether to start the movie in paused mode
*/
void setStartPaused(bool startPaused);
/**
* Whether to start the movie in paused mode
*/
bool startPaused() const;
/**
* Sets whether to show a poster image.
*

View File

@ -10,6 +10,7 @@
using namespace Okular;
DocumentObserver::DocumentObserver()
: d(nullptr)
{
}

View File

@ -48,7 +48,6 @@ public:
TextSelection = 8, ///< Text selection has been changed
Annotations = 16, ///< Annotations have been changed
BoundingBox = 32, ///< Bounding boxes have been changed
NeedSaveAs = 64 ///< Set when "Save" is needed or annotation/form changes will be lost @since 0.15 (KDE 4.9) @deprecated
};
/**

View File

@ -4,6 +4,7 @@ X-KDE-ServiceType=okular/Generator
Comment=File format backend for Okular
Comment[ar]=المنتهى الخلفي لـنسق الملف لأوكلار
Comment[az]=Okulyar üçün fayl formatı modulu
Comment[be]=Рухавік для апрацоўвання фарматаў файлаў для Okular
Comment[bg]=Ядро на Okular за файлови формати
Comment[bs]=Pozadina formata datoteke za Okular
Comment[ca]=Dorsal del format de fitxer per a l'Okular
@ -13,18 +14,21 @@ Comment[da]=Filformatmotor til Okular
Comment[de]=Dateiformat-Modul für Okular
Comment[el]=Σύστημα υποστήριξης τύπου αρχείων για το Okular
Comment[en_GB]=File format backend for Okular
Comment[eo]=Dosierformato backend por Okular
Comment[es]=Motor de formatos de archivos para Okular
Comment[et]=Okulari failivormingu taustaprogramm
Comment[eu]=Okularrentzako Fitxategi formatuaren bizkarraldekoa
Comment[fi]=Tiedostomuototaustaosa Okularille
Comment[fr]=Moteur de formats de fichiers pour Okular
Comment[ga]=Inneall formáide comhaid le haghaidh Okular
Comment[gl]=Infraestrutura dun formato de ficheiro para Okular
Comment[gl]=Infraestrutura dun formato de ficheiro para Okular.
Comment[he]=מנגנון סוגי קבצים ל־Okular
Comment[hne]=
Comment[hr]=Podrška za oblike datoteka za Okular
Comment[hu]=Fájlformátumkezelő az Okularhoz
Comment[ia]=Retro-administration de formato de file pro Okular
Comment[is]=Skráasniðsstuðningur fyrir Okular
Comment[ie]=Infrastructura de formate de file por Okular
Comment[is]=Skráasniðsbakendi fyrir Okular
Comment[it]=Backend per i formati di file di Okular
Comment[ja]=Okular
Comment[ka]= Okular-
@ -33,7 +37,7 @@ Comment[km]=ផ្នែក​ខាង​ក្រោយ​ទ្រង់ទ
Comment[ko]=Okular
Comment[ku]=Binesazî ya teşeya pelê Okular
Comment[lt]=Okular failų formato programinė sąsaja
Comment[lv]=Failu formātu Okular aizmugure
Comment[lv]=Okular datņu formātu aizmugursistēma
Comment[mr]= ि
Comment[nb]=Filformatmotor for Okular
Comment[nds]=Dateiformaat-Hülpprogramm för Okular
@ -47,7 +51,7 @@ Comment[ro]=Platformă Okular pentru formate de fișiere
Comment[ru]=Модуль поддержки формата для Okular
Comment[sk]=Backend formátu súborov pre Okular
Comment[sl]=Zaledje za vrste datotek za Okular
Comment[sq]=Mbështetës për formatet e skedarëve në Okular
Comment[sq]=Mbështetës për formatet e skedave në Okular
Comment[sr]=Позадина формата фајла за Окулар
Comment[sr@ijekavian]=Позадина формата фајла за Окулар
Comment[sr@ijekavianlatin]=Pozadina formata fajla za Okular
@ -59,7 +63,7 @@ Comment[tr]=Okular için dosya biçimi arka ucu
Comment[uk]=Модуль типів файлів для okular
Comment[vi]=Hu phương đnh dng tp cho Okular
Comment[x-test]=xxFile format backend for Okularxx
Comment[zh_CN]=Okular
Comment[zh_CN]=Okular
Comment[zh_TW]=Okular
# Priority of the plugin. 0 - disabled
[PropertyDef::X-KDE-Priority]

View File

@ -221,11 +221,6 @@ bool Page::hasPixmap(DocumentObserver *observer, int width, int height, const No
TilesManager *tm = d->tilesManager(observer);
if (tm) {
if (width != tm->width() || height != tm->height()) {
// FIXME hasPixmap should not be calling setSize on the TilesManager this is not very "const"
// as this function claims to be
if (width != -1 && height != -1) {
tm->setSize(width, height);
}
return false;
}
@ -250,21 +245,29 @@ bool Page::hasPixmap(DocumentObserver *observer, int width, int height, const No
return (pixmap->width() == width && pixmap->height() == height);
}
void Page::setPageSize(DocumentObserver *observer, int width, int height)
{
TilesManager *tm = d->tilesManager(observer);
if (tm) {
tm->setSize(width, height);
}
}
bool Page::hasTextPage() const
{
return d->m_text != nullptr;
}
RegularAreaRect *Page::wordAt(const NormalizedPoint &p, QString *word) const
std::unique_ptr<RegularAreaRect> Page::wordAt(const NormalizedPoint &p) const
{
if (d->m_text) {
return d->m_text->wordAt(p, word);
return d->m_text->wordAt(p);
}
return nullptr;
}
RegularAreaRect *Page::textArea(TextSelection *selection) const
std::unique_ptr<RegularAreaRect> Page::textArea(const TextSelection &selection) const
{
if (d->m_text) {
return d->m_text->textArea(selection);
@ -371,9 +374,7 @@ TextEntity::List Page::words(const RegularAreaRect *area, TextPage::TextAreaIncl
}
for (auto &retI : ret) {
const TextEntity *orig = retI;
retI = new TextEntity(orig->text(), new Okular::NormalizedRect(orig->transformedArea(d->rotationMatrix())));
delete orig;
retI = TextEntity(retI.text(), Okular::NormalizedRect(retI.transformedArea(d->rotationMatrix())));
}
return ret;
@ -388,7 +389,7 @@ void PagePrivate::rotateAt(Rotation orientation)
deleteTextSelections();
if (((int)m_orientation + (int)m_rotation) % 2 != ((int)m_orientation + (int)orientation) % 2) {
qSwap(m_width, m_height);
std::swap(m_width, m_height);
}
Rotation oldRotation = m_rotation;
@ -425,12 +426,12 @@ void PagePrivate::rotateAt(Rotation orientation)
* Rotate the object rects on the page.
*/
const QTransform matrix = rotationMatrix();
for (ObjectRect *objRect : qAsConst(m_page->m_rects)) {
for (ObjectRect *objRect : std::as_const(m_page->m_rects)) {
objRect->transform(matrix);
}
const QTransform highlightRotationMatrix = Okular::buildRotationMatrix((Rotation)(((int)m_rotation - (int)oldRotation + 4) % 4));
for (HighlightAreaRect *hlar : qAsConst(m_page->m_highlights)) {
for (HighlightAreaRect *hlar : std::as_const(m_page->m_highlights)) {
hlar->transform(highlightRotationMatrix);
}
}
@ -448,7 +449,7 @@ void PagePrivate::changeSize(const PageSize &size)
m_width = size.width();
m_height = size.height();
if (m_rotation % 2) {
qSwap(m_width, m_height);
std::swap(m_width, m_height);
}
}
@ -625,16 +626,13 @@ void PagePrivate::setHighlight(int s_id, RegularAreaRect *rect, const QColor &co
m_page->m_highlights.append(hr);
}
void PagePrivate::setTextSelections(RegularAreaRect *r, const QColor &color)
void PagePrivate::setTextSelections(const RegularAreaRect &r, const QColor &color)
{
deleteTextSelections();
if (r) {
HighlightAreaRect *hr = new HighlightAreaRect(r);
hr->s_id = -1;
hr->color = color;
m_textSelections = hr;
delete r;
}
HighlightAreaRect *hr = new HighlightAreaRect(&r);
hr->s_id = -1;
hr->color = color;
m_textSelections = hr;
}
void Page::setSourceReferences(const QList<SourceRefObjectRect *> &refRects)
@ -746,7 +744,7 @@ void Page::setFormFields(const QList<FormField *> &fields)
{
qDeleteAll(d->formfields);
d->formfields = fields;
for (FormField *ff : qAsConst(d->formfields)) {
for (FormField *ff : std::as_const(d->formfields)) {
ff->d_ptr->setDefault();
ff->d_ptr->m_page = this;
}
@ -875,7 +873,7 @@ bool PagePrivate::restoreLocalContents(const QDomNode &pageNode)
}
QHash<int, FormField *> hashedforms;
for (FormField *ff : qAsConst(formfields)) {
for (FormField *ff : std::as_const(formfields)) {
hashedforms[ff->id()] = ff;
}
@ -930,7 +928,7 @@ void PagePrivate::saveLocalContents(QDomNode &parentNode, QDomDocument &document
QDomElement annotListElement = document.createElement(QStringLiteral("annotationList"));
// add every annotation to the annotationList
for (const Annotation *a : qAsConst(m_page->m_annotations)) {
for (const Annotation *a : std::as_const(m_page->m_annotations)) {
// only save okular annotations (not the embedded in file ones)
if (!(a->flags() & Annotation::External)) {
// append an filled-up element called 'annotation' to the list
@ -1065,26 +1063,26 @@ FormField *PagePrivate::findEquivalentForm(const Page *p, FormField *oldField)
{
// given how id is not very good of id (at least for pdf) we do a few passes
// same rect, type and id
for (FormField *f : qAsConst(p->d->formfields)) {
for (FormField *f : std::as_const(p->d->formfields)) {
if (f->rect() == oldField->rect() && f->type() == oldField->type() && f->id() == oldField->id()) {
return f;
}
}
// same rect and type
for (FormField *f : qAsConst(p->d->formfields)) {
for (FormField *f : std::as_const(p->d->formfields)) {
if (f->rect() == oldField->rect() && f->type() == oldField->type()) {
return f;
}
}
// fuzzy rect, same type and id
for (FormField *f : qAsConst(p->d->formfields)) {
for (FormField *f : std::as_const(p->d->formfields)) {
if (f->type() == oldField->type() && f->id() == oldField->id() && qFuzzyCompare(f->rect().left, oldField->rect().left) && qFuzzyCompare(f->rect().top, oldField->rect().top) &&
qFuzzyCompare(f->rect().right, oldField->rect().right) && qFuzzyCompare(f->rect().bottom, oldField->rect().bottom)) {
return f;
}
}
// fuzzy rect and same type
for (FormField *f : qAsConst(p->d->formfields)) {
for (FormField *f : std::as_const(p->d->formfields)) {
if (f->type() == oldField->type() && qFuzzyCompare(f->rect().left, oldField->rect().left) && qFuzzyCompare(f->rect().top, oldField->rect().top) && qFuzzyCompare(f->rect().right, oldField->rect().right) &&
qFuzzyCompare(f->rect().bottom, oldField->rect().bottom)) {
return f;

View File

@ -141,6 +141,11 @@ public:
*/
bool hasPixmap(DocumentObserver *observer, int width = -1, int height = -1, const NormalizedRect &rect = NormalizedRect()) const;
/**
* Sets the size of the page (in screen pixels) if there is a TilesManager.
*/
void setPageSize(DocumentObserver *observer, int width, int height);
/**
* Returns whether the page provides a text page (@ref TextPage).
*/
@ -210,12 +215,12 @@ public:
* @see TextPage::wordAt()
* @since 0.15 (KDE 4.9)
*/
RegularAreaRect *wordAt(const NormalizedPoint &p, QString *word = nullptr) const;
std::unique_ptr<RegularAreaRect> wordAt(const NormalizedPoint &p) const;
/**
* Returns the rectangular area of the given @p selection.
*/
RegularAreaRect *textArea(TextSelection *selection) const;
std::unique_ptr<RegularAreaRect> textArea(const TextSelection &selection) const;
/**
* Returns the object rect of the given @p type which is at point (@p x, @p y) at scale (@p xScale, @p yScale).

View File

@ -95,7 +95,7 @@ public:
* @param r Areas of new text selections.
* @param color Color of new text selections.
*/
void setTextSelections(RegularAreaRect *r, const QColor &color);
void setTextSelections(const RegularAreaRect &r, const QColor &color);
/**
* Sets the @p color and @p rect of the highlight for the observer with

View File

@ -4,6 +4,19 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
/* QJSEngine limits what we can do with the global object in C++, so map Doc into this here. */
{
const props = Object.getOwnPropertyDescriptors(Doc);
for (prop in props) {
Object.defineProperty(this, prop, props[prop]);
}
for (const name of Object.getOwnPropertyNames(Doc)) {
if (typeof Doc[name] === 'function') {
this.__proto__[name] = Doc[name];
}
}
}
/* Builtin functions for Okular's PDF JavaScript interpretation. */
/** AFSimple_Calculate
@ -348,3 +361,49 @@ function AFPercent_Keystroke( nDec, sepStyle )
event.rc = !isNaN(event.change) || event.change == "." || event.change == ","
}
}
app.popUpMenuEx = function() {
return app.okular_popUpMenuEx(arguments);
}
app.popUpMenu = function() {
// Convert arguments like this:
// app.popUpMenu(["Fruits","Apples","Oranges"], "-","Beans","Corn");
// into this:
// app.popUpMenuEx(
// {cName:"Fruits", oSubMenu:[
// {cName:"Apples"},
// {cName:"Oranges"}
// ]},
// {cName:"-"},
// {cName:"Beans"},
// {cName:"Corn"}
// );
function convertArgument(arg) {
var exArguments = [];
for (element of arg) {
var newElement = null;
if (Array.isArray(element) && element.length > 0) {
newElement = {
cName: element[0],
oSubMenu: convertArgument(element.slice(1))
};
} else if (!Array.isArray(element)) {
newElement = {
cName: element
};
}
if (newElement !== null)
exArguments.push(newElement);
}
return exArguments;
}
var exArguments = convertArgument(arguments);
var result = app.okular_popUpMenuEx(exArguments);
return result;
}

View File

@ -57,17 +57,19 @@ Event::EventType Event::eventType() const
QString Event::name() const
{
switch (d->m_eventType) {
case (FieldCalculate):
case FieldCalculate:
return QStringLiteral("Calculate");
case (FieldFormat):
case FieldFormat:
return QStringLiteral("Format");
case (FieldKeystroke):
case FieldKeystroke:
return QStringLiteral("Keystroke");
case (FieldFocus):
case FieldFocus:
return QStringLiteral("Focus");
case (FieldValidate):
case FieldValidate:
return QStringLiteral("Validate");
case (UnknownEvent):
case FieldMouseUp:
return QStringLiteral("MouseUp");
case UnknownEvent:
default:
return QStringLiteral("Unknown");
}
@ -76,13 +78,14 @@ QString Event::name() const
QString Event::type() const
{
switch (d->m_eventType) {
case (FieldCalculate):
case (FieldFormat):
case (FieldKeystroke):
case (FieldFocus):
case (FieldValidate):
case FieldCalculate:
case FieldFormat:
case FieldKeystroke:
case FieldFocus:
case FieldValidate:
case FieldMouseUp:
return QStringLiteral("Field");
case (UnknownEvent):
case UnknownEvent:
default:
return QStringLiteral("Unknown");
}
@ -269,3 +272,12 @@ std::shared_ptr<Event> Event::createFormValidateEvent(FormField *target, Page *t
}
return ret;
}
std::shared_ptr<Event> Event::createFieldMouseUpEvent(FormField *target, Page *targetPage)
{
std::shared_ptr<Event> ret = std::make_shared<Event>(Event::FieldMouseUp);
ret->setTarget(target);
ret->setTargetPage(targetPage);
ret->setShiftModifier(QApplication::keyboardModifiers() & Qt::ShiftModifier);
return ret;
}

View File

@ -25,7 +25,7 @@ class Page;
*
* The implementation is currently limited. To implement support
* for a new event create the according data fields / getters
* and setters and update the handling in kjs_event
* and setters and update the handling in js_event
* accordingly.
*
* See Acrobat JavaScript Scripting Reference for the meaning
@ -59,7 +59,7 @@ public:
FieldMouseDown, /// < Not implemented.
FieldMouseEnter, /// < Not implemented.
FieldMouseExit, /// < Not implemented.
FieldMouseUp, /// < Not implemented.
FieldMouseUp, /// < This event is the result of a mouse up on a field.
/* Validates the field after every change is committed
* (clicked outside or tabbed to another field).
* The enter event is not handled
@ -116,6 +116,7 @@ public:
static std::shared_ptr<Event> createKeystrokeEvent(FormField *target, Page *targetPage);
static std::shared_ptr<Event> createFormFocusEvent(FormField *target, Page *targetPage, const QString &targetName = QString());
static std::shared_ptr<Event> createFormValidateEvent(FormField *target, Page *targetPage, const QString &targetName = QString());
static std::shared_ptr<Event> createFieldMouseUpEvent(FormField *target, Page *targetPage);
private:
class Private;

107
core/script/executor_js.cpp Normal file
View File

@ -0,0 +1,107 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config-okular.h"
#include "executor_js_p.h"
#include "../debug_p.h"
#include "../document_p.h"
#include "event_p.h"
#include "js_app_p.h"
#include "js_console_p.h"
#include "js_data_p.h"
#include "js_display_p.h"
#include "js_document_p.h"
#include "js_event_p.h"
#include "js_field_p.h"
#include "js_fullscreen_p.h"
#include "js_ocg_p.h"
#include "js_spell_p.h"
#include "js_util_p.h"
#include <QDebug>
#include <QJSEngine>
#include <QThread>
#include <QTimer>
using namespace Okular;
class Okular::ExecutorJSPrivate
{
public:
explicit ExecutorJSPrivate(DocumentPrivate *doc)
: m_doc(doc)
{
initTypes();
}
~ExecutorJSPrivate()
{
m_watchdogTimer->deleteLater();
m_watchdogThread.quit();
m_watchdogThread.wait();
}
void initTypes();
DocumentPrivate *m_doc;
QJSEngine m_interpreter;
QThread m_watchdogThread;
QTimer *m_watchdogTimer = nullptr;
};
void ExecutorJSPrivate::initTypes()
{
m_watchdogThread.start();
m_watchdogTimer = new QTimer;
m_watchdogTimer->setInterval(std::chrono::seconds(2)); // max 2 secs allowed
m_watchdogTimer->setSingleShot(true);
m_watchdogTimer->moveToThread(&m_watchdogThread);
QObject::connect(
m_watchdogTimer, &QTimer::timeout, &m_interpreter, [this]() { m_interpreter.setInterrupted(true); }, Qt::DirectConnection);
m_interpreter.globalObject().setProperty(QStringLiteral("app"), m_interpreter.newQObject(new JSApp(m_doc, m_watchdogTimer)));
m_interpreter.globalObject().setProperty(QStringLiteral("console"), m_interpreter.newQObject(new JSConsole));
m_interpreter.globalObject().setProperty(QStringLiteral("Doc"), m_interpreter.newQObject(new JSDocument(m_doc)));
m_interpreter.globalObject().setProperty(QStringLiteral("display"), m_interpreter.newQObject(new JSDisplay));
m_interpreter.globalObject().setProperty(QStringLiteral("spell"), m_interpreter.newQObject(new JSSpell));
m_interpreter.globalObject().setProperty(QStringLiteral("util"), m_interpreter.newQObject(new JSUtil));
}
ExecutorJS::ExecutorJS(DocumentPrivate *doc)
: d(new ExecutorJSPrivate(doc))
{
}
ExecutorJS::~ExecutorJS()
{
JSField::clearCachedFields();
JSApp::clearCachedFields();
delete d;
}
void ExecutorJS::execute(const QString &script, Event *event)
{
const auto eventVal = event ? d->m_interpreter.newQObject(new JSEvent(event)) : QJSValue(QJSValue::UndefinedValue);
d->m_interpreter.globalObject().setProperty(QStringLiteral("event"), eventVal);
QMetaObject::invokeMethod(d->m_watchdogTimer, qOverload<>(&QTimer::start));
d->m_interpreter.setInterrupted(false);
auto result = d->m_interpreter.evaluate(script, QStringLiteral("okular.js"));
QMetaObject::invokeMethod(d->m_watchdogTimer, qOverload<>(&QTimer::stop));
if (result.isError()) {
qCDebug(OkularCoreDebug) << "JS exception" << result.toString() << "(line " << result.property(QStringLiteral("lineNumber")).toInt() << ")";
} else {
qCDebug(OkularCoreDebug) << "result:" << result.toString();
if (event) {
qCDebug(OkularCoreDebug) << "Event Result:" << event->name() << event->type() << "value:" << event->value();
}
}
}

View File

@ -0,0 +1,36 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_EXECUTOR_JS_P_H
#define OKULAR_SCRIPT_EXECUTOR_JS_P_H
class QString;
namespace Okular
{
class DocumentPrivate;
class ExecutorJSPrivate;
class Event;
class ExecutorJS
{
public:
explicit ExecutorJS(DocumentPrivate *doc);
~ExecutorJS();
ExecutorJS(const ExecutorJS &) = delete;
ExecutorJS &operator=(const ExecutorJS &) = delete;
void execute(const QString &script, Event *event);
private:
friend class ExecutorJSPrivate;
ExecutorJSPrivate *d;
};
}
#endif

View File

@ -1,124 +0,0 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "executor_kjs_p.h"
#include <kjs/kjsarguments.h>
#include <kjs/kjsinterpreter.h>
#include <kjs/kjsobject.h>
#include <kjs/kjsprototype.h>
#include <kjs_version.h>
#include <QDebug>
#include "../debug_p.h"
#include "../document_p.h"
#include "config-okular.h"
#include "event_p.h"
#include "kjs_app_p.h"
#include "kjs_console_p.h"
#include "kjs_data_p.h"
#include "kjs_display_p.h"
#include "kjs_document_p.h"
#include "kjs_event_p.h"
#include "kjs_field_p.h"
#include "kjs_fullscreen_p.h"
#include "kjs_ocg_p.h"
#include "kjs_spell_p.h"
#include "kjs_util_p.h"
using namespace Okular;
class Okular::ExecutorKJSPrivate
{
public:
explicit ExecutorKJSPrivate(DocumentPrivate *doc)
: m_doc(doc)
{
initTypes();
}
~ExecutorKJSPrivate()
{
delete m_interpreter;
}
void initTypes();
DocumentPrivate *m_doc;
KJSInterpreter *m_interpreter;
KJSGlobalObject m_docObject;
};
void ExecutorKJSPrivate::initTypes()
{
m_docObject = JSDocument::wrapDocument(m_doc);
m_interpreter = new KJSInterpreter(m_docObject);
#if KJS_VERSION > QT_VERSION_CHECK(5, 71, 0)
m_interpreter->setTimeoutTime(2000); // max 2 secs allowed
#endif
KJSContext *ctx = m_interpreter->globalContext();
JSApp::initType(ctx);
JSFullscreen::initType(ctx);
JSConsole::initType(ctx);
JSData::initType(ctx);
JSDisplay::initType(ctx);
JSDocument::initType(ctx);
JSEvent::initType(ctx);
JSField::initType(ctx);
JSOCG::initType(ctx);
JSSpell::initType(ctx);
JSUtil::initType(ctx);
m_docObject.setProperty(ctx, QStringLiteral("app"), JSApp::object(ctx, m_doc));
m_docObject.setProperty(ctx, QStringLiteral("console"), JSConsole::object(ctx));
m_docObject.setProperty(ctx, QStringLiteral("Doc"), m_docObject);
m_docObject.setProperty(ctx, QStringLiteral("display"), JSDisplay::object(ctx));
m_docObject.setProperty(ctx, QStringLiteral("OCG"), JSOCG::object(ctx));
m_docObject.setProperty(ctx, QStringLiteral("spell"), JSSpell::object(ctx));
m_docObject.setProperty(ctx, QStringLiteral("util"), JSUtil::object(ctx));
}
ExecutorKJS::ExecutorKJS(DocumentPrivate *doc)
: d(new ExecutorKJSPrivate(doc))
{
}
ExecutorKJS::~ExecutorKJS()
{
JSField::clearCachedFields();
JSApp::clearCachedFields();
JSOCG::clearCachedFields();
delete d;
}
void ExecutorKJS::execute(const QString &script, Event *event)
{
KJSContext *ctx = d->m_interpreter->globalContext();
d->m_docObject.setProperty(ctx, QStringLiteral("event"), event ? JSEvent::wrapEvent(ctx, event) : KJSUndefined());
#if KJS_VERSION > QT_VERSION_CHECK(5, 71, 0)
d->m_interpreter->startTimeoutCheck();
#endif
KJSResult result = d->m_interpreter->evaluate(QStringLiteral("okular.js"), 1, script, &d->m_docObject);
#if KJS_VERSION > QT_VERSION_CHECK(5, 71, 0)
d->m_interpreter->stopTimeoutCheck();
#endif
if (result.isException() || ctx->hasException()) {
qCDebug(OkularCoreDebug) << "JS exception" << result.errorMessage();
} else {
qCDebug(OkularCoreDebug) << "result:" << result.value().toString(ctx);
if (event) {
qCDebug(OkularCoreDebug) << "Event Result:" << event->name() << event->type() << "value:" << event->value();
}
}
}

View File

@ -1,36 +0,0 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_EXECUTOR_KJS_P_H
#define OKULAR_SCRIPT_EXECUTOR_KJS_P_H
class QString;
namespace Okular
{
class DocumentPrivate;
class ExecutorKJSPrivate;
class Event;
class ExecutorKJS
{
public:
explicit ExecutorKJS(DocumentPrivate *doc);
~ExecutorKJS();
ExecutorKJS(const ExecutorKJS &) = delete;
ExecutorKJS &operator=(const ExecutorKJS &) = delete;
void execute(const QString &script, Event *event);
private:
friend class ExecutorKJSPrivate;
ExecutorKJSPrivate *d;
};
}
#endif

408
core/script/js_app.cpp Normal file
View File

@ -0,0 +1,408 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_app_p.h"
#include <QApplication>
#include <QLocale>
#include <QTimer>
#include <KLocalizedString>
#include <QCheckBox>
#include <QJSEngine>
#include <QMenu>
#include <QMessageBox>
#include "../document_p.h"
#include "../scripter.h"
#include "config-okular.h"
#include "js_fullscreen_p.h"
using namespace Okular;
#define OKULAR_TIMERID QStringLiteral("okular_timerID")
typedef QHash<int, QTimer *> TimerCache;
Q_GLOBAL_STATIC(TimerCache, g_timerCache)
// the acrobat version we fake
static const double fake_acroversion = 8.00;
// The property used to hold the value to return from app.popUpMenuEx
static const char *kResultProperty = "result";
static const struct FakePluginInfo {
const char *name;
bool certified;
bool loaded;
const char *path;
} s_fake_plugins[] = {{"Annots", true, true, ""}, {"EFS", true, true, ""}, {"EScript", true, true, ""}, {"Forms", true, true, ""}, {"ReadOutLoud", true, true, ""}, {"WebLink", true, true, ""}};
static const int s_num_fake_plugins = sizeof(s_fake_plugins) / sizeof(s_fake_plugins[0]);
int JSApp::formsVersion() const
{
// faking a bit...
return fake_acroversion;
}
QString JSApp::language() const
{
QLocale locale;
QString lang = QLocale::languageToString(locale.language());
QString country = QLocale::territoryToString(locale.territory());
QString acroLang = QStringLiteral("ENU");
if (lang == QLatin1String("da")) {
acroLang = QStringLiteral("DAN"); // Danish
} else if (lang == QLatin1String("de")) {
acroLang = QStringLiteral("DEU"); // German
} else if (lang == QLatin1String("en")) {
acroLang = QStringLiteral("ENU"); // English
} else if (lang == QLatin1String("es")) {
acroLang = QStringLiteral("ESP"); // Spanish
} else if (lang == QLatin1String("fr")) {
acroLang = QStringLiteral("FRA"); // French
} else if (lang == QLatin1String("it")) {
acroLang = QStringLiteral("ITA"); // Italian
} else if (lang == QLatin1String("ko")) {
acroLang = QStringLiteral("KOR"); // Korean
} else if (lang == QLatin1String("ja")) {
acroLang = QStringLiteral("JPN"); // Japanese
} else if (lang == QLatin1String("nl")) {
acroLang = QStringLiteral("NLD"); // Dutch
} else if (lang == QLatin1String("pt") && country == QLatin1String("BR")) {
acroLang = QStringLiteral("PTB"); // Brazilian Portuguese
} else if (lang == QLatin1String("fi")) {
acroLang = QStringLiteral("SUO"); // Finnish
} else if (lang == QLatin1String("sv")) {
acroLang = QStringLiteral("SVE"); // Swedish
} else if (lang == QLatin1String("zh") && country == QLatin1String("CN")) {
acroLang = QStringLiteral("CHS"); // Chinese Simplified
} else if (lang == QLatin1String("zh") && country == QLatin1String("TW")) {
acroLang = QStringLiteral("CHT"); // Chinese Traditional
}
return acroLang;
}
int JSApp::numPlugIns() const
{
return s_num_fake_plugins;
}
QString JSApp::platform() const
{
#if defined(Q_OS_WIN)
return QString::fromLatin1("WIN");
#elif defined(Q_OS_MAC)
return QString::fromLatin1("MAC");
#else
return QStringLiteral("UNIX");
#endif
}
QJSValue JSApp::plugIns() const
{
QJSValue plugins = qjsEngine(this)->newArray(s_num_fake_plugins);
for (int i = 0; i < s_num_fake_plugins; ++i) {
const FakePluginInfo &info = s_fake_plugins[i];
QJSValue plugin = qjsEngine(this)->newObject();
plugin.setProperty(QStringLiteral("certified"), info.certified);
plugin.setProperty(QStringLiteral("loaded"), info.loaded);
plugin.setProperty(QStringLiteral("name"), info.name);
plugin.setProperty(QStringLiteral("path"), info.path);
plugin.setProperty(QStringLiteral("version"), fake_acroversion);
plugins.setProperty(i, plugin);
}
return plugins;
}
QStringList JSApp::printColorProfiles() const
{
return QStringList();
}
QStringList JSApp::printerNames() const
{
return QStringList();
}
QString JSApp::viewerType() const
{
// faking a bit...
return QStringLiteral("Reader");
}
QString JSApp::viewerVariation() const
{
// faking a bit...
return QStringLiteral("Reader");
}
int JSApp::viewerVersion() const
{
// faking a bit...
return fake_acroversion;
}
/*
Alert function defined in the reference, it shows a Dialog Box with options.
app.alert()
*/
int JSApp::alert(const QJSValue &arguments)
{
const auto cMsg = arguments.property(QStringLiteral("cMsg")).toString();
const auto nIcon = arguments.property(QStringLiteral("nIcon")).toInt();
const auto nType = arguments.property(QStringLiteral("nType")).toInt();
const auto cTitle = arguments.property(QStringLiteral("cTitle")).toString();
const auto oCheckbox = arguments.property(QStringLiteral("oCheckbox"));
return alert(cMsg, nIcon, nType, cTitle, QJSValue(), oCheckbox);
}
int JSApp::alert(const QString &cMsg, int nIcon, int nType, const QString &cTitle, [[maybe_unused]] const QJSValue &oDoc, const QJSValue &oCheckbox)
{
QMessageBox::Icon icon = QMessageBox::Critical;
switch (nIcon) {
case 0:
icon = QMessageBox::Critical;
break;
case 1:
icon = QMessageBox::Warning;
break;
case 2:
icon = QMessageBox::Question;
break;
case 3:
icon = QMessageBox::Information;
break;
}
const QString title = !cTitle.isEmpty() ? cTitle : QStringLiteral("Okular");
QMessageBox box(icon, title, cMsg);
QMessageBox::StandardButtons buttons = QMessageBox::Ok;
switch (nType) {
case 0:
buttons = QMessageBox::Ok;
break;
case 1:
buttons = QMessageBox::Ok | QMessageBox::Cancel;
break;
case 2:
buttons = QMessageBox::Yes | QMessageBox::No;
break;
case 3:
buttons = QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel;
break;
}
box.setStandardButtons(buttons);
QCheckBox *checkBox = nullptr;
if (oCheckbox.isObject()) {
const auto oMsg = oCheckbox.property(QStringLiteral("cMsg"));
QString msg = i18n("Do not show this message again");
if (oMsg.isString()) {
msg = oMsg.toString();
}
bool bInitialValue = false;
const auto value = oCheckbox.property(QStringLiteral("bInitialValue"));
if (value.isBool()) {
bInitialValue = value.toBool();
}
checkBox = new QCheckBox(msg);
checkBox->setChecked(bInitialValue);
box.setCheckBox(checkBox);
}
// halt timeout until the user has responded
QMetaObject::invokeMethod(m_watchdogTimer, qOverload<>(&QTimer::stop));
int button = box.exec();
// restart max allowed time
QMetaObject::invokeMethod(m_watchdogTimer, qOverload<>(&QTimer::start));
int ret = 0;
switch (button) {
case QMessageBox::Ok:
ret = 1;
break;
case QMessageBox::Cancel:
ret = 2;
break;
case QMessageBox::No:
ret = 3;
break;
case QMessageBox::Yes:
ret = 4;
break;
}
if (checkBox) {
QJSValue(oCheckbox).setProperty(QStringLiteral("bAfterValue"), checkBox->isChecked());
}
delete checkBox;
return ret;
}
void JSApp::beep([[maybe_unused]] int nType)
{
QApplication::beep();
}
QJSValue JSApp::getNthPlugInName(int nIndex) const
{
if (nIndex < 0 || nIndex >= s_num_fake_plugins) {
return qjsEngine(this)->newErrorObject(QJSValue::TypeError, QStringLiteral("PlugIn index out of bounds"));
}
const FakePluginInfo &info = s_fake_plugins[nIndex];
return info.name;
}
void JSApp::goBack()
{
if (!m_doc->m_parent->historyAtBegin()) {
m_doc->m_parent->setPrevViewport();
}
}
void JSApp::goForward()
{
if (!m_doc->m_parent->historyAtEnd()) {
m_doc->m_parent->setNextViewport();
}
}
// app.setInterval()
QJSValue JSApp::setInterval(const QString &cExpr, int nMilliseconds)
{
QTimer *timer = new QTimer();
QObject::connect(timer, &QTimer::timeout, m_doc->m_parent, [=]() { m_doc->executeScript(cExpr); });
timer->start(nMilliseconds);
return JSApp::wrapTimer(timer);
}
// app.clearInterval()
void JSApp::clearInterval(const QJSValue &oInterval)
{
const int timerId = oInterval.property(OKULAR_TIMERID).toInt();
QTimer *timer = g_timerCache->value(timerId);
if (timer != nullptr) {
timer->stop();
g_timerCache->remove(timerId);
delete timer;
}
}
// app.setTimeOut()
QJSValue JSApp::setTimeOut(const QString &cExpr, int nMilliseconds)
{
QTimer *timer = new QTimer();
timer->setSingleShot(true);
QObject::connect(timer, &QTimer::timeout, m_doc->m_parent, [=]() { m_doc->executeScript(cExpr); });
timer->start(nMilliseconds);
return JSApp::wrapTimer(timer);
}
// app.clearTimeOut()
void JSApp::clearTimeOut(const QJSValue &oTime)
{
const int timerId = oTime.property(OKULAR_TIMERID).toInt();
QTimer *timer = g_timerCache->value(timerId);
if (timer != nullptr) {
timer->stop();
g_timerCache->remove(timerId);
delete timer;
}
}
// app.popUpMenuEx()
bool JSApp::createPopUpMenuTree(int depth, QMenu *rootMenu, const QJSValue &arguments)
{
const int nArgs = arguments.property(QStringLiteral("length")).toInt();
// If no menu to add or if we got too deep in recursion
if (nArgs == 0 || depth > 20) {
return false;
}
for (int i = 0; i < nArgs; ++i) {
const QJSValue item = arguments.property(i);
const QString cName = item.property(QStringLiteral("cName")).toString();
const QJSValue cResultProperty = item.property(QStringLiteral("cResult"));
const QJSValue oSubMenu = item.property(QStringLiteral("oSubMenu"));
if (oSubMenu.isArray()) {
QMenu *subMenu = rootMenu->addMenu(cName);
createPopUpMenuTree(depth + 1, subMenu, oSubMenu);
} else {
QAction *a = rootMenu->addAction(cName);
if (cResultProperty.isUndefined()) {
a->setProperty(kResultProperty, cName);
} else {
a->setProperty(kResultProperty, cResultProperty.toString());
}
}
}
return true;
}
QJSValue JSApp::okular_popUpMenuEx(const QJSValue &arguments)
{
QMenu m;
// Object name is used for tests.
m.setObjectName(QStringLiteral("popUpMenuEx"));
if (!createPopUpMenuTree(0, &m, arguments)) {
return {};
}
QAction *result = m.exec(QCursor::pos());
return result ? result->property(kResultProperty).toString() : QString();
}
JSApp::JSApp(DocumentPrivate *doc, QTimer *watchdogTimer, QObject *parent)
: QObject(parent)
, m_doc(doc)
, m_watchdogTimer(watchdogTimer)
{
}
JSApp::~JSApp() = default;
QJSValue JSApp::wrapTimer(QTimer *timer) const
{
QJSValue timerObject = qjsEngine(this)->newObject();
timerObject.setProperty(OKULAR_TIMERID, timer->timerId());
g_timerCache->insert(timer->timerId(), timer);
return timerObject;
}
void JSApp::clearCachedFields()
{
if (g_timerCache) {
qDeleteAll(g_timerCache->begin(), g_timerCache->end());
g_timerCache->clear();
}
}

74
core/script/js_app_p.h Normal file
View File

@ -0,0 +1,74 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_APP_P_H
#define OKULAR_SCRIPT_JS_APP_P_H
#include <QJSValue>
#include <QObject>
#include <QPoint>
class QMenu;
class QTimer;
namespace Okular
{
class DocumentPrivate;
class JSApp : public QObject
{
Q_OBJECT
Q_PROPERTY(int formsVersion READ formsVersion CONSTANT)
Q_PROPERTY(QString language READ language CONSTANT)
Q_PROPERTY(int numPlugIns READ numPlugIns CONSTANT)
Q_PROPERTY(QString platform READ platform CONSTANT)
Q_PROPERTY(QJSValue plugIns READ plugIns CONSTANT)
Q_PROPERTY(QStringList printColorProfiles READ printColorProfiles CONSTANT)
Q_PROPERTY(QStringList printerNames READ printerNames CONSTANT)
Q_PROPERTY(QString viewerType READ viewerType CONSTANT)
Q_PROPERTY(QString viewerVariation READ viewerVariation CONSTANT)
Q_PROPERTY(int viewerVersion READ viewerVersion CONSTANT)
public:
explicit JSApp(DocumentPrivate *doc, QTimer *watchdogTimer, QObject *parent = nullptr);
~JSApp() override;
static void clearCachedFields();
int formsVersion() const;
QString language() const;
int numPlugIns() const;
QString platform() const;
QJSValue plugIns() const;
QStringList printColorProfiles() const;
QStringList printerNames() const;
QString viewerType() const;
QString viewerVariation() const;
int viewerVersion() const;
Q_INVOKABLE int alert(const QJSValue &arguments);
Q_INVOKABLE int alert(const QString &cMsg, int nIcon = 0, int nType = 0, const QString &cTitle = {}, const QJSValue &oDoc = {}, const QJSValue &oCheckbox = {});
Q_INVOKABLE void beep(int nType = 4);
Q_INVOKABLE QJSValue getNthPlugInName(int nIndex) const;
Q_INVOKABLE void goBack();
Q_INVOKABLE void goForward();
Q_INVOKABLE QJSValue setInterval(const QString &cExpr, int nMilliseconds);
Q_INVOKABLE void clearInterval(const QJSValue &oInterval);
Q_INVOKABLE QJSValue setTimeOut(const QString &cExpr, int nMilliseconds);
Q_INVOKABLE void clearTimeOut(const QJSValue &oTime);
Q_INVOKABLE QJSValue okular_popUpMenuEx(const QJSValue &arguments);
private:
QJSValue wrapTimer(QTimer *timer) const;
static bool createPopUpMenuTree(int depth, QMenu *rootMenu, const QJSValue &arguments);
DocumentPrivate *m_doc = nullptr;
QTimer *m_watchdogTimer = nullptr;
};
}
#endif

View File

@ -0,0 +1,82 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_console_p.h"
#include <QDebug>
#include "../debug_p.h"
using namespace Okular;
#ifdef OKULAR_JS_CONSOLE
#include <QLayout>
#include <QPlainTextEdit>
#include <KDialog>
#include <KStandardGuiItem>
K_GLOBAL_STATIC(KDialog, g_jsConsoleWindow)
static QPlainTextEdit *g_jsConsoleLog = 0;
static void createConsoleWindow()
{
if (g_jsConsoleWindow.exists())
return;
g_jsConsoleWindow->setButtons(KDialog::Close | KDialog::User1);
g_jsConsoleWindow->setButtonGuiItem(KDialog::User1, KStandardGuiItem::clear());
QVBoxLayout *mainLay = new QVBoxLayout(g_jsConsoleWindow->mainWidget());
mainLay->setContentsMargins(0, 0, 0, 0);
g_jsConsoleLog = new QPlainTextEdit(g_jsConsoleWindow->mainWidget());
g_jsConsoleLog->setReadOnly(true);
mainLay->addWidget(g_jsConsoleLog);
QObject::connect(g_jsConsoleWindow, SIGNAL(closeClicked()), g_jsConsoleWindow, SLOT(close()));
QObject::connect(g_jsConsoleWindow, SIGNAL(user1Clicked()), g_jsConsoleLog, SLOT(clear()));
}
#endif
void JSConsole::show()
{
#ifdef OKULAR_JS_CONSOLE
createConsoleWindow();
g_jsConsoleWindow->show();
#endif
}
void JSConsole::hide()
{
#ifdef OKULAR_JS_CONSOLE
if (!g_jsConsoleWindow.exists())
return;
g_jsConsoleWindow->hide();
#endif
}
void JSConsole::clear()
{
#ifdef OKULAR_JS_CONSOLE
if (!g_jsConsoleWindow.exists())
return;
g_jsConsoleLog->clear();
#endif
}
void JSConsole::println(const QString &cMessage)
{
#ifdef OKULAR_JS_CONSOLE
showConsole();
g_jsConsoleLog->appendPlainText(cMessage);
#else
Q_UNUSED(cMessage);
#endif
}

View File

@ -0,0 +1,27 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_CONSOLE_P_H
#define OKULAR_SCRIPT_JS_CONSOLE_P_H
#include <QObject>
namespace Okular
{
class JSConsole : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void clear();
Q_INVOKABLE void hide();
Q_INVOKABLE void println(const QString &cMessage);
Q_INVOKABLE void show();
};
}
#endif

58
core/script/js_data.cpp Normal file
View File

@ -0,0 +1,58 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_data_p.h"
#include <QDateTime>
#include "../document.h"
#include "js_display_p.h"
using namespace Okular;
QDateTime JSData::creationDate() const
{
return m_file->creationDate();
}
QString JSData::description() const
{
return m_file->description();
}
QString JSData::MIMEType() const
{
return QLatin1String("");
}
QDateTime JSData::modDate() const
{
return m_file->modificationDate();
}
QString JSData::name() const
{
return m_file->name();
}
QString JSData::path() const
{
return QLatin1String("");
}
int JSData::size() const
{
return m_file->size();
}
JSData::JSData(EmbeddedFile *f, QObject *parent)
: QObject(parent)
, m_file(f)
{
}
JSData::~JSData() = default;

46
core/script/js_data_p.h Normal file
View File

@ -0,0 +1,46 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_DATA_P_H
#define OKULAR_SCRIPT_JS_DATA_P_H
#include <QObject>
namespace Okular
{
class EmbeddedFile;
class JSData : public QObject
{
Q_OBJECT
Q_PROPERTY(QDateTime creationDate READ creationDate CONSTANT)
Q_PROPERTY(QString description READ description CONSTANT)
Q_PROPERTY(QString MIMEType READ MIMEType CONSTANT)
Q_PROPERTY(QDateTime modDate READ modDate CONSTANT)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(int size READ size CONSTANT)
public:
explicit JSData(EmbeddedFile *f, QObject *parent = nullptr);
~JSData() override;
QDateTime creationDate() const;
QString description() const;
QString MIMEType() const;
QDateTime modDate() const;
QString name() const;
QString path() const;
int size() const;
private:
EmbeddedFile *m_file = nullptr;
};
}
#endif

View File

@ -0,0 +1,36 @@
/*
SPDX-FileCopyrightText: 2019 João Netto <joaonetto901@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "../form.h"
#include "js_display_p.h"
#include <QString>
using namespace Okular;
// display.hidden
int JSDisplay::hidden() const
{
return FormDisplay::FormHidden;
}
// display.visible
int JSDisplay::visible() const
{
return FormDisplay::FormVisible;
}
// display.noView
int JSDisplay::noView() const
{
return FormDisplay::FormNoView;
}
// display.noPrint
int JSDisplay::noPrint() const
{
return FormDisplay::FormNoPrint;
}

View File

@ -0,0 +1,35 @@
/*
SPDX-FileCopyrightText: 2019 João Netto <joaonetto901@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_DISPLAY_P_H
#define OKULAR_SCRIPT_JS_DISPLAY_P_H
#include <QObject>
namespace Okular
{
/**
* The display types of the field.
*/
enum FormDisplay { FormVisible, FormHidden, FormNoPrint, FormNoView };
class JSDisplay : public QObject
{
Q_OBJECT
Q_PROPERTY(int hidden READ hidden CONSTANT)
Q_PROPERTY(int visible READ visible CONSTANT)
Q_PROPERTY(int noView READ noView CONSTANT)
Q_PROPERTY(int noPrint READ noPrint CONSTANT)
public:
int hidden() const;
int visible() const;
int noView() const;
int noPrint() const;
};
}
#endif

242
core/script/js_document.cpp Normal file
View File

@ -0,0 +1,242 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_document_p.h"
#include <qwidget.h>
#include <QDebug>
#include <QJSEngine>
#include <assert.h>
#include "../document_p.h"
#include "../form.h"
#include "../page.h"
#include "js_data_p.h"
#include "js_field_p.h"
#include "js_ocg_p.h"
using namespace Okular;
// Document.numPages
int JSDocument::numPages() const
{
return m_doc->m_pagesVector.count();
}
// Document.pageNum (getter)
int JSDocument::pageNum() const
{
return m_doc->m_parent->currentPage();
}
// Document.pageNum (setter)
void JSDocument::setPageNum(int page)
{
if (page == (int)m_doc->m_parent->currentPage()) {
return;
}
m_doc->m_parent->setViewportPage(page);
}
// Document.documentFileName
QString JSDocument::documentFileName() const
{
return m_doc->m_url.fileName();
}
// Document.filesize
int JSDocument::filesize() const
{
return m_doc->m_docSize;
}
// Document.path
QString JSDocument::path() const
{
return m_doc->m_url.toDisplayString(QUrl::PreferLocalFile);
}
// Document.URL
QString JSDocument::URL() const
{
return m_doc->m_url.toDisplayString();
}
// Document.permStatusReady
bool JSDocument::permStatusReady() const
{
return true;
}
// Document.dataObjects
QJSValue JSDocument::dataObjects() const
{
const QList<EmbeddedFile *> *files = m_doc->m_generator->embeddedFiles();
QJSValue dataObjects = qjsEngine(this)->newArray(files ? files->count() : 0);
if (files) {
QList<EmbeddedFile *>::ConstIterator it = files->begin(), itEnd = files->end();
for (int i = 0; it != itEnd; ++it, ++i) {
QJSValue newdata = qjsEngine(this)->newQObject(new JSData(*it));
dataObjects.setProperty(i, newdata);
}
}
return dataObjects;
}
// Document.external
bool JSDocument::external() const
{
QWidget *widget = m_doc->m_widget;
const bool isShell = (widget && widget->parentWidget() && widget->parentWidget()->objectName().startsWith(QLatin1String("okular::Shell")));
return !isShell;
}
// Document.numFields
int JSDocument::numFields() const
{
unsigned int numFields = 0;
for (const Page *pIt : std::as_const(m_doc->m_pagesVector)) {
numFields += pIt->formFields().size();
}
return numFields;
}
QJSValue JSDocument::info() const
{
QJSValue obj = qjsEngine(this)->newObject();
QSet<DocumentInfo::Key> keys;
keys << DocumentInfo::Title << DocumentInfo::Author << DocumentInfo::Subject << DocumentInfo::Keywords << DocumentInfo::Creator << DocumentInfo::Producer;
const DocumentInfo docinfo = m_doc->m_parent->documentInfo(keys);
#define KEY_GET(key, property) \
do { \
const QString data = docinfo.get(key); \
if (!data.isEmpty()) { \
obj.setProperty(QStringLiteral(property), data); \
obj.setProperty(QStringLiteral(property).toLower(), data); \
} \
} while (0);
KEY_GET(DocumentInfo::Title, "Title");
KEY_GET(DocumentInfo::Author, "Author");
KEY_GET(DocumentInfo::Subject, "Subject");
KEY_GET(DocumentInfo::Keywords, "Keywords");
KEY_GET(DocumentInfo::Creator, "Creator");
KEY_GET(DocumentInfo::Producer, "Producer");
#undef KEY_GET
return obj;
}
#define DOCINFO_GET_METHOD(key, name) \
QString JSDocument::name() const \
{ \
const DocumentInfo docinfo = m_doc->m_parent->documentInfo(QSet<DocumentInfo::Key>() << key); \
return docinfo.get(key); \
}
DOCINFO_GET_METHOD(DocumentInfo::Author, author)
DOCINFO_GET_METHOD(DocumentInfo::Creator, creator)
DOCINFO_GET_METHOD(DocumentInfo::Keywords, keywords)
DOCINFO_GET_METHOD(DocumentInfo::Producer, producer)
DOCINFO_GET_METHOD(DocumentInfo::Title, title)
DOCINFO_GET_METHOD(DocumentInfo::Subject, subject)
#undef DOCINFO_GET_METHOD
// Document.getField()
QJSValue JSDocument::getField(const QString &cName) const
{
QVector<Page *>::const_iterator pIt = m_doc->m_pagesVector.constBegin(), pEnd = m_doc->m_pagesVector.constEnd();
for (; pIt != pEnd; ++pIt) {
const QList<Okular::FormField *> pageFields = (*pIt)->formFields();
for (FormField *form : pageFields) {
if (form->fullyQualifiedName() == cName) {
return JSField::wrapField(qjsEngine(this), form, *pIt);
}
}
}
return QJSValue(QJSValue::UndefinedValue);
}
// Document.getPageLabel()
QString JSDocument::getPageLabel(int nPage) const
{
Page *p = m_doc->m_pagesVector.value(nPage);
return p ? p->label() : QString();
}
// Document.getPageRotation()
int JSDocument::getPageRotation(int nPage) const
{
Page *p = m_doc->m_pagesVector.value(nPage);
return p ? p->orientation() * 90 : 0;
}
// Document.gotoNamedDest()
void JSDocument::gotoNamedDest(const QString &cName) const
{
DocumentViewport viewport(m_doc->m_generator->metaData(QStringLiteral("NamedViewport"), cName).toString());
if (viewport.isValid()) {
m_doc->m_parent->setViewport(viewport);
}
}
// Document.syncAnnotScan()
void JSDocument::syncAnnotScan() const
{
}
// Document.getNthFieldName
QJSValue JSDocument::getNthFieldName(int nIndex) const
{
for (const Page *pIt : std::as_const(m_doc->m_pagesVector)) {
const QList<Okular::FormField *> pageFields = pIt->formFields();
if (nIndex < pageFields.size()) {
const Okular::FormField *form = pageFields[nIndex];
return form->fullyQualifiedName();
}
nIndex -= pageFields.size();
}
return QJSValue(QJSValue::UndefinedValue);
}
QJSValue JSDocument::getOCGs([[maybe_unused]] int nPage) const
{
QAbstractItemModel *model = m_doc->m_parent->layersModel();
QJSValue array = qjsEngine(this)->newArray(model->rowCount());
for (int i = 0; i < model->rowCount(); ++i) {
for (int j = 0; j < model->columnCount(); ++j) {
const QModelIndex index = model->index(i, j);
QJSValue item = qjsEngine(this)->newQObject(new JSOCG(model, i, j));
item.setProperty(QStringLiteral("name"), model->data(index, Qt::DisplayRole).toString());
item.setProperty(QStringLiteral("initState"), model->data(index, Qt::CheckStateRole).toBool());
array.setProperty(i, item);
}
}
return array;
}
JSDocument::JSDocument(DocumentPrivate *doc, QObject *parent)
: QObject(parent)
, m_doc(doc)
{
}
JSDocument::~JSDocument() = default;

View File

@ -0,0 +1,79 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_DOCUMENT_P_H
#define OKULAR_SCRIPT_JS_DOCUMENT_P_H
#include <QJSValue>
#include <QObject>
namespace Okular
{
class DocumentPrivate;
class JSDocument : public QObject
{
Q_OBJECT
Q_PROPERTY(int numPages READ numPages CONSTANT)
Q_PROPERTY(int pageNum READ pageNum WRITE setPageNum) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(QString documentFileName READ documentFileName CONSTANT)
Q_PROPERTY(int filesize READ filesize CONSTANT)
Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(QString URL READ URL CONSTANT)
Q_PROPERTY(bool permStatusReady READ permStatusReady CONSTANT)
Q_PROPERTY(QJSValue dataObjects READ dataObjects CONSTANT)
Q_PROPERTY(bool external READ external CONSTANT)
Q_PROPERTY(int numFields READ numFields CONSTANT)
// info properties
Q_PROPERTY(QJSValue info READ info CONSTANT)
Q_PROPERTY(QString author READ author CONSTANT)
Q_PROPERTY(QString creator READ creator CONSTANT)
Q_PROPERTY(QString keywords READ keywords CONSTANT)
Q_PROPERTY(QString producer READ producer CONSTANT)
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(QString subject READ subject CONSTANT)
public:
explicit JSDocument(DocumentPrivate *doc, QObject *parent = nullptr);
~JSDocument() override;
int numPages() const;
int pageNum() const;
void setPageNum(int pageNum);
QString documentFileName() const;
int filesize() const;
QString path() const;
QString URL() const;
bool permStatusReady() const;
QJSValue dataObjects() const;
bool external() const;
int numFields() const;
QJSValue info() const;
QString author() const;
QString creator() const;
QString keywords() const;
QString producer() const;
QString title() const;
QString subject() const;
Q_INVOKABLE QJSValue getField(const QString &cName) const;
Q_INVOKABLE QString getPageLabel(int nPage) const;
Q_INVOKABLE int getPageRotation(int nPage) const;
Q_INVOKABLE void gotoNamedDest(const QString &cName) const;
Q_INVOKABLE void syncAnnotScan() const;
Q_INVOKABLE QJSValue getNthFieldName(int nIndex) const;
Q_INVOKABLE QJSValue getOCGs(int nPage = -1) const;
private:
DocumentPrivate *m_doc = nullptr;
};
}
#endif

122
core/script/js_event.cpp Normal file
View File

@ -0,0 +1,122 @@
/*
SPDX-FileCopyrightText: 2018 Intevation GmbH <intevation@intevation.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_event_p.h"
#include "event_p.h"
#include "js_field_p.h"
#include <QJSEngine>
using namespace Okular;
// Event.name
QString JSEvent::name() const
{
return m_event->name();
}
// Event.type
QString JSEvent::type() const
{
return m_event->type();
}
// Event.targetName (getter)
QString JSEvent::targetName() const
{
return m_event->targetName();
}
// Event.targetName (setter)
void JSEvent::setTargetName(const QString &targetName)
{
m_event->setTargetName(targetName);
}
// Event.shift
bool JSEvent::shift() const
{
return m_event->shiftModifier();
}
// Event.source
QJSValue JSEvent::source() const
{
if (m_event->eventType() == Event::FieldCalculate) {
FormField *src = m_event->source();
if (src) {
return JSField::wrapField(qjsEngine(this), src, m_event->sourcePage());
}
}
return QJSValue(QJSValue::UndefinedValue);
}
// Event.target
QJSValue JSEvent::target() const
{
switch (m_event->eventType()) {
case Event::FieldCalculate:
case Event::FieldFormat:
case Event::FieldKeystroke:
case Event::FieldFocus:
case Event::FieldValidate:
case Event::FieldMouseUp: {
FormField *target = static_cast<FormField *>(m_event->target());
if (target) {
return JSField::wrapField(qjsEngine(this), target, m_event->targetPage());
}
break;
}
default: {
}
}
return QJSValue(QJSValue::UndefinedValue);
}
// Event.value (getter)
QJSValue JSEvent::value() const
{
return m_event->value().toString();
}
// Event.value (setter)
void JSEvent::setValue(const QJSValue &value)
{
m_event->setValue(QVariant(value.toString()));
}
// Event.rc (getter)
bool JSEvent::returnCode() const
{
return m_event->returnCode();
}
// Event.rc (setter)
void JSEvent::setReturnCode(bool rc)
{
m_event->setReturnCode(rc);
}
// Event.willCommit (getter)
bool JSEvent::willCommit() const
{
return m_event->willCommit();
}
// Event.change (getter)
QString JSEvent::change() const
{
return m_event->change();
}
JSEvent::JSEvent(Event *event, QObject *parent)
: QObject(parent)
, m_event(event)
{
}
JSEvent::~JSEvent() = default;

55
core/script/js_event_p.h Normal file
View File

@ -0,0 +1,55 @@
/*
SPDX-FileCopyrightText: 2018 Intevation GmbH <intevation@intevation.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_EVENT_P_H
#define OKULAR_SCRIPT_JS_EVENT_P_H
#include <QJSValue>
#include <QObject>
namespace Okular
{
class Event;
class JSEvent : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString type READ type CONSTANT)
Q_PROPERTY(QString targetName READ targetName WRITE setTargetName) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(bool shift READ shift CONSTANT)
Q_PROPERTY(QJSValue source READ source CONSTANT)
Q_PROPERTY(QJSValue target READ target CONSTANT)
Q_PROPERTY(bool willCommit READ willCommit CONSTANT)
Q_PROPERTY(QJSValue value READ value WRITE setValue) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(bool rc READ returnCode WRITE setReturnCode) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(QString change READ change CONSTANT)
public:
explicit JSEvent(Event *event, QObject *parent = nullptr);
~JSEvent() override;
QString name() const;
QString type() const;
QString targetName() const;
void setTargetName(const QString &targetName);
bool shift() const;
QJSValue source() const;
QJSValue target() const;
bool willCommit() const;
QJSValue value() const;
void setValue(const QJSValue &value);
bool returnCode() const;
void setReturnCode(bool rc);
QString change() const;
private:
Event *m_event = nullptr;
};
}
#endif

304
core/script/js_field.cpp Normal file
View File

@ -0,0 +1,304 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_field_p.h"
#include <QDebug>
#include <QHash>
#include <QJSEngine>
#include <QTimer>
#include "../debug_p.h"
#include "../document_p.h"
#include "../form.h"
#include "../page.h"
#include "../page_p.h"
#include "js_display_p.h"
using namespace Okular;
#define OKULAR_NAME QStringLiteral("okular_name")
typedef QHash<FormField *, Page *> FormCache;
Q_GLOBAL_STATIC(FormCache, g_fieldCache)
typedef QHash<QString, FormField *> ButtonCache;
Q_GLOBAL_STATIC(ButtonCache, g_buttonCache)
// Helper for modified fields
static void updateField(FormField *field)
{
Page *page = g_fieldCache->value(field);
if (page) {
Document *doc = PagePrivate::get(page)->m_doc->m_parent;
const int pageNumber = page->number();
QTimer::singleShot(0, doc, [doc, pageNumber] { doc->refreshPixmaps(pageNumber); });
Q_EMIT doc->refreshFormWidget(field);
} else {
qWarning() << "Could not get page of field" << field;
}
}
// Field.doc
QJSValue JSField::doc() const
{
return qjsEngine(this)->globalObject();
}
// Field.name
QString JSField::name() const
{
return m_field->fullyQualifiedName();
}
// Field.readonly (getter)
bool JSField::readonly() const
{
return m_field->isReadOnly();
}
// Field.readonly (setter)
void JSField::setReadonly(bool readonly)
{
m_field->setReadOnly(readonly);
updateField(m_field);
}
static QString fieldGetTypeHelper(const FormField *field)
{
switch (field->type()) {
case FormField::FormButton: {
const FormFieldButton *button = static_cast<const FormFieldButton *>(field);
switch (button->buttonType()) {
case FormFieldButton::Push:
return QStringLiteral("button");
case FormFieldButton::CheckBox:
return QStringLiteral("checkbox");
case FormFieldButton::Radio:
return QStringLiteral("radiobutton");
}
break;
}
case FormField::FormText:
return QStringLiteral("text");
case FormField::FormChoice: {
const FormFieldChoice *choice = static_cast<const FormFieldChoice *>(field);
switch (choice->choiceType()) {
case FormFieldChoice::ComboBox:
return QStringLiteral("combobox");
case FormFieldChoice::ListBox:
return QStringLiteral("listbox");
}
break;
}
case FormField::FormSignature:
return QStringLiteral("signature");
}
return QString();
}
// Field.type
QString JSField::type() const
{
return fieldGetTypeHelper(m_field);
}
QJSValue JSField::fieldGetValueCore(bool asString) const
{
QJSValue result(QJSValue::UndefinedValue);
switch (m_field->type()) {
case FormField::FormButton: {
const FormFieldButton *button = static_cast<const FormFieldButton *>(m_field);
if (button->state()) {
result = QStringLiteral("Yes");
} else {
result = QStringLiteral("Off");
}
break;
}
case FormField::FormText: {
const FormFieldText *text = static_cast<const FormFieldText *>(m_field);
const QLocale locale;
bool ok;
const double textAsNumber = locale.toDouble(text->text(), &ok);
if (ok && !asString) {
result = textAsNumber;
} else {
result = text->text();
}
break;
}
case FormField::FormChoice: {
const FormFieldChoice *choice = static_cast<const FormFieldChoice *>(m_field);
const QList<int> currentChoices = choice->currentChoices();
if (currentChoices.count() == 1) {
result = choice->exportValueForChoice(choice->choices().at(currentChoices[0]));
}
break;
}
case FormField::FormSignature: {
break;
}
}
qCDebug(OkularCoreDebug) << "fieldGetValueCore:"
<< " Field: " << m_field->fullyQualifiedName() << " Type: " << fieldGetTypeHelper(m_field) << " Value: " << result.toString() << (result.isString() ? "(as string)" : "");
return result;
}
// Field.value (getter)
QJSValue JSField::value() const
{
return fieldGetValueCore(/*asString*/ false);
}
// Field.value (setter)
void JSField::setValue(const QJSValue &value)
{
qCDebug(OkularCoreDebug) << "fieldSetValue: Field: " << m_field->fullyQualifiedName() << " Type: " << fieldGetTypeHelper(m_field) << " Value: " << value.toString();
switch (m_field->type()) {
case FormField::FormButton: {
FormFieldButton *button = static_cast<FormFieldButton *>(m_field);
const QString text = value.toString();
if (text == QStringLiteral("Yes")) {
button->setState(true);
updateField(m_field);
} else if (text == QStringLiteral("Off")) {
button->setState(false);
updateField(m_field);
}
break;
}
case FormField::FormText: {
FormFieldText *textField = static_cast<FormFieldText *>(m_field);
const QString text = value.toString();
if (text != textField->text()) {
textField->setText(text);
updateField(m_field);
}
break;
}
case FormField::FormChoice: {
FormFieldChoice *choice = static_cast<FormFieldChoice *>(m_field);
Q_UNUSED(choice); // ###
break;
}
case FormField::FormSignature: {
break;
}
}
}
// Field.valueAsString (getter)
QJSValue JSField::valueAsString() const
{
return fieldGetValueCore(/*asString*/ true);
}
// Field.hidden (getter)
bool JSField::hidden() const
{
return !m_field->isVisible();
}
// Field.hidden (setter)
void JSField::setHidden(bool hidden)
{
m_field->setVisible(!hidden);
updateField(m_field);
}
// Field.display (getter)
int JSField::display() const
{
bool visible = m_field->isVisible();
if (visible) {
return m_field->isPrintable() ? FormDisplay::FormVisible : FormDisplay::FormNoPrint;
}
return m_field->isPrintable() ? FormDisplay::FormNoView : FormDisplay::FormHidden;
}
// Field.display (setter)
void JSField::setDisplay(int display)
{
switch (display) {
case FormDisplay::FormVisible:
m_field->setVisible(true);
m_field->setPrintable(true);
break;
case FormDisplay::FormHidden:
m_field->setVisible(false);
m_field->setPrintable(false);
break;
case FormDisplay::FormNoPrint:
m_field->setVisible(true);
m_field->setPrintable(false);
break;
case FormDisplay::FormNoView:
m_field->setVisible(false);
m_field->setPrintable(true);
break;
}
updateField(m_field);
}
// Instead of getting the Icon, we pick the field.
QJSValue JSField::buttonGetIcon([[maybe_unused]] int nFace) const
{
QJSValue fieldObject = qjsEngine(this)->newObject();
fieldObject.setProperty(OKULAR_NAME, m_field->fullyQualifiedName());
g_buttonCache->insert(m_field->fullyQualifiedName(), m_field);
return fieldObject;
}
/*
* Now we send to the button what Icon should be drawn on it
*/
void JSField::buttonSetIcon(const QJSValue &oIcon, [[maybe_unused]] int nFace)
{
const QString fieldName = oIcon.property(OKULAR_NAME).toString();
if (m_field->type() == Okular::FormField::FormButton) {
FormFieldButton *button = static_cast<FormFieldButton *>(m_field);
const auto formField = g_buttonCache->value(fieldName);
if (formField) {
button->setIcon(formField);
}
}
updateField(m_field);
}
JSField::JSField(FormField *field, QObject *parent)
: QObject(parent)
, m_field(field)
{
}
JSField::~JSField() = default;
QJSValue JSField::wrapField(QJSEngine *engine, FormField *field, Page *page)
{
// ### cache unique wrapper
QJSValue f = engine->newQObject(new JSField(field));
f.setProperty(QStringLiteral("page"), page->number());
g_fieldCache->insert(field, page);
return f;
}
void JSField::clearCachedFields()
{
if (g_fieldCache.exists()) {
g_fieldCache->clear();
}
if (g_buttonCache.exists()) {
g_buttonCache->clear();
}
}

62
core/script/js_field_p.h Normal file
View File

@ -0,0 +1,62 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_FIELD_P_H
#define OKULAR_SCRIPT_JS_FIELD_P_H
#include <QJSValue>
#include <QObject>
namespace Okular
{
class FormField;
class Page;
class JSField : public QObject
{
Q_OBJECT
Q_PROPERTY(QJSValue doc READ doc CONSTANT)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(bool readonly READ readonly WRITE setReadonly) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(QString type READ type CONSTANT)
Q_PROPERTY(QJSValue value READ value WRITE setValue) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(QJSValue valueAsString READ valueAsString) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(bool hidden READ hidden WRITE setHidden) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(int display READ display WRITE setDisplay) // clazy:exclude=qproperty-without-notify
public:
explicit JSField(FormField *field, QObject *parent = nullptr);
~JSField() override;
static QJSValue wrapField(QJSEngine *engine, FormField *field, Page *page);
static void clearCachedFields();
QJSValue doc() const;
QString name() const;
bool readonly() const;
void setReadonly(bool readonly);
int display() const;
void setDisplay(int display);
QString type() const;
QJSValue value() const;
void setValue(const QJSValue &value);
QJSValue valueAsString() const;
bool hidden() const;
void setHidden(bool hidden);
Q_INVOKABLE QJSValue buttonGetIcon(int nFace = 0) const;
Q_INVOKABLE void buttonSetIcon(const QJSValue &oIcon, int nFace = 0);
private:
QJSValue fieldGetValueCore(bool asString) const;
FormField *m_field = nullptr;
};
}
#endif

View File

@ -0,0 +1,42 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_fullscreen_p.h"
#include "settings_core.h"
using namespace Okular;
bool JSFullscreen::loop() const
{
return SettingsCore::slidesLoop();
}
void JSFullscreen::setLoop(bool loop)
{
SettingsCore::setSlidesLoop(loop);
}
bool JSFullscreen::useTimer() const
{
return SettingsCore::slidesAdvance();
}
void JSFullscreen::setUseTimer(bool use)
{
SettingsCore::setSlidesAdvance(use);
}
int JSFullscreen::timeDelay() const
{
return SettingsCore::slidesAdvanceTime();
}
void JSFullscreen::setTimeDelay(int time)
{
SettingsCore::setSlidesAdvanceTime(time);
}

View File

@ -0,0 +1,33 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_FULLSCREEN_P_H
#define OKULAR_SCRIPT_JS_FULLSCREEN_P_H
#include <QObject>
namespace Okular
{
class JSFullscreen : public QObject
{
Q_OBJECT
Q_PROPERTY(bool loop READ loop WRITE setLoop) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(bool useTimer READ useTimer WRITE setUseTimer) // clazy:exclude=qproperty-without-notify
Q_PROPERTY(int timeDelay READ timeDelay WRITE setTimeDelay) // clazy:exclude=qproperty-without-notify
public:
bool loop() const;
void setLoop(bool loop);
bool useTimer() const;
void setUseTimer(bool use);
int timeDelay() const;
void setTimeDelay(int time);
};
}
#endif

39
core/script/js_ocg.cpp Normal file
View File

@ -0,0 +1,39 @@
/*
SPDX-FileCopyrightText: 2019 João Netto <joaonetto901@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_ocg_p.h"
#include <QAbstractItemModel>
#include <QDebug>
#include <QString>
using namespace Okular;
// OCG.state (getter)
bool JSOCG::state() const
{
const QModelIndex index = m_model->index(m_i, m_j);
return m_model->data(index, Qt::CheckStateRole).toBool();
}
// OCG.state (setter)
void JSOCG::setState(bool state)
{
const QModelIndex index = m_model->index(m_i, m_j);
m_model->setData(index, QVariant(state ? Qt::Checked : Qt::Unchecked), Qt::CheckStateRole);
}
JSOCG::JSOCG(QAbstractItemModel *model, int i, int j, QObject *parent)
: QObject(parent)
, m_model(model)
, m_i(i)
, m_j(j)
{
}
JSOCG::~JSOCG() = default;

36
core/script/js_ocg_p.h Normal file
View File

@ -0,0 +1,36 @@
/*
SPDX-FileCopyrightText: 2019 João Netto <joaonetto901@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_OCG_P_H
#define OKULAR_SCRIPT_JS_OCG_P_H
#include <QObject>
class QAbstractItemModel;
namespace Okular
{
class JSOCG : public QObject
{
Q_OBJECT
Q_PROPERTY(bool state READ state WRITE setState) // clazy:exclude=qproperty-without-notify
public:
explicit JSOCG(QAbstractItemModel *model, int i, int j, QObject *parent = nullptr);
~JSOCG() override;
bool state() const;
void setState(bool state);
private:
QAbstractItemModel *m_model = nullptr;
int m_i;
int m_j;
};
}
#endif

15
core/script/js_spell.cpp Normal file
View File

@ -0,0 +1,15 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_spell_p.h"
using namespace Okular;
bool JSSpell::available() const
{
return false;
}

View File

@ -5,19 +5,19 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_KJS_CONSOLE_P_H
#define OKULAR_SCRIPT_KJS_CONSOLE_P_H
#ifndef OKULAR_SCRIPT_JS_SPELL_P_H
#define OKULAR_SCRIPT_JS_SPELL_P_H
class KJSContext;
class KJSObject;
#include <QObject>
namespace Okular
{
class JSConsole
class JSSpell : public QObject
{
Q_OBJECT
Q_PROPERTY(bool available READ available CONSTANT)
public:
static void initType(KJSContext *ctx);
static KJSObject object(KJSContext *ctx);
bool available() const;
};
}

133
core/script/js_util.cpp Normal file
View File

@ -0,0 +1,133 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "js_util_p.h"
#include <QDateTime>
#include <QDebug>
#include <QJSEngine>
#include <QLocale>
#include <QRegularExpression>
#include <QUrl>
#include <cmath>
using namespace Okular;
QJSValue JSUtil::crackURL(const QString &cURL) const
{
QUrl url(QUrl::fromLocalFile(cURL));
if (!url.isValid()) {
return qjsEngine(this)->newErrorObject(QJSValue::URIError, QStringLiteral("Invalid URL"));
}
if (url.scheme() != QLatin1String("file") || url.scheme() != QLatin1String("http") || url.scheme() != QLatin1String("https")) {
return qjsEngine(this)->newErrorObject(QJSValue::URIError, QStringLiteral("Protocol not valid: '") + url.scheme() + QLatin1Char('\''));
}
QJSValue obj;
obj.setProperty(QStringLiteral("cScheme"), url.scheme());
if (!url.userName().isEmpty()) {
obj.setProperty(QStringLiteral("cUser"), url.userName());
}
if (!url.password().isEmpty()) {
obj.setProperty(QStringLiteral("cPassword"), url.password());
}
obj.setProperty(QStringLiteral("cHost"), url.host());
obj.setProperty(QStringLiteral("nPort"), url.port(80));
// TODO cPath (Optional) The path portion of the URL.
// TODO cParameters (Optional) The parameter string portion of the URL.
if (url.hasFragment()) {
obj.setProperty(QStringLiteral("cFragments"), url.fragment(QUrl::FullyDecoded));
}
return obj;
}
QJSValue JSUtil::printd(const QJSValue &oFormat, const QDateTime &oDate) const
{
QString format;
QLocale defaultLocale;
if (oFormat.isNumber()) {
int formatType = oFormat.toInt();
switch (formatType) {
case 0:
format = QStringLiteral("D:yyyyMMddHHmmss");
break;
case 1:
format = QStringLiteral("yyyy.MM.dd HH:mm:ss");
break;
case 2:
format = defaultLocale.dateTimeFormat(QLocale::ShortFormat);
if (!format.contains(QStringLiteral("ss"))) {
format.insert(format.indexOf(QStringLiteral("mm")) + 2, QStringLiteral(":ss"));
}
break;
}
} else {
format = oFormat.toString().replace(QLatin1String("tt"), QLatin1String("ap"));
format.replace(QLatin1Char('t'), QLatin1Char('a'));
for (QChar &formatChar : format) {
if (formatChar == QLatin1Char('M')) {
formatChar = QLatin1Char('m');
} else if (formatChar == QLatin1Char('m')) {
formatChar = QLatin1Char('M');
}
}
}
return defaultLocale.toString(oDate, format);
}
/** Converts a Number to a String using l10n
*
* String numberToString( Number number, String format = 'g', int precision = 6,
* String LocaleName = system )
*/
QString JSUtil::numberToString(double number, const QString &fmt, int precision, const QString &localeName) const
{
if (std::isnan(number)) {
return QStringLiteral("NaN");
}
QChar format = QLatin1Char('g');
if (!fmt.isEmpty()) {
format = fmt[0];
}
QLocale locale;
if (!localeName.isEmpty()) {
locale = QLocale(localeName);
}
return locale.toString(number, format.toLatin1(), precision);
}
/** Converts a String to a Number trying with the current locale first and
* if that fails trying with the reverse locale for the decimal separator
*
* Number stringToNumber( String number ) */
double JSUtil::stringToNumber(const QString &number) const
{
if (number.isEmpty()) {
return 0;
}
const QLocale locale;
bool ok;
double converted = locale.toDouble(number, &ok);
if (!ok) {
const QLocale locale2(locale.decimalPoint() == QLatin1Char('.') ? QStringLiteral("de") : QStringLiteral("en"));
converted = locale2.toDouble(number, &ok);
if (!ok) {
return NAN;
}
}
return converted;
}

28
core/script/js_util_p.h Normal file
View File

@ -0,0 +1,28 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_JS_UTIL_P_H
#define OKULAR_SCRIPT_JS_UTIL_P_H
#include <QJSValue>
#include <QObject>
namespace Okular
{
class JSUtil : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QJSValue crackURL(const QString &cURL) const;
Q_INVOKABLE QJSValue printd(const QJSValue &oFormat, const QDateTime &oDate) const;
Q_INVOKABLE double stringToNumber(const QString &number) const;
Q_INVOKABLE QString numberToString(double number, const QString &fmt = QStringLiteral("g"), int precision = 6, const QString &localeName = {}) const;
};
}
#endif

View File

@ -1,439 +0,0 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kjs_app_p.h"
#include <kjs/kjsarguments.h>
#include <kjs/kjsinterpreter.h>
#include <kjs/kjsobject.h>
#include <kjs/kjsprototype.h>
#include <kjs_version.h>
#include <QApplication>
#include <QLocale>
#include <QTimer>
#include <KLocalizedString>
#include <QCheckBox>
#include <QMessageBox>
#include "../document_p.h"
#include "../scripter.h"
#include "config-okular.h"
#include "kjs_fullscreen_p.h"
using namespace Okular;
#define OKULAR_TIMERID QStringLiteral("okular_timerID")
static KJSPrototype *g_appProto;
typedef QHash<int, QTimer *> TimerCache;
Q_GLOBAL_STATIC(TimerCache, g_timerCache)
// the acrobat version we fake
static const double fake_acroversion = 8.00;
static const struct FakePluginInfo {
const char *name;
bool certified;
bool loaded;
const char *path;
} s_fake_plugins[] = {{"Annots", true, true, ""}, {"EFS", true, true, ""}, {"EScript", true, true, ""}, {"Forms", true, true, ""}, {"ReadOutLoud", true, true, ""}, {"WebLink", true, true, ""}};
static const int s_num_fake_plugins = sizeof(s_fake_plugins) / sizeof(s_fake_plugins[0]);
static KJSObject appGetFormsVersion(KJSContext *, void *)
{
// faking a bit...
return KJSNumber(fake_acroversion);
}
static KJSObject appGetLanguage(KJSContext *, void *)
{
QLocale locale;
QString lang = QLocale::languageToString(locale.language());
QString country = QLocale::countryToString(locale.country());
QString acroLang = QStringLiteral("ENU");
if (lang == QLatin1String("da")) {
acroLang = QStringLiteral("DAN"); // Danish
} else if (lang == QLatin1String("de")) {
acroLang = QStringLiteral("DEU"); // German
} else if (lang == QLatin1String("en")) {
acroLang = QStringLiteral("ENU"); // English
} else if (lang == QLatin1String("es")) {
acroLang = QStringLiteral("ESP"); // Spanish
} else if (lang == QLatin1String("fr")) {
acroLang = QStringLiteral("FRA"); // French
} else if (lang == QLatin1String("it")) {
acroLang = QStringLiteral("ITA"); // Italian
} else if (lang == QLatin1String("ko")) {
acroLang = QStringLiteral("KOR"); // Korean
} else if (lang == QLatin1String("ja")) {
acroLang = QStringLiteral("JPN"); // Japanese
} else if (lang == QLatin1String("nl")) {
acroLang = QStringLiteral("NLD"); // Dutch
} else if (lang == QLatin1String("pt") && country == QLatin1String("BR")) {
acroLang = QStringLiteral("PTB"); // Brazilian Portuguese
} else if (lang == QLatin1String("fi")) {
acroLang = QStringLiteral("SUO"); // Finnish
} else if (lang == QLatin1String("sv")) {
acroLang = QStringLiteral("SVE"); // Swedish
} else if (lang == QLatin1String("zh") && country == QLatin1String("CN")) {
acroLang = QStringLiteral("CHS"); // Chinese Simplified
} else if (lang == QLatin1String("zh") && country == QLatin1String("TW")) {
acroLang = QStringLiteral("CHT"); // Chinese Traditional
}
return KJSString(acroLang);
}
static KJSObject appGetNumPlugins(KJSContext *, void *)
{
return KJSNumber(s_num_fake_plugins);
}
static KJSObject appGetPlatform(KJSContext *, void *)
{
#if defined(Q_OS_WIN)
return KJSString(QString::fromLatin1("WIN"));
#elif defined(Q_OS_MAC)
return KJSString(QString::fromLatin1("MAC"));
#else
return KJSString(QStringLiteral("UNIX"));
#endif
}
static KJSObject appGetPlugIns(KJSContext *context, void *)
{
KJSArray plugins(context, s_num_fake_plugins);
for (int i = 0; i < s_num_fake_plugins; ++i) {
const FakePluginInfo &info = s_fake_plugins[i];
KJSObject plugin;
plugin.setProperty(context, QStringLiteral("certified"), info.certified);
plugin.setProperty(context, QStringLiteral("loaded"), info.loaded);
plugin.setProperty(context, QStringLiteral("name"), info.name);
plugin.setProperty(context, QStringLiteral("path"), info.path);
plugin.setProperty(context, QStringLiteral("version"), fake_acroversion);
plugins.setProperty(context, QString::number(i), plugin);
}
return plugins;
}
static KJSObject appGetPrintColorProfiles(KJSContext *context, void *)
{
return KJSArray(context, 0);
}
static KJSObject appGetPrinterNames(KJSContext *context, void *)
{
return KJSArray(context, 0);
}
static KJSObject appGetViewerType(KJSContext *, void *)
{
// faking a bit...
return KJSString(QStringLiteral("Reader"));
}
static KJSObject appGetViewerVariation(KJSContext *, void *)
{
// faking a bit...
return KJSString(QStringLiteral("Reader"));
}
static KJSObject appGetViewerVersion(KJSContext *, void *)
{
// faking a bit...
return KJSNumber(fake_acroversion);
}
/*
Alert function defined in the reference, it shows a Dialog Box with options.
app.alert()
*/
static KJSObject appAlert(KJSContext *context, void *, const KJSArguments &arguments)
{
if (arguments.count() < 1) {
return context->throwException(i18n("Missing alert type"));
}
KJSObject cMsg, nIcon, nType, cTitle, oCheckbox;
if (arguments.at(0).isObject()) {
KJSObject obj = arguments.at(0);
cMsg = obj.property(context, QStringLiteral("cMsg"));
nIcon = obj.property(context, QStringLiteral("nIcon"));
nType = obj.property(context, QStringLiteral("nType"));
cTitle = obj.property(context, QStringLiteral("cTitle"));
oCheckbox = obj.property(context, QStringLiteral("oCheckbox"));
} else {
cMsg = arguments.at(0);
nIcon = arguments.at(1);
nType = arguments.at(2);
cTitle = arguments.at(3);
oCheckbox = arguments.at(5);
}
QMessageBox::Icon icon = QMessageBox::Critical;
if (nIcon.isNumber()) {
switch (nIcon.toInt32(context)) {
case 0:
icon = QMessageBox::Critical;
break;
case 1:
icon = QMessageBox::Warning;
break;
case 2:
icon = QMessageBox::Question;
break;
case 3:
icon = QMessageBox::Information;
break;
}
}
const QString title = cTitle.isString() ? cTitle.toString(context) : QStringLiteral("Okular");
QMessageBox box(icon, title, cMsg.toString(context));
QMessageBox::StandardButtons buttons = QMessageBox::Ok;
if (nType.isNumber()) {
switch (nType.toInt32(context)) {
case 0:
buttons = QMessageBox::Ok;
break;
case 1:
buttons = QMessageBox::Ok | QMessageBox::Cancel;
break;
case 2:
buttons = QMessageBox::Yes | QMessageBox::No;
break;
case 3:
buttons = QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel;
break;
}
}
box.setStandardButtons(buttons);
QCheckBox *checkBox = nullptr;
if (oCheckbox.isObject()) {
KJSObject oMsg = oCheckbox.property(context, QStringLiteral("cMsg"));
QString msg = i18n("Do not show this message again");
if (oMsg.isString()) {
msg = oMsg.toString(context);
}
bool bInitialValue = false;
KJSObject value = oCheckbox.property(context, QStringLiteral("bInitialValue"));
if (value.isBoolean()) {
bInitialValue = value.toBoolean(context);
}
checkBox = new QCheckBox(msg);
checkBox->setChecked(bInitialValue);
box.setCheckBox(checkBox);
}
#if KJS_VERSION > QT_VERSION_CHECK(5, 71, 0)
// halt timeout until the user has responded
context->interpreter().stopTimeoutCheck();
#endif
int button = box.exec();
#if KJS_VERSION > QT_VERSION_CHECK(5, 71, 0)
// restart max allowed time
context->interpreter().startTimeoutCheck();
#endif
int ret;
switch (button) {
case QMessageBox::Ok:
ret = 1;
break;
case QMessageBox::Cancel:
ret = 2;
break;
case QMessageBox::No:
ret = 3;
break;
case QMessageBox::Yes:
ret = 4;
break;
}
if (checkBox) {
oCheckbox.setProperty(context, QStringLiteral("bAfterValue"), checkBox->isChecked());
}
delete checkBox;
return KJSNumber(ret);
}
static KJSObject appBeep(KJSContext *context, void *, const KJSArguments &arguments)
{
if (arguments.count() < 1) {
return context->throwException(QStringLiteral("Missing beep type"));
}
QApplication::beep();
return KJSUndefined();
}
static KJSObject appGetNthPlugInName(KJSContext *context, void *, const KJSArguments &arguments)
{
if (arguments.count() < 1) {
return context->throwException(QStringLiteral("Missing plugin index"));
}
const int nIndex = arguments.at(0).toInt32(context);
if (nIndex < 0 || nIndex >= s_num_fake_plugins) {
return context->throwException(QStringLiteral("PlugIn index out of bounds"));
}
const FakePluginInfo &info = s_fake_plugins[nIndex];
return KJSString(info.name);
}
static KJSObject appGoBack(KJSContext *, void *object, const KJSArguments &)
{
const DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object);
if (doc->m_parent->historyAtBegin()) {
return KJSUndefined();
}
doc->m_parent->setPrevViewport();
return KJSUndefined();
}
static KJSObject appGoForward(KJSContext *, void *object, const KJSArguments &)
{
const DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object);
if (doc->m_parent->historyAtEnd()) {
return KJSUndefined();
}
doc->m_parent->setNextViewport();
return KJSUndefined();
}
// app.setInterval()
static KJSObject appSetInterval(KJSContext *ctx, void *object, const KJSArguments &arguments)
{
DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object);
const QString function = arguments.at(0).toString(ctx) + QLatin1Char(';');
const int interval = arguments.at(1).toInt32(ctx);
QTimer *timer = new QTimer();
QObject::connect(timer, &QTimer::timeout, doc->m_parent, [=]() { doc->executeScript(function); });
timer->start(interval);
return JSApp::wrapTimer(ctx, timer);
}
// app.clearInterval()
static KJSObject appClearInterval(KJSContext *ctx, void *, const KJSArguments &arguments)
{
KJSObject timerObject = arguments.at(0);
const int timerId = timerObject.property(ctx, OKULAR_TIMERID).toInt32(ctx);
QTimer *timer = g_timerCache->value(timerId);
if (timer != nullptr) {
timer->stop();
g_timerCache->remove(timerId);
delete timer;
}
return KJSUndefined();
}
// app.setTimeOut()
static KJSObject appSetTimeOut(KJSContext *ctx, void *object, const KJSArguments &arguments)
{
DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object);
const QString function = arguments.at(0).toString(ctx) + QLatin1Char(';');
const int interval = arguments.at(1).toInt32(ctx);
QTimer *timer = new QTimer();
timer->setSingleShot(true);
QObject::connect(timer, &QTimer::timeout, doc->m_parent, [=]() { doc->executeScript(function); });
timer->start(interval);
return JSApp::wrapTimer(ctx, timer);
}
// app.clearTimeOut()
static KJSObject appClearTimeOut(KJSContext *ctx, void *, const KJSArguments &arguments)
{
KJSObject timerObject = arguments.at(0);
const int timerId = timerObject.property(ctx, OKULAR_TIMERID).toInt32(ctx);
QTimer *timer = g_timerCache->value(timerId);
if (timer != nullptr) {
timer->stop();
g_timerCache->remove(timerId);
delete timer;
}
return KJSUndefined();
}
void JSApp::initType(KJSContext *ctx)
{
static bool initialized = false;
if (initialized) {
return;
}
initialized = true;
g_appProto = new KJSPrototype();
g_appProto->defineProperty(ctx, QStringLiteral("formsVersion"), appGetFormsVersion);
g_appProto->defineProperty(ctx, QStringLiteral("language"), appGetLanguage);
g_appProto->defineProperty(ctx, QStringLiteral("numPlugIns"), appGetNumPlugins);
g_appProto->defineProperty(ctx, QStringLiteral("platform"), appGetPlatform);
g_appProto->defineProperty(ctx, QStringLiteral("plugIns"), appGetPlugIns);
g_appProto->defineProperty(ctx, QStringLiteral("printColorProfiles"), appGetPrintColorProfiles);
g_appProto->defineProperty(ctx, QStringLiteral("printerNames"), appGetPrinterNames);
g_appProto->defineProperty(ctx, QStringLiteral("viewerType"), appGetViewerType);
g_appProto->defineProperty(ctx, QStringLiteral("viewerVariation"), appGetViewerVariation);
g_appProto->defineProperty(ctx, QStringLiteral("viewerVersion"), appGetViewerVersion);
g_appProto->defineFunction(ctx, QStringLiteral("alert"), appAlert);
g_appProto->defineFunction(ctx, QStringLiteral("beep"), appBeep);
g_appProto->defineFunction(ctx, QStringLiteral("getNthPlugInName"), appGetNthPlugInName);
g_appProto->defineFunction(ctx, QStringLiteral("goBack"), appGoBack);
g_appProto->defineFunction(ctx, QStringLiteral("goForward"), appGoForward);
g_appProto->defineFunction(ctx, QStringLiteral("setInterval"), appSetInterval);
g_appProto->defineFunction(ctx, QStringLiteral("clearInterval"), appClearInterval);
g_appProto->defineFunction(ctx, QStringLiteral("setTimeOut"), appSetTimeOut);
g_appProto->defineFunction(ctx, QStringLiteral("clearTimeOut"), appClearTimeOut);
}
KJSObject JSApp::object(KJSContext *ctx, DocumentPrivate *doc)
{
return g_appProto->constructObject(ctx, doc);
}
KJSObject JSApp::wrapTimer(KJSContext *ctx, QTimer *timer)
{
KJSObject timerObject = g_appProto->constructObject(ctx, timer);
timerObject.setProperty(ctx, OKULAR_TIMERID, timer->timerId());
g_timerCache->insert(timer->timerId(), timer);
return timerObject;
}
void JSApp::clearCachedFields()
{
if (g_timerCache) {
qDeleteAll(g_timerCache->begin(), g_timerCache->end());
g_timerCache->clear();
}
}

View File

@ -1,30 +0,0 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_KJS_APP_P_H
#define OKULAR_SCRIPT_KJS_APP_P_H
class KJSContext;
class KJSObject;
class QTimer;
namespace Okular
{
class DocumentPrivate;
class JSApp
{
public:
static void initType(KJSContext *ctx);
static KJSObject object(KJSContext *ctx, DocumentPrivate *doc);
static KJSObject wrapTimer(KJSContext *ctx, QTimer *timer);
static void clearCachedFields();
};
}
#endif

View File

@ -1,145 +0,0 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kjs_console_p.h"
#include <kjs/kjsarguments.h>
#include <kjs/kjsobject.h>
#include <kjs/kjsprototype.h>
#include <QDebug>
#include "../debug_p.h"
using namespace Okular;
static KJSPrototype *g_consoleProto;
#ifdef OKULAR_JS_CONSOLE
#include <QLayout>
#include <QPlainTextEdit>
#include <KDialog>
#include <KStandardGuiItem>
K_GLOBAL_STATIC(KDialog, g_jsConsoleWindow)
static QPlainTextEdit *g_jsConsoleLog = 0;
static void createConsoleWindow()
{
if (g_jsConsoleWindow.exists())
return;
g_jsConsoleWindow->setButtons(KDialog::Close | KDialog::User1);
g_jsConsoleWindow->setButtonGuiItem(KDialog::User1, KStandardGuiItem::clear());
QVBoxLayout *mainLay = new QVBoxLayout(g_jsConsoleWindow->mainWidget());
mainLay->setContentsMargins(0, 0, 0, 0);
g_jsConsoleLog = new QPlainTextEdit(g_jsConsoleWindow->mainWidget());
g_jsConsoleLog->setReadOnly(true);
mainLay->addWidget(g_jsConsoleLog);
QObject::connect(g_jsConsoleWindow, SIGNAL(closeClicked()), g_jsConsoleWindow, SLOT(close()));
QObject::connect(g_jsConsoleWindow, SIGNAL(user1Clicked()), g_jsConsoleLog, SLOT(clear()));
}
static void showConsole()
{
createConsoleWindow();
g_jsConsoleWindow->show();
}
static void hideConsole()
{
if (!g_jsConsoleWindow.exists())
return;
g_jsConsoleWindow->hide();
}
static void clearConsole()
{
if (!g_jsConsoleWindow.exists())
return;
g_jsConsoleLog->clear();
}
static void outputToConsole(const QString &message)
{
showConsole();
g_jsConsoleLog->appendPlainText(message);
}
#else /* OKULAR_JS_CONSOLE */
static void showConsole()
{
}
static void hideConsole()
{
}
static void clearConsole()
{
}
static void outputToConsole(const QString &cMessage)
{
qCDebug(OkularCoreDebug) << "CONSOLE:" << cMessage;
}
#endif /* OKULAR_JS_CONSOLE */
static KJSObject consoleClear(KJSContext *, void *, const KJSArguments &)
{
clearConsole();
return KJSUndefined();
}
static KJSObject consoleHide(KJSContext *, void *, const KJSArguments &)
{
hideConsole();
return KJSUndefined();
}
static KJSObject consolePrintln(KJSContext *ctx, void *, const KJSArguments &arguments)
{
QString cMessage = arguments.at(0).toString(ctx);
outputToConsole(cMessage);
return KJSUndefined();
}
static KJSObject consoleShow(KJSContext *, void *, const KJSArguments &)
{
showConsole();
return KJSUndefined();
}
void JSConsole::initType(KJSContext *ctx)
{
static bool initialized = false;
if (initialized) {
return;
}
initialized = true;
g_consoleProto = new KJSPrototype();
g_consoleProto->defineFunction(ctx, QStringLiteral("clear"), consoleClear);
g_consoleProto->defineFunction(ctx, QStringLiteral("hide"), consoleHide);
g_consoleProto->defineFunction(ctx, QStringLiteral("println"), consolePrintln);
g_consoleProto->defineFunction(ctx, QStringLiteral("hide"), consoleShow);
}
KJSObject JSConsole::object(KJSContext *ctx)
{
return g_consoleProto->constructObject(ctx, nullptr);
}

View File

@ -1,89 +0,0 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kjs_data_p.h"
#include <kjs/kjsobject.h>
#include <kjs/kjsprototype.h>
#include <QDateTime>
#include "../document.h"
using namespace Okular;
static KJSPrototype *g_dataProto;
static KJSObject dataGetCreationDate(KJSContext *ctx, void *object)
{
const EmbeddedFile *file = reinterpret_cast<EmbeddedFile *>(object);
return KJSDate(ctx, file->creationDate());
}
static KJSObject dataGetDescription(KJSContext *, void *object)
{
const EmbeddedFile *file = reinterpret_cast<EmbeddedFile *>(object);
return KJSString(file->description());
}
static KJSObject dataGetMIMEType(KJSContext *, void *)
{
return KJSString("");
}
static KJSObject dataGetModDate(KJSContext *ctx, void *object)
{
const EmbeddedFile *file = reinterpret_cast<EmbeddedFile *>(object);
return KJSDate(ctx, file->modificationDate());
}
static KJSObject dataGetName(KJSContext *, void *object)
{
const EmbeddedFile *file = reinterpret_cast<EmbeddedFile *>(object);
return KJSString(file->name());
}
static KJSObject dataGetPath(KJSContext *, void *)
{
return KJSString("");
}
static KJSObject dataGetSize(KJSContext *, void *object)
{
const EmbeddedFile *file = reinterpret_cast<EmbeddedFile *>(object);
return KJSNumber(file->size());
}
void JSData::initType(KJSContext *ctx)
{
static bool initialized = false;
if (initialized) {
return;
}
initialized = true;
if (!g_dataProto) {
g_dataProto = new KJSPrototype();
}
g_dataProto->defineProperty(ctx, QStringLiteral("creationDate"), dataGetCreationDate);
g_dataProto->defineProperty(ctx, QStringLiteral("description"), dataGetDescription);
g_dataProto->defineProperty(ctx, QStringLiteral("MIMEType"), dataGetMIMEType);
g_dataProto->defineProperty(ctx, QStringLiteral("modDate"), dataGetModDate);
g_dataProto->defineProperty(ctx, QStringLiteral("name"), dataGetName);
g_dataProto->defineProperty(ctx, QStringLiteral("path"), dataGetPath);
g_dataProto->defineProperty(ctx, QStringLiteral("size"), dataGetSize);
}
KJSObject JSData::wrapFile(KJSContext *ctx, EmbeddedFile *f)
{
return g_dataProto->constructObject(ctx, f);
}

View File

@ -1,27 +0,0 @@
/*
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org>
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_KJS_DATA_P_H
#define OKULAR_SCRIPT_KJS_DATA_P_H
class KJSContext;
class KJSObject;
namespace Okular
{
class EmbeddedFile;
class JSData
{
public:
static void initType(KJSContext *ctx);
static KJSObject wrapFile(KJSContext *ctx, EmbeddedFile *f);
};
}
#endif

View File

@ -1,62 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 João Netto <joaonetto901@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "../form.h"
#include "kjs_display_p.h"
#include <kjs/kjsobject.h>
#include <kjs/kjsprototype.h>
#include <QString>
using namespace Okular;
static KJSPrototype *g_displayProto;
// display.hidden
static KJSObject displayGetHidden(KJSContext *, void *)
{
return KJSNumber(FormDisplay::FormHidden);
}
// display.visible
static KJSObject displayGetVisible(KJSContext *, void *)
{
return KJSNumber(FormDisplay::FormVisible);
}
// display.noView
static KJSObject displayGetNoView(KJSContext *, void *)
{
return KJSNumber(FormDisplay::FormNoView);
}
// display.noPrint
static KJSObject displayGetNoPrint(KJSContext *, void *)
{
return KJSNumber(FormDisplay::FormNoPrint);
}
void JSDisplay::initType(KJSContext *ctx)
{
static bool initialized = false;
if (initialized) {
return;
}
initialized = true;
g_displayProto = new KJSPrototype();
g_displayProto->defineProperty(ctx, QStringLiteral("hidden"), displayGetHidden);
g_displayProto->defineProperty(ctx, QStringLiteral("visible"), displayGetVisible);
g_displayProto->defineProperty(ctx, QStringLiteral("noView"), displayGetNoView);
g_displayProto->defineProperty(ctx, QStringLiteral("noPrint"), displayGetNoPrint);
}
KJSObject JSDisplay::object(KJSContext *ctx)
{
return g_displayProto->constructObject(ctx, nullptr);
}

View File

@ -1,29 +0,0 @@
/*
SPDX-FileCopyrightText: 2019 João Netto <joaonetto901@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef OKULAR_SCRIPT_KJS_DISPLAY_P_H
#define OKULAR_SCRIPT_KJS_DISPLAY_P_H
class KJSContext;
class KJSObject;
namespace Okular
{
/**
* The display types of the field.
*/
enum FormDisplay { FormVisible, FormHidden, FormNoPrint, FormNoView };
class JSDisplay
{
public:
static void initType(KJSContext *ctx);
static KJSObject object(KJSContext *ctx);
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More