From 88b5922cd67c5ea10ba219b46680c7b4db5f878f Mon Sep 17 00:00:00 2001 From: markffan Date: Mon, 17 Mar 2025 10:37:58 +0800 Subject: [PATCH 01/36] Rename qt to viewer, add third-party libraries and Remove support for Qt5 --- .github/workflows/build.yml | 2 +- DEPS | 14 +++ codeformat.sh | 1 + scripts/build_sparkle.sh | 28 ++++++ scripts/build_winsparkle.bat | 89 +++++++++++++++++++ vendor.json | 17 ++++ .../fileTemplates/includes/PAG File Header.h | 0 .../fileTemplates/internal/C Header File.h | 0 .../fileTemplates/internal/C Source File.c | 0 .../fileTemplates/internal/C++ Class Header.h | 0 .../.idea/fileTemplates/internal/C++ Class.cc | 0 {qt => viewer}/.idea/misc.xml | 0 {qt => viewer}/CMakeLists.txt | 33 +++---- {qt => viewer}/README.md | 4 +- {qt => viewer}/README.zh_CN.md | 6 +- {qt => viewer}/images/window-icon.png | 0 {qt => viewer}/qml/Main.qml | 0 {qt => viewer}/res.qrc | 0 {qt => viewer}/src/PAGView.cpp | 0 {qt => viewer}/src/PAGView.h | 0 {qt => viewer}/src/PAGViewer.cpp | 0 {qt => viewer}/src/PAGViewer.h | 0 {qt => viewer}/src/PAGWindow.cpp | 4 - {qt => viewer}/src/PAGWindow.h | 0 {qt => viewer}/src/main.cpp | 5 -- 25 files changed, 172 insertions(+), 31 deletions(-) create mode 100755 scripts/build_sparkle.sh create mode 100644 scripts/build_winsparkle.bat rename {qt => viewer}/.idea/fileTemplates/includes/PAG File Header.h (100%) rename {qt => viewer}/.idea/fileTemplates/internal/C Header File.h (100%) rename {qt => viewer}/.idea/fileTemplates/internal/C Source File.c (100%) rename {qt => viewer}/.idea/fileTemplates/internal/C++ Class Header.h (100%) rename {qt => viewer}/.idea/fileTemplates/internal/C++ Class.cc (100%) rename {qt => viewer}/.idea/misc.xml (100%) rename {qt => viewer}/CMakeLists.txt (78%) rename {qt => viewer}/README.md (86%) rename {qt => viewer}/README.zh_CN.md (86%) rename {qt => viewer}/images/window-icon.png (100%) rename {qt => viewer}/qml/Main.qml (100%) rename {qt => viewer}/res.qrc (100%) rename {qt => viewer}/src/PAGView.cpp (100%) rename {qt => viewer}/src/PAGView.h (100%) rename {qt => viewer}/src/PAGViewer.cpp (100%) rename {qt => viewer}/src/PAGViewer.h (100%) rename {qt => viewer}/src/PAGWindow.cpp (96%) rename {qt => viewer}/src/PAGWindow.h (100%) rename {qt => viewer}/src/main.cpp (91%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d4d4fc3a7e..85931d6506 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -331,7 +331,7 @@ jobs: - name: Build QT run: | mkdir -p third_party/tgfx/out/cache - node build_pag -s ./qt PAGViewer -DCMAKE_PREFIX_PATH="${{env.QT_ROOT_DIR}}/lib/cmake" -o ./out/release/viewer -a x64 + node build_pag -s ./viewer PAGViewer -DCMAKE_PREFIX_PATH="${{env.QT_ROOT_DIR}}/lib/cmake" -o ./out/release/viewer -a x64 - name: Save Third-Party Cache if: ${{ (github.event_name == 'push') && (steps.third-party-cache.outputs.cache-hit != 'true') }} diff --git a/DEPS b/DEPS index bccc65b995..f1a63e4219 100644 --- a/DEPS +++ b/DEPS @@ -35,6 +35,20 @@ "commit": "5ff839680134437dbf4678f3d0c7b371d84f4964", "dir": "third_party/lz4" } + ], + "mac": [ + { + "url": "https://github.com/sparkle-project/Sparkle.git", + "commit": "e9726ff718cffb2d045d84ede9548e38ea48fa01", + "dir": "third_party/sparkle" + } + ], + "win": [ + { + "url": "https://github.com/vslavik/winsparkle.git", + "commit": "4e6b30b9750b938b184b125d05a664413c72e671", + "dir": "third_party/winsparkle" + } ] }, "actions": { diff --git a/codeformat.sh b/codeformat.sh index cb8cc0755a..c1b2bfafe0 100755 --- a/codeformat.sh +++ b/codeformat.sh @@ -28,6 +28,7 @@ find include/ -iname '*.h' -print0 | xargs clang-format -i find src -name "*.cpp" -print -o -name "*.h" -print -o -name "*.mm" -print -o -name "*.m" -print | xargs clang-format -i # shellcheck disable=SC2038 find test \( -path test/framework/lzma \) -prune -o -name "*.cpp" -print -o -name "*.h" -print | xargs clang-format -i +find viewer -name "*.cpp" -print -o -name "*.h" -print -o -name "*.mm" -print | xargs clang-format -i git diff result=`git diff` diff --git a/scripts/build_sparkle.sh b/scripts/build_sparkle.sh new file mode 100755 index 0000000000..afb3436040 --- /dev/null +++ b/scripts/build_sparkle.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +ROOT_DIR=$(dirname $(cd "$(dirname "${0}")"; pwd)) +VENDOR_SOURCE_DIR=${ROOT_DIR}/third_party/sparkle +ORG_PWD=$(pwd) + +LIB_OUT_PATH="${ROOT_DIR}/third_party/out/sparkle/lib/" + +if [ -d "${LIB_OUT_PATH}" ]; then + echo "Sparkle path[${LIB_OUT_PATH}] is exist, skip build" + return +fi + +BUILD_DIR=${VENDOR_SOURCE_DIR}/build +if [ -d "${BUILD_DIR}" ]; +then + rm -fr ${BUILD_DIR} +fi +mkdir -p ${BUILD_DIR} + +cd ${VENDOR_SOURCE_DIR} +make release + +mkdir -p ${LIB_OUT_PATH} +cp -fRP ./build/Sparkle.*/Build/Products/Release/Sparkle.framework ${LIB_OUT_PATH}/ +rm -fr ${BUILD_DIR} + +cd ${ORG_PWD} diff --git a/scripts/build_winsparkle.bat b/scripts/build_winsparkle.bat new file mode 100644 index 0000000000..ffd42ccc94 --- /dev/null +++ b/scripts/build_winsparkle.bat @@ -0,0 +1,89 @@ +@echo off +setlocal EnableDelayedExpansion + +goto :main + +:: function GetMSBuildPath begin +:GetMSBuildPath +set "MSBUILD_PATH=" +set "vswhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" + +if not exist "%vswhere%" ( + echo Error: vswhere.exe not found + exit /b 1 +) + +for /f "usebackq tokens=*" %%i in (`"%vswhere%" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath`) do ( + set "VS_PATH=%%i" +) + +if not defined VS_PATH ( + echo Error: Visual Studio installation not found + exit /b 1 +) + +set "MSBUILD_PATH=%VS_PATH%\MSBuild\Current\Bin\MSBuild.exe" +if not exist "!MSBUILD_PATH!" ( + echo Error: MSBuild.exe not found + exit /b 1 +) + +echo !MSBUILD_PATH! +exit /b 0 +:: function GetMSBuildPath end + +:: function Build begin +:Build +set "BUILD_TYPE=%~1" +set "LIB_OUT_PATH=%OUT_PATH%\lib\%ARCH%\%BUILD_TYPE%" +set "INCLUDE_OUT_PATH=%OUT_PATH%\include\winsparkle" + +echo Building %BUILD_TYPE% configuration... +"%MSBUILD_PATH%" "%SLN_FILE_PATH%" /p:Platform=%ARCH% /p:Configuration=%BUILD_TYPE% +if %ERRORLEVEL% neq 0 ( + echo Failed to build %BUILD_TYPE% configuration + exit /b 1 +) + +if not exist "%LIB_OUT_PATH%" mkdir "%LIB_OUT_PATH%" +if not exist "%INCLUDE_OUT_PATH%" mkdir "%INCLUDE_OUT_PATH%" + +copy /Y "%VENDOR_SOURCE_DIR%\%ARCH%\%BUILD_TYPE%\WinSparkle.lib" "%LIB_OUT_PATH%" +copy /Y "%VENDOR_SOURCE_DIR%\%ARCH%\%BUILD_TYPE%\WinSparkle.dll" "%LIB_OUT_PATH%" +copy /Y "%VENDOR_SOURCE_DIR%\include\*.h" "%INCLUDE_OUT_PATH%" + +echo Finished building %BUILD_TYPE% configuration +exit /b 0 +:: function Build end + +:: function main begin +:main +set "ROOT_DIR=%~dp0..\" +set "VENDOR_SOURCE_DIR=%ROOT_DIR%\third_party\winsparkle" +set "SLN_FILE_PATH=%VENDOR_SOURCE_DIR%\WinSparkle.sln" +set "ARCH=x64" +set "OUT_PATH=%ROOT_DIR%\third_party\out\winsparkle" + +call :GetMSBuildPath +if %ERRORLEVEL% neq 0 ( + echo Failed to get MSBuild path + exit /b 1 +) + +git submodule init +git submodule update + +"%MSBUILD_PATH%" "%SLN_FILE_PATH%" /p:RestorePackagesConfig=true /t:Restore + +:: Build Debug +call :Build Debug +if %ERRORLEVEL% neq 0 exit /b 1 + +:: Build Release +call :Build Release +if %ERRORLEVEL% neq 0 exit /b 1 + +exit /b 0 +:: function main end + +endlocal \ No newline at end of file diff --git a/vendor.json b/vendor.json index c2faa67f6a..e411e6df54 100644 --- a/vendor.json +++ b/vendor.json @@ -49,6 +49,23 @@ "linux" ] } + }, + { + "name": "sparkle", + "scripts": { + "mac": { + "executor": "bash", + "file": "scripts/build_sparkle.sh" + } + } + }, + { + "name": "winsparkle", + "scripts": { + "win": { + "file": "scripts/build_winsparkle.bat" + } + } } ] } \ No newline at end of file diff --git a/qt/.idea/fileTemplates/includes/PAG File Header.h b/viewer/.idea/fileTemplates/includes/PAG File Header.h similarity index 100% rename from qt/.idea/fileTemplates/includes/PAG File Header.h rename to viewer/.idea/fileTemplates/includes/PAG File Header.h diff --git a/qt/.idea/fileTemplates/internal/C Header File.h b/viewer/.idea/fileTemplates/internal/C Header File.h similarity index 100% rename from qt/.idea/fileTemplates/internal/C Header File.h rename to viewer/.idea/fileTemplates/internal/C Header File.h diff --git a/qt/.idea/fileTemplates/internal/C Source File.c b/viewer/.idea/fileTemplates/internal/C Source File.c similarity index 100% rename from qt/.idea/fileTemplates/internal/C Source File.c rename to viewer/.idea/fileTemplates/internal/C Source File.c diff --git a/qt/.idea/fileTemplates/internal/C++ Class Header.h b/viewer/.idea/fileTemplates/internal/C++ Class Header.h similarity index 100% rename from qt/.idea/fileTemplates/internal/C++ Class Header.h rename to viewer/.idea/fileTemplates/internal/C++ Class Header.h diff --git a/qt/.idea/fileTemplates/internal/C++ Class.cc b/viewer/.idea/fileTemplates/internal/C++ Class.cc similarity index 100% rename from qt/.idea/fileTemplates/internal/C++ Class.cc rename to viewer/.idea/fileTemplates/internal/C++ Class.cc diff --git a/qt/.idea/misc.xml b/viewer/.idea/misc.xml similarity index 100% rename from qt/.idea/misc.xml rename to viewer/.idea/misc.xml diff --git a/qt/CMakeLists.txt b/viewer/CMakeLists.txt similarity index 78% rename from qt/CMakeLists.txt rename to viewer/CMakeLists.txt index bc21659b6f..14547c9ffa 100644 --- a/qt/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.13) project(PAGViewer) +include(../third_party/vendor_tools/vendor.cmake) + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -44,24 +46,10 @@ if (QT_VERSION) endif () endif () -find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) +find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets OpenGL Qml Quick) list(APPEND PAG_VIEWER_PLATFORM_LIBS Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::OpenGL Qt${QT_VERSION_MAJOR}::Qml Qt${QT_VERSION_MAJOR}::Quick) -if (${QT_VERSION} VERSION_LESS "5.15") - function(qt_add_resources outfiles) - qt5_add_resources("${outfiles}" ${ARGN}) - if (TARGET ${outfiles}) - cmake_parse_arguments(PARSE_ARGV 1 arg "" "OUTPUT_TARGETS" "") - if (arg_OUTPUT_TARGETS) - set(${arg_OUTPUT_TARGETS} ${${arg_OUTPUT_TARGETS}} PARENT_SCOPE) - endif () - else () - set("${outfiles}" "${${outfiles}}" PARENT_SCOPE) - endif () - endfunction() -endif () - qt_add_resources(QT_RESOURCES res.qrc) if (APPLE) @@ -95,6 +83,8 @@ set(PAG_VIEWER_INCLUDES ./ src ../ ../include ../src ../third_party/tgfx/include file(GLOB_RECURSE PAG_VIEWER_SOURCE_FILES src/*.*) set(PAG_USE_QT ON) +set(PAG_USE_RTTR ON) +set(PAG_USE_LIBAVC OFF) set(PAG_BUILD_SHARED OFF) set(PAG_BUILD_FRAMEWORK OFF) @@ -102,5 +92,16 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ ./libpag) add_executable(PAGViewer ${RC_FILES} ${PAG_VIEWER_SOURCE_FILES} ${QT_RESOURCES}) +if (APPLE) + add_vendor_target(viewer-vendor SHARED_VENDORS sparkle CONFIG_DIR ${CMAKE_SOURCE_DIR}/../) + list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/rttr/mac/include) + list(APPEND VIEWER_VENDOR_LIBRARIES ${CMAKE_SOURCE_DIR}/../third_party/out/sparkle/lib/Sparkle.framework) +elseif (WIN32) + add_vendor_target(viewer-vendor SHARED_VENDORS winsparkle CONFIG_DIR ${CMAKE_SOURCE_DIR}/../) + list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/rttr/win/include) + list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/winsparkle/include) + list(APPEND VIEWER_VENDOR_LIBRARIES ${CMAKE_SOURCE_DIR}/../third_party/out/winsparkle/lib/x64/${CMAKE_BUILD_TYPE}/WinSparkle.lib) +endif () + target_include_directories(PAGViewer PUBLIC ${PAG_VIEWER_INCLUDES}) -target_link_libraries(PAGViewer pag ${PAG_VIEWER_PLATFORM_LIBS}) \ No newline at end of file +target_link_libraries(PAGViewer pag ${PAG_VIEWER_PLATFORM_LIBS} ${VIEWER_VENDOR_LIBRARIES}) \ No newline at end of file diff --git a/qt/README.md b/viewer/README.md similarity index 86% rename from qt/README.md rename to viewer/README.md index e780895657..0f33139243 100644 --- a/qt/README.md +++ b/viewer/README.md @@ -12,8 +12,8 @@ local QT installation path. After that, you can build and run the `PAGViewer` ta ## Windows - Follow the build guide in [README.md](./../README.md) to install the necessary VS2019 modules and ensure the CLion ToolChain is set to Visual Studio with **amd64** architecture. -- Open the `qt` folder in CLion, then open the `qt/QTCMAKE.cfg` file and update the QT path to your local QT installation path. For example: `C:/Qt/Qt5.13.0/5.13.0/msvc2017_64/lib/cmake`. -- In CLion, open the configuration panel for the `PAGViewer` target and set the local QT DLL library path in the `Environment Variables` field. For example: `PATH=C:\Qt\Qt5.13.0\5.13.0\msvc2017_64\bin`. +- Open the `qt` folder in CLion, then open the `qt/QTCMAKE.cfg` file and update the QT path to your local QT installation path. For example: `C:/Qt/Qt6.8.1/6.8.1/msvc2017_64/lib/cmake`. +- In CLion, open the configuration panel for the `PAGViewer` target and set the local QT DLL library path in the `Environment Variables` field. For example: `PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2017_64\bin`. - Finally, build and run the `PAGViewer` target. diff --git a/qt/README.zh_CN.md b/viewer/README.zh_CN.md similarity index 86% rename from qt/README.zh_CN.md rename to viewer/README.zh_CN.md index f01a5edfa5..8249e82bbc 100644 --- a/qt/README.zh_CN.md +++ b/viewer/README.zh_CN.md @@ -3,7 +3,7 @@ # QT Demo 编译指南 请务必先参考根目录的 [README.md](./../README.zh_CN.md) 中编译指南一节,成功编译出 libpag 的库文件后再进行 QT 平台的 Demo 工程编译和运行。 -QT 环境推荐安装使用 **Qt5.13.0** 版本。 +QT 环境推荐安装使用 **Qt6.8.1** 版本。 ## macOS @@ -12,8 +12,8 @@ QT 环境推荐安装使用 **Qt5.13.0** 版本。 ## Windows - 请参考 [README.md](./../README.zh_CN.md) 中编译指南一节,提前安装好 VS2019 版本的必要模块并确保 CLion 的 ToolChain 是 Visual Studio,并选中 **amd64** 架构。 -- 用 CLion 打开根目录下的 qt 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 qt/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。例如:`C:/Qt/Qt5.13.0/5.13.0/msvc2017_64/lib/cmake`。 -- 在 CLion 中打开 PAGViewer 目标的配置面板,在 Environment Variables 一行填入本地 QT 的 DLL 库路径,例如:`PATH=C:\Qt\Qt5.13.0\5.13.0\msvc2017_64\bin`。 +- 用 CLion 打开根目录下的 qt 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 qt/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。例如:`C:/Qt/Qt6.8.1/6.8.1/msvc2017_64/lib/cmake`。 +- 在 CLion 中打开 PAGViewer 目标的配置面板,在 Environment Variables 一行填入本地 QT 的 DLL 库路径,例如:`PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2017_64\bin`。 - 最后编译并运行 PAGViewer 目标即可。 diff --git a/qt/images/window-icon.png b/viewer/images/window-icon.png similarity index 100% rename from qt/images/window-icon.png rename to viewer/images/window-icon.png diff --git a/qt/qml/Main.qml b/viewer/qml/Main.qml similarity index 100% rename from qt/qml/Main.qml rename to viewer/qml/Main.qml diff --git a/qt/res.qrc b/viewer/res.qrc similarity index 100% rename from qt/res.qrc rename to viewer/res.qrc diff --git a/qt/src/PAGView.cpp b/viewer/src/PAGView.cpp similarity index 100% rename from qt/src/PAGView.cpp rename to viewer/src/PAGView.cpp diff --git a/qt/src/PAGView.h b/viewer/src/PAGView.h similarity index 100% rename from qt/src/PAGView.h rename to viewer/src/PAGView.h diff --git a/qt/src/PAGViewer.cpp b/viewer/src/PAGViewer.cpp similarity index 100% rename from qt/src/PAGViewer.cpp rename to viewer/src/PAGViewer.cpp diff --git a/qt/src/PAGViewer.h b/viewer/src/PAGViewer.h similarity index 100% rename from qt/src/PAGViewer.h rename to viewer/src/PAGViewer.h diff --git a/qt/src/PAGWindow.cpp b/viewer/src/PAGWindow.cpp similarity index 96% rename from qt/src/PAGWindow.cpp rename to viewer/src/PAGWindow.cpp index 5b12c61e07..6529ad41d8 100644 --- a/qt/src/PAGWindow.cpp +++ b/viewer/src/PAGWindow.cpp @@ -39,11 +39,7 @@ void PAGWindow::Open() { engine->load(QUrl(QStringLiteral("qrc:/qml/Main.qml"))); window = static_cast(engine->rootObjects().at(0)); pagView = window->findChild("pagView"); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) window->setPersistentGraphics(true); -#else - window->setPersistentOpenGLContext(true); -#endif window->setPersistentSceneGraph(true); connect(window, SIGNAL(closing(QQuickCloseEvent*)), this, SLOT(onPAGViewerDestroyed()), Qt::QueuedConnection); diff --git a/qt/src/PAGWindow.h b/viewer/src/PAGWindow.h similarity index 100% rename from qt/src/PAGWindow.h rename to viewer/src/PAGWindow.h diff --git a/qt/src/main.cpp b/viewer/src/main.cpp similarity index 91% rename from qt/src/main.cpp rename to viewer/src/main.cpp index d2627a549b..2ee24a13a4 100644 --- a/qt/src/main.cpp +++ b/viewer/src/main.cpp @@ -35,12 +35,7 @@ int main(int argc, char* argv[]) { defaultFormat.setVersion(3, 2); defaultFormat.setProfile(QSurfaceFormat::CoreProfile); QSurfaceFormat::setDefaultFormat(defaultFormat); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); -#else - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); -#endif PAGViewer app(argc, argv); QApplication::setWindowIcon(QIcon(":/images/window-icon.png")); From e305f2b9c950c2d3605f6c6bf0aae81d7c7335a4 Mon Sep 17 00:00:00 2001 From: markffan Date: Mon, 17 Mar 2025 12:39:54 +0800 Subject: [PATCH 02/36] Update the qt path corresponding to vs2019 --- viewer/README.md | 4 ++-- viewer/README.zh_CN.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/viewer/README.md b/viewer/README.md index 0f33139243..48903a64d1 100644 --- a/viewer/README.md +++ b/viewer/README.md @@ -12,8 +12,8 @@ local QT installation path. After that, you can build and run the `PAGViewer` ta ## Windows - Follow the build guide in [README.md](./../README.md) to install the necessary VS2019 modules and ensure the CLion ToolChain is set to Visual Studio with **amd64** architecture. -- Open the `qt` folder in CLion, then open the `qt/QTCMAKE.cfg` file and update the QT path to your local QT installation path. For example: `C:/Qt/Qt6.8.1/6.8.1/msvc2017_64/lib/cmake`. -- In CLion, open the configuration panel for the `PAGViewer` target and set the local QT DLL library path in the `Environment Variables` field. For example: `PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2017_64\bin`. +- Open the `qt` folder in CLion, then open the `qt/QTCMAKE.cfg` file and update the QT path to your local QT installation path. For example: `C:/Qt/Qt6.8.1/6.8.1/msvc2019_64/lib/cmake`. +- In CLion, open the configuration panel for the `PAGViewer` target and set the local QT DLL library path in the `Environment Variables` field. For example: `PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2019_64\bin`. - Finally, build and run the `PAGViewer` target. diff --git a/viewer/README.zh_CN.md b/viewer/README.zh_CN.md index 8249e82bbc..6886b90b11 100644 --- a/viewer/README.zh_CN.md +++ b/viewer/README.zh_CN.md @@ -12,8 +12,8 @@ QT 环境推荐安装使用 **Qt6.8.1** 版本。 ## Windows - 请参考 [README.md](./../README.zh_CN.md) 中编译指南一节,提前安装好 VS2019 版本的必要模块并确保 CLion 的 ToolChain 是 Visual Studio,并选中 **amd64** 架构。 -- 用 CLion 打开根目录下的 qt 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 qt/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。例如:`C:/Qt/Qt6.8.1/6.8.1/msvc2017_64/lib/cmake`。 -- 在 CLion 中打开 PAGViewer 目标的配置面板,在 Environment Variables 一行填入本地 QT 的 DLL 库路径,例如:`PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2017_64\bin`。 +- 用 CLion 打开根目录下的 qt 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 qt/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。例如:`C:/Qt/Qt6.8.1/6.8.1/msvc2019_64/lib/cmake`。 +- 在 CLion 中打开 PAGViewer 目标的配置面板,在 Environment Variables 一行填入本地 QT 的 DLL 库路径,例如:`PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2019_64\bin`。 - 最后编译并运行 PAGViewer 目标即可。 From adc2a453ee29b1f2882491486c37416f86809b63 Mon Sep 17 00:00:00 2001 From: markffan Date: Mon, 17 Mar 2025 12:56:00 +0800 Subject: [PATCH 03/36] Update the README.md of viewer --- CPPLINT.cfg | 2 +- viewer/README.md | 4 ++-- viewer/README.zh_CN.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CPPLINT.cfg b/CPPLINT.cfg index 8872937695..4d0aa71e85 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -3,7 +3,7 @@ root=src exclude_files=mac exclude_files=android exclude_files=ios -exclude_files=qt +exclude_files=viewer exclude_files=vendor exclude_files=test exclude_files=.idea \ No newline at end of file diff --git a/viewer/README.md b/viewer/README.md index 48903a64d1..fde498d36a 100644 --- a/viewer/README.md +++ b/viewer/README.md @@ -6,13 +6,13 @@ Before building the QT demo project, make sure you have successfully built the l ## macOS -Open the `qt` folder in CLion, then open the `qt/QTCMAKE.cfg` file and update the QT path to your +Open the `viewer` folder in CLion, then open the `viewer/QTCMAKE.cfg` file and update the QT path to your local QT installation path. After that, you can build and run the `PAGViewer` target. ## Windows - Follow the build guide in [README.md](./../README.md) to install the necessary VS2019 modules and ensure the CLion ToolChain is set to Visual Studio with **amd64** architecture. -- Open the `qt` folder in CLion, then open the `qt/QTCMAKE.cfg` file and update the QT path to your local QT installation path. For example: `C:/Qt/Qt6.8.1/6.8.1/msvc2019_64/lib/cmake`. +- Open the `viewer` folder in CLion, then open the `viewer/QTCMAKE.cfg` file and update the QT path to your local QT installation path. For example: `C:/Qt/Qt6.8.1/6.8.1/msvc2019_64/lib/cmake`. - In CLion, open the configuration panel for the `PAGViewer` target and set the local QT DLL library path in the `Environment Variables` field. For example: `PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2019_64\bin`. - Finally, build and run the `PAGViewer` target. diff --git a/viewer/README.zh_CN.md b/viewer/README.zh_CN.md index 6886b90b11..094b8fd1e4 100644 --- a/viewer/README.zh_CN.md +++ b/viewer/README.zh_CN.md @@ -7,12 +7,12 @@ QT 环境推荐安装使用 **Qt6.8.1** 版本。 ## macOS -直接用 CLion 打开根目录下的 qt 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 qt/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。然后直接编译运行 PAGViewer。 +直接用 CLion 打开根目录下的 viewer 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 viewer/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。然后直接编译运行 PAGViewer。 ## Windows - 请参考 [README.md](./../README.zh_CN.md) 中编译指南一节,提前安装好 VS2019 版本的必要模块并确保 CLion 的 ToolChain 是 Visual Studio,并选中 **amd64** 架构。 -- 用 CLion 打开根目录下的 qt 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 qt/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。例如:`C:/Qt/Qt6.8.1/6.8.1/msvc2019_64/lib/cmake`。 +- 用 CLion 打开根目录下的 viewer 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 viewer/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。例如:`C:/Qt/Qt6.8.1/6.8.1/msvc2019_64/lib/cmake`。 - 在 CLion 中打开 PAGViewer 目标的配置面板,在 Environment Variables 一行填入本地 QT 的 DLL 库路径,例如:`PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2019_64\bin`。 - 最后编译并运行 PAGViewer 目标即可。 From dfec660538fc0e70be9b960624010adecb58d45b Mon Sep 17 00:00:00 2001 From: markffan Date: Mon, 17 Mar 2025 14:39:01 +0800 Subject: [PATCH 04/36] Migrate qt build job to macOS environment --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85931d6506..8f6fe25612 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -293,7 +293,7 @@ jobs: path: linux qt: - runs-on: ubuntu-latest + runs-on: macos-latest steps: - name: Check Out Repo uses: actions/checkout@v4 From a0e9ebd6182a3ccd739545d575af73524eb00f8f Mon Sep 17 00:00:00 2001 From: markffan Date: Mon, 17 Mar 2025 14:52:47 +0800 Subject: [PATCH 05/36] Fixed an issue that caused the qt job on the pipeline to run failed --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8f6fe25612..a2cb478752 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -320,7 +320,7 @@ jobs: - name: Install Qt uses: jurplel/install-qt-action@v4 with: - host: 'linux' + host: 'mac' target: 'desktop' version: '6.8.1' dir: '${{github.workspace}}/qt/' From 7037bdfbd722ad0dacbafa72ffdd0b3547c2a4f5 Mon Sep 17 00:00:00 2001 From: markffan Date: Mon, 17 Mar 2025 20:53:18 +0800 Subject: [PATCH 06/36] Set the minimum Qt version required by libpag to 6.2.0 --- README.md | 2 +- README.zh_CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b0c77f5fa9..7632f69211 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ you can download the precompiled libraries from [here](https://github.com/Tencen - NodeJS 14.14.0+ - Ninja 1.9.0+ - CMake 3.13.0+ -- QT 5.13.0+ +- QT 6.2.0+ - NDK 28+ (**28.0.13004108 recommended**) - Emscripten 3.1.58+ diff --git a/README.zh_CN.md b/README.zh_CN.md index af0959be76..e71a76b82b 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -204,7 +204,7 @@ Web 端更多接入方式请参考:[Web端接入指南](https://pag.io/docs/sd - NodeJS 14.14.0+ - Ninja 1.9.0+ - CMake 3.13.0+ -- QT 5.13.0+ +- QT 6.2.0+ - NDK 28+ (**推荐 28.0.13004108 版本**) - Emscripten 3.1.58+ From 548e00504644cd7fc209c85311d5832e5a1c7c4d Mon Sep 17 00:00:00 2001 From: markffan Date: Tue, 18 Mar 2025 16:42:26 +0800 Subject: [PATCH 07/36] Set both the minimum Qt version required by libpag and the Qt version recommended by the viewer to be 6.2.0 --- codeformat.sh | 2 +- viewer/README.md | 4 ++-- viewer/README.zh_CN.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/codeformat.sh b/codeformat.sh index c1b2bfafe0..3750327476 100755 --- a/codeformat.sh +++ b/codeformat.sh @@ -28,7 +28,7 @@ find include/ -iname '*.h' -print0 | xargs clang-format -i find src -name "*.cpp" -print -o -name "*.h" -print -o -name "*.mm" -print -o -name "*.m" -print | xargs clang-format -i # shellcheck disable=SC2038 find test \( -path test/framework/lzma \) -prune -o -name "*.cpp" -print -o -name "*.h" -print | xargs clang-format -i -find viewer -name "*.cpp" -print -o -name "*.h" -print -o -name "*.mm" -print | xargs clang-format -i +find viewer -name "*.cpp" -print -o -name "*.h" -print -o -name "*.mm" -print -o -name "*.m" -print | xargs clang-format -i git diff result=`git diff` diff --git a/viewer/README.md b/viewer/README.md index fde498d36a..8ef8aabf82 100644 --- a/viewer/README.md +++ b/viewer/README.md @@ -12,8 +12,8 @@ local QT installation path. After that, you can build and run the `PAGViewer` ta ## Windows - Follow the build guide in [README.md](./../README.md) to install the necessary VS2019 modules and ensure the CLion ToolChain is set to Visual Studio with **amd64** architecture. -- Open the `viewer` folder in CLion, then open the `viewer/QTCMAKE.cfg` file and update the QT path to your local QT installation path. For example: `C:/Qt/Qt6.8.1/6.8.1/msvc2019_64/lib/cmake`. -- In CLion, open the configuration panel for the `PAGViewer` target and set the local QT DLL library path in the `Environment Variables` field. For example: `PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2019_64\bin`. +- Open the `viewer` folder in CLion, then open the `viewer/QTCMAKE.cfg` file and update the QT path to your local QT installation path. For example: `C:/Qt/Qt6.2.0/6.2.0/msvc2019_64/lib/cmake`. +- In CLion, open the configuration panel for the `PAGViewer` target and set the local QT DLL library path in the `Environment Variables` field. For example: `PATH=C:\Qt\Qt6.2.0\6.2.0\msvc2019_64\bin`. - Finally, build and run the `PAGViewer` target. diff --git a/viewer/README.zh_CN.md b/viewer/README.zh_CN.md index 094b8fd1e4..c4ecbb06ce 100644 --- a/viewer/README.zh_CN.md +++ b/viewer/README.zh_CN.md @@ -3,7 +3,7 @@ # QT Demo 编译指南 请务必先参考根目录的 [README.md](./../README.zh_CN.md) 中编译指南一节,成功编译出 libpag 的库文件后再进行 QT 平台的 Demo 工程编译和运行。 -QT 环境推荐安装使用 **Qt6.8.1** 版本。 +QT 环境推荐安装使用 **Qt6.2.0** 版本。 ## macOS @@ -12,8 +12,8 @@ QT 环境推荐安装使用 **Qt6.8.1** 版本。 ## Windows - 请参考 [README.md](./../README.zh_CN.md) 中编译指南一节,提前安装好 VS2019 版本的必要模块并确保 CLion 的 ToolChain 是 Visual Studio,并选中 **amd64** 架构。 -- 用 CLion 打开根目录下的 viewer 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 viewer/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。例如:`C:/Qt/Qt6.8.1/6.8.1/msvc2019_64/lib/cmake`。 -- 在 CLion 中打开 PAGViewer 目标的配置面板,在 Environment Variables 一行填入本地 QT 的 DLL 库路径,例如:`PATH=C:\Qt\Qt6.8.1\6.8.1\msvc2019_64\bin`。 +- 用 CLion 打开根目录下的 viewer 文件夹,首次刷新会提示 QT SDK 找不到,请打开自动生成的 viewer/QTCMAKE.cfg 配置文件,修改其中的 QT 路径为本地安装路径即可。例如:`C:/Qt/Qt6.2.0/6.2.0/msvc2019_64/lib/cmake`。 +- 在 CLion 中打开 PAGViewer 目标的配置面板,在 Environment Variables 一行填入本地 QT 的 DLL 库路径,例如:`PATH=C:\Qt\Qt6.2.0\6.2.0\msvc2019_64\bin`。 - 最后编译并运行 PAGViewer 目标即可。 From 8c22e82dc34d34e4cdf78640d9b0f39767c55318 Mon Sep 17 00:00:00 2001 From: markffan Date: Thu, 20 Mar 2025 15:39:33 +0800 Subject: [PATCH 08/36] Implement the main window and basic playback controls of viewer --- viewer/CMakeLists.txt | 5 +- viewer/images/background-off.png | 3 + viewer/images/background-on.png | 3 + viewer/images/close-dark.svg | 1 + viewer/images/maximize-dark.svg | 1 + viewer/images/minimize-dark.svg | 1 + viewer/images/next.png | 3 + viewer/images/panels-off.png | 3 + viewer/images/panels-on.png | 3 + viewer/images/pause.png | 3 + viewer/images/play.png | 3 + viewer/images/previous.png | 3 + viewer/images/restore-dark.svg | 1 + viewer/images/tiles.png | 3 + viewer/images/update-red.png | 3 + viewer/images/window-icon-32x.png | 3 + viewer/qml/ControlForm.qml | 290 +++++++++++++ viewer/qml/Main.qml | 167 +++++++- viewer/qml/MainForm.qml | 112 +++++ viewer/qml/components/PAGRectangle.qml | 58 +++ viewer/qml/components/PAGRectangle.qml~ | 56 +++ viewer/qml/components/PAGResizeArea.qml | 45 ++ viewer/qml/components/PAGWindow.qml | 401 ++++++++++++++++++ viewer/res.qrc | 25 ++ viewer/src/PAGView.cpp | 120 ------ viewer/src/PAGViewer.cpp | 18 +- viewer/src/PAGViewer.h | 10 +- viewer/src/main.cpp | 20 +- viewer/src/rendering/PAGRenderThread.cpp | 43 ++ .../PAGRenderThread.h} | 32 +- viewer/src/rendering/PAGView.cpp | 275 ++++++++++++ viewer/src/rendering/PAGView.h | 90 ++++ viewer/src/{ => rendering}/PAGWindow.cpp | 49 +-- viewer/src/{ => rendering}/PAGWindow.h | 26 +- 34 files changed, 1653 insertions(+), 226 deletions(-) create mode 100755 viewer/images/background-off.png create mode 100755 viewer/images/background-on.png create mode 100644 viewer/images/close-dark.svg create mode 100644 viewer/images/maximize-dark.svg create mode 100644 viewer/images/minimize-dark.svg create mode 100755 viewer/images/next.png create mode 100755 viewer/images/panels-off.png create mode 100644 viewer/images/panels-on.png create mode 100644 viewer/images/pause.png create mode 100755 viewer/images/play.png create mode 100755 viewer/images/previous.png create mode 100644 viewer/images/restore-dark.svg create mode 100644 viewer/images/tiles.png create mode 100644 viewer/images/update-red.png create mode 100644 viewer/images/window-icon-32x.png create mode 100644 viewer/qml/ControlForm.qml create mode 100644 viewer/qml/MainForm.qml create mode 100644 viewer/qml/components/PAGRectangle.qml create mode 100644 viewer/qml/components/PAGRectangle.qml~ create mode 100644 viewer/qml/components/PAGResizeArea.qml create mode 100644 viewer/qml/components/PAGWindow.qml delete mode 100644 viewer/src/PAGView.cpp create mode 100644 viewer/src/rendering/PAGRenderThread.cpp rename viewer/src/{PAGView.h => rendering/PAGRenderThread.h} (59%) create mode 100644 viewer/src/rendering/PAGView.cpp create mode 100644 viewer/src/rendering/PAGView.h rename viewer/src/{ => rendering}/PAGWindow.cpp (67%) rename viewer/src/{ => rendering}/PAGWindow.h (83%) diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index 14547c9ffa..2a029a3e21 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -47,9 +47,10 @@ if (QT_VERSION) endif () find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core) -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets OpenGL Qml Quick) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets OpenGL Qml Quick QuickControls2) list(APPEND PAG_VIEWER_PLATFORM_LIBS Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets - Qt${QT_VERSION_MAJOR}::OpenGL Qt${QT_VERSION_MAJOR}::Qml Qt${QT_VERSION_MAJOR}::Quick) + Qt${QT_VERSION_MAJOR}::OpenGL Qt${QT_VERSION_MAJOR}::Qml Qt${QT_VERSION_MAJOR}::Quick + Qt${QT_VERSION_MAJOR}::QuickControls2) qt_add_resources(QT_RESOURCES res.qrc) if (APPLE) diff --git a/viewer/images/background-off.png b/viewer/images/background-off.png new file mode 100755 index 0000000000..90941b4401 --- /dev/null +++ b/viewer/images/background-off.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf3ac1f1a2a798ce5fbb9246e3090046d18558a209f5c5af8df4ca977430934d +size 3987 diff --git a/viewer/images/background-on.png b/viewer/images/background-on.png new file mode 100755 index 0000000000..a832b0e60e --- /dev/null +++ b/viewer/images/background-on.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b6d7de95fbe518cea1cdca5867ce55757adb080d6a4e485e093dcce06a26c85 +size 4114 diff --git a/viewer/images/close-dark.svg b/viewer/images/close-dark.svg new file mode 100644 index 0000000000..bb243036bb --- /dev/null +++ b/viewer/images/close-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/viewer/images/maximize-dark.svg b/viewer/images/maximize-dark.svg new file mode 100644 index 0000000000..b6645e8c82 --- /dev/null +++ b/viewer/images/maximize-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/viewer/images/minimize-dark.svg b/viewer/images/minimize-dark.svg new file mode 100644 index 0000000000..1f6a7016f8 --- /dev/null +++ b/viewer/images/minimize-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/viewer/images/next.png b/viewer/images/next.png new file mode 100755 index 0000000000..988a0d2e32 --- /dev/null +++ b/viewer/images/next.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18ebfd712d4bd763b6e991a90718737f02718dc151bd1718c3da3535b9eabd17 +size 1827 diff --git a/viewer/images/panels-off.png b/viewer/images/panels-off.png new file mode 100755 index 0000000000..e3ab04109a --- /dev/null +++ b/viewer/images/panels-off.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:634f54c7d12a0da7ce83b80cb59d0756234882e96fd5ac6fd429495c1ff26673 +size 4038 diff --git a/viewer/images/panels-on.png b/viewer/images/panels-on.png new file mode 100644 index 0000000000..8952294c38 --- /dev/null +++ b/viewer/images/panels-on.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d60287dd877232ecd0ffee1cbf62ee4d13cb8a4c6b04361028384f2ac5bc69f +size 4148 diff --git a/viewer/images/pause.png b/viewer/images/pause.png new file mode 100644 index 0000000000..cca72b0404 --- /dev/null +++ b/viewer/images/pause.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47f8a2689ab77e45f245ce1be9463328dd28f40a6bda77f385d92c2b5525c4c5 +size 9465 diff --git a/viewer/images/play.png b/viewer/images/play.png new file mode 100755 index 0000000000..e96dd9160c --- /dev/null +++ b/viewer/images/play.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02d6327ab57f8d524c942c12648ea8ceb6be2accf10948b5039585e4578e22c2 +size 10365 diff --git a/viewer/images/previous.png b/viewer/images/previous.png new file mode 100755 index 0000000000..05de8888b5 --- /dev/null +++ b/viewer/images/previous.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51be6154e4d8b6b0568c1b9d2d7ee745bb07049ea6a169da3b67232c13bba76d +size 1813 diff --git a/viewer/images/restore-dark.svg b/viewer/images/restore-dark.svg new file mode 100644 index 0000000000..d9f814370b --- /dev/null +++ b/viewer/images/restore-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/viewer/images/tiles.png b/viewer/images/tiles.png new file mode 100644 index 0000000000..80df3123ab --- /dev/null +++ b/viewer/images/tiles.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2388b27c0f99b49a5162d267d8a3414709905c2a797c36366ce1b3bf40eade87 +size 3069 diff --git a/viewer/images/update-red.png b/viewer/images/update-red.png new file mode 100644 index 0000000000..2238138b52 --- /dev/null +++ b/viewer/images/update-red.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cfc4bf393e72369088ce0bbf063ee336b99f709fda00ced46574122b549c2de +size 14778 diff --git a/viewer/images/window-icon-32x.png b/viewer/images/window-icon-32x.png new file mode 100644 index 0000000000..3d29aaeb43 --- /dev/null +++ b/viewer/images/window-icon-32x.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f035c3365b88fafcbe64459e13eac3f8fea10f09135e46d7d63475d7ff805fff +size 2381 diff --git a/viewer/qml/ControlForm.qml b/viewer/qml/ControlForm.qml new file mode 100644 index 0000000000..3f045fe9dd --- /dev/null +++ b/viewer/qml/ControlForm.qml @@ -0,0 +1,290 @@ +import QtQuick +import QtQuick.Controls +import "components" + +Item { + id: form + required property var pagView + property bool hasPAGFile: (pagView && pagView.filePath !== "") + + property bool hasNewVersion: false + + property bool lastPlayStatusIsPlaying: false + + property int sliderHeight: 12 + + property int controlFormHeight: 76 + + property alias progressSlider: progressSlider + + property alias updateButton: updateButton + + property alias panelsButton: panelsButton + + property alias backgroundButton: backgroundButton + + property alias timeDisplayedText: timeDisplayedText + + property alias currentFrameText: currentFrameText + + property alias totalFrameText: totalFrameText + + PAGRectangle { + height: controlFormHeight + color: "#20202A" + anchors.fill: parent + radius: 5 + leftTopRadius: false + rightTopRadius: false + rightBottomRadius: !panelsButton.checked + } + Slider { + id: progressSlider + height: sliderHeight + z: 1 + padding: 0 + anchors.top: parent.top + anchors.topMargin: -4 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + enabled: hasPAGFile + background: Rectangle { + x: progressSlider.leftPadding + y: progressSlider.topPadding + progressSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: progressSlider.availableWidth + height: implicitHeight + radius: 0 + color: "#7D7D7D" + + Rectangle { + width: progressSlider.visualPosition * parent.width + height: parent.height + color: "#448EF9" + radius: 0 + } + } + handle: Rectangle { + x: progressSlider.leftPadding + progressSlider.visualPosition * (progressSlider.availableWidth - width) + y: progressSlider.topPadding + progressSlider.availableHeight / 2 - height / 2 + implicitWidth: 12 + implicitHeight: 12 + radius: 12 + color: progressSlider.pressed ? "#f0f0f0" : "#f6f6f6" + border.color: "#bdbebf" + } + } + Row { + z: 1 + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Row { + id: leftControls + spacing: 8 + width: implicitWidth + + /* Space Holder */ + Item { + width: 10 + height: 1 + } + Button { + id: playButton + width: 48 + height: 48 + visible: true + enabled: hasPAGFile + focusPolicy: Qt.NoFocus + flat: true + display: AbstractButton.IconOnly + opacity: pressed ? 1 : hovered ? 0.9 : 0.8 + background: Image { + source: ((pagView && pagView.isPlaying) || form.lastPlayStatusIsPlaying) ? "qrc:/images/pause.png" : "qrc:/images/play.png" + } + onClicked: { + pagView.isPlaying = !pagView.isPlaying; + } + } + Button { + id: previousButton + width: 24 + height: 20 + enabled: hasPAGFile + focusPolicy: Qt.NoFocus + display: AbstractButton.IconOnly + flat: true + anchors.verticalCenter: parent.verticalCenter + opacity: pressed ? 1 : hovered ? 0.9 : 0.8 + background: Image { + source: "qrc:/images/previous.png" + } + onPressAndHold: { + pagView.previousFrame(); + longPressPreTimer.start(); + } + onReleased: { + longPressPreTimer.stop(); + } + onClicked: { + pagView.previousFrame(); + } + + Timer { + id: longPressPreTimer + interval: 60 + repeat: true + running: false + onTriggered: { + pagView.previousFrame(); + } + } + } + Button { + id: nextButton + width: 24 + height: 20 + enabled: hasPAGFile + focusPolicy: Qt.NoFocus + display: AbstractButton.IconOnly + flat: true + anchors.verticalCenter: parent.verticalCenter + opacity: pressed ? 1 : hovered ? 0.9 : 0.8 + background: Image { + source: "qrc:/images/next.png" + } + onPressAndHold: { + pagView.nextFrame(); + longPressNextTimer.start(); + } + onReleased: { + longPressNextTimer.stop(); + } + onClicked: { + pagView.nextFrame(); + } + + Timer { + id: longPressNextTimer + interval: 60 + repeat: true + running: false + onTriggered: { + pagView.nextFrame(); + } + } + } + Text { + id: timeDisplayedText + color: "#ffffff" + text: qsTr("00:00") + font.pixelSize: 12 + anchors.verticalCenterOffset: 1 + anchors.verticalCenter: parent.verticalCenter + } + Row { + spacing: 2 + anchors.verticalCenterOffset: 1 + anchors.verticalCenter: parent.verticalCenter + + Text { + id: currentFrameText + color: "#ffffff" + text: qsTr("0") + font.pixelSize: 12 + } + Text { + id: frameSeparator + color: "#EEEEEE" + text: qsTr("/") + font.pixelSize: 12 + } + Text { + id: totalFrameText + color: "#ffffff" + text: qsTr("0") + font.pixelSize: 12 + } + } + } + /* Space Holder */ + Item { + id: centerSpace + width: parent.width - leftControls.width - rightControls.width + height: parent.height + } + Row { + id: rightControls + spacing: 2 + + CheckBox { + id: updateButton + width: 44 + height: 44 + enabled: hasNewVersion + visible: hasNewVersion + focusPolicy: Qt.NoFocus + opacity: mouseArea.pressed ? 0.5 : 1.0 + indicator: Image { + width: 44 + height: 44 + source: "qrc:/images/update-red.png" + } + + MouseArea { + id: mouseArea + property bool entered: false + hoverEnabled: true + anchors.fill: parent + onEntered: { + entered = true; + } + onExited: { + entered = false; + } + + ToolTip { + visible: parent.entered + text: qsTr("Discover a new version, click to update") + } + } + } + CheckBox { + id: backgroundButton + width: 44 + height: 44 + enabled: hasPAGFile + focusPolicy: Qt.NoFocus + anchors.verticalCenterOffset: 0 + anchors.verticalCenter: parent.verticalCenter + indicator: Image { + width: 44 + height: 44 + source: backgroundButton.checked ? "qrc:/images/background-on.png" : "qrc:/images/background-off.png" + } + } + CheckBox { + id: panelsButton + width: 44 + height: 44 + enabled: hasPAGFile + focusPolicy: Qt.NoFocus + indicator: Image { + width: 44 + height: 44 + source: panelsButton.checked ? "qrc:/images/panels-on.png" : "qrc:/images/panels-off.png" + } + } + + /* Space Holder */ + Item { + width: 18 + height: 1 + } + } + } +} diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index b62d1722d0..3da6a209d4 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -1,19 +1,156 @@ -import QtQuick 2.5 -import QtQuick.Window 2.13 -import PAG 1.0 +import PAG +import QtCore +import QtQuick +import QtQuick.Window +import Qt.labs.platform as Lab11 +import "components" -Window { - id: wind +PAGWindow { + id: viewWindow visible: true - width: 600 - height: 800 - - PAGView { - id: pagView - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - objectName: "pagView" + width: isWindows ? 520 : 500 + height: 360 + minimumWidth: 400 + windowPadding + minimumHeight: 320 + windowTitleBarHeight + + property string filePath + property bool lastPlayStatusIsPlaying: false + + property bool isWindows: Qt.platform.os === 'windows' + + property int windowPadding: isWindows ? 2 : 0 + + property int windowTitleBarHeight: isWindows ? 32 : 22 + + Settings { + id: settings + property bool isEditPanelOpen: false + + property bool isShowVideoFrames: true + + property double lastX: 0 + + property double lastY: 0 + } + MainForm { + id: mainForm + pagView { + showVideoFrames: settings.isShowVideoFrames + onProgressChanged: function (progress) { + if (controlForm.progressSlider.value === progress) { + return; + } + controlForm.progressSlider.value = progress; + updateProgress(); + } + onFileChanged: { + let path = pagView.filePath; + path = path.replace(/\\/ig, '/').match(/\/([^\/]+)$/)[1]; + viewWindow.title = path; + viewWindow.filePath = path; + centerItem.color = pagView.backgroundColor; + updateProgress(); + let oldX = viewWindow.x; + let oldY = viewWindow.y; + let oldWidth = viewWindow.width; + let oldHeight = viewWindow.height; + let preferredSize = pagView.preferredSize; + let width = Math.max(viewWindow.minimumWidth, preferredSize.width); + let height = Math.max(viewWindow.minimumHeight, preferredSize.height); + let x = Math.max(0, oldX - ((width - oldWidth) / 2)); + let y = Math.max(50, oldY - ((height - oldHeight) / 2)); + settings.lastX = x; + settings.lastY = y; + viewWindow.x = x; + viewWindow.y = y; + viewWindow.width = width + windowPadding; + viewWindow.height = height; + } + } + controlForm { + progressSlider { + onValueChanged: { + if (pagView.progress === controlForm.progressSlider.value) { + return; + } + pagView.progress = controlForm.progressSlider.value; + updateProgress(); + } + onPressedChanged: { + if (controlForm.progressSlider.pressed) { + viewWindow.lastPlayStatusIsPlaying = pagView.isPlaying; + pagView.isPlaying = false; + } else { + pagView.isPlaying = viewWindow.lastPlayStatusIsPlaying; + viewWindow.lastPlayStatusIsPlaying = false; + } + } + } + backgroundButton { + checked: mainForm.isBackgroundOn + onClicked: { + toggleBackground(mainForm.controlForm.backgroundButton.checked); + } + } + panelsButton { + onClicked: { + toggleEditPanel(mainForm.controlForm.panelsButton.checked); + } + } + } + dropArea { + onEntered: function (fileInfo) { + fileInfo.accept(Qt.CopyAction); + if (fileInfo.urls[0]) { + viewWindow.filePath = fileInfo.urls[0]; + } + } + onDropped: function (fileInfo) { + let path; + if (viewWindow.filePath === "") { + path = fileInfo.source.text; + } else { + path = viewWindow.filePath; + } + if (path.endsWith("pag")) { + mainForm.pagView.setFile(path); + } + } + } + } + + Component.onCompleted: { + viewWindow.title = "PAGViewer"; + } + + function updateProgress() { + let duration = mainForm.pagView.duration; + let displayedTime = duration * mainForm.pagView.progress; + mainForm.controlForm.timeDisplayedText.text = msToTime(displayedTime); + mainForm.controlForm.currentFrameText.text = mainForm.pagView.currentFrame; + mainForm.controlForm.totalFrameText.text = mainForm.pagView.totalFrame; + } + function msToTime(duration) { + let seconds = parseInt((duration / 1000) % 60); + let minutes = parseInt((duration / (1000 * 60)) % 60); + minutes = (minutes < 10) ? "0" + minutes : minutes; + seconds = (seconds < 10) ? "0" + seconds : seconds; + return minutes + ":" + seconds; + } + function toggleBackground(checked) { + if (checked === undefined) { + checked = !mainForm.isBackgroundOn; + } + if (mainForm.isBackgroundOn !== checked) { + mainForm.isBackgroundOn = checked; + } + } + function toggleEditPanel(willOpen) { + if (willOpen === undefined) { + willOpen = !mainForm.isEditPanelOpen; + } + if (mainForm.controlForm.panelsButton.checked !== willOpen) { + mainForm.controlForm.panelsButton.checked = willOpen; + } } } diff --git a/viewer/qml/MainForm.qml b/viewer/qml/MainForm.qml new file mode 100644 index 0000000000..190258b337 --- /dev/null +++ b/viewer/qml/MainForm.qml @@ -0,0 +1,112 @@ +import PAG +import QtQuick +import QtQuick.Controls +import "components" + +SplitView { + id: splitView + property bool hasPAGFile: pagView.filePath !== "" + + property bool isBackgroundOn: false + + property bool isEditPanelOpen: false + + property int minPlayerWidth: 360 + + property int splitHandleWidth: 0 + + property int splitHandleHeight: 0 + + property int controlFormHeight: 76 + + property alias pagView: pagView + + property alias dropArea: dropArea + + property alias centerItem: centerItem + + property alias controlForm: controlForm + anchors.fill: parent + orientation: Qt.Horizontal + handle: Rectangle { + id: splitHandle + implicitWidth: splitHandleWidth + implicitHeight: splitHandleHeight + color: "#000000" + } + + PAGRectangle { + id: centerItem + SplitView.minimumWidth: minPlayerWidth + SplitView.fillWidth: true + color: "#000000" + radius: 5 + leftTopRadius: false + rightTopRadius: false + rightBottomRadius: !controlForm.panelsButton.checked + + Image { + id: backgroundTiles + visible: hasPAGFile && !isBackgroundOn + smooth: false + source: "qrc:/images/tiles.png" + fillMode: Image.Tile + anchors.fill: parent + anchors.bottom: parent.bottom + anchors.bottomMargin: controlFormHeight + sourceSize.width: 32 + sourceSize.height: 32 + } + PAGView { + id: pagView + height: splitView.height - controlFormHeight + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + objectName: "pagView" + } + ControlForm { + id: controlForm + pagView: pagView + height: controlFormHeight + z: 1 + anchors.bottom: parent.bottom + anchors.bottomMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + } + MouseArea { + id: mouseArea + z: 2 + anchors.fill: parent + anchors.bottom: parent.bottom + anchors.bottomMargin: controlFormHeight + 9 + onClicked: { + pagView.isPlaying = !pagView.isPlaying; + } + } + DropArea { + id: dropArea + z: 3 + anchors.fill: parent + } + Rectangle { + visible: !hasPAGFile + color: "#16161d" + anchors.fill: parent + anchors.bottomMargin: controlFormHeight + + Text { + color: "#80ffffff" + text: qsTr("Click the menu or drag-drop here to open a PAG file") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + font.pixelSize: 20 + wrapMode: Text.WordWrap + } + } + } +} diff --git a/viewer/qml/components/PAGRectangle.qml b/viewer/qml/components/PAGRectangle.qml new file mode 100644 index 0000000000..596e46a022 --- /dev/null +++ b/viewer/qml/components/PAGRectangle.qml @@ -0,0 +1,58 @@ +import QtQuick + +Rectangle { + id: root + property bool leftTopRadius: true + + property bool leftBottomRadius: true + + property bool rightTopRadius: true + + property bool rightBottomRadius: true + + Component { + id: cornerRect + Rectangle { + radius: 0 + width: root.radius + 3 + height: root.radius + 3 + color: root.color + } + } + Loader { + sourceComponent: cornerRect + active: !leftTopRadius + + anchors { + left: parent.left + top: parent.top + } + } + Loader { + sourceComponent: cornerRect + active: !rightTopRadius + + anchors { + right: parent.right + top: parent.top + } + } + Loader { + sourceComponent: cornerRect + active: !leftBottomRadius + + anchors { + left: parent.left + bottom: parent.bottom + } + } + Loader { + sourceComponent: cornerRect + active: !rightBottomRadius + + anchors { + right: parent.right + bottom: parent.bottom + } + } +} diff --git a/viewer/qml/components/PAGRectangle.qml~ b/viewer/qml/components/PAGRectangle.qml~ new file mode 100644 index 0000000000..a1f93bc286 --- /dev/null +++ b/viewer/qml/components/PAGRectangle.qml~ @@ -0,0 +1,56 @@ +import QtQuick + +Rectangle { + id: root + + property bool leftTopRadius: true + property bool leftBottomRadius: true + property bool rightTopRadius: true + property bool rightBottomRadius: true + + Component { + id: cornerRect + Rectangle { + radius: 0 + width: root.radius + 3 + height: root.radius + 3 + color: root.color + } + } + + Loader { + sourceComponent: cornerRect + active: !leftTopRadius + anchors { + left: parent.left + top: parent.top + } + } + + Loader { + sourceComponent: cornerRect + active: !rightTopRadius + anchors { + right: parent.right + top: parent.top + } + } + + Loader { + sourceComponent: cornerRect + active: !leftBottomRadius + anchors { + left: parent.left + bottom: parent.bottom + } + } + + Loader { + sourceComponent: cornerRect + active: !rightBottomRadius + anchors { + right: parent.right + bottom: parent.bottom + } + } +} diff --git a/viewer/qml/components/PAGResizeArea.qml b/viewer/qml/components/PAGResizeArea.qml new file mode 100644 index 0000000000..7937eb0d21 --- /dev/null +++ b/viewer/qml/components/PAGResizeArea.qml @@ -0,0 +1,45 @@ +import QtQuick + +MouseArea { + id: mouseArea + required property bool isEnable + required property int direction + property int startX: 0 + + property int startY: 0 + + readonly property int directionNone: 0 + + readonly property int directionLeft: 1 + + readonly property int directionRight: 2 + + readonly property int directionTop: 4 + + readonly property int directionBottom: 8 + enabled: isEnable + visible: isEnable + width: (direction & directionLeft) || (direction & directionRight) ? 4 : undefined + height: (direction & directionTop) || (direction & directionBottom) ? 4 : undefined + anchors.top: (direction & directionBottom) ? undefined : parent.top + anchors.topMargin: 0 + anchors.bottom: (direction & directionTop) ? undefined : parent.bottom + anchors.bottomMargin: 0 + anchors.left: (direction & directionRight) ? undefined : parent.left + anchors.leftMargin: (direction & directionLeft) || (direction & directionRight) ? 0 : 5 + anchors.right: (direction & directionLeft) ? undefined : parent.right + anchors.rightMargin: (direction & directionLeft) || (direction & directionRight) ? 0 : 5 + cursorShape: { + if (direction === directionTop || direction === directionBottom || direction === directionLeft || direction === directionRight) { + return Qt.SizeVerCursor; + } else if (((direction & directionLeft) && (direction & directionTop)) || ((direction & directionRight) && (direction & directionBottom))) { + return Qt.SizeFDiagCursor; + } else { + return Qt.SizeBDiagCursor; + } + } + onPressed: { + startX = mouseX; + startY = mouseY; + } +} diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml new file mode 100644 index 0000000000..bd242c1fba --- /dev/null +++ b/viewer/qml/components/PAGWindow.qml @@ -0,0 +1,401 @@ +import QtQuick +import QtQuick.Window +import QtQuick.Controls + +Window { + id: window + default property alias contents: placeholder.data + + property bool isWindows: Qt.platform.os === "windows" + + property bool isMaximized: false + + property bool hasMenu: false + + property bool canResize: true + + property int windowLastX: 0 + + property int windowLastY: 0 + + property int windowLastWidth: 0 + + property int windowLastHeight: 0 + + property int titleBarHeight: 32 + visible: true + color: "#00000000" + flags: isWindows ? (Qt.FramelessWindowHint | Qt.Window | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) : Qt.Window + + Rectangle { + visible: window.isWindows + anchors.fill: parent + radius: 5 + clip: true + border.width: 0 + border.color: "#00FFFFFF" + color: "#15FFFFFF" + + PAGRectangle { + id: titleBar + height: window.titleBarHeight + color: "#22FFFFFF" + radius: 5 + leftTopRadius: !window.isMaximized + rightTopRadius: !window.isMaximized + rightBottomRadius: false + leftBottomRadius: false + anchors.top: parent.top + anchors.topMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 1 + anchors.right: parent.right + anchors.rightMargin: 1 + + PAGRectangle { + height: window.titleBarHeight - 1 + color: "#20202a" + radius: 5 + leftTopRadius: !window.isMaximized + rightTopRadius: !window.isMaximized + rightBottomRadius: false + leftBottomRadius: false + anchors.top: parent.top + anchors.topMargin: 1 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + } + MouseArea { + id: mouseArea + property int mouseLastX: 0 + + property int mouseLastY: 0 + + property int windowX: 0 + + property int windowY: 0 + + property int windowWidth: 0 + height: 40 + anchors.fill: parent + onPressed: { + mouseLastX = mouseX; + mouseLastY = mouseY; + windowX = window.x; + windowY = window.y; + windowWidth = window.width; + } + onPositionChanged: { + let offsetX = mouseX - mouseLastX; + let offsetY = mouseY - mouseLastY; + if (((windowX - window.x) === offsetX) && ((windowY - window.y) === offsetY)) { + return; + } + if (window.isMaximized) { + setMaximized(false); + let sourceX = (mouseLastX - 240) / (windowWidth - 240 - 120); + let targetX = (window.width - 240 - 120) * sourceX + 240; + let xChange = mouseLastX - targetX; + window.x += xChange; + mouseLastX = targetX; + } + if (offsetX !== 0) { + window.x += offsetX; + } + if (offsetY !== 0) { + window.y += offsetY; + } + ensurePositionMovable(); + } + onDoubleClicked: { + setMaximized(!window.isMaximized); + } + } + Text { + id: windowsTitleText + visible: window.width > (window.hasMenu ? 400 : 150) + y: 8 + height: 16 + color: "#DDDDDD" + text: window.title + verticalAlignment: Text.AlignVCenter + font.weight: Font.Bold + renderType: Text.NativeRendering + elide: Text.ElideRight + anchors.left: parent.left + anchors.leftMargin: window.hasMenu ? (window.width < 600 ? 220 : 0) : 0 + anchors.right: parent.right + anchors.rightMargin: window.hasMenu ? (window.width < 600 ? 120 : 0) : 0 + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 12 + font.family: "Microsoft Yahei" + } + Image { + id: iconImage + x: 10 + y: 8 + width: 16 + height: 16 + fillMode: Image.PreserveAspectFit + source: "qrc:/images/window-icon-32x.png" + } + Row { + anchors.right: parent.right + + PAGRectangle { + id: minButton + width: 40 + height: window.titleBarHeight - 1 + border.width: 0 + color: minMouseArea.containsMouse ? "#33FFFFFF" : "#00ffffff" + + Image { + id: minImage + width: 10 + height: 10 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + sourceSize.height: 24 + sourceSize.width: 24 + fillMode: Image.PreserveAspectFit + source: "qrc:/images/minimize-dark.svg" + } + MouseArea { + id: minMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: { + window.visibility = Window.Minimized; + window.isMaximized = false; + } + } + } + PAGRectangle { + id: zoomButton + width: 40 + height: window.titleBarHeight - 1 + color: zoomMouseArea.containsMouse ? "#33FFFFFF" : "#00ffffff" + border.width: 0 + + Image { + id: zoomImage + width: 10 + height: 10 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + sourceSize.height: 24 + sourceSize.width: 24 + fillMode: Image.PreserveAspectFit + source: window.isMaximized ? "qrc:/images/restore-dark.svg" : "qrc:/images/maximize-dark.svg" + } + MouseArea { + id: zoomMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: { + setMaximized(!window.isMaximized); + } + } + } + PAGRectangle { + id: closeButton + width: 40 + height: window.titleBarHeight - 1 + color: closeMouseArea.containsMouse ? "#FFDD0000" : "#00ffffff" + border.width: 0 + radius: titleBar.radius + leftTopRadius: false + leftBottomRadius: false + rightBottomRadius: false + + Image { + id: closeImage + width: 10 + height: 10 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + sourceSize.height: 24 + sourceSize.width: 24 + fillMode: Image.PreserveAspectFit + source: "qrc:/images/close-dark.svg" + } + MouseArea { + id: closeMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: { + window.close(); + } + } + } + } + } + } + Rectangle { + visible: !window.isWindows + height: window.titleBarHeight + color: "#20202a" + anchors.top: parent.top + anchors.topMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + + Text { + id: macTitleText + y: 3 + height: 16 + color: "#DDDDDD" + text: window.title + verticalAlignment: Text.AlignVCenter + font.weight: Font.Medium + elide: Text.ElideRight + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 12 + } + } + Item { + id: placeholder + anchors.fill: parent + anchors.topMargin: window.isWindows ? 32 : 22 + anchors.leftMargin: window.isWindows ? 1 : 0 + anchors.rightMargin: window.isWindows ? 1 : 0 + anchors.bottomMargin: window.isWindows ? 1 : 0 + clip: true + } + PAGResizeArea { + id: topResizeHandle + isEnable: window.isWindows && window.canResize + direction: directionTop + onPositionChanged: { + window.y += (mouseY - startY); + updateHeight(startY - mouseY); + ensurePositionMovable(); + } + } + PAGResizeArea { + id: bottomResizeHandle + isEnable: window.isWindows && window.canResize + direction: directionBottom + onPositionChanged: { + updateHeight(mouseY - startY); + ensurePositionMovable(); + } + } + PAGResizeArea { + id: leftResizeHandle + isEnable: window.isWindows && window.canResize + direction: directionLeft + onPositionChanged: { + window.x += (mouseX - startX); + updateWidth(startX - mouseX); + ensurePositionMovable(); + } + } + PAGResizeArea { + id: rightResizeHandle + isEnable: window.isWindows && window.canResize + direction: directionRight + onPositionChanged: { + updateWidth(mouseX - startX); + ensurePositionMovable(); + } + } + PAGResizeArea { + id: leftTopResizeHandle + isEnable: window.isWindows && window.canResize + direction: directionLeft & directionTop + onPositionChanged: { + window.x += (mouseX - startX); + window.y += (mouseY - startY); + updateWidth(startX - mouseX); + updateHeight(startY - mouseY); + ensurePositionMovable(); + } + } + PAGResizeArea { + id: rightTopResizeHandle + isEnable: window.isWindows && window.canResize + direction: directionRight & directionTop + onPositionChanged: { + window.y += (mouseY - startY); + updateWidth(mouseX - startX); + updateHeight(startY - mouseY); + ensurePositionMovable(); + } + } + PAGResizeArea { + id: leftBottomResizeHandle + isEnable: window.isWindows && window.canResize + direction: directionLeft & directionBottom + onPositionChanged: { + window.x += (mouseX - startX); + updateWidth(startX - mouseX); + updateHeight(mouseY - startY); + ensurePositionMovable(); + } + } + PAGResizeArea { + id: rightBottomResizeHandle + isEnable: window.isWindows && window.canResize + direction: directionRight & directionBottom + onPositionChanged: { + updateWidth(mouseX - startX); + updateHeight(mouseY - startY); + ensurePositionMovable(); + } + } + + function updateWidth(offset) { + let width = window.width; + let target = width + offset; + target = Math.max(window.minimumWidth, target); + window.width = target; + } + function updateHeight(offset) { + let height = window.height; + let target = height + offset; + target = Math.max(window.minimumHeight, target); + window.height = target; + } + function setMaximized(maximized) { + isMaximized = maximized; + let useFake = Qt.platform.os === 'windows' && Screen.height === Screen.desktopAvailableHeight && Screen.width === Screen.desktopAvailableWidth && isWindows; + if (!useFake) { + window.visibility = maximized ? Window.Maximized : Window.AutomaticVisibility; + return; + } + if (maximized) { + windowLastX = window.x; + windowLastY = window.y; + windowLastWidth = window.width; + windowLastHeight = window.height; + window.visibility = Window.AutomaticVisibility; + window.y = 0; + window.x = 0; + window.width = Screen.width; + window.height = Screen.desktopAvailableHeight - 1; + } else { + window.visibility = Window.AutomaticVisibility; + window.x = windowLastX; + window.y = windowLastY; + window.width = windowLastWidth; + window.height = windowLastHeight; + } + } + function ensurePositionMovable() { + if ((window.x + window.width) < 10) { + window.x = 10 - window.width; + } + if (window.y < 0) { + window.y = 0; + } + } +} diff --git a/viewer/res.qrc b/viewer/res.qrc index 0ed153b24f..b347e63fc7 100644 --- a/viewer/res.qrc +++ b/viewer/res.qrc @@ -1,6 +1,31 @@ + + images/play.png + images/next.png + images/pause.png + images/tiles.png + images/previous.png + images/panels-on.png + images/panels-off.png images/window-icon.png + images/window-icon-32x.png + images/update-red.png + images/background-on.png + images/background-off.png + + + images/close-dark.svg + images/restore-dark.svg + images/maximize-dark.svg + images/minimize-dark.svg + + qml/Main.qml + qml/MainForm.qml + qml/ControlForm.qml + qml/components/PAGWindow.qml + qml/components/PAGRectangle.qml + qml/components/PAGResizeArea.qml diff --git a/viewer/src/PAGView.cpp b/viewer/src/PAGView.cpp deleted file mode 100644 index e8a800700f..0000000000 --- a/viewer/src/PAGView.cpp +++ /dev/null @@ -1,120 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making libpag available. -// -// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file -// except in compliance with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "PAGView.h" -#include -#include -#include -#include -#include "pag/file.h" -#include "platform/qt/GPUDrawable.h" -#include "tgfx/core/Clock.h" - -namespace pag { -class RenderThread : public QThread { - Q_OBJECT - public: - explicit RenderThread(PAGView* pagView) : pagView(pagView) { - } - - Q_SLOT - void flush() { - pagView->pagPlayer->flush(); - QMetaObject::invokeMethod(pagView, "update", Qt::QueuedConnection); - } - - Q_SLOT - void shutDown() { - // Stop event processing, move the thread to GUI and make sure it is deleted. - exit(); - if (QGuiApplication::instance()) { - auto mainThread = QGuiApplication::instance()->thread(); - if (pagView->drawable) { - pagView->drawable->moveToThread(mainThread); - } - moveToThread(mainThread); - } - } - - private: - PAGView* pagView = nullptr; -}; - -PAGView::PAGView(QQuickItem* parent) : QQuickItem(parent) { - setFlag(ItemHasContents, true); - drawable = GPUDrawable::MakeFrom(this); - pagPlayer = new PAGPlayer(); - auto pagSurface = PAGSurface::MakeFrom(drawable); - pagPlayer->setSurface(pagSurface); - renderThread = new RenderThread(this); - renderThread->moveToThread(renderThread); - drawable->moveToThread(renderThread); -} - -PAGView::~PAGView() { - QMetaObject::invokeMethod(renderThread, "shutDown", Qt::QueuedConnection); - renderThread->wait(); - delete renderThread; - delete pagPlayer; -} - -void PAGView::setFile(const std::shared_ptr pagFile) { - pagPlayer->setComposition(pagFile); -} - -QSGNode* PAGView::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) { - if (!renderThread->isRunning()) { - renderThread->start(); - } - - if (lastWidth != width() || lastHeight != height() || - lastPixelRatio != window()->devicePixelRatio()) { - lastWidth = width(); - lastHeight = height(); - lastPixelRatio = window()->devicePixelRatio(); - auto pagSurface = pagPlayer->getSurface(); - if (pagSurface) { - pagSurface->updateSize(); - } - } - - auto node = static_cast(oldNode); - auto texture = drawable->getTexture(); - if (texture) { - if (node == nullptr) { - node = window()->createImageNode(); - } - node->setTexture(texture); - node->markDirty(QSGNode::DirtyMaterial); - node->setRect(boundingRect()); - } - - if (isPlaying) { - auto duration = pagPlayer->duration(); - if (duration > 0) { - auto playTime = (tgfx::Clock::Now() - startTime) % duration; - auto progress = static_cast(playTime) / static_cast(duration); - pagPlayer->setProgress(progress); - } - QMetaObject::invokeMethod(renderThread, "flush", Qt::QueuedConnection); - } - return node; -} -} // namespace pag - -#include "PAGView.moc" \ No newline at end of file diff --git a/viewer/src/PAGViewer.cpp b/viewer/src/PAGViewer.cpp index fb8cb8b15d..4b2e967357 100644 --- a/viewer/src/PAGViewer.cpp +++ b/viewer/src/PAGViewer.cpp @@ -17,21 +17,23 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "PAGViewer.h" +#include #include +#include "rendering/PAGWindow.h" PAGViewer::PAGViewer(int& argc, char** argv) : QApplication(argc, argv) { } -bool PAGViewer::event(QEvent* event) { +auto PAGViewer::event(QEvent* event) -> bool { if (event->type() == QEvent::FileOpen) { - auto openEvent = static_cast(event); + auto openEvent = dynamic_cast(event); auto path = openEvent->file(); - OpenFile(path); + openFile(path); } return QApplication::event(event); } -void PAGViewer::OpenFile(QString path) { +auto PAGViewer::openFile(QString path) -> void { PAGWindow* window = nullptr; for (int i = 0; i < PAGWindow::AllWindows.count(); i++) { auto win = PAGWindow::AllWindows[i]; @@ -44,16 +46,16 @@ void PAGViewer::OpenFile(QString path) { if (!window) { window = new PAGWindow(); - window->Open(); + window->open(); PAGWindow::AllWindows.append(window); - QObject::connect(window, &PAGWindow::DestroyWindow, this, &PAGViewer::onWindowDestroyed, + QObject::connect(window, &PAGWindow::destroyWindow, this, &PAGViewer::onWindowDestroyed, Qt::UniqueConnection); } - window->OpenFile(path); + window->openFile(path); } -void PAGViewer::onWindowDestroyed(PAGWindow* window) { +auto PAGViewer::onWindowDestroyed(PAGWindow* window) -> void { PAGWindow::AllWindows.removeOne(window); window->deleteLater(); } diff --git a/viewer/src/PAGViewer.h b/viewer/src/PAGViewer.h index e0921eab4e..091e61bde1 100644 --- a/viewer/src/PAGViewer.h +++ b/viewer/src/PAGViewer.h @@ -19,16 +19,16 @@ #pragma once #include -#include -#include -#include "PAGWindow.h" + +class PAGWindow; class PAGViewer : public QApplication { Q_OBJECT public: PAGViewer(int& argc, char** argv); - void OpenFile(QString path); + auto event(QEvent* event) -> bool override; + auto openFile(QString path) -> void; + Q_SLOT void onWindowDestroyed(PAGWindow* window); - bool event(QEvent* event) override; }; \ No newline at end of file diff --git a/viewer/src/main.cpp b/viewer/src/main.cpp index 2ee24a13a4..43091fcc97 100644 --- a/viewer/src/main.cpp +++ b/viewer/src/main.cpp @@ -16,16 +16,12 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include #include -#include -#include -#include -#include -#include -#include "PAGView.h" +#include +#include +#include #include "PAGViewer.h" -#include "qobject.h" +#include "rendering/PAGView.h" int main(int argc, char* argv[]) { QApplication::setApplicationName("PAGViewer"); @@ -36,12 +32,18 @@ int main(int argc, char* argv[]) { defaultFormat.setProfile(QSurfaceFormat::CoreProfile); QSurfaceFormat::setDefaultFormat(defaultFormat); QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); + QQuickStyle::setStyle("Universal"); + QFont defaultFonts("Helvetica Neue,PingFang SC"); + defaultFonts.setStyleHint(QFont::SansSerif); + QApplication::setFont(defaultFonts); + std::vector fallbackList = {"PingFang SC", "Apple Color Emoji"}; + pag::PAGFont::SetFallbackFontNames(fallbackList); PAGViewer app(argc, argv); QApplication::setWindowIcon(QIcon(":/images/window-icon.png")); qmlRegisterType("PAG", 1, 0, "PAGView"); auto rootPath = QApplication::applicationDirPath(); rootPath = QFileInfo(rootPath + "/../../").absolutePath(); - app.OpenFile(rootPath + "/assets/test2.pag"); + app.openFile(rootPath + "/assets/test2.pag"); return QApplication::exec(); } diff --git a/viewer/src/rendering/PAGRenderThread.cpp b/viewer/src/rendering/PAGRenderThread.cpp new file mode 100644 index 0000000000..8a3d973516 --- /dev/null +++ b/viewer/src/rendering/PAGRenderThread.cpp @@ -0,0 +1,43 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "PAGRenderThread.h" +#include + +namespace pag { + +PAGRenderThread::PAGRenderThread(PAGView* pagView) : pagView(pagView) { +} + +auto PAGRenderThread::flush() -> void { + pagView->pagPlayer->flush(); + QMetaObject::invokeMethod(pagView, "update", Qt::QueuedConnection); +} + +auto PAGRenderThread::shutDown() -> void { + exit(); + if (QGuiApplication::instance()) { + auto mainThread = QGuiApplication::instance()->thread(); + if (pagView->drawable) { + pagView->drawable->moveToThread(mainThread); + } + moveToThread(mainThread); + } +} + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/PAGView.h b/viewer/src/rendering/PAGRenderThread.h similarity index 59% rename from viewer/src/PAGView.h rename to viewer/src/rendering/PAGRenderThread.h index a9b67ac1c9..fbdbc6f66e 100644 --- a/viewer/src/PAGView.h +++ b/viewer/src/rendering/PAGRenderThread.h @@ -18,36 +18,20 @@ #pragma once -#include -#include -#include "pag/pag.h" +#include +#include "rendering/PAGView.h" namespace pag { -class RenderThread; -class GPUDrawable; - -class PAGView : public QQuickItem { +class PAGRenderThread : public QThread { Q_OBJECT public: - explicit PAGView(QQuickItem* parent = nullptr); - ~PAGView() override; - - void setFile(const std::shared_ptr pagFile); + explicit PAGRenderThread(PAGView* pagView); - QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) override; + Q_SLOT void flush(); + Q_SLOT void shutDown(); private: - int64_t startTime = 0; - bool isPlaying = true; - qreal lastPixelRatio = 1; - qreal lastWidth = 0; - qreal lastHeight = 0; - QString filePath = ""; - PAGPlayer* pagPlayer = nullptr; - RenderThread* renderThread = nullptr; - std::shared_ptr drawable = nullptr; - - friend class RenderThread; + PAGView* pagView = nullptr; }; -} // namespace pag +} // namespace pag \ No newline at end of file diff --git a/viewer/src/rendering/PAGView.cpp b/viewer/src/rendering/PAGView.cpp new file mode 100644 index 0000000000..7eeb01d03c --- /dev/null +++ b/viewer/src/rendering/PAGView.cpp @@ -0,0 +1,275 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "PAGView.h" +#include +#include +#include +#include "pag/file.h" +#include "rendering/PAGRenderThread.h" +#include "tgfx/core/Clock.h" + +namespace pag { + +PAGView::PAGView(QQuickItem* parent) : QQuickItem(parent) { + setFlag(ItemHasContents, true); + drawable = GPUDrawable::MakeFrom(this); + pagPlayer = new PAGPlayer(); + auto pagSurface = PAGSurface::MakeFrom(drawable); + pagPlayer->setSurface(pagSurface); + renderThread = new PAGRenderThread(this); + renderThread->moveToThread(renderThread); + drawable->moveToThread(renderThread); +} + +PAGView::~PAGView() { + QMetaObject::invokeMethod(renderThread, "shutDown", Qt::QueuedConnection); + renderThread->wait(); + delete renderThread; + delete pagPlayer; +} + +auto PAGView::getPAGWidth() const -> int { + if (pagFile == nullptr) { + return 100; + } + return pagFile->width(); +} + +auto PAGView::getPAGHeight() const -> int { + if (pagFile == nullptr) { + return 100; + } + return pagFile->height(); +} + +auto PAGView::getTotalFrame() const -> int { + if (pagFile == nullptr) { + return 0; + } + int totalFrames = static_cast((getDuration() * pagFile->frameRate() + 500) / 1000); + if (totalFrames < 1) { + totalFrames = 1; + } + return totalFrames; +} + +auto PAGView::getCurrentFrame() const -> int { + int totalFrames = getTotalFrame(); + return static_cast(std::round(getProgress() * (totalFrames - 1))); +} + +auto PAGView::getIsPlaying() const -> bool { + return isPlaying; +} + +auto PAGView::getShowVideoFrames() const -> bool { + if (pagPlayer == nullptr) { + return false; + } + return pagPlayer->videoEnabled(); +} + +auto PAGView::getDuration() const -> double { + if (pagPlayer == nullptr) { + return 1.0; + } + return static_cast(pagPlayer->duration()) / 1000.0; +} + +auto PAGView::getProgress() const -> double { + return progress; +} + +auto PAGView::getFilePath() const -> QString { + return filePath; +} + +auto PAGView::getBackgroundColor() const -> QColor { + if (pagFile == nullptr) { + return QColor::fromRgb(0, 0, 0); + } + + auto color = pagFile->getFile()->getRootLayer()->composition->backgroundColor; + return QColor::fromRgb((int32_t)color.red, (int32_t)color.green, (int32_t)color.blue); +} + +auto PAGView::getPreferredSize() const -> QSizeF { + if (pagFile == nullptr) { + return {200, 200}; + } + + auto quickWindow = window(); + qreal scaleRatio = quickWindow->devicePixelRatio(); + int pagWidth = getPAGWidth(); + int pagHeight = getPAGHeight(); + auto screen = quickWindow->screen(); + QSize screenSize = screen->availableVirtualSize(); + while ((pagHeight / scaleRatio) > (screenSize.height() * 0.9)) { + scaleRatio *= 1.2; + } + while ((pagWidth / scaleRatio) > screenSize.width()) { + scaleRatio *= 1.2; + } + + auto height = pagHeight / scaleRatio; + auto width = pagWidth / scaleRatio; + if ((height < quickWindow->minimumHeight()) && (width < quickWindow->minimumWidth())) { + if (height > width) { + height = quickWindow->minimumHeight(); + scaleRatio = pagHeight / height; + width = pagWidth / scaleRatio; + } else { + width = quickWindow->minimumWidth(); + scaleRatio = pagWidth / width; + height = pagHeight / scaleRatio; + } + } + + return {width, height}; +} + +auto PAGView::setIsPlaying(bool isPlaying) -> void { + if (pagFile == nullptr) { + return; + } + if (this->isPlaying == isPlaying) { + return; + } + this->isPlaying = isPlaying; + Q_EMIT isPlayingChanged(isPlaying); + if (isPlaying) { + lastPlayTime = tgfx::Clock::Now(); + QMetaObject::invokeMethod(renderThread, "flush", Qt::QueuedConnection); + } +} + +auto PAGView::setShowVideoFrames(bool isShow) -> void { + if (pagPlayer == nullptr) { + return; + } + pagPlayer->setVideoEnabled(isShow); +} + +auto PAGView::setProgress(double progress) -> void { + if (this->progress == progress) { + return; + } + pagPlayer->setProgress(progress); + this->progress = progress; + Q_EMIT progressChanged(progress); + QMetaObject::invokeMethod(renderThread, "flush", Qt::QueuedConnection); +} + +auto PAGView::setFile(const QString& filePath) -> bool { + auto strPath = std::string(filePath.toLocal8Bit()); + if (filePath.startsWith("file://")) { + strPath = std::string(QUrl(filePath).toLocalFile().toLocal8Bit()); + } + auto byteData = pag::ByteData::FromPath(strPath); + if (byteData == nullptr) { + return false; + } + auto newPagFile = pag::PAGFile::Load(byteData->data(), byteData->length()); + if (newPagFile == nullptr) { + return false; + } + setIsPlaying(false); + pagFile = newPagFile; + this->filePath = strPath.c_str(); + pagPlayer->setComposition(pagFile); + setSize(getPreferredSize()); + progressPerFrame = 1.0 / (pagFile->frameRate() * pagFile->duration() / 1000000); + Q_EMIT fileChanged(pagFile, strPath); + setProgress(0); + setIsPlaying(true); + + return true; +} + +auto PAGView::nextFrame() -> void { + if (pagFile == nullptr) { + return; + } + setIsPlaying(false); + auto progress = this->progress + progressPerFrame; + if (progress > 1) { + progress = 0; + } + setProgress(progress); +} + +auto PAGView::previousFrame() -> void { + if (pagFile == nullptr) { + return; + } + setIsPlaying(false); + auto progress = this->progress - progressPerFrame; + if (progress < 0) { + progress = 1; + } + setProgress(progress); +} + +QSGNode* PAGView::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) { + if (!renderThread->isRunning()) { + renderThread->start(); + } + + if (lastWidth != width() || lastHeight != height() || + lastPixelRatio != window()->devicePixelRatio()) { + lastWidth = width(); + lastHeight = height(); + lastPixelRatio = window()->devicePixelRatio(); + auto pagSurface = pagPlayer->getSurface(); + if (pagSurface) { + pagSurface->updateSize(); + } + } + + auto node = dynamic_cast(oldNode); + auto texture = drawable->getTexture(); + if (texture) { + if (node == nullptr) { + node = window()->createImageNode(); + } + node->setTexture(texture); + node->markDirty(QSGNode::DirtyMaterial); + node->setRect(boundingRect()); + } + + auto timeNow = tgfx::Clock::Now(); + auto displayTime = timeNow - lastPlayTime; + lastPlayTime = timeNow; + + if (isPlaying) { + auto duration = pagPlayer->duration(); + if (duration > 0) { + auto progress = + static_cast(displayTime) / static_cast(duration) + this->progress; + if (progress > 1) { + progress = 0.0; + } + pagPlayer->setProgress(progress); + setProgress(progress); + } + QMetaObject::invokeMethod(renderThread, "flush", Qt::QueuedConnection); + } + return node; +} +} // namespace pag \ No newline at end of file diff --git a/viewer/src/rendering/PAGView.h b/viewer/src/rendering/PAGView.h new file mode 100644 index 0000000000..df5aa49d42 --- /dev/null +++ b/viewer/src/rendering/PAGView.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "pag/pag.h" +#include "platform/qt/GPUDrawable.h" + +namespace pag { + +class PAGRenderThread; + +class PAGView : public QQuickItem { + Q_OBJECT + public: + explicit PAGView(QQuickItem* parent = nullptr); + ~PAGView() override; + + Q_PROPERTY(int pagWidth READ getPAGWidth) + Q_PROPERTY(int pagHeight READ getPAGHeight) + Q_PROPERTY(int totalFrame READ getTotalFrame) + Q_PROPERTY(int currentFrame READ getCurrentFrame) + Q_PROPERTY(bool isPlaying READ getIsPlaying WRITE setIsPlaying NOTIFY isPlayingChanged) + Q_PROPERTY(bool showVideoFrames READ getShowVideoFrames WRITE setShowVideoFrames) + Q_PROPERTY(double duration READ getDuration) + Q_PROPERTY(double progress READ getProgress WRITE setProgress NOTIFY progressChanged) + Q_PROPERTY(QString filePath READ getFilePath NOTIFY fileChanged) + Q_PROPERTY(QColor backgroundColor READ getBackgroundColor) + Q_PROPERTY(QSizeF preferredSize READ getPreferredSize) + + auto getPAGWidth() const -> int; + auto getPAGHeight() const -> int; + auto getTotalFrame() const -> int; + auto getCurrentFrame() const -> int; + auto getIsPlaying() const -> bool; + auto getShowVideoFrames() const -> bool; + auto getDuration() const -> double; + auto getProgress() const -> double; + auto getFilePath() const -> QString; + auto getBackgroundColor() const -> QColor; + auto getPreferredSize() const -> QSizeF; + + auto setIsPlaying(bool isPlaying) -> void; + auto setShowVideoFrames(bool isShow) -> void; + auto setProgress(double progress) -> void; + + Q_SIGNAL void isPlayingChanged(bool isPlaying); + Q_SIGNAL void progressChanged(double progress); + Q_SIGNAL void fileChanged(std::shared_ptr pagFile, std::string filePath); + + Q_INVOKABLE bool setFile(const QString& filePath); + Q_INVOKABLE void nextFrame(); + Q_INVOKABLE void previousFrame(); + + auto updatePaintNode(QSGNode*, UpdatePaintNodeData*) -> QSGNode* override; + + private: + int64_t lastPlayTime = 0; + bool isPlaying = true; + qreal lastWidth = 0; + qreal lastHeight = 0; + qreal lastPixelRatio = 1; + double progress = 0.0; + double progressPerFrame = 0.0; + QString filePath; + PAGPlayer* pagPlayer = nullptr; + PAGRenderThread* renderThread = nullptr; + std::shared_ptr pagFile = nullptr; + std::shared_ptr drawable = nullptr; + + friend class PAGRenderThread; +}; +} // namespace pag diff --git a/viewer/src/PAGWindow.cpp b/viewer/src/rendering/PAGWindow.cpp similarity index 67% rename from viewer/src/PAGWindow.cpp rename to viewer/src/rendering/PAGWindow.cpp index 6529ad41d8..f27578eb65 100644 --- a/viewer/src/PAGWindow.cpp +++ b/viewer/src/rendering/PAGWindow.cpp @@ -30,44 +30,35 @@ PAGWindow::~PAGWindow() { delete engine; } -void PAGWindow::onPAGViewerDestroyed() { - Q_EMIT DestroyWindow(this); +auto PAGWindow::openFile(QString path) -> void { + bool result = pagView->setFile(path); + if (!result) { + return; + } + filePath = path; + window->raise(); + window->requestActivate(); } -void PAGWindow::Open() { +auto PAGWindow::onPAGViewerDestroyed() -> void { + Q_EMIT destroyWindow(this); +} + +auto PAGWindow::open() -> void { engine = new QQmlApplicationEngine; engine->load(QUrl(QStringLiteral("qrc:/qml/Main.qml"))); + window = static_cast(engine->rootObjects().at(0)); - pagView = window->findChild("pagView"); window->setPersistentGraphics(true); window->setPersistentSceneGraph(true); + window->setTextRenderType(QQuickWindow::TextRenderType::NativeTextRendering); + + pagView = window->findChild("pagView"); + connect(window, SIGNAL(closing(QQuickCloseEvent*)), this, SLOT(onPAGViewerDestroyed()), Qt::QueuedConnection); } -void PAGWindow::OpenFile(QString path) { - auto strPath = std::string(path.toLocal8Bit()); - if (path.startsWith("file://")) { - strPath = std::string(QUrl(path).toLocalFile().toLocal8Bit()); - } - auto byteData = pag::ByteData::FromPath(strPath); - if (byteData == nullptr) { - return; - } - auto pagFile = pag::PAGFile::Load(byteData->data(), byteData->length()); - if (pagFile == nullptr) { - return; - } - filePath = path; - pagView->setFile(pagFile); - auto width = pagFile->width(); - auto height = pagFile->height(); - if (height > 800) { - float scale = static_cast(800) / static_cast(height); - height = 800; - width = static_cast(static_cast(width) * scale); - } - window->resize(width, height); - window->raise(); - window->requestActivate(); +auto PAGWindow::getFilePath() -> QString { + return filePath; } diff --git a/viewer/src/PAGWindow.h b/viewer/src/rendering/PAGWindow.h similarity index 83% rename from viewer/src/PAGWindow.h rename to viewer/src/rendering/PAGWindow.h index 9b48c84dec..92213030bd 100644 --- a/viewer/src/PAGWindow.h +++ b/viewer/src/rendering/PAGWindow.h @@ -19,31 +19,29 @@ #pragma once #include -#include #include #include "PAGView.h" class PAGWindow : public QObject { Q_OBJECT public: - PAGWindow(QObject* parent = nullptr); - ~PAGWindow(); + explicit PAGWindow(QObject* parent = nullptr); + ~PAGWindow() override; - static QList AllWindows; - - void Open(); - QString getFilePath() { - return filePath; - } + Q_SIGNAL void destroyWindow(PAGWindow* window); - Q_SLOT void OpenFile(QString path); + Q_SLOT void openFile(QString path); Q_SLOT void onPAGViewerDestroyed(); - Q_SIGNAL void DestroyWindow(PAGWindow* window); + + auto open() -> void; + auto getFilePath() -> QString; + + static QList AllWindows; private: - QString filePath = ""; - QQmlApplicationEngine* engine = nullptr; - pag::PAGView* pagView = nullptr; + QString filePath; QQuickWindow* window = nullptr; + pag::PAGView* pagView = nullptr; QThread* renderThread = nullptr; + QQmlApplicationEngine* engine = nullptr; }; From 912bd40c95a6f0f81ad7aeb2dad8d56d3cd6bac9 Mon Sep 17 00:00:00 2001 From: markffan Date: Thu, 20 Mar 2025 20:18:48 +0800 Subject: [PATCH 09/36] Modify the size adjustment control of the viewer on Windows --- viewer/qml/Main.qml | 2 + viewer/qml/MainForm.qml | 5 + viewer/qml/components/PAGRectangle.qml~ | 56 ----------- viewer/qml/components/PAGResizeArea.qml | 45 --------- viewer/qml/components/PAGWindow.qml | 126 +++++++++--------------- viewer/res.qrc | 1 - 6 files changed, 55 insertions(+), 180 deletions(-) delete mode 100644 viewer/qml/components/PAGRectangle.qml~ delete mode 100644 viewer/qml/components/PAGResizeArea.qml diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 3da6a209d4..6f7cf2a16b 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -12,6 +12,7 @@ PAGWindow { height: 360 minimumWidth: 400 + windowPadding minimumHeight: 320 + windowTitleBarHeight + resizeHandleSize: 5 property string filePath property bool lastPlayStatusIsPlaying: false @@ -34,6 +35,7 @@ PAGWindow { } MainForm { id: mainForm + resizeHandleSize: resizeHandleSize pagView { showVideoFrames: settings.isShowVideoFrames onProgressChanged: function (progress) { diff --git a/viewer/qml/MainForm.qml b/viewer/qml/MainForm.qml index 190258b337..dfe1ad091e 100644 --- a/viewer/qml/MainForm.qml +++ b/viewer/qml/MainForm.qml @@ -5,6 +5,9 @@ import "components" SplitView { id: splitView + + required property int resizeHandleSize + property bool hasPAGFile: pagView.filePath !== "" property bool isBackgroundOn: false @@ -81,6 +84,8 @@ SplitView { id: mouseArea z: 2 anchors.fill: parent + anchors.leftMargin: resizeHandleSize + anchors.rightMargin: resizeHandleSize anchors.bottom: parent.bottom anchors.bottomMargin: controlFormHeight + 9 onClicked: { diff --git a/viewer/qml/components/PAGRectangle.qml~ b/viewer/qml/components/PAGRectangle.qml~ deleted file mode 100644 index a1f93bc286..0000000000 --- a/viewer/qml/components/PAGRectangle.qml~ +++ /dev/null @@ -1,56 +0,0 @@ -import QtQuick - -Rectangle { - id: root - - property bool leftTopRadius: true - property bool leftBottomRadius: true - property bool rightTopRadius: true - property bool rightBottomRadius: true - - Component { - id: cornerRect - Rectangle { - radius: 0 - width: root.radius + 3 - height: root.radius + 3 - color: root.color - } - } - - Loader { - sourceComponent: cornerRect - active: !leftTopRadius - anchors { - left: parent.left - top: parent.top - } - } - - Loader { - sourceComponent: cornerRect - active: !rightTopRadius - anchors { - right: parent.right - top: parent.top - } - } - - Loader { - sourceComponent: cornerRect - active: !leftBottomRadius - anchors { - left: parent.left - bottom: parent.bottom - } - } - - Loader { - sourceComponent: cornerRect - active: !rightBottomRadius - anchors { - right: parent.right - bottom: parent.bottom - } - } -} diff --git a/viewer/qml/components/PAGResizeArea.qml b/viewer/qml/components/PAGResizeArea.qml deleted file mode 100644 index 7937eb0d21..0000000000 --- a/viewer/qml/components/PAGResizeArea.qml +++ /dev/null @@ -1,45 +0,0 @@ -import QtQuick - -MouseArea { - id: mouseArea - required property bool isEnable - required property int direction - property int startX: 0 - - property int startY: 0 - - readonly property int directionNone: 0 - - readonly property int directionLeft: 1 - - readonly property int directionRight: 2 - - readonly property int directionTop: 4 - - readonly property int directionBottom: 8 - enabled: isEnable - visible: isEnable - width: (direction & directionLeft) || (direction & directionRight) ? 4 : undefined - height: (direction & directionTop) || (direction & directionBottom) ? 4 : undefined - anchors.top: (direction & directionBottom) ? undefined : parent.top - anchors.topMargin: 0 - anchors.bottom: (direction & directionTop) ? undefined : parent.bottom - anchors.bottomMargin: 0 - anchors.left: (direction & directionRight) ? undefined : parent.left - anchors.leftMargin: (direction & directionLeft) || (direction & directionRight) ? 0 : 5 - anchors.right: (direction & directionLeft) ? undefined : parent.right - anchors.rightMargin: (direction & directionLeft) || (direction & directionRight) ? 0 : 5 - cursorShape: { - if (direction === directionTop || direction === directionBottom || direction === directionLeft || direction === directionRight) { - return Qt.SizeVerCursor; - } else if (((direction & directionLeft) && (direction & directionTop)) || ((direction & directionRight) && (direction & directionBottom))) { - return Qt.SizeFDiagCursor; - } else { - return Qt.SizeBDiagCursor; - } - } - onPressed: { - startX = mouseX; - startY = mouseY; - } -} diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index bd242c1fba..18180773bb 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -6,6 +6,8 @@ Window { id: window default property alias contents: placeholder.data + required property int resizeHandleSize + property bool isWindows: Qt.platform.os === "windows" property bool isMaximized: false @@ -23,6 +25,7 @@ Window { property int windowLastHeight: 0 property int titleBarHeight: 32 + visible: true color: "#00000000" flags: isWindows ? (Qt.FramelessWindowHint | Qt.Window | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) : Qt.Window @@ -80,6 +83,7 @@ Window { property int windowWidth: 0 height: 40 anchors.fill: parent + anchors.topMargin: resizeHandleSize onPressed: { mouseLastX = mouseX; mouseLastY = mouseY; @@ -142,6 +146,7 @@ Window { source: "qrc:/images/window-icon-32x.png" } Row { + id: windowControlRow anchors.right: parent.right PAGRectangle { @@ -270,86 +275,51 @@ Window { anchors.bottomMargin: window.isWindows ? 1 : 0 clip: true } - PAGResizeArea { - id: topResizeHandle - isEnable: window.isWindows && window.canResize - direction: directionTop - onPositionChanged: { - window.y += (mouseY - startY); - updateHeight(startY - mouseY); - ensurePositionMovable(); - } - } - PAGResizeArea { - id: bottomResizeHandle - isEnable: window.isWindows && window.canResize - direction: directionBottom - onPositionChanged: { - updateHeight(mouseY - startY); - ensurePositionMovable(); - } - } - PAGResizeArea { - id: leftResizeHandle - isEnable: window.isWindows && window.canResize - direction: directionLeft - onPositionChanged: { - window.x += (mouseX - startX); - updateWidth(startX - mouseX); - ensurePositionMovable(); - } - } - PAGResizeArea { - id: rightResizeHandle - isEnable: window.isWindows && window.canResize - direction: directionRight - onPositionChanged: { - updateWidth(mouseX - startX); - ensurePositionMovable(); - } - } - PAGResizeArea { - id: leftTopResizeHandle - isEnable: window.isWindows && window.canResize - direction: directionLeft & directionTop - onPositionChanged: { - window.x += (mouseX - startX); - window.y += (mouseY - startY); - updateWidth(startX - mouseX); - updateHeight(startY - mouseY); - ensurePositionMovable(); - } - } - PAGResizeArea { - id: rightTopResizeHandle - isEnable: window.isWindows && window.canResize - direction: directionRight & directionTop - onPositionChanged: { - window.y += (mouseY - startY); - updateWidth(mouseX - startX); - updateHeight(startY - mouseY); - ensurePositionMovable(); - } - } - PAGResizeArea { - id: leftBottomResizeHandle - isEnable: window.isWindows && window.canResize - direction: directionLeft & directionBottom - onPositionChanged: { - window.x += (mouseX - startX); - updateWidth(startX - mouseX); - updateHeight(mouseY - startY); - ensurePositionMovable(); + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: { + const pos = Qt.point(mouseX, mouseY); + const offset = resizeHandleSize; + if ((pos.x < offset) && (pos.y >= (height - offset))) { + return Qt.SizeBDiagCursor; + } + if ((pos.x < offset) && (pos.y < offset)) { + return Qt.SizeFDiagCursor; + } + if ((pos.x >= (width - offset)) && (pos.y >= (height - offset))) { + return Qt.SizeFDiagCursor; + } + if ((pos.x < offset) || ((pos.x >= (width - offset)) && (pos.y > titleBarHeight))) { + return Qt.SizeHorCursor; + } + if ((pos.y > (height - offset)) || ((pos.y < offset) && (pos.x < (width - 120)))) { + return Qt.SizeVerCursor; + } } + acceptedButtons: Qt.NoButton } - PAGResizeArea { - id: rightBottomResizeHandle - isEnable: window.isWindows && window.canResize - direction: directionRight & directionBottom - onPositionChanged: { - updateWidth(mouseX - startX); - updateHeight(mouseY - startY); - ensurePositionMovable(); + DragHandler { + id: resizeHandler + target: null + grabPermissions: TapHandler.TakeOverForbidden + onActiveChanged: if (active) { + const pos = resizeHandler.centroid.position; + const offset = resizeHandleSize + 10; + let edges = 0; + if (pos.x < offset) { + edges |= Qt.LeftEdge; + } + if (pos.x >= (width - offset)) { + edges += Qt.RightEdge; + } + if (pos.y < offset) { + edges |= Qt.TopEdge; + } + if (pos.y >= (height - offset)) { + edges |= Qt.BottomEdge; + } + window.startSystemResize(edges); } } diff --git a/viewer/res.qrc b/viewer/res.qrc index b347e63fc7..285b05b945 100644 --- a/viewer/res.qrc +++ b/viewer/res.qrc @@ -26,6 +26,5 @@ qml/ControlForm.qml qml/components/PAGWindow.qml qml/components/PAGRectangle.qml - qml/components/PAGResizeArea.qml From 795672642abcfbc010837f4586a7ef4872e8561d Mon Sep 17 00:00:00 2001 From: markffan Date: Thu, 20 Mar 2025 20:29:52 +0800 Subject: [PATCH 10/36] Using qmlformat to do code-format on QML files --- viewer/qml/Main.qml | 1 + viewer/qml/MainForm.qml | 2 -- viewer/qml/components/PAGWindow.qml | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 6f7cf2a16b..1310e97257 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -36,6 +36,7 @@ PAGWindow { MainForm { id: mainForm resizeHandleSize: resizeHandleSize + pagView { showVideoFrames: settings.isShowVideoFrames onProgressChanged: function (progress) { diff --git a/viewer/qml/MainForm.qml b/viewer/qml/MainForm.qml index dfe1ad091e..1049b98031 100644 --- a/viewer/qml/MainForm.qml +++ b/viewer/qml/MainForm.qml @@ -5,9 +5,7 @@ import "components" SplitView { id: splitView - required property int resizeHandleSize - property bool hasPAGFile: pagView.filePath !== "" property bool isBackgroundOn: false diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index 18180773bb..c96069916f 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -7,7 +7,6 @@ Window { default property alias contents: placeholder.data required property int resizeHandleSize - property bool isWindows: Qt.platform.os === "windows" property bool isMaximized: false @@ -25,7 +24,6 @@ Window { property int windowLastHeight: 0 property int titleBarHeight: 32 - visible: true color: "#00000000" flags: isWindows ? (Qt.FramelessWindowHint | Qt.Window | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) : Qt.Window From 332275b9969f95252cc5167bc08345b1903a6c59 Mon Sep 17 00:00:00 2001 From: markffan Date: Thu, 20 Mar 2025 21:24:00 +0800 Subject: [PATCH 11/36] Upload the third-party library winspark to the 'vendor' directory --- DEPS | 7 - vendor.json | 8 - vendor/winsparkle/.gitattributes | 4 + .../include/winsparkle/winsparkle-version.h | 64 ++ .../include/winsparkle/winsparkle.h | 617 ++++++++++++++++++ .../winsparkle/win/x64/Debug/WinSparkle.dll | 3 + .../winsparkle/win/x64/Debug/WinSparkle.lib | 3 + .../winsparkle/win/x64/Release/WinSparkle.dll | 3 + .../winsparkle/win/x64/Release/WinSparkle.lib | 3 + viewer/CMakeLists.txt | 5 +- viewer/qml/components/PAGWindow.qml | 15 +- 11 files changed, 702 insertions(+), 30 deletions(-) create mode 100644 vendor/winsparkle/.gitattributes create mode 100644 vendor/winsparkle/include/winsparkle/winsparkle-version.h create mode 100644 vendor/winsparkle/include/winsparkle/winsparkle.h create mode 100644 vendor/winsparkle/win/x64/Debug/WinSparkle.dll create mode 100644 vendor/winsparkle/win/x64/Debug/WinSparkle.lib create mode 100644 vendor/winsparkle/win/x64/Release/WinSparkle.dll create mode 100644 vendor/winsparkle/win/x64/Release/WinSparkle.lib diff --git a/DEPS b/DEPS index f1a63e4219..9906f30134 100644 --- a/DEPS +++ b/DEPS @@ -42,13 +42,6 @@ "commit": "e9726ff718cffb2d045d84ede9548e38ea48fa01", "dir": "third_party/sparkle" } - ], - "win": [ - { - "url": "https://github.com/vslavik/winsparkle.git", - "commit": "4e6b30b9750b938b184b125d05a664413c72e671", - "dir": "third_party/winsparkle" - } ] }, "actions": { diff --git a/vendor.json b/vendor.json index e411e6df54..2bb9377706 100644 --- a/vendor.json +++ b/vendor.json @@ -58,14 +58,6 @@ "file": "scripts/build_sparkle.sh" } } - }, - { - "name": "winsparkle", - "scripts": { - "win": { - "file": "scripts/build_winsparkle.bat" - } - } } ] } \ No newline at end of file diff --git a/vendor/winsparkle/.gitattributes b/vendor/winsparkle/.gitattributes new file mode 100644 index 0000000000..34b483c7e6 --- /dev/null +++ b/vendor/winsparkle/.gitattributes @@ -0,0 +1,4 @@ +win/x64/Release/WinSparkle.dll filter=lfs diff=lfs merge=lfs -text +win/x64/Release/WinSparkle.lib filter=lfs diff=lfs merge=lfs -text +win/x64/Debug/WinSparkle.dll filter=lfs diff=lfs merge=lfs -text +win/x64/Debug/WinSparkle.lib filter=lfs diff=lfs merge=lfs -text diff --git a/vendor/winsparkle/include/winsparkle/winsparkle-version.h b/vendor/winsparkle/include/winsparkle/winsparkle-version.h new file mode 100644 index 0000000000..6967cf16ae --- /dev/null +++ b/vendor/winsparkle/include/winsparkle/winsparkle-version.h @@ -0,0 +1,64 @@ +/* + * This file is part of WinSparkle (https://winsparkle.org) + * + * Copyright (C) 2009-2024 Vaclav Slavik + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _winsparkle_version_h_ +#define _winsparkle_version_h_ + +/*--------------------------------------------------------------------------* + Version information + *--------------------------------------------------------------------------*/ + +#define WIN_SPARKLE_VERSION_MAJOR 0 +#define WIN_SPARKLE_VERSION_MINOR 8 +#define WIN_SPARKLE_VERSION_MICRO 3 + +/** + Checks if WinSparkle version is at least @a major.@a minor.@a micro. + */ +#define WIN_SPARKLE_CHECK_VERSION(major, minor, micro) \ + ( \ + WIN_SPARKLE_VERSION_MAJOR > (major) \ + || \ + (WIN_SPARKLE_VERSION_MAJOR == (major) && \ + WIN_SPARKLE_VERSION_MINOR >= (minor)) \ + || \ + (WIN_SPARKLE_VERSION_MAJOR == (major) && \ + WIN_SPARKLE_VERSION_MINOR == (minor) && \ + WIN_SPARKLE_VERSION_MICRO >= (micro)) \ + ) + +#define _WIN_SPARKLE_MAKE_STR(x) #x +#define _WIN_SPARKLE_MAKE_VERSION_STR(a,b,c) \ + _WIN_SPARKLE_MAKE_STR(a) "." _WIN_SPARKLE_MAKE_STR(b) "." _WIN_SPARKLE_MAKE_STR(c) + +/** + WinSparkle version as a string in the form of e.g. "0.1.3". + */ +#define WIN_SPARKLE_VERSION_STRING \ + _WIN_SPARKLE_MAKE_VERSION_STR(WIN_SPARKLE_VERSION_MAJOR, \ + WIN_SPARKLE_VERSION_MINOR, \ + WIN_SPARKLE_VERSION_MICRO) + +#endif // _winsparkle_version_h_ diff --git a/vendor/winsparkle/include/winsparkle/winsparkle.h b/vendor/winsparkle/include/winsparkle/winsparkle.h new file mode 100644 index 0000000000..0db0654b7a --- /dev/null +++ b/vendor/winsparkle/include/winsparkle/winsparkle.h @@ -0,0 +1,617 @@ +/* + * This file is part of WinSparkle (https://winsparkle.org) + * + * Copyright (C) 2009-2024 Vaclav Slavik + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _winsparkle_h_ +#define _winsparkle_h_ + +#include +#include + +#include "winsparkle-version.h" + +#if !defined(BUILDING_WIN_SPARKLE) && defined(_MSC_VER) +#pragma comment(lib, "WinSparkle.lib") +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef BUILDING_WIN_SPARKLE + #define WIN_SPARKLE_API __declspec(dllexport) +#else + #define WIN_SPARKLE_API __declspec(dllimport) +#endif + + +/// Return value for boolean functions to indicate unexpected error. +/// Only used by functions or callbacks that are explicitly documented as using it. +#define WINSPARKLE_RETURN_ERROR (-1) + + +/*--------------------------------------------------------------------------* + Initialization and shutdown + *--------------------------------------------------------------------------*/ + +/** + @name Initialization functions + */ +//@{ + +/** + Starts WinSparkle. + + If WinSparkle is configured to check for updates on startup, proceeds + to perform the check. You should only call this function when your app + is initialized and shows its main window. + + @note This call doesn't block and returns almost immediately. If an + update is available, the respective UI is shown later from a separate + thread. + + @see win_sparkle_cleanup() + */ +WIN_SPARKLE_API void __cdecl win_sparkle_init(); + +/** + Cleans up after WinSparkle. + + Should be called by the app when it's shutting down. Cancels any + pending Sparkle operations and shuts down its helper threads. + */ +WIN_SPARKLE_API void __cdecl win_sparkle_cleanup(); + +//@} + + +/*--------------------------------------------------------------------------* + Language settings +*--------------------------------------------------------------------------*/ + +/** +@name Language settings + +These functions set user interface language. They must be called before +win_sparkle_init() to have any effect. If none of them is called, WinSparkle +detects the UI language automatically. +*/ +//@{ + +/** + Sets UI language from its ISO code. + + This function must be called before win_sparkle_init(). + + @param lang ISO 639 language code with an optional ISO 3116 country + code, e.g. "fr", "pt-PT", "pt-BR" or "pt_BR", as used + e.g. by ::GetThreadPreferredUILanguages() too. + + @since 0.5 + + @see win_sparkle_set_langid() +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_lang(const char *lang); + +/** + Sets UI language from its Win32 LANGID code. + + This function must be called before win_sparkle_init(). + + @param lang Language code (LANGID) as created by the MAKELANGID macro + or returned by e.g. ::GetThreadUILanguage() + + @since 0.5 + + @see win_sparkle_set_lang() +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_langid(unsigned short lang); + +//@} + +/*--------------------------------------------------------------------------* + Configuration + *--------------------------------------------------------------------------*/ + +/** + @name Configuration functions + + Functions for setting up WinSparkle. + + All functions in this category can only be called @em before the first + call to win_sparkle_init()! + + Typically, the application would configure WinSparkle on startup and then + call win_sparkle_init(), all from its main thread. + */ +//@{ + +/** + Sets URL for the app's appcast. + + Only http and https schemes are supported. + + If this function isn't called by the app, the URL is obtained from + Windows resource named "FeedURL" of type "APPCAST". + + @param url URL of the appcast. + + @note Always use HTTPS feeds, do not use unencrypted HTTP! This is + necessary to prevent both leaking user information and preventing + various MITM attacks. + + @note See https://github.com/vslavik/winsparkle/wiki/Appcast-Feeds for + more information about appcast feeds. + */ +WIN_SPARKLE_API void __cdecl win_sparkle_set_appcast_url(const char *url); + +/** + Sets DSA public key. + + Only PEM format is supported. + + Public key will be used to verify DSA signature of the update file. + PEM data will be set only if it contains valid DSA public key. + + If this function isn't called by the app, public key is obtained from + Windows resource named "DSAPub" of type "DSAPEM". + + @param dsa_pub_pem DSA public key in PEM format. + + @return 1 if valid DSA public key provided, 0 otherwise. + + @since 0.6.0 + */ +WIN_SPARKLE_API int __cdecl win_sparkle_set_dsa_pub_pem(const char *dsa_pub_pem); + +/** + Sets application metadata. + + Normally, these are taken from VERSIONINFO/StringFileInfo resources, + but if your application doesn't use them for some reason, using this + function is an alternative. + + @param company_name Company name of the vendor. + @param app_name Application name. This is both shown to the user + and used in HTTP User-Agent header. + @param app_version Version of the app, as string (e.g. "1.2" or "1.2rc1"). + + @note @a company_name and @a app_name are used to determine the location + of WinSparkle settings in registry. + (HKCU\Software\\\WinSparkle is used.) + + @since 0.3 + + @see win_sparkle_set_app_build_version(); + */ +WIN_SPARKLE_API void __cdecl win_sparkle_set_app_details(const wchar_t *company_name, + const wchar_t *app_name, + const wchar_t *app_version); + +/** + Sets application build version number. + + This is the internal version number that is not normally shown to the user. + It can be used for finer granularity that official release versions, e.g. for + interim builds. + + If this function is called, then the provided *build* number is used for comparing + versions; it is compared to the "version" attribute in the appcast and corresponds + to OS X Sparkle's CFBundleVersion handling. If used, then the appcast must + also contain the "shortVersionString" attribute with human-readable display + version string. The version passed to win_sparkle_set_app_details() + corresponds to this and is used for display. + + @since 0.4 + + @see win_sparkle_set_app_details() + */ +WIN_SPARKLE_API void __cdecl win_sparkle_set_app_build_version(const wchar_t *build); + +/** + Set custom HTTP header for appcast checks. + + @since 0.7 + + @see win_sparkle_clear_http_headers() +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_http_header(const char *name, const char *value); + +/** + Clears all custom HTTP headers previously added using + win_sparkle_set_http_header(). + + @since 0.7 + + @see win_sparkle_set_http_header() +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_clear_http_headers(); + +/** + Set the registry path where settings will be stored. + + Normally, these are stored in + "HKCU\Software\\\WinSparkle" + but if your application needs to store the data elsewhere for + some reason, using this function is an alternative. + + Note that @a path is relative to HKCU/HKLM root and the root is not part + of it. For example: + @code + win_sparkle_set_registry_path("Software\\My App\\Updates"); + @endcode + + @param path Registry path where settings will be stored. + + @since 0.3 + */ +WIN_SPARKLE_API void __cdecl win_sparkle_set_registry_path(const char *path); + +/// Type used to override WinSparkle configuration's read, write and delete functions +typedef struct win_sparkle_config_methods_tag { + /// Copy config value named @a name to the buffer pointed by @a buf, returns TRUE on success, FALSE on failure + int(__cdecl *config_read)(const char *name, wchar_t *buf, size_t len, void *user_data); + /// Write @a value as config value @a name 's new value + void(__cdecl *config_write)(const char *name, const wchar_t *value, void *user_data); + /// Delete config value named @a name + void(__cdecl *config_delete)(const char *name, void *user_data); + /// Arbitrary data which will be passed to the above functions, WinSparkle will not read or alter it. + void *user_data; +} win_sparkle_config_methods_t; + + +/** + Override WinSparkle's configuration read, write and delete functions. + + By default, WinSparkle will read, write and delete configuration values by + interacting directly with Windows Registry. + If you want to manage configuration by yourself, or if you don't want let WinSparkle + write settings directly to the Windows Registry, you can provide your own functions + to read, write and delete configuration. + + These functions needs to return TRUE on success, FALSE on failure. + If you passed NULL as a configuration action (read, write or delete)'s function pointer, + WinSparkle will use the default function for that action. + + @param config_methods Your own configuration read, write and delete functions. + Pass NULL to let WinSparkle continue to use its default functions. + + @note There's no guarantee about the thread from which these functions are called, + Make sure your functions are thread-safe. + + @since 0.7 +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_config_methods(win_sparkle_config_methods_t *config_methods); + +/** + Sets whether updates are checked automatically or only through a manual call. + + If disabled, win_sparkle_check_update_with_ui() must be used explicitly. + + @param state 1 to have updates checked automatically, 0 otherwise + + @since 0.4 + */ +WIN_SPARKLE_API void __cdecl win_sparkle_set_automatic_check_for_updates(int state); + +/** + Gets the automatic update checking state + + @return 1 if updates are set to be checked automatically, 0 otherwise + + @note Defaults to 0 when not yet configured (as happens on first start). + + @since 0.4 + */ +WIN_SPARKLE_API int __cdecl win_sparkle_get_automatic_check_for_updates(); + +/** + Sets the automatic update interval. + + @param interval The interval in seconds between checks for updates. + The minimum update interval is 3600 seconds (1 hour). + + @since 0.4 + */ +WIN_SPARKLE_API void __cdecl win_sparkle_set_update_check_interval(int interval); + +/** + Gets the automatic update interval in seconds. + + Default value is one day. + + @since 0.4 + */ +WIN_SPARKLE_API int __cdecl win_sparkle_get_update_check_interval(); + +/** + Gets the time for the last update check. + + Default value is -1, indicating that the update check has never run. + + @since 0.4 +*/ +WIN_SPARKLE_API time_t __cdecl win_sparkle_get_last_check_time(); + +/// Callback type for win_sparkle_error_callback() +typedef void (__cdecl *win_sparkle_error_callback_t)(); + +/** + Set callback to be called when the updater encounters an error. + + @since 0.5 +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_error_callback(win_sparkle_error_callback_t callback); + +/// Callback type for win_sparkle_can_shutdown_callback() +typedef int (__cdecl *win_sparkle_can_shutdown_callback_t)(); + +/** + Set callback for querying the application if it can be closed. + + This callback will be called to ask the host if it's ready to shut down, + before attempting to launch the installer. The callback returns TRUE if + the host application can be safely shut down or FALSE if not (e.g. because + the user has unsaved documents). + + @note There's no guarantee about the thread from which the callback is called, + except that it certainly *won't* be called from the app's main thread. + Make sure the callback is thread-safe. + + @since 0.4 + + @see win_sparkle_set_shutdown_request_callback() +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_can_shutdown_callback(win_sparkle_can_shutdown_callback_t callback); + + +/// Callback type for win_sparkle_shutdown_request_callback() +typedef void (__cdecl *win_sparkle_shutdown_request_callback_t)(); + +/** + Set callback for shutting down the application. + + This callback will be called to ask the host to shut down immediately after + launching the installer. Its implementation should gracefully terminate the + application. + + It will only be called if the call to the callback set with + win_sparkle_set_can_shutdown_callback() returns TRUE. + + @note There's no guarantee about the thread from which the callback is called, + except that it certainly *won't* be called from the app's main thread. + Make sure the callback is thread-safe. + + @since 0.4 + + @see win_sparkle_set_can_shutdown_callback() +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_shutdown_request_callback(win_sparkle_shutdown_request_callback_t); + +/// Callback type for win_sparkle_did_find_update_callback() +typedef void(__cdecl *win_sparkle_did_find_update_callback_t)(); + +/** + Set callback to be called when the updater did find an update. + + This is useful in combination with + win_sparkle_check_update_with_ui_and_install() as it allows you to perform + some action after WinSparkle checks for updates. + + @since 0.5 + + @see win_sparkle_did_not_find_update_callback() + @see win_sparkle_check_update_with_ui_and_install() +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_did_find_update_callback(win_sparkle_did_find_update_callback_t callback); + +/// Callback type for win_sparkle_did_not_find_update_callback() +typedef void (__cdecl *win_sparkle_did_not_find_update_callback_t)(); + +/** + Set callback to be called when the updater did not find an update. + + This is useful in combination with + win_sparkle_check_update_with_ui_and_install() as it allows you to perform + some action after WinSparkle checks for updates. + + @since 0.5 + + @see win_sparkle_did_find_update_callback() + @see win_sparkle_check_update_with_ui_and_install() +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_did_not_find_update_callback(win_sparkle_did_not_find_update_callback_t callback); + +/// Callback type for win_sparkle_update_cancelled_callback() +typedef void (__cdecl *win_sparkle_update_cancelled_callback_t)(); + +/** + Set callback to be called when the user cancels an update, e.g. + by closing the window, skipping an update or cancelling download. + This callback is not called when there's no update to install or an error occurs. + + This is useful in combination with + win_sparkle_check_update_with_ui_and_install() as it allows you to perform + some action when the installation is interrupted. + + @since 0.5 + + @see win_sparkle_check_update_with_ui_and_install(), win_sparkle_set_update_dismissed_callback() +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_update_cancelled_callback(win_sparkle_update_cancelled_callback_t callback); + +/// Callback type for win_sparkle_update_skipped_callback() +typedef void(__cdecl* win_sparkle_update_skipped_callback_t)(); + +/** + Set callback to be called when the user skips an update. + + This is useful in combination with + win_sparkle_check_update_with_ui() or similar as it + allows you to perform some action when the update + is skipped. + + @see win_sparkle_check_update_with_ui_and_install() + + @since 0.8 +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_update_skipped_callback(win_sparkle_update_skipped_callback_t callback); + +/// Callback type for win_sparkle_update_postponed_callback() +typedef void(__cdecl* win_sparkle_update_postponed_callback_t)(); + +/** + Set callback to be called when the user postpones an update + ( presses 'remind me later' button). + + This is useful in combination with + win_sparkle_check_update_with_ui() or similar as it + allows you to perform some action when the download + is postponed. + + @see win_sparkle_check_update_with_ui() + + @since 0.8 +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_update_postponed_callback(win_sparkle_update_postponed_callback_t callback); + +/// Callback type for win_sparkle_update_dismissed_callback() +typedef void(__cdecl* win_sparkle_update_dismissed_callback_t)(); + +/** + Set callback to be called when the user dismisses + (closes) update dialog, including when there were no updates or an error occured. + See win_sparkle_set_update_cancelled_callback() for a subtly different callback + that may be more appropriate. + + This is useful in combination with + win_sparkle_check_update_with_ui() or similar as it + allows you to perform some action when the update + dialog is closed. + + @see win_sparkle_check_update_with_ui(), win_sparkle_set_update_cancelled_callback() + + @since 0.8 +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_update_dismissed_callback(win_sparkle_update_dismissed_callback_t callback); + + +/// Callback type for win_sparkle_user_run_installer_callback() +typedef int(__cdecl* win_sparkle_user_run_installer_callback_t)(const wchar_t *); + +/** + Set callback to be called when the update payload is + downloaded and read to be executed.or handled in some + other manner. + + The callback returns: + - 1 if the file was handled by the callback + - 0 if it was not, in which case WinSparkle default handling will take place + - WINSPARKLE_RETURN_ERROR in case of an error + + @since 0.8 +*/ +WIN_SPARKLE_API void __cdecl win_sparkle_set_user_run_installer_callback(win_sparkle_user_run_installer_callback_t callback); + +//@} + + +/*--------------------------------------------------------------------------* + Manual usage + *--------------------------------------------------------------------------*/ + +/** + @name Manually using WinSparkle + */ +//@{ + +/** + Checks if an update is available, showing progress UI to the user. + + Normally, WinSparkle checks for updates on startup and only shows its UI + when it finds an update. If the application disables this behavior, it + can hook this function to "Check for updates..." menu item. + + When called, background thread is started to check for updates. A small + window is shown to let the user know the progress. If no update is found, + the user is told so. If there is an update, the usual "update available" + window is shown. + + This function returns immediately. + + @note Because this function is intended for manual, user-initiated checks + for updates, it ignores "Skip this version" even if the user checked + it previously. + + @see win_sparkle_check_update_without_ui() + */ +WIN_SPARKLE_API void __cdecl win_sparkle_check_update_with_ui(); + +/** + Checks if an update is available, showing progress UI to the user and + immediately installing the update if one is available. + + This is useful for the case when users should almost always use the + newest version of your software. When called, WinSparkle will check for + updates showing a progress UI to the user. If an update is found the update + prompt will be skipped and the update will be installed immediately. + + If your application expects to do something after checking for updates you + may wish to use win_sparkle_set_did_not_find_update_callback() and + win_sparkle_set_update_cancelled_callback(). + + @since 0.5 + + @see win_sparkle_set_did_find_update_callback() + @see win_sparkle_set_update_cancelled_callback() + */ +WIN_SPARKLE_API void __cdecl win_sparkle_check_update_with_ui_and_install(); + +/** + Checks if an update is available. + + No progress UI is shown to the user when checking. If an update is + available, the usual "update available" window is shown; this function + is *not* completely UI-less. + + Use with caution, it usually makes more sense to use the automatic update + checks on interval option or manual check with visible UI. + + This function returns immediately. + + @note This function respects "Skip this version" choice by the user. + + @since 0.4 + + @see win_sparkle_check_update_with_ui() + */ +WIN_SPARKLE_API void __cdecl win_sparkle_check_update_without_ui(); + +//@} + +#ifdef __cplusplus +} +#endif + +#endif // _winsparkle_h_ diff --git a/vendor/winsparkle/win/x64/Debug/WinSparkle.dll b/vendor/winsparkle/win/x64/Debug/WinSparkle.dll new file mode 100644 index 0000000000..3e574e4346 --- /dev/null +++ b/vendor/winsparkle/win/x64/Debug/WinSparkle.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b541a4c10bf793d46d35872a51b7e96fbe541751d965932f24b02faeacc18bc4 +size 11821568 diff --git a/vendor/winsparkle/win/x64/Debug/WinSparkle.lib b/vendor/winsparkle/win/x64/Debug/WinSparkle.lib new file mode 100644 index 0000000000..8a94d9094d --- /dev/null +++ b/vendor/winsparkle/win/x64/Debug/WinSparkle.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12c965324e66ff9997fad504e66c59b4c47c0144d2dc00a30ab10a3e1aad4894 +size 10460 diff --git a/vendor/winsparkle/win/x64/Release/WinSparkle.dll b/vendor/winsparkle/win/x64/Release/WinSparkle.dll new file mode 100644 index 0000000000..bb7550ebb8 --- /dev/null +++ b/vendor/winsparkle/win/x64/Release/WinSparkle.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:763d540a7f975401ac087835e7e0df650806bfab1e3e86af182c5399f85a5eca +size 2793472 diff --git a/vendor/winsparkle/win/x64/Release/WinSparkle.lib b/vendor/winsparkle/win/x64/Release/WinSparkle.lib new file mode 100644 index 0000000000..4b30839381 --- /dev/null +++ b/vendor/winsparkle/win/x64/Release/WinSparkle.lib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9512139af36eeccad508bda34a84404db39dca57a0a0d7cd285ca7605661e683 +size 10460 diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index 2a029a3e21..f4f5ce8e97 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -98,10 +98,9 @@ if (APPLE) list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/rttr/mac/include) list(APPEND VIEWER_VENDOR_LIBRARIES ${CMAKE_SOURCE_DIR}/../third_party/out/sparkle/lib/Sparkle.framework) elseif (WIN32) - add_vendor_target(viewer-vendor SHARED_VENDORS winsparkle CONFIG_DIR ${CMAKE_SOURCE_DIR}/../) list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/rttr/win/include) - list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/winsparkle/include) - list(APPEND VIEWER_VENDOR_LIBRARIES ${CMAKE_SOURCE_DIR}/../third_party/out/winsparkle/lib/x64/${CMAKE_BUILD_TYPE}/WinSparkle.lib) + list(APPEND PAG_VIEWER_INCLUDES ../vendor/winsparkle/include) + list(APPEND VIEWER_VENDOR_LIBRARIES ${CMAKE_SOURCE_DIR}/../vendor/winsparkle/win/x64/${CMAKE_BUILD_TYPE}/WinSparkle.lib) endif () target_include_directories(PAGViewer PUBLIC ${PAG_VIEWER_INCLUDES}) diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index c96069916f..cc1de653e0 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -29,6 +29,7 @@ Window { flags: isWindows ? (Qt.FramelessWindowHint | Qt.Window | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) : Qt.Window Rectangle { + z: 1 visible: window.isWindows anchors.fill: parent radius: 5 @@ -274,6 +275,7 @@ Window { clip: true } MouseArea { + enabled: window.isWindows && window.canResize anchors.fill: parent hoverEnabled: true cursorShape: { @@ -299,6 +301,7 @@ Window { } DragHandler { id: resizeHandler + enabled: window.isWindows && window.canResize target: null grabPermissions: TapHandler.TakeOverForbidden onActiveChanged: if (active) { @@ -321,18 +324,6 @@ Window { } } - function updateWidth(offset) { - let width = window.width; - let target = width + offset; - target = Math.max(window.minimumWidth, target); - window.width = target; - } - function updateHeight(offset) { - let height = window.height; - let target = height + offset; - target = Math.max(window.minimumHeight, target); - window.height = target; - } function setMaximized(maximized) { isMaximized = maximized; let useFake = Qt.platform.os === 'windows' && Screen.height === Screen.desktopAvailableHeight && Screen.width === Screen.desktopAvailableWidth && isWindows; From 1f1360d8f1d39b048ceb66eb4af4944405652d99 Mon Sep 17 00:00:00 2001 From: markffan Date: Fri, 21 Mar 2025 10:00:42 +0800 Subject: [PATCH 12/36] Remove the build script of winsparkle --- scripts/build_winsparkle.bat | 89 ------------------------------------ 1 file changed, 89 deletions(-) delete mode 100644 scripts/build_winsparkle.bat diff --git a/scripts/build_winsparkle.bat b/scripts/build_winsparkle.bat deleted file mode 100644 index ffd42ccc94..0000000000 --- a/scripts/build_winsparkle.bat +++ /dev/null @@ -1,89 +0,0 @@ -@echo off -setlocal EnableDelayedExpansion - -goto :main - -:: function GetMSBuildPath begin -:GetMSBuildPath -set "MSBUILD_PATH=" -set "vswhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" - -if not exist "%vswhere%" ( - echo Error: vswhere.exe not found - exit /b 1 -) - -for /f "usebackq tokens=*" %%i in (`"%vswhere%" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath`) do ( - set "VS_PATH=%%i" -) - -if not defined VS_PATH ( - echo Error: Visual Studio installation not found - exit /b 1 -) - -set "MSBUILD_PATH=%VS_PATH%\MSBuild\Current\Bin\MSBuild.exe" -if not exist "!MSBUILD_PATH!" ( - echo Error: MSBuild.exe not found - exit /b 1 -) - -echo !MSBUILD_PATH! -exit /b 0 -:: function GetMSBuildPath end - -:: function Build begin -:Build -set "BUILD_TYPE=%~1" -set "LIB_OUT_PATH=%OUT_PATH%\lib\%ARCH%\%BUILD_TYPE%" -set "INCLUDE_OUT_PATH=%OUT_PATH%\include\winsparkle" - -echo Building %BUILD_TYPE% configuration... -"%MSBUILD_PATH%" "%SLN_FILE_PATH%" /p:Platform=%ARCH% /p:Configuration=%BUILD_TYPE% -if %ERRORLEVEL% neq 0 ( - echo Failed to build %BUILD_TYPE% configuration - exit /b 1 -) - -if not exist "%LIB_OUT_PATH%" mkdir "%LIB_OUT_PATH%" -if not exist "%INCLUDE_OUT_PATH%" mkdir "%INCLUDE_OUT_PATH%" - -copy /Y "%VENDOR_SOURCE_DIR%\%ARCH%\%BUILD_TYPE%\WinSparkle.lib" "%LIB_OUT_PATH%" -copy /Y "%VENDOR_SOURCE_DIR%\%ARCH%\%BUILD_TYPE%\WinSparkle.dll" "%LIB_OUT_PATH%" -copy /Y "%VENDOR_SOURCE_DIR%\include\*.h" "%INCLUDE_OUT_PATH%" - -echo Finished building %BUILD_TYPE% configuration -exit /b 0 -:: function Build end - -:: function main begin -:main -set "ROOT_DIR=%~dp0..\" -set "VENDOR_SOURCE_DIR=%ROOT_DIR%\third_party\winsparkle" -set "SLN_FILE_PATH=%VENDOR_SOURCE_DIR%\WinSparkle.sln" -set "ARCH=x64" -set "OUT_PATH=%ROOT_DIR%\third_party\out\winsparkle" - -call :GetMSBuildPath -if %ERRORLEVEL% neq 0 ( - echo Failed to get MSBuild path - exit /b 1 -) - -git submodule init -git submodule update - -"%MSBUILD_PATH%" "%SLN_FILE_PATH%" /p:RestorePackagesConfig=true /t:Restore - -:: Build Debug -call :Build Debug -if %ERRORLEVEL% neq 0 exit /b 1 - -:: Build Release -call :Build Release -if %ERRORLEVEL% neq 0 exit /b 1 - -exit /b 0 -:: function main end - -endlocal \ No newline at end of file From 26620d6b11d36aa194e59071fca0c3198d8bc92b Mon Sep 17 00:00:00 2001 From: markffan Date: Fri, 21 Mar 2025 16:12:51 +0800 Subject: [PATCH 13/36] Replace source code compilation with dynamic library integration for Sparkle --- DEPS | 7 - scripts/build_sparkle.sh | 28 - vendor.json | 9 - vendor/sparkle/.gitattributes | 5 + .../sparkle/mac/Sparkle.framework/Autoupdate | 1 + vendor/sparkle/mac/Sparkle.framework/Headers | 1 + vendor/sparkle/mac/Sparkle.framework/Modules | 1 + .../mac/Sparkle.framework/PrivateHeaders | 1 + .../sparkle/mac/Sparkle.framework/Resources | 1 + vendor/sparkle/mac/Sparkle.framework/Sparkle | 1 + .../Sparkle.framework/Versions/B/Autoupdate | 3 + .../Versions/B/Headers/SPUDownloadData.h | 52 + .../B/Headers/SPUStandardUpdaterController.h | 121 + .../B/Headers/SPUStandardUserDriver.h | 47 + .../B/Headers/SPUStandardUserDriverDelegate.h | 193 ++ .../Versions/B/Headers/SPUUpdateCheck.h | 33 + .../B/Headers/SPUUpdatePermissionRequest.h | 42 + .../Versions/B/Headers/SPUUpdater.h | 368 +++ .../Versions/B/Headers/SPUUpdaterDelegate.h | 476 ++++ .../Versions/B/Headers/SPUUpdaterSettings.h | 69 + .../Versions/B/Headers/SPUUserDriver.h | 300 +++ .../Versions/B/Headers/SPUUserUpdateState.h | 85 + .../Versions/B/Headers/SUAppcast.h | 45 + .../Versions/B/Headers/SUAppcastItem.h | 406 +++ .../Versions/B/Headers/SUErrors.h | 107 + .../Versions/B/Headers/SUExport.h | 18 + .../B/Headers/SUStandardVersionComparator.h | 63 + .../B/Headers/SUUpdatePermissionResponse.h | 73 + .../Versions/B/Headers/SUUpdater.h | 212 ++ .../Versions/B/Headers/SUUpdaterDelegate.h | 363 +++ .../B/Headers/SUVersionComparisonProtocol.h | 42 + .../B/Headers/SUVersionDisplayProtocol.h | 78 + .../Versions/B/Headers/Sparkle.h | 39 + .../Versions/B/Modules/module.modulemap | 6 + .../SPUAppcastItemStateResolver.h | 38 + .../SPUGentleUserDriverReminders.h | 22 + .../B/PrivateHeaders/SPUInstallationType.h | 19 + .../SPUStandardUserDriver+Private.h | 40 + .../B/PrivateHeaders/SPUUserAgent+Private.h | 29 + .../B/PrivateHeaders/SUAppcastItem+Private.h | 40 + .../SUInstallerLauncher+Private.h | 37 + .../B/Resources/Base.lproj/SUUpdateAlert.nib | Bin 0 -> 17730 bytes .../keyedobjects-101300.nib | Bin 0 -> 25696 bytes .../keyedobjects-110000.nib | Bin 0 -> 25673 bytes .../B/Resources/Base.lproj/Sparkle.strings | Bin 0 -> 20046 bytes .../Versions/B/Resources/Info.plist | 48 + .../B/Resources/ReleaseNotesColorStyle.css | 13 + .../Versions/B/Resources/SUStatus.nib | Bin 0 -> 12654 bytes .../Resources/ar.lproj/SUUpdateAlert.strings | 17 + .../ar.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/ar.lproj/Sparkle.strings | Bin 0 -> 5104 bytes .../Resources/ca.lproj/SUUpdateAlert.strings | 17 + .../B/Resources/ca.lproj/Sparkle.strings | Bin 0 -> 5966 bytes .../Resources/cs.lproj/SUUpdateAlert.strings | 17 + .../cs.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/cs.lproj/Sparkle.strings | Bin 0 -> 17266 bytes .../Resources/da.lproj/SUUpdateAlert.strings | 17 + .../da.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/da.lproj/Sparkle.strings | Bin 0 -> 8334 bytes .../Resources/de.lproj/SUUpdateAlert.strings | 17 + .../de.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/de.lproj/Sparkle.strings | Bin 0 -> 21514 bytes .../Resources/el.lproj/SUUpdateAlert.strings | 17 + .../el.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/el.lproj/Sparkle.strings | Bin 0 -> 7972 bytes .../Resources/en.lproj/SUUpdateAlert.strings | 18 + .../en.lproj/SUUpdatePermissionPrompt.strings | 27 + .../Resources/es.lproj/SUUpdateAlert.strings | 17 + .../es.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/es.lproj/Sparkle.strings | Bin 0 -> 10262 bytes .../B/Resources/fa.lproj/Sparkle.strings | Bin 0 -> 10492 bytes .../Resources/fi.lproj/SUUpdateAlert.strings | 17 + .../fi.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/fi.lproj/Sparkle.strings | Bin 0 -> 4602 bytes .../Resources/fr.lproj/SUUpdateAlert.strings | 17 + .../fr.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/fr.lproj/Sparkle.strings | Bin 0 -> 11366 bytes .../Resources/he.lproj/SUUpdateAlert.strings | 17 + .../he.lproj/SUUpdatePermissionPrompt.strings | 27 + .../B/Resources/he.lproj/Sparkle.strings | Bin 0 -> 17530 bytes .../Resources/hr.lproj/SUUpdateAlert.strings | 17 + .../hr.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/hr.lproj/Sparkle.strings | Bin 0 -> 9832 bytes .../Resources/hu.lproj/SUUpdateAlert.strings | 17 + .../hu.lproj/SUUpdatePermissionPrompt.strings | 20 + .../B/Resources/hu.lproj/Sparkle.strings | Bin 0 -> 6580 bytes .../Resources/is.lproj/SUUpdateAlert.strings | 17 + .../is.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/is.lproj/Sparkle.strings | Bin 0 -> 4806 bytes .../Resources/it.lproj/SUUpdateAlert.strings | 17 + .../it.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/it.lproj/Sparkle.strings | Bin 0 -> 18028 bytes .../Resources/ja.lproj/SUUpdateAlert.strings | 17 + .../ja.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/ja.lproj/Sparkle.strings | Bin 0 -> 17294 bytes .../Resources/ko.lproj/SUUpdateAlert.strings | 17 + .../ko.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/ko.lproj/Sparkle.strings | Bin 0 -> 14468 bytes .../Resources/nb.lproj/SUUpdateAlert.strings | 18 + .../nb.lproj/SUUpdatePermissionPrompt.strings | 24 + .../B/Resources/nb.lproj/Sparkle.strings | Bin 0 -> 8996 bytes .../Resources/nl.lproj/SUUpdateAlert.strings | 17 + .../nl.lproj/SUUpdatePermissionPrompt.strings | 17 + .../B/Resources/nl.lproj/Sparkle.strings | Bin 0 -> 20920 bytes .../Resources/nn.lproj/SUUpdateAlert.strings | 18 + .../nn.lproj/SUUpdatePermissionPrompt.strings | 24 + .../B/Resources/nn.lproj/Sparkle.strings | Bin 0 -> 9070 bytes .../Resources/pl.lproj/SUUpdateAlert.strings | 17 + .../pl.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/pl.lproj/Sparkle.strings | Bin 0 -> 8408 bytes .../pt-BR.lproj/SUUpdateAlert.strings | 17 + .../SUUpdatePermissionPrompt.strings | 26 + .../B/Resources/pt-BR.lproj/Sparkle.strings | Bin 0 -> 16756 bytes .../pt-PT.lproj/SUUpdateAlert.strings | 17 + .../SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/pt-PT.lproj/Sparkle.strings | Bin 0 -> 8210 bytes .../Resources/ro.lproj/SUUpdateAlert.strings | 17 + .../ro.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/ro.lproj/Sparkle.strings | Bin 0 -> 9418 bytes .../Resources/ru.lproj/SUUpdateAlert.strings | 17 + .../ru.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/ru.lproj/Sparkle.strings | Bin 0 -> 7964 bytes .../Resources/sk.lproj/SUUpdateAlert.strings | 17 + .../sk.lproj/SUUpdatePermissionPrompt.strings | 20 + .../B/Resources/sk.lproj/Sparkle.strings | Bin 0 -> 7940 bytes .../Resources/sl.lproj/SUUpdateAlert.strings | 17 + .../sl.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/sl.lproj/Sparkle.strings | Bin 0 -> 8180 bytes .../Resources/sv.lproj/SUUpdateAlert.strings | 17 + .../sv.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/sv.lproj/Sparkle.strings | Bin 0 -> 7588 bytes .../Resources/th.lproj/SUUpdateAlert.strings | 17 + .../th.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/th.lproj/Sparkle.strings | Bin 0 -> 7902 bytes .../Resources/tr.lproj/SUUpdateAlert.strings | 17 + .../tr.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/tr.lproj/Sparkle.strings | Bin 0 -> 8620 bytes .../Resources/uk.lproj/SUUpdateAlert.strings | 17 + .../uk.lproj/SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/uk.lproj/Sparkle.strings | Bin 0 -> 8100 bytes .../zh_CN.lproj/SUUpdateAlert.strings | 17 + .../SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/zh_CN.lproj/Sparkle.strings | Bin 0 -> 7720 bytes .../zh_HK.lproj/SUUpdateAlert.strings | 17 + .../SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/zh_HK.lproj/Sparkle.strings | Bin 0 -> 13272 bytes .../zh_TW.lproj/SUUpdateAlert.strings | 17 + .../SUUpdatePermissionPrompt.strings | 23 + .../B/Resources/zh_TW.lproj/Sparkle.strings | Bin 0 -> 13422 bytes .../mac/Sparkle.framework/Versions/B/Sparkle | 3 + .../Downloader.xpc/Contents/Info.plist | 62 + .../Downloader.xpc/Contents/MacOS/Downloader | 3 + .../Contents/_CodeSignature/CodeResources | 115 + .../Installer.xpc/Contents/Info.plist | 57 + .../Installer.xpc/Contents/MacOS/Installer | 3 + .../Contents/_CodeSignature/CodeResources | 115 + .../Versions/B/_CodeSignature/CodeResources | 2258 +++++++++++++++++ .../mac/Sparkle.framework/Versions/Current | 1 + .../sparkle/mac/Sparkle.framework/XPCServices | 1 + viewer/CMakeLists.txt | 3 +- viewer/images/update-red.png | 4 +- 161 files changed, 7500 insertions(+), 48 deletions(-) delete mode 100755 scripts/build_sparkle.sh create mode 100644 vendor/sparkle/.gitattributes create mode 120000 vendor/sparkle/mac/Sparkle.framework/Autoupdate create mode 120000 vendor/sparkle/mac/Sparkle.framework/Headers create mode 120000 vendor/sparkle/mac/Sparkle.framework/Modules create mode 120000 vendor/sparkle/mac/Sparkle.framework/PrivateHeaders create mode 120000 vendor/sparkle/mac/Sparkle.framework/Resources create mode 120000 vendor/sparkle/mac/Sparkle.framework/Sparkle create mode 100755 vendor/sparkle/mac/Sparkle.framework/Versions/B/Autoupdate create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUDownloadData.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUpdaterController.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUserDriver.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUserDriverDelegate.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdateCheck.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdatePermissionRequest.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdater.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdaterDelegate.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdaterSettings.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUserDriver.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUserUpdateState.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUAppcast.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUAppcastItem.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUErrors.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUExport.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUStandardVersionComparator.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdatePermissionResponse.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdater.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdaterDelegate.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUVersionComparisonProtocol.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUVersionDisplayProtocol.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/Sparkle.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Modules/module.modulemap create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUAppcastItemStateResolver.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUGentleUserDriverReminders.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUInstallationType.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUStandardUserDriver+Private.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUUserAgent+Private.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SUAppcastItem+Private.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SUInstallerLauncher+Private.h create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/SUUpdateAlert.nib create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-101300.nib create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-110000.nib create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Info.plist create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ReleaseNotesColorStyle.css create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/SUStatus.nib create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ca.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ca.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/en.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/en.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fa.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/Sparkle.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/SUUpdateAlert.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.strings create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/Sparkle.strings create mode 100755 vendor/sparkle/mac/Sparkle.framework/Versions/B/Sparkle create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/Info.plist create mode 100755 vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/MacOS/Downloader create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/_CodeSignature/CodeResources create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/Info.plist create mode 100755 vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/MacOS/Installer create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/_CodeSignature/CodeResources create mode 100644 vendor/sparkle/mac/Sparkle.framework/Versions/B/_CodeSignature/CodeResources create mode 120000 vendor/sparkle/mac/Sparkle.framework/Versions/Current create mode 120000 vendor/sparkle/mac/Sparkle.framework/XPCServices diff --git a/DEPS b/DEPS index 9906f30134..bccc65b995 100644 --- a/DEPS +++ b/DEPS @@ -35,13 +35,6 @@ "commit": "5ff839680134437dbf4678f3d0c7b371d84f4964", "dir": "third_party/lz4" } - ], - "mac": [ - { - "url": "https://github.com/sparkle-project/Sparkle.git", - "commit": "e9726ff718cffb2d045d84ede9548e38ea48fa01", - "dir": "third_party/sparkle" - } ] }, "actions": { diff --git a/scripts/build_sparkle.sh b/scripts/build_sparkle.sh deleted file mode 100755 index afb3436040..0000000000 --- a/scripts/build_sparkle.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -ROOT_DIR=$(dirname $(cd "$(dirname "${0}")"; pwd)) -VENDOR_SOURCE_DIR=${ROOT_DIR}/third_party/sparkle -ORG_PWD=$(pwd) - -LIB_OUT_PATH="${ROOT_DIR}/third_party/out/sparkle/lib/" - -if [ -d "${LIB_OUT_PATH}" ]; then - echo "Sparkle path[${LIB_OUT_PATH}] is exist, skip build" - return -fi - -BUILD_DIR=${VENDOR_SOURCE_DIR}/build -if [ -d "${BUILD_DIR}" ]; -then - rm -fr ${BUILD_DIR} -fi -mkdir -p ${BUILD_DIR} - -cd ${VENDOR_SOURCE_DIR} -make release - -mkdir -p ${LIB_OUT_PATH} -cp -fRP ./build/Sparkle.*/Build/Products/Release/Sparkle.framework ${LIB_OUT_PATH}/ -rm -fr ${BUILD_DIR} - -cd ${ORG_PWD} diff --git a/vendor.json b/vendor.json index 2bb9377706..c2faa67f6a 100644 --- a/vendor.json +++ b/vendor.json @@ -49,15 +49,6 @@ "linux" ] } - }, - { - "name": "sparkle", - "scripts": { - "mac": { - "executor": "bash", - "file": "scripts/build_sparkle.sh" - } - } } ] } \ No newline at end of file diff --git a/vendor/sparkle/.gitattributes b/vendor/sparkle/.gitattributes new file mode 100644 index 0000000000..192abd0021 --- /dev/null +++ b/vendor/sparkle/.gitattributes @@ -0,0 +1,5 @@ +mac/Sparkle.framework/Versions/B/Autoupdate filter=lfs diff=lfs merge=lfs -text +mac/Sparkle.framework/Versions/B/Updater.app/Contents/MacOS/Updater filter=lfs diff=lfs merge=lfs -text +mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/MacOS/Downloader filter=lfs diff=lfs merge=lfs -text +mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/MacOS/Installer filter=lfs diff=lfs merge=lfs -text +mac/Sparkle.framework/Versions/B/Sparkle filter=lfs diff=lfs merge=lfs -text diff --git a/vendor/sparkle/mac/Sparkle.framework/Autoupdate b/vendor/sparkle/mac/Sparkle.framework/Autoupdate new file mode 120000 index 0000000000..1a4fc02ca6 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Autoupdate @@ -0,0 +1 @@ +Versions/Current/Autoupdate \ No newline at end of file diff --git a/vendor/sparkle/mac/Sparkle.framework/Headers b/vendor/sparkle/mac/Sparkle.framework/Headers new file mode 120000 index 0000000000..a177d2a6b9 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/vendor/sparkle/mac/Sparkle.framework/Modules b/vendor/sparkle/mac/Sparkle.framework/Modules new file mode 120000 index 0000000000..5736f3186e --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/vendor/sparkle/mac/Sparkle.framework/PrivateHeaders b/vendor/sparkle/mac/Sparkle.framework/PrivateHeaders new file mode 120000 index 0000000000..d8e5645269 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/PrivateHeaders @@ -0,0 +1 @@ +Versions/Current/PrivateHeaders \ No newline at end of file diff --git a/vendor/sparkle/mac/Sparkle.framework/Resources b/vendor/sparkle/mac/Sparkle.framework/Resources new file mode 120000 index 0000000000..953ee36f3b --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/vendor/sparkle/mac/Sparkle.framework/Sparkle b/vendor/sparkle/mac/Sparkle.framework/Sparkle new file mode 120000 index 0000000000..b2c52731ea --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Sparkle @@ -0,0 +1 @@ +Versions/Current/Sparkle \ No newline at end of file diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Autoupdate b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Autoupdate new file mode 100755 index 0000000000..dac8d69f3c --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Autoupdate @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6e45a3060eb22e168182bbdfb72c0b69275cbc568a96a4fb07bcbe5cbb36422 +size 661024 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUDownloadData.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUDownloadData.h new file mode 100644 index 0000000000..d33ab01127 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUDownloadData.h @@ -0,0 +1,52 @@ +// +// SPUDownloadData.h +// Sparkle +// +// Created by Mayur Pawashe on 8/10/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + * A class for containing downloaded data along with some information about it. + */ +SU_EXPORT @interface SPUDownloadData : NSObject + +/** + * The raw data that was downloaded. + */ +@property (nonatomic, readonly) NSData *data; + +/** + * The URL that was fetched from. + * + * This may be different from the URL in the request if there were redirects involved. + */ +@property (nonatomic, readonly, copy) NSURL *URL; + +/** + * The IANA charset encoding name if available. Eg: "utf-8" + */ +@property (nonatomic, readonly, nullable, copy) NSString *textEncodingName; + +/** + * The MIME type if available. Eg: "text/plain" + */ +@property (nonatomic, readonly, nullable, copy) NSString *MIMEType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUpdaterController.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUpdaterController.h new file mode 100644 index 0000000000..3851e67b36 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUpdaterController.h @@ -0,0 +1,121 @@ +// +// SPUStandardUpdaterController.h +// Sparkle +// +// Created by Mayur Pawashe on 2/28/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SPUUpdater; +@class SPUStandardUserDriver; +@class NSMenuItem; +@protocol SPUUserDriver, SPUUpdaterDelegate, SPUStandardUserDriverDelegate; + +/** + A controller class that instantiates a `SPUUpdater` and allows binding UI to its updater settings. + + This class can be instantiated in a nib or created programmatically using `-initWithUpdaterDelegate:userDriverDelegate:` or `-initWithStartingUpdater:updaterDelegate:userDriverDelegate:`. + + The controller's updater targets the application's main bundle and uses Sparkle's standard user interface. + Typically, this class is used by sticking it as a custom NSObject subclass in an Interface Builder nib (probably in MainMenu) but it works well programmatically too. + + The controller creates an `SPUUpdater` instance using a `SPUStandardUserDriver` and allows hooking up the check for updates action and handling menu item validation. + It also allows hooking up the updater's and user driver's delegates. + + If you need more control over what bundle you want to update, or you want to provide a custom user interface (via `SPUUserDriver`), please use `SPUUpdater` directly instead. + */ +SU_EXPORT @interface SPUStandardUpdaterController : NSObject +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-interface-ivars" + /** + * Interface builder outlet for the updater's delegate. + */ + IBOutlet __weak id updaterDelegate; + + /** + * Interface builder outlet for the user driver's delegate. + */ + IBOutlet __weak id userDriverDelegate; +#pragma clang diagnostic pop +} + +/** + Accessible property for the updater. Some properties on the updater can be binded via KVO + + When instantiated from a nib, don't perform update checks before the application has finished launching in a MainMenu nib (i.e applicationDidFinishLaunching:) or before the corresponding window/view controller has been loaded (i.e, windowDidLoad or viewDidLoad). The updater is not guaranteed to be started yet before these points. + */ +@property (nonatomic, readonly) SPUUpdater *updater; + +/** + Accessible property for the updater's user driver. + */ +@property (nonatomic, readonly) SPUStandardUserDriver *userDriver; + +/** + Create a new `SPUStandardUpdaterController` from a nib. + + You cannot call this initializer directly. You must instantiate a `SPUStandardUpdaterController` inside of a nib (typically the MainMenu nib) to use it. + + To create a `SPUStandardUpdaterController` programmatically, use `-initWithUpdaterDelegate:userDriverDelegate:` or `-initWithStartingUpdater:updaterDelegate:userDriverDelegate:` instead. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** + Create a new `SPUStandardUpdaterController` programmatically. + + The updater is started automatically. See `-startUpdater` for more information. + */ +- (instancetype)initWithUpdaterDelegate:(nullable id)updaterDelegate userDriverDelegate:(nullable id)userDriverDelegate; + +/** + Create a new `SPUStandardUpdaterController` programmatically allowing you to specify whether or not to start the updater immediately. + + You can specify whether or not you want to start the updater immediately. + If you do not start the updater, you must invoke `-startUpdater` at a later time to start it. + */ +- (instancetype)initWithStartingUpdater:(BOOL)startUpdater updaterDelegate:(nullable id)updaterDelegate userDriverDelegate:(nullable id)userDriverDelegate; + +/** + Starts the updater if it has not already been started. + + You should only call this method yourself if you opted out of starting the updater on initialization. + Hence, do not call this yourself if you are instantiating this controller from a nib. + + This invokes `-[SPUUpdater startUpdater:]`. If the application is misconfigured with Sparkle, an error is logged and an alert is shown to the user (after a few seconds) to contact the developer. + If you want more control over this behavior, you can create your own `SPUUpdater` instead of using `SPUStandardUpdaterController`. + */ +- (void)startUpdater; + +/** + Explicitly checks for updates and displays a progress dialog while doing so. + + This method is meant for a main menu item. + Connect any NSMenuItem to this action in Interface Builder or programmatically, + and Sparkle will check for updates and report back its findings verbosely when it is invoked. + + When the target/action of the menu item is set to this controller and this method, + this controller also handles enabling/disabling the menu item by checking + `-[SPUUpdater canCheckForUpdates]` + + This action checks updates by invoking `-[SPUUpdater checkForUpdates]` + */ +- (IBAction)checkForUpdates:(nullable id)sender; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUserDriver.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUserDriver.h new file mode 100644 index 0000000000..f8d5ef09ad --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUserDriver.h @@ -0,0 +1,47 @@ +// +// SPUStandardUserDriver.h +// Sparkle +// +// Created by Mayur Pawashe on 2/14/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SPUUserDriver.h" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@protocol SPUStandardUserDriverDelegate; + +/** + Sparkle's standard built-in user driver for updater interactions + */ +SU_EXPORT @interface SPUStandardUserDriver : NSObject + +/** + Initializes a Sparkle's standard user driver for user update interactions + + @param hostBundle The target bundle of the host that is being updated. + @param delegate The optional delegate to this user driver. + */ +- (instancetype)initWithHostBundle:(NSBundle *)hostBundle delegate:(nullable id)delegate; + +/** + Use initWithHostBundle:delegate: instead. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUserDriverDelegate.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUserDriverDelegate.h new file mode 100644 index 0000000000..7ed3bff042 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUStandardUserDriverDelegate.h @@ -0,0 +1,193 @@ +// +// SPUStandardUserDriverDelegate.h +// Sparkle +// +// Created by Mayur Pawashe on 3/3/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@protocol SUVersionDisplay; +@class SUAppcastItem; +@class SPUUserUpdateState; + +/** + A protocol for Sparkle's standard user driver's delegate + + This includes methods related to UI interactions + */ +SU_EXPORT @protocol SPUStandardUserDriverDelegate + +@optional + +/** + Called before showing a modal alert window, + to give the opportunity to hide attached windows that may get in the way. + */ +- (void)standardUserDriverWillShowModalAlert; + +/** + Called after showing a modal alert window, + to give the opportunity to hide attached windows that may get in the way. + */ +- (void)standardUserDriverDidShowModalAlert; + +/** + Returns an object that formats version numbers for display to the user. + If you don't implement this method or return @c nil, the standard version formatter will be used. + */ +- (_Nullable id )standardUserDriverRequestsVersionDisplayer; + +/** + Decides whether or not the standard user driver should provide an option to show full release notes to the user. + + When a user checks for new updates and no new update is found, Sparkle by default will offer to show the application's version history to the user + by providing a "Version History" button in the no new update available alert. + + If this delegate method is implemented to return `NO`, then Sparkle will not provide an option to show full release notes to the user. + + @param item The appcast item corresponding to the latest version available. + @return @c YES to allow Sparkle to show full release notes to the user, otherwise @c NO to disallow this. + */ +- (BOOL)standardUserDriverShouldShowVersionHistoryForAppcastItem:(SUAppcastItem *)item; + +/** + Handles showing the full release notes to the user. + + When a user checks for new updates and no new update is found, Sparkle will offer to show the application's version history to the user + by providing a "Version History" button in the no new update available alert. + + If this delegate method is not implemented, Sparkle will instead offer to open the + `fullReleaseNotesLink` (or `releaseNotesLink` if the former is unavailable) from the appcast's latest `item` in the user's web browser. + + If this delegate method is implemented, Sparkle will instead ask the delegate to show the full release notes to the user. + A delegate may want to implement this method if they want to show in-app or offline release notes. + + @param item The appcast item corresponding to the latest version available. + */ +- (void)standardUserDriverShowVersionHistoryForAppcastItem:(SUAppcastItem *)item; + +/** + Specifies whether or not the download, extraction, and installing status windows allows to be minimized. + + By default, the status window showing the current status of the update (download, extraction, ready to install) is allowed to be minimized + for regular application bundle updates. + + @return @c YES if the status window is allowed to be minimized (default behavior), otherwise @c NO. + */ +- (BOOL)standardUserDriverAllowsMinimizableStatusWindow; + +/** + Declares whether or not gentle scheduled update reminders are supported. + + The delegate may implement scheduled update reminders that are presented in a gentle manner by implementing one or both of: + `-standardUserDriverWillHandleShowingUpdate:forUpdate:state:` and `-standardUserDriverShouldHandleShowingScheduledUpdate:andInImmediateFocus:` + + Visit https://sparkle-project.org/documentation/gentle-reminders for more information and examples. + + @return @c YES if gentle scheduled update reminders are implemented by standard user driver delegate, otherwise @c NO (default). + */ +@property (nonatomic, readonly) BOOL supportsGentleScheduledUpdateReminders; + +/** + Specifies if the standard user driver should handle showing a new scheduled update, or if its delegate should handle showing the update instead. + + If you implement this method and return @c NO the delegate is then responsible for showing the update, + which must be implemented and done in `-standardUserDriverWillHandleShowingUpdate:forUpdate:state:` + The motivation for the delegate being responsible for showing updates is to override Sparkle's default behavior + and add gentle reminders for new updates. + + Returning @c YES is the default behavior and allows the standard user driver to handle showing the update. + + If the standard user driver handles showing the update, `immediateFocus` reflects whether or not it will show the update in immediate and utmost focus. + The standard user driver may choose to show the update in immediate and utmost focus when the app was launched recently + or the system has been idle for some time. + + If `immediateFocus` is @c NO the standard user driver may want to defer showing the update until the user comes back to the app. + For background running applications, when `immediateFocus` is @c NO the standard user driver will always want to show + the update alert immediately, but behind other running applications or behind the app's own windows if it's currently active. + + There should be no side effects made when implementing this method so you should just return @c YES or @c NO + You will also want to implement `-standardUserDriverWillHandleShowingUpdate:forUpdate:state:` for adding additional update reminders. + + This method is not called for user-initiated update checks. The standard user driver always handles those. + + Visit https://sparkle-project.org/documentation/gentle-reminders for more information and examples. + + @param update The update the standard user driver should show. + @param immediateFocus If @c immediateFocus is @c YES, then the standard user driver proposes to show the update in immediate and utmost focus. See discussion for more details. + + @return @c YES if the standard user should handle showing the scheduled update (default behavior), otherwise @c NO if the delegate handles showing it. + */ +- (BOOL)standardUserDriverShouldHandleShowingScheduledUpdate:(SUAppcastItem *)update andInImmediateFocus:(BOOL)immediateFocus; + +/** + Called before an update will be shown to the user. + + If the standard user driver handles showing the update, `handleShowingUpdate` will be `YES`. + Please see `-standardUserDriverShouldHandleShowingScheduledUpdate:andInImmediateFocus:` for how the standard user driver + may handle showing scheduled updates when `handleShowingUpdate` is `YES` and `state.userInitiated` is `NO`. + + If the delegate declared it handles showing the update by returning @c NO in `-standardUserDriverShouldHandleShowingScheduledUpdate:andInImmediateFocus:` + then the delegate should handle showing update reminders in this method, or at some later point. + In this case, `handleShowingUpdate` will be @c NO. + To bring the update alert in focus, you may call `-[SPUStandardUpdaterController checkForUpdates:]` or `-[SPUUpdater checkForUpdates]`. + You may want to show additional UI indicators in your application that will show this update in focus + and want to dismiss additional UI indicators in `-standardUserDriverWillFinishUpdateSession` or `-standardUserDriverDidReceiveUserAttentionForUpdate:` + + If `state.userInitiated` is @c YES then the standard user driver always handles showing the new update and `handleShowingUpdate` will be @c YES. + In this case, it may still be useful for the delegate to intercept this method right before a new update will be shown. + + This method is not called when bringing an update that has already been presented back in focus. + + Visit https://sparkle-project.org/documentation/gentle-reminders for more information and examples. + + @param handleShowingUpdate @c YES if the standard user driver handles showing the update, otherwise @c NO if the delegate handles showing the update. + @param update The update that will be shown. + @param state The user state of the update which includes if the update check was initiated by the user. + */ +- (void)standardUserDriverWillHandleShowingUpdate:(BOOL)handleShowingUpdate forUpdate:(SUAppcastItem *)update state:(SPUUserUpdateState *)state; + +/** + Called when a new update first receives attention from the user. + + This occurs either when the user first brings the update alert in utmost focus or when the user makes a choice to install an update or dismiss/skip it. + + This may be useful to intercept for dismissing custom attention-based UI indicators (e.g, user notifications) introduced when implementing + `-standardUserDriverWillHandleShowingUpdate:forUpdate:state:` + + For custom UI indicators that need to still be on screen after the user has started to install an update, please see `-standardUserDriverWillFinishUpdateSession`. + + @param update The new update that the user gave attention to. + */ +- (void)standardUserDriverDidReceiveUserAttentionForUpdate:(SUAppcastItem *)update; + +/** + Called before the standard user driver session will finish its current update session. + + This may occur after the user has dismissed / skipped a new update or after an update error has occurred. + For updaters updating external/other bundles, this may also be called after an update has been successfully installed. + + This may be useful to intercept for dismissing custom UI indicators introduced when implementing + `-standardUserDriverWillHandleShowingUpdate:forUpdate:state:` + + For UI indicators that need to be dismissed when the user has given attention to a new update alert, + please see `-standardUserDriverDidReceiveUserAttentionForUpdate:` + */ +- (void)standardUserDriverWillFinishUpdateSession; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdateCheck.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdateCheck.h new file mode 100644 index 0000000000..80a2001961 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdateCheck.h @@ -0,0 +1,33 @@ +// +// SPUUpdateCheck.h +// SPUUpdateCheck +// +// Created by Mayur Pawashe on 8/28/21. +// Copyright © 2021 Sparkle Project. All rights reserved. +// + +#ifndef SPUUpdateCheck_h +#define SPUUpdateCheck_h + +/** + Describes the type of update check being performed. + + Each update check corresponds to an update check method on `SPUUpdater`. + */ +typedef NS_ENUM(NSInteger, SPUUpdateCheck) +{ + /** + The user-initiated update check corresponding to `-[SPUUpdater checkForUpdates]`. + */ + SPUUpdateCheckUpdates = 0, + /** + The background scheduled update check corresponding to `-[SPUUpdater checkForUpdatesInBackground]`. + */ + SPUUpdateCheckUpdatesInBackground = 1, + /** + The informational probe update check corresponding to `-[SPUUpdater checkForUpdateInformation]`. + */ + SPUUpdateCheckUpdateInformation = 2 +}; + +#endif /* SPUUpdateCheck_h */ diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdatePermissionRequest.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdatePermissionRequest.h new file mode 100644 index 0000000000..c1a362c39b --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdatePermissionRequest.h @@ -0,0 +1,42 @@ +// +// SPUUpdatePermissionRequest.h +// Sparkle +// +// Created by Mayur Pawashe on 8/14/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + This class represents information needed to make a permission request for checking updates. + */ +SU_EXPORT @interface SPUUpdatePermissionRequest : NSObject + +/** + Initializes a new update permission request instance. + + @param systemProfile The system profile information. + */ +- (instancetype)initWithSystemProfile:(NSArray *> *)systemProfile; + +/** + A read-only property for the user's system profile. + */ +@property (nonatomic, readonly) NSArray *> *systemProfile; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdater.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdater.h new file mode 100644 index 0000000000..9f035d3392 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdater.h @@ -0,0 +1,368 @@ +// +// SPUUpdater.h +// Sparkle +// +// Created by Andy Matuschak on 1/4/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#import + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#import "SPUUserDriver.h" +#pragma clang diagnostic pop +#else +#import +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SUAppcastItem, SUAppcast; + +@protocol SPUUpdaterDelegate; + +/** + The main API in Sparkle for controlling the update mechanism. + + This class is used to configure the update parameters as well as manually and automatically schedule and control checks for updates. + + For convenience, you can create a standard or nib instantiable updater by using `SPUStandardUpdaterController`. + + Prefer to set initial properties in your bundle's Info.plist as described in [Customizing Sparkle](https://sparkle-project.org/documentation/customization/). + + Otherwise only if you need dynamic behavior for user settings should you set properties on the updater such as: + - `automaticallyChecksForUpdates` + - `updateCheckInterval` + - `automaticallyDownloadsUpdates` + - `feedURL` + + Please view the documentation on each of these properties for more detail if you are to configure them dynamically. + */ +SU_EXPORT @interface SPUUpdater : NSObject + +/** + Initializes a new `SPUUpdater` instance + + This creates an updater, but to start it and schedule update checks `-startUpdater:` needs to be invoked first. + + Related: See `SPUStandardUpdaterController` which wraps a `SPUUpdater` instance and is suitable for instantiating inside of nib files. + + @param hostBundle The bundle that should be targeted for updating. + @param applicationBundle The application bundle that should be waited for termination and relaunched (unless overridden). Usually this can be the same as hostBundle. This may differ when updating a plug-in or other non-application bundle. + @param userDriver The user driver that Sparkle uses for user update interaction. + @param delegate The delegate for `SPUUpdater`. + */ +- (instancetype)initWithHostBundle:(NSBundle *)hostBundle applicationBundle:(NSBundle *)applicationBundle userDriver:(id )userDriver delegate:(nullable id)delegate; + +/** + Use `-initWithHostBundle:applicationBundle:userDriver:delegate:` or `SPUStandardUpdaterController` standard adapter instead. + + If you want to drop an updater into a nib, use `SPUStandardUpdaterController`. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** + Starts the updater. + + This method first checks if Sparkle is configured properly. A valid feed URL should be set before this method is invoked. + + If the configuration is valid, an update cycle is started in the next main runloop cycle. + During this cycle, a permission prompt may be brought up (if needed) for checking if the user wants automatic update checking. + Otherwise if automatic update checks are enabled, a scheduled update alert may be brought up if enough time has elapsed since the last check. + See `automaticallyChecksForUpdates` for more information. + + After starting the updater and before the next runloop cycle, one of `-checkForUpdates`, `-checkForUpdatesInBackground`, or `-checkForUpdateInformation` can be invoked. + This may be useful if you want to check for updates immediately or without showing a potential permission prompt. + + If the updater cannot be started (i.e, due to a configuration issue in the application), you may want to fall back appropriately. + For example, the standard updater controller (`SPUStandardUpdaterController`) alerts the user that the app is misconfigured and to contact the developer. + + This must be called on the main thread. + + @param error The error that is populated if this method fails. Pass NULL if not interested in the error information. + @return YES if the updater started otherwise NO with a populated error + */ +- (BOOL)startUpdater:(NSError * __autoreleasing *)error; + +/** + Checks for updates, and displays progress while doing so if needed. + + This is meant for users initiating a new update check or checking the current update progress. + + If an update hasn't started, the user may be shown that a new check for updates is occurring. + If an update has already been downloaded or begun installing from a previous session, the user may be presented to install that update. + If the user is already being presented with an update, that update will be shown to the user in active focus. + + This will find updates that the user has previously opted into skipping. + + See `canCheckForUpdates` property which can determine when this method may be invoked. + */ +- (void)checkForUpdates; + +/** + Checks for updates, but does not show any UI unless an update is found. + + You usually do not need to call this method directly. If `automaticallyChecksForUpdates` is @c YES, + Sparkle calls this method automatically according to its update schedule using the `updateCheckInterval` + and the `lastUpdateCheckDate`. Therefore, you should typically only consider calling this method directly if you + opt out of automatic update checks. Calling this method when updating your own bundle is invalid if Sparkle is configured + to ask the user's permission to check for updates automatically and `automaticallyChecksForUpdates` is `NO`. + If you want to reset the updater's cycle after an updater setting change, see `resetUpdateCycle` or `resetUpdateCycleAfterShortDelay` instead. + + This is meant for programmatically initiating a check for updates in the background without the user initiating it. + This check will not show UI if no new updates are found. + + If a new update is found, the updater's user driver may handle showing it at an appropriate (but not necessarily immediate) time. + If you want control over when and how a new update is shown, please see https://sparkle-project.org/documentation/gentle-reminders/ + + Note if automated downloading/installing is turned on, either a new update may be downloaded in the background to be installed silently, + or an already downloaded update may be shown. + + This will not find updates that the user has opted into skipping. + + This method does not do anything if there is a `sessionInProgress`. + */ +- (void)checkForUpdatesInBackground; + +/** + Begins a "probing" check for updates which will not actually offer to + update to that version. + + However, the delegate methods + `-[SPUUpdaterDelegate updater:didFindValidUpdate:]` and + `-[SPUUpdaterDelegate updaterDidNotFindUpdate:]` will be called, + so you can use that information in your UI. + + `-[SPUUpdaterDelegate updater:didFinishUpdateCycleForUpdateCheck:error:]` will be called when + this probing check is completed. + + Updates that have been skipped by the user will not be found. + + This method does not do anything if there is a `sessionInProgress`. + */ +- (void)checkForUpdateInformation; + +/** + A property indicating whether or not updates can be checked by the user. + + An update check can be made by the user when an update session isn't in progress, or when an update or its progress is being shown to the user. + A user cannot check for updates when data (such as the feed or an update) is still being downloaded automatically in the background. + + This property is suitable to use for menu item validation for seeing if `-checkForUpdates` can be invoked. + + This property is also KVO-compliant. + + Note this property does not reflect whether or not an update session is in progress. Please see `sessionInProgress` property instead. + */ +@property (nonatomic, readonly) BOOL canCheckForUpdates; + +/** + A property indicating whether or not an update session is in progress. + + An update session is in progress when the appcast is being downloaded, an update is being downloaded, + an update is being shown, update permission is being requested, or the installer is being started. + + An active session is when Sparkle's fired scheduler is running. + + Note an update session may not be running even though Sparkle's installer (ran as a separate process) may be running, + or even though the update has been downloaded but the installation has been deferred. In both of these cases, a new update session + may be activated with the update resumed at a later point (automatically or manually). + + See also: + - `canCheckForUpdates` property which is more suited for menu item validation and deciding if the user can initiate update checks. + - `-[SPUUpdaterDelegate updater:didFinishUpdateCycleForUpdateCheck:error:]` which lets the updater delegate know when an update cycle and session finishes. + */ +@property (nonatomic, readonly) BOOL sessionInProgress; + +/** + A property indicating whether or not to check for updates automatically. + + By default, Sparkle asks users on second launch for permission if they want automatic update checks enabled + and sets this property based on their response. If `SUEnableAutomaticChecks` is set in the Info.plist, + this permission request is not performed however. + + Setting this property will persist in the host bundle's user defaults. + Hence developers shouldn't maintain an additional user default for this property. + Only set this property if the user wants to change the default via a user settings option. + Do not always set it on launch unless you want to ignore the user's preference. + For testing environments, you can disable update checks by passing `-SUEnableAutomaticChecks NO` + to your app's command line arguments instead of setting this property. + + The update schedule cycle will be reset in a short delay after the property's new value is set. + This is to allow reverting this property without kicking off a schedule change immediately + */ +@property (nonatomic) BOOL automaticallyChecksForUpdates; + +/** + A property indicating the current automatic update check interval in seconds. + + Prefer to set SUScheduledCheckInterval directly in your Info.plist for setting the initial value. + + Setting this property will persist in the host bundle's user defaults. + Hence developers shouldn't maintain an additional user default for this property. + Only set this property if the user wants to change the default via a user settings option. + Do not always set it on launch unless you want to ignore the user's preference. + + The update schedule cycle will be reset in a short delay after the property's new value is set. + This is to allow reverting this property without kicking off a schedule change immediately + */ +@property (nonatomic) NSTimeInterval updateCheckInterval; + +/** + A property indicating whether or not updates can be automatically downloaded in the background. + + By default, updates are not automatically downloaded. + + By default starting from Sparkle 2.4, users are provided an option to opt in to automatically downloading and installing updates when they are asked if they want automatic update checks enabled. + The default value for this option is based on what the developer sets `SUAutomaticallyUpdate` in their Info.plist. + This is not done if `SUEnableAutomaticChecks` is set in the Info.plist however. Please check `automaticallyChecksForUpdates` property for more details. + + Note that the developer can disallow automatic downloading of updates from being enabled (via `SUAllowsAutomaticUpdates` Info.plist key). + In this case, this property will return NO regardless of how this property is set. + + Prefer to set `SUAutomaticallyUpdate` directly in your Info.plist for setting the initial value. + + Setting this property will persist in the host bundle's user defaults. + Hence developers shouldn't maintain an additional user default for this property. + Only set this property if the user wants to change the default via a user settings option. + Do not always set it on launch unless you want to ignore the user's preference. + */ +@property (nonatomic) BOOL automaticallyDownloadsUpdates; + +/** + The URL of the appcast used to download update information. + + If the updater's delegate implements `-[SPUUpdaterDelegate feedURLStringForUpdater:]`, this will return that feed URL. + Otherwise if the feed URL has been set before using `-[SPUUpdater setFeedURL:]`, the feed URL returned will be retrieved from the host bundle's user defaults. + Otherwise the feed URL in the host bundle's Info.plist will be returned. + If no feed URL can be retrieved, returns nil. + + For setting a primary feed URL, please set the `SUFeedURL` property in your Info.plist. + For setting an alternative feed URL, please prefer `-[SPUUpdaterDelegate feedURLStringForUpdater:]` over `-setFeedURL:`. + Please see the documentation for `-setFeedURL:` for migrating away from that API. + + This property must be called on the main thread; calls from background threads will return nil. + */ +@property (nonatomic, readonly, nullable) NSURL *feedURL; + +/** + Set the URL of the appcast used to download update information. This method is deprecated. + + Setting this property will persist in the host bundle's user defaults. + To avoid this undesirable behavior, please consider implementing + `-[SPUUpdaterDelegate feedURLStringForUpdater:]` instead of using this method. + + Calling `-clearFeedURLFromUserDefaults` will remove any feed URL that has been set in the host bundle's user defaults. + Passing nil to this method can also do this, but using `-clearFeedURLFromUserDefaults` is preferred. + To migrate away from using this API, you must clear and remove any feed URLs set in the user defaults through this API. + + If you do not need to alternate between multiple feeds, set the SUFeedURL in your Info.plist instead of invoking this method. + + For beta updates, you may consider migrating to `-[SPUUpdaterDelegate allowedChannelsForUpdater:]` in the future. + + Updaters that update other developer's bundles should not call this method. + + This method must be called on the main thread; calls from background threads will have no effect. + */ +- (void)setFeedURL:(nullable NSURL *)feedURL __deprecated_msg("Please call -[SPUUpdater clearFeedURLFromUserDefaults] to migrate away from using this API and transition to either specifying the feed URL in your Info.plist, using channels in Sparkle 2, or using -[SPUUpdaterDelegate feedURLStringForUpdater:] to specify the dynamic feed URL at runtime"); + +/** + Clears any feed URL from the host bundle's user defaults that was set via `-setFeedURL:` + + You should call this method if you have used `-setFeedURL:` in the past and want to stop using that API. + Otherwise for compatibility Sparkle will prefer to use the feed URL that was set in the user defaults over the one that was specified in the host bundle's Info.plist, + which is often undesirable (except for testing purposes). + + If a feed URL is found stored in the host bundle's user defaults (from calling `-setFeedURL:`) before it gets cleared, + then that previously set URL is returned from this method. + + This method should be called as soon as possible, after your application finished launching or right after the updater has been started + if you manually manage starting the updater. + + Updaters that update other developer's bundles should not call this method. + + This method must be called on the main thread. + + @return A previously set feed URL in the host bundle's user defaults, if available, otherwise this returns `nil` + */ +- (nullable NSURL *)clearFeedURLFromUserDefaults; + +/** + The host bundle that is being updated. + */ +@property (nonatomic, readonly) NSBundle *hostBundle; + +/** + The user agent used when checking for updates. + + By default the user agent string returned is in the format: + `$(BundleDisplayName)/$(BundleDisplayVersion) Sparkle/$(SparkleDisplayVersion)` + + BundleDisplayVersion is derived from the main application's Info.plist's CFBundleShortVersionString. + + Note if Sparkle is being used to update another application, the bundle information retrieved is from the main application performing the updating. + + This default implementation can be overridden. + */ +@property (nonatomic, copy) NSString *userAgentString; + +/** + The HTTP headers used when checking for updates, downloading release notes, and downloading updates. + + The keys of this dictionary are HTTP header fields and values are corresponding values. + */ +@property (nonatomic, copy, nullable) NSDictionary *httpHeaders; + +/** + A property indicating whether or not the user's system profile information is sent when checking for updates. + + Setting this property will persist in the host bundle's user defaults. + */ +@property (nonatomic) BOOL sendsSystemProfile; + +/** + The date of the last update check or nil if no check has been performed yet. + + For testing purposes, the last update check is stored in the `SULastCheckTime` key in the host bundle's user defaults. + For example, `defaults delete my-bundle-id SULastCheckTime` can be invoked to clear the last update check time and test + if update checks are automatically scheduled. + */ +@property (nonatomic, readonly, copy, nullable) NSDate *lastUpdateCheckDate; + +/** + Appropriately re-schedules the update checking timer according to the current updater settings. + + This method should only be called in response to a user changing updater settings. This method may trigger a new update check to occur in the background if an updater setting such as the updater's feed or allowed channels has changed. + + If the `updateCheckInterval` or `automaticallyChecksForUpdates` properties are changed, this method is automatically invoked after a short delay using `-resetUpdateCycleAfterShortDelay`. In these cases, manually resetting the update cycle is not necessary. + + See also `-resetUpdateCycleAfterShortDelay` which gives the user a short delay before triggering a cycle reset. + */ +- (void)resetUpdateCycle; + +/** + Appropriately re-schedules the update checking timer according to the current updater settings after a short cancellable delay. + + This method calls `resetUpdateCycle` after a short delay to give the user a short amount of time to cancel changing an updater setting. + If this method is called again, any previous reset request that is still inflight will be cancelled. + + For example, if the user changes the `automaticallyChecksForUpdates` setting to `YES`, but quickly undoes their change then + no cycle reset will be done. + + If the `updateCheckInterval` or `automaticallyChecksForUpdates` properties are changed, this method is automatically invoked. In these cases, manually resetting the update cycle is not necessary. + */ +- (void)resetUpdateCycleAfterShortDelay; + +/** + The system profile information that is sent when checking for updates. + */ +@property (nonatomic, readonly, copy) NSArray *> *systemProfileArray; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdaterDelegate.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdaterDelegate.h new file mode 100644 index 0000000000..1b63750ef2 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdaterDelegate.h @@ -0,0 +1,476 @@ +// +// SPUUpdaterDelegate.h +// Sparkle +// +// Created by Mayur Pawashe on 8/12/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#import "SPUUpdateCheck.h" +#import "SPUUserUpdateState.h" +#pragma clang diagnostic pop +#else +#import +#import +#import +#endif + +@protocol SUVersionComparison; +@class SPUUpdater, SUAppcast, SUAppcastItem, SPUUserUpdateState; + +NS_ASSUME_NONNULL_BEGIN + +// ----------------------------------------------------------------------------- +// SUUpdater Notifications for events that might be interesting to more than just the delegate +// The updater will be the notification object +// ----------------------------------------------------------------------------- +SU_EXPORT extern NSString *const SUUpdaterDidFinishLoadingAppCastNotification; +SU_EXPORT extern NSString *const SUUpdaterDidFindValidUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterDidNotFindUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterWillRestartNotification; +#define SUUpdaterWillRelaunchApplicationNotification SUUpdaterWillRestartNotification; +#define SUUpdaterWillInstallUpdateNotification SUUpdaterWillRestartNotification; + +// Key for the SUAppcastItem object in the SUUpdaterDidFindValidUpdateNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastItemNotificationKey; +// Key for the SUAppcast object in the SUUpdaterDidFinishLoadingAppCastNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey; + +// ----------------------------------------------------------------------------- +// System Profile Keys +// ----------------------------------------------------------------------------- + +SU_EXPORT extern NSString *const SUSystemProfilerApplicationNameKey; +SU_EXPORT extern NSString *const SUSystemProfilerApplicationVersionKey; +SU_EXPORT extern NSString *const SUSystemProfilerCPU64bitKey; +SU_EXPORT extern NSString *const SUSystemProfilerCPUCountKey; +SU_EXPORT extern NSString *const SUSystemProfilerCPUFrequencyKey; +SU_EXPORT extern NSString *const SUSystemProfilerCPUTypeKey; +SU_EXPORT extern NSString *const SUSystemProfilerCPUSubtypeKey; +SU_EXPORT extern NSString *const SUSystemProfilerHardwareModelKey; +SU_EXPORT extern NSString *const SUSystemProfilerMemoryKey; +SU_EXPORT extern NSString *const SUSystemProfilerOperatingSystemVersionKey; +SU_EXPORT extern NSString *const SUSystemProfilerPreferredLanguageKey; + +// ----------------------------------------------------------------------------- +// SPUUpdater Delegate: +// ----------------------------------------------------------------------------- + +/** + Provides delegation methods to control the behavior of an `SPUUpdater` object. + */ +@protocol SPUUpdaterDelegate +@optional + +/** + Returns whether to allow Sparkle to check for updates. + + For example, this may be used to prevent Sparkle from interrupting a setup assistant. + Alternatively, you may want to consider starting the updater after eg: the setup assistant finishes. + + Note in Swift, this method returns Void and is marked with the throws keyword. If this method + doesn't throw an error, the updater may perform an update check. Otherwise if an error is thrown (we recommend using an NSError), + then the updater may not perform an update check. + + @param updater The updater instance. + @param updateCheck The type of update check that will be performed if the updater is allowed to check for updates. + @param error The populated error object if the updater may not perform a new update check. The @c NSLocalizedDescriptionKey user info key should be populated indicating a description of the error. + @return @c YES if the updater is allowed to check for updates, otherwise @c NO +*/ +- (BOOL)updater:(SPUUpdater *)updater mayPerformUpdateCheck:(SPUUpdateCheck)updateCheck error:(NSError * __autoreleasing *)error; + +/** + Returns the set of Sparkle channels the updater is allowed to find new updates from. + + An appcast item can specify a channel the update is posted to. Without specifying a channel, the appcast item is posted to the default channel. + For instance: + ``` + + 2.0 Beta 1 + beta + + ``` + + This example posts an update to the @c beta channel, so only updaters that are allowed to use the @c beta channel can find this update. + + If the @c element is not present, the update item is posted to the default channel and can be found by any updater. + + You can pick any name you'd like for the channel. The valid characters for channel names are letters, numbers, dashes, underscores, and periods. + + Note to use this feature, all app versions that your users may update from in your feed must use a version of Sparkle that supports this feature. + This feature was added in Sparkle 2. + + @return The set of channel names the updater is allowed to find new updates in. An empty set is the default behavior, + which means the updater will only look for updates in the default channel. + */ +- (NSSet *)allowedChannelsForUpdater:(SPUUpdater *)updater; + +/** + Returns a custom appcast URL used for checking for new updates. + + Override this to dynamically specify the feed URL. + + @param updater The updater instance. + @return An appcast feed URL to check for new updates in, or @c nil for the default behavior and if you don't want to be delegated this task. + */ +- (nullable NSString *)feedURLStringForUpdater:(SPUUpdater *)updater; + +/** + Returns additional parameters to append to the appcast URL's query string. + + This is potentially based on whether or not Sparkle will also be sending along the system profile. + + @param updater The updater instance. + @param sendingProfile Whether the system profile will also be sent. + + @return An array of dictionaries with keys: `key`, `value`, `displayKey`, `displayValue`, the latter two being specifically for display to the user. + */ +- (NSArray *> *)feedParametersForUpdater:(SPUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; + +/** + Returns whether Sparkle should prompt the user about checking for new updates automatically. + + Use this to override the default behavior. + + @param updater The updater instance. + @return @c YES if the updater should prompt for permission to check for new updates automatically, otherwise @c NO + */ +- (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SPUUpdater *)updater; + +/** + Returns an allowed list of system profile keys to be appended to the appcast URL's query string. + + By default all keys will be included. This method allows overriding which keys should only be allowed. + + @param updater The updater instance. + + @return An array of system profile keys to include in the appcast URL's query string. Elements must be one of the `SUSystemProfiler*Key` constants. Return @c nil for the default behavior and if you don't want to be delegated this task. + */ +- (nullable NSArray *)allowedSystemProfileKeysForUpdater:(SPUUpdater *)updater; + +/** + Called after Sparkle has downloaded the appcast from the remote server. + + Implement this if you want to do some special handling with the appcast once it finishes loading. + + @param updater The updater instance. + @param appcast The appcast that was downloaded from the remote server. + */ +- (void)updater:(SPUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; + +/** + Called when a new valid update is found by the update driver. + + @param updater The updater instance. + @param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SPUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item; + +/** + Called when a valid new update is not found. + + There are various reasons a new update is unavailable and can't be installed. + + The userInfo dictionary on the error is populated with three keys: + - `SPULatestAppcastItemFoundKey`: if available, this may provide the latest `SUAppcastItem` that was found. This will be @c nil if it's unavailable. + - `SPUNoUpdateFoundReasonKey`: This will provide the `SPUNoUpdateFoundReason`. + For example the reason could be because the latest version in the feed requires a newer OS version or could be because the user is already on the latest version. + - `SPUNoUpdateFoundUserInitiatedKey`: A boolean that indicates if a new update was not found when the user intitiated an update check manually. + + @param updater The updater instance. + @param error An error containing information on why a new valid update was not found + */ +- (void)updaterDidNotFindUpdate:(SPUUpdater *)updater error:(NSError *)error; + +/** + Called when a valid new update is not found. + + If more information is needed on why an update was not found, use `-[SPUUpdaterDelegate updaterDidNotFindUpdate:error:]` instead. + + @param updater The updater instance. + */ +- (void)updaterDidNotFindUpdate:(SPUUpdater *)updater; + +/** + Returns the item in the appcast corresponding to the update that should be installed. + + Please consider using or migrating to other supported features before adopting this method. + Specifically: + - If you want to filter out certain tagged updates (like beta updates), consider `-[SPUUpdaterDelegate allowedChannelsForUpdater:]` instead. + - If you want to treat certain updates as informational-only, consider supplying @c with a set of affected versions users are updating from. + + If you're using special logic or extensions in your appcast, implement this to use your own logic for finding a valid update, if any, in the given appcast. + + Do not base your logic by filtering out items with a minimum or maximum OS version or minimum autoupdate version + because Sparkle already has logic for determining whether or not those items should be filtered out. + + Also do not return a non-top level item from the appcast such as a delta item. Delta items will be ignored. + Sparkle picks the delta item from your selection if the appropriate one is available. + + This method will not be invoked with an appcast that has zero items. Pick the best item from the appcast. + If an item is available that has the same version as the application or bundle to update, do not pick an item that is worse than that version. + + This method may be called multiple times for different selections and filters. This method should be efficient. + + Return `+[SUAppcastItem emptyAppcastItem]` if no appcast item is valid. + + Return @c nil if you don't want to be delegated this task and want to let Sparkle handle picking the best valid update. + + @param appcast The appcast that was downloaded from the remote server. + @param updater The updater instance. + @return The best valid appcast item. + */ +- (nullable SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SPUUpdater *)updater; + +/** + Returns whether or not the updater should proceed with the new chosen update from the appcast. + + By default, the updater will always proceed with the best selected update found in an appcast. Override this to override this behavior. + + If you return @c NO and populate the @c error, the user is not shown this @c updateItem nor is the update downloaded or installed. + + Note in Swift, this method returns Void and is marked with the throws keyword. If this method doesn't throw an error, the updater will proceed with the update. + Otherwise if an error is thrown (we recommend using an NSError), then the will not proceed with the update. + + @param updater The updater instance. + @param updateItem The selected update item to proceed with. + @param updateCheck The type of update check that would be performed if proceeded. + @param error An error object that must be populated by the delegate if the updater should not proceed with the update. The @c NSLocalizedDescriptionKey user info key should be populated indicating a description of the error. + @return @c YES if the updater should proceed with @c updateItem, otherwise @c NO if the updater should not proceed with the update with an @c error populated. + */ +- (BOOL)updater:(SPUUpdater *)updater shouldProceedWithUpdate:(SUAppcastItem *)updateItem updateCheck:(SPUUpdateCheck)updateCheck error:(NSError * __autoreleasing *)error; + +/** + Called when a user makes a choice to install, dismiss, or skip an update. + + If the @c choice is `SPUUserUpdateChoiceDismiss` and @c state.stage is `SPUUserUpdateStageDownloaded` the downloaded update is kept + around until the next time Sparkle reminds the user of the update. + + If the @c choice is `SPUUserUpdateChoiceDismiss` and @c state.stage is `SPUUserUpdateStageInstalling` the update is still set to install on application termination. + + If the @c choice is `SPUUserUpdateChoiceSkip` the user will not be reminded in the future for this update unless they initiate an update check themselves. + + If @c updateItem.isInformationOnlyUpdate is @c YES the @c choice cannot be `SPUUserUpdateChoiceInstall`. + + @param updater The updater instance. + @param choice The choice (install, dismiss, or skip) the user made for this @c updateItem + @param updateItem The appcast item corresponding to the update that the user made a choice on. + @param state The current state for the update which includes if the update has already been downloaded or already installing. + */ +- (void)updater:(SPUUpdater *)updater userDidMakeChoice:(SPUUserUpdateChoice)choice forUpdate:(SUAppcastItem *)updateItem state:(SPUUserUpdateState *)state; + +/** + Returns whether the release notes (if available) should be downloaded after an update is found and shown. + + This is specifically for the @c element in the appcast item. + + @param updater The updater instance. + @param updateItem The update item to download and show release notes from. + + @return @c YES to download and show the release notes if available, otherwise @c NO. The default behavior is @c YES. + */ +- (BOOL)updater:(SPUUpdater *)updater shouldDownloadReleaseNotesForUpdate:(SUAppcastItem *)updateItem; + +/** + Called immediately before downloading the specified update. + + @param updater The updater instance. + @param item The appcast item corresponding to the update that is proposed to be downloaded. + @param request The mutable URL request that will be used to download the update. + */ +- (void)updater:(SPUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request; + +/** + Called immediately after successful download of the specified update. + + @param updater The SUUpdater instance. + @param item The appcast item corresponding to the update that has been downloaded. + */ +- (void)updater:(SPUUpdater *)updater didDownloadUpdate:(SUAppcastItem *)item; + +/** + Called after the specified update failed to download. + + @param updater The updater instance. + @param item The appcast item corresponding to the update that failed to download. + @param error The error generated by the failed download. + */ +- (void)updater:(SPUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error; + +/** + Called when the user cancels an update while it is being downloaded. + + @param updater The updater instance. + */ +- (void)userDidCancelDownload:(SPUUpdater *)updater; + +/** + Called immediately before extracting the specified downloaded update. + + @param updater The SUUpdater instance. + @param item The appcast item corresponding to the update that is proposed to be extracted. + */ +- (void)updater:(SPUUpdater *)updater willExtractUpdate:(SUAppcastItem *)item; + +/** + Called immediately after extracting the specified downloaded update. + + @param updater The SUUpdater instance. + @param item The appcast item corresponding to the update that has been extracted. + */ +- (void)updater:(SPUUpdater *)updater didExtractUpdate:(SUAppcastItem *)item; + +/** + Called immediately before installing the specified update. + + @param updater The updater instance. + @param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SPUUpdater *)updater willInstallUpdate:(SUAppcastItem *)item; + +/** + Returns whether the relaunch should be delayed in order to perform other tasks. + + This is not called if the user didn't relaunch on the previous update, + in that case it will immediately restart. + + This may also not be called if the application is not going to relaunch after it terminates. + + @param updater The updater instance. + @param item The appcast item corresponding to the update that is proposed to be installed. + @param installHandler The install handler that must be completed before continuing with the relaunch. + + @return @c YES to delay the relaunch until @c installHandler is invoked. + */ +- (BOOL)updater:(SPUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item untilInvokingBlock:(void (^)(void))installHandler; + +/** + Returns whether the application should be relaunched at all. + + Some apps **cannot** be relaunched under certain circumstances. + This method can be used to explicitly prevent a relaunch. + + @param updater The updater instance. + @return @c YES if the updater should be relaunched, otherwise @c NO if it shouldn't. + */ +- (BOOL)updaterShouldRelaunchApplication:(SPUUpdater *)updater; + +/** + Called immediately before relaunching. + + @param updater The updater instance. + */ +- (void)updaterWillRelaunchApplication:(SPUUpdater *)updater; + +/** + Returns an object that compares version numbers to determine their arithmetic relation to each other. + + This method allows you to provide a custom version comparator. + If you don't implement this method or return @c nil, + the standard version comparator will be used. + + Note that the standard version comparator may be used during installation for preventing a downgrade, + even if you provide a custom comparator here. + + @param updater The updater instance. + @return The custom version comparator or @c nil if you don't want to be delegated this task. + */ +- (nullable id)versionComparatorForUpdater:(SPUUpdater *)updater; + +/** + Called when a background update will be scheduled after a delay. + + Automatic update checks need to be enabled for this to trigger. + + @param delay The delay in seconds until the next scheduled update will occur. This is an approximation and may vary due to system state. + + @param updater The updater instance. + */ +- (void)updater:(SPUUpdater *)updater willScheduleUpdateCheckAfterDelay:(NSTimeInterval)delay; + +/** + Called when no update checks will be scheduled in the future. + + This may later change if automatic update checks become enabled. + + @param updater The updater instance. + */ +- (void)updaterWillNotScheduleUpdateCheck:(SPUUpdater *)updater; + +/** + Returns the decryption password (if any) which is used to extract the update archive DMG. + + Return @c nil if no password should be used. + + @param updater The updater instance. + @return The password used for decrypting the archive, or @c nil if no password should be used. + */ +- (nullable NSString *)decryptionPasswordForUpdater:(SPUUpdater *)updater; + +/** + Called when an update is scheduled to be silently installed on quit after downloading the update automatically. + + If the updater is given responsibility, it can later remind the user an update is available if they have not terminated the application for a long time. + + Also if the updater is given responsibility and the update item is marked critical, the new update will be presented to the user immediately after. + + Even if the @c immediateInstallHandler is not invoked, the installer will attempt to install the update on termination. + + @param updater The updater instance. + @param item The appcast item corresponding to the update that is proposed to be installed. + @param immediateInstallHandler The install handler for the delegate to immediately install the update. No UI interaction will be shown and the application will be relaunched after installation. This handler can only be used if @c YES is returned and the delegate handles installing the update. For Sparkle 2.3 onwards, this handler can be invoked multiple times in case the application cancels the termination request. + @return @c YES if the delegate will handle installing the update or @c NO if the updater should be given responsibility. + */ +- (BOOL)updater:(SPUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationBlock:(void (^)(void))immediateInstallHandler; + +/** + Called after the update driver aborts due to an error. + + The update driver runs when checking for updates. This delegate method is called an error occurs during this process. + + Some special possible values of `error.code` are: + + - `SUNoUpdateError`: No new update was found. + - `SUInstallationCanceledError`: The user canceled installing the update when requested for authorization. + + @param updater The updater instance. + @param error The error that caused the update driver to abort. + */ +- (void)updater:(SPUUpdater *)updater didAbortWithError:(NSError *)error; + +/** + Called after the update driver finishes. + + The update driver runs when checking for updates. This delegate method is called when that check is finished. + + An update may be scheduled to be installed during the update cycle, or no updates may be found, or an available update may be dismissed or skipped (which is the same as no error). + + If the @c error is @c nil, no error has occurred. + + Some special possible values of `error.code` are: + + - `SUNoUpdateError`: No new update was found. + - `SUInstallationCanceledError`: The user canceled installing the update when requested for authorization. + + @param updater The updater instance. + @param updateCheck The type of update check was performed. + @param error The error that caused the update driver to abort. This is @c nil if the update driver finished normally and there is no error. + */ +- (void)updater:(SPUUpdater *)updater didFinishUpdateCycleForUpdateCheck:(SPUUpdateCheck)updateCheck error:(nullable NSError *)error; + +/* Deprecated methods */ + +- (BOOL)updaterMayCheckForUpdates:(SPUUpdater *)updater __deprecated_msg("Please use -[SPUUpdaterDelegate updater:mayPerformUpdateCheck:error:] instead."); + +- (void)updater:(SPUUpdater *)updater userDidSkipThisVersion:(SUAppcastItem *)item __deprecated_msg("Please use -[SPUUpdaterDelegate updater:userDidMakeChoice:forUpdate:state:] instead."); + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdaterSettings.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdaterSettings.h new file mode 100644 index 0000000000..8b69e962c5 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUpdaterSettings.h @@ -0,0 +1,69 @@ +// +// SPUUpdaterSettings.h +// Sparkle +// +// Created by Mayur Pawashe on 3/27/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + This class can be used for reading certain updater settings. + + It retrieves the settings by first looking into the host's user defaults. + If the setting is not found in there, then the host's Info.plist file is looked at. + */ +SU_EXPORT @interface SPUUpdaterSettings : NSObject + +- (instancetype)initWithHostBundle:(NSBundle *)hostBundle; + +/** + * Indicates whether or not automatic update checks are enabled. + */ +@property (readonly, nonatomic) BOOL automaticallyChecksForUpdates; + +/** + * The regular update check interval. + */ +@property (readonly, nonatomic) NSTimeInterval updateCheckInterval; + +/** + * Indicates whether or not automatically downloading updates is allowed to be turned on by the user. + * If this value is nil, the developer has not explicitly specified this option. + */ +@property (readonly, nonatomic, nullable) NSNumber *allowsAutomaticUpdatesOption; + +/** + * Indicates whether or not automatically downloading updates is allowed to be turned on by the user. + */ +@property (readonly, nonatomic) BOOL allowsAutomaticUpdates; + +/** + * Indicates whether or not automatically downloading updates is enabled by the user or developer. + * + * Note this does not indicate whether or not automatic downloading of updates is allowable. + * See `-allowsAutomaticUpdates` property for that. + */ +@property (readonly, nonatomic) BOOL automaticallyDownloadsUpdates; + +/** + * Indicates whether or not anonymous system profile information is sent when checking for updates. + */ +@property (readonly, nonatomic) BOOL sendsSystemProfile; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUserDriver.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUserDriver.h new file mode 100644 index 0000000000..3d90e44ad2 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUserDriver.h @@ -0,0 +1,300 @@ +// +// SPUUserDriver.h +// Sparkle +// +// Created by Mayur Pawashe on 2/14/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SPUUserUpdateState.h" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SPUUpdatePermissionRequest, SUUpdatePermissionResponse, SUAppcastItem, SPUDownloadData; + +/** + The API in Sparkle for controlling the user interaction. + + This protocol is used for implementing a user interface for the Sparkle updater. Sparkle's internal drivers tell + an object that implements this protocol what actions to take and show to the user. + + Every method in this protocol can be assumed to be called from the main thread. + */ +SU_EXPORT @protocol SPUUserDriver + +/** + * Show an updater permission request to the user + * + * Ask the user for their permission regarding update checks. + * This is typically only called once per app installation. + * + * @param request The update permission request. + * @param reply A reply with a update permission response. + */ +- (void)showUpdatePermissionRequest:(SPUUpdatePermissionRequest *)request reply:(void (^)(SUUpdatePermissionResponse *))reply; + +/** + * Show the user initiating an update check + * + * Respond to the user initiating an update check. Sparkle uses this to show the user a window with an indeterminate progress bar. + * + * @param cancellation Invoke this cancellation block to cancel the update check before the update check is completed. + */ +- (void)showUserInitiatedUpdateCheckWithCancellation:(void (^)(void))cancellation; + +/** + * Show the user a new update is found. + * + * Let the user know a new update is found and ask them what they want to do. + * Before this point, `-showUserInitiatedUpdateCheckWithCancellation:` may be called. + * + * The potential `stage`s on the updater @c state are: + * + * `SPUUpdateStateNotDownloaded` - Update has not been downloaded yet. + * + * `SPUUpdateStateDownloaded` - Update has already been downloaded in the background automatically (via `SUAutomaticallyUpdate`) but not started installing yet. + * + * `SPUUpdateStateInstalling` - Update has been downloaded and already started installing. + * + * The `userInitiated` property on the @c state indicates if the update was initiated by the user or if it was automatically scheduled in the background. + * + * Additionally, these properties on the @c appcastItem are of importance: + * + * @c appcastItem.informationOnlyUpdate indicates if the update is only informational and should not be downloaded. You can direct the user to the infoURL property of the appcastItem in their web browser. Sometimes information only updates are used as a fallback in case a bad update is shipped, so you'll want to support this case. + * + * @c appcastItem.majorUpgrade indicates if the update is a major or paid upgrade. + * + * @c appcastItem.criticalUpdate indicates if the update is a critical update. + * + * A reply of `SPUUserUpdateChoiceInstall` begins or resumes downloading, extracting, or installing the update. + * If the state.stage is `SPUUserUpdateStateInstalling`, this may send a quit event to the application and relaunch it immediately (in this state, this behaves as a fast "install and Relaunch"). + * If the state.stage is `SPUUpdateStateNotDownloaded` or `SPUUpdateStateDownloaded` the user may be presented an authorization prompt to install the update after `-showDownloadDidStartExtractingUpdate` is called if authorization is required for installation. For example, this may occur if the update on disk is owned by a different user (e.g. root or admin for non-admin users), or if the update is a package install. + * Do not use a reply of `SPUUserUpdateChoiceInstall` if @c appcastItem.informationOnlyUpdate is YES. + * + * A reply of `SPUUserUpdateChoiceDismiss` dismisses the update for the time being. The user may be reminded of the update at a later point. + * If the state.stage is `SPUUserUpdateStateDownloaded`, the downloaded update is kept after dismissing until the next time an update is shown to the user. + * If the state.stage is `SPUUserUpdateStateInstalling`, the installing update is also preserved after dismissing. In this state however, the update will also still be installed after the application is terminated. + * + * A reply of `SPUUserUpdateChoiceSkip` skips this particular version and won't notify the user again, unless they initiate an update check themselves. + * If @c appcastItem.majorUpgrade is YES, the major update and any future minor updates to that major release are skipped, unless a future minor update specifies a `` requirement. + * If the state.stage is `SPUUpdateStateInstalling`, the installation is also canceled when the update is skipped. + * + * @param appcastItem The Appcast Item containing information that reflects the new update. + * @param state The current state of the user update. See above discussion for notable properties. + * @param reply The reply which indicates if the update should be installed, dismissed, or skipped. See above discussion for more details. + */ +- (void)showUpdateFoundWithAppcastItem:(SUAppcastItem *)appcastItem state:(SPUUserUpdateState *)state reply:(void (^)(SPUUserUpdateChoice))reply; + +/** + * Show the user the release notes for the new update + * + * Display the release notes to the user. This will be called after showing the new update. + * This is only applicable if the release notes are linked from the appcast, and are not directly embedded inside of the appcast file. + * That is, this may be invoked if the releaseNotesURL from the appcast item is non-nil. + * + * @param downloadData The data for the release notes that was downloaded from the new update's appcast. + */ +- (void)showUpdateReleaseNotesWithDownloadData:(SPUDownloadData *)downloadData; + +/** + * Show the user that the new update's release notes could not be downloaded + * + * This will be called after showing the new update. + * This is only applicable if the release notes are linked from the appcast, and are not directly embedded inside of the appcast file. + * That is, this may be invoked if the releaseNotesURL from the appcast item is non-nil. + * + * @param error The error associated with why the new update's release notes could not be downloaded. + */ +- (void)showUpdateReleaseNotesFailedToDownloadWithError:(NSError *)error; + +/** + * Show the user a new update was not found + * + * Let the user know a new update was not found after they tried initiating an update check. + * Before this point, `-showUserInitiatedUpdateCheckWithCancellation:` may be called. + * + * There are various reasons a new update is unavailable and can't be installed. + * The @c error object is populated with recovery and suggestion strings suitable to be shown in an alert. + * + * The @c userInfo dictionary on the @c error is also populated with two keys: + * + * `SPULatestAppcastItemFoundKey`: if available, this may provide the latest SUAppcastItem that was found. + * + * `SPUNoUpdateFoundReasonKey`: if available, this will provide the `SUNoUpdateFoundReason`. For example the reason could be because + * the latest version in the feed requires a newer OS version or could be because the user is already on the latest version. + * + * @param error The error associated with why a new update was not found. See above discussion for more details. + * @param acknowledgement Acknowledge to the updater that no update found error was shown. + */ +- (void)showUpdateNotFoundWithError:(NSError *)error acknowledgement:(void (^)(void))acknowledgement; + +/** + * Show the user an update error occurred + * + * Let the user know that the updater failed with an error. This will not be invoked without the user having been + * aware that an update was in progress. + * + * Before this point, any of the non-error user driver methods may have been invoked. + * + * @param error The error associated with what update error occurred. + * @param acknowledgement Acknowledge to the updater that the error was shown. + */ +- (void)showUpdaterError:(NSError *)error acknowledgement:(void (^)(void))acknowledgement; + +/** + * Show the user that downloading the new update initiated + * + * Let the user know that downloading the new update started. + * + * @param cancellation Invoke this cancellation block to cancel the download at any point before `-showDownloadDidStartExtractingUpdate` is invoked. + */ +- (void)showDownloadInitiatedWithCancellation:(void (^)(void))cancellation; + +/** + * Show the user the content length of the new update that will be downloaded + * + * @param expectedContentLength The expected content length of the new update being downloaded. + * An implementor should be able to handle if this value is invalid (more or less than actual content length downloaded). + * Additionally, this method may be called more than once for the same download in rare scenarios. + */ +- (void)showDownloadDidReceiveExpectedContentLength:(uint64_t)expectedContentLength; + +/** + * Show the user that the update download received more data + * + * This may be an appropriate time to advance a visible progress indicator of the download + * @param length The length of the data that was just downloaded + */ +- (void)showDownloadDidReceiveDataOfLength:(uint64_t)length; + +/** + * Show the user that the update finished downloading and started extracting + * + * Sparkle uses this to show an indeterminate progress bar. + * + * Before this point, `showDownloadDidReceiveDataOfLength:` or `showUpdateFoundWithAppcastItem:state:reply:` may be called. + * An update can potentially resume at this point after having been automatically downloaded in the background (without the user driver) before. + * + * After extraction starts, the user may be shown an authorization prompt to install the update if authorization is required for installation. + * For example, this may occur if the update on disk is owned by a different user (e.g. root or admin for non-admin users), or if the update is a package install. + */ +- (void)showDownloadDidStartExtractingUpdate; + +/** + * Show the user that the update is extracting with progress + * + * Let the user know how far along the update extraction is. + * + * Before this point, `-showDownloadDidStartExtractingUpdate` is called. + * + * @param progress The progress of the extraction from a 0.0 to 1.0 scale + */ +- (void)showExtractionReceivedProgress:(double)progress; + +/** + * Show the user that the update is ready to install & relaunch + * + * Let the user know that the update is ready to install and relaunch, and ask them whether they want to proceed. + * Note if the target application has already terminated, this method may not be invoked. + * + * A reply of `SPUUserUpdateChoiceInstall` installs the update the new update immediately. The application is relaunched only if it is still running by the time this reply is invoked. If the application terminates on its own, Sparkle will attempt to automatically install the update. + * + * A reply of `SPUUserUpdateChoiceDismiss` dismisses the update installation for the time being. Note the update may still be installed automatically after the application terminates. + * + * A reply of `SPUUserUpdateChoiceSkip` cancels the current update that has begun installing and dismisses the update. In this circumstance, the update is canceled but this update version is not skipped in the future. + * + * Before this point, `-showExtractionReceivedProgress:` or `-showUpdateFoundWithAppcastItem:state:reply:` may be called. + * + * @param reply The reply which indicates if the update should be installed, dismissed, or skipped. See above discussion for more details. + */ +- (void)showReadyToInstallAndRelaunch:(void (^)(SPUUserUpdateChoice))reply; + +/** + * Show the user that the update is installing + * + * Let the user know that the update is currently installing. + * + * Before this point, `-showReadyToInstallAndRelaunch:` or `-showUpdateFoundWithAppcastItem:state:reply:` will be called. + * + * @param applicationTerminated Indicates if the application has been terminated already. + * If the application hasn't been terminated, a quit event is sent to the running application before installing the update. + * If the application or user delays or cancels termination, there may be an indefinite period of time before the application fully quits. + * It is up to the implementor whether or not to decide to continue showing installation progress in this case. + * + * @param retryTerminatingApplication This handler gives a chance for the application to re-try sending a quit event to the running application before installing the update. + * The application may cancel or delay termination. This handler gives the user driver another chance to allow the user to try terminating the application again. + * If the application does not delay or cancel application termination, there is no need to invoke this handler. This handler may be invoked multiple times. + * Note this handler should not be invoked if @c applicationTerminated is already @c YES + */ +- (void)showInstallingUpdateWithApplicationTerminated:(BOOL)applicationTerminated retryTerminatingApplication:(void (^)(void))retryTerminatingApplication; + +/** + * Show the user that the update installation finished + * + * Let the user know that the update finished installing. + * + * This will only be invoked if the updater process is still alive, which is typically not the case if + * the updater's lifetime is tied to the application it is updating. This implementation must not try to reference + * the old bundle prior to the installation, which will no longer be around. + * + * Before this point, `-showInstallingUpdateWithApplicationTerminated:retryTerminatingApplication:` will be called. + * + * @param relaunched Indicates if the update was relaunched. + * @param acknowledgement Acknowledge to the updater that the finished installation was shown. + */ +- (void)showUpdateInstalledAndRelaunched:(BOOL)relaunched acknowledgement:(void (^)(void))acknowledgement; + +/** + * Show the user the current presented update or its progress in utmost focus + * + * The user wishes to check for updates while the user is being shown update progress. + * Bring whatever is on screen to frontmost focus (permission request, update information, downloading or extraction status, choice to install update, etc). + */ +- (void)showUpdateInFocus; + +/** + * Dismiss the current update installation + * + * Stop and tear down everything. + * Dismiss all update windows, alerts, progress, etc from the user. + * Basically, stop everything that could have been started. Sparkle may invoke this when aborting or finishing an update. + */ +- (void)dismissUpdateInstallation; + +/* + * Below are deprecated methods that have been replaced by better alternatives. + * The deprecated methods will be used if the alternatives have not been implemented yet. + * In the future support for using these deprecated methods may be removed however. + */ +@optional + +// Clients should move to non-deprecated methods +// Deprecated methods are only (temporarily) kept around for compatibility reasons + +- (void)showUpdateNotFoundWithAcknowledgement:(void (^)(void))acknowledgement __deprecated_msg("Implement -showUpdateNotFoundWithError:acknowledgement: instead"); + +- (void)showUpdateInstallationDidFinishWithAcknowledgement:(void (^)(void))acknowledgement __deprecated_msg("Implement -showUpdateInstalledAndRelaunched:acknowledgement: instead"); + +- (void)dismissUserInitiatedUpdateCheck __deprecated_msg("Transition to new UI appropriately when a new update is shown, when no update is found, or when an update error occurs."); + +- (void)showInstallingUpdate __deprecated_msg("Implement -showInstallingUpdateWithApplicationTerminated:retryTerminatingApplication: instead."); + +- (void)showSendingTerminationSignal __deprecated_msg("Implement -showInstallingUpdateWithApplicationTerminated:retryTerminatingApplication: instead."); + +- (void)showInstallingUpdateWithApplicationTerminated:(BOOL)applicationTerminated __deprecated_msg("Implement -showInstallingUpdateWithApplicationTerminated:retryTerminatingApplication: instead.");; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUserUpdateState.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUserUpdateState.h new file mode 100644 index 0000000000..06725ccc68 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SPUUserUpdateState.h @@ -0,0 +1,85 @@ +// +// SPUUserUpdateState.h +// Sparkle +// +// Created by Mayur Pawashe on 2/29/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#ifndef SPUUserUpdateState_h +#define SPUUserUpdateState_h + +#import + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + A choice made by the user when prompted with a new update. + */ +typedef NS_ENUM(NSInteger, SPUUserUpdateChoice) { + /** + Dismisses the update and skips being notified of it in the future. + */ + SPUUserUpdateChoiceSkip, + /** + Downloads (if needed) and installs the update. + */ + SPUUserUpdateChoiceInstall, + /** + Dismisses the update until Sparkle reminds the user of it at a later time. + */ + SPUUserUpdateChoiceDismiss, +}; + +/** + Describes the current stage an update is undergoing. + */ +typedef NS_ENUM(NSInteger, SPUUserUpdateStage) { + /** + The update has not been downloaded. + */ + SPUUserUpdateStageNotDownloaded, + /** + The update has already been downloaded but not begun installing. + */ + SPUUserUpdateStageDownloaded, + /** + The update has already been downloaded and began installing in the background. + */ + SPUUserUpdateStageInstalling +}; + +/** + This represents the user's current update state. + */ +SU_EXPORT @interface SPUUserUpdateState : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + The current update stage. + + This stage indicates if data has been already downloaded or not, or if an update is currently being installed. + */ +@property (nonatomic, readonly) SPUUserUpdateStage stage; + +/** + Indicates whether or not the update check was initiated by the user. + */ +@property (nonatomic, readonly) BOOL userInitiated; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* SPUUserUpdateState_h */ diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUAppcast.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUAppcast.h new file mode 100644 index 0000000000..4f8e3cb160 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUAppcast.h @@ -0,0 +1,45 @@ +// +// SUAppcast.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCAST_H +#define SUAPPCAST_H + +#import +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SUAppcastItem; + +/** + The appcast representing a collection of `SUAppcastItem` items in the feed. + */ +SU_EXPORT @interface SUAppcast : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + The collection of update items. + + These `SUAppcastItem` items are in the same order as specified in the appcast XML feed and are thus not sorted by version. + */ +@property (readonly, nonatomic, copy) NSArray *items; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUAppcastItem.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUAppcastItem.h new file mode 100644 index 0000000000..e875e501c1 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUAppcastItem.h @@ -0,0 +1,406 @@ +// +// SUAppcastItem.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCASTITEM_H +#define SUAPPCASTITEM_H + +#import + +#ifdef BUILDING_SPARKLE_SOURCES_EXTERNALLY +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + The appcast item describing an update in the application's appcast feed. + + An appcast item represents a single update item in the `SUAppcast` contained within the @c element. + + Every appcast item must have a `versionString`, and either a `fileURL` or an `infoURL`. + All the remaining properties describing an update to the application are optional. + + Extended documentation and examples on using appcast item features are available at: + https://sparkle-project.org/documentation/publishing/ + */ +SU_EXPORT @interface SUAppcastItem : NSObject + +/** + The version of the update item. + + Sparkle uses this property to compare update items and determine the best available update item in the `SUAppcast`. + + This corresponds to the application update's @c CFBundleVersion + + This is extracted from the @c element, or the @c sparkle:version attribute from the @c element. + */ +@property (nonatomic, copy, readonly) NSString *versionString; + +/** + The human-readable display version of the update item if provided. + + This is the version string shown to the user when they are notified of a new update. + + This corresponds to the application update's @c CFBundleShortVersionString + + This is extracted from the @c element, or the @c sparkle:shortVersionString attribute from the @c element. + + If no short version string is available, this falls back to the update's `versionString`. + */ +@property (nonatomic, copy, readonly) NSString *displayVersionString; + +/** + The file URL to the update item if provided. + + This download contains the actual update Sparkle will attempt to install. + In cases where a download cannot be provided, an `infoURL` must be provided instead. + + A file URL should have an accompanying `contentLength` provided. + + This is extracted from the @c url attribute in the @c element. + */ +@property (nonatomic, readonly, nullable) NSURL *fileURL; + +/** + The content length of the download in bytes. + + This property is used as a fallback when the server doesn't report the content length of the download. + In that case, it is used to report progress of the downloading update to the user. + + A warning is outputted if this property is not equal the server's expected content length (if provided). + + This is extracted from the @c length attribute in the @c element. + It should be specified if a `fileURL` is provided. + */ +@property (nonatomic, readonly) uint64_t contentLength; + +/** + The info URL to the update item if provided. + + This informational link is used to direct the user to learn more about an update they cannot download/install directly from within the application. + The link should point to the product's web page. + + The informational link will be used if `informationOnlyUpdate` is @c YES + + This is extracted from the @c element. + */ +@property (nonatomic, readonly, nullable) NSURL *infoURL; + +/** + Indicates whether or not the update item is only informational and has no download. + + If `infoURL` is not present, this is @c NO + + If `fileURL` is not present, this is @c YES + + Otherwise this is determined based on the contents extracted from the @c element. + */ +@property (nonatomic, getter=isInformationOnlyUpdate, readonly) BOOL informationOnlyUpdate; + +/** + The title of the appcast item if provided. + + This is extracted from the @c element. + */ +@property (nonatomic, copy, readonly, nullable) NSString *title; + +/** + The date string of the appcast item if provided. + + The `date` property is constructed from this property and expects this string to comply with the following date format: + `E, dd MMM yyyy HH:mm:ss Z` + + This is extracted from the @c <pubDate> element. + */ +@property (nonatomic, copy, readonly, nullable) NSString *dateString; + +/** + The date constructed from the `dateString` property if provided. + + Sparkle by itself only uses this property for phased group rollouts specified via `phasedRolloutInterval`, but clients may query this property too. + + This date is constructed using the @c en_US locale. + */ +@property (nonatomic, copy, readonly, nullable) NSDate *date; + +/** + The release notes URL of the appcast item if provided. + + This external link points to an HTML file that Sparkle downloads and renders to show the user a new or old update item's changelog. + + An alternative to using an external release notes link is providing an embedded `itemDescription`. + + This is extracted from the @c <sparkle:releaseNotesLink> element. + */ +@property (nonatomic, readonly, nullable) NSURL *releaseNotesURL; + +/** + The description of the appcast item if provided. + + A description may be provided for inline/embedded release notes for new updates using @c <![CDATA[...]]> + This is an alternative to providing a `releaseNotesURL`. + + This is extracted from the @c <description> element. + */ +@property (nonatomic, copy, readonly, nullable) NSString *itemDescription; + +/** + The format of the `itemDescription` for inline/embedded release notes if provided. + + This may be: + - @c html + - @c plain-text + + This is extracted from the @c sparkle:descriptionFormat attribute in the @c <description> element. + + If the format is not provided in the @c <description> element of the appcast item, then this property may default to `html`. + + If the @c <description> element of the appcast item is not available, this property is `nil`. + */ +@property (nonatomic, readonly, nullable) NSString *itemDescriptionFormat; + +/** + The full release notes URL of the appcast item if provided. + + The link should point to the product's full changelog. + + Sparkle's standard user interface offers to show these full release notes when a user checks for a new update and no new update is available. + + This is extracted from the @c <sparkle:fullReleaseNotesLink> element. + */ +@property (nonatomic, readonly, nullable) NSURL *fullReleaseNotesURL; + +/** + The required minimum system operating version string for this update if provided. + + This version string should contain three period-separated components. + + Example: @c 10.13.0 + + Use `minimumOperatingSystemVersionIsOK` property to test if the current running system passes this requirement. + + This is extracted from the @c <sparkle:minimumSystemVersion> element. + */ +@property (nonatomic, copy, readonly, nullable) NSString *minimumSystemVersion; + +/** + Indicates whether or not the current running system passes the `minimumSystemVersion` requirement. + */ +@property (nonatomic, readonly) BOOL minimumOperatingSystemVersionIsOK; + +/** + The required maximum system operating version string for this update if provided. + + A maximum system operating version requirement should only be made in unusual scenarios. + + This version string should contain three period-separated components. + + Example: @c 10.14.0 + + Use `maximumOperatingSystemVersionIsOK` property to test if the current running system passes this requirement. + + This is extracted from the @c <sparkle:maximumSystemVersion> element. + */ +@property (nonatomic, copy, readonly, nullable) NSString *maximumSystemVersion; + +/** + Indicates whether or not the current running system passes the `maximumSystemVersion` requirement. + */ +@property (nonatomic, readonly) BOOL maximumOperatingSystemVersionIsOK; + +/** + The channel the update item is on if provided. + + An update item may specify a custom channel name (such as @c beta) that can only be found by updaters that filter for that channel. + If no channel is provided, the update item is assumed to be on the default channel. + + This is extracted from the @c <sparkle:channel> element. + Old applications must be using Sparkle 2 or later to interpret the channel element and to ignore unmatched channels. + */ +@property (nonatomic, readonly, nullable) NSString *channel; + +/** + The installation type of the update at `fileURL` + + This may be: + - @c application - indicates this is a regular application update. + - @c package - indicates this is a guided package installer update. + - @c interactive-package - indicates this is an interactive package installer update (deprecated; use "package" instead) + + This is extracted from the @c sparkle:installationType attribute in the @c <enclosure> element. + + If no installation type is provided in the enclosure, the installation type is inferred from the `fileURL` file extension instead. + + If the file extension is @c pkg or @c mpkg, the installation type is @c package otherwise it is @c application + + Hence, the installation type in the enclosure element only needs to be specified for package based updates distributed inside of a @c zip or other archive format. + + Old applications must be using Sparkle 1.26 or later to support downloading bare package updates (`pkg` or `mpkg`) that are not additionally archived inside of a @c zip or other archive format. + */ +@property (nonatomic, copy, readonly) NSString *installationType; + +/** + The phased rollout interval of the update item in seconds if provided. + + This is the interval between when different groups of users are notified of a new update. + + For this property to be used by Sparkle, the published `date` on the update item must be present as well. + + After each interval after the update item's `date`, a new group of users become eligible for being notified of the new update. + + This is extracted from the @c <sparkle:phasedRolloutInterval> element. + + Old applications must be using Sparkle 1.25 or later to support phased rollout intervals, otherwise they may assume updates are immediately available. + */ +@property (nonatomic, copy, readonly, nullable) NSNumber* phasedRolloutInterval; + +/** + The minimum bundle version string this update requires for automatically downloading and installing updates if provided. + + If an application's bundle version meets this version requirement, it can install the new update item in the background automatically. + + Otherwise if the requirement is not met, the user is always prompted to install the update. In this case, the update is assumed to be a `majorUpgrade`. + + If the update is a `majorUpgrade` and the update is skipped by the user, other future update alerts with the same `minimumAutoupdateVersion` will also be skipped automatically unless an update specifies `ignoreSkippedUpgradesBelowVersion`. + + This version string corresponds to the application's @c CFBundleVersion + + This is extracted from the @c <sparkle:minimumAutoupdateVersion> element. + */ +@property (nonatomic, copy, readonly, nullable) NSString *minimumAutoupdateVersion; + +/** + Indicates whether or not the update item is a major upgrade. + + An update is a major upgrade if the application's bundle version doesn't meet the `minimumAutoupdateVersion` requirement. + */ +@property (nonatomic, getter=isMajorUpgrade, readonly) BOOL majorUpgrade; + +/** + Previously skipped upgrades by the user will be ignored if they skipped an update whose version precedes this version. + + This can only be applied if the update is a `majorUpgrade`. + + This version string corresponds to the application's @c CFBundleVersion + + This is extracted from the @c <sparkle:ignoreSkippedUpgradesBelowVersion> element. + + Old applications must be using Sparkle 2.1 or later, otherwise this property will be ignored. + */ +@property (nonatomic, readonly, nullable) NSString *ignoreSkippedUpgradesBelowVersion; + +/** + Indicates whether or not the update item is critical. + + Critical updates are shown to the user more promptly. Sparkle's standard user interface also does not allow them to be skipped. + + This is determined and extracted from a top-level @c <sparkle:criticalUpdate> element or a @c sparkle:criticalUpdate element inside of a @c sparkle:tags element. + + Old applications must be using Sparkle 2 or later to support the top-level @c <sparkle:criticalUpdate> element. + */ +@property (nonatomic, getter=isCriticalUpdate, readonly) BOOL criticalUpdate; + +/** + Specifies the operating system the download update is available for if provided. + + If this property is not provided, then the supported operating system is assumed to be macOS. + + Known potential values for this string are @c macos and @c windows + + Sparkle on Mac ignores update items that are for other operating systems. + This is only useful for sharing appcasts between Sparkle on Mac and Sparkle on other operating systems. + + Use `macOsUpdate` property to test if this update item is for macOS. + + This is extracted from the @c sparkle:os attribute in the @c <enclosure> element. + */ +@property (nonatomic, copy, readonly, nullable) NSString *osString; + +/** + Indicates whether or not this update item is for macOS. + + This is determined from the `osString` property. + */ +@property (nonatomic, getter=isMacOsUpdate, readonly) BOOL macOsUpdate; + +/** + The delta updates for this update item. + + Sparkle uses these to download and apply a smaller update based on the version the user is updating from. + + The key is based on the @c sparkle:version of the update. + The value is an update item that will have `deltaUpdate` be @c YES + + Clients typically should not need to examine the contents of the delta updates. + + This is extracted from the @c <sparkle:deltas> element. + */ +@property (nonatomic, copy, readonly, nullable) NSDictionary<NSString *, SUAppcastItem *> *deltaUpdates; + +/** + The expected size of the Sparkle executable file before applying this delta update. + + This attribute is used to test if the delta item can still be applied. If Sparkle's executable file has changed (e.g. from having an architecture stripped), + then the delta item cannot be applied. + + This is extracted from the @c sparkle:deltaFromSparkleExecutableSize attribute from the @c <enclosure> element of a @c sparkle:deltas item. + This attribute is optional for delta update items. + */ +@property (nonatomic, nonatomic, readonly, nullable) NSNumber *deltaFromSparkleExecutableSize; + +/** + An expected set of Sparkle's locales present on disk before applying this delta update. + + This attribute is used to test if the delta item can still be applied. If Sparkle's list of locales present on disk (.lproj directories) do not contain any items from this set, + (e.g. from having localization files stripped) then the delta item cannot be applied. This set does not need to be a complete list of locales. Sparkle may even decide + to not process all them. 1-10 should be a decent amount. + + This is extracted from the @c sparkle:deltaFromSparkleLocales attribute from the @c <enclosure> element of a @c sparkle:deltas item. + The locales extracted from this attribute are delimited by a comma (e.g. "en,ca,fr,hr,hu"). This attribute is optional for delta update items. + */ +@property (nonatomic, nonatomic, readonly, nullable) NSSet<NSString *> *deltaFromSparkleLocales; + +/** + Indicates whether or not the update item is a delta update. + + An update item is a delta update if it is in the `deltaUpdates` of another update item. + */ +@property (nonatomic, getter=isDeltaUpdate, readonly) BOOL deltaUpdate; + +/** + The dictionary representing the entire appcast item. + + This is useful for querying custom extensions or elements from the appcast item. + */ +@property (nonatomic, readonly, copy) NSDictionary *propertiesDictionary; + +- (instancetype)init NS_UNAVAILABLE; + +/** + An empty appcast item. + + This may be used as a potential return value in `-[SPUUpdaterDelegate bestValidUpdateInAppcast:forUpdater:]` + */ ++ (instancetype)emptyAppcastItem; + +// Deprecated initializers +- (nullable instancetype)initWithDictionary:(NSDictionary *)dict __deprecated_msg("Properties that depend on the system or application version are not supported when used with this initializer. The designated initializer is available in SUAppcastItem+Private.h. Please first explore other APIs or contact us to describe your use case."); +- (nullable instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString * _Nullable __autoreleasing *_Nullable)error __deprecated_msg("Properties that depend on the system or application version are not supported when used with this initializer. The designated initializer is available in SUAppcastItem+Private.h. Please first explore other APIs or contact us to describe your use case."); +- (nullable instancetype)initWithDictionary:(NSDictionary *)dict relativeToURL:(NSURL * _Nullable)appcastURL failureReason:(NSString * _Nullable __autoreleasing *_Nullable)error __deprecated_msg("Properties that depend on the system or application version are not supported when used with this initializer. The designated initializer is available in SUAppcastItem+Private.h. Please first explore other APIs or contact us to describe your use case."); + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUErrors.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUErrors.h new file mode 100644 index 0000000000..2211c172e1 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUErrors.h @@ -0,0 +1,107 @@ +// +// SUErrors.h +// Sparkle +// +// Created by C.W. Betts on 10/13/14. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUERRORS_H +#define SUERRORS_H + +#import <Foundation/Foundation.h> + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +#endif + +/** + * Error domain used by Sparkle + */ +SU_EXPORT extern NSString *const SUSparkleErrorDomain; + +typedef NS_ENUM(OSStatus, SUError) { + // Configuration phase errors + SUNoPublicDSAFoundError = 0001, + SUInsufficientSigningError = 0002, + SUInsecureFeedURLError = 0003, + SUInvalidFeedURLError = 0004, + SUInvalidUpdaterError = 0005, + SUInvalidHostBundleIdentifierError = 0006, + SUInvalidHostVersionError = 0007, + + // Appcast phase errors. + SUAppcastParseError = 1000, + SUNoUpdateError = 1001, + SUAppcastError = 1002, + SURunningFromDiskImageError = 1003, + SUResumeAppcastError = 1004, + SURunningTranslocated = 1005, + SUWebKitTerminationError = 1006, + SUReleaseNotesError = 1007, + + // Download phase errors. + SUTemporaryDirectoryError = 2000, + SUDownloadError = 2001, + + // Extraction phase errors. + SUUnarchivingError = 3000, + SUSignatureError = 3001, + SUValidationError = 3002, + + // Installation phase errors. + SUFileCopyFailure = 4000, + SUAuthenticationFailure = 4001, + SUMissingUpdateError = 4002, + SUMissingInstallerToolError = 4003, + SURelaunchError = 4004, + SUInstallationError = 4005, + SUDowngradeError = 4006, + SUInstallationCanceledError = 4007, + SUInstallationAuthorizeLaterError = 4008, + SUNotValidUpdateError = 4009, + SUAgentInvalidationError = 4010, + SUInstallationRootInteractiveError = 4011, + SUInstallationWriteNoPermissionError = 4012, + + // API misuse errors. + SUIncorrectAPIUsageError = 5000 +}; + +/** + The reason why a new update is not available. + */ +typedef NS_ENUM(OSStatus, SPUNoUpdateFoundReason) { + /** + A new update is unavailable for an unknown reason. + */ + SPUNoUpdateFoundReasonUnknown, + /** + A new update is unavailable because the user is on the latest known version in the appcast feed. + */ + SPUNoUpdateFoundReasonOnLatestVersion, + /** + A new update is unavailable because the user is on a version newer than the latest known version in the appcast feed. + */ + SPUNoUpdateFoundReasonOnNewerThanLatestVersion, + /** + A new update is unavailable because the user's operating system version is too old for the update. + */ + SPUNoUpdateFoundReasonSystemIsTooOld, + /** + A new update is unavailable because the user's operating system version is too new for the update. + */ + SPUNoUpdateFoundReasonSystemIsTooNew +}; + +SU_EXPORT extern NSString *const SPUNoUpdateFoundReasonKey; +SU_EXPORT extern NSString *const SPULatestAppcastItemFoundKey; +SU_EXPORT extern NSString *const SPUNoUpdateFoundUserInitiatedKey; + +#endif diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUExport.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUExport.h new file mode 100644 index 0000000000..3e3f8a1646 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUExport.h @@ -0,0 +1,18 @@ +// +// SUExport.h +// Sparkle +// +// Created by Jake Petroules on 2014-08-23. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUEXPORT_H +#define SUEXPORT_H + +#ifdef BUILDING_SPARKLE +#define SU_EXPORT __attribute__((visibility("default"))) +#else +#define SU_EXPORT +#endif + +#endif diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUStandardVersionComparator.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUStandardVersionComparator.h new file mode 100644 index 0000000000..1316819aef --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUStandardVersionComparator.h @@ -0,0 +1,63 @@ +// +// SUStandardVersionComparator.h +// Sparkle +// +// Created by Andy Matuschak on 12/21/07. +// Copyright 2007 Andy Matuschak. All rights reserved. +// + +#ifndef SUSTANDARDVERSIONCOMPARATOR_H +#define SUSTANDARDVERSIONCOMPARATOR_H + +#import <Foundation/Foundation.h> + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#import "SUVersionComparisonProtocol.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +#import <Sparkle/SUVersionComparisonProtocol.h> +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + Sparkle's default version comparator. + + This comparator is adapted from MacPAD, by Kevin Ballard. + It's "dumb" in that it does essentially string comparison, + in components split by character type. +*/ +SU_EXPORT @interface SUStandardVersionComparator : NSObject <SUVersionComparison> + +/** + Initializes a new instance of the standard version comparator. +*/ +- (instancetype)init; + +/** + A singleton instance of the comparator. + */ +@property (nonatomic, class, readonly) SUStandardVersionComparator *defaultComparator; + +/** + Compares two version strings through textual analysis. + + These version strings should be in the format of x, x.y, or x.y.z where each component is a number. + For example, valid version strings include "1.5.3", "500", or "4000.1" + These versions that are compared correspond to the @c CFBundleVersion values of the updates. + + @param versionA The first version string to compare. + @param versionB The second version string to compare. + @return A comparison result between @c versionA and @c versionB +*/ +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; + +@end + +NS_ASSUME_NONNULL_END +#endif diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdatePermissionResponse.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdatePermissionResponse.h new file mode 100644 index 0000000000..d2a50ae0ca --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdatePermissionResponse.h @@ -0,0 +1,73 @@ +// +// SUUpdatePermissionResponse.h +// Sparkle +// +// Created by Mayur Pawashe on 2/8/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + This class represents a response for permission to check updates. +*/ +SU_EXPORT @interface SUUpdatePermissionResponse : NSObject<NSSecureCoding> + +/** + Initializes a new update permission response instance. + + @param automaticUpdateChecks Flag to enable automatic update checks. + @param sendSystemProfile Flag for if system profile information should be sent to the server hosting the appcast. + */ +- (instancetype)initWithAutomaticUpdateChecks:(BOOL)automaticUpdateChecks sendSystemProfile:(BOOL)sendSystemProfile; + +/** + Initializes a new update permission response instance. + + @param automaticUpdateChecks Flag to enable automatic update checks. + @param automaticUpdateDownloading Flag to enable automatic downloading and installing of updates. If this is nil, this option will be ignored. + @param sendSystemProfile Flag for if system profile information should be sent to the server hosting the appcast. + */ +- (instancetype)initWithAutomaticUpdateChecks:(BOOL)automaticUpdateChecks automaticUpdateDownloading:(NSNumber * _Nullable)automaticUpdateDownloading sendSystemProfile:(BOOL)sendSystemProfile; + +/* + Use -initWithAutomaticUpdateChecks:sendSystemProfile: instead. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** + A read-only property indicating if update checks should be done automatically. + */ +@property (nonatomic, readonly) BOOL automaticUpdateChecks; + +/** + A read-only property indicating if updates should be automatically downloaded and installed. + + If this property is `nil`, then no user choice was made for this option. + + If `automaticUpdateChecks` is `NO` then this property should not be `@(YES)`. + Set it to `NO` if the user was given the choice of automatically downloading and installing updates, + otherwise set it to `nil`. + */ +@property (nonatomic, readonly, nullable) NSNumber *automaticUpdateDownloading; + +/** + A read-only property indicating if system profile should be sent or not. + */ +@property (nonatomic, readonly) BOOL sendSystemProfile; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdater.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdater.h new file mode 100644 index 0000000000..6d2c771c30 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdater.h @@ -0,0 +1,212 @@ +// +// SUUpdater.h +// Sparkle +// +// Created by Andy Matuschak on 1/4/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUUPDATER_H +#define SUUPDATER_H + +#import <Foundation/Foundation.h> + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#import "SUVersionComparisonProtocol.h" +#import "SUVersionDisplayProtocol.h" +#import "SUUpdaterDelegate.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +#import <Sparkle/SUVersionComparisonProtocol.h> +#import <Sparkle/SUVersionDisplayProtocol.h> +#import <Sparkle/SUUpdaterDelegate.h> +#endif + +@class SUAppcastItem, SUAppcast, NSMenuItem; + +@protocol SUUpdaterDelegate; + +/** + The legacy API in Sparkle for controlling the update mechanism. + + This class is now deprecated and acts as a thin wrapper around `SPUUpdater` and `SPUStandardUserDriver`. + + If you are migrating to Sparkle 2, use `SPUStandardUpdaterController` instead, or `SPUUpdater` if you need more control. + */ +__deprecated_msg("Deprecated in Sparkle 2. Use SPUStandardUpdaterController instead, or SPUUpdater if you need more control.") +SU_EXPORT @interface SUUpdater : NSObject + +@property (unsafe_unretained, nonatomic) IBOutlet id<SUUpdaterDelegate> delegate; + +/*! + The shared updater for the main bundle. + + This is equivalent to passing [NSBundle mainBundle] to SUUpdater::updaterForBundle: + */ ++ (SUUpdater *)sharedUpdater; + +/*! + The shared updater for a specified bundle. + If an updater has already been initialized for the provided bundle, that shared instance will be returned. + */ ++ (SUUpdater *)updaterForBundle:(NSBundle *)bundle; + +/*! + Designated initializer for SUUpdater. + + If an updater has already been initialized for the provided bundle, that shared instance will be returned. + */ +- (instancetype)initForBundle:(NSBundle *)bundle; + +/*! + Explicitly checks for updates and displays a progress dialog while doing so. + + This method is meant for a main menu item. + Connect any menu item to this action in Interface Builder, + and Sparkle will check for updates and report back its findings verbosely + when it is invoked. + + This will find updates that the user has opted into skipping. + */ +- (IBAction)checkForUpdates:(id)sender; + +/*! + The menu item validation used for the -checkForUpdates: action + */ +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem; + +/*! + Checks for updates, but does not display any UI unless an update is found. + + This is meant for programmatically initiating a check for updates. That is, + it will display no UI unless it actually finds an update, in which case it + proceeds as usual. + + If automatic downloading of updates it turned on and allowed, however, + this will invoke that behavior, and if an update is found, it will be downloaded + in the background silently and will be prepped for installation. + + This will not find updates that the user has opted into skipping. + */ +- (void)checkForUpdatesInBackground; + +/*! + A property indicating whether or not to check for updates automatically. + + Setting this property will persist in the host bundle's user defaults. + The update schedule cycle will be reset in a short delay after the property's new value is set. + This is to allow reverting this property without kicking off a schedule change immediately + */ +@property (nonatomic) BOOL automaticallyChecksForUpdates; + +/*! + A property indicating whether or not updates can be automatically downloaded in the background. + + Note that automatic downloading of updates can be disallowed by the developer. + In this case, -automaticallyDownloadsUpdates will return NO regardless of how this property is set. + + Setting this property will persist in the host bundle's user defaults. + */ +@property (nonatomic) BOOL automaticallyDownloadsUpdates; + +/*! + A property indicating the current automatic update check interval. + + Setting this property will persist in the host bundle's user defaults. + The update schedule cycle will be reset in a short delay after the property's new value is set. + This is to allow reverting this property without kicking off a schedule change immediately + */ +@property (nonatomic) NSTimeInterval updateCheckInterval; + +/*! + Begins a "probing" check for updates which will not actually offer to + update to that version. + + However, the delegate methods + SUUpdaterDelegate::updater:didFindValidUpdate: and + SUUpdaterDelegate::updaterDidNotFindUpdate: will be called, + so you can use that information in your UI. + + Updates that have been skipped by the user will not be found. + */ +- (void)checkForUpdateInformation; + +/*! + The URL of the appcast used to download update information. + + Setting this property will persist in the host bundle's user defaults. + If you don't want persistence, you may want to consider instead implementing + SUUpdaterDelegate::feedURLStringForUpdater: or SUUpdaterDelegate::feedParametersForUpdater:sendingSystemProfile: + + This property must be called on the main thread. + */ +@property (nonatomic, copy) NSURL *feedURL; + +/*! + The host bundle that is being updated. + */ +@property (readonly, nonatomic) NSBundle *hostBundle; + +/*! + The bundle this class (SUUpdater) is loaded into. + */ +@property (nonatomic, readonly) NSBundle *sparkleBundle; + +/*! + The user agent used when checking for and downloading updates. + + The default implementation can be overridden. + */ +@property (nonatomic, copy) NSString *userAgentString; + +/*! + The HTTP headers used when checking for and downloading updates. + + The keys of this dictionary are HTTP header fields (NSString) and values are corresponding values (NSString) + */ +@property (copy, nonatomic) NSDictionary<NSString *, NSString *> *httpHeaders; + +/*! + A property indicating whether or not the user's system profile information is sent when checking for updates. + + Setting this property will persist in the host bundle's user defaults. + */ +@property (nonatomic) BOOL sendsSystemProfile; + +/*! + A property indicating the decryption password used for extracting updates shipped as Apple Disk Images (dmg) + */ +@property (nonatomic, copy) NSString *decryptionPassword; + +/*! + Returns the date of last update check. + + \returns \c nil if no check has been performed. + */ +@property (nonatomic, readonly, copy) NSDate *lastUpdateCheckDate; + +/*! + Appropriately schedules or cancels the update checking timer according to + the preferences for time interval and automatic checks. + + This call does not change the date of the next check, + but only the internal NSTimer. + */ +- (void)resetUpdateCycle; + +/*! + A property indicating whether or not an update is in progress. + + Note this property is not indicative of whether or not user initiated updates can be performed. + Use SUUpdater::validateMenuItem: for that instead. + */ +@property (nonatomic, readonly) BOOL updateInProgress; + +@end + +#endif diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdaterDelegate.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdaterDelegate.h new file mode 100644 index 0000000000..affe0a7c07 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUUpdaterDelegate.h @@ -0,0 +1,363 @@ +// +// SUUpdaterDelegate.h +// Sparkle +// +// Created by Mayur Pawashe on 3/12/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +#endif + +@protocol SUVersionComparison, SUVersionDisplay; +@class SUUpdater, SUAppcast, SUAppcastItem; + +NS_ASSUME_NONNULL_BEGIN + +// ----------------------------------------------------------------------------- +// SUUpdater Notifications for events that might be interesting to more than just the delegate +// The updater will be the notification object +// ----------------------------------------------------------------------------- +SU_EXPORT extern NSString *const SUUpdaterDidFinishLoadingAppCastNotification; +SU_EXPORT extern NSString *const SUUpdaterDidFindValidUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterDidNotFindUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterWillRestartNotification; +#define SUUpdaterWillRelaunchApplicationNotification SUUpdaterWillRestartNotification; +#define SUUpdaterWillInstallUpdateNotification SUUpdaterWillRestartNotification; + +// Key for the SUAppcastItem object in the SUUpdaterDidFindValidUpdateNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastItemNotificationKey; +// Key for the SUAppcast object in the SUUpdaterDidFinishLoadingAppCastNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey; + +// ----------------------------------------------------------------------------- +// SUUpdater Delegate: +// ----------------------------------------------------------------------------- + +/*! + Provides methods to control the behavior of an SUUpdater object. + */ +__deprecated_msg("Deprecated in Sparkle 2. See SPUUpdaterDelegate instead") +@protocol SUUpdaterDelegate <NSObject> +@optional + +/*! + Returns whether to allow Sparkle to pop up. + + For example, this may be used to prevent Sparkle from interrupting a setup assistant. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)updater; + +/*! + Returns additional parameters to append to the appcast URL's query string. + + This is potentially based on whether or not Sparkle will also be sending along the system profile. + + \param updater The SUUpdater instance. + \param sendingProfile Whether the system profile will also be sent. + + \return An array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user. + */ +- (NSArray<NSDictionary<NSString *, NSString *> *> *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; + +/*! + Returns a custom appcast URL. + + Override this to dynamically specify the entire URL. + + An alternative may be to use SUUpdaterDelegate::feedParametersForUpdater:sendingSystemProfile: + and let the server handle what kind of feed to provide. + + \param updater The SUUpdater instance. + */ +- (nullable NSString *)feedURLStringForUpdater:(SUUpdater *)updater; + +/*! + Returns whether Sparkle should prompt the user about automatic update checks. + + Use this to override the default behavior. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)updater; + +/*! + Called after Sparkle has downloaded the appcast from the remote server. + + Implement this if you want to do some special handling with the appcast once it finishes loading. + + \param updater The SUUpdater instance. + \param appcast The appcast that was downloaded from the remote server. + */ +- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; + +/*! + Returns the item in the appcast corresponding to the update that should be installed. + + If you're using special logic or extensions in your appcast, + implement this to use your own logic for finding a valid update, if any, + in the given appcast. + + \param appcast The appcast that was downloaded from the remote server. + \param updater The SUUpdater instance. + */ +- (nullable SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)updater; + +/*! + Called when a valid update is found by the update driver. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item; + +/*! + Called when a valid update is not found. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidNotFindUpdate:(SUUpdater *)updater; + +/*! + Called just before the scheduled update driver prompts the user to install an update. + + \param updater The SUUpdater instance. + + \return YES to allow the update prompt to be shown (the default behavior), or NO to suppress it. + */ + - (BOOL)updaterShouldShowUpdateAlertForScheduledUpdate:(SUUpdater *)updater forItem:(SUAppcastItem *)item; + + /*! + Called after the user dismisses the update alert. + + \param updater The SUUpdater instance. + \param permanently YES if the alert will not appear again for this update; NO if it may reappear. + */ + - (void)updater:(SUUpdater *)updater didDismissUpdateAlertPermanently:(BOOL)permanently forItem:(SUAppcastItem *)item; + +/*! + Called immediately before downloading the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be downloaded. + \param request The mutable URL request that will be used to download the update. + */ +- (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request; + +/*! + Called immediately after successful download of the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that has been downloaded. + */ +- (void)updater:(SUUpdater *)updater didDownloadUpdate:(SUAppcastItem *)item; + +/*! + Called after the specified update failed to download. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that failed to download. + \param error The error generated by the failed download. + */ +- (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error; + +/*! + Called when the user clicks the cancel button while and update is being downloaded. + + \param updater The SUUpdater instance. + */ +- (void)userDidCancelDownload:(SUUpdater *)updater; + +/*! + Called immediately before extracting the specified downloaded update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be extracted. + */ +- (void)updater:(SUUpdater *)updater willExtractUpdate:(SUAppcastItem *)item; + +/*! + Called immediately after extracting the specified downloaded update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that has been extracted. + */ +- (void)updater:(SUUpdater *)updater didExtractUpdate:(SUAppcastItem *)item; + +/*! + Called immediately before installing the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)item; + +/*! + Called when an update is skipped by the user. + + \param updater The updater instance. + \param item The appcast item corresponding to the update that the user skipped. + */ +- (void)updater:(SUUpdater *)updater userDidSkipThisVersion:(SUAppcastItem *)item; + +/*! + Returns whether the relaunch should be delayed in order to perform other tasks. + + This is not called if the user didn't relaunch on the previous update, + in that case it will immediately restart. + + This may also not be called if the application is not going to relaunch after it terminates. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param invocation The invocation that must be completed with `[invocation invoke]` before continuing with the relaunch. + + \return \c YES to delay the relaunch until \p invocation is invoked. + */ +- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item untilInvoking:(NSInvocation *)invocation; + +/*! + Returns whether the relaunch should be delayed in order to perform other tasks. + + This is not called if the user didn't relaunch on the previous update, + in that case it will immediately restart. + + This method acts as a simpler alternative to SUUpdaterDelegate::updater:shouldPostponeRelaunchForUpdate:untilInvoking: avoiding usage of NSInvocation, which is not available in Swift environments. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + + \return \c YES to delay the relaunch. + */ +- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item; + +/*! + Returns whether the application should be relaunched at all. + + Some apps \b cannot be relaunched under certain circumstances. + This method can be used to explicitly prevent a relaunch. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater; + +/*! + Called immediately before relaunching. + + \param updater The SUUpdater instance. + */ +- (void)updaterWillRelaunchApplication:(SUUpdater *)updater; + +/*! + Called immediately after relaunching. SUUpdater delegate must be set before applicationDidFinishLaunching: to catch this event. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidRelaunchApplication:(SUUpdater *)updater; + +/*! + Returns an object that compares version numbers to determine their arithmetic relation to each other. + + This method allows you to provide a custom version comparator. + If you don't implement this method or return \c nil, + the standard version comparator will be used. Note that the + standard version comparator may be used during installation for preventing + a downgrade, even if you provide a custom comparator here. + + \sa SUStandardVersionComparator + + \param updater The SUUpdater instance. + */ +- (nullable id<SUVersionComparison>)versionComparatorForUpdater:(SUUpdater *)updater; + +/*! + Returns an object that formats version numbers for display to the user. + If you don't implement this method or return \c nil, the standard version formatter will be used. + + \sa SUUpdateAlert + \param updater The SUUpdater instance. + */ +- (nullable id <SUVersionDisplay>)versionDisplayerForUpdater:(SUUpdater *)updater; + +/*! + Returns the path to the application which is used to relaunch after the update is installed. + + The installer also waits for the termination of the application at this path. + + The default is the path of the host bundle. + + \param updater The SUUpdater instance. + */ +- (nullable NSString *)pathToRelaunchForUpdater:(SUUpdater *)updater; + +/*! + Called before an updater shows a modal alert window, + to give the host the opportunity to hide attached windows that may get in the way. + + \param updater The SUUpdater instance. + */ +- (void)updaterWillShowModalAlert:(SUUpdater *)updater; + +/*! + Called after an updater shows a modal alert window, + to give the host the opportunity to hide attached windows that may get in the way. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidShowModalAlert:(SUUpdater *)updater; + +/*! + Called when an update is scheduled to be silently installed on quit. + + This is after an update has been automatically downloaded in the background. + (i.e. SUUpdater::automaticallyDownloadsUpdates is YES) + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param invocation Can be used to trigger an immediate silent install and relaunch. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation; + +/*! + Called when an update is scheduled to be silently installed on quit. + This is after an update has been automatically downloaded in the background. + (i.e. SUUpdater::automaticallyDownloadsUpdates is YES) + This method acts as a more modern alternative to SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationInvocation: using a block instead of NSInvocation, which is not available in Swift environments. + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param installationBlock Can be used to trigger an immediate silent install and relaunch. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationBlock:(void (^)(void))installationBlock; + +/*! + Calls after an update that was scheduled to be silently installed on quit has been canceled. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that was proposed to be installed. + + \deprecated This method is no longer invoked. The installer will try to its best ability to install the update. + */ +- (void)updater:(SUUpdater *)updater didCancelInstallUpdateOnQuit:(SUAppcastItem *)item __deprecated; + +/*! + Called after an update is aborted due to an error. + + \param updater The SUUpdater instance. + \param error The error that caused the abort + */ +- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUVersionComparisonProtocol.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUVersionComparisonProtocol.h new file mode 100644 index 0000000000..8d22d7a9f9 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUVersionComparisonProtocol.h @@ -0,0 +1,42 @@ +// +// SUVersionComparisonProtocol.h +// Sparkle +// +// Created by Andy Matuschak on 12/21/07. +// Copyright 2007 Andy Matuschak. All rights reserved. +// + +#ifndef SUVERSIONCOMPARISONPROTOCOL_H +#define SUVERSIONCOMPARISONPROTOCOL_H + +#import <Foundation/Foundation.h> + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + Provides version comparison facilities for Sparkle. +*/ +@protocol SUVersionComparison + +/** + An abstract method to compare two version strings. + + Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, + and NSOrderedSame if they are equivalent. +*/ +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; // *** MAY BE CALLED ON NON-MAIN THREAD! + +@end + +NS_ASSUME_NONNULL_END +#endif diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUVersionDisplayProtocol.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUVersionDisplayProtocol.h new file mode 100644 index 0000000000..2874706e69 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/SUVersionDisplayProtocol.h @@ -0,0 +1,78 @@ +// +// SUVersionDisplayProtocol.h +// EyeTV +// +// Created by Uli Kusterer on 08.12.09. +// Copyright 2009 Elgato Systems GmbH. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +#endif + +@class SUAppcastItem; + +NS_ASSUME_NONNULL_BEGIN + +/** + Applies special display formatting to version numbers of the bundle to update and the update before presenting them to the user. +*/ +SU_EXPORT @protocol SUVersionDisplay <NSObject> + +/** + Formats an update's version string and bundle's version string for display. + + This method is used to format both the display version of the update and the display version of the bundle to update. + + The display versions returned by this method are then used for presenting to the user when a new update is available, + or when the user cannot download/install the latest update for a specific reason, or when the user has a newer version + installed than the latest known version in the update feed. + + On input, the `update.displayVersionString` and `*inOutBundleDisplayVersion` may be the same, but the + `update.versionString` and `bundleVersion` will differ. To differentiate between these display versions, you may + choose to return different display version strings for the update and bundle. + + @param update The update to format the update display version from. You can query `update.displayVersionString` and `update.versionString` to retrieve the update's version information. + @param inOutBundleDisplayVersion On input, the display version string (or `CFBundleShortVersionString`) of the bundle to update. On output, this is the display version string of the bundle to show to the user. + @param bundleVersion The version (or CFBundleVersion) of the bundle to update. + @return A new display version string of the `update.displayVersionString` to show to the user. + */ +- (NSString *)formatUpdateDisplayVersionFromUpdate:(SUAppcastItem *)update andBundleDisplayVersion:(NSString * _Nonnull __autoreleasing * _Nonnull)inOutBundleDisplayVersion withBundleVersion:(NSString *)bundleVersion; + +@optional + +/** + Formats a bundle's version string for display. + + This method is used to format the display version of the bundle. + This method may be used when no new update is available and the user is already on the latest known version. + In this case, no new update version is shown to the user. + + This method is optional. If it's not implemented, Sparkle will default to using the `bundleDisplayVersion` passed to this method. + + @param bundleDisplayVersion The display version string (or `CFBundleShortVersionString`) of the bundle to update. + @param bundleVersion The version (or `CFBundleVersion`) of the bundle to update. + @param matchingUpdate The update in the feed that corresponds to the current bundle, or `nil` if no matching update item could be found in the feed. + @return A new display version string of the bundle to show to the user. + */ +- (NSString *)formatBundleDisplayVersion:(NSString *)bundleDisplayVersion withBundleVersion:(NSString *)bundleVersion matchingUpdate:(SUAppcastItem * _Nullable)matchingUpdate; + +/** + Formats two version strings. + + Both versions are provided so that important distinguishing information + can be displayed while also leaving out unnecessary/confusing parts. +*/ +- (void)formatVersion:(NSString *_Nonnull*_Nonnull)inOutVersionA andVersion:(NSString *_Nonnull*_Nonnull)inOutVersionB __deprecated_msg("Please use -formatUpdateDisplayVersionFromUpdate:andBundleDisplayVersion:withBundleVersion:"); + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/Sparkle.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/Sparkle.h new file mode 100644 index 0000000000..a048d2670a --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Headers/Sparkle.h @@ -0,0 +1,39 @@ +// +// Sparkle.h +// Sparkle +// +// Created by Andy Matuschak on 3/16/06. (Modified by CDHW on 23/12/07) +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SPARKLE_H +#define SPARKLE_H + +// This list should include the shared headers. It doesn't matter if some of them aren't shared (unless +// there are name-space collisions) so we can list all of them to start with: + +#import <Sparkle/SUExport.h> +#import <Sparkle/SUAppcast.h> +#import <Sparkle/SUAppcastItem.h> +#import <Sparkle/SUStandardVersionComparator.h> +#import <Sparkle/SPUUpdater.h> +#import <Sparkle/SPUUpdaterDelegate.h> +#import <Sparkle/SPUUpdaterSettings.h> +#import <Sparkle/SUVersionComparisonProtocol.h> +#import <Sparkle/SUVersionDisplayProtocol.h> +#import <Sparkle/SUErrors.h> +#import <Sparkle/SPUUpdatePermissionRequest.h> +#import <Sparkle/SUUpdatePermissionResponse.h> +#import <Sparkle/SPUUserDriver.h> +#import <Sparkle/SPUDownloadData.h> + +// UI bits +#import <Sparkle/SPUStandardUpdaterController.h> +#import <Sparkle/SPUStandardUserDriver.h> +#import <Sparkle/SPUStandardUserDriverDelegate.h> + +// Deprecated bits +#import <Sparkle/SUUpdater.h> +#import <Sparkle/SUUpdaterDelegate.h> + +#endif diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Modules/module.modulemap b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Modules/module.modulemap new file mode 100644 index 0000000000..01a494aa9b --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module Sparkle { + umbrella header "Sparkle.h" + export * + + module * { export * } +} diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUAppcastItemStateResolver.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUAppcastItemStateResolver.h new file mode 100644 index 0000000000..929d3f1b71 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUAppcastItemStateResolver.h @@ -0,0 +1,38 @@ +// +// SPUAppcastItemStateResolver.h +// Sparkle +// +// Created by Mayur Pawashe on 5/31/21. +// Copyright © 2021 Sparkle Project. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SUStandardVersionComparator, SPUAppcastItemState; +@protocol SUVersionComparison; + +/** + Private exposed class used to resolve Appcast Item properties that rely on external factors such as a host. + This resolver is used for constructing appcast items. + */ +SU_EXPORT @interface SPUAppcastItemStateResolver : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithHostVersion:(NSString *)hostVersion applicationVersionComparator:(id<SUVersionComparison>)applicationVersionComparator standardVersionComparator:(SUStandardVersionComparator *)standardVersionComparator; + +@end + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUGentleUserDriverReminders.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUGentleUserDriverReminders.h new file mode 100644 index 0000000000..a509e0e07c --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUGentleUserDriverReminders.h @@ -0,0 +1,22 @@ +// +// SPUGentleUserDriverReminders.h +// Sparkle +// +// Copyright © 2022 Sparkle Project. All rights reserved. +// + +#ifndef SPUGentleUserDriverReminders_h +#define SPUGentleUserDriverReminders_h + +/** + A private protocol for user drivers implementing gentle scheduled reminders + */ +@protocol SPUGentleUserDriverReminders + +- (void)logGentleScheduledUpdateReminderWarningIfNeeded; + +- (void)resetTimeSinceOpportuneUpdateNotice; + +@end + +#endif /* SPUGentleUserDriverReminders_h */ diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUInstallationType.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUInstallationType.h new file mode 100644 index 0000000000..2c6e556195 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUInstallationType.h @@ -0,0 +1,19 @@ +// +// SPUInstallationType.h +// Sparkle +// +// Created by Mayur Pawashe on 7/24/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#ifndef SPUInstallationType_h +#define SPUInstallationType_h + +#define SPUInstallationTypeApplication @"application" // the default installation type for ordinary application updates +#define SPUInstallationTypeGuidedPackage @"package" // the preferred installation type for package installations +#define SPUInstallationTypeInteractivePackage @"interactive-package" // the deprecated installation type; use guided package instead + +#define SPUInstallationTypesArray (@[SPUInstallationTypeApplication, SPUInstallationTypeGuidedPackage, SPUInstallationTypeInteractivePackage]) +#define SPUValidInstallationType(x) ((x != nil) && [SPUInstallationTypesArray containsObject:(NSString * _Nonnull)x]) + +#endif /* SPUInstallationType_h */ diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUStandardUserDriver+Private.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUStandardUserDriver+Private.h new file mode 100644 index 0000000000..45bd43feff --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUStandardUserDriver+Private.h @@ -0,0 +1,40 @@ +// +// SPUStandardUserDriver+Private.h +// Sparkle +// +// Copyright © 2022 Sparkle Project. All rights reserved. +// + +#ifndef SPUStandardUserDriver_Private_h +#define SPUStandardUserDriver_Private_h + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SPUStandardUserDriver.h" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SPUStandardUserDriver.h> +#import <Sparkle/SUExport.h> +#endif + +@class NSWindowController; + +NS_ASSUME_NONNULL_BEGIN + +SU_EXPORT @interface SPUStandardUserDriver (Private) + +/** + Private API for accessing the active update alert's window controller. + This is the window controller that shows the update's release notes and install choices. + This can be accessed in -[SPUStandardUserDriverDelegate standardUserDriverWillHandleShowingUpdate:forUpdate:state:] + */ +@property (nonatomic, readonly, nullable) NSWindowController *activeUpdateAlert; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* SPUStandardUserDriver_Private_h */ diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUUserAgent+Private.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUUserAgent+Private.h new file mode 100644 index 0000000000..392fccb40c --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SPUUserAgent+Private.h @@ -0,0 +1,29 @@ +// +// SPUUserAgent+Private.h +// Sparkle +// +// Created by Mayur Pawashe on 11/12/21. +// Copyright © 2021 Sparkle Project. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SUHost; + +SU_EXPORT NSString *SPUMakeUserAgentWithHost(SUHost *responsibleHost, NSString * _Nullable displayNameSuffix); + +SU_EXPORT NSString *SPUMakeUserAgentWithBundle(NSBundle *responsibleBundle, NSString * _Nullable displayNameSuffix); + +NS_ASSUME_NONNULL_END diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SUAppcastItem+Private.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SUAppcastItem+Private.h new file mode 100644 index 0000000000..ab49b4178f --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SUAppcastItem+Private.h @@ -0,0 +1,40 @@ +// +// SUAppcastItem+Private.h +// Sparkle +// +// Created by Mayur Pawashe on 4/30/21. +// Copyright © 2021 Sparkle Project. All rights reserved. +// + +#ifndef SUAppcastItem_Private_h +#define SUAppcastItem_Private_h + +#import <Foundation/Foundation.h> +#import <Sparkle/SUAppcastItem.h> + +NS_ASSUME_NONNULL_BEGIN + +// Available in SPUAppcastItemStateResolver.h (a private exposed header) +@class SPUAppcastItemStateResolver; +@class SUSignatures; + +@interface SUAppcastItem (Private) <NSSecureCoding> + +/** + Initializes with data from a dictionary provided by the RSS class and state resolver + + This initializer method is intended to be marked "private" and discouraged from public usage. + This method is available however. Talk to us to describe your use case and if you need to construct appcast items yourself. + */ +- (nullable instancetype)initWithDictionary:(NSDictionary *)dict relativeToURL:(NSURL * _Nullable)appcastURL stateResolver:(SPUAppcastItemStateResolver *)stateResolver failureReason:(NSString * _Nullable __autoreleasing *_Nullable)error; + +/** + The EdDSA and DSA signatures along with their statuses. + */ +@property (readonly, nonatomic, nullable) SUSignatures *signatures; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* SUAppcastItem_Private_h */ diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SUInstallerLauncher+Private.h b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SUInstallerLauncher+Private.h new file mode 100644 index 0000000000..8922e18139 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/PrivateHeaders/SUInstallerLauncher+Private.h @@ -0,0 +1,37 @@ +// +// SUInstallerLauncher+Private.h +// SUInstallerLauncher+Private +// +// Created by Mayur Pawashe on 8/21/21. +// Copyright © 2021 Sparkle Project. All rights reserved. +// + +#ifndef SUInstallerLauncher_Private_h +#define SUInstallerLauncher_Private_h + +#if defined(BUILDING_SPARKLE_SOURCES_EXTERNALLY) +// Ignore incorrect warning +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" +#import "SUExport.h" +#import "SPUInstallationType.h" +#pragma clang diagnostic pop +#else +#import <Sparkle/SUExport.h> +// Chances are clients will need this too +#import <Sparkle/SPUInstallationType.h> +#endif + +@class NSString; + +/** + Private API for determining if the system needs authorization access to update a bundle path + + This API is not supported when used directly from a Sandboxed applications and will always return @c YES in that case. + + @param bundlePath The bundle path to test if authorization is needed when performing an update that replaces this bundle. + @return @c YES if Sparkle thinks authorization is needed to update the @c bundlePath, otherwise @c NO. + */ +SU_EXPORT BOOL SPUSystemNeedsAuthorizationAccessForBundlePath(NSString *bundlePath); + +#endif /* SUInstallerLauncher_Private_h */ diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/SUUpdateAlert.nib b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000000000000000000000000000000000000..d62665666fda2c018f375bab6e3528109925568c GIT binary patch literal 17730 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 WHp&zO`z@Z<TFSVd<tUzwoc{tNu60QO literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-101300.nib b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-101300.nib new file mode 100644 index 0000000000000000000000000000000000000000..9eb406cffd880c7bf3abf770fceca7e8885d3913 GIT binary patch literal 25696 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 ab|l-6RQ$IXv&Ax_+p7HPs`5kG?Ee6D#4z#z literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-110000.nib b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-110000.nib new file mode 100644 index 0000000000000000000000000000000000000000..b2c37cb2979d08ae05608601f8eed4ff7c4f577e GIT binary patch literal 25673 zcmeHvcX(4r*7wMkZ21DF_a=I^akpu~-I!tnwjmG#$kMefkR_vHFeTyIvMkF5Q$kN@ z32ArJc2k!mq$e9TjfC`sBuo14lHE<%b+XC#n{%&Z*(TY|_q>0+-}8L-bAEU3+?g|H z&YU?jcV?~%@-s6WrV2~7s9}r=pwAIV!1Wc-qmhg)iehZ)AjYQSzRt+ljvcj9t-rRp zwyw3#vrDSqEqV7GY>+*^dk%P>ml|KF^PlzlU-LG-)#~|FYW}Rw^QGkZN^1Dp({icF z`+aT8zgxV5RO{<09q^2oq{azgHc9d}#Yj6_=SX#)d6MKwm!#$lscvUB!a1N^q|1}M z{>7jo$?v~bYV?;$4gN~0(Nm3Qo1}XGHr$8THuwcl-`WPxkXor>XsyRLvi5){xmIdS zsjc;=)iyNEt8HyvTwCW^QY(3uf|h}bkhU0UudY4l*@(0bq;(<f*4mw|LS3DwSDoaE z2K5H@sgoM|)_Gb6)-`zzb+s*H>sq`s>gs$6bq72P>ZHbnb+!J*b>60;x}B}1$SWdG z8RE<98vRvuUVk;N*Vp;|QJzMBKTm^yD6V5Ye*bh&Lt~<+-k*lJd7xa-Vo!so*dsNR zcswmDJxyM-r>?Esv(LNT<JqOD-|6jBU%R_s{Vkr<dTCEuz0Z?gFL^WS>-J{XH+mM= zOE(nMH#C>j*Y8_ZU%P*GeS^nTFC8$~OCAwa1}evWMZI*;QeS&;L%qjiM_d)|H`aR& zx$El=Z?2b))YSW0HQqXp)+>2*Ug=<j*W(dz?cwzt?2T}Lue5i7x6w1wE8Q^4+i-A# zxAw>+udj6m@+2T#BGRPdnuhdq5Kc!p1J@j{bYrgfpl2=ku0z;}@D_x(dZor~-dg{4 zUT>qeq4uVT2Ct`QgLJf4!y(V42B~p!L!EzGgV!J5(BMgHkdDl3@U><%)OoTSU@HyM z!Q2LqX92<sk#<po&tKfo=)V?J2C_Fa_^WZf9;EmAJbiqUr>{?H=;!nJ2l>1$BYaJs zNj{X}Lm57=Cmzodd>&5<uBkrHvDrSqH^=8Wn(sU0F(IAVSL-kHH5{q*`C8q+I?rbC ztMN(ATM*vvla5}8bVC}Y#-WXM{t=B{|JcTcBU2iEt?`X@o}@;}liVmBPiZ{jUDhbo z7d19|RwM3O&>GNM&^nM2R0=YI%-|)0R|R<48oei6jsBC@HP(B={gOuu()p#EBmA`< zJ;G6bsZsFz{R8}s{*j=GexE-Ul!EX)T=V=5{sM$o_!~Xz{8EF_?`aYJP2MVhZHvR- z;=SHq=Lu_)8pE4>{vJ(Uf8VA?|1i)v&~(Hl;X1dep)tFu-k;yp;9mk-0b0}4;1Qdo zhO#ElE$f>Od9=+^qprEuAKC2n^lFwGqMJRZ`Zc$C#x_fyam~`P@y&j3Y;$d6TywoY zxw*kV2b2X`(A?lziZshWMWABjEot_6RyIqwu4=CHT#M@(T#dMvHrL%Ig8v4jvw|v{ zrQ2-C??$+~S=zs;x!$v_Svs&C_t)WiJ)W}`uP35K@<g^sx9M9NJiQU_1M1r%?eEu8 z=NX9bAUqq4bR!WSh4APW@9h&>Jl-iS^`2P7%>c!L;#;IUcI@yZAee|`Nyst_;n@h! zK{ylPEKqif)R@z9sW!Q_wk{RUb5<+Tv`Tl*X|45Mh5HQLXSPa>S*@2E6L&^QjdR+f zqyss-qyxF2JkWelK4<}GA!re3F{l7k2wDPK3R(s#0u_TwK+8cZKv#oSf>wd90j&mI z3t9tO3t9&<f=WRqkQpR`%0T6y3Xlb~9<%{u1yzD<AUmiEv=QV0IYBOv8&nP21lkO$ z0c`<o1#JUu2VDoc9>jJ_2Q;8CP&h~n(t#pCksv)N3M7DffO>*@fucdZL481dLH$7e zK?6VoL4!bpK|?@8LBl}9K_fsTL8CyUK?cwm&{)to(0I@U&_vKA&}7gQ&{WVg&~#7? zC>AsW6bFh2C4dq^NuXp<3Mdtn2AT<)1)2?-1DXrE3N#Ot4$1&!g0eu_pd3&xC=WCr zl#c;-!EOvRyQQ{*qWnr@xtL+IR2p3tyRD_5D9dhhi8j}Ai@51fK~bLQbh#bkGSOr& zw^_D`j#!t`Q7*c67Zhb!t@cgM;+iV4NVJM37xHE!wZ%r98Vib66z5uv<<6#pqGk3? z^F>Q}h09-16zc?=YNK^;Nq&K)G{<JLn?*-XWtFRDp;&{oyx=UW(dn#*a9LRgfDp)G z60O#vDx=9_D_2<Eq=@RUTbGGtZl~zXwK$xvWumjnZUe_X1w};=+Gfshxg3^Kw@X}} zK&4lSCX3Muw#CL$tH?{sbJ*Qg5V6QrV-*_;iWV75MJwuF<T9Ez@B}&LaxvfL6kX0( zt61i0Ehx%#yIgh~RWISll~b15k)#quLZMEV(dI(k<`<bDuT^wFJK1&<=k4X%vAdeV zgq~$ox|~q%647C?n=K})JG8Z{TXmEiqG&_g@a%;avvax8>K2`c@-t(dHBOgUnTxh8 zsWKZ;dC}oS`=VAn4(()eSgKrhM^HOXu*kAIY@&lpbsQ2HZQUCmdPKb!8=V{WcjZ)A z#SMtdP7M)cygUK2bDpFJhtXnlId?+b()EyAmH1e&Efu56*<59`ae0gFc5AVvif0ID zBwMr^Yfws-+X?+<*{ya5w+j*G7t$*Gf*gy*&8`fqrQB8tt46-X7RV1<F0+`(P!IBy zR(GW>!wr3iuuhm5nF1Oq7ScldP#d!4x?zGQ2UM>1ks??!I2P4ZmfEcrQ=p+C2`szV z?lx6q8Xd5fY|&&jIz%%J+zD&J!%U0KjAD?(1})ef9nBLoIz+i3qIJHzyc|iPBI>GK zlS{0qZ-w0oGhgBmO`=(}nM5>5hRGy4ot9FI)#9q*_P`57e2}Nv;#4{VR11}^fE4yk znXqyc;43I9BZEfI$*`iwp&yHCMV;ssq(j(nu-l*}*$yKKS7djigL1RTwL2EsAvL%u zkua-#$mfE-MF-dv;UVv2)NnBVEc9!rR}lq~+>=1MOtHdPZLvGhjzQ@Q9TqU6MuYHN zG<2p6N)8j_)hI_#wwZ}FIv$#Ag~jZu*q@&XF2*u3!yy_M8EforSDxEq7ST`85nvgl zSnw;el)D|opEtO1^A#H$6`C&^p?WU)Em!i8M(!9zGZ_GPkIus(l?d@A4(<`*f;;z@ zy-;;kmDM6T^D8SwvjweStwEcp_2n0f&24u(kyFuHk-OAoaaqNt{7iD31;$FT1wOz| zT@1b72un6q2b^)P1JAkX!ZP75$=-R>p;1U(@Vkm9=B-QepA2dBK$C~K`aoxa#yDFP zhWty6t_tqW10D}*%M`bW)<ELSJZdHsR%|RsW1$k&Fl{Gt7b~LP8ltuNc86sPYHqY< zSuIsnFjp=)cdI55nU(QN<jyB(E$U!_r|=kJIn;>ys!HTyCZL<DPKb9C)R8*?J`BP_ ztu-0u^>8pwo&iRucz-a+w^=PV(M*mOM%c-dwB=_mwwv8naf!oT4f}GywPc{Rvh0;r za4g)PErC~epmU-(XWI+xu3`uLoE5T#Iv?H`5H3VkD3WZHw=DVTN(^!k%Zb{l5_12Y zjiy$5-3i27MWX|QBJaxZ5*SNROred}1dhdSgZV7Cz*$&D#fmtu*i!gg_&Wpx?&BzR zo(dxzg=khh9ZKxzDG;s5T_rkr<3VVJS1~t7^bPKNb1kCP9CW~Zuu_z%x;!|F*wPx8 z=!D&O8*iLPf5yiY*lpxyP-&$^?iSG=8Sc%>Ai(XG_avI5tbod4GJG)Mvk~fV`4}D{ zVOE933Wq_i1mkiRJOo#cGNZ9OxWakAsIa)WU8`eUN8|8fp{yLU#l>e6Hz*@rz7t+X zbQEK#m~VITNzXD2TAVK?EkOg#7BRo55S{ZaW;jUB$q9oAJ`QmdE_)RmQaA0y8p-!@ zgMceiCL{ZJb16P<F(zsjo0D7}BrZT(z|eU)$f3-QxDrr3>XXoxqRM<H`WD6{ULmxO z;$U~K<;HHqT+s~c-dux*2}(NO=tSW(W1J5sZ^Uv?4dhi!kMnX^FwzGNPthJ9_n;8x zT|YC^u(>NsMMq&7c~KD}ZFE~;#b{$xA_q4xQ}lq)$ybMyP{+)mb#SY@v7o2~rcV>9 zGK`<R?j0i&T8<klX`*9xgz784i1&Q17WfUWmX6uQB6x#L7|jOsd~PY^Tyf>MR^I<Z zhh*59GTb}44$xVx_Hu4vD28jW+}Ru$k+_3`ttjTZ$X?EUlG3W#&;n@=V<%6=XEesm zo%|4lg{U+nbM8=4chiA%<P*^yc)bI@I9sfS42l<1B`c=+K87&0mq3ZmBJ?J!$m`DQ ziSlSt04A75@wu9sRLQ!Fn<RJJ&SHBeAD_98S0_N+c7roG?!7DQZmT&HBQ-54nmy2a zHk74UCXH^Vls>isGiqlhjEh&*>gKjoSS8x>78SW2ym=u7w|G2N-Uqt2MaOg*isV&= zKMYQL$k{?Q!G49#5j6LpFT)tiGdlU~Gd~*zsMS{8O|Ye?lFB>4@&wM6*O=#oV?f`b zK^M_^4vR8%!8k%S#e0qD;8Q!K<F1`c1J%WvOSz9mD96T)(TvA10b+wlc;hO&!-Z)d znYsy?)S)m?P;iofE)lGi;t-6Enqc)Xr&82TokE@St5qx#@nS`0$m8KhgM0$h*6!o< zs{&skzL9D63t<TSUC;psN<suKp9&>-c(bqu_^q%OctltS93u&z;_^G;GvG{#F4>Y0 zjw}l$L5s^Pgwv&{FQeM^2Z~CR*B!hpR9eo)H1bKU1UpMb?^<`eo6#u3gY%q1)( ze1z}`!lwwoC+rOYe8ND&@q`lzClgL3oJKf<FqLo?VFqC~VFBS%;Av8WN6>?Ro6r}i zk)qNhAs?Z;qee)Yo|@5;fW>omnc`9<-PgL40$*Z(Hmau-6)!QCC+W@zy$~}&(*2pS zi;TUbZPvz0+S{3C6rRtM!hcf#3M8ie4m#Sqb!%GkbVE%1_UVSL@hQ^{@hRK4bM5p1 z8=ue<$RrZ#1dBY&BGV<VHvGk<CrP@!!JA+r{aL2}xkPg$-qhM2xa?qhh<8bdMnt|O z^ycwOIk$zg^yeSqV(!+|v-g<pAxbn@qRS~wo8}-}_#+Ez*YB7m34M@pVnC*;=?`iK zXxsI>WBHs}ETXsGl6?M-+pfQ9t<h$;)l_1xoNp_$XIq>mtKEs!3}uQC(0_k0Y!hYx zN&iu*?mrX;AnKej76{d!6s95&R6MM23huey2I7il<7|RivE89&y-W|(wi}1&E3p`? zu;83JT7C%bE)#b(_t4PX)bhg-LoKhl$TY#0XFt>O`?hY4k4p+PeL_-NN81krBQz+N z;{@x}pwmNJeR_ImgNDK0G?#=C$b<$RjSCu-v$!pcElr!hn}xl{!e&c?0Z~_O((rcu z9i3*~223+;R=d$`Fxt!p%w8}?S`BVm!f>7!MuO8cNf?DoyS^=>#1xa57_%|2AeP>L zeigH>mNXHTG@|EUgfYY$BXZFui_28eu5a9$l$bi*kPt_mWIP`Ggb9Qb2`3Rw210?n z1v__}C0j!jh?`)dh_fsrQev8DMQ1H6;t-1n_M@bbTr7#_Qrbp$mg$g#m(#*@hnOyq zBYjm!4%`IG(Vk`6U>Vv&Op9I|WEXyxg$LP%A7bGFc5FxpJKVJEF--EgBe(czDBUOU z{XCx#Ll_GLQ{D$oFkZ7DQ(h~jL`64h1$auO0?I=rrz)#byAD~EoT{u!PA)5Qw(F0t zHHN;nWvlk13<FLsaiE{aqsTU47EtlCKO)r8+xU*<Wr4@3y)6;JU|*%)mW-JH-p}&h zmIOx|pGb~29>K%}#nUE%5wyhxQjCS$K|u5AD?>Don~)yIR2Q0pj2$kNv*9juhnHL^ z)4azt!R|H_4}%^wvR&VjZMS{A<DkosRUw)-oEK8@96c%x7xXAo)`FPjaWTeXOQ1(} zPT#JVhJ(ji6Myfvc6Ot4z@kma0bWVHa}nQ3y;mV3sNN5lhBTax$X})5gp|0ThEw3$ z0vetNM$j;qsk?^Hl!j;+H*=(Lr1Nm?@qsF!<qU*6w9HvS%%4?sHlBwndJies(8;@< z7c!9w>dwLi>MpEU6%)TOCc82tpzbT>g3^1n>zmi&UD33mv(EFtr%f;dy9{0P5fw6Y zt;TIo*&#z0pK4&};%tIL*MeZy%k)6)4n;r1g3n%j!a`uTsmmfncAB~@4&^}&UWl0g z>j5l|x4k-fDF7ohIG5u;4Pc!bdxtuREkVYPL5#EcHG|l(e4EMYHj5aw)Cq?{S%5cG zIqYQ?t9V{m3YKX3Ww@Z_%eSP)ELt2>UQiZj`Txj*JKkdC_6a3~%L!KyUQM`?a24S- zK<I-v@P8bScoUvrs$=Fkx{NF(A>AdtBt6JBWCY_1`;Q}-(l|d~PWKT^5nmk@lrlO- z1%;hDDkx(A(g+r`4laN?@ba2>5xY-FWmz9yS*0A|EE6!ODLI5gOu*>Fo8(%s@Cj=O z*AlJ+A{*~i!Ld*guVXA!DyFJ}rwknnm7Hp=m7E>5R&uJfR&oZ%!kud^nCE6y*exd7 z*b0n%CKS*nJPhn`6X88IYzKFF9|~qXI49f<L|;EC+=IZCoP~&ZpKub0p3YeYT}Byh z@TzhnE|=*^FG(oJjp{mXLhyUx7%<3ooD|j%uRre!hY@>&Z~&-eIiPPtnEo<$FvjVQ zNag;Od6NRu{yynnsF9cE@kZc<`G|{;I6^=!EeXgp9|{$?fq?sQxlB(WpanM&FeuVK zig{nyh4|N)_A_c-o(Rl1e`40J?>S*TZY~Gv7t9WK21j;F*S9crF3uQUzt~Y>1M<$0 zgi2f%1S+FXZ&EA6(x0Z{vm{|JQh3$q>FF2XvyQ#STkB6z-$dQXw7(C&*8PA(Rob^0 z+sQQTdjHnM_yjmb%sJ8|<wb^R&Iwj93$1Fq-nW&{M-tL#PHV$spWp{RD%gQ<sO7Fp ze?|>ur_WcVP2ZXRf>4D-Ig)@HvDaDnNJ;ky>8@RWEI8bSEC_Whvc<+>GLz1B;C)cj z`E76`SUoLl0Zx^Kt+-radd~U`mjnl%WlGwc1SbN=!?hl~SK^NwfeTlk;3ljl+(fvU zum;$!$BIW*F@HPcOJ`Vt6U*&b@d)e?%M33h*cn!ml;oE<`QCWoDIS&NE02Oph1j*I zz-Cap9vd_Pw!sX!ffk;ao+z>KFiGDM7n{ic5@x0)r!YiDjFp6KkSs$Iw&U`+a2<*1 zRbFJ<^*cFttRx4P=u|$naqL#%dL;d;zAnxXpDeK`16so`?4ax;&`J9qG2)Gw&t@vI zxSh8wW98K=R~aJTV|q52^=7fGx6$OR%2=|5kJ1do7Vj`!{=PfUG(75D4CmG5;eWc- znX$=?L}x%XW~T`@ufZfrKg?9+KvPBWF>Bl=R}~L)6%5!=yetc0^nZO%H7}ys=sXdd z(iW2;&ta@F<QXd~jfRBS1Ot|#`N+zX{LlVXTHRc3{>R^Gdpb81@gX1<@_Wq2Y#zo- zgyuezF^5lz(h&Z-+OoU=;dI8r##gyA`6!QhRG8Vlp(F$0=?LFd=E&tEHzvMe?^L+w z^KcSl;X}4m6!TGh7{ZG-*cbA=V-YU1l@{>n5y}epnVeax5T1<ilNDk<Z!^4!hJWd> zFXL&Z;?+CaEN15*3^}xE7FT|;8umJ?i+DWb)V{u@B8$t3vUEMy8y74=7;mw<iJ~=+ zmkU0+MOCgPoG0#e)i!GZFDo74=S3&g7xxi*S4HuB#3vwJ>~a+IypSitTV~1S15_@; zcT_m$^LW%Z;!>5B><l`Ij5RuP@(>T5L~eGtmvKFyj*&Nuwi2#S*hu6DY`MnBL}q8D zKohgEkC=funT1tTsGJ#DCCC5?ri1d3$_8>Eg_CVSd^y4wgU|Ur&%yJ=uqr&WvofT$ zB4;4pz|7#F<m6oJAb!U=e@>iDYKgHZF>BN<%#G(2>}x!)Kxi?$Krx#kVI}Uf5bMUX za)d)mNq{9Kuq7m|A_ITUOZh@A#f}_iDoe@7>#4|=1(|JZ3!ZV=sP5Xq+Bj_@Xts8d zcCI##N^>AZ4U3`pITRNtwWH>|ZeIjT-3VDjYagnyfR>+tzFgpJMGbALew>iz2gtoa z&E26*DzR;tdvaCPzSgDrYfeF4)V$WQ)?#{j=cVViogTsr=bO*GzzlMqho7hVaLpLr z&_1;KT+ipW{X(mDv}QoI&aGM)YByv_=5BU_vUIQMUeSG^dz~3@{gdtu-RB6st9uXs zZwKY+XajCj7Q`zyp;((q6HY0)6=py)R(e_qZSa;7sh#qW&j|fGp*=V3j<<CTEFiQk zI$H?m7W$l9gi?Ar_$qfD7H^^$ZXvvUZY|yQ^J{uYNdG9awGFGPT61dK_hOaOe!T6l z(e+&ctDPQqPu$mWH^<3we~<fsv_S0__h#Im<KBonjwfG*?+L#@{L%1-!ymzn)quN4 z!k-9#m_l;+Y5d<C%-6X`C_3)kBe;E<)E>ZV?^10rw6BDE1>PQ<4{r^gKA^+(YR?X} zRj%<+8{)eB?<5M<b!QKvv_AFH=yB2U(UH+pqm!dEqBYTD@ShN!j?lR1vC)g72jl7d z=t<Gp(ZhrK2(*|LG2DW<<#pJi;`dfU%7CTu`c<Iy9pvSVYJI!b(a@=u+yh!d`<bQ} zda_cI;zI&u2J}iT+{uHMpd<_11UVegEB6jI_#Q*2G~9P{uLX<OTtjVm1sjc+6)`Cy zCt?!vD&EQvksUEVB9|E=;(2_;jEDsY&*Z7JqqOnbe0aS=X3%D6leOt;n0vxG_~(95 z(NT<c9-dyM&DN%I9~oL(NTTSLd&?`=G_)6DuHnK&3}Y0_vRBp6@f`#0I~ef(AIsN> z<1r%gc@|HDEv}_Bf6@#(YjQjIloWqj4mx}&(VGomL)j=c4x@SuWKCtWG0x^--cZPj z*-Ews(p8`>9nkGICNVE-Vr}dOc8DEiH?!N>UF<&gAUn;TV9&A_@v+4l>|KmN7ui?r z68nyQuhD2CHN7<bHA6L{H4`+`GzpqC%~hHl%_7Y*%}UKWO}WOVacj0|>NHK7-I_z1 z<C@zw_i7&2JfV3(^Qz`;%}1IqG=I~yhlPjr3>y$OGHhbljIgw@jIf1a#bMWmm4|H% z+Y;spYYjUPb|Op;dm!wIuouJL2)hvWRoJ)TEWAhfpztx_G2v<9S>c7@tHR5|9pTr7 z`@{Ezp9sGTUf{X#bKw`lzYhPGHbUE9YtY7^ZRTrBv?lFF?RD6~I;6c#`+)Xo?K$m7 z+DqCWb-i>WbklURbosigb>+HEI<Ib@?v(C+-81mo7j@r7L_`dNU8KTZRz|Fk*c#Cs zabv{Y5oaP^jrb(u+sMesp^?)f=SCJr8Y5ki-pGTIa^&NYuSH&r{HMN$ezZP8pQ~S` zx9My3`}B9{AJf04|4jdVRG+8`QL~~7qs&n?Q9Gk<j(Q~O<*18M-wS<(Ny1#ASXeJe z!U5qf;ThpQ;qN_q^cdS?W{;&kEIoGgIMCys9?$pqsK>v0_Uk#dXI9VEJ>5N9d*0gf zOwYG_{=HYPUK4wz_gdA<)vLAF?Y*Ar^+B(HM)!}7jn0oYM_(U(IQoI;*P{Q@JF54D z-Wk2u^xo2YU+>@ce!2J8eWLnI?32~U*yp-Fhx<I-=gmIf_U+#{zHedQ%D&Bg@96tN z-_QF+_M6x*r=Pi>r{BqbPxbqxe|Z0K{j>U;`q%Zpx&Jf$FAj(pFlj*kfb|2K2K;8g zO9L(q>^CrJ;PQc+2Ob{y=)enu!Us(plt0KiXy>5&2Av<&K6vEdjKSjI#=&<Der@pO zA;X8H4-tp>hul5njUnwr4MTH=ZWy|2=z~MwAEq5Pb=cBjn}*#q?3rO-4<9gm_Hfg1 z|M2^UzcV6i#MBYXMr<8%a>UsY-;NwLGH+zn$b%!F8u|68L8H<~tsk{_)MKN*7~OyL zRiiDV_l|yi^p}Q#h75z%aKP}i;nJ89W9E->k2yZ(r7_=+oiw&&Z0*>)$G$&K7?(D# zY~0>)Pma4Ze)Rao<F}2!bNo9Kq9&wGsF<*S!gCWYPn<My#l(h*4^I4i(vV5{leSE{ zbJBa0drh7<dE?|0lV6__F(qxvhAB5rd1Y$Y)a0oZQx8vld0N=Clxdb}H%@zXx^DW+ z>9*-7roS1}GbSUZI_A!pk75VKE{d&<eK7VfGse$YHDlL|=VtsEmmFus`1WpmzxW05 zwegR{|1Du^f+^u}!t05>5_1!;Pkboxo1|$;V$w}XZzcCnUYy*J{ABXKQ&LkLDR-rO zkvcwgUFwn4H`4~BElF!mdw!;FX6DT6XP%z<uURRxT(j<*_1D=kvp3AXWA^8BCe0D& z+&t%#xnt)V=N_MX;VQ#b>#jO>)rEOu<{9UmnD=q|xO8*+sr1h>rev(okTd?08JFqE zyg&1D)~u}US!c4tv-7h3*)Qev%PGk@l=DIE*xd5mJ997PCFgC;J2PK5f8qRH^WV%L znQzL!J^#{z)CJoYJiV~z!lH$T7Jj^F>LSOYM;3=IUa)x2;`a(B6xa$LEM$fGg?kF$ zUovUQ#wEXB8nJZA(!)zXT^7G=+p_11`WLM&x})g3;>_Zm#qX9(E^(JUxxDxC)yr>R zetAXqirp(NTs`CJ?N`6Ja@b1C%7<1(u3En8)T(c<$+_l+Yc8%%UhQ3d{@O{`Zoc+~ zHN)0e*F3tm_gdrH->%cGTe0qrbw3yjjVFxXmgbipDZOOMGVM2gWllHmHGd(_6?coD zmCY^NUG{nT-10r;UsTMi*jMqjCChTi^3D4B>yNI#yrFQzts8!_uCU%+sjpmD`LL~@ zZG-J;`zZTn`>R#cs=QSfHqP33!^TUFg^p8Bjq_UPL#_dMDLw0+?5=lzRDD(T;p%^G zTE6MGoBM9IZ9ZEwwWhJ=vn{z>ZrQ5cYTEkbwlUkJZ69sV*na#v&2^>MJ$e1O>pj<B z+>y8AHY|^9kp5U3SG&9RA9XA09`TIy?C^Y2pI3jUH`?p)zS%Il;iymRv-n<WOl&;Z z_@m$K|3gz;)4rx3noZ4rXo+t*(DGAjS?fzXQ+D3e7SU#FJHKn*uG@C^-MwY^Cwms} zd0?+$uYd19ZdiN63;UAx9o;YNckjP&V9|kx4vs(AcJPNo6^Gt9oO$@}BO{J99=Uv@ z`Nr37O26r@qa%+t9c@2mIri4^yyFj?n0R8}$%vEHCqKP;`OVMWl6K4Or-q$sI`!{c zZMR;yZP{&4-=2E=?RSj0W9OY=ce?NVLS8Mu@|*16JakvgUB~Ypba&G|>>l?$U*5a! z-t+e@yzj~1rv3J=-%a@4k^B4K?|(q^K+OY}9;|rq!b2+`diCK24?p?H>__hZeeCaV zJw4|1;YSBPy7MvNG0$T^J-+4fZ_iYn`TB|SCq8*{?UV05b@fxPKfUznm!4Vp%=6FY zKKs;j8P7fb{M_eHzcA~ChyRfFhX-FwdGUd>$!G8XWAY#Ge<|gq2VPEn`Jq>4zVgVc zb6$PywRx{SaW4DZv#;mB{^A=2Z@hZG<osK2u72~vTjsYuf7|-@H}6!x^ZmO!FtPW) z-{<{(AB_0m#D`Npy!%4Rg-1Wi{^;z-MIXQWiRqKC{^b1A4;MY3_WE?+XQMy6{quy+ zPk)j7#VcQ4^X0{_s=oUEYww@?{`tsXCjaGke@*}E*-KYny7-Obo1gyH{P&@Mzx5wU z|9JA-CEtGVUFCP*U-tiV=s$1!SK7axYhTg++4q}%i2UKezo-2BkslZS_})*IKmCL? zFb!rcwAG~UKGb4P(6Rf#7agA#>Va4c^WlO`Xubku;0J)t3w4OWPJ)LX_>vL>I|v<V zIxj4Fk??V$fs*gx6PPY1r2}h9@f~YQ@z}NOv^dpBJe0+$Hj{E(GPo%9sL+JR>Z+l^ z_;ag<%}9h*!&Y2=-Kt^yFRvQ5fT6l-s8*%(GTRmOy%SmfYkl(t=@5M<EBfxNpWR6F zxUdJ<uHUEX=gMksj*Cr+OGu4RPE1V<D!dhufxpzQy1#*V?<3xg<$*(D9aT+ANJ>df zNK8pi2=c3si%mAf(^;Q*EC=%q=-|RIR)&L%<KdW}(k1{F#}A?aWpO-0(uJ-TV_Ewt zY<S=RkdpDwozEGCu5fF|Ntk!WDCzo2l&f8TaO>8Xc)nT3kz}EL@%ZiA`L?Fn9ylUC z>t`f8@;@XQ>n7O^kSu{Om@8ZK2qYwPspzCk;&g)|Z9F!X<KyvwuQDHnFxacWyFizX z#xtu!c8+kv6CX#aKQImEi6;=MrCd{#%*PlTgkAr3{h_t^)@6moRnhH`Cf5gFiB$Jl z&_*}oPT65mw^nXJ#8<*?z|f6|Gs5kN>AsP!y~4quf3dKiaExNVdNwFNfsQMXwWP)= zRXzn~KH*j%Dt;2!c}Fqb8B%fF1QYRdM3EsU8+I%WNr)T1Uy+auyGTur+m2oe&Wcw| z?{kr#6uScv9lewn{Iu{JV9+bhk}`f~zZFM^%Ips1n>K^RX~6ekW`oOas1U7HxG_{& zjW$D>n}6D7FpJfq)n3Iv4>D}BJ2u1`mf+~TQ#9~?j3d@Oy`{{6&qfURCWY_u))+S8 zGb=j2Y%rGMa}tBA!s3h-gkoHHajpunlLW2`Bj=5yt%g$3;6zEY&I@;<D)1{ZE^zSi zX+cN+|8liDyT!Xuw}4x0R^1|<e&tP{l!$(n5SL8d>RzPu3HK5Hmhg9k_Y*!q_#hC* z#k)jsW0hM)mmS>hN2e5<)hS1(c=pa4+&e-yo^ca2V0D*O$<b+Vd2)y)v&lRMPjrH* zo3`9Lw6Km%Tg6+b=c2eYT}0N0veR>RN)A<IC5JBLv=`H#!TxjLFsA-YFo!C#l7kCy zm+%nEqoY#%q(48a%un!C;!`(%C^Gbb)bEk7O*jL@HZeG!MyT^)ier`#L2;vQG6wgO z&k2trkLsHqLon!-7Aof@m3F7&bV|EFjzm6zmmDlG^w4S7({t_m6Km0T17E)>UxF$J z<Foi@mh^dHVB4F^-zGc>M164RA~a`cy`QFZ?Mk}9zG+Wv8$W}nbHZPNsM1N{5(2>r zargc#B7DLtgl_}kuQ{&{fBhWpE>oPUljjj}C4c<~#Q!RP9XQLGl%Ndm{Gge76y^o+ zf-<>qO6!ICVAkan?-ktci^vGK`w}i%g-sW?drUdn1P2>9nQ7>B25;VZ9F`#8_D5X! zX_v~1nBt_E%)+?9T=hS)!2a*c$lU3m?+`sk=I#f5Gw~s0H_g9_%$+p<8X~Tw`8N>% zt2B=}1D&E(=LY;(Uy?FTo&zhN@H!Cc=T^~qoU95RCvg)@)ODO>86mw4H&<w*^Wfew zH1e{#w9(G&LPJ4ev0+hRR>q?IRXOK{H<1X#&0DyjNlazNnDucnD~(BkCh3}^vr0b{ z-a*DUg?E9Ni2MncPU^jgh+yBKqg|gPCRDLHI+cE<v&lM12bcd{lyXS_J>>HV?-PDN z_#xp1!jA|)Cj115*`=}qyCgf;P-VnN&!u*w17khD|H9l#eTHrO?hgYQ3-Y=@!{<Qw zd`YzN&pVb7NB%(>j+7UOR~H+K^K)|z)k(2&-7}sv*(+oDZn+q%u)sDozC@s}jTYmB zFa8}zyZ-KuR2?$M;;a}?YL4Oi+A(~%#6%%RsmTlG8e6QfA|=6`<fu+6cc;~uQp{LX z-n7Y>xVaM7`0@>Ax3dzanN+oTV~xAQm{7IZS-mMvOinI!R&FvGQ_YFa(oJr9D@efP zHYSO?4t{DfMK$10Nv)p&VVm5cbv9tOB-AdKa06BnbviKKCD9y-G38Wc7;NN{@CDey zt?-NwGxiwM^hSb@bvuFwS};<4iHDOV;VWFOl7z2u87~QV?O@Nrjbjv}&vV-K_&|lX zFaNL+U;pGoVK}9%oV#mfVGY7xkb@s}NyH2v2SbBLxl@&6FW=y?Pxu?*-wFRg_$}dg zgqI2bN%${du*&Ml%-wzG$plLcRZ?n)lGKq|$<b*tkr0|Af#=|ff>Rvs$d&m*a4bg> z#l3f9+C7OXDOY%xSw~`MxryD%<zA(Gxr%7&JV!~=G0)-m!IOmlRf$RtwHiuIIlI7N zXl4kN(U9J*Z(7T{Nfmu<7#Qe(K(TE?w1x)y&;k74cyLbOn=mwhM<Eb&QtFI^kK;cg z8QdLbqmJDl3O@z2UZw}?0IvBN7V${#qw~f=GfP%xI9Tk#lW;;Up^h+uFp^LYM4NIs zI(NCQ^B*MQB`G@Q<2sHc@?44yhBm&!OKm5fN12ZX72AE5tT0xaLSfp;Ok+cvB9@zr zQieLMokL~tBaWeB@YtY5C}MQ_&Y{caC19%LP>)+GIYN$G3Z$<dggC(n&Gu4s#~Ux$ zWfbW<j$0~%sy%_H3_WhC<Wx1K<m|9$C8w$sC1>!sWy@LzeO>76bl9>F<Y^NI0O_!$ zFccxxs?G_0adVlXRD0@&h@eS6$ux`5rgJ5xE0iJ_+mihe;e8Q5{Zx}RM54<lh++)y zMNvEnW4j1nB-}%&mvsI4rxE-W3sddH)S2{()gMJczg)gwHZtT~iw|#<-#!WSu|dez zCX553T8km&11xMPzDhwvJ>J|mulpx`t@SctiX;rdgT|;~Qq;exx`zb=BHk9p0H4q> zO&>}38(}PNo+qRQh%^jJNi%6eo0Nu+lepmU+V~bn`3^DAv-@FcJ`7nEbyCkBj@zIb zd6(w3<ku0n`h<~$qX<U>p$6V8L3`n;6<3EBrJ#;fd*LZV?M2C{+KZC2!(J5as`jGf z4B89k>{#red1FE%{}3x4>wvt5$1tM|{lk;c+c+fgDp?WOL_gYbnoeAL=o7*=VFE;? zcN|~fyWZ1uQaSK^5}xBQq03&5&vqB9uYR*mlP?LRnL=q=$}BdEvx3iBs$57h2g(Xf zahfK!ftcyYlOPE(xV$680`KKt=^en=scvx=Kl=_Qfv>4CM7Qhf=@EwRZqH8BIqLx9 zxKPINxCxhpgs%LZ^szAi6cys~ITQGHi{nO+h@3tli7=Tk1xOQ!0{o^+$j_<Bd_@jl zcP}?uEoK}K4gC#{f9Ourv?U;88h8XNKNB}p`6jjU7MhND_@#4O%vQ3^!Q~xcE)Z3_ zDv<le3i>gCSq7yZ8O{wEe0?ccKd<t<U4O$B6Y$E;123PD4h+=9Ez-|S@C7lEzB`5~ z;G1Xq{yP)Di69yw;^2^m&P3*5&9VZrom6Yq>0PhO_g3xtBfs?cG@UZ`2sz-{CM*gq zX0cMtJwh%5{c!3YmjROQZ*&Yh4>vwxK4CuL0wDZUwekxV_(=dPC95}RJ2y<B89#P| zrGgsu*00`D(qL5_`27w1q=EW7AhDgZ95MdF<I{qTG^SLg5aI+iwgfk13&#T*yD}dV z<<QR*f`_L`PXTjVrW8?x%R54Gpolxw*MtldQ3JD~4`5Yu;p;o<8Yn%6PC`@TH1sbf z=f<=g<@f~d`+ejw702om_*n9D&eca^`-D}LhR+uRD<yoD!zb|diNLA}fBW_c9WTy4 z>R~>CzdZVcPYL<^h)?*2@Yl`OeY7g4e6!%AHF2MCiumxwY#%MM1wJ0IPz-NVpCA$X z30nyd5FRGvI{-f6Uc&nb@%D<CD=%33X!fYgF_j4-Iwz%{N62S5J|USfiSWOjF#3cm zPlkN->4}fN{ZYQm@(G2+xqwhl*o&|SVNb$h!V<#egewTICR|Clitrl3YYEp7t|eSY z*q?A9A@?diVL0J1!tsO?2qzLwBAiS(jc__)3}GzcX~M?|e-F%-gcUF^%;8t!@``XZ zQ2h-6Oc#{yjAhPFFNpUD*C5p&$)T)92;c4iu@+KPqJF*=bhEpPN<xMe-j8@^;2RVH z?A|5#U?1otq2bV6N;tGjJWD*482&C4=om^^iRUj;fsUbsmH1zaO6exz#utbK9YYB# z@gl{S6NYvPC9HG_B~2(QdH)+!WZ)C&&Rv4fGXgP6mr#(;zXS2V99DRCN2SC(;*(Ff zFa(ttrAvgO693<$!oNEzrFZWVO2<$-$<LvZt2-(@Pg5;8cG%|sch~>lUH@-%*Z<po zdWH8G<#oVFUfWH5sg%MdLNlRASVmY*SV34r=`9pqPq=~5N?1u~BeWA%5pE=O5IPB6 zgl@uW!cBym32O+q5N;*hM!22uI>PG-cMwX1wS;wq9>RJ;FJS|rkFb%@PuN7*OxQx$ zO1P7-jc^y?Zo)l;dkJqK+()>d@BraK!b60I36BuoNO%+BQNm+{#|cjmo+P}P@D{>T zgtrpjMtD2n9fWrh%7nimyo>N|!g~nsCA^RDw}ihVyr1v^!UqW-B7B(e5yIaSo+f;h z@G-*23C|E>T?1p)lY~zZK27)x;j@I#5k61&0^uJBUnD$B_(#H*2wx_Ah459v*9gxM zzE1cC;d#P03Ev`ooA4dNcM0Dke4p?G!Vd{A5Pn4XG2thKe<HjHlv$X}v@+AlEJ9|H zGSkZ}N@h5{&{Jl;WEL&6-ZJYWv%WIxC$s)C8z8fRG8-hb!7>{nv!OB@CbQu(8zHli zG8-kc(K5dLVPj-AR%YX5HeO~EWHwP|lVmnoW>aJ~Rc6y<HeF^hGK-bj44K8rEM8^_ zGE0<MlFX82mLjuMnWf2Wrp#u^Y_`ni$ZW36u9De2nWf7tLuQ#W%aU2P%yMLwE3-VA z&6ioe%ofOOq0AP^Y_ZG=WL7A%B{Ewovt=?Xl3B6LN@TWNW-DZNwaiw^Y?aKek=bgQ zT`RLSGFvONbuu%`tW;(unVDrK%B)Og<ua>~nMG#nWwt?PR+&}G%qBCt%&KIzQDzRA zIc4UOnOkPnGTS7x%`&Tz*%p~?mDx6#ZI{_~GP_>Z>|nA+0@Z@*Kps#%$O~!!`7ri> zu4~*8nDVln2<B%h^IyKzL=nm_<tmYE*N(2gF2;ENBK%07t(>(WxY$j<WmqJ-D7Sh` zZ;<Q;Jjc)17P|5Kj`-0y_2=w}gYwH1%!d?8+C2z$-I}KYl$881BWpk!{@pCaD{}Ik zdA675<-a3q*}{L~OqGtuDw$~qNcpKt%Bmb8qpVFx#D9E@ZklmJpCZ#;>kj;iVaJag zv(~^<oJqki*RkD!`;H%Yg}xLC1It);2Njr&pESgZ7Je^{DqldqR!Ehn^DRn!g1>A> z^`!P_<3->MCjBZVsesEHI047@<K{{~yv(Q~fqcX+SU=K5=R0D|C0A4$_%%S1kJdwo zU+_2EsFKPWYq3~~MH83E_8~V~8g0yfWw)cL)p+&y`#R#e?x@|>9QZL!*2eW6`m2%b XAX4#PVayiGjBcy)^Qy`(Vzd7PsQxB; literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Base.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..78c6c96b32526d8be1dc5cd3275dbc340cd0b0c5 GIT binary patch literal 20046 zcmeI4TW=dh6vyY8XI}cYML?l~LILrD5KRje2wDnBK}fu~Nm?utC-{OXpN3Bg{C{(N zn3>Hwc7n%tqbiCoyR$Rrew#D?=kNEz&CrC;!(kYO{V)vua2m#XJqZI{AM5%bdTqi} zT^Z_qH$2wgX*dYIcy}ipY0NN=nTB8V*`DK5Gn<k5_BH-US0>rBjmG!1ZWHef^+Zqe zKG*jr`fM{DXx*viJkmRN5B1OZL441j8EZtNl?Iw|9A`WVw{+!ZcrUyXUJGx8+u`+a zM_1kmAIv`a`pd4@kI<f7b)xHs;kxGD6HYeDUV#*-j)gZA1hPS-R!)geG-j&%6X6Md z&*SGt-}>QK%?u~bV#mDjCa1=czD=~kzTf~;$9bUVr{Ocvp$VVr|0O+~9-l}$UXhH{ z)PtPYS5fMJyppoJF0671d>Hk7U$4jd!v><^r?MO;;dkk~TfisLPICX5t`0O3E4;|w zxlQ_AR-!NK(vzjdeq(VbaV4`nSo&k#-4EZ&;x}5E*++WD{DRGcO~NX%`c&`kBgX&P zz+CQuEm_C>P7`N6iWrTehj3eoZ(yX`zXa1?vTYwmD}HN%O}nZ%zvxnPGV^uwJsT3U z6QvRvB~m~ax?})Ph^*k5f0M-Y<EI1Z!}<QMj%=(8v)U3s%r*$u8mLZ$R7P*aDShd@ zMIqN3qd&37K-aNLSOo0Wu|8oFn1MKsc~gvOG0Zgbw~>A8iuaI_ZeDI5jGN}lJEAh& zf-)ADdYtGqK9NPmb66~$c+}IA9^1YviW7Acja%f;UGf2sWP2LR-8M%r$ER(8x)$dw z=it@s&A#|DjFiT^8;xB*%$Jglp>=J)+tZcOb9YlaWjJ+Kd@(8Yvn2Yh@RQ_uBHALq zSfnm)3^gLRV<jJmJ?_XJy}guo>6-uY++HOP9zTIc&e2U(158%i9>8PNGPZ5&l;yW@ zwXGqqHl9tB?ZZj(V)&QGqaFzibahqPdK(kB!^`F32GK?Fa-MD;w{=KVnMJH$ewN7) zH!B6Jfp8PN9L(}b;(d=6t%f+2hGId<Jjp-X6s}<@mZ76%DMLX;mO|8ot-*fbaq#Cr zZBklFu2)+vMxN@qCktzJ|JOcj2BTb(_GURjn}1&Kcor!*ikTf51+*jwA-{l{Co%h= z5<*-$3dE3yvg^;HZNDAf*IyI94tMpMs=#=T_KHNpd8RmjOb-9UD1ED`)uiaqEq*Q` z<XV;|cSR!C3)sElTgu0>vsq=F88?M#iOnLqv?0B?Ou0Ofzg|S5Z0v1G9a8q7L&|If zu|_Ui&L6*P6t6|^9l_@Hxl(o<Tj2A|<ZSemb7K{A)sd=;LFPFw*=twz>GC))GjlBp zU>~W{QgNYvOPwX<9#*Yk3^0!M+SfZ3WvkTW_5o?=^1iq9^dse~R*S>8F%MMi$>x%m zGCx&i>@n5oB=<Y<-uA(_Ic~69N)6s}P$B^IbYHY{o|tSBGcbm$X=l^uTbCc3fp9xG z(vA=kPmCM6zSP!>oDa3(+79Yl*TGPhq|^taJ(U~wxBD@hUnliz!?hjM&wB{ka=%$C zR3EbK;J~RQ-}YP0JG&3atq!{@?E03wT+8^`+@4%~rR|_lyF%MVA=?kH8L{M)tOwC{ zP~2h<I+(W0Ce((jCiNFpnms?sbF@V{Mmy%NvqmkdoN8~9uD5cJ_@V3}-7q<Bi$<;& z!XE@(>X2CsXM21dXzz(`^a37=rU|C<YW<a)qGPlhj`o;ZP!U1ytEbX!nD;E@o^=%7 zY;q`<f2J1Hk3>PQ(R>^}3h%1gL=80W=e(!feEz7qknP5f0_!JuOSP~s!=J_OsKYjI zTy5wks;7@tsi5y6=d4j|S2gAQeqCtx=E9Ng<Phvhhph(~n~3$deGl7DVE=>8K&;PG zX*u09cn!;x%r}mOAJt9vP3W6u3|W)c#6OQ$QjfLkMy?aNMl3siy;yt^JJs7c<2)Re z8=YgYx@!M@@zE+jsjd%S>3zh@wQD_H{K#u-tD3J4wqF%>D{6(-yZ^l~&GWgWm&-qq zL(EA8t2+*~2A|LatfbZ0+|u6&tb9Dv1uFN9{ATvdFx=2L>xQNNMn#h-|Gd)(xs0J_ z=qb-x&s@$~`0ZNP#?sdMPBH+@^QqAum(!rWttg@H3s3s|ath>@V!6>d2CLID);4+Y zDiT%_uFy$pzKZOZXn^`P`sci34-Wfk90bd}@(LI2+=|;-YVP)gb=;M;@6AJ!Q+Rnj zDY;ihXy<8(Q3bib5oroF&+fUJm20GpyRpYSRWp)~e4{z=d1%uE&BC0ye9gO`<GVVE zdJv_>AfXRGkEy%v)q(=+1ufWlA=<iI#9G+1uKRVMfx5|7*|%CN;T_RP+ufq`*y74j z0l|5r>Ol6bdJGZ`>|3psc7=62ck~loc2=W(w+<waqAsGd*2#)Z#E0S!CVI!7CYmfa zx(=l7QPXKS`b5wW^9R;Tf{nNRwsZn!NBP3pY6M<_Sb~#!V9#vWr976ww&6cgZ*{VX zDFQmXdzwdeFB;r+xc9v%>!(7EKc!bnGPZbR9SC<79j23;>BJ@7_dx9ZYea=d`on6o zdu4S9lO-%#9z%_k**E`nDtkQ1^YrjPpG7K*=CHFegQ$<ba$6#kRxp|<cT()zUrp@l zNW$JM>&BL(crkS^!s|TqI}oicOR-aQ7C~B<GIJ1FQ?VF|GjwyM^LM5RQ{BPN*c_Je zd#_I}O}SOsW*F;oC8tQPffi4coH6>Qodw1dabf4#kw%NDp!ivWQDFlZi#M^nBUNL1 z(Vq|r(|Lo(u#-4wzpcppe)iFxvo1d4zDbMc&&&Y-pGJ#<e?ijH+40ovZoVgV=73lG zY)O}lyrdXD9sQa|v2{wUO9JPw$T!V0m_NZXJd?~>R_hXi9QrQ`kb|X+mNOFUviPq! zu!nrHULvZF$nC{teX*onB~0r`$Yb%^&&9ayxR=Eg_h;OBEGcmw=ULKapRa{Yg0cx; zNUp4hG4I37F=>xk2UKO$iJqm%vxKc{WNipNS}1#s6I|u}TF_8cYr=im6)L>uSIw_s zB~GKCO7XGXao<!Y*CID_Yj}RP^;kCk#e=>MC(8;#quo3#GW??El;nQVxH{1NBtGS~ z%)i%K%}$Gw#gVz;W6jH&{8~<DyKdAJ4n(V``X^`eQ%hDcz@~8$K4}1Y!Y?Y3&k<#C zcUSM@_$2W^PjWuNwTU_kU5`YTK=bD83G4GF7vQQ(yy+D6O0HN<#&SF&0fQeHSl#d- zLm_MBd@|ZFDf~z7xjpiINtaQ~mupHNaTavG$gi;^Ej3PEZjSB2%Ia<Z4=bM|$7(mq zzVDEu9pu@jD~&!;+u`@e@?AjpS-#hkXOmtXgx;n0_^zV&aiDQ%nOC>cSq*YWkFR|# v-yN}i6rdbvcZG2&0yletu44tt)rs$Y=g}nMT9b6#t%1jidE~X+XlVK`h!zz` literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Info.plist b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Info.plist new file mode 100644 index 0000000000..1f290ce9b1 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/Info.plist @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>BuildMachineOSBuild</key> + <string>24D81</string> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>Sparkle</string> + <key>CFBundleIdentifier</key> + <string>org.sparkle-project.Sparkle</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>Sparkle</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleShortVersionString</key> + <string>2.7.0-beta.1</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>CFBundleVersion</key> + <string>2040</string> + <key>DTCompiler</key> + <string>com.apple.compilers.llvm.clang.1_0</string> + <key>DTPlatformBuild</key> + <string>24B75</string> + <key>DTPlatformName</key> + <string>macosx</string> + <key>DTPlatformVersion</key> + <string>15.1</string> + <key>DTSDKBuild</key> + <string>24B75</string> + <key>DTSDKName</key> + <string>macosx15.1</string> + <key>DTXcode</key> + <string>1610</string> + <key>DTXcodeBuild</key> + <string>16B40</string> + <key>LSMinimumSystemVersion</key> + <string>10.13</string> +</dict> +</plist> diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ReleaseNotesColorStyle.css b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ReleaseNotesColorStyle.css new file mode 100644 index 0000000000..bcd84a2056 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ReleaseNotesColorStyle.css @@ -0,0 +1,13 @@ +@media (prefers-color-scheme: dark) { + html { + color-scheme: dark; + color: white; + background: transparent; + } + :link { + color: #419CFF; + } + :link:active { + color: #FF1919; + } +} diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/SUStatus.nib b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/SUStatus.nib new file mode 100644 index 0000000000000000000000000000000000000000..784ae306a5b47093a2b7dd613eaa9ee063cef644 GIT binary patch literal 12654 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 Q(pXBER?_SR*}R4P7c?o!=l}o! literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..55b02304b5 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "محدث البرنامج"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "معلومات عن الإصدار:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "تذكيري لاحقًا"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "تخطي هذا الإصدار"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "تثبيت التحديث"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "تنزيل التحديثات وتثبيتها تلقائيًا في المستقبل"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..dcd6dde094 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "عدم التحقق"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "هل تريد أن يتم التحقق من وجود تحديثات تلقائيًا؟"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "تضمين تقرير عن النظام دون ذكر معلومات عن المستخدم"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "تنزيل التحديثات وتثبيتها تلقائيًا في المست"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "التحقق تلقائيًا"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..7ba248ad3f4368f1824a64625728545302da71cb GIT binary patch literal 5104 zcmds5?{C{S5WP?QQ1pK&<1BNl*zufo7}8=`?O3srVqLec81_+doXn`}82(W&|9rRi zj$%pKZquRFh7CbvQKZPb<9m0H`txl|60!13u5lgU`Wk;jnaBd|sm$=?jf~_1&o1!f zTyAh>{}rB2a5u!513XKlC3mDId-4P=cOA(b_XAmC%uL@}$XI7s$nR)doneK$xvuv{ zXPaZ@YxG#jv38Eu7}s-LhghkG=ilMKCUw&@d*&O{GIi57T=P)A(AE1ULwnaW(YMc@ zXjEzv^mg=(Cf@9sGxHR`R(k09*4#(CtFyIrwwgSKUw7ag<Mc~m_XR9m!NxIubB&CP z)s^}=SHBq_`e5Pz1YR(r*K&y~qqtD78)(t1D~(}WzQUiCr_#l>9MSa^xgQ}GiAiB| zVouB<?C+`lPtgBqwtKs|i+{&*H?}Pe_2!2y-nbD&N$_D$T*R%U@FVPJ*^8dWZGbqB zfX~G{tT3K`XpApm>kJmZkeB$IB2KwRyQ2u2>6n%7BlpK6aT$%Y=*@KxbG$?3a*v7p zbKDdC7VR<u96xgOktgvx3yid|bD}8cjN>)Nc)tUJ{rVrfo@3hPs!OanlpU=9E38;z zWm<Qm_ZF>@yh1;tuxTT0CT3%w$V8%IfCzDiyyX+Tzmo5x-Q0#qKRWoC8OPNI7}y7P z$-*A)zXlf%l-a4di>H0XbOP2s0z<9bNB_QZH9?CkPL%Cu%J&R;;Rv&|%mef$<{{ql ztnfDQof>d<&y?}?`7&V#5n_g1;dec=1^hG0<kx1d+liui2LF84inCb*KAz#<bEcfZ z)-%Ar<=ZAM%WQ}}<-WuovVEJER?7GZ*r;xAnFCn+SRt8huEB{=>Busd|LOYzBS!dT zC1vGgMF@GxjdWS-HE`f_+5yHr2S=HG$YQc|rdcS*9IT79fsADCV-*g$)KxYs2dHSf zKt~JcVNPW<o2Z5veytqgdaBsrU4|M`2hOZCi++v{ydn?zq>cTko1LPSV!pbLC)dc` zCh8|ED(#8u&*wzn?a^n3r*dH2)^oMrg>AY2)1vP5hL8F#ve8KqtGG7*U*hU2?hFVV zC~6(HeN=N`l67LF*{`a&`dZOOoErrucY)D|$cr7V9sf<tP7&1^c>f`+TBu3tk&89% ze+Y$5Fxb^X#wA6ajG6DA9fHq4p{hL7`nf$DRLAaPcus{e1bah9+k>5tluK1*m3UoS zQjXiKSXUiYS#^q7@O0#KV^Cv1YkYzl3pR%8XXX7$$mJk^-l@v^A90ommZiW|9jxqD zrnwjuuhpHVEypj|@=q&<7s!M`fd}Qx9i*kc@(+rg=4pf3bBOCJ-1}2atQ_W0+*W?m zc{!TAAwtH`oH>+&JAL0aht;(wu)|@DD_<h_(57%c3KqNX1Itp*>4TkA+FqY`kYjiP zr{<t`r6!0^)n2b=$l00d*Pt_;21zhu7ijqsGf_?4)0t9pj5&{S@9^vAbtlI6&qj@$ z6*F1!*3rfct5EZ}8QhAg<~{Fmb(t<cox%dd+^NY}5BqNz+cbrb0nY&Ym2oOq>1$E> z+#Yt0C&KE^94`wMe4;gxU!<tJ9n1OUYO31U@0#!NYNUhyDVQ-*o_LK-hLBl27jr&- zkD+2OpL5+gc%2g*$MODrys6G(MP3JPyULPaA#0k`-woXH6j#p5jtLnQ{87OXf0FIL YLu_9aCK<7>-+;UtC!%?7YL&G3Uxa|<$^ZZW literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ca.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ca.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..284cf6dc62 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ca.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Actualització del programari"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Notes d'aquesta versió:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Recorda-m'ho més tard"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Omet aquesta versió"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Instal·la l'actualització"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Descarrega i instal·la les actualitzacions automàticament en el futur"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ca.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..1a8d279b47d2139d970821576085c18caf0c6312 GIT binary patch literal 5966 zcmdUz$!=p+5QfjX*KB%sY0;#+)dOLRP-93VrZ6P|A+fpf6ssM_T-!0>X?P1>hb`;u zSOEW5mFjb@odgNl0u;seJwu%u{`#xV{qv7!=~1fE=jlwJU45SFub(DquIFiLG;*Fs zsi(1?Mvl{kKA9hCbfRB<t$C`kN9kF*lkTPa={-I9=P1qeyPFnT)7Y+g8k>Z9I?;1w z5|;Wkv-8eP+DwwqG*hKoXV-cf>+@KjeVueq<L~OfebR4s66-4MK_{eK>ZzyinJhu# z=t@c@8+}JD?dH<2mULvlBOC6>lK+=azL#we(s#0PA&YDC_4R&lv|?hOAcCc@@XyZ{ zx2NghR=vCpZOgdr8J@cG!bowS>(kLR*Z7Zyvw=n$S$~j@^fwhoYK`|($1v8|nx!EW z6b4oo#mdT11-j1CQl1;@yEZw2)7vs)V!N)x_iNRK=7{2n<bh<*B{J+=n;GkrN;%}K z#?s@Zt(t3OT_5x$ll{c!H^kNR?^oM@UA8<hOp#l7i9A9>a{Qs?{x>?C>~!?yjN<H{ zY%eF5F*>};|5g75=L`+QGs|jtrZU_j4@Nml1AW3p=rc=?^#nHzO`F~Hmj0^rX?p+F z+Fh+-Y~RM(T;<ZRrsS60^oB7R%!jRS+jHBBO|oX3*Ok8}>25{Tj_vFccRjO}an>zl zJKA}Sml|>JOf?EOgOq_h39Ey)%Df6rpJ)u!!sea}5#3Yc%0{ajn+4gN6?xQV;LsX; z5d+SLYtg>s){KNI{UE)u5#3;;uJlJ6G{D=)=%pQ-;}?kRUX6`p3AzlmZhL&)(P{W^ zsqb7(oJBHPf&<p8)awP3K9c^P&5mKL#9LIhQu?Ky0&7&z>*^MA%PiU){yNf$_)(`F z4@5mzZjj`8NFASR)kxoT23|)>jQUFFSdW&+7Y_T<cmkY)TjYSiTJfaLqXRXZtfs5T z`B-`pW*iC~U<~}M>V0hcVJYuoyer@->jdW9<2#bJ&R9N=#)sw)(BSMA3`P7J<<W`e zb3ZY+(cV_Exo+`lq^MnVP59Vr9{G%w5wECe*wD4r;2e9mA0c|JtQUR-Q$(IvG^)|m zm**P!ulfE1=?7o+rEA7!Ir@uc>Xj@#)QHz;c+_o$0l=?UZP)<B544+pBKI8h#pDA_ zUulN;dCeqNQOEXdSMEY0%4Jr*S!Io*LqZ$$A^Rd%Zp#N`l>D=O%U)Q=wu;`H?8>WQ zr+;G6<{0&3o$t{bE2?lDk*m_zh8}7+NIEHSRPJyb!K7ZZiReh{N0QTy0X?hpB^8uk zpoe<v`5(RRj-7m}8P{XqR(^K1r%~?FZ@{Wx5{}6o@M@RlokbZ1&<VQP=Uof9nn@nF zN$d`+mR8~yKkF;4MCXiA{8U;EW3b-ur1Xci^#beM+xE})G1|5Wow-?+-YIjhT<uZ* zcX9fK`sD2OJ>y{B12l?S+moBi4Pr|dO6_+BEWJ{4E1lR>zH}ea*K%*e$B|p^!)9x` z9$1B62z#LTeu|El3`Y0pa6mtqj!on>9r{cCj{Ga}Fth3w_w~pd_S3^(7}x*343JO8 z`P&OWId}U};j_m6UT4wjtIC@jeL3urcli6D{1&K^X5P)_o|Z~R^@0Iz>ZHhVpC^Nf zUT|4so;Wb6NP4V!?hkar^joZT)+6rT$+flOOz-7NZ{&Aa`9|UV;WNKWTG+Y0d&H4! ziFXTCdLit3cU|5O@~Gc>Zt~$}+Ftjc^~B$>SNk^Hk&ojoS={72BYj=j6Kswpj)WQT z_@SYIID!G^3otOXT#G2iOyItw`Kb^C(x{7m2LXj-B-xBS@J6KqSy7Qe9lhCVF0qdF zQOVbDFXfG8U#E~4{-z|e1{rX!Irs~Se$(R@K8AP6Ewm5a!MA5nXqz(#`A@XcGl!in z<M#W?D&h~cD|eY>2yb4vWuf297?8JIWls=(_ID6OCNoxenb9`yMUR1xgAuSjszJVS zI==ia8fOi((z^*d611GCUJ)zsz>c^@5+j(neR|n5JNw?PJO-=_1VzS{S^9}2(}55* OkE!2cxTV*xGV?FJz3zSh literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..ff7d56ff6c --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Aktualizace aplikace"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Poznámky k vydání:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Připomenout později"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Přeskočit tuto verzi"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Instalovat aktualizaci"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "V budoucnu stahovat a instalovat aktualizace automaticky"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..6c971d19d7 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Informace z anonymního systémového profilu pomáhají vývojářům lépe plánovat budoucí vývoj aplikace.\nBudete-li mít nějaký dotaz, obraťte se na nás.\n\nToto jsou informace, které budou odeslány:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Nevyhledávat"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Vyhledávat aktualizace automaticky?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Odeslat anonymní systémový profil"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Stahovat a instalovat aktualizace automaticky"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Automaticky vyhledávat"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..8025bac12afa22be5155648d877b5b002f2de237 GIT binary patch literal 17266 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..271ae308f4 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Software Update"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Om denne udgivelse:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Påmind mig senere"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Spring over"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Installer"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Hent og installer opdateringer automatisk i fremtiden"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..0bc75d70c9 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Søg ikke"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Søg efter opdateringer automatisk?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Vedhæft anonym systemprofil"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Hent og installer opdateringer automatisk"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Søg automatisk"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/da.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..07b5827d5f7fb598adeff46316c602320c13fe2f GIT binary patch literal 8334 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 N-+JiFJ(=a$@h{<+aXtV5 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..93e067a3b1 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Softwareupdate"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Versionshinweise:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Später erinnern"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Diese Version überspringen"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Installieren"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Updates in Zukunft automatisch laden und installieren"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..b34f5a44cf --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = ""; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = ""; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Das anonymisierte Systemprofil unterstützt uns bei der zukünftigen Entwicklung. Bitte kontaktiere uns, wenn du Fragen hierzu hast.\n\nDiese Informationen würden an uns gesendet werden:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Nicht suchen"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Automatisch nach Updates suchen?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Anonymisiertes Systemprofil übertragen"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Updates automatisch laden und installieren"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Automatisch suchen"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/de.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..e1dc7f159086186b631f2a7f37a72c48362c3ae6 GIT binary patch literal 21514 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 BOJe{4 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..fc8679d8e5 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Ενημέρωση προγράμματος"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Σημειώσεις Έκδοσης:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Υπενθύμιση Αργότερα"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Παράλειψη Έκδοσης"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Εγκατάσταση Ενημέρωσης"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Αυτόματη λήψη και εγκατάσταση ενημερώσεων στο μέλλον"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..febfca22e6 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Οι ανώνυμες πληροφορίες του προφίλ του συστήματός σας, μας βοηθούν στο σχεδιασμό της μελλοντικής ανάπτυξης του προγράμματος. Παρακαλώ επικοινωνήστε μαζί μας άν έχετε ερωτήσεις.\n\nΑυτές είναι οι πληροφορίες που θα σταλούν σε εμάς:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Κανένας έλεγχος"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Αυτόματος έλεγχος για ενημερώσεις;"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Συμπερίληψη του ανώνυμου προφίλ του συστήματός σας"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Αυτόματη λήψη και εγκατάσταση ενημερώσεων"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Αυτόματος Ελεγχος"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/el.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..805e4074cf333e240dc666b6d987f95bb5c8457e GIT binary patch literal 7972 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 UE$!T6gtq=C{SbI&%}4b93qvYz+yDRo literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/en.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/en.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..45a4cfc063 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/en.lproj/SUUpdateAlert.strings @@ -0,0 +1,18 @@ + +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Software Update"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Release Notes:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Remind Me Later"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Skip This Version"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Install Update"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Automatically download and install updates in the future"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/en.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/en.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..fe430d94b7 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/en.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,27 @@ + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "176"; */ +"OhZ-1K-DmA.title" = "Check Automatically"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "177"; */ +"cCJ-V0-aTi.title" = "Don’t Check"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "178"; */ +"gmh-T4-BO0.title" = "Check for updates automatically?"; + +/* Class = "NSTextFieldCell"; title = "DO NOT LOCALIZE"; ObjectID = "179"; */ +"179.title" = "DO NOT LOCALIZE"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "180"; */ +"gz7-LM-gNf.title" = "Include anonymous system profile"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Automatically download and install updates"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..ab59ec28f2 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Actualización de software"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Notas de la versión:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Recordármelo"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "No instalar esta versión"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Instalar actualización"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Descargar e instalar actualizaciones automáticamente"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..885bf29814 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "La información de perfil de sistema anónimo se usa para ayudarnos a planear el trabajo de desarrollo futuro. Por favor, póngase en contacto con nosotros si tiene preguntas sobre esto.\n\nEsta es la información que nos enviaría:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "No comprobar"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "¿Comprobar si hay actualizaciones automáticamente?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Incluir perfil de sistema anónimo"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Descargar e instalar actualizaciones automáticamente"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Comprobar automáticamente"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/es.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..3e860d24b6891f4ad0f48b1376ba95702f3f3a65 GIT binary patch literal 10262 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 LCq13~CYa}6=7ibp literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fa.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fa.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..342f6584f6266a3a861dab0644fa63edc13e65f0 GIT binary patch literal 10492 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..dca6e2e39c --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Ohjelmiston pävitys"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Tietoa päivityksestä:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Muistuta myöhemmin"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Ohita tämä versio"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Asenna päivitys"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Hae ja asenna päivitykset jatkossa automaattisesti"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..04bf32bed3 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Älä tarkista"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Tarkista päivitykset käynnistyksen yhteydessä?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Sisällytä nimetön järjestelmäprofiili"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Hae ja asenna päivitykset automaattisesti"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Tarkista automaattisesti"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..974473a4ec2951434135f6b19714483cf9bcf924 GIT binary patch literal 4602 zcmdUz-;UE(5XR@4>)vqZqS6AY*lv4Sp>_*MAVj<BvQ$XDG1+W3Y;YpmA>?iPaQh6s zZ2SFYa`165v=Uh=WW}+M&zUoSzWHYCzyI2^TUOZ5HrIKi^Gh9LtE|=i!fK7YvZ<YD z>_j8aZLKrUCmOAEHP)Ql8oOnCcEzsQb^B3wah_VE>ya%rr}kB?&0IojXSy$3!b(?- z@At~3HIh8nlfp{vUFvS8^K+fY+Uc6c@94))`}W0Vw{JIBv2Ljyk%EjyYfDK-7Lt%v zNIue#Ug(Ssu`BYwmmODR&8Ov;-(~F$dnU`4vZ-``5Bt8>j7kUonQYl#*&iOe+jjHP zJslb!$n)R{EVVk&K92~o3a!5L7>8zgjP7Y=Bb3#$Xdwv+J*~!H>%Op4$*A>zWXC!d zLNbUL+kvB?_Bks@B#24aTxK&1Nd?O%lC+XOGyRt?C*ksa8BqDEAv}Lpto00=&m<4K zBX`KC-~7qU^DfPrNsn`%)#~Zn`k*Hk$BsYzrx;esNY)Hz$_>d{YtOYL6C*s7$A`R$ z7@>K@=%zEm16eeaZTR}!wI%n$_vGEeF0_`cY<-8o0lK3Py5~`TvSEzv2qc!8IN}?R zQe-|TM>cVr3j@Jk#J#bvbqAYFT+@B~Mn_?f?58(#_ce#H$3E6)w~z+M4YARlR$aGX zq`nQiW7z}kK{QtS&!a(fI_(l_-{#TJGtG|`qljWI2{~u>LVL_q5lYQ~Lkk_j?ZLD| zm_c?Pxm{X~eXuokiHx2r@4Na!g^JiyZCcHk>K}v!(`{#TwP6PAOT0nm9Xc;yGx>-d zs$MCyP>s+5B!eVsUx$Ii9-{FW`;)2ok#%G_-o|f8E|mqPM&Uj32wiJOI(j^F3tCAn z6oa+>=})N83E@lS6A_}CvBTxnuLIZr{l14osj+x1xVM&dBlkl@AaN`8Y2>rP+=uzR z6uX>jpQX-;Ll(NLeH33Y`YHPSuB@u$CnDUT?2&AHDNL1@mS_3CMXzFZS{zC%I*FyD zc$m%GOVp75aij>R+)gz<&+*2-EIN&!$YWpvJdnF&AO0QEeX5*eZO&t?kJ>wONzuQ~ zC5KF=8q+J|l~cuwKB4O+f<4J{s?o}|j!FjhQ+2uLtGezY*_PXsTSSF@w;k;He^_zf zyRPlbf0qr1@=37ua5RRk*j-I|C<H`wU{P4U@{a3Qc@~zZi-4&*9CaNl9R5Oc3gO~H z{2X0%XuMrN3rX5$>yM4EzODa;uI*}uZ-QT=SC4pYXUXm&^Naei&g*fP=i}~nBGz=K zRQbr{zNb1Zo@yTb(9|=S$S0q`CiD^Drytkd>3Q6HBe}1<i9RUr=AKHk=pcr8E$ZOK zN6;{#tKTK(n1Z=3bilwdE06Axp3-9lWAaTRD)>ZKysMmTxDqy{79b^c7p#GYdH+py z0^S|25xf^r#ky{JrnMa+y8bf0ibUs`_Bz$f_%;Hcqh)1Zxir3}up;%0AOnQuOxSqu wllVX0-mezy9`h0&(;I6Z=_AMBQg}??H1Gi(<Wxu;>r7W4V|atRxux#>AIxJ3_W%F@ literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..fd8042edc7 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Mise à jour logiciel"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Notes de version :"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Pas maintenant"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Ignorer cette version"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Installer"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Télécharger et installer automatiquement les mises à jour"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..c059f185ae --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Les informations anonymes de profil système nous aident à planifier les futurs développements. Contactez-nous pour toute question à ce sujet.\n\nCi-dessous figurent les informations qui seront transmises :"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Ne pas vérifier"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Rechercher automatiquement les mises à jour ?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Avec transmission anonyme de mon profil système"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Télécharger et installer automatiquement les mises à jour"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Vérifier automatiquement"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..8a9ee0479725fff158a55300a096f3d126b1fd0a GIT binary patch literal 11366 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 UlJ|dC_0MOIZ~_;#f0u~!ANVt$Z~y=R literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..0fe2a96799 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "עדכון תוכנה"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "הערות שחרור:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "הזכר לי מאוחר יותר"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "דלג על גרסה זו"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "התקן את העדכון"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "הורד והתקן עדכונים בעתיד באופן אוטומטי"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..61f47f059e --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,27 @@ + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "176"; */ +"OhZ-1K-DmA.title" = "חפש עדכונים אוטומטית"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "177"; */ +"cCJ-V0-aTi.title" = "אל תחפש"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "178"; */ +"gmh-T4-BO0.title" = "לחפש עדכונים באופן אוטומטי?"; + +/* Class = "NSTextFieldCell"; title = "DO NOT LOCALIZE"; ObjectID = "179"; */ +"179.title" = "DO NOT LOCALIZE"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "180"; */ +"gz7-LM-gNf.title" = "כלול מידע מערכת אנונימי"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "הורד והתקן עדכונים באופן אוטומטי"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "מידע מערכת אנונימי משמש כדי לעזור לנו לתכנן את עבודת הפיתוח העתידית. אנא צור איתנו קשר אם יש לך שאלות בנושא.\n\nזהו המידע שיישלח:"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/he.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..11ec3bbb2e936a81ed08a92117fea88e4731da22 GIT binary patch literal 17530 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 NQ8}`zf!}Wu{U0_JDC7VD literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..60525afb3a --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Aktualiziranje softvera"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Napomene uz izdanje:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Podsjeti me kasnije"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Zanemari ovu verziju"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Instaliraj nadogradnju"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Ubuduće preuzmi i instaliraj nadogradnje automatski"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..665e3a1aeb --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonimizirani podaci profila susatava pomažu nam planirati budući razvoj. Kontaktiraj nas, ako imaš pitanja o tome.\n\nŠalju se sljedeći podaci:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Nemoj provjeravati"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Automatski provjeriti nadogradnje?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Uključi anonimizirane podatke o profilu sustava"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Preuzmi i instaliraj nadogradnje automatski"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Provjeri automatski"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hr.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..2dc1a9184161bd8d3711f6d0f562898483e68c11 GIT binary patch literal 9832 zcmds-U2j`O5QaBbNR@hl#7*x`D@viFErrVpL7)Yp(hoH$2#FiVaT7O=ZP{@{BhlZ& zF9qIb#?#$%e43BSPAQ6dYM--bcRt>EXLi>A{Cz83PgQ!DM!I%&-Pd0)4by?XkJChV zUZj57*1c`r*-9^U<@t{84)v+0F*kMZdb*V^q$}xa`c~ik+D}t`?xvZ>Ozf?LG_V;C z(yqQ&Hp8JlO>MmwHrrG)kMyKU9j)Ebw}Gx(y7siv72W?@|5)jE`gp$Dm-A20ZPtVN zYBSB+Nf+0Krrl7VpKD&%=()aC(rILC&!j8QE3Jq|M_QE;tanFxU66L~mz{o>KA)!F zq~A<hb<85|zL;plP=EMy=d}gG3xAm}Z>G=A-SV!%W~(p9(reG2e6ZF_owd5((D-Wl z$}r)Qmp;5J4^M^4i8LMyA%V67-T%XI_)N1;G{;7Is=u*N($W21S~m<%Y|Nn{7?cK< z!BC8>G%MH#Ux%{mK>v5_Y2f&{hykOAdV3N7A5|~)4E=WPDKSK>5rMzjlYv&L6k|SW zAUXCVIZ_hgO?i-$NcR=LIhBv6b4?bb2Prl+W2H5*AU43hon!q|r9b6EBxM}`SBgDk z&MU!0J?VX<D>EYLyYT5SIX3oXgFsG4GFS2!mfY1RXPc&_vCW$~a&;buHHm(Zx^G^= zvhXPg!S_h*XssjIIXKV_OK+LoPv+n3w<f<X4<W2^B(2;J#6S2!H*gNekAxz;LEMeR zY!mx5)LjtfY|pi`)|X}QAR68<%rX{6?CO7f-Nf6bMSoj7dCp`tSOyM&SFw0yJnQi` z5{^8Z!YkACxxSIzb}W)_r!VwZrTghSUF)c6;)=Xn=0vh@Jz7SMSUJ1w<Ok$No3lvn zV{c14w0Y2?jgNrAN*yAvkBrN%CeQBY6Y-uGh;4jNWY?nt-o*|)!^fbDnrKU(sf!jd z;gu73f|<*AoRh%^Jjsj$^MCeuSR!oTywCclUC$$Xan+gD?4`?^2|V~HzalB9-4~8v zaU}D4u(ZS-jXBiwnPfgL<J7f_d{bJ3>JEf;jXiT!cb1$~M?A4cj8Es0u&>#<cVt(z z7)dK^vZa-=!6V~~$U9V>uT(Yi4%a4sP#2yiYn8h9wVrbou};(zr*N!um4EY^W+<sZ zAi3QU(Bz-<sbT(qSU*jTRC<m0cgFs&A@<~&snlb+=zA#4E^D_K3<ui|)1``v1a7PI z?zUw}?laE0D?D?kJD!=wdd{8{K6<8jV>j!WH~8;{MV#+Jy~ZcTiN;_%Y9;ubik-Eh z%4^?wk{7*JrMB9UWb8*+b5C#1^gs11yU8;8d4v6hznS+k9`{IuK1i^q8M?36K>s5> z$<;OMUtHVMmC6$q^gRQgVRLNCJ{Hay+H;s|TiQ}-)HCYxyt;0y^=uE*+|%cbkN-TA zHh1}#i)!GmtUz{zwVjdOYR;S3s=i|nmZzp*o^qG)t~3nvfvdKhU+RmD*yA|80y*vZ zZ87e_0J(m3)}Uel)5ILeCU$b)hj>IPuYSn9eT}AK_l`qZSNO=qT^6`~teBwUqH@8K z9;qE+nJy9YQ%exd^rUh{35Is`Z(E}nL3}ZTSLq$a5BJzrlj(QO{vWJ)>~NR4vqT<` zya5Zg)dzj64<76J9vq;V*om;pRO5p&%Iv?&SUlM`^fIsvHo*dA$0B?b`@qsytM>_v zhGcXxoXvBkM}->v*6s&(RG{X4^n>@J;Pb8!-Sh%kih8+k?DsDGfMoCwna;DDXBu+f zq#@b+Xn!=DNW-S(OZ-!1u<P4?3$|HA_DFRPR{c=@2r@v}vyEZA6TYh424kGG|BFmk zcKT1sbD5EUk$%VTu*y9CcKL2q+3tJuO;>&&6glRRP(WSkyXF4Ma&5VKRqIY?>pm8a zdKQ;tn>SUXUKOm)N}TX?=TdO45MA{$f~M({sxCLxkJra!*f#8=5pR{~-;__h&rQva z_Pon3KXA&t)CJGvHSexRMsuH0VN#9K$E8!l7<e^$G1L%kd9l={sf%~4bDnak7W)a@ zKYxwx*=a{J!@NnKC$eju?}MYxqSf*$@So(R+%*H^-d}qnFP9yqjIQzx&sbDtpsB3H z>V24I9IqZ{rIge?ixqN-vT}6J!e{Zwx~%0DGqL7<De4VQYMCdZ_F5{}qA&7Pc8l|_ zYx+(v?LdEiZqPg*BPOp|gb|mEb*5L@ReGXRw+&l2GHZ^(pEU>3O@4ZyS(vl5#)h8y znE;;6Cj!mbS{@1?sYjm?=HE(QTHZ?FI(p5%hphV}OR*upU>UleSlYXuASvRdL`t1U zmnZLrYQWqbjy@)F3QDNuy|)5i)Awd?OhxNCBjc{*qp8kes@G@Qya^I`pB~UlL)Xc3 za1O?s-ktH4bGIn6LYCNn4>7CNc#GXPA|dx{n|e+y4|<2D%y7QbZ_9E$)AgZdB^Q;a zS41V<rdRWmZy1a-{P>*%-p^|g$&Bzb@#wbEli~!BSYmJG+N}n;B~Mi8p4Rj(LD}{A zQTK%J`A&SXHg4~yd4;^XzDAau=%I#bo>Xz#=Dmb=30D<ePv@)tU>Sm6=g@U=Uf@?e zV~eMZVSf_XUf1)nqQ=j-qQgn`1KV?+!+i7%@k*IZVQFfrX1p+t@sWiWdoyJ}=Dtao zb8wauloFj|!z*(7-amIiS%rKZCnaFpJA(9ge0?G$@_N~UDf4`@<FX;WU?i^siFr<M z=`Ata)Yy#rj|vf%;#1g@ciaP+eZrdfrOsiIDUp#;@Ry-q@;eKbzn7xR<i7P^OvR4V zGbmCHl~?fze&?JndSTdt`Rc3R)Sakl{ntU4DpvP#fuYFjWsKsr`fobmt@0ODc(3fn Ja<=9X@Gsj(Z<+uA literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..841a5423c8 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Szoftverfrissítés"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Változások az előző verzióhoz képest:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Emlékeztessen később"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Verzió kihagyása"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Telepítés"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "A jövőben automatikusan töltse le és telepítse a frissítéseket"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..d1a121f362 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,20 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Manuális keresés"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Keresse automatikusan a frissítéseket?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Anonim rendszerinformáció küldése"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Automatikus keresés"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/hu.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..6b397d42ecbc145bd03d4ff64ef8c362fa54ab6c GIT binary patch literal 6580 zcmd^^%WoT16vnT&{VOIB*;J?@Y%2r?S`}3ROj;q9<427XzhsYVn*TP-ELc^_0#(5G zJI6P7#&+sNWdbU)I+?l8bG~~X-?{zk&%^LAWa0ZT(X*rHNWWefheF@eFjLFxFbG|> zb=A@iZ}g<Uuhy~NdK&XcZ4bj?co6o(LHJ5v{v3q4-aBETF*BQ0grTiagkSZY*$PX& z&27Kew%S}PPt=ozTzlvG8tU2Bv!|W*)&7P4?T4nG2jM7uvfA<U)myIiQ;lW(*q(j0 z@O>#M*tZC+Mz-pCr8eZ8tG%sXrajT=T&-_yzsJ(+fwTjs7c05Dl5lCOPb3@2I97er zlhrcKcO5e6Kh_tBPQoiaL9x&-^YE#@(5i139)-{J%fj>UwVqW(*LUPR5*Cg(8W-V8 zniZzSe)uriqugk77s-9>k+egb<1O0w2&69S5YO^ZvkrovXH|!94YiJ)*;HCaO99Q_ z^qmRciF&he5?<;z6-v3<d!cFPJ+m=OJ4H?>+M%z=dL4})s%KG}n`wM*XG$NI%$ef} zZM|b<nZBzQ;r^1V59=t;DKyfp1FL7np`Cs@?}$UhAnkwK{oM05cU_pB?`eN5*+=;k zNs($Ky+IJkJPMSNc&sr?^)DnddcCoCd}$EQgan9gYR9^qO&JY*8vE)<j<O$*HGdht zEZN0PeqQ#|Q?2G69qqr>=*klc+xODW?o9qamtXsNT^R1jgv=f-rr~$JGYaJP)bm8+ z@zB1VHns&O6I(AujyMZ+d<W(i8hfdhk&Q?H>OROZum~`c6sLygANo7f7qO#j(tz1O zbCEMeJFMnRZ9}b|OU4WBTNr9(kFU#H$`+Aq6KTp8cUxO6&)H@@kEHqi`o_;XvHy#O zpO{DdH|+JFV<Ao14D4j7zw4|b<sRY^xowkY>}|v!SWS!rcqxpeeZ9J~1&>1OOoO$` zUioinxy#Q!MCZH5*7`6ZkHO&Jt}ua#a!lf&WG!cAZu79eo@|6Rcw#lS(dvwXI2-u} z5$Q)oJ9q%Rk;p!?43TRMkKSY>XCCn5#B!c#r?z^SbER428_%<}ZRV7o;=E@zFrFzZ z^yIPRD0<KkX`Wl(X?UkLI3;ZKt@a_yxmCu@l4%69TMg_N$?CGr-RA%ggWZGRI)94i z5gA(fT<#P9DywkZ!SG6_K!zKCB=|djhI@3(&fOa#`nl{0rZ%u>dcXLeY1fV{mf95m zn#f|{JNV1TcMenOHIsJlvwz|Qbw~Hsk;Pf;dIfw_w85@Hnd*DK!aBy9dgH~e`h%!R zeNnz&*p=5(?1dHh%$Uh{F?u4?U2|Jq2z{f|pk9JsB%O{G&HFl~p1k8ecFrnQb&}($ ztQIykzpkOEe88ECC>M%UN$$6G$@!h|Ni}t<H-9qBQoWg^eqlX5<;z&ZWtJVY5c4H| z=#?0<*CqK6%HT;V|Eec1gGIc(a9^sTmZZR2sue4@X4|}>E>upGaZ)8#u6`F52O=l) zI~%`QBun+=C9tM=7gXjBb5+S%_{G-cZW5ouQiiG(a&b7nvD~!=w$03i&Gem#rsOD4 z^V`iNvEQjKD)D-~@`AUauirJ0-O_w~-tRM84gBm}KVlQLKWz7Qk)1mUXTM#mZ?R~P z>tOOst9jKlG7ibtO&Nl_9^&rYyyeo?#WGwj?$3w?E?M#*ZqbSrAFb-=_`DONYBjhG ztIMKZR&Mp4NC&SH%C*mCB;)=C?gMYHubNA;fmmOAuHTPRgKj&K`q1*~=Baci3UcN# z1M`0cQ>%HBm3lmh*56eBRF>&?s<C#$D)3A04_ObZ$C9e|fqq}O?oo_mJf24#w^?<S zE3ISgaxNU<FwPdMHiyq|-l0XGYHsPBSX9CD(d@$P0eO;z`#v#uP<xK`Lte%))9?A- NSD$V7yW-j_{st|?kU{_e literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..314a8caa0e --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Hugbúnaðaruppfærsla"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Útgáfupunktar:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Áminntu mig síðar"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Sleppa þessari útgáfu"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Innsetja"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Sækja og innsetja uppfærslur sjálfkrafa framvegis"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..66dc318788 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Upplýsingar úr nafnlausum kerfisskýrslum eru notaðar til að hjálpa okkur við framtíðarþróun hugbúnaðarins. Ekki hika við að hafa samband ef spurningar vakna um þetta.\n\nÞetta eru upplýsingarnar sem yrðu sendar:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Ekki kanna"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Athuga sjálfkrafa með uppfærslur?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Innifela nafnlausa kerfisskýrslu"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Sækja og innsetja uppfærslur sjálfkrafa"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Kanna sjálfkrafa"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/is.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..1b1858138eae7672e365fd82b3252de327c76081 GIT binary patch literal 4806 zcmdUzO^*~+6o${5wHtRO5mBPx;F1^_WfF}MjSUzRHbqZ=4AWEH>8hfgf8!tW54bR4 zL*l~3m5WIjeV=pYQn#i%u$cx)r|aY1Tj!pS_q^v+{d;=Du32GsZLZ@`$0PklR#~I_ zg)KGm#13p<WBVF;WXC!(KhbEVtC7}R*Vr|?VVCT(U9sD`i{pXSx*l4qHA{c0v6)L~ zY^wXhC9HH+`+iScS}n<Q%@kH@?^1U&9Utj9(oUB({<;4BozMbl&+W0+BCXW@iOv&y zYGd6Yua<-(on!S_GmIm5qR~^GYyEf<39aV#-OAGUy(RlD$;!9OUk_!)hxW5P+RCod z<FGj<OSiaki}6Q48ozo>uiHm&ECSoGB#-GGkN;2}IuP<39Rr1p#(&X$Aso#eX9K&h z-$LjpH9oRk$I#N(tQ^^(En&RPRu+;<PV7t4%CR?>4CEwyzbum~e`-@+yi*)&jtEaB zi^xR|l9@mG%uKr!%Dq@MlOBh@s?o^0KIn<XvEvt6%zxMTM0=1^>~YiH_l#$R`<=7< zl#wSI?PnTtVGuHoSynOz@BFRv75mI(p!);qG1DxT)%Hv}liy)w<yVztaD|R}H1C}; z%y~~IR-1Sxknh-vHsm{2GctE;U_0Vl+s7J%eJ0LzJNBu5g?(#ZzF51XHH>}dV|_LZ zX>j6s_DWu3CfEMr_;^=3!P6_9`x%|A%(;4<ao@{yzAJw1vnp7OxWoFJc$Me3Wf@Up zPpTCBKUb~ksttIL$WnV6tvJw`YDV=U-dzUA%E<2{*_5&$4F2HRO;sVQkV6eDJqtoU z^$DC%3A+dfc9|=LTPkbpxzHIcz<TM&9NB2KkWZgUTCJU1*K?t(=eiHwVeyn5l}1a+ zjw%+luh9HRT7qYGsqHVF<Eb|p4Y-kf#*e|NOKCN9Z%0fLXQJ!SSA*H@M`0{3dTjr= z@8JP_b|@X_5QqaP$J75euYV=YDtT%ouXf1$TIgx?19^e8e$?-2CfI+dE3gtw4+}>< zpz8zAW67mbb=53-D;OD`Tsb0xWARPmy*vICy;an=O*!{L(P=!$xIJ}oIn%Vp>YPWm zZ^NEf{qqRTStGY#SH1#6rDF)suV?ak8!nFJzj1UD@tJ<`3cYJ#KRPl>=>kfo8Ab-0 zY0j!?XbOMAi%Z2Kx?oUGq?dZ`_4>YV&#TdcEy3aB?(6jY_v!*V9RH@>VSL_>`aZk3 zSQArvMygO`Yu{U)HjKksc&F>@$#?2PtDf`3^LJaC4um-R&FDS5-Vi*Zd8)Lq5V*x6 zP&(4_P}iy7&U3C-r&qjqpW#U&&hslBkDZlXErV{8CtH6qb{-4oQKeHA>NJjb;&wxQ zkKQ!uc<4)?LH|JI?Ih44Otl}#=z86Cq|(RmCQ6ST*C9K4tH{m9eXuA0P)85-L*_)+ zk*%q-?)D5Byc@s^)P$v;B4<+n7Jd6Cn!}g8jqnVa7gp!_e{op8FSbl?QHAYEtLWwO zt~2kWQs1=hx9#<rosNlz`kg%UsbeD!F`N5qAWsvEsF`3XzB3RpIt{Xfw?XPD=;tb7 zkY^(H9jd9kn{$ING~^as$SPt<z9+uuy5so56Mb{qYpj*=orj8!mX%#~X?z*t3BJZe zzEz&B#398mx}($q#21Hm3w94iPHdId=P~|6lF65_G}r(u@;GxpRmFO$^QrwQyHd>m E1MsRvQvd(} literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..cc0d7c3498 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Aggiornamento Software"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Note di rilascio:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Ricordamelo più tardi"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Ignora questa versione"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Installa"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "In futuro scarica e installa automaticamente gli aggiornamenti"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..a051914fdb --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Le informazioni del profilo di sistema anomino sono utilizzate per aiutarci in futuri lavori di sviluppo. Contattaci se hai dei quesiti sull’argomento.\n\nQueste sono le informazioni che verrebbero inviate:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Non controllare"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Controllo automaticamente gli aggiornamenti?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Include profilo di sistema anonimo"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Scarica e installa automaticamente gli aggiornamenti"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Controlla Automaticamente"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/it.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..27f0834001b8df46c33ac8c76f7166bd4f35f36c GIT binary patch literal 18028 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..34745b9992 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "ソフトウェア・アップデート"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "リリースノート:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "あとで通知"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "このバージョンはスキップ"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "アップデートをインストール"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "今後はアップデートのダウンロードとインストールを自動で行う"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..0707b0677f --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "匿名のシステムプロファイル情報は、今後の開発の参考にさせていただきます。この件に関してご質問があればご連絡下さい。\n\n以下の情報が送信されます:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "確認しない"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "アップデートを自動で確認しますか?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "匿名のシステム情報を含める"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "自動で確認"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "アップデートを自動でダウンロードしてインストール"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..7de694609a3c252f40f580fd9db78824cc1f9e7b GIT binary patch literal 17294 zcmds8OKhB16~2iCBvG?tlNHlI8#ltuF;r{{YV5dCsK=8ywsEZlT)*SknQ`qIy9vq* zRfyQN2!jGr7d%=hpemv)fSMw;2o*vrq#}`4NGxbeK}m(WpbG>c;rs6K?Y;M(89x&L zZi1qWXa2{1oac8Q_uADRX1mFloGIY5AD>6?Z_wmT5x>XH1g=b(5i@{m1Gv&>j^dN| zhjBHJZ-aQ|Zd}`Lc9>0OtGUDM#4r6EF_ZY-Z%TM(!oF2BV|Ik1IfUOCJHj-+P1^aU z>}Zo1xqv$v(~G%#@oNm9efS*2Oj~jNGx*P$8qCKQX8VNwR*<WcbzOsPEq%k%DzjpS zpBa2buE?|UcloCbOSi0-t>#{=a}!p3Ywi0WC{ky-L79@J4Cz^&wi9?FkALLPFrJC2 z`W?%gyUkrUOi8Y2!!=U;<leeceJ(Ga)&;r4eA4nnslM@AO!sEX^L|Lh2&Am|9><Hg z{)m-@A?$Plt8X`*_%{ye>&5j!(`e;j!ag%?rHGQ^<Vi>5c>Q>O40lSwyBS=kL=Ir= zX{>$>e|zo7PP!J8<Gg*VnpA!`a}@W;mqQqj^i~a^UUcA^&Z?iin3wA~f^~6+MZBTf z7OwPOJTq;_9J6}o#vj9*qjvlv?pE%EBU5vAt@ka;<E%JMIRVYsaBV3rZoIZ$+T}4R z$nU>)`pjW@LVhi;$#0=w7Z!BurChh{lNaPEXxl$<{ddwO9J@uhl2Gr`;(tiP_k-JA zHzCh%KCa}AYWESyzSDix)@duhhZZEbQogBCwLQ)CY3OozM&Hv5ZKduWv3g4#r&prh zQ{!lXRKsH#R8QvY6MbEuNkcF5kv`^Sa;8g~<;8~@9xO|jopo4#@yHymR@X?{Deft0 z8yyN5#_s8-X+P;%xO-Yx`Wf!O0G=v7pp~99AIC5H>tTxy8jRpy#<Z9we8wD1&Pb#Y zdik9uv?v?$CsX#}%0(wjAvcMD)b3XEskbLqT?qUk@z7|*p-x!R6R?jx!uLYkm;)_W z6N@djpUO3dR=xHbS1v71QYb$wkrjQm0rEuoX{{nZ`UEK)N{wQ}9Y8yZ4PzRo<$spK zAH)Zw0IePMiRgofx)0xpsw?$RaW^pnr7wI(C6T&F^vn^*5=cCWj}@PA{^ic6vQs&0 z33Cpb+c6R)L7&pk)x|gQ?i{)aslnKQvel1u5OujdFAo8QmL-&S;Lc?r9XDH9z6ZoJ z4;*ww-iA&O$z0p?+{OL1Y;9W77uqV%>;E5FTaKrG|0G6n*7eH7YFd4#1J3q2BwQI! z(-J6t(FlVP7g1Ti*^9_489TahXnE+P82?Y>uZz`)a?}chl1R+vtweZ-o`6vpE#M^9 zG5%gmL>tB5&foeqF@Dv^a`9Y%MmO)Y|MwIl(5$SBbhI1Ufl)*<e%vs6&B`-_U3e>J zu@a-67+r<$`SqMY6x4-S=jwq!_s|A3<Lv|T^;B~XYsKjTxa84KH60_D8!Zx4tgIOw zPhU?bCq?us4};4i)~gLHk=wa3e*U%HbF<o--paLpBP%cVoIaCwmdJ68`H8&Qk|B1c zH;ywEr1gkJ)I`@?GSr7;US|kbn6LBM59TvCu8XNS2FIVqHI7gXX{&~Xx<qNe=fR!G zp%tQ7p8l~%LVQ?8mPhw9DkEp>O3b>goy;#qK}H~?ET2=nlB7og7KW%xF_xz%BO8Z# z>H{0487banjN>vmj0<VOlGG1r>gZIE^D`&q-)&v8{$O3{1n5~1lWQ7!bn5T|&1cWG zfX=iYv$%UYqW4<Rw-GyWc0G=A9BvPDyOka#j3{VDn}aAQ4|&;X;=EIO(a*|u;`mKY zRzudy*_R@K5DO~Dx>Jnt8o@0_+RU)4e9{K^oV!{QKEoXr@i~IO%;{@(m|oW9R;eG= zax=8;`yk(pSBU#bA(i=F#P`h5b9^Fk>ILfoJ`xM<1+x)(dA0qUJ@vU!`Jr3_Ue3y~ zsgIP1lo<=JmnJy`Kl-T5$h%YYqI=~b>4X%em$_dK1(|7P9fEsjm4x|qS{7PbrK8FW z5fGo@RUehP8p-w2&`Z=MpqB6VKYsjHreD$vc?v4+LWx{0$ty>m>{h0p)<-6wYq9hx z?dXdb36Iz+#Ent!iaZU>{3_5u=fT-?w=&Jb96?gr($mzkFb8le(`_75HUT@9q*<t; zjQ*Irit&4B^)JekwiZ;DH_x0Mu1$|PL$`YI@Jj7_5@$P`9`0Ie-q&l-J$0U!vV8kM z_A9lia`XE`^>eIbCgmhe-FmtEX4I{v>beeFh8TJ&<ms^#-^y31zGkD`hQpj`A|_^P zSc{IM%^d6nyGQ(rpFoB9vTW{1Pq(YD<a1bsS}tO><a&IcLR#zE1xhd{JqRlK+0V<H z$P+(@9PwJrn~C}+{3^Wlxd7`^pX(b<Z#6_E{peV5mceBTLt8p&?#$F$omMP0m)0$O zE}cr*F2pyT7Pm7`@+otVsYj)W*^{tVt4|dqc|YAXwc9L(<|yh=0o!AaMcq5Ps(JaN z^|Bds3HfKzURenlmLjqr%%gZ7>WY2}?D#w^(`zY8yK=3g#tN#<-T}?j4jS4n+FtFJ z(HcIp+U$6v^%#QgvQLOMThX<8x}wb{F{?(Y)@X)E+f}uj?OVRP$jN>v^R*>DtVp>m za;4;_zl-~AA67EXSl%8OFVtT9>LI(`;Gjn3J~vH{vX($Ul#IixaiUhPA|LtGxwhGJ zqtejbg^ZTVRHS8#J{#o@SS!}r?|$qkjAXpN`TRgweT>)Fh<7zRlgEhsM!BYi)2xQG z%-am-IkF(zthn>Z?7}+}W*dHK&4?Kf=6!q@UNW**n-$6ott<78?`hUM<QKW<v?xgh zovY2(3yOiQX*&L8<WEN<ZpAxKUsvL_R+F;Y`kv$+t45XjzIr;twW=Jr3<9kkxuLl| z@_=z4y*9IZ)F#KXJUH&_72Ic-<KmlIXK?nC8A*LZ^Y3wH@alnwXZ{4AHIEF#AJ9wq z6MTOb`S7>#_k#QlpFcw;un=*!b_RU;@*Q(4MoEiFx9*Day3Nvr)TpefsLjbhi`s1k z)knu(wzC+6zK2@SicvUbC?URgD?P;1YI&^l4`BWfr|P{o(A$|FQl(wdPC91XiCLN= zRP(yLfyg1elMa@lW_e56j7;;M)bL8q`X$;u*4edhNwLyoum-&{J{ViE)w2M5(S_%C ztiRHst@aSqip6+0{YY$|)h=qGo<X3MqBmpKzJ$NDaGo05+xE1vH^9t%YbGuFJ0Iq| zl~}2yE~q8enk}uX))ahaMk6e9W!z2sOz+JKJ$K2`Xi-9}K&wQ{?Q5Lgnt6m#S=9;A zk*i^rg+8|vy$$O`<9k}}pW8DQ7v)E=884tp@t>m?_vd9(Zm#XAFFYVa=sQinlCvMX zb{%y}yV%xyK8Ie{<xA4ycRQj7b_r_-g`*;=&dwWF@U>!3T*Mwe+C%2>HFEadH8GC5 z4^MU5ah-SOh-0?ae{{iTFExht`sA%1xS5j@;-!>Nvszw?>U;iv+#^$#%cn2yPxc&V zOqhM(DG}`-cwJtWw;)9=az<ME_+GON!25s6_AFB)+ff(H;tD$q(mco0u@LLU=qBu` z7y_Mr)G+{hD3;caD8)N^4uoUy1P-I)2~dx{QT}w3O6WB1P&ai9jWb>SJ>+<(iCR|= zDOr|(4YkTIzj_K9`D0|5=H;%#Svd?UvWK8j+S!-HZY5S+(~^5f$#55;WUCj9si`#a z8D&0Cd_;T3dKa~Wv9^=^$w;@fGRt0AKI`N5UTZgr)mZtaWz^0OdSRWB<Gb@X`kwD; zWi(0dxHya>kY__yQ)x#j9U1&eo&i!D>nr2jSF)!r<ZH;$U&#-EiJyi}WTk(U)^i@& z6g_L>x@;T=lU8{4+_9+-oEnvnp6xx3?tfI#+0(N*H{G_Oqbv;%KG4wx+ieo3<C+$< zth{heU@dCz)n&xdo*g!fAyj`{pWp~|%Ju9xo{;m-!p2bwPY^Ow?Rp$or&?Jz1Cbgf zW#!UUi??~1IJvna3yzGO{Sa*3+{~wEd}QOe8diRIhH5b`uT7^!C+O}@Vmi&)6?m%M z3zX^v#~i&wWE;x2N?CH(Y1yK1MHzEH_^I78VOLMMI_*tKuQw@PPmEHSHStI#Tvc4> z@%`%QQ_XoYR-pXa_^S-L^ydKUbLp)`JCAllmzfV!@1fS4nmKN*vBMKiI-Lbh2RRdI zI}5a2?3~o%7%|f>e;(GFILMtL=&?OfYuOPMM{F5X9B7eAYj%Bk{`=@{@L(~uDpEIi z9a@nBP)%A==5PfuIdM4sjAk)a(iEX<Re%xOfW-lE9aQ5n+86fbdWodm@;o!qIq@Fv zw&QOR|5U&DCeN}s>1OQ8P9EYJuIA2#H?+HtsF7El1geZPqr)p2*(zdJEJS?Dc@$j{ z?=!Yw48rsIL^!3$Zin9MrQBmZp`OPlUR*c*Iz)l<@*DY!)XzMAATPI}_Hzm7^A%jb z0{s4*oCnIkj3=Iy&G>yAYzOPZ-~HkTi<^N*9jz$+dD+<g?hw$*%sc4ap0&N%S!lvc z%J^R;;CjwMc)cLnq6FzFituDXAv#%*t`x6q51Kz%-X456eY}Hq$P%$Iv%MNEGTu>1 z<OvVP!8~^#)(o75b{XO@LU5T4?j)8?SA(EL^C{{jW!OdkzW-Iv)+>T@bGe+XW^ZX- y^2q5@OFYI?ZKWnr$Ku(EAYGk8<z-98YmoCj4grN2gP42IL|J&&-dD&R&iEhtO^Gi6 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..ad0b22981f --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "소프트웨어 업데이트"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "릴리즈 노트:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "나중에"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "이 버전 건너뛰기"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "업데이트 설치"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "향후 업데이트 자동 다운로드 및 설치"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..95d023acfa --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "176"; */ +"OhZ-1K-DmA.title" = "자동으로 확인"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "177"; */ +"cCJ-V0-aTi.title" = "취소"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "178"; */ +"gmh-T4-BO0.title" = "업데이트를 자동으로 확인할까요?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "180"; */ +"gz7-LM-gNf.title" = "익명 시스템 정보 포함"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "업데이트 자동 다운로드 및 설치"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "익명으로 보내지는 시스템 정보로 차후 프로그램 개발에 도움이 될 수 있습니다. 질문이 있으시면 연락 주십시오.\n\n아래 정보가 전송될 것입니다."; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..5a5652baaa41351db819a46b084ef66d52314353 GIT binary patch literal 14468 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..bd58fbb6b3 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/SUUpdateAlert.strings @@ -0,0 +1,18 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Programoppdatering"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Om oppdateringen:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Utsett"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Hopp over"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Installer"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Last ned og installer automatisk i fremtiden"; + diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..c921f62672 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,24 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Den anonyme systemprofilen hjelper oss med å planlegge fremtidig utviklingsarbeid. Ta gjerne kontakt med oss hvis du har spørsmål om dette.
\nFølgende innhold vil bli sendt:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Ikke søk"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Søk etter oppdateringer automatisk?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Inkluder anonym systemprofil"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Last ned og installer automatisk"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Søk automatisk"; + diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..3d8e2aa2ad9df2672d34c36843a891bc8904838c GIT binary patch literal 8996 zcmd^_&2AG{6vywHH5+yVDou+@DD4teZCZ$gfPx6Ls??3+I3dK?k;f*@+w`&e0=xsO zw*TKbKDl@7$p^)QS}F_2bLY;v=i`4qhrj;3m2RXe{g}r3?(6%NK7%w$Q(YgXiB?{x z{j{UC9j$DqV|{afPpc#S8feeD)^4O*=~B9uuBY#G<=^|M*6)6rY0t!-nx>&qn5GxH zu8hKwe${r~Yok_+@>qAO)YI8LT@CfUt?z+Ox~BDS^pBHnr%&dmeKr3DH&8vE)8vex z`qHRE;f^@HB!2%}lK(2XK1;tyikYP9nMT{SJJF7jK3Hh4(17dpceBoV`g{etTqRFm zn+=_>(n5F5mVL>)FGNmH&@t8eZ-$#)=>)rPrRVw_3aLG<57LHVW@3Ae3~`_;us17q zR-y`4cSPw(S{v$b&*%j1&O-Reo@&GE<?2}Xu-6OG1Bvb%c+AgsXJ~#EyN2T9z;;b_ zcbOmf#54WGZ!c)UH+llS4TXl6q7Etsy4u&@%hk>af9UXf3OiKk5ACdtAMW@*XyK1| zEYc7Ihzw&x8i?agPkZoJyq-O861<jHeKwB(iyDupvuDrNW)-NS-O6z78W^WtT@#Ps zqfTGwiukZ+(dc&iQlBb4NZ;$b8JiYQg#R+C5Wlj7u5I7*e*EK;@D9%2g?rz7TM{DC zqZWyL2QjylBcda?!>%49n^x$`x7=I7T;H&O<+5M8H=qY_0!HvPGRwApgU@B;JIZ>x z2inSK+*6v!!@siCLldEceEvnx^XRaA>P*%gq^mj&Jo_&HhEu%tmE<Nm;S-)!OHJI- zo+I6#Ne{?%S{!If7g3<p28ab=eer>{L*|P`3Va72Z^M1~w(mHwJ?!;}g3ZV`2d0H8 zfvBlI_`N^j_N0n9uxH3fV`(MhfyhowLK|=Ot1ad<H4u@-qYifQsDqw4xs-o7_H>2% z(y|$29}mValChe0T0K%%gVWP8Ta)|uL)j{8>hNZw=_p;P$V$YnW?bE|EXX?hJT`>| zj<n*L=TP?}YwyZ;srfy-Mtr-eHEJLtvR78*UOX$J8Pz9usgpP>s<QU_{J^+|lLM<J zmi-_9t(4Ix+Ufm8mDVI-RJqg=_56BmzJm?|J=Ajvc9r=DqF-AD;8CY<xxLKWtFy|6 z&PI<%`Wrn)bS^m~BeiF`#a`xWk*)8E64cOX(DHu8t4bmob%~?@v|0{|{{~qDM@Z9_ zo&UMyIr~J-zwEKB>?s>{rweR_CGZ_GAwH11T=F<Pc$G5Mj(zPW|G`(&V|}N~@~YuS zReB=tqB5ewK#%TmJ<*^ihCbOEucQOdm7im2SL;;#;cMi_vDFyh3m)+wDw5#K>%{iV zOiw<Ne<Ck@*6JgO5$mG!lhGkw&`)rV*YIzx?qtn-4m_0;J*sxshi~YN^Xx-6h<4EI zVm}c2XzLS^w=26)z3rHGqN|E(EI2yLLTL75u!i^a<bi&H&9DXefC_itqU49N#!Q?X ziX*bu|H&4}JdxLi%wdIQolB%4qP$BCDzWalJ<d1_<4gmHgKrgsd`j7M6gjVdi~Ls3 zJI~90i;?;woe{N>=bEw($k{M@u{VlOXwti(JcFq_%Z5J)BXph2ld@zR@=y9grZZjT z;qv6FG52fdBQjx32czpGa(2ffS+-xqjj&_sLv{MJ$~<og-^>81ETWh8Dx<AuW*<+N zb>B0KRpd5mbMQz;8Os)dSEl+rxhtzN@nqd6@{XLTbq~xYK$B-kqHWAn?%Gp%P8!}@ zN>$4AP<%kyrHbyOz1F}UA6}w+HcMX2JEzI>#55dMB0rpku0?59*Mr=buClMwEBJiu zsWeUX5EG${uI3YN51*nq9iNsTYGUjC$cIK&lfUzD)!dCVAyK=pEF+6|1dhv*^u;fH zfjO;LhvBblsd-j2;?R?BOWRDo{N2^!`vy2$ldaqnm#fa`8J_Ab#+G*FteU;<p=jXG zBi(<bbD&vz*&|&ODbN~xcZa*jnyhC``BfQ#R)=tq9MfU%C7X80Ts$zuBma5Xu!-r_ z@w%lLnrFFr28kcSdvth2WpXq9G1aVRh@9igs8j3BNp<qpk0-G{&oj|EHbk9Iqq4I{ z;v=7&XFn19Gsj+C%^Ee`ma-C0m($gHo~z)fDI+u7i{>vRysPIcI|2E4V}vDRwu6p_ z7U@{?{Fr6p9uYX^M<K_}d6mf9V7jBO?Ebu%WmoB*IQPjwIp^3E58jVQ|9yJ)>uz?D zIVDCgjcC3#db}`~_juxay7BMlvL`Wjb6#oqiSFtAe}qHxlCWshFM-$%<Cn<;o`K&J zeSMZV7Am}-=G`E%vdoA?$-s<HK%FN0*dLj6dG1p7Ih&$MjSHocN4NA7!vSwHdqN^U zHAy!O<Nk(}ybCfpuRZ%xRbVClC&uDDWf9L?;sPBo_r*7unATe14CoV^c(+Yehd=Qv z^ysr<JTAwQ&?2kNh!nXF9kPq4fZifb=UJG)@$$|k&Wg<DIS?*K%8Y0bd-Fuhx3B>* ovU&QtR-z8|{|#}a;&k5^2#G4c^g%L*Yusxao;l46CnwS3Z_DlH4gdfE literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..3edac367ff --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Software-update"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Versiegegevens:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Herinner mij later"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Sla deze versie over"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Installeer update"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Download en installeer updates voortaan automatisch"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..8995361036 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,17 @@ +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Aan de hand van anonieme informatie over het systeemprofiel kunnen wij toekomstige ontwikkelingswerkzaamheden beter plannen. Neem contact met ons op als je hierover vragen hebt.\n\nDit is de informatie die wordt verzonden:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Zoek niet"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Automatisch zoeken naar updates?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Voeg anoniem systeemprofiel bij"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Download en installeer updates automatisch"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Zoek automatisch"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..8e4e28b19763c2d3855e0efb56990485bd2875c5 GIT binary patch literal 20920 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 O_RTm0>4BxF#r_XT)x6UH literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..4de4de2d72 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/SUUpdateAlert.strings @@ -0,0 +1,18 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Programoppdatering"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Om oppdateringen:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Utsett"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Hopp over"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Installer"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Last ned og installer automatisk i framtida"; + diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..b5dd5d1653 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,24 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Den anonyme systemprofilen hjelper oss med å planleggja framtidig utviklingsarbeid. Ta gjerne kontakt med oss om du har spørsmål om dette.
\nFølgjande innhald vil bli sendt:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Ikkje sjå etter"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Sjå etter etter oppdateringar automatisk?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Inkluder anonym systemprofil"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Last ned og installer automatisk"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Sjå etter automatisk"; + diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/nn.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..400fad7ef5ab7b3ae0d781b9991f83544192a043 GIT binary patch literal 9070 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..4092fd0ac7 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Uaktualnienie oprogramowania"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Szczegóły wydania:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Przypomnij później"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Pomiń tę wersję"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Zainstaluj teraz"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Automatycznie pobierz i zainstaluj przyszłe uaktualnienia"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..ca85ca81ad --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Nie sprawdzaj"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Sprawdzać automatycznie uaktualnienia?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Załącz anonimowe informacje o systemie"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Automatycznie pobierz i zainstaluj uaktualnienia"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Sprawdzaj automatycznie"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..33226f240c86da5a63bf6111b0e1676f35dfeef1 GIT binary patch literal 8408 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..65aab08753 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Atualização de Software"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Notas do Lançamento:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Mais Tarde"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Ignorar Esta Versão"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Instalar Atualização"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Baixar e instalar atualizações futuras automaticamente"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..a435270785 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,26 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "As informações anônimas do sistema são usadas para nos ajudar a planejar o desenvolvimento futuro do aplicativo. Contate-nos caso tenha dúvidas sobre este procedimento.\n\nAs seguintes informações seriam enviadas:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Não Buscar"; + +/* Class = "NSTextFieldCell"; title = "DO NOT LOCALIZE"; ObjectID = "cfa-j0-Ya4"; */ +"cfa-j0-Ya4.title" = ""; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Buscar atualizações automaticamente?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Incluir perfil anônimo do sistema"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Baixar e instalar atualizações automaticamente"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Buscar Automaticamente"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-BR.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..6c393ab9474405886865b8e24feb9aa01b434316 GIT binary patch literal 16756 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..ae805c423d --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Actualização de Software"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Notas de lançamento:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Lembrar mais tarde"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Saltar esta versão"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Instalar actualização"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "No futuro, transferir e instalar actualizações automaticamente"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..cb41028ab9 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "A informação anónima do perfil de sistema é usada para no futuro nos ajudar a planear o trabalho de desenvolvimento. Por favor contacte-nos se tiver alguma questão acerca deste assunto.\n\nEsta é a informação que seria enviada:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Não procurar"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Procurar actualizações automaticamente?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Incluir perfil de sistema anónimo"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Transferir e instalar actualizações automaticamente"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Procurar automaticamente"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/pt-PT.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..fddae71bb57ac6e07a140af7c2d19a74c5da5380 GIT binary patch literal 8210 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..37e9bc76e4 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Actualizarea aplicației"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Note de ediție:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Amintește-mi mai târziu"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Sari peste…"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Instalează actualizarea"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "În viitor descarcă și instalează în automat actualizările"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..c9d3813782 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Nu verifica"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Verifică pentru actualizări în mod automat?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Include profil anomin de sistem"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Descarcă și instalează în automat actualizările"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Verifică în mod automat"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..f08fba0eb336e1d7d3e689743f06553863f428a6 GIT binary patch literal 9418 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 B88-j` literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..137fd579f4 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Обновление программного обеспечения"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Заметки о выпуске:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Напоминать позже"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Пропустить эту версию"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Установить обновление"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Автоматически загружать и устанавливать обновления в будущем"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..99f15966c1 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Использование анонимного профиля системы помогает нам в планировании будущей работы по разработке. Если у вас есть какие-либо вопросы по этой теме, обращайтесь к нам.\n\nЭто информация, предназначенная для отправления:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Не проверять"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Выполнять автоматическую проверку наличия обновлений?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Включить анонимный профиль системы"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Автоматически загружать и устанавливать обновления"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Проверять автоматически"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..1ce90e80bd734980106b6f62a68782643910544a GIT binary patch literal 7964 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 I>qq$f7oRVfD*ylh literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..266f0fb406 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Aktualizácia softvéru"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Poznámky k vydaniu:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Pripomenúť neskôr"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Vynechať túto verziu"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Nainštalovať"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "V budúcnosti aktualizácie preberať a inštalovať automaticky"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..25c836d80b --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,20 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonymný profil systému nám umožní zlepšiť plánovanie budúceho vývoja aplikácie. Ak máte ohľadom tohto akékoľvek otázky, neváhajte a kontaktujte nás.\n\nOdosielané budú nasledujúce informácie:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Nekontrolovať"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Kontrolovať aktualizácie automaticky?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Zahrnúť anonymný profil systému"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Kontrolovať automaticky"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..9e3444e0d8b13ef8ab6e5fd950011afe386b2cf5 GIT binary patch literal 7940 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..d106021343 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Posodabljanje programske opreme"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Opombe ob izdaji:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Spomni me kasneje"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Preskoči to verzijo"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Namesti posodobitev"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "V prihodnje samodejno nameščaj posodobitve"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..03b141ca08 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonimni profil sistema se uporablja za načrtovanje nadaljnega razvoja programa. V primeru vprašanj nas lahko kontaktirate.\n\nPošljejo se sledeče informacije:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Ne preverjaj"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Naj občasno preverjam, če so na voljo posodobitve?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Vključi anonimni profil sistema"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Samodejno namestite posodobitve"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Samodejno preverjaj"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..2e64a63baf63e71ad471e92d4d2b473b747b76c5 GIT binary patch literal 8180 zcmeI1OK%%h7=}-_m9p!myJ<zCtsteaM2MDNP=#8erUfCfnZ!=wTx`pB5}F0Sg<lH1 z&w2CZ%-Bwws&)Z^tW0LkoXdB8zsvmj$K7-%Rq1&e=(?rrp?=-ePZK>4(?}!7sh2t$ z>u6*%z1EfYI~wiluB$m~8oQJ3rYq@2x|#0l$=6;Q>wYU8Y0fCGnxuog!zAtMxyn17 z=x&_PJI=d}wevu4s#NRjT2BYMZtB|ANjEh9wf;EiUi#?lv@g!?_Re;#)1G#K_O9;g zm0Byj`>FQRh@Q>ejwq|ebDj78N<3T<C;u*){32d&rJu#`k@%{!tmn&pq#1qv(B00t zOhV$nXT7bZ+e?-5BB}i{>)jMLb@xbmAItJa;&dq63X7U({MT%2+mhBu64*$u^c%{$ zYmIl)gKTG`Jm(}^DV7-adeoR%X;<vKqn%D9&jbCh^FCp#Z}JMP^F*s>ZTxEWTJO;0 zZhniO;7@qjPx;M3_QjZWAUgI%IaK1s)AT?m-s}^<Y3TO3_Qx*<qJ1x~wg1fXtg96> zdz#<N-h!;4D>P80@A7WHt#q{;C-NR211;>~tVUvEjpNOeyke|7q`#M-t*OPAXDBht zQSn`NW=|{{X;#_7&G8^@>lw^~nsNG6PhiMSmgT+lxqempK7Ffe8^5Mcgu;S6g{xZJ z;!V5SA!K*|y}7_g;Q+qgg$K{QCqCip*&Mz+18XYW6m1W(UENGZ)3z=?j7MSda9ztj zupIn}h_k7CBt2{0j!U)P5uciC><!oj(S;okvfhiPXX8hlf7$bl0JEna>CA4rE{ZPW zv27Z|Uz<D;0kMj$Y-vSfvp!wkg&xB)H)T@?q6pfC$aaO*O&_&2&+}1xmn|IY2UUri z$J&umG}G6!uVfI&+$eCIr9KZGSkatobYZWdD1*A=e67-{X71z@*cGI!g?WQHnDIRG zw3tjL>|;x(_0zQq6vZ--VTl->j4O;Y&zcV%o@m5gjKsk~taMwlBhxZoh4p-<F`@|e z?%0GpL9W9r59B?{Z;3RV6>+VZ6|LvJOI8C$2OpqHrQHUS<shvJyDaIko<KY-ggwDY zA`0<wN9TAJcYR6RaS{$s^#)q@HMXT^EWvBht9ck|e0;W&^GpM$MchFy*x{|rC6r-1 zjM(0MKh{aaIjHI6J>&jRG#WABZlGP@VO7@6T<?y(+?E}-F>P_S@<1n{T_XB)7F#eE znHk-t^V;hTJiaI@+aKWRZJBK33;zsm!@jZWd6+!S`@3iE<~Qtx45Z*ty@H)%3(M3# zn1#RK*LV&7S?V5q-Ux~;CYsUHf3hiO;Y~F0OlPN5CB?_9v@RbYCK6karO^Onq)LJY z;sl66O;#fOGmV&rarPj*7*v_({m>PXG}6~A__!ml<vP?U;KQ0mjW<*fczo=*pK;Lm z?=f4iJspjJ9e500yr=us75MtQwM52GltjMMIbdjOUM*lryq|WEwk?%<)jA4!Ht`wn zpBJNzmj7*?xw<1#Ze`E0e}%?~pJY(IjG&9q8G0eYaboc*&haUr_5XUoijfH@(bh;C z`L<=*AXO8)h=#@+WNGU*MjGerHN<ykAmjiqRMl_jKM0Fv>@DP!?M#O#e5g6giwqBS z0$DpXalzwtQ3djm(X6iYmX?(?Pp?{Cj*Dq&B96OmWPG;mW#tnO<t=2-`{(u%-I=C+ zjMK+eq5G+HN$j{7CmqRH)9xuacF`ZhCY>t`gcgyVxNA64mQLIwqYM0S9XZLdt<oE; z*--*4iMsMpURC-*Ve?I1nw;1g$WShW`yZa4$GKxRjj0!L^Fn-_o_TB&7n^n!m4+kP zG)Ife%AZO<rS_*raF^gk_Qz6D7pnqoN<|u3+q`O&ER$O1Lt9syr?=Tw@Iw~-CSO^$ zCVo(ttw{Mtc?+J`ktem~w>aIS<LDl_>S#-@P=o)yTR&r2G;at8>HNCCTk3W!6&|7< z0J-R9QBjf47(3}JxcX@N=2%_T>ipXPEBkt8J-vz7@{h~xLDztlM|q`BE?tbk$3iha z#O&!vPNrXGk?&JA@O8;mpCdfTO$|fjp`YMR1+m-p2a&z>tmnn`Zsz)BUO`Q?t60Hm zA_l$YQGO==woi<T1&pW5K<3~a0W6?qF=z1&iM~|fXnA?WD<Nupp{UYh(eFxgx^|w; zl!|oP(R_KHS7;4%YWJJhr3vR@g(7NE&uYf%#i79~ok*mAA}taFu&#*r^a_XBmx^T? zF~Y7N=sjO7dO2r^iX6|u<M3AY!%{)DcCQwTYIgj*C-aye5oxxTZ+ea;)~=}wt<He) z&wcUc4jvxjJ{c$NXY>H&@IS1WoI3hx&YQ!^Z>5X4y`3p8{XlB@i~6%jS*IvQx|Sxp zu}q6SMiwxee{X0m^XaA9<M_rypTm_eD9IN$axl=3yU>;UuV}X*V`z%eHa61F1zYiM zW=(&0C_Nm15jVcZSy8XJS3u9ZpFYXEVN<M#z6^Q>t=p%sX(X~nf4#j{p)b!1`wYKt d`Z_)aEyaVVhTJFb=q!KDGm83)_u#zB`3qBpSe^g? literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..382b634a2f --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Programuppdatering"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Versionsinformation:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Påminn mig senare"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Hoppa över denna version"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Installera uppdatering"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Hämta och installera nya uppdateringar automatiskt i framtiden."; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..b26b32e4ef --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Textcell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Textcell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Anonym systemprofilinformation används för att hjälpa oss att planera framtida utvecklingsarbete. Vänligen kontakta oss ifall du har några frågot om detta.\n\nDetta är informationen som skulle sändas:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Kontrollera inte"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Leta efter uppdateringar automatiskt?\n"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Inkludera anonym systemprofil"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Hämta och installera nya uppdateringar automatiskt."; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Kontrollera automatiskt"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..df36ec1c657801897900d105e096e948cf69abcb GIT binary patch literal 7588 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 literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..c57e3d3c59 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "อัพเดทซอฟต์แวร์"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Release Notes:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "เตือนในภายหลัง"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "ข้ามเวอร์ชั่นนี้"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "ติดตั้งอัพเดท"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "ดาวน์โหลดและติดตั้งอัพเดทโดยอัตโนมัติในอนาคต"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..b755d03816 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "ข้อมูลระบบแบบนิรนามช่วยในการวางแผนพัฒนาแอปพลิเคชันของเราในอนาคต กรุณาติดต่อเราถ้าคุณมีข้อสงสัยในเรื่องนี้\n\nนี่คือข้อมูลที่จะถูกส่งไป:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "ไม่ต้องตรวจสอบ"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "ตรวจสอบอัพเดทอัตโนมัติ?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "ส่งข้อมูลระบบแบบนิรนาม"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "ดาวน์โหลดและติดตั้งอัพเดทโดยอัตโนมัติ"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "ตรวจสอบโดยอัตโนมัติ"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/th.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..6b8c878683fd90609772e5373469ba98d58d0ce3 GIT binary patch literal 7902 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 b%A4tgvui(RL+-m=h`niFohE(Hb%^{2iywkw literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..03fc614526 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Yazılım Güncelleme"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Sürüm notları:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Daha Sonra Anımsat"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Bu Sürümü Atla"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Yükle"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Gelecekte güncellemeleri otomatik olarak indir ve yükle"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..b747e3c714 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Gönderdiğiniz anonim sistem bilgileri bu yazılımın geliştirilmesi için kullanılacaktır. Konu ile ilgili ayrıntılı bilgi için lütfen bizimle iletişime geçin. Gönderilecek bilgiler:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Denetleme"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Güncellemeler otomatik olarak denetlensin mi?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Anonim sistem profilini içer"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Güncellemeleri otomatik olarak indir ve yükle"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Otomatik Olarak Denetle"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..88784d3263b222515c610fa717e923d112c56f5f GIT binary patch literal 8620 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 WAQam&gDInYUAxt1(PdOcg#QgqO2U8u literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..ece6670799 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "Оновлення програмного забезпечення"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "Примітки про нову версію:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "Нагадати пізніше"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "Пропустити цю версію"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "Встановити оновлення"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "Автоматично завантажувати та встановлювати оновлення у майбутньому"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..27451a334d --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "Використання анонімного профілю системи допомагає нам у планування майбутньої розробки. Якщо у вас виникли питання щодо цього, звертайтесь до нас.\n\nІнформація, що буде надіслано:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "Не перервіряти"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "Виконувати автоматичну перевірку оновлень?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "Автоматично надсилати профіль системи"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "Автоматично завантажувати та встановлювати оновлення"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "Перевіряти автоматично"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..e3f079190233e190b772d44dd39b8b88f5078a07 GIT binary patch literal 8100 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 LLV6FKs%8HG8eOkd literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..5772fc63f3 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "软件更新"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "更新信息:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "稍后提示我"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "跳过这个版本"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "安装更新"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "以后自动下载并安装更新"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..bdf7d69eb3 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "无记名系统概况信息被用于帮助我们安排将来的开发工作。如果对此存在疑问请联系我们。\n\n这是将要被发送的信息::"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "不核查"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "自动核查更新?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "包括无记名系统概况"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "自动下载并安装更新"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "自动核查"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..3acf2a0c653431e49f2d201048cfb78e16f4e1a5 GIT binary patch literal 7720 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 b&Y;eCrq>&*gY25lRyq%bGx7_n@yq`JAepQe literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..d7cb6fe144 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "軟體更新"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "更新事項:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "稍後提醒我"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "跳過此版本"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "安裝更新"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "以後自動下載並安裝更新"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..b40c572fba --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "匿名系統概況資訊可用來協助我等計畫未來開發工作。若對此有任何疑問,請聯繫我等。\n\n以下係會傳送嘅資訊:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "毋檢查"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "自動檢查更新?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "包含匿名系統概況"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "自動下載並安裝更新"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "自動檢查"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_HK.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..4d758a3c877f66e7bb28e2ae29f4ad766c2b11da GIT binary patch literal 13272 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 K#^ML-mH!6{2jC9? literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/SUUpdateAlert.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/SUUpdateAlert.strings new file mode 100644 index 0000000000..d53374dd24 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/SUUpdateAlert.strings @@ -0,0 +1,17 @@ +/* Class = "NSWindow"; title = "Software Update"; ObjectID = "5"; */ +"5.title" = "軟體更新"; + +/* Class = "NSTextFieldCell"; title = "Release Notes:"; ObjectID = "170"; */ +"170.title" = "更新事項:"; + +/* Class = "NSButtonCell"; title = "Remind Me Later"; ObjectID = "171"; */ +"171.title" = "暫緩提醒"; + +/* Class = "NSButtonCell"; title = "Skip This Version"; ObjectID = "172"; */ +"172.title" = "跳過此版本"; + +/* Class = "NSButtonCell"; title = "Install Update"; ObjectID = "173"; */ +"173.title" = "安裝更新項目"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates in the future"; ObjectID = "175"; */ +"175.title" = "自動下載並安裝未來的更新項目"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.strings new file mode 100644 index 0000000000..635b4532c4 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.strings @@ -0,0 +1,23 @@ +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "43"; */ +"43.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "45"; */ +"45.title" = "Text Cell"; + +/* Class = "NSTextFieldCell"; title = "Anonymous system profile information is used to help us plan future development work. Please contact us if you have any questions about this.\n\nThis is the information that would be sent:"; ObjectID = "183"; */ +"183.title" = "匿名系統描述資訊可用來協助我們計畫未來的開發工作。若您有任何相關問題,請與我們聯繫。\n\n以下是會傳送的資訊:"; + +/* Class = "NSButtonCell"; title = "Don’t Check"; ObjectID = "cCJ-V0-aTi"; */ +"cCJ-V0-aTi.title" = "不要檢查"; + +/* Class = "NSTextFieldCell"; title = "Check for updates automatically?"; ObjectID = "gmh-T4-BO0"; */ +"gmh-T4-BO0.title" = "自動檢查更新項目?"; + +/* Class = "NSButtonCell"; title = "Include anonymous system profile"; ObjectID = "gz7-LM-gNf"; */ +"gz7-LM-gNf.title" = "包含匿名的系統描述資料"; + +/* Class = "NSButtonCell"; title = "Automatically download and install updates"; ObjectID = "AUc-33-qGN"; */ +"AUc-33-qGN.title" = "自動下載並安裝更新項目"; + +/* Class = "NSButtonCell"; title = "Check Automatically"; ObjectID = "OhZ-1K-DmA"; */ +"OhZ-1K-DmA.title" = "自動檢查"; diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/Sparkle.strings b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 0000000000000000000000000000000000000000..6018e8bca0ce73f8692deddae66c397998339dba GIT binary patch literal 13422 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 Hc<FxwggzS) literal 0 HcmV?d00001 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/Sparkle b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Sparkle new file mode 100755 index 0000000000..4ec08228c7 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/Sparkle @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9ade97f79bb15d096d4dd6e714b7f8dbbf1a516812e2e3e7de04f794183224f +size 864560 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/Info.plist b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/Info.plist new file mode 100644 index 0000000000..3c424d48db --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/Info.plist @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>BuildMachineOSBuild</key> + <string>24D81</string> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>Downloader</string> + <key>CFBundleIdentifier</key> + <string>org.sparkle-project.DownloaderService</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>Downloader</string> + <key>CFBundlePackageType</key> + <string>XPC!</string> + <key>CFBundleShortVersionString</key> + <string>2.7.0-beta.1</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>CFBundleVersion</key> + <string>2040</string> + <key>DTCompiler</key> + <string>com.apple.compilers.llvm.clang.1_0</string> + <key>DTPlatformBuild</key> + <string>24B75</string> + <key>DTPlatformName</key> + <string>macosx</string> + <key>DTPlatformVersion</key> + <string>15.1</string> + <key>DTSDKBuild</key> + <string>24B75</string> + <key>DTSDKName</key> + <string>macosx15.1</string> + <key>DTXcode</key> + <string>1610</string> + <key>DTXcodeBuild</key> + <string>16B40</string> + <key>LSMinimumSystemVersion</key> + <string>10.13</string> + <key>NSAppTransportSecurity</key> + <dict> + <key>NSAllowsArbitraryLoads</key> + <false/> + </dict> + <key>NSHumanReadableCopyright</key> + <string>Copyright © 2016 Sparkle Project. All rights reserved.</string> + <key>XPCService</key> + <dict> + <key>RunLoopType</key> + <string>NSRunLoop</string> + <key>ServiceType</key> + <string>Application</string> + </dict> +</dict> +</plist> diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/MacOS/Downloader b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/MacOS/Downloader new file mode 100755 index 0000000000..fde684849d --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/MacOS/Downloader @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2af03fc3b72c91c5649b77ce945169e96f0b3fc1d6e95696f27337381ec7300c +size 170464 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/_CodeSignature/CodeResources b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/_CodeSignature/CodeResources new file mode 100644 index 0000000000..d5d0fd7441 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc/Contents/_CodeSignature/CodeResources @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>files</key> + <dict/> + <key>files2</key> + <dict/> + <key>rules</key> + <dict> + <key>^Resources/</key> + <true/> + <key>^Resources/.*\.lproj/</key> + <dict> + <key>optional</key> + <true/> + <key>weight</key> + <real>1000</real> + </dict> + <key>^Resources/.*\.lproj/locversion.plist$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>1100</real> + </dict> + <key>^Resources/Base\.lproj/</key> + <dict> + <key>weight</key> + <real>1010</real> + </dict> + <key>^version.plist$</key> + <true/> + </dict> + <key>rules2</key> + <dict> + <key>.*\.dSYM($|/)</key> + <dict> + <key>weight</key> + <real>11</real> + </dict> + <key>^(.*/)?\.DS_Store$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>2000</real> + </dict> + <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key> + <dict> + <key>nested</key> + <true/> + <key>weight</key> + <real>10</real> + </dict> + <key>^.*</key> + <true/> + <key>^Info\.plist$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>20</real> + </dict> + <key>^PkgInfo$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>20</real> + </dict> + <key>^Resources/</key> + <dict> + <key>weight</key> + <real>20</real> + </dict> + <key>^Resources/.*\.lproj/</key> + <dict> + <key>optional</key> + <true/> + <key>weight</key> + <real>1000</real> + </dict> + <key>^Resources/.*\.lproj/locversion.plist$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>1100</real> + </dict> + <key>^Resources/Base\.lproj/</key> + <dict> + <key>weight</key> + <real>1010</real> + </dict> + <key>^[^/]+$</key> + <dict> + <key>nested</key> + <true/> + <key>weight</key> + <real>10</real> + </dict> + <key>^embedded\.provisionprofile$</key> + <dict> + <key>weight</key> + <real>20</real> + </dict> + <key>^version\.plist$</key> + <dict> + <key>weight</key> + <real>20</real> + </dict> + </dict> +</dict> +</plist> diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/Info.plist b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/Info.plist new file mode 100644 index 0000000000..762253708d --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/Info.plist @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>BuildMachineOSBuild</key> + <string>24D81</string> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>Installer</string> + <key>CFBundleIdentifier</key> + <string>org.sparkle-project.InstallerLauncher</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>Installer</string> + <key>CFBundlePackageType</key> + <string>XPC!</string> + <key>CFBundleShortVersionString</key> + <string>2.7.0-beta.1</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>CFBundleVersion</key> + <string>2040</string> + <key>DTCompiler</key> + <string>com.apple.compilers.llvm.clang.1_0</string> + <key>DTPlatformBuild</key> + <string>24B75</string> + <key>DTPlatformName</key> + <string>macosx</string> + <key>DTPlatformVersion</key> + <string>15.1</string> + <key>DTSDKBuild</key> + <string>24B75</string> + <key>DTSDKName</key> + <string>macosx15.1</string> + <key>DTXcode</key> + <string>1610</string> + <key>DTXcodeBuild</key> + <string>16B40</string> + <key>LSMinimumSystemVersion</key> + <string>10.13</string> + <key>NSHumanReadableCopyright</key> + <string>Copyright © 2016 Sparkle Project. All rights reserved.</string> + <key>XPCService</key> + <dict> + <key>JoinExistingSession</key> + <true/> + <key>ServiceType</key> + <string>Application</string> + </dict> +</dict> +</plist> diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/MacOS/Installer b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/MacOS/Installer new file mode 100755 index 0000000000..79d4e49c21 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/MacOS/Installer @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a216d32a3d1a1f643c1f98a36db88e8fb6bf76bd20e59c40cb4c72148a3e4ed +size 207088 diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/_CodeSignature/CodeResources b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/_CodeSignature/CodeResources new file mode 100644 index 0000000000..d5d0fd7441 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/_CodeSignature/CodeResources @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>files</key> + <dict/> + <key>files2</key> + <dict/> + <key>rules</key> + <dict> + <key>^Resources/</key> + <true/> + <key>^Resources/.*\.lproj/</key> + <dict> + <key>optional</key> + <true/> + <key>weight</key> + <real>1000</real> + </dict> + <key>^Resources/.*\.lproj/locversion.plist$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>1100</real> + </dict> + <key>^Resources/Base\.lproj/</key> + <dict> + <key>weight</key> + <real>1010</real> + </dict> + <key>^version.plist$</key> + <true/> + </dict> + <key>rules2</key> + <dict> + <key>.*\.dSYM($|/)</key> + <dict> + <key>weight</key> + <real>11</real> + </dict> + <key>^(.*/)?\.DS_Store$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>2000</real> + </dict> + <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key> + <dict> + <key>nested</key> + <true/> + <key>weight</key> + <real>10</real> + </dict> + <key>^.*</key> + <true/> + <key>^Info\.plist$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>20</real> + </dict> + <key>^PkgInfo$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>20</real> + </dict> + <key>^Resources/</key> + <dict> + <key>weight</key> + <real>20</real> + </dict> + <key>^Resources/.*\.lproj/</key> + <dict> + <key>optional</key> + <true/> + <key>weight</key> + <real>1000</real> + </dict> + <key>^Resources/.*\.lproj/locversion.plist$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>1100</real> + </dict> + <key>^Resources/Base\.lproj/</key> + <dict> + <key>weight</key> + <real>1010</real> + </dict> + <key>^[^/]+$</key> + <dict> + <key>nested</key> + <true/> + <key>weight</key> + <real>10</real> + </dict> + <key>^embedded\.provisionprofile$</key> + <dict> + <key>weight</key> + <real>20</real> + </dict> + <key>^version\.plist$</key> + <dict> + <key>weight</key> + <real>20</real> + </dict> + </dict> +</dict> +</plist> diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/B/_CodeSignature/CodeResources b/vendor/sparkle/mac/Sparkle.framework/Versions/B/_CodeSignature/CodeResources new file mode 100644 index 0000000000..0d41be551c --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/B/_CodeSignature/CodeResources @@ -0,0 +1,2258 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>files</key> + <dict> + <key>Resources/Base.lproj/SUUpdateAlert.nib</key> + <data> + CwzRlLP+3NEQAAEUMnf22jOrOFU= + </data> + <key>Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-101300.nib</key> + <data> + M/+1YFs6uAMZZqj4eL0oJxUe8RU= + </data> + <key>Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-110000.nib</key> + <data> + euRTl1eDCqHbR8MGAGbH4P8Mf94= + </data> + <key>Resources/Base.lproj/Sparkle.strings</key> + <data> + R6ek1FEtKYTEqN20MGTUJKyx6zU= + </data> + <key>Resources/Info.plist</key> + <data> + B41U4XHgE2BoNXVrE0DmC/YoWLM= + </data> + <key>Resources/ReleaseNotesColorStyle.css</key> + <data> + NjIvb1z7eJuLCKf9HS15O5heg50= + </data> + <key>Resources/SUStatus.nib</key> + <data> + XkU7xaQLCsO40wDWct0aBSelnCc= + </data> + <key>Resources/ar.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + qTOMJ1P/HhCcJQi4qSJV9l/b7q0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ar.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + zZ/0sjHdlPnBGe10CetKo1kF1xQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ar.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 5Ukin0TnIF0ot6Daz8OSgIoDZJ0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ca.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + l9CaCmAXFcs+Z+8rRt7PX9onkf8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ca.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + DL6k3g2A8CPQPkykQht4w+H/xYc= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/cs.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + G9Wgf14zMhU2alRSZvqclMmlTCA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/cs.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + rhsuTqRoVAfmLW+GJ1vvxJPRJ0U= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/cs.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + KTLNyu97zLvTNgaUfYWqc8nB9C4= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/da.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + NEt5JVKz+OoMSynKxJC18KXMGaA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/da.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + s6oFpgOPENk+LCyXJoLfVqZauVQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/da.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 7MO7J38OUDrmZMLJiNSeNRATia8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/de.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + YLQxXHDo3e3Udzaj8LHDIjotWzE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/de.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + W8+shbfn38JAPBpgHTMWuU0oHfQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/de.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + v/GhDrYIMwMCYqGrCESj0l1fGrE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/el.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + BS+NpAFPK7X/XzX+n99gJLhlNKU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/el.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + TNa05IunzylN4fz2uHvkj5EnyRk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/el.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 6VadVc0qrgmUnWfL3FgiI6TzchM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/en.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + FSez7jCd0gDTFFGHiWL1QXY8OUU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/en.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + 7+SiSQLU1hqbN74YfiBS1cQFVqU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/es.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + Q36SuanjGk70efU6liei3uz+Uds= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/es.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + kNfRs9Pgn30BdjtuNzhRvKXcqu0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/es.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + bt9xytBAy/CZ0aLyzGAKrh1dVZc= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fa.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + IySTqO8MqmOO/IHR5WBZdf0jYaA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fi.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + My5YiAuNV+4oR1vPL1np+nMMMOI= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fi.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + NcjaY8nD4cpjcpK4M878R5JDK1s= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fi.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + KRoZrUbgs7+EwIxs18H121Szw+0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fr.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + ffz6ccHMgxcBdH6by1YAYX1jpOQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fr.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + p4tAU3Ek6hEWqW9e8+C1L8WMQIM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fr.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + BS6wdN+n1R2u/skiaNGAfrXwcKA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/he.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + UqQyzt7i1BzLE/1l70C8EbAHpPw= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/he.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + dexoxvq5Mj4kIvh+qtUm1tChHvo= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/he.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + KN5pPk1AjF12P4/55Al/MAV3Gqs= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hr.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + b/ru54Y0QwvH9Kz9sfRPEoP5z5k= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hr.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + rdv7bU5k1tUG/tyNsQ1i/Rniypk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hr.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 70rdfUc3cUNcMed6Hq4zQBWoGrk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hu.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + VD/QPXFfEHRW7ksDLYiiO1xl1LQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hu.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + PMarJZpNhDysjzZuBuyKv8KBTXQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hu.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 5CCN2xKgiom6y3+mcWd48RVdX48= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/is.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + bQiB5tUCaD24QKubEYeBTXsAF1g= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/is.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + vGNXtUX/4qNYIzE89IO7e4GxS60= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/is.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + tplLwN1kGq9MoWLnyPQhozI6c54= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/it.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + Yev0Ro2PsLfgCLoY7JNED63PnqM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/it.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + IGMzQ4TCQgpEQaOcESzlhe8ny9I= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/it.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + HvJh7rlxinaVRX3rGu84YDTq5j8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ja.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + aqXsBwqycwXfSX0SDJcWfHOnzWA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ja.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + cAx3RQDMM17OoU39/UaI2sMDZLA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ja.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + HmrZGddjNRUGgKNxKbREtJHI93E= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ko.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + Qw6/m/LeQ/b+ApIWLXJp2ByBp7k= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ko.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + ddXQhUAjY+oi62JXHPY2OYvbpa4= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ko.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 30lZbzu4QerfbEwayFqoboTwLSs= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nb.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + fck+vL9Sgcx19X7HthrjizRGhu8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nb.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + eiq9zVX/y56Q0ymxVNFnYahFbxs= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nb.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + Xm8t/g53ktlmyq8w1aI29nEiGO8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nl.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + 5ZpTsHPgV4inhhYiISGjC03BMG4= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nl.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + LgvDZbmPK7Ox9+gNe7zXN3egxlM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nl.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + bLPDpoRNNTbPnFCZRl4HiXpYKVU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nn.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + v2c8gYdlWN2U2PbZlUQwmfWIeGk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nn.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + xNizW6Xg8cPtYBhWBmexjNf5j8U= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nn.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 8GnPpOmTh7bQRiUCzZDL6pq1KNE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pl.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + HX2RXVrN+fpwO4I60/UDyNuGj5Y= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pl.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + abNyxpda7OkXoR5Ok35XgMr9eBc= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pl.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + MXjhjMKrcFaSZhXYssMrBTXPu80= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-BR.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + YFXY6v+45ptf8TuBq2MsKKdhfQ8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-BR.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + 2iCpI0fy7Tm1zxR19dV1iCYW3bo= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-BR.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + mVP9x5C0h0Q+njDLXhZXmDsOjWM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-PT.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + pWRHcAJRvjUt7BOLr/gd+IupcGA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-PT.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + Om//DOu8+gBjHYrCHVmxKxBDvPs= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-PT.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 6IY8J7Jbjd3eG/BMld5iJSwZZvM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ro.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + a/RNqEdkehva+SwGWz11MktFGWA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ro.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + JDMBsS6fp2v5X+C0d1EJAREHIkA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ro.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + rihcAKPJ1j7EoW5B+lq7Dpci/Zo= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ru.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + Lmn0e5MDPfan55gnani1dQbR10Q= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ru.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + z+XqvyZR2X6cb0PioKpfYDCF3YY= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ru.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + ZtVq/Mf1qT9j8xdhz9ULfJ1O05k= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sk.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + 8o3l6mjHafwy5sLMMO2rZIe7xiQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sk.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + s3Cllq+eYT+urMLfXvnwsMkboWQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sk.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + gqdvwKoHMg+gDZ4MZVVqbV4yqI8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sl.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + Ny5EoZGpd5UK5c3eMIUKLR8x4/I= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sl.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + vbP9bj0jn5GKz9uEu3amXnozkWo= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sl.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + hz8NwYxW1d0aWPQDMF8/c4lJRwU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sv.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + YWicg3ZZLCEoiJ9WOUUZ6WoTZJY= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sv.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + JKbTlT+iKE5KOwvLD9N/Go2K+q0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sv.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + UH7udC5C4WHwnnx4Eg6Io23rBzk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/th.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + Cd6guArNrSoJO3e2ntd1Eys3bok= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/th.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + L4ZWMKTKnMsbMsL8V2V6OLySKLE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/th.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + O9PZTdxbP4Y306ym/2sJ6p5klE0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/tr.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + B+Y273q3UDMsG2/yaWuxMUGr2dI= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/tr.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + NstD9tjAfqULKbFJEcULPgVeJKk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/tr.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + DzlYjutWBP2dCq5D9RQaB5mCf94= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/uk.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + 6/WdcAg1mJs1/HT5krHhOxqyMWk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/uk.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + I4B4qXPwnMhj/A+yU0vvngP7oak= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/uk.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 3YLrl/aRzoPCubIKa873XDZeU1w= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_CN.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + DjCjxSor6wnKAz8bFLcPCnW1Kw0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_CN.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + xeCj4c1ifxxhDFeLtNsSc4NgBFw= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_CN.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + Q18TnLdR6a0E72xXP6ETh0FF4CY= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_HK.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + +9prrb68fl57+m9WFQ+8Ay6XjRk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_HK.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + 5Pazf5ErH02Ny5mFB+R+dwCWPVM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_HK.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + v2NQCc/vUJacBpqiLL5yANtiGc4= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_TW.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash</key> + <data> + 167IbTfOhYu699bxXBhaGehjrco= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_TW.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash</key> + <data> + SNJz+3Rb1AJ2cKstnbGWL6Q8OW8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_TW.lproj/Sparkle.strings</key> + <dict> + <key>hash</key> + <data> + 48nGyIkkDrsDKSq77pFReYsumCA= + </data> + <key>optional</key> + <true/> + </dict> + </dict> + <key>files2</key> + <dict> + <key>Autoupdate</key> + <dict> + <key>cdhash</key> + <data> + 6BtXcZ6gS50JtQWfmhDpePurpeI= + </data> + <key>requirement</key> + <string>cdhash H"e81b57719ea04b9d09b5059f9a10e978fbaba5e2" or cdhash H"99339c1a4f57bacd44d6a2ba8d72211986418570"</string> + </dict> + <key>Headers/SPUDownloadData.h</key> + <dict> + <key>hash2</key> + <data> + yhS+aH6hPb1hSOt0tT6Rej2kQoPz6wrtntLOBuoIIJs= + </data> + </dict> + <key>Headers/SPUStandardUpdaterController.h</key> + <dict> + <key>hash2</key> + <data> + gf8QsQfI+rsdQIHZdznFSFICnui4Oavnhgn9ybmv6Hs= + </data> + </dict> + <key>Headers/SPUStandardUserDriver.h</key> + <dict> + <key>hash2</key> + <data> + Y4fOofv2Ua1TAI9qM7wL8CalQGHLa1spvUNg9JB71NE= + </data> + </dict> + <key>Headers/SPUStandardUserDriverDelegate.h</key> + <dict> + <key>hash2</key> + <data> + Y1CXvdztYvr7TNU5uykV9jSjdvcpl2aCQPwjVHZe+IQ= + </data> + </dict> + <key>Headers/SPUUpdateCheck.h</key> + <dict> + <key>hash2</key> + <data> + H30F2i5GYmOu/j4JEw5WsuZbiGJXnge5gpyb9e2SHAM= + </data> + </dict> + <key>Headers/SPUUpdatePermissionRequest.h</key> + <dict> + <key>hash2</key> + <data> + hnRNYPeaK0NWoKPXEYs2AoyD6QOE0CHDJWJxKM5Ma24= + </data> + </dict> + <key>Headers/SPUUpdater.h</key> + <dict> + <key>hash2</key> + <data> + gHf2JF29zPpLfNllC9hyoYGyZVO9xJ0wNDCZNd65+hQ= + </data> + </dict> + <key>Headers/SPUUpdaterDelegate.h</key> + <dict> + <key>hash2</key> + <data> + A7Rnysd5Q/yqhviRSeLa7hyuJQcqe4eRb+qxnUx7S9g= + </data> + </dict> + <key>Headers/SPUUpdaterSettings.h</key> + <dict> + <key>hash2</key> + <data> + Nnk1lMwSl/z+ugdM8ovU0U/bQUoOWG6zjYQHXPF+kCU= + </data> + </dict> + <key>Headers/SPUUserDriver.h</key> + <dict> + <key>hash2</key> + <data> + 6/byufu2xa4bjEdEwfuFxkHUiYnK9gbYEtplebyvTSY= + </data> + </dict> + <key>Headers/SPUUserUpdateState.h</key> + <dict> + <key>hash2</key> + <data> + Z4UKJcXMF/bUmSkIiM+D8jTiS8i5x1CLqoo4uG4aQvg= + </data> + </dict> + <key>Headers/SUAppcast.h</key> + <dict> + <key>hash2</key> + <data> + M3KUgO+Ud/n5t/rXjFYzQMUTPDA8fK7W46QQfuh5DnA= + </data> + </dict> + <key>Headers/SUAppcastItem.h</key> + <dict> + <key>hash2</key> + <data> + imKkb6r+8fp+9enH9Xlnh0VZ5S12ZkwmU53UHTx/Tdo= + </data> + </dict> + <key>Headers/SUErrors.h</key> + <dict> + <key>hash2</key> + <data> + fmP8Y0mI10K5McjVGtVKtgzae36JLxmqLw3sr7vdBGY= + </data> + </dict> + <key>Headers/SUExport.h</key> + <dict> + <key>hash2</key> + <data> + XO8CQmbFThLbYg949NEGhg3g+iouIw3/3+BCCLtEdFE= + </data> + </dict> + <key>Headers/SUStandardVersionComparator.h</key> + <dict> + <key>hash2</key> + <data> + fUB7nOch1cZQ50HstpLnfxLvO14Y6oE29yRI6NcgjGw= + </data> + </dict> + <key>Headers/SUUpdatePermissionResponse.h</key> + <dict> + <key>hash2</key> + <data> + HScQok/zuc704lNfwGl1Csr6nigQLAsjeRJXG1HTuks= + </data> + </dict> + <key>Headers/SUUpdater.h</key> + <dict> + <key>hash2</key> + <data> + lvb1mNWCyofJ0X88zXvMsK5PBXrG6Pr7hUrY0E3jNvY= + </data> + </dict> + <key>Headers/SUUpdaterDelegate.h</key> + <dict> + <key>hash2</key> + <data> + oaCMM/1B690QWRbYGB0iJBf5/toBzcQq+moxURBs3jY= + </data> + </dict> + <key>Headers/SUVersionComparisonProtocol.h</key> + <dict> + <key>hash2</key> + <data> + KLu0JKx6uB4rk/YeNZ/vo+0J1qONCyZQBNIQPA9GvbQ= + </data> + </dict> + <key>Headers/SUVersionDisplayProtocol.h</key> + <dict> + <key>hash2</key> + <data> + PyPE+5u9vBlxRYDuTdf3P/wxQ26nM8m7MIw/dOerUSw= + </data> + </dict> + <key>Headers/Sparkle.h</key> + <dict> + <key>hash2</key> + <data> + OkQqMusip3u1oI5hrGeNr/32xpfTMCC4Kmg7r0Aijgw= + </data> + </dict> + <key>Modules/module.modulemap</key> + <dict> + <key>hash2</key> + <data> + K8rh62bVXcSfWKz+hWN1pbvJHG6DvbKY0ZRCrmJkz6Y= + </data> + </dict> + <key>PrivateHeaders/SPUAppcastItemStateResolver.h</key> + <dict> + <key>hash2</key> + <data> + HDvimACMGXV647Jwg2IqAFvOgJoB8G0sdPbeoW8yFO4= + </data> + </dict> + <key>PrivateHeaders/SPUGentleUserDriverReminders.h</key> + <dict> + <key>hash2</key> + <data> + 9W2dJ38WQX151mpIS0r8/EfCqZV6jEh621xwna2JVAI= + </data> + </dict> + <key>PrivateHeaders/SPUInstallationType.h</key> + <dict> + <key>hash2</key> + <data> + hj9Br7Gf1Y8X1dqNvSUHMP70K+Q+S9xZAyPYMqKthFQ= + </data> + </dict> + <key>PrivateHeaders/SPUStandardUserDriver+Private.h</key> + <dict> + <key>hash2</key> + <data> + rMdk5zH+nm0wf+Mt6k0GtFGWQiXCsql0WiPwNanN6q0= + </data> + </dict> + <key>PrivateHeaders/SPUUserAgent+Private.h</key> + <dict> + <key>hash2</key> + <data> + DD6nxqq6syhA5BxWuyLPq03uTd4zAmA7b0q6msG1hQw= + </data> + </dict> + <key>PrivateHeaders/SUAppcastItem+Private.h</key> + <dict> + <key>hash2</key> + <data> + 9H7OIT9nEoYXUBBcvTZdSOzlq81uWIUS8Mqjuhfz9Ww= + </data> + </dict> + <key>PrivateHeaders/SUInstallerLauncher+Private.h</key> + <dict> + <key>hash2</key> + <data> + yy6cRl8IqwMJwr86TS98NdnHb/WkSAaUcAoiKYUg3ZQ= + </data> + </dict> + <key>Resources/Base.lproj/SUUpdateAlert.nib</key> + <dict> + <key>hash2</key> + <data> + Ljl/rK2RBYbzB2DKeTqfO00nOp8F0b7te4vJoUFAMWE= + </data> + </dict> + <key>Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-101300.nib</key> + <dict> + <key>hash2</key> + <data> + 5Cj52Tka+Z031/CQxWJEH9YKX3Y6Tp1u6DW3YkPUGhY= + </data> + </dict> + <key>Resources/Base.lproj/SUUpdatePermissionPrompt.nib/keyedobjects-110000.nib</key> + <dict> + <key>hash2</key> + <data> + Tnq770p5QHGqzpkUx6n7W/z0kyHpNdDrcNdkt8OU2OE= + </data> + </dict> + <key>Resources/Base.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + 0wc9IIfC7mFVOBiNlVIoON5FVQhkTqbyyIQRb8RlQ7k= + </data> + </dict> + <key>Resources/Info.plist</key> + <dict> + <key>hash2</key> + <data> + 7HnNZPXUkPcbuRzacumFio5dvuNB32H5mnT23zi7GdY= + </data> + </dict> + <key>Resources/ReleaseNotesColorStyle.css</key> + <dict> + <key>hash2</key> + <data> + dr1pmXWP2OUdF+a0gttDT5tHaMArA3r2vS46AAzoy8E= + </data> + </dict> + <key>Resources/SUStatus.nib</key> + <dict> + <key>hash2</key> + <data> + SLmgK/HdXiQXJwuKJl7fLSaUfWMPoK873osUWmYIjMg= + </data> + </dict> + <key>Resources/ar.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + 33nOBJb6OPaZt3PKT2iUJ3RfF/c59DAGmt9TCQVn74A= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ar.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + ku6BdTbNrkSmKEdwyNA1hmoKbQ3uRv8JR4LK4cjqgpA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ar.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + yx9tkKjj3aOHvgdYCWXM89uhlyVeNb4oqcAenJxibwI= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ca.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + 18qLsTRnJfi0wDf6A85XbiMXGORSmuo9Ul3IK4m5gq0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ca.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + 3iHHzb3P1DvR6KaXp59ybrj1JySnfOgPbOigOIgI7es= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/cs.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + qSoDl0PIYv+OrSxtJfUYk9xeQihmzfaxAf+egKyw4y4= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/cs.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + obkk1c1EawdfEyPHqo5ddIzsUcWfClFUbg895zj3/Ag= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/cs.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + 2Nm0MQj4WgMucaSuEdljuTIGS/oceXEuVWi2kDgjRq0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/da.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + aKNcPadrNnf7wuYmBAxoRzES9XhxXRHMrW/+9MtZBQs= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/da.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + xremLoAOqEfufOFyjOrH8S8ZkWwRklRhGCGB/igGD38= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/da.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + BOUi5PfyUb/ZRM6WZOuFC34IOic4+XPJkLikDtwhZIw= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/de.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + A6JiLH5c4UX2iobAPXPHv7TLiBInrdHvtvqnnsTBxLI= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/de.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + GPjZbm0EAKfj0CK7Pb1UITo5WoDzNpf4m2XELfj3eio= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/de.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + vzIO5OLHb4N9c9rVdpvbkcMnCsZeWg05CgnyuVtFvAQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/el.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + utAXO7a8Od4ICYV3R0WQBa8ncUQ30SfruZACTuvyDxk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/el.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + V0h7tXPJI0b1Z0FEMxe7RJIn2oWGg9QUhF/cRSz7aWE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/el.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + Bl2oDZnDwbj55sSp/MNoHmcSbiOW5kxY1OGcL3k5scY= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/en.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + EBVS8ZfEIJxGSghO17emwoHQo0LVWWzBJMFs8RwvKWg= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/en.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + MN0HeTdXIxqALqUMUoLnVkRcDcvnDXqjsifU07tV3a8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/es.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + 8KSmmlZHYEiMGUwXQRV+ZDxs07XmaeH4XIYI+di1ono= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/es.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + GEtUsrVDWqXyHAV8lWPrEUWQm30jetvOjJZdlI7egwQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/es.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + ePEUtDjS6DBYvydizpitYtRl22FpVXHcVWNAkniUsQg= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fa.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + Ue4NSlcLQL9OAOrD5Ibul1RaIwZOl6vcIv7DsffzQMA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fi.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + O+ja0EMKj5RxMmW3TRALc9XTpMJ7Y7dwXm706E33rUA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fi.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + MfjVC0QQ0Dxvz6Rt03EhMaahM5Gh5rhqMSJFEqzSRLo= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fi.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + O6+s8+GKGX06x08WB1v526jOSl30MEoNnzjhYKe4IA0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fr.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + Avyaxx14FRXq/CTIDvvF7uww42SRhYgNSc960h7MCfc= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fr.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + qlNtkoH6vAA93/yxp8Stav74m46gvKb+3R26QDMSsXs= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/fr.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + O5To7z4frtvm1D6zwFzz6rpleVtia2BFro3bElXznDg= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/he.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + WsgO6lp/qlATRSKTuRsgSEyWC3VdH7EHaf6dwQH2R7E= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/he.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + l8hApwbD8wJajAI7FMKOztb0glfifBsELIttJ0rut5I= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/he.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + QThza066VUyJZXSBYuxj3O08bSXfWBkHIqiYC3HtzQg= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hr.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + L7shRNgdZIfbt5y5pioLEIo+A9I7VtgIUFpzoCFkB1I= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hr.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + 5x5zRCWzWYlbd7MkUcbPs5ZWrWQRDZnj3s9K2LmsnBw= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hr.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + Nmip9NgB014UDYN3yOsmsOFa9D3wED0L56Mve3WIVQg= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hu.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + 8LbsWTkMSczHFa4Rh9XZDRo0uCOyrV9VXUYEiEvnG7I= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hu.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + twHi8JXysxao7MlTGr178ZpB8yz1mXkij2V5n8NJWSQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/hu.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + 0UBqgjXjtRG51lEacNaLTmNvj5aFUeJ7oo1J4WYkrCw= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/is.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + 04Q9PpqtuYz6kfVhf6eI9XBxJn0LQB9Ck/ceBq1ztGU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/is.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + heK63dQF7YJvehrOEQk1jesq6v3bQBJy2jL+w5jjMlE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/is.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + 5/4A+HgH/PhUAQ3NVnURPeiIJsQYJyZ28sAObmxJlVU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/it.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + 1zxkJlohqYtSJb0pj93fJXlPkedYm2IllbilGRDFo90= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/it.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + XjyG4RGvcVUZia4jJHGYQEfgocs1iEx7iljn+vue5xY= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/it.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + 2Glm2TwT78wZfe8iqBg8z/oCgrmtzqthnzNlHvHt5ls= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ja.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + +grczSeMRPV+Oh8FAj+IPqtvuOC4WhJdknfh0eNdHWM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ja.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + pG/1lv3zRpIQkIlhEdvLHcTuPW/GfIPM8FALHstdt5c= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ja.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + z/DCngcVc1NCbqLyE8//ODAP1CugDeZOMIT273ekGn8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ko.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + /sIr/9ihcLhTxpc9CG0ZmCyhtI5lxHnRU2AY2Z7KjnA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ko.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + Q6RJbOzFDzk17QpGPfUciOrtuitReWCBlt8ucofX2AM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ko.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + 5GHHunaDv0dty7CogRs+i3C+zY3SkTVkReD90hjRdGk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nb.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + RQbKlvLGnVjjVMP5eHHNUCv5kLJl4EA6zNGdDKatbH0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nb.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + F85iUA2aHbvo23Z0jJ/T/pwJ2HOQdYD5eRyAow9cSgY= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nb.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + 6sl0vlKoRBQtvGvC7oGwtHA8/B+fNdwBGwN2AyISsXQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nl.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + tp3fY8ogv+xcQOFkz5BkDNTZHIaRrhGgT9uKfCjDB70= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nl.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + Yrwc9ESTayZoqv2JWm0nD1IHGLeAiBncPc2OeaVz8J8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nl.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + LKqbw6BLczQosJmOprpWb1goTVtKYMzVx0BxstbcoOI= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nn.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + giHYMaf7DkBFSModZ1CL3EswwwGHyOmkxRjtDP6bmk8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nn.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + V7VdecLbSo5sr8pMVUBnsJ04no1tM8XXsU/SHRWphs8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/nn.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + ouNyYGUrFy2VqYdwZnSn1emcGTGoyHGqb8Sb9Cg0cWk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pl.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + oB2rGM/SPnJLdvhUz2CJfm8TS6XhrhmHD2gFyrVSq8U= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pl.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + ubFfFWaG2RKXgeGR4DRtvbY0fH+SNJZzBebSPCo6K5c= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pl.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + KwctPd8Y+mrsEYWALAkKXeCfY4celUbA3MVn+Ye4Imc= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-BR.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + hDN04zbJliR6KRqEv4lEuAVNTjbkmyYUpKjCbWKaKdU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-BR.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + 6NvKj6GmzGQLAsGBC6IUvRBoLSRfEJuWi7ZitoTyoTk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-BR.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + Kcwam/xq0V6VzMD4+rbUrDyAtrsl186J6lEeu5K2qD4= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-PT.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + gto4ribWYRWZl0Eez6/7XZg3EesExPlGb5Nz1YVTuzE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-PT.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + st4wdNoLjj5sVIFHqDAh3cjRFhxhpzkcFP7AJSXjYkw= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/pt-PT.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + RLdbB4cQnps2k/crFyvGScdjmGE3KSkG448wTbYi4vY= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ro.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + PZZnueQNOLmQuEtkELhzxhnG+MDu7RyeOaySHSoHmYU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ro.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + IkIaDJ/HgpnBNNkP+MF2JGSd+rNgAI+o9c2aNor+ewo= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ro.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + w2L0Ki7vhlIa92HDkDRmDExmCXIGkOWil+ROXr+6I+k= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ru.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + wdiMmOcek4MJvdl1u2OoccWD56zCu2lKDGUd40bnMb8= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ru.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + /uARbwIQFupNOZvlyWgeE722GAsKcu4/QooLAEGHCBQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/ru.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + INHL+VHSTd0hjYmUXHPShl3l4xTB4C3KcCLgVn/AHGw= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sk.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + FDJ/dTwG5X34BF9lDDkFVGJUwpLeKi1MUbF072nYass= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sk.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + xa+UPXIC+og1IpGE6bA/+51E2uR9ZG+HGWKFA83tTNU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sk.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + VmLLF6tJBA+j9jFby3BVx9GagD//qx4ETRywoU71PVQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sl.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + OAq7ojI6K/xR4nFEK1OBTiJeNaHqgb8xCgzZ5Y3P7Uo= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sl.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + ik4klDAQYgMT5hrecUkfi+K/tuGyvOYk96xp+z98l74= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sl.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + VTtzpxCnkPCO1yB2GviO6AkaZFKPpcUh530dTdqFcQE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sv.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + LYXEBB7MF82Ig5MgIM9pTtJJAYJL51nzYzbVW1kdSGI= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sv.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + XDORKepHKWfDihFVMFnshPW/qjSLPLoU/zHqJQbRoBk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/sv.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + nidShaf5BKX1wQntkXeQhcGQoWUzNgVQhfHSM44GXVM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/th.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + dvWO9t2NYZ+cQoe/9B3Tib+EPOdPp4wgatHaVVhu8gQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/th.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + mxCL8k437ikdUpl3px2Ii/2fZqL85x1Fn/xe7h1YI6E= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/th.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + vj4h7104vuQqis9NXvSgrgQNz9czX6lMJqdvem4VCUk= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/tr.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + LbuuEfntGOLEDD5x0g9XHJ0C/aSUk14ltYe7Lr8tBXQ= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/tr.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + cPWCofME3hhaqw5W8btHUEa2OMuG1gW/GQTnXET0/m0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/tr.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + 4gu++3cg7m13GuNxuYNnb8ug7xC0s/B4KtUZ5D+AMec= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/uk.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + lR6DOvFkMHpmbtXQJNE1aXtRXgBbd0siVMoq01D4dhM= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/uk.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + +MphWMKEy2hsIqrjroJQcq+x1mytcNeZm+z3Lv+ll6A= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/uk.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + gvuLzOKGahqSQtAwSCb9CuBAYuDVwfj+lwwSv/NPq8s= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_CN.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + NXEAoNAKcjI5GBtGxYcUXmtz+rP06ocJSSVlaR/lnMA= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_CN.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + 8co96MJCMuKNvaPFe13uh2d028P/Cgpa8iOiNml9rfE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_CN.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + nKdLa3IQhWTKipj6MF+VwRUugjpvTVuGIpzQF/QDUYI= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_HK.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + zegYIhIFwtdJa87mjTlkalyYSz31LrnhiwNWDJDPqBU= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_HK.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + 54ogzTvsgJOl4aSWIQRzRzky1TddmGlpamTLhHMJWb0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_HK.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + AKEvYEi8FGZbbYnhpr2nqeUWrBQaj7wJjo8/KjED1U0= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_TW.lproj/SUUpdateAlert.strings</key> + <dict> + <key>hash2</key> + <data> + jdmB9inrJUf1OmYmVnORSMfdz5z1SWmBtdv39I776K4= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_TW.lproj/SUUpdatePermissionPrompt.strings</key> + <dict> + <key>hash2</key> + <data> + 4tCLZKKcNuOJ1up1IgFXUeEp7s5U5BOBGHC1EZMyrhE= + </data> + <key>optional</key> + <true/> + </dict> + <key>Resources/zh_TW.lproj/Sparkle.strings</key> + <dict> + <key>hash2</key> + <data> + NhrxIO01cQJckmOoEhEbQ73y2RcEy8drXCHDvsvcLEw= + </data> + <key>optional</key> + <true/> + </dict> + <key>Updater.app</key> + <dict> + <key>cdhash</key> + <data> + 90WG2MfFpkgJBlWKxEiwATuBXaA= + </data> + <key>requirement</key> + <string>cdhash H"f74586d8c7c5a6480906558ac448b0013b815da0" or cdhash H"93ea9650d62143da3c5869ed815d96005d44c454"</string> + </dict> + <key>XPCServices/Downloader.xpc</key> + <dict> + <key>cdhash</key> + <data> + mkFr8v/eKmF0abL7I739J66yeEk= + </data> + <key>requirement</key> + <string>cdhash H"9a416bf2ffde2a617469b2fb23bdfd27aeb27849" or cdhash H"cb9dd305745fee5fc1163adf0d902cb640baba8b"</string> + </dict> + <key>XPCServices/Installer.xpc</key> + <dict> + <key>cdhash</key> + <data> + ZUJ95bllkODwN+tBXD2q8k/9L8E= + </data> + <key>requirement</key> + <string>cdhash H"65427de5b96590e0f037eb415c3daaf24ffd2fc1" or cdhash H"66dbf7ed433bb6c7ee161b018560c1c18609febc"</string> + </dict> + </dict> + <key>rules</key> + <dict> + <key>^Resources/</key> + <true/> + <key>^Resources/.*\.lproj/</key> + <dict> + <key>optional</key> + <true/> + <key>weight</key> + <real>1000</real> + </dict> + <key>^Resources/.*\.lproj/locversion.plist$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>1100</real> + </dict> + <key>^Resources/Base\.lproj/</key> + <dict> + <key>weight</key> + <real>1010</real> + </dict> + <key>^version.plist$</key> + <true/> + </dict> + <key>rules2</key> + <dict> + <key>.*\.dSYM($|/)</key> + <dict> + <key>weight</key> + <real>11</real> + </dict> + <key>^(.*/)?\.DS_Store$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>2000</real> + </dict> + <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key> + <dict> + <key>nested</key> + <true/> + <key>weight</key> + <real>10</real> + </dict> + <key>^.*</key> + <true/> + <key>^Info\.plist$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>20</real> + </dict> + <key>^PkgInfo$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>20</real> + </dict> + <key>^Resources/</key> + <dict> + <key>weight</key> + <real>20</real> + </dict> + <key>^Resources/.*\.lproj/</key> + <dict> + <key>optional</key> + <true/> + <key>weight</key> + <real>1000</real> + </dict> + <key>^Resources/.*\.lproj/locversion.plist$</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <real>1100</real> + </dict> + <key>^Resources/Base\.lproj/</key> + <dict> + <key>weight</key> + <real>1010</real> + </dict> + <key>^[^/]+$</key> + <dict> + <key>nested</key> + <true/> + <key>weight</key> + <real>10</real> + </dict> + <key>^embedded\.provisionprofile$</key> + <dict> + <key>weight</key> + <real>20</real> + </dict> + <key>^version\.plist$</key> + <dict> + <key>weight</key> + <real>20</real> + </dict> + </dict> +</dict> +</plist> diff --git a/vendor/sparkle/mac/Sparkle.framework/Versions/Current b/vendor/sparkle/mac/Sparkle.framework/Versions/Current new file mode 120000 index 0000000000..7371f47a6f --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/Versions/Current @@ -0,0 +1 @@ +B \ No newline at end of file diff --git a/vendor/sparkle/mac/Sparkle.framework/XPCServices b/vendor/sparkle/mac/Sparkle.framework/XPCServices new file mode 120000 index 0000000000..99c46ea236 --- /dev/null +++ b/vendor/sparkle/mac/Sparkle.framework/XPCServices @@ -0,0 +1 @@ +Versions/Current/XPCServices \ No newline at end of file diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index f4f5ce8e97..42b806ce5a 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -94,9 +94,8 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ ./libpag) add_executable(PAGViewer ${RC_FILES} ${PAG_VIEWER_SOURCE_FILES} ${QT_RESOURCES}) if (APPLE) - add_vendor_target(viewer-vendor SHARED_VENDORS sparkle CONFIG_DIR ${CMAKE_SOURCE_DIR}/../) list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/rttr/mac/include) - list(APPEND VIEWER_VENDOR_LIBRARIES ${CMAKE_SOURCE_DIR}/../third_party/out/sparkle/lib/Sparkle.framework) + list(APPEND VIEWER_VENDOR_LIBRARIES ${CMAKE_SOURCE_DIR}/../vendor/sparkle/mac/Sparkle.framework) elseif (WIN32) list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/rttr/win/include) list(APPEND PAG_VIEWER_INCLUDES ../vendor/winsparkle/include) diff --git a/viewer/images/update-red.png b/viewer/images/update-red.png index 2238138b52..4ebb227bfe 100644 --- a/viewer/images/update-red.png +++ b/viewer/images/update-red.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cfc4bf393e72369088ce0bbf063ee336b99f709fda00ced46574122b549c2de -size 14778 +oid sha256:22cda8f54c74aedd8611917efa850888cd4fac3118ecd87d435eccb467a6300a +size 10611 From e7c8a96543fb6f68dd10cd1d6d723b20abe92d38 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Fri, 21 Mar 2025 17:57:24 +0800 Subject: [PATCH 14/36] disable rtti for viewer, modify some programming details --- viewer/CMakeLists.txt | 3 +- viewer/qml/components/PAGWindow.qml | 104 ++++++++++++++++------------ viewer/src/PAGViewer.cpp | 2 +- viewer/src/PAGViewer.h | 3 +- viewer/src/main.cpp | 21 +++++- viewer/src/rendering/PAGView.cpp | 18 +++-- viewer/src/rendering/PAGView.h | 2 +- 7 files changed, 92 insertions(+), 61 deletions(-) diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index 42b806ce5a..f4bd81c9c0 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -11,11 +11,12 @@ if (NOT CMAKE_BUILD_TYPE) endif () if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_definitions(-Werror -Wall -Wextra -Weffc++ -pedantic -Werror=return-type) + add_definitions(-Werror -Wall -Wextra -Weffc++ -pedantic -Werror=return-type -fno-rtti) endif () if (MSVC) add_compile_options("/utf-8") + add_compile_options(/GR-) endif (MSVC) # Sets flags diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index cc1de653e0..df01c15ed1 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -274,53 +274,71 @@ Window { anchors.bottomMargin: window.isWindows ? 1 : 0 clip: true } - MouseArea { - enabled: window.isWindows && window.canResize - anchors.fill: parent - hoverEnabled: true - cursorShape: { - const pos = Qt.point(mouseX, mouseY); - const offset = resizeHandleSize; - if ((pos.x < offset) && (pos.y >= (height - offset))) { - return Qt.SizeBDiagCursor; - } - if ((pos.x < offset) && (pos.y < offset)) { - return Qt.SizeFDiagCursor; - } - if ((pos.x >= (width - offset)) && (pos.y >= (height - offset))) { - return Qt.SizeFDiagCursor; - } - if ((pos.x < offset) || ((pos.x >= (width - offset)) && (pos.y > titleBarHeight))) { - return Qt.SizeHorCursor; - } - if ((pos.y > (height - offset)) || ((pos.y < offset) && (pos.x < (width - 120)))) { - return Qt.SizeVerCursor; + + Loader { + active: window.isWindows && window.canResize + sourceComponent: Component { + MouseArea { + enabled: window.isWindows && window.canResize + anchors.fill: parent + hoverEnabled: window.isWindows && window.canResize + cursorShape: { + console.log("MouseArea::cursorShape") + console.log("window.isWindows && window.canResize: " + (window.isWindows && window.canResize)) + const pos = Qt.point(mouseX, mouseY); + const offset = resizeHandleSize; + if ((pos.x < offset) && (pos.y >= (height - offset))) { + return Qt.SizeBDiagCursor; + } + if ((pos.x < offset) && (pos.y < offset)) { + return Qt.SizeFDiagCursor; + } + if ((pos.x >= (width - offset)) && (pos.y >= (height - offset))) { + return Qt.SizeFDiagCursor; + } + if ((pos.x < offset) || ((pos.x >= (width - offset)) && (pos.y > titleBarHeight))) { + return Qt.SizeHorCursor; + } + if ((pos.y > (height - offset)) || ((pos.y < offset) && (pos.x < (width - 120)))) { + return Qt.SizeVerCursor; + } + } + onClicked: { + console.log("width: " + width + " height: " + height) + } + acceptedButtons: Qt.NoButton } } - acceptedButtons: Qt.NoButton } - DragHandler { - id: resizeHandler - enabled: window.isWindows && window.canResize - target: null - grabPermissions: TapHandler.TakeOverForbidden - onActiveChanged: if (active) { - const pos = resizeHandler.centroid.position; - const offset = resizeHandleSize + 10; - let edges = 0; - if (pos.x < offset) { - edges |= Qt.LeftEdge; - } - if (pos.x >= (width - offset)) { - edges += Qt.RightEdge; - } - if (pos.y < offset) { - edges |= Qt.TopEdge; - } - if (pos.y >= (height - offset)) { - edges |= Qt.BottomEdge; + Loader { + active: window.isWindows && window.canResize + sourceComponent: Component { + DragHandler { + id: resizeHandler + enabled: window.isWindows && window.canResize + target: null + grabPermissions: TapHandler.TakeOverForbidden + onActiveChanged: { + if (active) { + const pos = resizeHandler.centroid.position; + const offset = resizeHandleSize + 10; + let edges = 0; + if (pos.x < offset) { + edges |= Qt.LeftEdge; + } + if (pos.x >= (width - offset)) { + edges += Qt.RightEdge; + } + if (pos.y < offset) { + edges |= Qt.TopEdge; + } + if (pos.y >= (height - offset)) { + edges |= Qt.BottomEdge; + } + window.startSystemResize(edges); + } + } } - window.startSystemResize(edges); } } diff --git a/viewer/src/PAGViewer.cpp b/viewer/src/PAGViewer.cpp index 4b2e967357..95a35cdc8c 100644 --- a/viewer/src/PAGViewer.cpp +++ b/viewer/src/PAGViewer.cpp @@ -26,7 +26,7 @@ PAGViewer::PAGViewer(int& argc, char** argv) : QApplication(argc, argv) { auto PAGViewer::event(QEvent* event) -> bool { if (event->type() == QEvent::FileOpen) { - auto openEvent = dynamic_cast<QFileOpenEvent*>(event); + auto openEvent = static_cast<QFileOpenEvent*>(event); auto path = openEvent->file(); openFile(path); } diff --git a/viewer/src/PAGViewer.h b/viewer/src/PAGViewer.h index 091e61bde1..f81f73026a 100644 --- a/viewer/src/PAGViewer.h +++ b/viewer/src/PAGViewer.h @@ -19,8 +19,7 @@ #pragma once #include <QApplication> - -class PAGWindow; +#include "rendering/PAGWindow.h" class PAGViewer : public QApplication { Q_OBJECT diff --git a/viewer/src/main.cpp b/viewer/src/main.cpp index 43091fcc97..b83951f447 100644 --- a/viewer/src/main.cpp +++ b/viewer/src/main.cpp @@ -24,6 +24,22 @@ #include "rendering/PAGView.h" int main(int argc, char* argv[]) { + bool cpuMode = false; + std::string filePath; + + for (int i = 0; i < argc; i++) { + auto arg = std::string(argv[i]); + if (arg == "-cpu") { + cpuMode = true; + } else if (argc > 1) { + filePath = arg; + } + } + + if (cpuMode) { + QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); + } + QApplication::setApplicationName("PAGViewer"); QApplication::setOrganizationName("Tencent"); QSurfaceFormat defaultFormat = QSurfaceFormat(); @@ -42,8 +58,7 @@ int main(int argc, char* argv[]) { PAGViewer app(argc, argv); QApplication::setWindowIcon(QIcon(":/images/window-icon.png")); qmlRegisterType<pag::PAGView>("PAG", 1, 0, "PAGView"); - auto rootPath = QApplication::applicationDirPath(); - rootPath = QFileInfo(rootPath + "/../../").absolutePath(); - app.openFile(rootPath + "/assets/test2.pag"); + app.openFile(filePath.data()); + return QApplication::exec(); } diff --git a/viewer/src/rendering/PAGView.cpp b/viewer/src/rendering/PAGView.cpp index 7eeb01d03c..a9834a0d33 100644 --- a/viewer/src/rendering/PAGView.cpp +++ b/viewer/src/rendering/PAGView.cpp @@ -17,8 +17,6 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "PAGView.h" -#include <QGuiApplication> -#include <QQuickWindow> #include <QSGImageNode> #include "pag/file.h" #include "rendering/PAGRenderThread.h" @@ -46,14 +44,14 @@ PAGView::~PAGView() { auto PAGView::getPAGWidth() const -> int { if (pagFile == nullptr) { - return 100; + return -1; } return pagFile->width(); } auto PAGView::getPAGHeight() const -> int { if (pagFile == nullptr) { - return 100; + return -1; } return pagFile->height(); } @@ -62,7 +60,7 @@ auto PAGView::getTotalFrame() const -> int { if (pagFile == nullptr) { return 0; } - int totalFrames = static_cast<int>((getDuration() * pagFile->frameRate() + 500) / 1000); + int totalFrames = static_cast<int>(std::round(getDuration() * pagFile->frameRate() / 1000.0)); if (totalFrames < 1) { totalFrames = 1; } @@ -102,7 +100,7 @@ auto PAGView::getFilePath() const -> QString { auto PAGView::getBackgroundColor() const -> QColor { if (pagFile == nullptr) { - return QColor::fromRgb(0, 0, 0); + return QColorConstants::Black; } auto color = pagFile->getFile()->getRootLayer()->composition->backgroundColor; @@ -111,7 +109,7 @@ auto PAGView::getBackgroundColor() const -> QColor { auto PAGView::getPreferredSize() const -> QSizeF { if (pagFile == nullptr) { - return {200, 200}; + return {0, 0}; } auto quickWindow = window(); @@ -209,7 +207,7 @@ auto PAGView::nextFrame() -> void { setIsPlaying(false); auto progress = this->progress + progressPerFrame; if (progress > 1) { - progress = 0; + progress = 0.0; } setProgress(progress); } @@ -221,7 +219,7 @@ auto PAGView::previousFrame() -> void { setIsPlaying(false); auto progress = this->progress - progressPerFrame; if (progress < 0) { - progress = 1; + progress = 1.0; } setProgress(progress); } @@ -242,7 +240,7 @@ QSGNode* PAGView::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) { } } - auto node = dynamic_cast<QSGImageNode*>(oldNode); + auto node = static_cast<QSGImageNode*>(oldNode); auto texture = drawable->getTexture(); if (texture) { if (node == nullptr) { diff --git a/viewer/src/rendering/PAGView.h b/viewer/src/rendering/PAGView.h index df5aa49d42..252c4839c8 100644 --- a/viewer/src/rendering/PAGView.h +++ b/viewer/src/rendering/PAGView.h @@ -79,7 +79,7 @@ class PAGView : public QQuickItem { qreal lastPixelRatio = 1; double progress = 0.0; double progressPerFrame = 0.0; - QString filePath; + QString filePath = ""; PAGPlayer* pagPlayer = nullptr; PAGRenderThread* renderThread = nullptr; std::shared_ptr<PAGFile> pagFile = nullptr; From 19a4aaf7be2334b43d50d25612948894372a52a0 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Mon, 24 Mar 2025 15:20:28 +0800 Subject: [PATCH 15/36] Modify the method getPreferredSize() for viewer --- viewer/qml/components/PAGWindow.qml | 437 ++++++++++++----------- viewer/src/rendering/PAGRenderThread.cpp | 2 +- viewer/src/rendering/PAGView.cpp | 46 ++- viewer/src/rendering/PAGView.h | 6 +- 4 files changed, 249 insertions(+), 242 deletions(-) diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index df01c15ed1..627fdd9738 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -28,243 +28,254 @@ Window { color: "#00000000" flags: isWindows ? (Qt.FramelessWindowHint | Qt.Window | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) : Qt.Window - Rectangle { - z: 1 - visible: window.isWindows - anchors.fill: parent - radius: 5 - clip: true - border.width: 0 - border.color: "#00FFFFFF" - color: "#15FFFFFF" - - PAGRectangle { - id: titleBar - height: window.titleBarHeight - color: "#22FFFFFF" - radius: 5 - leftTopRadius: !window.isMaximized - rightTopRadius: !window.isMaximized - rightBottomRadius: false - leftBottomRadius: false - anchors.top: parent.top - anchors.topMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 1 - anchors.right: parent.right - anchors.rightMargin: 1 - - PAGRectangle { - height: window.titleBarHeight - 1 - color: "#20202a" + Loader { + active: window.isWindows + sourceComponent: Component { + Rectangle { + z: 1 + anchors.fill: parent radius: 5 - leftTopRadius: !window.isMaximized - rightTopRadius: !window.isMaximized - rightBottomRadius: false - leftBottomRadius: false - anchors.top: parent.top - anchors.topMargin: 1 - anchors.left: parent.left - anchors.leftMargin: 0 - anchors.right: parent.right - anchors.rightMargin: 0 - } - MouseArea { - id: mouseArea - property int mouseLastX: 0 + clip: true + border.width: 0 + border.color: "#00FFFFFF" + color: "#15FFFFFF" - property int mouseLastY: 0 + PAGRectangle { + id: titleBar + height: window.titleBarHeight + color: "#22FFFFFF" + radius: 5 + leftTopRadius: !window.isMaximized + rightTopRadius: !window.isMaximized + rightBottomRadius: false + leftBottomRadius: false + anchors.top: parent.top + anchors.topMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 1 + anchors.right: parent.right + anchors.rightMargin: 1 - property int windowX: 0 + PAGRectangle { + height: window.titleBarHeight - 1 + color: "#20202a" + radius: 5 + leftTopRadius: !window.isMaximized + rightTopRadius: !window.isMaximized + rightBottomRadius: false + leftBottomRadius: false + anchors.top: parent.top + anchors.topMargin: 1 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + } + MouseArea { + id: mouseArea + property int mouseLastX: 0 - property int windowY: 0 + property int mouseLastY: 0 - property int windowWidth: 0 - height: 40 - anchors.fill: parent - anchors.topMargin: resizeHandleSize - onPressed: { - mouseLastX = mouseX; - mouseLastY = mouseY; - windowX = window.x; - windowY = window.y; - windowWidth = window.width; - } - onPositionChanged: { - let offsetX = mouseX - mouseLastX; - let offsetY = mouseY - mouseLastY; - if (((windowX - window.x) === offsetX) && ((windowY - window.y) === offsetY)) { - return; - } - if (window.isMaximized) { - setMaximized(false); - let sourceX = (mouseLastX - 240) / (windowWidth - 240 - 120); - let targetX = (window.width - 240 - 120) * sourceX + 240; - let xChange = mouseLastX - targetX; - window.x += xChange; - mouseLastX = targetX; - } - if (offsetX !== 0) { - window.x += offsetX; - } - if (offsetY !== 0) { - window.y += offsetY; - } - ensurePositionMovable(); - } - onDoubleClicked: { - setMaximized(!window.isMaximized); - } - } - Text { - id: windowsTitleText - visible: window.width > (window.hasMenu ? 400 : 150) - y: 8 - height: 16 - color: "#DDDDDD" - text: window.title - verticalAlignment: Text.AlignVCenter - font.weight: Font.Bold - renderType: Text.NativeRendering - elide: Text.ElideRight - anchors.left: parent.left - anchors.leftMargin: window.hasMenu ? (window.width < 600 ? 220 : 0) : 0 - anchors.right: parent.right - anchors.rightMargin: window.hasMenu ? (window.width < 600 ? 120 : 0) : 0 - horizontalAlignment: Text.AlignHCenter - font.pixelSize: 12 - font.family: "Microsoft Yahei" - } - Image { - id: iconImage - x: 10 - y: 8 - width: 16 - height: 16 - fillMode: Image.PreserveAspectFit - source: "qrc:/images/window-icon-32x.png" - } - Row { - id: windowControlRow - anchors.right: parent.right + property int windowX: 0 - PAGRectangle { - id: minButton - width: 40 - height: window.titleBarHeight - 1 - border.width: 0 - color: minMouseArea.containsMouse ? "#33FFFFFF" : "#00ffffff" + property int windowY: 0 - Image { - id: minImage - width: 10 - height: 10 - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - sourceSize.height: 24 - sourceSize.width: 24 - fillMode: Image.PreserveAspectFit - source: "qrc:/images/minimize-dark.svg" - } - MouseArea { - id: minMouseArea - hoverEnabled: true + property int windowWidth: 0 + height: 40 anchors.fill: parent - onClicked: { - window.visibility = Window.Minimized; - window.isMaximized = false; + anchors.topMargin: resizeHandleSize + onPressed: { + mouseLastX = mouseX; + mouseLastY = mouseY; + windowX = window.x; + windowY = window.y; + windowWidth = window.width; } - } - } - PAGRectangle { - id: zoomButton - width: 40 - height: window.titleBarHeight - 1 - color: zoomMouseArea.containsMouse ? "#33FFFFFF" : "#00ffffff" - border.width: 0 - - Image { - id: zoomImage - width: 10 - height: 10 - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - sourceSize.height: 24 - sourceSize.width: 24 - fillMode: Image.PreserveAspectFit - source: window.isMaximized ? "qrc:/images/restore-dark.svg" : "qrc:/images/maximize-dark.svg" - } - MouseArea { - id: zoomMouseArea - hoverEnabled: true - anchors.fill: parent - onClicked: { + onPositionChanged: { + let offsetX = mouseX - mouseLastX; + let offsetY = mouseY - mouseLastY; + if (((windowX - window.x) === offsetX) && ((windowY - window.y) === offsetY)) { + return; + } + if (window.isMaximized) { + setMaximized(false); + let sourceX = (mouseLastX - 240) / (windowWidth - 240 - 120); + let targetX = (window.width - 240 - 120) * sourceX + 240; + let xChange = mouseLastX - targetX; + window.x += xChange; + mouseLastX = targetX; + } + if (offsetX !== 0) { + window.x += offsetX; + } + if (offsetY !== 0) { + window.y += offsetY; + } + ensurePositionMovable(); + } + onDoubleClicked: { setMaximized(!window.isMaximized); } } - } - PAGRectangle { - id: closeButton - width: 40 - height: window.titleBarHeight - 1 - color: closeMouseArea.containsMouse ? "#FFDD0000" : "#00ffffff" - border.width: 0 - radius: titleBar.radius - leftTopRadius: false - leftBottomRadius: false - rightBottomRadius: false - + Text { + id: windowsTitleText + visible: window.width > (window.hasMenu ? 400 : 150) + y: 8 + height: 16 + color: "#DDDDDD" + text: window.title + verticalAlignment: Text.AlignVCenter + font.weight: Font.Bold + renderType: Text.NativeRendering + elide: Text.ElideRight + anchors.left: parent.left + anchors.leftMargin: window.hasMenu ? (window.width < 600 ? 220 : 0) : 0 + anchors.right: parent.right + anchors.rightMargin: window.hasMenu ? (window.width < 600 ? 120 : 0) : 0 + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 12 + font.family: "Microsoft Yahei" + } Image { - id: closeImage - width: 10 - height: 10 - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - sourceSize.height: 24 - sourceSize.width: 24 + id: iconImage + x: 10 + y: 8 + width: 16 + height: 16 fillMode: Image.PreserveAspectFit - source: "qrc:/images/close-dark.svg" + source: "qrc:/images/window-icon-32x.png" } - MouseArea { - id: closeMouseArea - hoverEnabled: true - anchors.fill: parent - onClicked: { - window.close(); + Row { + id: windowControlRow + anchors.right: parent.right + + PAGRectangle { + id: minButton + width: 40 + height: window.titleBarHeight - 1 + border.width: 0 + color: minMouseArea.containsMouse ? "#33FFFFFF" : "#00ffffff" + + Image { + id: minImage + width: 10 + height: 10 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + sourceSize.height: 24 + sourceSize.width: 24 + fillMode: Image.PreserveAspectFit + source: "qrc:/images/minimize-dark.svg" + } + MouseArea { + id: minMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: { + window.visibility = Window.Minimized; + window.isMaximized = false; + } + } + } + PAGRectangle { + id: zoomButton + width: 40 + height: window.titleBarHeight - 1 + color: zoomMouseArea.containsMouse ? "#33FFFFFF" : "#00ffffff" + border.width: 0 + + Image { + id: zoomImage + width: 10 + height: 10 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + sourceSize.height: 24 + sourceSize.width: 24 + fillMode: Image.PreserveAspectFit + source: window.isMaximized ? "qrc:/images/restore-dark.svg" : "qrc:/images/maximize-dark.svg" + } + MouseArea { + id: zoomMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: { + setMaximized(!window.isMaximized); + } + } + } + PAGRectangle { + id: closeButton + width: 40 + height: window.titleBarHeight - 1 + color: closeMouseArea.containsMouse ? "#FFDD0000" : "#00ffffff" + border.width: 0 + radius: titleBar.radius + leftTopRadius: false + leftBottomRadius: false + rightBottomRadius: false + + Image { + id: closeImage + width: 10 + height: 10 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + sourceSize.height: 24 + sourceSize.width: 24 + fillMode: Image.PreserveAspectFit + source: "qrc:/images/close-dark.svg" + } + MouseArea { + id: closeMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: { + window.close(); + } + } } } } } } } - Rectangle { - visible: !window.isWindows - height: window.titleBarHeight - color: "#20202a" - anchors.top: parent.top - anchors.topMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - anchors.right: parent.right - anchors.rightMargin: 0 - Text { - id: macTitleText - y: 3 - height: 16 - color: "#DDDDDD" - text: window.title - verticalAlignment: Text.AlignVCenter - font.weight: Font.Medium - elide: Text.ElideRight - anchors.left: parent.left - anchors.leftMargin: 0 - anchors.right: parent.right - anchors.rightMargin: 0 - horizontalAlignment: Text.AlignHCenter - font.pixelSize: 12 + Loader { + active: !window.isWindows + sourceComponent: Component { + Rectangle { + visible: !window.isWindows + height: window.titleBarHeight + color: "#20202a" + anchors.top: parent.top + anchors.topMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + + Text { + id: macTitleText + y: 3 + height: 16 + color: "#DDDDDD" + text: window.title + verticalAlignment: Text.AlignVCenter + font.weight: Font.Medium + elide: Text.ElideRight + anchors.left: parent.left + anchors.leftMargin: 0 + anchors.right: parent.right + anchors.rightMargin: 0 + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 12 + } + } } } + Item { id: placeholder anchors.fill: parent diff --git a/viewer/src/rendering/PAGRenderThread.cpp b/viewer/src/rendering/PAGRenderThread.cpp index 8a3d973516..880617bde4 100644 --- a/viewer/src/rendering/PAGRenderThread.cpp +++ b/viewer/src/rendering/PAGRenderThread.cpp @@ -30,7 +30,6 @@ auto PAGRenderThread::flush() -> void { } auto PAGRenderThread::shutDown() -> void { - exit(); if (QGuiApplication::instance()) { auto mainThread = QGuiApplication::instance()->thread(); if (pagView->drawable) { @@ -38,6 +37,7 @@ auto PAGRenderThread::shutDown() -> void { } moveToThread(mainThread); } + exit(); } } // namespace pag \ No newline at end of file diff --git a/viewer/src/rendering/PAGView.cpp b/viewer/src/rendering/PAGView.cpp index a9834a0d33..e286756575 100644 --- a/viewer/src/rendering/PAGView.cpp +++ b/viewer/src/rendering/PAGView.cpp @@ -62,7 +62,7 @@ auto PAGView::getTotalFrame() const -> int { } int totalFrames = static_cast<int>(std::round(getDuration() * pagFile->frameRate() / 1000.0)); if (totalFrames < 1) { - totalFrames = 1; + totalFrames = 0; } return totalFrames; } @@ -72,8 +72,8 @@ auto PAGView::getCurrentFrame() const -> int { return static_cast<int>(std::round(getProgress() * (totalFrames - 1))); } -auto PAGView::getIsPlaying() const -> bool { - return isPlaying; +auto PAGView::isPlaying() const -> bool { + return isPlaying_; } auto PAGView::getShowVideoFrames() const -> bool { @@ -85,7 +85,7 @@ auto PAGView::getShowVideoFrames() const -> bool { auto PAGView::getDuration() const -> double { if (pagPlayer == nullptr) { - return 1.0; + return 0.0; } return static_cast<double>(pagPlayer->duration()) / 1000.0; } @@ -113,30 +113,26 @@ auto PAGView::getPreferredSize() const -> QSizeF { } auto quickWindow = window(); - qreal scaleRatio = quickWindow->devicePixelRatio(); int pagWidth = getPAGWidth(); int pagHeight = getPAGHeight(); auto screen = quickWindow->screen(); QSize screenSize = screen->availableVirtualSize(); - while ((pagHeight / scaleRatio) > (screenSize.height() * 0.9)) { - scaleRatio *= 1.2; - } - while ((pagWidth / scaleRatio) > screenSize.width()) { - scaleRatio *= 1.2; + qreal maxHeight = screenSize.height() * 0.8; + qreal minHeight = quickWindow->minimumHeight(); + qreal width = 0; + qreal height = 0; + + if (pagWidth < minHeight) { + height = minHeight; + width = pagWidth * height / pagHeight; + } else { + height = pagHeight; + width = pagWidth; } - auto height = pagHeight / scaleRatio; - auto width = pagWidth / scaleRatio; - if ((height < quickWindow->minimumHeight()) && (width < quickWindow->minimumWidth())) { - if (height > width) { - height = quickWindow->minimumHeight(); - scaleRatio = pagHeight / height; - width = pagWidth / scaleRatio; - } else { - width = quickWindow->minimumWidth(); - scaleRatio = pagWidth / width; - height = pagHeight / scaleRatio; - } + if (height > maxHeight) { + width = width * maxHeight / height; + height = maxHeight; } return {width, height}; @@ -146,10 +142,10 @@ auto PAGView::setIsPlaying(bool isPlaying) -> void { if (pagFile == nullptr) { return; } - if (this->isPlaying == isPlaying) { + if (this->isPlaying_ == isPlaying) { return; } - this->isPlaying = isPlaying; + this->isPlaying_ = isPlaying; Q_EMIT isPlayingChanged(isPlaying); if (isPlaying) { lastPlayTime = tgfx::Clock::Now(); @@ -255,7 +251,7 @@ QSGNode* PAGView::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) { auto displayTime = timeNow - lastPlayTime; lastPlayTime = timeNow; - if (isPlaying) { + if (isPlaying_) { auto duration = pagPlayer->duration(); if (duration > 0) { auto progress = diff --git a/viewer/src/rendering/PAGView.h b/viewer/src/rendering/PAGView.h index 252c4839c8..95fdddd624 100644 --- a/viewer/src/rendering/PAGView.h +++ b/viewer/src/rendering/PAGView.h @@ -37,7 +37,7 @@ class PAGView : public QQuickItem { Q_PROPERTY(int pagHeight READ getPAGHeight) Q_PROPERTY(int totalFrame READ getTotalFrame) Q_PROPERTY(int currentFrame READ getCurrentFrame) - Q_PROPERTY(bool isPlaying READ getIsPlaying WRITE setIsPlaying NOTIFY isPlayingChanged) + Q_PROPERTY(bool isPlaying READ isPlaying WRITE setIsPlaying NOTIFY isPlayingChanged) Q_PROPERTY(bool showVideoFrames READ getShowVideoFrames WRITE setShowVideoFrames) Q_PROPERTY(double duration READ getDuration) Q_PROPERTY(double progress READ getProgress WRITE setProgress NOTIFY progressChanged) @@ -49,7 +49,7 @@ class PAGView : public QQuickItem { auto getPAGHeight() const -> int; auto getTotalFrame() const -> int; auto getCurrentFrame() const -> int; - auto getIsPlaying() const -> bool; + auto isPlaying() const -> bool; auto getShowVideoFrames() const -> bool; auto getDuration() const -> double; auto getProgress() const -> double; @@ -73,7 +73,7 @@ class PAGView : public QQuickItem { private: int64_t lastPlayTime = 0; - bool isPlaying = true; + bool isPlaying_ = true; qreal lastWidth = 0; qreal lastHeight = 0; qreal lastPixelRatio = 1; From 2ee835bf73bf043388afb794f547041f4916580d Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Mon, 24 Mar 2025 16:55:10 +0800 Subject: [PATCH 16/36] Modify the loading method of the viewer's titleBar --- viewer/qml/Main.qml | 1 + viewer/qml/components/PAGWindow.qml | 36 ++++++++++------------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 1310e97257..1a84b9c1c3 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -13,6 +13,7 @@ PAGWindow { minimumWidth: 400 + windowPadding minimumHeight: 320 + windowTitleBarHeight resizeHandleSize: 5 + titleBarHeight: windowTitleBarHeight property string filePath property bool lastPlayStatusIsPlaying: false diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index 627fdd9738..a64bd2ca30 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -7,6 +7,7 @@ Window { default property alias contents: placeholder.data required property int resizeHandleSize + required property int titleBarHeight property bool isWindows: Qt.platform.os === "windows" property bool isMaximized: false @@ -22,14 +23,14 @@ Window { property int windowLastWidth: 0 property int windowLastHeight: 0 - - property int titleBarHeight: 32 visible: true color: "#00000000" flags: isWindows ? (Qt.FramelessWindowHint | Qt.Window | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) : Qt.Window Loader { + id: windowsTitleBarLoader active: window.isWindows + anchors.fill: parent sourceComponent: Component { Rectangle { z: 1 @@ -241,41 +242,34 @@ Window { } } } - Loader { + id: macTitleLoader active: !window.isWindows + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: window.titleBarHeight sourceComponent: Component { Rectangle { - visible: !window.isWindows - height: window.titleBarHeight + anchors.fill: parent color: "#20202a" - anchors.top: parent.top - anchors.topMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - anchors.right: parent.right - anchors.rightMargin: 0 Text { id: macTitleText - y: 3 height: 16 + anchors.fill: parent color: "#DDDDDD" text: window.title verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter font.weight: Font.Medium elide: Text.ElideRight - anchors.left: parent.left - anchors.leftMargin: 0 - anchors.right: parent.right - anchors.rightMargin: 0 - horizontalAlignment: Text.AlignHCenter font.pixelSize: 12 + renderType: Text.NativeRendering } } } } - Item { id: placeholder anchors.fill: parent @@ -285,7 +279,6 @@ Window { anchors.bottomMargin: window.isWindows ? 1 : 0 clip: true } - Loader { active: window.isWindows && window.canResize sourceComponent: Component { @@ -294,8 +287,6 @@ Window { anchors.fill: parent hoverEnabled: window.isWindows && window.canResize cursorShape: { - console.log("MouseArea::cursorShape") - console.log("window.isWindows && window.canResize: " + (window.isWindows && window.canResize)) const pos = Qt.point(mouseX, mouseY); const offset = resizeHandleSize; if ((pos.x < offset) && (pos.y >= (height - offset))) { @@ -314,9 +305,6 @@ Window { return Qt.SizeVerCursor; } } - onClicked: { - console.log("width: " + width + " height: " + height) - } acceptedButtons: Qt.NoButton } } From e8a0daf03210bf202b79da3b31fb4397e4096cec Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Mon, 24 Mar 2025 17:47:34 +0800 Subject: [PATCH 17/36] Fix the issue with the viewer's Windows resize handle --- viewer/qml/components/PAGWindow.qml | 129 +++++++++++++--------------- 1 file changed, 62 insertions(+), 67 deletions(-) diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index 627fdd9738..40a1d25561 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -29,7 +29,42 @@ Window { flags: isWindows ? (Qt.FramelessWindowHint | Qt.Window | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) : Qt.Window Loader { + active: window.isWindows && window.canResize + anchors.fill: parent + sourceComponent: Component { + MouseArea { + enabled: window.canResize + anchors.fill: parent + hoverEnabled: window.canResize + cursorShape: { + const pos = Qt.point(mouseX, mouseY); + const offset = resizeHandleSize; + if ((pos.x < offset) && (pos.y >= (height - offset))) { + return Qt.SizeBDiagCursor; + } + if ((pos.x < offset) && (pos.y < offset)) { + return Qt.SizeFDiagCursor; + } + if ((pos.x >= (width - offset)) && (pos.y >= (height - offset))) { + return Qt.SizeFDiagCursor; + } + if ((pos.x < offset) || ((pos.x >= (width - offset)) && (pos.y > titleBarHeight))) { + return Qt.SizeHorCursor; + } + if ((pos.y > (height - offset)) || ((pos.y < offset) && (pos.x < (width - 120)))) { + return Qt.SizeVerCursor; + } + } + acceptedButtons: Qt.NoButton + } + } + } + + Loader { + id: windowsControlsLoader active: window.isWindows + anchors.fill: parent + sourceComponent: Component { Rectangle { z: 1 @@ -238,6 +273,33 @@ Window { } } } + + DragHandler { + id: resizeHandler + enabled: window.canResize + target: null + grabPermissions: TapHandler.TakeOverForbidden + onActiveChanged: { + if (active) { + const pos = resizeHandler.centroid.position; + const offset = resizeHandleSize + 10; + let edges = 0; + if (pos.x < offset) { + edges |= Qt.LeftEdge; + } + if (pos.x >= (width - offset)) { + edges += Qt.RightEdge; + } + if (pos.y < offset) { + edges |= Qt.TopEdge; + } + if (pos.y >= (height - offset)) { + edges |= Qt.BottomEdge; + } + window.startSystemResize(edges); + } + } + } } } } @@ -286,73 +348,6 @@ Window { clip: true } - Loader { - active: window.isWindows && window.canResize - sourceComponent: Component { - MouseArea { - enabled: window.isWindows && window.canResize - anchors.fill: parent - hoverEnabled: window.isWindows && window.canResize - cursorShape: { - console.log("MouseArea::cursorShape") - console.log("window.isWindows && window.canResize: " + (window.isWindows && window.canResize)) - const pos = Qt.point(mouseX, mouseY); - const offset = resizeHandleSize; - if ((pos.x < offset) && (pos.y >= (height - offset))) { - return Qt.SizeBDiagCursor; - } - if ((pos.x < offset) && (pos.y < offset)) { - return Qt.SizeFDiagCursor; - } - if ((pos.x >= (width - offset)) && (pos.y >= (height - offset))) { - return Qt.SizeFDiagCursor; - } - if ((pos.x < offset) || ((pos.x >= (width - offset)) && (pos.y > titleBarHeight))) { - return Qt.SizeHorCursor; - } - if ((pos.y > (height - offset)) || ((pos.y < offset) && (pos.x < (width - 120)))) { - return Qt.SizeVerCursor; - } - } - onClicked: { - console.log("width: " + width + " height: " + height) - } - acceptedButtons: Qt.NoButton - } - } - } - Loader { - active: window.isWindows && window.canResize - sourceComponent: Component { - DragHandler { - id: resizeHandler - enabled: window.isWindows && window.canResize - target: null - grabPermissions: TapHandler.TakeOverForbidden - onActiveChanged: { - if (active) { - const pos = resizeHandler.centroid.position; - const offset = resizeHandleSize + 10; - let edges = 0; - if (pos.x < offset) { - edges |= Qt.LeftEdge; - } - if (pos.x >= (width - offset)) { - edges += Qt.RightEdge; - } - if (pos.y < offset) { - edges |= Qt.TopEdge; - } - if (pos.y >= (height - offset)) { - edges |= Qt.BottomEdge; - } - window.startSystemResize(edges); - } - } - } - } - } - function setMaximized(maximized) { isMaximized = maximized; let useFake = Qt.platform.os === 'windows' && Screen.height === Screen.desktopAvailableHeight && Screen.width === Screen.desktopAvailableWidth && isWindows; From ea34b5d17318b2a689dfd5def6cac1437e6c1a80 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 25 Mar 2025 15:28:33 +0800 Subject: [PATCH 18/36] Fix a logic error in obtaining viewer's preferred size --- viewer/src/main.cpp | 4 +++- viewer/src/rendering/PAGView.cpp | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/viewer/src/main.cpp b/viewer/src/main.cpp index b83951f447..39d7559967 100644 --- a/viewer/src/main.cpp +++ b/viewer/src/main.cpp @@ -29,7 +29,9 @@ int main(int argc, char* argv[]) { for (int i = 0; i < argc; i++) { auto arg = std::string(argv[i]); - if (arg == "-cpu") { + auto lowerArg = arg; + std::transform(lowerArg.begin(), lowerArg.end(), lowerArg.begin(), ::tolower); + if (lowerArg == "-cpu") { cpuMode = true; } else if (argc > 1) { filePath = arg; diff --git a/viewer/src/rendering/PAGView.cpp b/viewer/src/rendering/PAGView.cpp index e286756575..24a913b73c 100644 --- a/viewer/src/rendering/PAGView.cpp +++ b/viewer/src/rendering/PAGView.cpp @@ -122,7 +122,7 @@ auto PAGView::getPreferredSize() const -> QSizeF { qreal width = 0; qreal height = 0; - if (pagWidth < minHeight) { + if (pagHeight < minHeight) { height = minHeight; width = pagWidth * height / pagHeight; } else { @@ -259,7 +259,6 @@ QSGNode* PAGView::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) { if (progress > 1) { progress = 0.0; } - pagPlayer->setProgress(progress); setProgress(progress); } QMetaObject::invokeMethod(renderThread, "flush", Qt::QueuedConnection); From d3c376da54c3c36b274df3898d8da68df5bfd24b Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 25 Mar 2025 17:43:28 +0800 Subject: [PATCH 19/36] Add menu bar for windows viewer --- viewer/qml/Main.qml | 15 ++- viewer/qml/Menu.qml | 146 +++++++++++++++++++++++++++ viewer/qml/components/PAGMenu.qml | 65 ++++++++++++ viewer/qml/components/PAGMenuBar.qml | 47 +++++++++ viewer/qml/components/PAGWindow.qml | 2 +- viewer/res.qrc | 3 + 6 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 viewer/qml/Menu.qml create mode 100644 viewer/qml/components/PAGMenu.qml create mode 100644 viewer/qml/components/PAGMenuBar.qml diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 1a84b9c1c3..9c6259002d 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -1,8 +1,6 @@ import PAG import QtCore import QtQuick -import QtQuick.Window -import Qt.labs.platform as Lab11 import "components" PAGWindow { @@ -30,6 +28,8 @@ PAGWindow { property bool isShowVideoFrames: true + property bool isUseEnglish: true + property double lastX: 0 property double lastY: 0 @@ -125,6 +125,13 @@ PAGWindow { Component.onCompleted: { viewWindow.title = "PAGViewer"; + + let component = Qt.createComponent("Menu.qml"); + let menuBar = component.createObject(viewWindow, { + hasPAGFile: Qt.binding(function() { return mainForm.hasPAGFile; }), + isUseEnglish: Qt.binding(function() { return settings.isUseEnglish; }) + }); + menuBar.command.connect(onCommand); } function updateProgress() { @@ -157,4 +164,8 @@ PAGWindow { mainForm.controlForm.panelsButton.checked = willOpen; } } + function onCommand(command) { + console.log(`Get command: [${command}]`); + + } } diff --git a/viewer/qml/Menu.qml b/viewer/qml/Menu.qml new file mode 100644 index 0000000000..09322d49d1 --- /dev/null +++ b/viewer/qml/Menu.qml @@ -0,0 +1,146 @@ +import QtQuick +import QtQuick.Controls +import "components" + +Item { + id: root + + required property bool hasPAGFile + required property bool isUseEnglish + signal command(string command) + + Loader { + id: windowsMenuLoader + active: Qt.platform.os === "windows" + sourceComponent: PAGMenuBar { + id: windowsMenuBar + + property int menuWidth: root.isUseEnglish ? 300 : 200 + + anchors.top: parent.top + anchors.topMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 36 + + PAGMenu { + menuWidth: windowsMenuBar.menuWidth + title: qsTr("File") + Action { + text: qsTr("Open...") + shortcut: "Ctrl+O" + onTriggered: { + root.command("open-pag-file") + } + } + Action { + text: qsTr("Close") + shortcut: "Ctrl+Q" + onTriggered: { + root.command("close-window") + } + } + Action { + text: qsTr("Settings...") + onTriggered: { + root.command("open-preferences") + } + } + } + + PAGMenu { + menuWidth: windowsMenuBar.menuWidth + title: qsTr("Play") + Action { + text: qsTr("Pause and go to the first frame") + enabled: root.hasPAGFile + shortcut: StandardKey.MoveToPreviousLine + onTriggered: { + root.command('first-frame') + } + } + Action { + text: qsTr("Pause and go to the last frame") + enabled: root.hasPAGFile + shortcut: StandardKey.MoveToNextLine + onTriggered: { + root.command('last-frame') + } + } + Action { + text: qsTr("Previous frame") + enabled: root.hasPAGFile + shortcut: StandardKey.MoveToPreviousChar + onTriggered: { + root.command('previous-frame') + } + } + Action { + text: qsTr("Next frame ") + enabled: root.hasPAGFile + shortcut: StandardKey.MoveToNextChar + onTriggered: { + root.command('next-frame') + } + } + Action { + text: qsTr("Pause/Play") + enabled: root.hasPAGFile + shortcut: "space" + onTriggered: { + root.command('pause-or-play') + } + } + } + + PAGMenu { + menuWidth: windowsMenuBar.menuWidth + title: qsTr("View") + Action { + text: qsTr("Show/Hide Background") + enabled: root.hasPAGFile + shortcut: "B" + onTriggered: { + root.command('toggle-background') + } + } + Action { + text: qsTr("Show/Hide Edit Panel") + enabled: root.hasPAGFile + shortcut: "L" + onTriggered: { + root.command('toggle-edit-panel') + } + } + } + + PAGMenu { + menuWidth: windowsMenuBar.menuWidth + title: qsTr("Help") + Action { + text: qsTr("Help for PAGViewer") + onTriggered: { + root.command('open-help') + } + } + Action { + text: qsTr("About PAGViewer") + onTriggered: { + root.command('open-about') + } + } + Action { + text: qsTr("Feedback") + onTriggered: { + root.command('open-feedback') + } + } + Action { + text: qsTr("About PAG Enterprise Edition") + onTriggered: { + root.command('open-commerce-page') + } + } + } + } + } +} diff --git a/viewer/qml/components/PAGMenu.qml b/viewer/qml/components/PAGMenu.qml new file mode 100644 index 0000000000..b9985b6f7d --- /dev/null +++ b/viewer/qml/components/PAGMenu.qml @@ -0,0 +1,65 @@ +import QtQuick +import QtQuick.Controls + +Menu { + required property int menuWidth + property int itemHeight: 30 + + id: pagMenu + background: Rectangle { + implicitWidth: menuWidth + implicitHeight: pagMenu.contentHeight + 8 + color: "#20202A" + radius: 4 + border.color: "#918F8F" + border.width: 1 + } + + delegate: MenuItem { + id: menuItem + implicitHeight: itemHeight + + contentItem: Item { + Text { + text: menuItem.text + font.pixelSize: 12 + font.family: "Microsoft Yahei" + opacity: menuItem.enabled ? 1.0 : 0.3 + color: "#DFE1E5" + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + Text { + text: shortcut.nativeText + font.pixelSize: 12 + font.family: "Microsoft Yahei" + opacity: menuItem.enabled ? 1.0 : 0.3 + anchors.right: parent.right + color: "#DFE1E5" + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + + background: Rectangle { + anchors.top: parent.top + anchors.topMargin: 4 + anchors.bottom: parent.bottom + anchors.bottomMargin: 0 + anchors.left: parent.left + anchors.leftMargin: 4 + anchors.right: parent.right + anchors.rightMargin: 4 + implicitHeight: 30 + color: menuItem.highlighted ? "#2E436E" : "#00FFFFFF" + radius: 2 + } + + Shortcut { + id: shortcut + enabled: menuItem.action ? menuItem.action.enabled : false + sequence: menuItem.action.shortcut + } + } +} diff --git a/viewer/qml/components/PAGMenuBar.qml b/viewer/qml/components/PAGMenuBar.qml new file mode 100644 index 0000000000..43ab9d85af --- /dev/null +++ b/viewer/qml/components/PAGMenuBar.qml @@ -0,0 +1,47 @@ +import QtCore +import QtQuick +import QtQuick.Controls + +MenuBar { + property int barHeight: 32 + property int itemHeight: 30 + property int itemWidth: 30 + + id: baseMenuBar + height: barHeight + contentWidth: itemWidth + contentHeight: itemHeight + + delegate: MenuBarItem { + id: menuBarItem + topPadding: (baseMenuBar.height - baseMenuBar.contentHeight) / 2 + bottomPadding: (baseMenuBar.height - baseMenuBar.contentHeight) / 2 + + hoverEnabled: true + + contentItem: Text { + text: menuBarItem.text + font.pixelSize: 12 + font.family: "Microsoft Yahei" + opacity: enabled ? 1.0 : 0.3 + color: menuBarItem.highlighted ? "#FFFFFF" : "#9B9B9B" + renderType: Text.NativeRendering + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + implicitWidth: baseMenuBar.contentWidth + implicitHeight: baseMenuBar.contentHeight + opacity: enabled ? 1 : 0.3 + color: menuBarItem.highlighted ? "#2E436E" : "#00FFFFFF" + } + } + + background: Rectangle { + implicitWidth: baseMenuBar.contentHeight + implicitHeight: baseMenuBar.contentHeight + color: "#00000000" + } +} \ No newline at end of file diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index e89b575ebc..c0415a3792 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -12,7 +12,7 @@ Window { property bool isMaximized: false - property bool hasMenu: false + property bool hasMenu: isWindows property bool canResize: true diff --git a/viewer/res.qrc b/viewer/res.qrc index 285b05b945..d79840e800 100644 --- a/viewer/res.qrc +++ b/viewer/res.qrc @@ -22,8 +22,11 @@ <!-- qml --> <file>qml/Main.qml</file> + <file>qml/Menu.qml</file>> <file>qml/MainForm.qml</file> <file>qml/ControlForm.qml</file> + <file>qml/components/PAGMenu.qml</file> + <file>qml/components/PAGMenuBar.qml</file> <file>qml/components/PAGWindow.qml</file> <file>qml/components/PAGRectangle.qml</file> </qresource> From 022c41289e75073c745fc07942232f5b03fbca06 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 25 Mar 2025 20:25:41 +0800 Subject: [PATCH 20/36] Add menu bar for macos viewer --- viewer/qml/Main.qml | 16 ++- viewer/qml/Menu.qml | 191 +++++++++++++++++++++++++-- viewer/qml/components/PAGMenu.qml | 3 +- viewer/qml/components/PAGMenuBar.qml | 5 +- viewer/res.qrc | 2 +- 5 files changed, 194 insertions(+), 23 deletions(-) diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 9c6259002d..922fa1702a 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -1,6 +1,7 @@ import PAG import QtCore import QtQuick +import Qt.labs.settings import "components" PAGWindow { @@ -128,8 +129,18 @@ PAGWindow { let component = Qt.createComponent("Menu.qml"); let menuBar = component.createObject(viewWindow, { - hasPAGFile: Qt.binding(function() { return mainForm.hasPAGFile; }), - isUseEnglish: Qt.binding(function() { return settings.isUseEnglish; }) + hasPAGFile: Qt.binding(function () { + return mainForm.hasPAGFile; + }), + windowActive: Qt.binding(function () { + return viewWindow.active; + }), + isUseEnglish: Qt.binding(function () { + return settings.isUseEnglish; + }), + isFullScreen: Qt.binding(function () { + return viewWindow.visibility === 5; + }) }); menuBar.command.connect(onCommand); } @@ -166,6 +177,5 @@ PAGWindow { } function onCommand(command) { console.log(`Get command: [${command}]`); - } } diff --git a/viewer/qml/Menu.qml b/viewer/qml/Menu.qml index 09322d49d1..55cb9b178f 100644 --- a/viewer/qml/Menu.qml +++ b/viewer/qml/Menu.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import Qt.labs.platform as Platform import "components" Item { @@ -7,6 +8,8 @@ Item { required property bool hasPAGFile required property bool isUseEnglish + required property bool windowActive + required property bool isFullScreen signal command(string command) Loader { @@ -29,20 +32,20 @@ Item { text: qsTr("Open...") shortcut: "Ctrl+O" onTriggered: { - root.command("open-pag-file") + root.command("open-pag-file"); } } Action { text: qsTr("Close") shortcut: "Ctrl+Q" onTriggered: { - root.command("close-window") + root.command("close-window"); } } Action { text: qsTr("Settings...") onTriggered: { - root.command("open-preferences") + root.command("open-preferences"); } } } @@ -55,7 +58,7 @@ Item { enabled: root.hasPAGFile shortcut: StandardKey.MoveToPreviousLine onTriggered: { - root.command('first-frame') + root.command("first-frame"); } } Action { @@ -63,7 +66,7 @@ Item { enabled: root.hasPAGFile shortcut: StandardKey.MoveToNextLine onTriggered: { - root.command('last-frame') + root.command("last-frame"); } } Action { @@ -71,7 +74,7 @@ Item { enabled: root.hasPAGFile shortcut: StandardKey.MoveToPreviousChar onTriggered: { - root.command('previous-frame') + root.command("previous-frame"); } } Action { @@ -79,7 +82,7 @@ Item { enabled: root.hasPAGFile shortcut: StandardKey.MoveToNextChar onTriggered: { - root.command('next-frame') + root.command("next-frame"); } } Action { @@ -87,7 +90,7 @@ Item { enabled: root.hasPAGFile shortcut: "space" onTriggered: { - root.command('pause-or-play') + root.command("pause-or-play"); } } } @@ -100,7 +103,7 @@ Item { enabled: root.hasPAGFile shortcut: "B" onTriggered: { - root.command('toggle-background') + root.command("toggle-background"); } } Action { @@ -108,7 +111,7 @@ Item { enabled: root.hasPAGFile shortcut: "L" onTriggered: { - root.command('toggle-edit-panel') + root.command("toggle-edit-panel"); } } } @@ -119,25 +122,185 @@ Item { Action { text: qsTr("Help for PAGViewer") onTriggered: { - root.command('open-help') + root.command("open-help"); } } Action { text: qsTr("About PAGViewer") onTriggered: { - root.command('open-about') + root.command("open-about"); } } Action { text: qsTr("Feedback") onTriggered: { - root.command('open-feedback') + root.command("open-feedback"); } } Action { text: qsTr("About PAG Enterprise Edition") onTriggered: { - root.command('open-commerce-page') + root.command("open-commerce-page"); + } + } + } + } + } + + Loader { + id: macosMenuBarLoader + active: Qt.platform.os !== "windows" + sourceComponent: Platform.MenuBar { + id: macosMenuBar + Platform.Menu { + title: qsTr("PAGViewer") + Platform.MenuItem { + visible: windowActive + text: qsTr("About PAG Enterprise Edition") + role: "ApplicationSpecificRole" + onTriggered: { + root.command("open-commerce-page"); + } + } + Platform.MenuItem { + visible: windowActive + text: qsTr("Preference Settings") + role: "ApplicationSpecificRole" + onTriggered: { + root.command("open-preferences"); + } + } + Platform.MenuItem { + visible: windowActive + text: qsTr("About PAGViewer") + role: "ApplicationSpecificRole" + onTriggered: { + root.command("open-about"); + } + } + Platform.MenuItem { + text: qsTr("Close") + shortcut: StandardKey.Close + role: "QuitRole" + onTriggered: { + root.command("close-window"); + } + } + } + Platform.Menu { + title: qsTr("File") + Platform.MenuItem { + text: qsTr("Open...") + shortcut: StandardKey.Open + onTriggered: { + root.command("open-pag-file"); + } + } + } + Platform.Menu { + title: qsTr("Play") + Platform.MenuItem { + text: qsTr("Pause and go to the first frame") + enabled: root.hasPAGFile + shortcut: StandardKey.MoveToPreviousLine + onTriggered: { + root.command("first-frame"); + } + } + Platform.MenuItem { + text: qsTr("Pause and go to the last frame") + enabled: root.hasPAGFile + shortcut: StandardKey.MoveToNextLine + onTriggered: { + root.command("last-frame"); + } + } + Platform.MenuItem { + text: qsTr("Previous frame") + enabled: root.hasPAGFile + shortcut: StandardKey.MoveToPreviousChar + onTriggered: { + root.command("previous-frame"); + } + } + Platform.MenuItem { + text: qsTr("Next frame") + enabled: root.hasPAGFile + shortcut: StandardKey.MoveToNextChar + onTriggered: { + root.command("next-frame"); + } + } + Platform.MenuItem { + text: qsTr("Pause/Play") + enabled: root.hasPAGFile + shortcut: "space" + onTriggered: { + root.command("pause-or-play"); + } + } + } + Platform.Menu { + title: qsTr("View") + Platform.MenuItem { + text: qsTr("Show/Hide Background") + enabled: root.hasPAGFile + shortcut: "B" + onTriggered: { + root.command("toggle-background"); + } + } + Platform.MenuItem { + text: qsTr("Show/Hide Edit Panel") + enabled: root.hasPAGFile + shortcut: "L" + onTriggered: { + root.command("toggle-edit-panel"); + } + } + } + Platform.Menu { + title: qsTr("Window") + Platform.MenuItem { + text: qsTr("Minimize") + shortcut: "Ctrl+M" + onTriggered: { + root.command("minimize-window"); + } + } + Platform.MenuItem { + text: qsTr("Zoom") + onTriggered: { + root.command("zoom-window"); + } + } + Platform.MenuItem { + text: qsTr("Exit Fullscreen") + visible: root.isFullScreen + onTriggered: { + root.command("fullscreen-window"); + } + } + Platform.MenuItem { + text: qsTr("Fullscreen") + visible: !root.isFullScreen + onTriggered: { + root.command("fullscreen-window"); + } + } + } + Platform.Menu { + title: qsTr("Help") + Platform.MenuItem { + text: qsTr("Help for PAGViewer") + onTriggered: { + root.command("open-help"); + } + } + Platform.MenuItem { + text: qsTr("Feedback") + onTriggered: { + root.command("open-feedback"); } } } diff --git a/viewer/qml/components/PAGMenu.qml b/viewer/qml/components/PAGMenu.qml index b9985b6f7d..d84f2e4460 100644 --- a/viewer/qml/components/PAGMenu.qml +++ b/viewer/qml/components/PAGMenu.qml @@ -2,10 +2,9 @@ import QtQuick import QtQuick.Controls Menu { + id: pagMenu required property int menuWidth property int itemHeight: 30 - - id: pagMenu background: Rectangle { implicitWidth: menuWidth implicitHeight: pagMenu.contentHeight + 8 diff --git a/viewer/qml/components/PAGMenuBar.qml b/viewer/qml/components/PAGMenuBar.qml index 43ab9d85af..b6b43fa2c5 100644 --- a/viewer/qml/components/PAGMenuBar.qml +++ b/viewer/qml/components/PAGMenuBar.qml @@ -3,11 +3,10 @@ import QtQuick import QtQuick.Controls MenuBar { + id: baseMenuBar property int barHeight: 32 property int itemHeight: 30 property int itemWidth: 30 - - id: baseMenuBar height: barHeight contentWidth: itemWidth contentHeight: itemHeight @@ -44,4 +43,4 @@ MenuBar { implicitHeight: baseMenuBar.contentHeight color: "#00000000" } -} \ No newline at end of file +} diff --git a/viewer/res.qrc b/viewer/res.qrc index d79840e800..e08725e689 100644 --- a/viewer/res.qrc +++ b/viewer/res.qrc @@ -22,7 +22,7 @@ <!-- qml --> <file>qml/Main.qml</file> - <file>qml/Menu.qml</file>> + <file>qml/Menu.qml</file> <file>qml/MainForm.qml</file> <file>qml/ControlForm.qml</file> <file>qml/components/PAGMenu.qml</file> From 23a2207b276de4b28d6a53f89cdaddca91a5b94f Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Wed, 26 Mar 2025 19:29:39 +0800 Subject: [PATCH 21/36] Implement the basic functions of the menu bar for viewer --- viewer/CMakeLists.txt | 10 ++ viewer/qml/AboutWindow.qml | 142 ++++++++++++++++++ viewer/qml/Main.qml | 120 +++++++++++++-- viewer/qml/Menu.qml | 9 +- viewer/qml/SettingsWindow.qml | 99 ++++++++++++ viewer/qml/components/PAGWindow.qml | 16 +- viewer/qml/utils/Utils.qml | 26 ++++ viewer/qml/utils/qmldir | 1 + viewer/res.qrc | 4 + viewer/src/PAGViewer.cpp | 4 + viewer/src/PAGViewer.h | 6 +- viewer/src/main.cpp | 2 +- viewer/src/platform/macos/PAGWindowHelper.h | 14 ++ viewer/src/platform/macos/PAGWindowHelper.mm | 24 +++ .../src/platform/windows/PAGWindowHelper.cpp | 12 ++ viewer/src/platform/windows/PAGWindowHelper.h | 14 ++ viewer/src/rendering/PAGView.cpp | 16 ++ viewer/src/rendering/PAGView.h | 2 + viewer/src/rendering/PAGWindow.cpp | 9 ++ viewer/src/rendering/PAGWindow.h | 7 +- 20 files changed, 513 insertions(+), 24 deletions(-) create mode 100644 viewer/qml/AboutWindow.qml create mode 100644 viewer/qml/SettingsWindow.qml create mode 100644 viewer/qml/utils/Utils.qml create mode 100644 viewer/qml/utils/qmldir create mode 100644 viewer/src/platform/macos/PAGWindowHelper.h create mode 100644 viewer/src/platform/macos/PAGWindowHelper.mm create mode 100644 viewer/src/platform/windows/PAGWindowHelper.cpp create mode 100644 viewer/src/platform/windows/PAGWindowHelper.h diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index f4bd81c9c0..03c28780a7 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -83,6 +83,16 @@ endif () # collects pag include directories. set(PAG_VIEWER_INCLUDES ./ src ../ ../include ../src ../third_party/tgfx/include) file(GLOB_RECURSE PAG_VIEWER_SOURCE_FILES src/*.*) +if (APPLE) + list(APPEND PAG_VIEWER_INCLUDES src/platform/macos) + file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/windows/*.*) +elseif (WIN32) + list(APPEND PAG_VIEWER_INCLUDES src/platform/windows) + file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/macos/*.*) +endif () +foreach (FILE ${PLATFORM_SOURCE_FILES}) + list(REMOVE_ITEM PAG_VIEWER_SOURCE_FILES ${FILE}) +endforeach () set(PAG_USE_QT ON) set(PAG_USE_RTTR ON) diff --git a/viewer/qml/AboutWindow.qml b/viewer/qml/AboutWindow.qml new file mode 100644 index 0000000000..6beb6f0a68 --- /dev/null +++ b/viewer/qml/AboutWindow.qml @@ -0,0 +1,142 @@ +import QtQuick +import QtQuick.Controls +import "components" + +PAGWindow { + id: aboutWindow + + property string aboutMessage: "" + property string licenseMessage: "" + property string privacyMessage: "" + property string licenseUrl: "" + property string privacyUrl: "" + + minimumWidth: width + maximumWidth: width + minimumHeight: height + maximumHeight: height + hasMenu: false + canResize: false + + PAGRectangle { + id: rectangle + + color: "#2D2D37" + anchors.fill: parent + leftTopRadius: false + rightTopRadius: false + radius: 5 + + Row { + anchors.fill: parent + Item { + width: 30 + height: 1 + } + + Image { + id: image + width: 80 + height: 80 + sourceSize.width: 160 + sourceSize.height: 160 + anchors.verticalCenterOffset: 0 + anchors.verticalCenter: parent.verticalCenter + fillMode: Image.PreserveAspectFit + source: "qrc:/images/window-icon.png" + } + + Item { + width: 20 + height: 1 + } + + Column { + spacing: 10 + Item { + width: 1 + height: 20 + } + Text { + id: aboutText + color: "#ffffff" + text: aboutMessage + verticalAlignment: Text.AlignVCenter + font.pixelSize: 12 + } + Row { + visible: licenseMessage !== "" || privacyMessage !== "" + height: 25 + spacing: 15 + + Text { + id: licenseText + visible: licenseUrl !== "" + text: licenseMessage + color: "#008EFF" + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 13 + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Qt.openUrlExternally(licenseUrl); + } + } + } + + Text { + id: privacyText + visible: privacyUrl !== "" + text: privacyMessage + color: "#008EFF" + verticalAlignment: Text.AlignVCenter + anchors.topMargin: 10 + anchors.bottomMargin: 10 + anchors.verticalCenter: parent.verticalCenter + font.pixelSize: 13 + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Qt.openUrlExternally(privacyUrl); + } + } + } + } + + Row { + spacing: 0 + Item { + width: aboutWindow.width - 75 - 70 - 100 + height: 1 + } + Button { + id: btnOk + width: 75 + height: 25 + font.pixelSize: 12 + text: qsTr("Confirm") + onClicked: { + aboutWindow.close(); + } + + background: Rectangle { + radius: 3 + color: "#3485F6" + } + contentItem: Text { + text: btnOk.text + font: btnOk.font + color: "#FFFFFF" + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + } + } + } + } +} \ No newline at end of file diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 922fa1702a..0740f9652c 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -1,8 +1,10 @@ import PAG import QtCore import QtQuick +import QtQuick.Dialogs import Qt.labs.settings import "components" +import "utils" PAGWindow { id: viewWindow @@ -11,6 +13,7 @@ PAGWindow { height: 360 minimumWidth: 400 + windowPadding minimumHeight: 320 + windowTitleBarHeight + hasMenu: true resizeHandleSize: 5 titleBarHeight: windowTitleBarHeight @@ -124,6 +127,42 @@ PAGWindow { } } + FileDialog { + id: openPAGFileDialog + visible: false + title: qsTr("Open PAG File") + fileMode: FileDialog.OpenFile + nameFilters: [ "PAG files(*.pag)" ] + onAccepted: { + let filePath = openPAGFileDialog.selectedFile + mainForm.pagView.setFile(filePath); + } + } + + SettingsWindow { + id: settingsWindow + visible: false + width: 500 + height: 160 + title: qsTr("Settings") + useEnglish: settings.isUseEnglish + onUseEnglishChanged: { + if (!settingsWindow.visible || settingsWindow.useEnglish === settings.isUseEnglish) { + return; + } + settings.isUseEnglish = settingsWindow.useEnglish; + } + } + + AboutWindow { + id: aboutWindow + visible: false + width: settings.isUseEnglish ? 600 : 500 + height: 160 + windowTitleBarHeight + title: qsTr("About PAGViewer") + aboutMessage: "<b>PAGViewer</b> " + Qt.application.version + "<br><br>Copyright © 2017-present Tencent. All rights reserved." + } + Component.onCompleted: { viewWindow.title = "PAGViewer"; @@ -139,7 +178,7 @@ PAGWindow { return settings.isUseEnglish; }), isFullScreen: Qt.binding(function () { - return viewWindow.visibility === 5; + return viewWindow.visibility === Window.FullScreen; }) }); menuBar.command.connect(onCommand); @@ -148,17 +187,11 @@ PAGWindow { function updateProgress() { let duration = mainForm.pagView.duration; let displayedTime = duration * mainForm.pagView.progress; - mainForm.controlForm.timeDisplayedText.text = msToTime(displayedTime); + mainForm.controlForm.timeDisplayedText.text = Utils.msToTime(displayedTime); mainForm.controlForm.currentFrameText.text = mainForm.pagView.currentFrame; mainForm.controlForm.totalFrameText.text = mainForm.pagView.totalFrame; } - function msToTime(duration) { - let seconds = parseInt((duration / 1000) % 60); - let minutes = parseInt((duration / (1000 * 60)) % 60); - minutes = (minutes < 10) ? "0" + minutes : minutes; - seconds = (seconds < 10) ? "0" + seconds : seconds; - return minutes + ":" + seconds; - } + function toggleBackground(checked) { if (checked === undefined) { checked = !mainForm.isBackgroundOn; @@ -167,15 +200,82 @@ PAGWindow { mainForm.isBackgroundOn = checked; } } + function toggleEditPanel(willOpen) { if (willOpen === undefined) { - willOpen = !mainForm.isEditPanelOpen; + willOpen = !settings.isEditPanelOpen; } if (mainForm.controlForm.panelsButton.checked !== willOpen) { mainForm.controlForm.panelsButton.checked = willOpen; } + settings.isEditPanelOpen = willOpen; } + function onCommand(command) { console.log(`Get command: [${command}]`); + switch (command) { + case "open-pag-file": + if (mainForm.hasPAGFile) { + let filePath = mainForm.pagView.filePath; + openPAGFileDialog.currentFolder = Utils.getFileDir(filePath); + } else { + openPAGFileDialog.currentFolder = StandardPaths.writableLocation(StandardPaths.DocumentsLocation); + } + openPAGFileDialog.open(); + break; + case "close-window": + viewWindow.close(); + break; + case "open-preferences": + settingsWindow.visible = true; + settingsWindow.raise(); + break; + case "first-frame": + mainForm.pagView.firstFrame(); + break; + case "last-frame": + mainForm.pagView.lastFrame(); + break; + case "previous-frame": + mainForm.pagView.previousFrame(); + break; + case "next-frame": + mainForm.pagView.nextFrame(); + break; + case "pause-or-play": + mainForm.pagView.isPlaying = !mainForm.pagView.isPlaying; + break; + case "toggle-background": + toggleBackground(); + break; + case "toggle-edit-panel": + toggleEditPanel(); + break; + case "open-help": + Qt.openUrlExternally("https://pag.art/#pag-player"); + break; + case "open-about": + aboutWindow.visible = true; + aboutWindow.raise(); + break; + case "open-feedback": + Qt.openUrlExternally("https://github.com/Tencent/libpag/discussions"); + break; + case "open-commerce-page": + Qt.openUrlExternally("https://pag.io/product.html#pag-enterprise-edition"); + break; + case "minimize-window": + viewWindow.showMinimized(); + break; + case "zoom-window": + viewWindow.visibility = viewWindow.visibility !== Window.Maximized ? Window.Maximized : Window.AutomaticVisibility; + break; + case "fullscreen-window": + viewWindow.visibility = viewWindow.visibility !== Window.Maximized ? Window.Maximized : Window.AutomaticVisibility; + break; + default: + console.log(`Undefined command: [${command}]`) + break; + } } } diff --git a/viewer/qml/Menu.qml b/viewer/qml/Menu.qml index 55cb9b178f..54ec8addf2 100644 --- a/viewer/qml/Menu.qml +++ b/viewer/qml/Menu.qml @@ -275,19 +275,12 @@ Item { } } Platform.MenuItem { - text: qsTr("Exit Fullscreen") + text: root.isFullScreen ? qsTr("Exit Fullscreen") : qsTr("Fullscreen") visible: root.isFullScreen onTriggered: { root.command("fullscreen-window"); } } - Platform.MenuItem { - text: qsTr("Fullscreen") - visible: !root.isFullScreen - onTriggered: { - root.command("fullscreen-window"); - } - } } Platform.Menu { title: qsTr("Help") diff --git a/viewer/qml/SettingsWindow.qml b/viewer/qml/SettingsWindow.qml new file mode 100644 index 0000000000..4c00f0b50e --- /dev/null +++ b/viewer/qml/SettingsWindow.qml @@ -0,0 +1,99 @@ +import QtQuick +import QtQuick.Controls +import "components" + +PAGWindow { + id: settingsWindow + + property bool useEnglish: true + + minimumWidth: width + maximumWidth: width + minimumHeight: height + maximumHeight: height + hasMenu: false + canResize: false + + PAGRectangle { + id: rectangle + + color: "#2D2D37" + anchors.fill: parent + leftTopRadius: false + rightTopRadius: false + radius: 5 + + Row { + spacing: 0 + + anchors.fill: parent + + Item { + width: 30 + height: 1 + } + + Image { + id: image + width: 96 + height: 96 + sourceSize.width: 192 + sourceSize.height: 192 + anchors.verticalCenterOffset: 0 + anchors.verticalCenter: parent.verticalCenter + fillMode: Image.PreserveAspectFit + source: "qrc:/images/window-icon.png" + } + + Item { + width: 20 + height: 1 + } + + Column { + spacing: 0 + anchors.top: parent.top + anchors.bottom: parent.bottom + + Item { + width: 1 + height: (parent.height - useEnglishCheckBox.height) / 2 + } + + CheckBox { + id: useEnglishCheckBox + checked: settingsWindow.useEnglish + height: 30 + text: qsTr("Use English - Take Effect After Restart") + scale: 0.6 + font.pixelSize: 22 + padding: 0 + spacing: 0 + anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenterOffset: 20 - (parent.width / 4) + focusPolicy: Qt.ClickFocus + display: AbstractButton.TextBesideIcon + contentItem: Text { + text: parent.text + anchors.left: parent.indicator.right + anchors.leftMargin: 8 + font: parent.font + color: "#FFFFFF" + verticalAlignment: Text.AlignVCenter + } + onCheckedChanged: { + if (settingsWindow.useEnglish === useEnglishCheckBox.checked) { + return; + } + settingsWindow.useEnglish = useEnglishCheckBox.checked + } + } + } + + Item { + width: 1 + height: (parent.height - useEnglishCheckBox.height) / 2 + } + } + } +} \ No newline at end of file diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index c0415a3792..7e3febbe47 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -1,3 +1,4 @@ +import PAG import QtQuick import QtQuick.Window import QtQuick.Controls @@ -6,13 +7,11 @@ Window { id: window default property alias contents: placeholder.data - required property int resizeHandleSize - required property int titleBarHeight property bool isWindows: Qt.platform.os === "windows" property bool isMaximized: false - property bool hasMenu: isWindows + property bool hasMenu: false property bool canResize: true @@ -23,6 +22,11 @@ Window { property int windowLastWidth: 0 property int windowLastHeight: 0 + + property int titleBarHeight: 0 + + property int resizeHandleSize: 5 + visible: true color: "#00000000" flags: isWindows ? (Qt.FramelessWindowHint | Qt.Window | Qt.WindowSystemMenuHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) : Qt.Window @@ -340,6 +344,12 @@ Window { clip: true } + onVisibleChanged: { + if (visible && typeof(windowHelper) !== "undefined") { + windowHelper.setWindowStyle(window, 0.125, 0.125, 0.164); + } + } + function setMaximized(maximized) { isMaximized = maximized; let useFake = Qt.platform.os === 'windows' && Screen.height === Screen.desktopAvailableHeight && Screen.width === Screen.desktopAvailableWidth && isWindows; diff --git a/viewer/qml/utils/Utils.qml b/viewer/qml/utils/Utils.qml new file mode 100644 index 0000000000..0a6ed3d424 --- /dev/null +++ b/viewer/qml/utils/Utils.qml @@ -0,0 +1,26 @@ +pragma Singleton +import QtQuick + +QtObject { + function msToTime(duration) { + let seconds = parseInt((duration / 1000) % 60); + let minutes = parseInt((duration / (1000 * 60)) % 60); + minutes = (minutes < 10) ? "0" + minutes : minutes; + seconds = (seconds < 10) ? "0" + seconds : seconds; + return minutes + ":" + seconds; + } + + function getFileDir(filePath) { + let url = Qt.resolvedUrl(filePath); + let urlObject = new URL(url); + let directory = urlObject.pathname.substring(0, urlObject.pathname.lastIndexOf('/')); + return directory; + } + + function getFileName(filePath) { + let url = Qt.resolvedUrl(filePath); + let urlObject = new URL(url); + let fileName = urlObject.pathname.split('/').pop(); + return fileName; + } +} \ No newline at end of file diff --git a/viewer/qml/utils/qmldir b/viewer/qml/utils/qmldir new file mode 100644 index 0000000000..41eef8aa6b --- /dev/null +++ b/viewer/qml/utils/qmldir @@ -0,0 +1 @@ +singleton Utils 1.0 Utils.qml \ No newline at end of file diff --git a/viewer/res.qrc b/viewer/res.qrc index e08725e689..bb07745699 100644 --- a/viewer/res.qrc +++ b/viewer/res.qrc @@ -24,7 +24,11 @@ <file>qml/Main.qml</file> <file>qml/Menu.qml</file> <file>qml/MainForm.qml</file> + <file>qml/AboutWindow.qml</file> <file>qml/ControlForm.qml</file> + <file>qml/SettingsWindow.qml</file> + <file>qml/utils/qmldir</file> + <file>qml/utils/Utils.qml</file> <file>qml/components/PAGMenu.qml</file> <file>qml/components/PAGMenuBar.qml</file> <file>qml/components/PAGWindow.qml</file> diff --git a/viewer/src/PAGViewer.cpp b/viewer/src/PAGViewer.cpp index 95a35cdc8c..2c49b1f69f 100644 --- a/viewer/src/PAGViewer.cpp +++ b/viewer/src/PAGViewer.cpp @@ -21,6 +21,8 @@ #include <QObject> #include "rendering/PAGWindow.h" +namespace pag { + PAGViewer::PAGViewer(int& argc, char** argv) : QApplication(argc, argv) { } @@ -59,3 +61,5 @@ auto PAGViewer::onWindowDestroyed(PAGWindow* window) -> void { PAGWindow::AllWindows.removeOne(window); window->deleteLater(); } + +} // namespace pag diff --git a/viewer/src/PAGViewer.h b/viewer/src/PAGViewer.h index f81f73026a..fb67c4e07f 100644 --- a/viewer/src/PAGViewer.h +++ b/viewer/src/PAGViewer.h @@ -21,6 +21,8 @@ #include <QApplication> #include "rendering/PAGWindow.h" +namespace pag { + class PAGViewer : public QApplication { Q_OBJECT public: @@ -30,4 +32,6 @@ class PAGViewer : public QApplication { auto openFile(QString path) -> void; Q_SLOT void onWindowDestroyed(PAGWindow* window); -}; \ No newline at end of file +}; + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/main.cpp b/viewer/src/main.cpp index 39d7559967..66cde5f1f2 100644 --- a/viewer/src/main.cpp +++ b/viewer/src/main.cpp @@ -57,7 +57,7 @@ int main(int argc, char* argv[]) { std::vector<std::string> fallbackList = {"PingFang SC", "Apple Color Emoji"}; pag::PAGFont::SetFallbackFontNames(fallbackList); - PAGViewer app(argc, argv); + pag::PAGViewer app(argc, argv); QApplication::setWindowIcon(QIcon(":/images/window-icon.png")); qmlRegisterType<pag::PAGView>("PAG", 1, 0, "PAGView"); app.openFile(filePath.data()); diff --git a/viewer/src/platform/macos/PAGWindowHelper.h b/viewer/src/platform/macos/PAGWindowHelper.h new file mode 100644 index 0000000000..856445451a --- /dev/null +++ b/viewer/src/platform/macos/PAGWindowHelper.h @@ -0,0 +1,14 @@ +#pragma once + +#include <QQuickWindow> + +namespace pag { + +class PAGWindowHelper : public QObject { + Q_OBJECT + public: + explicit PAGWindowHelper(QObject* parent = nullptr); + Q_INVOKABLE void setWindowStyle(QQuickWindow* quickWindow, double red, double green, double blue); +}; + +} // namespace pag diff --git a/viewer/src/platform/macos/PAGWindowHelper.mm b/viewer/src/platform/macos/PAGWindowHelper.mm new file mode 100644 index 0000000000..fdc68915fb --- /dev/null +++ b/viewer/src/platform/macos/PAGWindowHelper.mm @@ -0,0 +1,24 @@ +#include "PAGWindowHelper.h" +#include <Cocoa/Cocoa.h> + +namespace pag { +PAGWindowHelper::PAGWindowHelper(QObject* parent) : QObject(parent) { +} + +auto PAGWindowHelper::setWindowStyle(QQuickWindow* quickWindow, double red, double green, + double blue) -> void { + if (quickWindow != nullptr) { + NSView* view = (NSView*)quickWindow->winId(); + NSWindow* window = [view window]; + if (@available(macOS 10.10, *)) { + window.titleVisibility = NSWindowTitleHidden; + window.styleMask |= NSWindowStyleMaskFullSizeContentView; + window.titlebarAppearsTransparent = true; + window.contentView.wantsLayer = true; + } + window.colorSpace = [NSColorSpace extendedSRGBColorSpace]; + window.backgroundColor = [NSColor colorWithRed:red green:green blue:blue alpha:1.]; + } +} + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/platform/windows/PAGWindowHelper.cpp b/viewer/src/platform/windows/PAGWindowHelper.cpp new file mode 100644 index 0000000000..d2443f123e --- /dev/null +++ b/viewer/src/platform/windows/PAGWindowHelper.cpp @@ -0,0 +1,12 @@ +#include "PAGWindowHelper.h" + +namespace pag { +PAGWindowHelper::PAGWindowHelper(QObject* parent) : QObject(parent) { +} + +auto PAGWindowHelper::setWindowStyle(QQuickWindow* quickWindow, double red, double green, + double blue) -> void { + return; +} + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/platform/windows/PAGWindowHelper.h b/viewer/src/platform/windows/PAGWindowHelper.h new file mode 100644 index 0000000000..0599c4bf7d --- /dev/null +++ b/viewer/src/platform/windows/PAGWindowHelper.h @@ -0,0 +1,14 @@ +#pragma once + +#include <QQuickWindow> + +namespace pag { + +class PAGWindowHelper : public QObject { + Q_OBJECT + public: + explicit PAGWindowHelper(QObject* parent = nullptr); + auto setWindowStyle(QQuickWindow* quickWindow, double red, double green, double blue) -> void; +}; + +} // namespace pag diff --git a/viewer/src/rendering/PAGView.cpp b/viewer/src/rendering/PAGView.cpp index 24a913b73c..900759b1b6 100644 --- a/viewer/src/rendering/PAGView.cpp +++ b/viewer/src/rendering/PAGView.cpp @@ -196,6 +196,22 @@ auto PAGView::setFile(const QString& filePath) -> bool { return true; } +auto PAGView::firstFrame() -> void { + if (pagFile == nullptr) { + return; + } + setIsPlaying(false); + setProgress(0); +} + +auto PAGView::lastFrame() -> void { + if (pagFile == nullptr) { + return; + } + setIsPlaying(false); + setProgress(1); +} + auto PAGView::nextFrame() -> void { if (pagFile == nullptr) { return; diff --git a/viewer/src/rendering/PAGView.h b/viewer/src/rendering/PAGView.h index 95fdddd624..1d5f9b77df 100644 --- a/viewer/src/rendering/PAGView.h +++ b/viewer/src/rendering/PAGView.h @@ -66,6 +66,8 @@ class PAGView : public QQuickItem { Q_SIGNAL void fileChanged(std::shared_ptr<pag::PAGFile> pagFile, std::string filePath); Q_INVOKABLE bool setFile(const QString& filePath); + Q_INVOKABLE void firstFrame(); + Q_INVOKABLE void lastFrame(); Q_INVOKABLE void nextFrame(); Q_INVOKABLE void previousFrame(); diff --git a/viewer/src/rendering/PAGWindow.cpp b/viewer/src/rendering/PAGWindow.cpp index f27578eb65..c5fec1d983 100644 --- a/viewer/src/rendering/PAGWindow.cpp +++ b/viewer/src/rendering/PAGWindow.cpp @@ -19,6 +19,8 @@ #include "PAGWindow.h" #include <QQmlContext> #include <QThread> +#include "PAGWindowHelper.h" +namespace pag { QList<PAGWindow*> PAGWindow::AllWindows; @@ -46,6 +48,11 @@ auto PAGWindow::onPAGViewerDestroyed() -> void { auto PAGWindow::open() -> void { engine = new QQmlApplicationEngine; + windowHelper = new PAGWindowHelper(this); + + auto context = engine->rootContext(); + context->setContextProperty("windowHelper", windowHelper); + engine->load(QUrl(QStringLiteral("qrc:/qml/Main.qml"))); window = static_cast<QQuickWindow*>(engine->rootObjects().at(0)); @@ -62,3 +69,5 @@ auto PAGWindow::open() -> void { auto PAGWindow::getFilePath() -> QString { return filePath; } + +} // namespace pag diff --git a/viewer/src/rendering/PAGWindow.h b/viewer/src/rendering/PAGWindow.h index 92213030bd..e1659a69cd 100644 --- a/viewer/src/rendering/PAGWindow.h +++ b/viewer/src/rendering/PAGWindow.h @@ -21,7 +21,9 @@ #include <QQmlApplicationEngine> #include <QString> #include "PAGView.h" +#include "PAGWindowHelper.h" +namespace pag { class PAGWindow : public QObject { Q_OBJECT public: @@ -41,7 +43,10 @@ class PAGWindow : public QObject { private: QString filePath; QQuickWindow* window = nullptr; - pag::PAGView* pagView = nullptr; + PAGView* pagView = nullptr; QThread* renderThread = nullptr; + PAGWindowHelper* windowHelper = nullptr; QQmlApplicationEngine* engine = nullptr; }; + +} // namespace pag From 8bb7f562e4f17737567d026d255ca149c0e6334e Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Wed, 26 Mar 2025 20:21:35 +0800 Subject: [PATCH 22/36] Fix some issue about the menu of viewer --- viewer/qml/AboutWindow.qml | 1 + viewer/qml/Main.qml | 2 +- viewer/qml/SettingsWindow.qml | 1 + viewer/src/platform/windows/PAGWindowHelper.h | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/viewer/qml/AboutWindow.qml b/viewer/qml/AboutWindow.qml index 6beb6f0a68..410c6e4ab8 100644 --- a/viewer/qml/AboutWindow.qml +++ b/viewer/qml/AboutWindow.qml @@ -17,6 +17,7 @@ PAGWindow { maximumHeight: height hasMenu: false canResize: false + titleBarHeight: isWindows ? 32 : 22 PAGRectangle { id: rectangle diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 0740f9652c..1862cef1c7 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -143,7 +143,7 @@ PAGWindow { id: settingsWindow visible: false width: 500 - height: 160 + height: 160 + windowTitleBarHeight title: qsTr("Settings") useEnglish: settings.isUseEnglish onUseEnglishChanged: { diff --git a/viewer/qml/SettingsWindow.qml b/viewer/qml/SettingsWindow.qml index 4c00f0b50e..dc261b2436 100644 --- a/viewer/qml/SettingsWindow.qml +++ b/viewer/qml/SettingsWindow.qml @@ -13,6 +13,7 @@ PAGWindow { maximumHeight: height hasMenu: false canResize: false + titleBarHeight: isWindows ? 32 : 22 PAGRectangle { id: rectangle diff --git a/viewer/src/platform/windows/PAGWindowHelper.h b/viewer/src/platform/windows/PAGWindowHelper.h index 0599c4bf7d..856445451a 100644 --- a/viewer/src/platform/windows/PAGWindowHelper.h +++ b/viewer/src/platform/windows/PAGWindowHelper.h @@ -8,7 +8,7 @@ class PAGWindowHelper : public QObject { Q_OBJECT public: explicit PAGWindowHelper(QObject* parent = nullptr); - auto setWindowStyle(QQuickWindow* quickWindow, double red, double green, double blue) -> void; + Q_INVOKABLE void setWindowStyle(QQuickWindow* quickWindow, double red, double green, double blue); }; } // namespace pag From 8d32286dd318d2c64322441fd31b6f380ebf066f Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Wed, 26 Mar 2025 20:30:22 +0800 Subject: [PATCH 23/36] Codeformat the qml files of the viewer --- viewer/qml/AboutWindow.qml | 2 +- viewer/qml/Main.qml | 128 ++++++++++++++-------------- viewer/qml/SettingsWindow.qml | 4 +- viewer/qml/components/PAGWindow.qml | 2 +- 4 files changed, 68 insertions(+), 68 deletions(-) diff --git a/viewer/qml/AboutWindow.qml b/viewer/qml/AboutWindow.qml index 410c6e4ab8..83655f9d86 100644 --- a/viewer/qml/AboutWindow.qml +++ b/viewer/qml/AboutWindow.qml @@ -140,4 +140,4 @@ PAGWindow { } } } -} \ No newline at end of file +} diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 1862cef1c7..f23c3f5206 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -132,9 +132,9 @@ PAGWindow { visible: false title: qsTr("Open PAG File") fileMode: FileDialog.OpenFile - nameFilters: [ "PAG files(*.pag)" ] + nameFilters: ["PAG files(*.pag)"] onAccepted: { - let filePath = openPAGFileDialog.selectedFile + let filePath = openPAGFileDialog.selectedFile; mainForm.pagView.setFile(filePath); } } @@ -214,68 +214,68 @@ PAGWindow { function onCommand(command) { console.log(`Get command: [${command}]`); switch (command) { - case "open-pag-file": - if (mainForm.hasPAGFile) { - let filePath = mainForm.pagView.filePath; - openPAGFileDialog.currentFolder = Utils.getFileDir(filePath); - } else { - openPAGFileDialog.currentFolder = StandardPaths.writableLocation(StandardPaths.DocumentsLocation); - } - openPAGFileDialog.open(); - break; - case "close-window": - viewWindow.close(); - break; - case "open-preferences": - settingsWindow.visible = true; - settingsWindow.raise(); - break; - case "first-frame": - mainForm.pagView.firstFrame(); - break; - case "last-frame": - mainForm.pagView.lastFrame(); - break; - case "previous-frame": - mainForm.pagView.previousFrame(); - break; - case "next-frame": - mainForm.pagView.nextFrame(); - break; - case "pause-or-play": - mainForm.pagView.isPlaying = !mainForm.pagView.isPlaying; - break; - case "toggle-background": - toggleBackground(); - break; - case "toggle-edit-panel": - toggleEditPanel(); - break; - case "open-help": - Qt.openUrlExternally("https://pag.art/#pag-player"); - break; - case "open-about": - aboutWindow.visible = true; - aboutWindow.raise(); - break; - case "open-feedback": - Qt.openUrlExternally("https://github.com/Tencent/libpag/discussions"); - break; - case "open-commerce-page": - Qt.openUrlExternally("https://pag.io/product.html#pag-enterprise-edition"); - break; - case "minimize-window": - viewWindow.showMinimized(); - break; - case "zoom-window": - viewWindow.visibility = viewWindow.visibility !== Window.Maximized ? Window.Maximized : Window.AutomaticVisibility; - break; - case "fullscreen-window": - viewWindow.visibility = viewWindow.visibility !== Window.Maximized ? Window.Maximized : Window.AutomaticVisibility; - break; - default: - console.log(`Undefined command: [${command}]`) - break; + case "open-pag-file": + if (mainForm.hasPAGFile) { + let filePath = mainForm.pagView.filePath; + openPAGFileDialog.currentFolder = Utils.getFileDir(filePath); + } else { + openPAGFileDialog.currentFolder = StandardPaths.writableLocation(StandardPaths.DocumentsLocation); + } + openPAGFileDialog.open(); + break; + case "close-window": + viewWindow.close(); + break; + case "open-preferences": + settingsWindow.visible = true; + settingsWindow.raise(); + break; + case "first-frame": + mainForm.pagView.firstFrame(); + break; + case "last-frame": + mainForm.pagView.lastFrame(); + break; + case "previous-frame": + mainForm.pagView.previousFrame(); + break; + case "next-frame": + mainForm.pagView.nextFrame(); + break; + case "pause-or-play": + mainForm.pagView.isPlaying = !mainForm.pagView.isPlaying; + break; + case "toggle-background": + toggleBackground(); + break; + case "toggle-edit-panel": + toggleEditPanel(); + break; + case "open-help": + Qt.openUrlExternally("https://pag.art/#pag-player"); + break; + case "open-about": + aboutWindow.visible = true; + aboutWindow.raise(); + break; + case "open-feedback": + Qt.openUrlExternally("https://github.com/Tencent/libpag/discussions"); + break; + case "open-commerce-page": + Qt.openUrlExternally("https://pag.io/product.html#pag-enterprise-edition"); + break; + case "minimize-window": + viewWindow.showMinimized(); + break; + case "zoom-window": + viewWindow.visibility = viewWindow.visibility !== Window.Maximized ? Window.Maximized : Window.AutomaticVisibility; + break; + case "fullscreen-window": + viewWindow.visibility = viewWindow.visibility !== Window.Maximized ? Window.Maximized : Window.AutomaticVisibility; + break; + default: + console.log(`Undefined command: [${command}]`); + break; } } } diff --git a/viewer/qml/SettingsWindow.qml b/viewer/qml/SettingsWindow.qml index dc261b2436..2ce89424a7 100644 --- a/viewer/qml/SettingsWindow.qml +++ b/viewer/qml/SettingsWindow.qml @@ -86,7 +86,7 @@ PAGWindow { if (settingsWindow.useEnglish === useEnglishCheckBox.checked) { return; } - settingsWindow.useEnglish = useEnglishCheckBox.checked + settingsWindow.useEnglish = useEnglishCheckBox.checked; } } } @@ -97,4 +97,4 @@ PAGWindow { } } } -} \ No newline at end of file +} diff --git a/viewer/qml/components/PAGWindow.qml b/viewer/qml/components/PAGWindow.qml index 7e3febbe47..c4616f0d45 100644 --- a/viewer/qml/components/PAGWindow.qml +++ b/viewer/qml/components/PAGWindow.qml @@ -345,7 +345,7 @@ Window { } onVisibleChanged: { - if (visible && typeof(windowHelper) !== "undefined") { + if (visible && typeof (windowHelper) !== "undefined") { windowHelper.setWindowStyle(window, 0.125, 0.125, 0.164); } } From 959dcc6b9d5baaba98e633f4ef794520bc794a97 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Fri, 28 Mar 2025 11:34:18 +0800 Subject: [PATCH 24/36] Fix some issues found in code review --- viewer/CMakeLists.txt | 8 ++++---- viewer/qml/Menu.qml | 2 +- viewer/src/platform/{macos => mac}/PAGWindowHelper.h | 0 viewer/src/platform/{macos => mac}/PAGWindowHelper.mm | 0 viewer/src/platform/{windows => win}/PAGWindowHelper.cpp | 0 viewer/src/platform/{windows => win}/PAGWindowHelper.h | 0 viewer/src/rendering/PAGWindow.cpp | 3 ++- 7 files changed, 7 insertions(+), 6 deletions(-) rename viewer/src/platform/{macos => mac}/PAGWindowHelper.h (100%) rename viewer/src/platform/{macos => mac}/PAGWindowHelper.mm (100%) rename viewer/src/platform/{windows => win}/PAGWindowHelper.cpp (100%) rename viewer/src/platform/{windows => win}/PAGWindowHelper.h (100%) diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index 03c28780a7..689e012078 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -84,11 +84,11 @@ endif () set(PAG_VIEWER_INCLUDES ./ src ../ ../include ../src ../third_party/tgfx/include) file(GLOB_RECURSE PAG_VIEWER_SOURCE_FILES src/*.*) if (APPLE) - list(APPEND PAG_VIEWER_INCLUDES src/platform/macos) - file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/windows/*.*) + list(APPEND PAG_VIEWER_INCLUDES src/platform/mac) + file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/win/*.*) elseif (WIN32) - list(APPEND PAG_VIEWER_INCLUDES src/platform/windows) - file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/macos/*.*) + list(APPEND PAG_VIEWER_INCLUDES src/platform/win) + file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/mac/*.*) endif () foreach (FILE ${PLATFORM_SOURCE_FILES}) list(REMOVE_ITEM PAG_VIEWER_SOURCE_FILES ${FILE}) diff --git a/viewer/qml/Menu.qml b/viewer/qml/Menu.qml index 54ec8addf2..2ae63e4065 100644 --- a/viewer/qml/Menu.qml +++ b/viewer/qml/Menu.qml @@ -149,7 +149,7 @@ Item { Loader { id: macosMenuBarLoader - active: Qt.platform.os !== "windows" + active: Qt.platform.os === "osx" sourceComponent: Platform.MenuBar { id: macosMenuBar Platform.Menu { diff --git a/viewer/src/platform/macos/PAGWindowHelper.h b/viewer/src/platform/mac/PAGWindowHelper.h similarity index 100% rename from viewer/src/platform/macos/PAGWindowHelper.h rename to viewer/src/platform/mac/PAGWindowHelper.h diff --git a/viewer/src/platform/macos/PAGWindowHelper.mm b/viewer/src/platform/mac/PAGWindowHelper.mm similarity index 100% rename from viewer/src/platform/macos/PAGWindowHelper.mm rename to viewer/src/platform/mac/PAGWindowHelper.mm diff --git a/viewer/src/platform/windows/PAGWindowHelper.cpp b/viewer/src/platform/win/PAGWindowHelper.cpp similarity index 100% rename from viewer/src/platform/windows/PAGWindowHelper.cpp rename to viewer/src/platform/win/PAGWindowHelper.cpp diff --git a/viewer/src/platform/windows/PAGWindowHelper.h b/viewer/src/platform/win/PAGWindowHelper.h similarity index 100% rename from viewer/src/platform/windows/PAGWindowHelper.h rename to viewer/src/platform/win/PAGWindowHelper.h diff --git a/viewer/src/rendering/PAGWindow.cpp b/viewer/src/rendering/PAGWindow.cpp index c5fec1d983..06d2b91019 100644 --- a/viewer/src/rendering/PAGWindow.cpp +++ b/viewer/src/rendering/PAGWindow.cpp @@ -30,6 +30,7 @@ PAGWindow::PAGWindow(QObject* parent) : QObject(parent) { PAGWindow::~PAGWindow() { delete pagView; delete engine; + delete windowHelper; } auto PAGWindow::openFile(QString path) -> void { @@ -48,7 +49,7 @@ auto PAGWindow::onPAGViewerDestroyed() -> void { auto PAGWindow::open() -> void { engine = new QQmlApplicationEngine; - windowHelper = new PAGWindowHelper(this); + windowHelper = new PAGWindowHelper(); auto context = engine->rootContext(); context->setContextProperty("windowHelper", windowHelper); From 26e95e52d1340a7af96086b19800a2cc416374e1 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Mon, 31 Mar 2025 14:15:02 +0800 Subject: [PATCH 25/36] Fix some issues found in code review --- viewer/CMakeLists.txt | 9 ++++----- viewer/src/rendering/PAGWindow.cpp | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index 689e012078..d493374cdc 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -83,16 +83,15 @@ endif () # collects pag include directories. set(PAG_VIEWER_INCLUDES ./ src ../ ../include ../src ../third_party/tgfx/include) file(GLOB_RECURSE PAG_VIEWER_SOURCE_FILES src/*.*) +list(FILTER PAG_VIEWER_SOURCE_FILES EXCLUDE REGEX src/platform/*.*) if (APPLE) list(APPEND PAG_VIEWER_INCLUDES src/platform/mac) - file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/win/*.*) + file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/mac/*.*) elseif (WIN32) list(APPEND PAG_VIEWER_INCLUDES src/platform/win) - file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/mac/*.*) + file(GLOB_RECURSE PLATFORM_SOURCE_FILES src/platform/win/*.*) endif () -foreach (FILE ${PLATFORM_SOURCE_FILES}) - list(REMOVE_ITEM PAG_VIEWER_SOURCE_FILES ${FILE}) -endforeach () +list(APPEND PAG_VIEWER_SOURCE_FILES ${PLATFORM_SOURCE_FILES}) set(PAG_USE_QT ON) set(PAG_USE_RTTR ON) diff --git a/viewer/src/rendering/PAGWindow.cpp b/viewer/src/rendering/PAGWindow.cpp index 06d2b91019..7e1d91c255 100644 --- a/viewer/src/rendering/PAGWindow.cpp +++ b/viewer/src/rendering/PAGWindow.cpp @@ -28,7 +28,6 @@ PAGWindow::PAGWindow(QObject* parent) : QObject(parent) { } PAGWindow::~PAGWindow() { - delete pagView; delete engine; delete windowHelper; } From ac2e73f27d0063e96c2ca8ab26b28bc0f7fa5ef2 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 1 Apr 2025 14:25:21 +0800 Subject: [PATCH 26/36] Add PAG export support in viewer: PNG sequence, single frame PNG and APNG formats --- viewer/CMakeLists.txt | 4 + viewer/qml/Main.qml | 204 +++- viewer/qml/Menu.qml | 50 + viewer/qml/utils/Utils.qml | 20 +- viewer/src/main.cpp | 2 + viewer/src/platform/mac/PAGUtilsImpl.h | 9 + viewer/src/platform/mac/PAGUtilsImpl.mm | 23 + viewer/src/platform/win/PAGUtilsImpl.cpp | 36 + viewer/src/platform/win/PAGUtilsImpl.h | 9 + viewer/src/rendering/PAGWindow.cpp | 5 +- viewer/src/task/PAGTask.cpp | 167 +++ viewer/src/task/PAGTask.h | 97 ++ viewer/src/task/PAGTaskFactory.cpp | 70 ++ viewer/src/task/PAGTaskFactory.h | 45 + viewer/src/task/export/PAGExportAPNGTask.cpp | 46 + viewer/src/task/export/PAGExportAPNGTask.h | 40 + viewer/src/task/export/PAGExportPNGTask.cpp | 62 + viewer/src/task/export/PAGExportPNGTask.h | 42 + viewer/src/utils/PAGFileUtils.cpp | 86 ++ viewer/src/utils/PAGFileUtils.h | 33 + viewer/src/utils/PAGTimeUtils.cpp | 31 + viewer/src/utils/PAGTimeUtils.h | 28 + viewer/src/utils/PAGUtils.cpp | 31 + viewer/src/utils/PAGUtils.h | 28 + viewer/src/utils/apng/APNGAssembler.cpp | 776 ++++++++++++ viewer/src/utils/apng/APNGAssembler.h | 93 ++ viewer/src/utils/apng/image.cpp | 1116 ++++++++++++++++++ viewer/src/utils/apng/image.h | 86 ++ 28 files changed, 3222 insertions(+), 17 deletions(-) create mode 100644 viewer/src/platform/mac/PAGUtilsImpl.h create mode 100644 viewer/src/platform/mac/PAGUtilsImpl.mm create mode 100644 viewer/src/platform/win/PAGUtilsImpl.cpp create mode 100644 viewer/src/platform/win/PAGUtilsImpl.h create mode 100644 viewer/src/task/PAGTask.cpp create mode 100644 viewer/src/task/PAGTask.h create mode 100644 viewer/src/task/PAGTaskFactory.cpp create mode 100644 viewer/src/task/PAGTaskFactory.h create mode 100644 viewer/src/task/export/PAGExportAPNGTask.cpp create mode 100644 viewer/src/task/export/PAGExportAPNGTask.h create mode 100644 viewer/src/task/export/PAGExportPNGTask.cpp create mode 100644 viewer/src/task/export/PAGExportPNGTask.h create mode 100644 viewer/src/utils/PAGFileUtils.cpp create mode 100644 viewer/src/utils/PAGFileUtils.h create mode 100644 viewer/src/utils/PAGTimeUtils.cpp create mode 100644 viewer/src/utils/PAGTimeUtils.h create mode 100644 viewer/src/utils/PAGUtils.cpp create mode 100644 viewer/src/utils/PAGUtils.h create mode 100644 viewer/src/utils/apng/APNGAssembler.cpp create mode 100644 viewer/src/utils/apng/APNGAssembler.h create mode 100644 viewer/src/utils/apng/image.cpp create mode 100644 viewer/src/utils/apng/image.h diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index 689e012078..4fabc3f20a 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -106,9 +106,13 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ ./libpag) add_executable(PAGViewer ${RC_FILES} ${PAG_VIEWER_SOURCE_FILES} ${QT_RESOURCES}) if (APPLE) list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/rttr/mac/include) + list(APPEND PAG_VIEWER_INCLUDES ../third_party/tgfx/third_party/out/zlib/mac/include) + list(APPEND PAG_VIEWER_INCLUDES ../third_party/tgfx/third_party/out/libpng/mac/include) list(APPEND VIEWER_VENDOR_LIBRARIES ${CMAKE_SOURCE_DIR}/../vendor/sparkle/mac/Sparkle.framework) elseif (WIN32) list(APPEND PAG_VIEWER_INCLUDES ../third_party/out/rttr/win/include) + list(APPEND PAG_VIEWER_INCLUDES ../third_party/tgfx/third_party/out/zlib/win/include) + list(APPEND PAG_VIEWER_INCLUDES ../third_party/tgfx/third_party/out/libpng/win/include) list(APPEND PAG_VIEWER_INCLUDES ../vendor/winsparkle/include) list(APPEND VIEWER_VENDOR_LIBRARIES ${CMAKE_SOURCE_DIR}/../vendor/winsparkle/win/x64/${CMAKE_BUILD_TYPE}/WinSparkle.lib) endif () diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index f23c3f5206..4c4d00f224 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -2,7 +2,9 @@ import PAG import QtCore import QtQuick import QtQuick.Dialogs +import QtQuick.Controls import Qt.labs.settings +import Qt.labs.platform as Platform import "components" import "utils" @@ -127,18 +129,6 @@ PAGWindow { } } - FileDialog { - id: openPAGFileDialog - visible: false - title: qsTr("Open PAG File") - fileMode: FileDialog.OpenFile - nameFilters: ["PAG files(*.pag)"] - onAccepted: { - let filePath = openPAGFileDialog.selectedFile; - mainForm.pagView.setFile(filePath); - } - } - SettingsWindow { id: settingsWindow visible: false @@ -163,6 +153,108 @@ PAGWindow { aboutMessage: "<b>PAGViewer</b> " + Qt.application.version + "<br><br>Copyright © 2017-present Tencent. All rights reserved." } + PAGTaskFactory { + id: taskFactory + objectName: "taskFactory" + } + + FileDialog { + id: openFileDialog + + property var currentAcceptHandler: null + + visible: false + title: "" + fileMode: FileDialog.OpenFile + nameFilters: [] + } + + Platform.FolderDialog { + id: openFolderDialog + + property var currentAcceptHandler: null + + visible: false + title: qsTr("Select Save Path") + } + + PAGWindow { + id: progressWindow + + property var task + property alias progressBar: progressBar + + width: 300 + height: 64 + hasMenu: false + canResize: false + titleBarHeight: windowTitleBarHeight + visible: false + + ProgressBar { + id: progressBar + width: parent.width - 24 + height: 30 + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + value: 0 + + contentItem: Item { + Rectangle { + width: parent.width + height: 15 + radius: 5 + color: "#DDDDDD" + anchors.verticalCenter: parent.verticalCenter + } + + Rectangle { + width: progressBar.visualPosition * parent.width + height: 15 + radius: 5 + color: "#448EF9" + anchors.verticalCenter: parent.verticalCenter + } + + Text { + anchors.centerIn: parent + text: Math.round(progressBar.value * 100) + "%" + color: progressBar.value > 0.5 ? "white" : "black" // 根据进度自动调整文字颜色 + font.pixelSize: 12 + } + } + } + + onClosing: { + if (task) { + task.stop(); + } + } + } + + Connections { + id: taskConnections + onProgressChanged: function (progress) { + console.log("progress: " + progress); + progressWindow.progressBar.value = progress; + } + + onVisibleChanged: function (visible) { + console.log("visible: " + visible); + progressWindow.visible = visible; + } + + onTaskFinished: function (filePath, result) { + if (result !== 0) { + let errStr = qsTr("Export failed, error code: "); + alert(errStr + result); + } + progressWindow.task = null; + progressWindow.progressBar.value = 0; + progressWindow.visible = false; + } + } + Component.onCompleted: { viewWindow.title = "PAGViewer"; @@ -217,11 +309,19 @@ PAGWindow { case "open-pag-file": if (mainForm.hasPAGFile) { let filePath = mainForm.pagView.filePath; - openPAGFileDialog.currentFolder = Utils.getFileDir(filePath); + openFileDialog.currentFolder = Utils.getFileDir(filePath); } else { - openPAGFileDialog.currentFolder = StandardPaths.writableLocation(StandardPaths.DocumentsLocation); + openFileDialog.currentFolder = StandardPaths.writableLocation(StandardPaths.DocumentsLocation); } - openPAGFileDialog.open(); + openFileDialog.accepted.disconnect(); + openFileDialog.fileMode = FileDialog.OpenFile; + openFileDialog.title = qsTr("Open PAG File"); + openFileDialog.nameFilters = ["PAG files(*.pag)"]; + openFileDialog.accepted.connect(function () { + let filePath = openFileDialog.selectedFile; + mainForm.pagView.setFile(filePath); + }); + openFileDialog.open(); break; case "close-window": viewWindow.close(); @@ -273,6 +373,80 @@ PAGWindow { case "fullscreen-window": viewWindow.visibility = viewWindow.visibility !== Window.Maximized ? Window.Maximized : Window.AutomaticVisibility; break; + case "export-frame-as-png": + if (openFileDialog.currentAcceptHandler) { + openFileDialog.accepted.disconnect(openFileDialog.currentAcceptHandler); + } + openFileDialog.fileMode = FileDialog.SaveFile; + openFileDialog.title = qsTr("Select save path"); + openFileDialog.nameFilters = ["PNG files(*.png)"]; + openFileDialog.defaultSuffix = "png"; + openFileDialog.currentFolder = Utils.getFileDir(mainForm.pagView.filePath); + openFileDialog.currentAcceptHandler = function () { + let filePath = openFileDialog.selectedFile; + let task = taskFactory.createTask(PAGTaskFactory.PAGTaskType_ExportPNG, filePath, { + "exportFrame": mainForm.pagView.currentFrame + }); + if (task) { + taskConnections.target = task; + progressWindow.title = qsTr("Exporting"); + progressWindow.task = task; + progressWindow.visible = true; + progressWindow.raise(); + task.start(); + } + }; + openFileDialog.accepted.connect(openFileDialog.currentAcceptHandler); + openFileDialog.open(); + break; + case "export-as-png-sequence": + if (openFolderDialog.currentAcceptHandler) { + openFolderDialog.accepted.disconnect(openFolderDialog.currentAcceptHandler); + } + openFolderDialog.title = qsTr("Select save path"); + openFolderDialog.currentFolder = Utils.getNativeFilePath(Utils.getFileDir(mainForm.pagView.filePath)); + console.log("openFolderDialog.folder: " + openFolderDialog.folder); + openFolderDialog.currentAcceptHandler = function () { + let filePath = openFolderDialog.folder; + let task = taskFactory.createTask(PAGTaskFactory.PAGTaskType_ExportPNG, filePath, {}); + if (task) { + taskConnections.target = task; + progressWindow.title = qsTr("Exporting"); + progressWindow.progressBar.value = 0; + progressWindow.task = task; + progressWindow.visible = true; + progressWindow.raise(); + task.start(); + } + }; + openFolderDialog.accepted.connect(openFolderDialog.currentAcceptHandler); + openFolderDialog.open(); + break; + case "export-as-apng": + if (openFileDialog.currentAcceptHandler) { + openFileDialog.accepted.disconnect(openFileDialog.currentAcceptHandler); + } + openFileDialog.fileMode = FileDialog.SaveFile; + openFileDialog.title = qsTr("Select save path"); + openFileDialog.nameFilters = ["PNG files(*.png)"]; + openFileDialog.defaultSuffix = "png"; + openFileDialog.currentFolder = Utils.getFileDir(mainForm.pagView.filePath); + openFileDialog.currentAcceptHandler = function () { + let filePath = openFileDialog.selectedFile; + let task = taskFactory.createTask(PAGTaskFactory.PAGTaskType_ExportAPNG, filePath, {}); + if (task) { + taskConnections.target = task; + progressWindow.title = qsTr("Exporting"); + progressWindow.progressBar.value = 0; + progressWindow.task = task; + progressWindow.visible = true; + progressWindow.raise(); + task.start(); + } + }; + openFileDialog.accepted.connect(openFileDialog.currentAcceptHandler); + openFileDialog.open(); + break; default: console.log(`Undefined command: [${command}]`); break; diff --git a/viewer/qml/Menu.qml b/viewer/qml/Menu.qml index 2ae63e4065..bf2d5252f5 100644 --- a/viewer/qml/Menu.qml +++ b/viewer/qml/Menu.qml @@ -48,6 +48,31 @@ Item { root.command("open-preferences"); } } + PAGMenu { + title: qsTr("Export") + Action { + text: qsTr("Export as PNG Sequence Frames") + enabled: root.hasPAGFile + onTriggered: { + root.command('export-as-png-sequence'); + } + } + Action { + text: qsTr("Export as APNG") + enabled: root.hasPAGFile + onTriggered: { + root.command('export-as-apng'); + } + } + Action { + text: qsTr("Export current frame as PNG") + enabled: root.hasPAGFile + shortcut: "Ctrl+P" + onTriggered: { + root.command('export-frame-as-png'); + } + } + } } PAGMenu { @@ -196,6 +221,31 @@ Item { root.command("open-pag-file"); } } + Platform.Menu { + title: qsTr("Export") + Platform.MenuItem { + text: qsTr("Export as PNG Sequence Frames") + enabled: root.hasPAGFile + onTriggered: { + root.command('export-as-png-sequence'); + } + } + Platform.MenuItem { + text: qsTr("Export as APNG") + enabled: root.hasPAGFile + onTriggered: { + root.command('export-as-apng'); + } + } + Platform.MenuItem { + text: qsTr("Export current frame as PNG") + enabled: root.hasPAGFile + shortcut: "Meta+P" + onTriggered: { + root.command('export-frame-as-png'); + } + } + } } Platform.Menu { title: qsTr("Play") diff --git a/viewer/qml/utils/Utils.qml b/viewer/qml/utils/Utils.qml index 0a6ed3d424..bbaa755fdc 100644 --- a/viewer/qml/utils/Utils.qml +++ b/viewer/qml/utils/Utils.qml @@ -23,4 +23,22 @@ QtObject { let fileName = urlObject.pathname.split('/').pop(); return fileName; } -} \ No newline at end of file + + function replaceLastIndexOf(str, oldPara, newPara) { + let lastIndex = str.lastIndexOf(oldPara); + if (lastIndex !== -1) { + return str; + } + + return str.substring(0, lastIndex) + newPara + str.substring(lastIndex + oldPara.length); + } + + function getNativeFilePath(str) { + str = str.replace(/\\/ig, '/'); + let schema = "file://"; + if (Qt.platform.os === "windows") { + schema += "/"; + } + return schema + str; + } +} diff --git a/viewer/src/main.cpp b/viewer/src/main.cpp index 66cde5f1f2..137aa2612c 100644 --- a/viewer/src/main.cpp +++ b/viewer/src/main.cpp @@ -22,6 +22,7 @@ #include <QSGRendererInterface> #include "PAGViewer.h" #include "rendering/PAGView.h" +#include "task/PAGTaskFactory.h" int main(int argc, char* argv[]) { bool cpuMode = false; @@ -60,6 +61,7 @@ int main(int argc, char* argv[]) { pag::PAGViewer app(argc, argv); QApplication::setWindowIcon(QIcon(":/images/window-icon.png")); qmlRegisterType<pag::PAGView>("PAG", 1, 0, "PAGView"); + qmlRegisterType<pag::PAGTaskFactory>("PAG", 1, 0, "PAGTaskFactory"); app.openFile(filePath.data()); return QApplication::exec(); diff --git a/viewer/src/platform/mac/PAGUtilsImpl.h b/viewer/src/platform/mac/PAGUtilsImpl.h new file mode 100644 index 0000000000..4e9eac2ce6 --- /dev/null +++ b/viewer/src/platform/mac/PAGUtilsImpl.h @@ -0,0 +1,9 @@ +#pragma once + +#include <QFileInfo> + +namespace pag::Utils { + +auto openFileInFinder(QFileInfo& fileInfo) -> void; + +} // namespace pag::Utils diff --git a/viewer/src/platform/mac/PAGUtilsImpl.mm b/viewer/src/platform/mac/PAGUtilsImpl.mm new file mode 100644 index 0000000000..d5eb5821a8 --- /dev/null +++ b/viewer/src/platform/mac/PAGUtilsImpl.mm @@ -0,0 +1,23 @@ +#include "PAGUtilsImpl.h" +#import <AppKit/AppKit.h> + +namespace pag::Utils { + +auto openFileInFinder(QFileInfo& fileInfo) -> void { + if (!fileInfo.exists()) { + return; + } + @autoreleasepool { + NSString* filePath = + [NSString stringWithUTF8String:fileInfo.absoluteFilePath().toUtf8().constData()]; + NSURL* fileUrl = [NSURL fileURLWithPath:filePath]; + if (!fileUrl) { + return; + } + fileUrl = [fileUrl URLByStandardizingPath]; + [[NSWorkspace sharedWorkspace] selectFile:filePath + inFileViewerRootedAtPath:[[fileUrl URLByDeletingLastPathComponent] path]]; + } +} + +} // namespace pag::Utils \ No newline at end of file diff --git a/viewer/src/platform/win/PAGUtilsImpl.cpp b/viewer/src/platform/win/PAGUtilsImpl.cpp new file mode 100644 index 0000000000..afbebb475e --- /dev/null +++ b/viewer/src/platform/win/PAGUtilsImpl.cpp @@ -0,0 +1,36 @@ +#include "PAGUtilsImpl.h" +#include <shlobj_core.h> +#include <windows.h> + +namespace pag::Utils { + +auto openFileInFinder(QFileInfo& fileInfo) -> void { + HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + if (FAILED(hr)) { + return; + } + + bool success = false; + PIDLIST_ABSOLUTE pidlFolder = ILCreateFromPathW(fileInfo.absolutePath().toStdWString().c_str()); + PIDLIST_ABSOLUTE pidlFile = ILCreateFromPathW(fileInfo.absoluteFilePath().toStdWString().c_str()); + if (pidlFolder && pidlFile) { + PCUITEMID_CHILD pidlChild = ILFindChild(pidlFolder, pidlFile); + if (pidlChild) { + PCUITEMID_CHILD_ARRAY pidls = const_cast<PCUITEMID_CHILD_ARRAY>(&pidlChild); + hr = SHOpenFolderAndSelectItems(pidlFolder, 1, pidls, 0); + success = SUCCEEDED(hr); + } + } + + if (pidlFolder) { + ILFree(pidlFolder); + } + if (pidlFile) { + ILFree(pidlFile); + } + CoUninitialize(); + + return; +} + +} // namespace pag::Utils \ No newline at end of file diff --git a/viewer/src/platform/win/PAGUtilsImpl.h b/viewer/src/platform/win/PAGUtilsImpl.h new file mode 100644 index 0000000000..4e9eac2ce6 --- /dev/null +++ b/viewer/src/platform/win/PAGUtilsImpl.h @@ -0,0 +1,9 @@ +#pragma once + +#include <QFileInfo> + +namespace pag::Utils { + +auto openFileInFinder(QFileInfo& fileInfo) -> void; + +} // namespace pag::Utils diff --git a/viewer/src/rendering/PAGWindow.cpp b/viewer/src/rendering/PAGWindow.cpp index 06d2b91019..b71cd51009 100644 --- a/viewer/src/rendering/PAGWindow.cpp +++ b/viewer/src/rendering/PAGWindow.cpp @@ -20,6 +20,8 @@ #include <QQmlContext> #include <QThread> #include "PAGWindowHelper.h" +#include "task/PAGTaskFactory.h" + namespace pag { QList<PAGWindow*> PAGWindow::AllWindows; @@ -28,7 +30,6 @@ PAGWindow::PAGWindow(QObject* parent) : QObject(parent) { } PAGWindow::~PAGWindow() { - delete pagView; delete engine; delete windowHelper; } @@ -62,9 +63,11 @@ auto PAGWindow::open() -> void { window->setTextRenderType(QQuickWindow::TextRenderType::NativeTextRendering); pagView = window->findChild<pag::PAGView*>("pagView"); + auto* taskFactory = window->findChild<PAGTaskFactory*>("taskFactory"); connect(window, SIGNAL(closing(QQuickCloseEvent*)), this, SLOT(onPAGViewerDestroyed()), Qt::QueuedConnection); + connect(pagView, &PAGView::fileChanged, taskFactory, &PAGTaskFactory::resetFile); } auto PAGWindow::getFilePath() -> QString { diff --git a/viewer/src/task/PAGTask.cpp b/viewer/src/task/PAGTask.cpp new file mode 100644 index 0000000000..4269e5876d --- /dev/null +++ b/viewer/src/task/PAGTask.cpp @@ -0,0 +1,167 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "PAGTask.h" +#include <QOpenGLContext> +#include "utils/PAGFileUtils.h" +#include "utils/PAGTimeUtils.h" + +namespace pag { + +PAGTask::PAGTask() : QObject(nullptr) { +} + +auto PAGTask::getVisible() const -> bool { + return visible; +} + +auto PAGTask::getProgress() const -> double { + return progress; +} + +PAGFileTask::PAGFileTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath) + : filePath(filePath), pagFile(pagFile) { + QObject::connect(&workerThread, &QThread::started, this, &PAGFileTask::startInternal, + Qt::DirectConnection); +} + +PAGFileTask::~PAGFileTask() { + if (workerThread.isRunning()) { + workerThread.quit(); + } + QObject::disconnect(&workerThread, &QThread::started, this, &PAGFileTask::startInternal); + releaseResource(); +} + +auto PAGFileTask::start() -> void { + if (pagFile == nullptr) { + return; + } + isWorking = true; + onBegin(); + workerThread.start(); +} + +auto PAGFileTask::pause() -> void { + isWorking = false; + currentFrame = 0; +} + +auto PAGFileTask::stop() -> void { + bool isWorking = this->isWorking; + this->isWorking = false; + if (isWorking) { + onFinish(); + } + currentFrame = 0; +} + +auto PAGFileTask::onBegin() -> void { + initOpenGLEnvironment(); + visible = true; + Q_EMIT visibleChanged(visible); +} + +auto PAGFileTask::onFinish() -> int { + visible = false; + Q_EMIT visibleChanged(visible); + return 0; +} + +auto PAGFileTask::onFrameFlush(double progress) -> void { + this->progress = progress; + Q_EMIT progressChanged(progress); +} + +auto PAGFileTask::isNeedRenderCurrentFrame() -> bool { + return currentFrame >= 0; +} + +auto PAGFileTask::releaseResource() -> void { + if (pagPlayer != nullptr) { + delete pagPlayer; + pagPlayer = nullptr; + } + surface = nullptr; + if (context != nullptr) { + if (frameBuffer != nullptr) { + context->makeCurrent(offscreenSurface); + frameBuffer->release(); + delete frameBuffer; + frameBuffer = nullptr; + } + context->doneCurrent(); + delete frameBuffer; + } + if (offscreenSurface != nullptr) { + delete offscreenSurface; + offscreenSurface = nullptr; + } +} + +auto PAGFileTask::startInternal() -> void { + float frameRate = pagFile->frameRate(); + pag::Frame totalFrame = Utils::usToFrame(pagFile->duration(), frameRate); + while (currentFrame < totalFrame) { + if (isNeedRenderCurrentFrame()) { + pagFile->setCurrentTime(Utils::frameToUs(currentFrame, frameRate)); + pagPlayer->flush(); + onFrameFlush(pagPlayer->getProgress()); + } + currentFrame++; + if (isWorking != true) { + releaseResource(); + workerThread.exit(0); + return; + } + } + isWorking = false; + int result = onFinish(); + currentFrame = 0; + releaseResource(); + Q_EMIT taskFinished(result, filePath); + Utils::openInFinder(filePath, true); + workerThread.exit(0); +} + +auto PAGFileTask::initOpenGLEnvironment() -> void { + if (surface != nullptr) { + return; + } + pagPlayer = new pag::PAGPlayer(); + context = new QOpenGLContext(); + context->create(); + offscreenSurface = new QOffscreenSurface(); + offscreenSurface->setFormat(context->format()); + offscreenSurface->create(); + context->makeCurrent(offscreenSurface); + frameBuffer = + new QOpenGLFramebufferObject(QSize(pagFile->width(), pagFile->height()), GL_TEXTURE_2D); + GLFrameBufferInfo frameBufferInfo; + frameBufferInfo.id = frameBuffer->handle(); + BackendRenderTarget renderTarget = + BackendRenderTarget(frameBufferInfo, frameBuffer->width(), frameBuffer->height()); + surface = pag::PAGSurface::MakeFrom(renderTarget, pag::ImageOrigin::TopLeft); + pagPlayer->setSurface(surface); + pagPlayer->setComposition(pagFile); + context->doneCurrent(); + context->moveToThread(&workerThread); + offscreenSurface->moveToThread(&workerThread); +} + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/task/PAGTask.h b/viewer/src/task/PAGTask.h new file mode 100644 index 0000000000..60461682bc --- /dev/null +++ b/viewer/src/task/PAGTask.h @@ -0,0 +1,97 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include <QObject> +#include <QOffscreenSurface> +#include <QOpenGLFramebufferObject> +#include <QThread> +#include <atomic> +#include "pag/pag.h" + +namespace pag { + +class PAGTask : public QObject { + Q_OBJECT + public: + PAGTask(); + + Q_PROPERTY(bool visible READ getVisible NOTIFY visibleChanged) + Q_PROPERTY(double progress READ getProgress NOTIFY progressChanged) + + virtual auto getVisible() const -> bool; + virtual auto getProgress() const -> double; + + Q_SIGNAL void taskFinished(int result, QString filePath); + Q_SIGNAL void visibleChanged(bool visible); + Q_SIGNAL void progressChanged(double progress); + + Q_INVOKABLE virtual void start() = 0; + Q_INVOKABLE virtual void pause() = 0; + Q_INVOKABLE virtual void stop() = 0; + + protected: + virtual auto onBegin() -> void = 0; + virtual auto onFinish() -> int = 0; + + private: + virtual auto startInternal() -> void = 0; + + protected: + bool visible = false; + double progress = 0.0; +}; + +class PAGFileTask : public PAGTask { + Q_OBJECT + public: + explicit PAGFileTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath); + ~PAGFileTask() override; + + auto start() -> void override; + auto pause() -> void override; + auto stop() -> void override; + + protected: + auto onBegin() -> void override; + auto onFinish() -> int override; + virtual auto onFrameFlush(double progress) -> void; + virtual auto isNeedRenderCurrentFrame() -> bool; + auto releaseResource() -> void; + + private: + auto startInternal() -> void override; + auto initOpenGLEnvironment() -> void; + + protected: + Frame currentFrame = 0; + QString filePath; + PAGPlayer* pagPlayer = nullptr; + QOpenGLContext* context = nullptr; + QOffscreenSurface* offscreenSurface = nullptr; + QOpenGLFramebufferObject* frameBuffer = nullptr; + std::shared_ptr<PAGFile> pagFile = nullptr; + std::shared_ptr<PAGSurface> surface = nullptr; + + private: + QThread workerThread; + std::atomic_bool isWorking = false; +}; + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/task/PAGTaskFactory.cpp b/viewer/src/task/PAGTaskFactory.cpp new file mode 100644 index 0000000000..ce2f2b9da1 --- /dev/null +++ b/viewer/src/task/PAGTaskFactory.cpp @@ -0,0 +1,70 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "PAGTaskFactory.h" +#include <QDir> +#include <QUrl> +#include <QVariantMap> +#include "task/export/PAGExportAPNGTask.h" +#include "task/export/PAGExportPNGTask.h" + +namespace pag { + +auto PAGTaskFactory::createTask(PAGTaskType taskType, const QString& outPath, + const QVariantMap& extraParams) -> PAGTask* { + if (pagFile == nullptr) { + return nullptr; + } + + QString path = outPath; + if (path.startsWith("file://")) { + path = QUrl(path).toLocalFile(); + } + + switch (taskType) { + case PAGTaskType_ExportPNG: { + int exportFrame = -1; + if (extraParams.contains("exportFrame")) { + exportFrame = extraParams.value("exportFrame").toInt(); + } + task = new PAGExportPNGTask(pagFile, path, exportFrame); + break; + } + case PAGTaskType_ExportAPNG: { + QFileInfo fileInfo(path); + QString pngFilePath = fileInfo.absolutePath() + "/" + fileInfo.baseName() + "_PNG"; + task = new PAGExportAPNGTask(pagFile, path, pngFilePath); + break; + } + default: { + delete task; + task = nullptr; + break; + } + } + + return task; +} + +auto PAGTaskFactory::resetFile([[maybe_unused]] std::shared_ptr<PAGFile> pagFile, + std::string filePath) -> void { + this->pagFile = PAGFile::Load(filePath); + this->filePath = filePath; +} + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/task/PAGTaskFactory.h b/viewer/src/task/PAGTaskFactory.h new file mode 100644 index 0000000000..fcc6cb1c15 --- /dev/null +++ b/viewer/src/task/PAGTaskFactory.h @@ -0,0 +1,45 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include <QObject> +#include "PAGTask.h" +#include "pag/pag.h" + +namespace pag { + +class PAGTaskFactory : public QObject { + Q_OBJECT + public: + Q_ENUMS(PAGTaskType) + + enum PAGTaskType { PAGTaskType_None, PAGTaskType_ExportPNG, PAGTaskType_ExportAPNG }; + + Q_INVOKABLE PAGTask* createTask(PAGTaskType taskType, const QString& outPath, + const QVariantMap& extraParams); + + auto resetFile(std::shared_ptr<PAGFile> pagFile, std::string filePath) -> void; + + private: + PAGTask* task = nullptr; + std::string filePath; + std::shared_ptr<PAGFile> pagFile; +}; + +} // namespace pag diff --git a/viewer/src/task/export/PAGExportAPNGTask.cpp b/viewer/src/task/export/PAGExportAPNGTask.cpp new file mode 100644 index 0000000000..bcbc16431d --- /dev/null +++ b/viewer/src/task/export/PAGExportAPNGTask.cpp @@ -0,0 +1,46 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "PAGExportAPNGTask.h" +#include <QDebug> +#include "utils/PAGFileUtils.h" +#include "utils/PAGUtils.h" + +namespace pag { + +PAGExportAPNGTask::PAGExportAPNGTask(std::shared_ptr<PAGFile>& pagFile, const QString& apngFilePath, + const QString& pngFilePath) + : PAGExportPNGTask(pagFile, pngFilePath), apngFilePath(apngFilePath) { +} + +auto PAGExportAPNGTask::onFrameFlush(double progress) -> void { + PAGExportPNGTask::onFrameFlush(progress * 0.9); +} + +auto PAGExportAPNGTask::onFinish() -> int { + std::string outPath = apngFilePath.toStdString(); + std::string firstPNGPath = QString("%1/1.png").arg(filePath).toStdString(); + int frameRate = static_cast<int>(pagFile->frameRate()); + Utils::exportAPNGFromPNGSequence(outPath, firstPNGPath, frameRate); + PAGExportPNGTask::onFrameFlush(1.0); + Utils::deleteDir(filePath); + Utils::openInFinder(apngFilePath, true); + return PAGExportPNGTask::onFinish(); +} + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/task/export/PAGExportAPNGTask.h b/viewer/src/task/export/PAGExportAPNGTask.h new file mode 100644 index 0000000000..d4f7287cd4 --- /dev/null +++ b/viewer/src/task/export/PAGExportAPNGTask.h @@ -0,0 +1,40 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "task/export/PAGExportPNGTask.h" + +namespace pag { + +class PAGExportAPNGTask : public PAGExportPNGTask { + Q_OBJECT + public: + explicit PAGExportAPNGTask(std::shared_ptr<PAGFile>& pagFile, const QString& apngFilePath, + const QString& pngFilePath); + + protected: + auto onFrameFlush(double progress) -> void override; + auto onFinish() -> int override; + + private: + QString apngFilePath = ""; + int exportFrame = -1; +}; + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/task/export/PAGExportPNGTask.cpp b/viewer/src/task/export/PAGExportPNGTask.cpp new file mode 100644 index 0000000000..4f8573afb4 --- /dev/null +++ b/viewer/src/task/export/PAGExportPNGTask.cpp @@ -0,0 +1,62 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "task/export/PAGExportPNGTask.h" +#include <QImage> +#include <QOpenGLContext> +#include "utils/PAGFileUtils.h" + +namespace pag { + +PAGExportPNGTask::PAGExportPNGTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath, + int exportFrame) + : PAGFileTask(pagFile, filePath), exportFrame(exportFrame) { + if (!filePath.endsWith(".png")) { + Utils::makeDir(filePath); + } +} + +auto PAGExportPNGTask::onFrameFlush(double progress) -> void { + PAGFileTask::onFrameFlush(progress); + QString outPath; + if (exportFrame >= 0) { + outPath = filePath; + } else { + outPath = QString("%1/%2.png").arg(filePath).arg(currentFrame); + } + exportCurrentFrameAsPNG(outPath); +} + +auto PAGExportPNGTask::isNeedRenderCurrentFrame() -> bool { + if (exportFrame < 0) { + return true; + } + return exportFrame == currentFrame; +} + +auto PAGExportPNGTask::exportCurrentFrameAsPNG(const QString& outPath) -> void { + context->makeCurrent(offscreenSurface); + auto image = frameBuffer->toImage(false); + bool result = image.save(outPath, "PNG"); + if (!result) { + qDebug() << "Failed to save image to path: " << outPath; + } + context->doneCurrent(); +} + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/task/export/PAGExportPNGTask.h b/viewer/src/task/export/PAGExportPNGTask.h new file mode 100644 index 0000000000..f0bebc6706 --- /dev/null +++ b/viewer/src/task/export/PAGExportPNGTask.h @@ -0,0 +1,42 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "task/PAGTask.h" + +namespace pag { + +class PAGExportPNGTask : public PAGFileTask { + Q_OBJECT + public: + explicit PAGExportPNGTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath, + int exportFrame = -1); + + protected: + auto onFrameFlush(double progress) -> void override; + auto isNeedRenderCurrentFrame() -> bool override; + + private: + auto exportCurrentFrameAsPNG(const QString& outPath) -> void; + + private: + int exportFrame = -1; +}; + +} // namespace pag \ No newline at end of file diff --git a/viewer/src/utils/PAGFileUtils.cpp b/viewer/src/utils/PAGFileUtils.cpp new file mode 100644 index 0000000000..38f36e298c --- /dev/null +++ b/viewer/src/utils/PAGFileUtils.cpp @@ -0,0 +1,86 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "utils/PAGFileUtils.h" +#include <QDesktopServices> +#include <QDir> +#include <QUrl> +#include "PAGUtilsImpl.h" + +namespace pag::Utils { + +auto openInFinder(const QString& path, bool select) -> void { + QFileInfo fileInfo(path); + if (fileInfo.exists() == false) { + return; + } + + if (select) { + openFileInFinder(fileInfo); + } else { + QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.absoluteFilePath())); + } +} + +auto deleteFile(const QString& path) -> bool { + QFile file(path); + if (!file.exists()) { + return true; + } + return file.remove(); +} + +auto deleteDir(const QString& path) -> bool { + QDir dir(path); + if (!dir.exists()) { + return true; + } + + dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + QFileInfoList fileList = dir.entryInfoList(); + for (auto& fileInfo : fileList) { + if (fileInfo.isFile()) { + if (!fileInfo.dir().remove(fileInfo.fileName())) { + return false; + } + } else { + if (!deleteDir(fileInfo.absoluteFilePath())) { + return false; + } + } + } + + return dir.rmdir(path); +} + +auto makeDir(const QString& path, bool isDir) -> bool { + QString dirPath; + QFileInfo fileInfo(path); + if (isDir) { + dirPath = path; + } else { + dirPath = fileInfo.absolutePath(); + } + QDir dir(dirPath); + if (dir.exists()) { + return true; + } + return dir.mkpath(dirPath); +} + +} // namespace pag::Utils diff --git a/viewer/src/utils/PAGFileUtils.h b/viewer/src/utils/PAGFileUtils.h new file mode 100644 index 0000000000..8de55a27a2 --- /dev/null +++ b/viewer/src/utils/PAGFileUtils.h @@ -0,0 +1,33 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include <QString> + +namespace pag::Utils { + +auto openInFinder(const QString& path, bool select = true) -> void; + +auto deleteFile(const QString& path) -> bool; + +auto deleteDir(const QString& path) -> bool; + +auto makeDir(const QString& path, bool isDir = true) -> bool; + +} // namespace pag::Utils diff --git a/viewer/src/utils/PAGTimeUtils.cpp b/viewer/src/utils/PAGTimeUtils.cpp new file mode 100644 index 0000000000..214eba6eac --- /dev/null +++ b/viewer/src/utils/PAGTimeUtils.cpp @@ -0,0 +1,31 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "PAGTimeUtils.h" + +namespace pag::Utils { + +auto usToFrame(int64_t time, float frameRate) -> Frame { + return static_cast<Frame>(std::floor(time / 1000000 * frameRate)); +} + +auto frameToUs(Frame frame, float frameRate) -> int64_t { + return static_cast<int64_t>(std::ceil(frame * 1000000 / frameRate)); +} + +} // namespace pag::Utils diff --git a/viewer/src/utils/PAGTimeUtils.h b/viewer/src/utils/PAGTimeUtils.h new file mode 100644 index 0000000000..825b208067 --- /dev/null +++ b/viewer/src/utils/PAGTimeUtils.h @@ -0,0 +1,28 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "pag/pag.h" + +namespace pag::Utils { + +auto usToFrame(int64_t time, float frameRate) -> Frame; +auto frameToUs(Frame frame, float frameRate) -> int64_t; + +} // namespace pag::Utils diff --git a/viewer/src/utils/PAGUtils.cpp b/viewer/src/utils/PAGUtils.cpp new file mode 100644 index 0000000000..45d09457f0 --- /dev/null +++ b/viewer/src/utils/PAGUtils.cpp @@ -0,0 +1,31 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "PAGUtils.h" +#include "utils/apng/APNGAssembler.h" + +namespace pag::Utils { + +auto exportAPNGFromPNGSequence(const std::string& outPath, const std::string& firstPNGPath, + int frameRate) -> int { + auto apngAssembler = APNGAssembler(); + + return apngAssembler.exportFromPNGSequence(outPath, firstPNGPath, frameRate); +} + +} // namespace pag::Utils \ No newline at end of file diff --git a/viewer/src/utils/PAGUtils.h b/viewer/src/utils/PAGUtils.h new file mode 100644 index 0000000000..83a27c857f --- /dev/null +++ b/viewer/src/utils/PAGUtils.h @@ -0,0 +1,28 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include <iostream> + +namespace pag::Utils { + +auto exportAPNGFromPNGSequence(const std::string& outPath, const std::string& firstPNGPath, + int frameRate) -> int; + +} // namespace pag::Utils \ No newline at end of file diff --git a/viewer/src/utils/apng/APNGAssembler.cpp b/viewer/src/utils/apng/APNGAssembler.cpp new file mode 100644 index 0000000000..ef82940196 --- /dev/null +++ b/viewer/src/utils/apng/APNGAssembler.cpp @@ -0,0 +1,776 @@ +/* APNG Assembler 2.91 + * + * This program creates APNG animation from PNG/TGA image sequence. + * + * http://apngasm.sourceforge.net/ + * + * Copyright (c) 2009-2016 Max Stepin + * maxst at users.sourceforge.net + * + * zlib license + * ------------ + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + */ + +#include "APNGAssembler.h" +#include "png.h" + +APNGAssembler::APNGAssembler() = default; + +APNGAssembler::~APNGAssembler() = default; + +auto APNGAssembler::exportFromPNGSequence(const std::string& outPath, + const std::string& firstPNGPath, int frameRate) -> int { + std::string str1 = outPath; + std::string str2 = firstPNGPath; + char* szOut = str1.data(); + char* szImage = str2.data(); + + delay_den = frameRate; + + int ret = load_image_sequence(szImage, first, img, delay_num, delay_den, &coltype); + if (ret) { + return ret; + } + + for (auto& image : img) { + if (image.type != coltype) { + optim_upconvert(&image, coltype); + } + } + + if (coltype == 6 || coltype == 4) { + for (auto& image : img) { + optim_dirty_transp(&image); + } + } + + optim_duplicates(img, first); + + if (!keep_coltype) { + optim_downconvert(img); + } + + coltype = static_cast<unsigned char>(img[0].type); + if (coltype == 3 && !keep_palette) { + optim_palette(img); + } + if (coltype == 2 || coltype == 0) { + optim_add_transp(img); + } + + ret = save_apng(szOut, img, loops, first, deflate_method, iter); + for (auto& image : img) { + image.free(); + } + if (ret) { + return ret; + } + + return 0; +}; + +auto APNGAssembler::write_chunk(FILE* f, const char* name, unsigned char* data, unsigned int length) + -> void { + unsigned char buf[4]; + unsigned int crc = (unsigned int)crc32(0, Z_NULL, 0); + + png_save_uint_32(buf, length); + fwrite(buf, 1, 4, f); + fwrite(name, 1, 4, f); + crc = (unsigned int)crc32(crc, (const Bytef*)name, 4); + + if (memcmp(name, "fdAT", 4) == 0) { + png_save_uint_32(buf, next_seq_num++); + fwrite(buf, 1, 4, f); + crc = (unsigned int)crc32(crc, buf, 4); + length -= 4; + } + + if (data != NULL && length > 0) { + fwrite(data, 1, length, f); + crc = (unsigned int)crc32(crc, data, length); + } + + png_save_uint_32(buf, crc); + fwrite(buf, 1, 4, f); +} + +auto APNGAssembler::write_IDATs(FILE* f, unsigned int frame, unsigned char* data, + unsigned int length, unsigned int idat_size) -> void { + unsigned int z_cmf = data[0]; + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) { + if (length >= 2) { + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + while (idat_size <= half_z_window_size && half_z_window_size >= 256) { + z_cinfo--; + half_z_window_size >>= 1; + } + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + if (data[0] != (unsigned char)z_cmf) { + data[0] = (unsigned char)z_cmf; + data[1] &= 0xe0; + data[1] += (unsigned char)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + + while (length > 0) { + unsigned int ds = length; + if (ds > 32768) ds = 32768; + + if (frame == 0) write_chunk(f, "IDAT", data, ds); + else + write_chunk(f, "fdAT", data, ds + 4); + + data += ds; + length -= ds; + } +} + +auto APNGAssembler::deflate_rect_op(Image* image, int x, int y, int w, int h, int bpp, + int zbuf_size, int n) -> void { + op_zstream1.data_type = Z_BINARY; + op_zstream1.next_out = op_zbuf1; + op_zstream1.avail_out = zbuf_size; + + op_zstream2.data_type = Z_BINARY; + op_zstream2.next_out = op_zbuf2; + op_zstream2.avail_out = zbuf_size; + + process_rect(image, x * bpp, w * bpp, y, h, bpp, NULL); + + deflate(&op_zstream1, Z_FINISH); + deflate(&op_zstream2, Z_FINISH); + op[n].image = image; + + if (op_zstream1.total_out < op_zstream2.total_out) { + op[n].size = (unsigned int)op_zstream1.total_out; + op[n].filters = 0; + } else { + op[n].size = (unsigned int)op_zstream2.total_out; + op[n].filters = 1; + } + op[n].x = x; + op[n].y = y; + op[n].w = w; + op[n].h = h; + op[n].valid = 1; + deflateReset(&op_zstream1); + deflateReset(&op_zstream2); +} + +auto APNGAssembler::deflate_rect_fin([[maybe_unused]] int deflate_method, [[maybe_unused]] int iter, + unsigned char* zbuf, unsigned int* zsize, int bpp, + unsigned char* dest, int zbuf_size, int n) -> void { + Image* image = op[n].image; + int xbytes = op[n].x * bpp; + int rowbytes = op[n].w * bpp; + int y = op[n].y; + int h = op[n].h; + + if (op[n].filters == 0) { + unsigned char* dp = dest; + for (int j = y; j < y + h; j++) { + *dp++ = 0; + memcpy(dp, image->rows[j] + xbytes, rowbytes); + dp += rowbytes; + } + } else + process_rect(image, xbytes, rowbytes, y, h, bpp, dest); + + z_stream fin_zstream; + + fin_zstream.data_type = Z_BINARY; + fin_zstream.zalloc = Z_NULL; + fin_zstream.zfree = Z_NULL; + fin_zstream.opaque = Z_NULL; + deflateInit2(&fin_zstream, Z_BEST_COMPRESSION, 8, 15, 8, + op[n].filters ? Z_FILTERED : Z_DEFAULT_STRATEGY); + + fin_zstream.next_out = zbuf; + fin_zstream.avail_out = zbuf_size; + fin_zstream.next_in = dest; + fin_zstream.avail_in = h * (rowbytes + 1); + deflate(&fin_zstream, Z_FINISH); + *zsize = (unsigned int)fin_zstream.total_out; + deflateEnd(&fin_zstream); +} + +auto APNGAssembler::get_rect(unsigned int w, unsigned int h, Image* image1, Image* image2, + Image* temp, unsigned int bpp, int zbuf_size, unsigned int has_tcolor, + unsigned int tcolor, int n) -> void { + unsigned int i, j, x0, y0, w0, h0; + unsigned int x_min = w - 1; + unsigned int y_min = h - 1; + unsigned int x_max = 0; + unsigned int y_max = 0; + unsigned int diffnum = 0; + unsigned int over_is_possible = 1; + + if (!has_tcolor) over_is_possible = 0; + + if (bpp == 1) { + for (j = 0; j < h; j++) { + unsigned char* pa = image1->rows[j]; + unsigned char* pb = image2->rows[j]; + unsigned char* pc = temp->rows[j]; + for (i = 0; i < w; i++) { + unsigned char c = *pb++; + if (*pa++ != c) { + diffnum++; + if (has_tcolor && c == tcolor) over_is_possible = 0; + if (i < x_min) x_min = i; + if (i > x_max) x_max = i; + if (j < y_min) y_min = j; + if (j > y_max) y_max = j; + } else + c = tcolor; + + *pc++ = c; + } + } + } else if (bpp == 2) { + for (j = 0; j < h; j++) { + unsigned short* pa = (unsigned short*)image1->rows[j]; + unsigned short* pb = (unsigned short*)image2->rows[j]; + unsigned short* pc = (unsigned short*)temp->rows[j]; + for (i = 0; i < w; i++) { + unsigned int c1 = *pa++; + unsigned int c2 = *pb++; + if ((c1 != c2) && ((c1 >> 8) || (c2 >> 8))) { + diffnum++; + if ((c2 >> 8) != 0xFF) over_is_possible = 0; + if (i < x_min) x_min = i; + if (i > x_max) x_max = i; + if (j < y_min) y_min = j; + if (j > y_max) y_max = j; + } else + c2 = 0; + + *pc++ = c2; + } + } + } else if (bpp == 3) { + for (j = 0; j < h; j++) { + unsigned char* pa = image1->rows[j]; + unsigned char* pb = image2->rows[j]; + unsigned char* pc = temp->rows[j]; + for (i = 0; i < w; i++) { + unsigned int c1 = (pa[2] << 16) + (pa[1] << 8) + pa[0]; + unsigned int c2 = (pb[2] << 16) + (pb[1] << 8) + pb[0]; + if (c1 != c2) { + diffnum++; + if (has_tcolor && c2 == tcolor) over_is_possible = 0; + if (i < x_min) x_min = i; + if (i > x_max) x_max = i; + if (j < y_min) y_min = j; + if (j > y_max) y_max = j; + } else + c2 = tcolor; + + memcpy(pc, &c2, 3); + pa += 3; + pb += 3; + pc += 3; + } + } + } else if (bpp == 4) { + for (j = 0; j < h; j++) { + unsigned int* pa = (unsigned int*)image1->rows[j]; + unsigned int* pb = (unsigned int*)image2->rows[j]; + unsigned int* pc = (unsigned int*)temp->rows[j]; + for (i = 0; i < w; i++) { + unsigned int c1 = *pa++; + unsigned int c2 = *pb++; + if ((c1 != c2) && ((c1 >> 24) || (c2 >> 24))) { + diffnum++; + if ((c2 >> 24) != 0xFF) over_is_possible = 0; + if (i < x_min) x_min = i; + if (i > x_max) x_max = i; + if (j < y_min) y_min = j; + if (j > y_max) y_max = j; + } else + c2 = 0; + + *pc++ = c2; + } + } + } + + if (diffnum == 0) { + x0 = y0 = 0; + w0 = h0 = 1; + } else { + x0 = x_min; + y0 = y_min; + w0 = x_max - x_min + 1; + h0 = y_max - y_min + 1; + } + + deflate_rect_op(image2, x0, y0, w0, h0, bpp, zbuf_size, n * 2); + + if (over_is_possible) deflate_rect_op(temp, x0, y0, w0, h0, bpp, zbuf_size, n * 2 + 1); +} + +auto APNGAssembler::process_rect(Image* image, int xbytes, int rowbytes, int y, int h, int bpp, + unsigned char* dest) -> void { + int i, j, v; + int a, b, c, pa, pb, pc, p; + unsigned char* prev = NULL; + unsigned char* dp = dest; + unsigned char* out; + + for (j = y; j < y + h; j++) { + unsigned char* row = image->rows[j] + xbytes; + unsigned int sum = 0; + unsigned char* best_row = row_buf; + unsigned int mins = ((unsigned int)(-1)) >> 1; + + out = row_buf + 1; + for (i = 0; i < rowbytes; i++) { + v = out[i] = row[i]; + sum += (v < 128) ? v : 256 - v; + } + mins = sum; + + sum = 0; + out = sub_row + 1; + for (i = 0; i < bpp; i++) { + v = out[i] = row[i]; + sum += (v < 128) ? v : 256 - v; + } + for (i = bpp; i < rowbytes; i++) { + v = out[i] = row[i] - row[i - bpp]; + sum += (v < 128) ? v : 256 - v; + if (sum > mins) break; + } + if (sum < mins) { + mins = sum; + best_row = sub_row; + } + + if (prev) { + sum = 0; + out = up_row + 1; + for (i = 0; i < rowbytes; i++) { + v = out[i] = row[i] - prev[i]; + sum += (v < 128) ? v : 256 - v; + if (sum > mins) break; + } + if (sum < mins) { + mins = sum; + best_row = up_row; + } + + sum = 0; + out = avg_row + 1; + for (i = 0; i < bpp; i++) { + v = out[i] = row[i] - prev[i] / 2; + sum += (v < 128) ? v : 256 - v; + } + for (i = bpp; i < rowbytes; i++) { + v = out[i] = row[i] - (prev[i] + row[i - bpp]) / 2; + sum += (v < 128) ? v : 256 - v; + if (sum > mins) break; + } + if (sum < mins) { + mins = sum; + best_row = avg_row; + } + + sum = 0; + out = paeth_row + 1; + for (i = 0; i < bpp; i++) { + v = out[i] = row[i] - prev[i]; + sum += (v < 128) ? v : 256 - v; + } + for (i = bpp; i < rowbytes; i++) { + a = row[i - bpp]; + b = prev[i]; + c = prev[i - bpp]; + p = b - c; + pc = a - c; + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); + p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; + v = out[i] = row[i] - p; + sum += (v < 128) ? v : 256 - v; + if (sum > mins) break; + } + if (sum < mins) { + best_row = paeth_row; + } + } + + if (dest == NULL) { + // deflate_rect_op() + op_zstream1.next_in = row_buf; + op_zstream1.avail_in = rowbytes + 1; + deflate(&op_zstream1, Z_NO_FLUSH); + + op_zstream2.next_in = best_row; + op_zstream2.avail_in = rowbytes + 1; + deflate(&op_zstream2, Z_NO_FLUSH); + } else { + // deflate_rect_fin() + memcpy(dp, best_row, rowbytes + 1); + dp += rowbytes + 1; + } + + prev = row; + } +} + +auto APNGAssembler::load_image_sequence(char* szImage, [[maybe_unused]] unsigned int first, + std::vector<Image>& img, int delay_num, int delay_den, + unsigned char* coltype) -> int { + char szFormat[256]; + char szNext[256]; + char* szExt = strrchr(szImage, '.'); + unsigned int i, cur = 0, frames = 0; + FILE* f; + + if (szExt == NULL || szExt == szImage) { + printf("Error: *%s sequence not found\n", szExt); + return 1; + } else if (*(szExt - 1) == '*') { + f = NULL; + for (i = 1; i < 6; i++) { + strcpy(szFormat, szImage); + snprintf(szFormat + (szExt - 1 - szImage), sizeof(szFormat) - (szExt - 1 - szImage), + "%%0%dd%%s", i); + cur = 0; + snprintf(szNext, sizeof(szNext), szFormat, cur, szExt); + if ((f = fopen(szNext, "rb")) != 0) break; + cur = 1; + snprintf(szNext, sizeof(szNext), szFormat, cur, szExt); + if ((f = fopen(szNext, "rb")) != 0) break; + } + + if (f != NULL) { + fclose(f); + } else { + printf("Error: *%s sequence not found\n", szExt); + return 1; + } + } else { + for (i = 0; i < 6; i++) { + if (szImage == szExt - i) break; + if (*(szExt - i - 1) < '0') break; + if (*(szExt - i - 1) > '9') break; + } + cur = static_cast<unsigned int>(atoi(szExt - i)); + strcpy(szFormat, szImage); + snprintf(szFormat + (szExt - i - szImage), sizeof(szFormat) - (szExt - i - szImage), + "%%0%dd%%s", i); + strcpy(szNext, szImage); + } + + if ((f = fopen(szNext, "rb")) == 0) { + printf("Error: can't open the file '%s'", szNext); + return 1; + } + + do { + frames++; + fclose(f); + snprintf(szNext, sizeof(szNext), szFormat, cur + frames, szExt); + f = fopen(szNext, "rb"); + } while (f != 0); + + img.resize(frames); + + for (i = 0; i < frames; i++) { + snprintf(szNext, sizeof(szNext), szFormat, cur + i, szExt); + + if (load_image(szNext, &img[i])) return 1; + + img[i].delay_num = static_cast<unsigned int>(delay_num); + img[i].delay_den = static_cast<unsigned int>(delay_den); + + snprintf(szNext, sizeof(szNext), szFormat, cur + i, ".txt"); + f = fopen(szNext, "rt"); + if (f != 0) { + char szStr[256]; + if (fgets(szStr, 256, f) != NULL) { + int d1, d2; + if (sscanf(szStr, "delay=%d/%d", &d1, &d2) == 2) { + if (d1 != 0) img[i].delay_num = static_cast<unsigned int>(d1); + if (d2 != 0) img[i].delay_den = static_cast<unsigned int>(d2); + } + } + fclose(f); + } + + if (img[0].w != img[i].w || img[0].h != img[i].h) { + printf("Error at %s: different image size\n", szNext); + return 1; + } + } + + *coltype = find_common_coltype(img); + return 0; +} + +auto APNGAssembler::save_apng(const char* szOut, std::vector<Image>& img, unsigned int loops, + unsigned int first, int deflate_method, [[maybe_unused]] int iter) + -> int { + unsigned char coltype = img[0].type; + unsigned int has_tcolor = 0; + unsigned int tcolor = 0; + + if (coltype == 0) { + if (img[0].ts) { + has_tcolor = 1; + tcolor = img[0].tr[1]; + } + } else if (coltype == 2) { + if (img[0].ts) { + has_tcolor = 1; + tcolor = (((img[0].tr[5] << 8) + img[0].tr[3]) << 8) + img[0].tr[1]; + } + } else if (coltype == 3) { + for (int c = 0; c < img[0].ts; c++) + if (img[0].tr[c] == 0) { + has_tcolor = 1; + tcolor = c; + break; + } + } else + has_tcolor = 1; + + FILE* f; + if ((f = fopen(szOut, "wb")) == 0) { + printf("Error: can't save to file '%s'\n", szOut); + return 1; + } + + unsigned char buf_IHDR[13]; + unsigned char buf_acTL[8]; + unsigned char buf_fcTL[26]; + + unsigned int width = img[0].w; + unsigned int height = img[0].h; + unsigned int bpp = img[0].bpp; + unsigned int rowbytes = width * bpp; + unsigned int visible = (unsigned int)(img.size() - first); + + Image temp, over1, over2, over3, rest; + temp.init(&img[0]); + over1.init(&img[0]); + over2.init(&img[0]); + over3.init(&img[0]); + rest.init(&img[0]); + unsigned char* dest = new unsigned char[(rowbytes + 1) * height]; + + png_save_uint_32(buf_IHDR, width); + png_save_uint_32(buf_IHDR + 4, height); + buf_IHDR[8] = 8; + buf_IHDR[9] = coltype; + buf_IHDR[10] = 0; + buf_IHDR[11] = 0; + buf_IHDR[12] = 0; + + png_save_uint_32(buf_acTL, visible); + png_save_uint_32(buf_acTL + 4, loops); + + fwrite(png_sign, 1, 8, f); + + write_chunk(f, "IHDR", buf_IHDR, 13); + + if (img.size() > 1) write_chunk(f, "acTL", buf_acTL, 8); + else + first = 0; + + if (img[0].ps > 0) write_chunk(f, "PLTE", (unsigned char*)(&img[0].pl), img[0].ps * 3); + + if (img[0].ts > 0) write_chunk(f, "tRNS", img[0].tr, img[0].ts); + + op_zstream1.data_type = Z_BINARY; + op_zstream1.zalloc = Z_NULL; + op_zstream1.zfree = Z_NULL; + op_zstream1.opaque = Z_NULL; + deflateInit2(&op_zstream1, Z_BEST_SPEED + 1, 8, 15, 8, Z_DEFAULT_STRATEGY); + + op_zstream2.data_type = Z_BINARY; + op_zstream2.zalloc = Z_NULL; + op_zstream2.zfree = Z_NULL; + op_zstream2.opaque = Z_NULL; + deflateInit2(&op_zstream2, Z_BEST_SPEED + 1, 8, 15, 8, Z_FILTERED); + + unsigned int idat_size = (rowbytes + 1) * height; + unsigned int zbuf_size = idat_size + ((idat_size + 7) >> 3) + ((idat_size + 63) >> 6) + 11; + + unsigned char* zbuf = new unsigned char[zbuf_size]; + op_zbuf1 = new unsigned char[zbuf_size]; + op_zbuf2 = new unsigned char[zbuf_size]; + row_buf = new unsigned char[rowbytes + 1]; + sub_row = new unsigned char[rowbytes + 1]; + up_row = new unsigned char[rowbytes + 1]; + avg_row = new unsigned char[rowbytes + 1]; + paeth_row = new unsigned char[rowbytes + 1]; + + row_buf[0] = 0; + sub_row[0] = 1; + up_row[0] = 2; + avg_row[0] = 3; + paeth_row[0] = 4; + + unsigned int i, j, k; + unsigned int zsize = 0; + unsigned int x0 = 0; + unsigned int y0 = 0; + unsigned int w0 = width; + unsigned int h0 = height; + unsigned char bop = 0; + unsigned char dop = 0; + next_seq_num = 0; + + for (j = 0; j < 6; j++) op[j].valid = 0; + deflate_rect_op(&img[0], x0, y0, w0, h0, bpp, zbuf_size, 0); + deflate_rect_fin(deflate_method, iter, zbuf, &zsize, bpp, dest, zbuf_size, 0); + + if (first) { + write_IDATs(f, 0, zbuf, zsize, idat_size); + + printf("saving %s (frame %d of %d)\n", szOut, 1, visible); + for (j = 0; j < 6; j++) op[j].valid = 0; + deflate_rect_op(&img[1], x0, y0, w0, h0, bpp, zbuf_size, 0); + deflate_rect_fin(deflate_method, iter, zbuf, &zsize, bpp, dest, zbuf_size, 0); + } + + for (i = first; i < img.size() - 1; i++) { + unsigned int op_min; + int op_best; + +#ifdef DEBUG +// printf("saving %s (frame %d of %d)\n", szOut, i-first+2, visible); +#endif + + for (j = 0; j < 6; j++) op[j].valid = 0; + + /* dispose = none */ + get_rect(width, height, &img[i], &img[i + 1], &over1, bpp, zbuf_size, has_tcolor, tcolor, 0); + + /* dispose = background */ + if (has_tcolor) { + for (j = 0; j < height; j++) memcpy(temp.rows[j], img[i].rows[j], rowbytes); + if (coltype == 2) + for (j = 0; j < h0; j++) + for (k = 0; k < w0; k++) memcpy(temp.rows[j + y0] + (k + x0) * 3, &tcolor, 3); + else + for (j = 0; j < h0; j++) memset(temp.rows[j + y0] + x0 * bpp, tcolor, w0 * bpp); + + get_rect(width, height, &temp, &img[i + 1], &over2, bpp, zbuf_size, has_tcolor, tcolor, 1); + } + + /* dispose = previous */ + if (i > first) + get_rect(width, height, &rest, &img[i + 1], &over3, bpp, zbuf_size, has_tcolor, tcolor, 2); + + op_min = op[0].size; + op_best = 0; + for (j = 1; j < 6; j++) + if (op[j].valid) { + if (op[j].size < op_min) { + op_min = op[j].size; + op_best = j; + } + } + + dop = op_best >> 1; + + png_save_uint_32(buf_fcTL, next_seq_num++); + png_save_uint_32(buf_fcTL + 4, w0); + png_save_uint_32(buf_fcTL + 8, h0); + png_save_uint_32(buf_fcTL + 12, x0); + png_save_uint_32(buf_fcTL + 16, y0); + png_save_uint_16(buf_fcTL + 20, img[i].delay_num); + png_save_uint_16(buf_fcTL + 22, img[i].delay_den); + buf_fcTL[24] = dop; + buf_fcTL[25] = bop; + write_chunk(f, "fcTL", buf_fcTL, 26); + + write_IDATs(f, i, zbuf, zsize, idat_size); + + /* process apng dispose - exportStart */ + if (dop != 2) + for (j = 0; j < height; j++) memcpy(rest.rows[j], img[i].rows[j], rowbytes); + + if (dop == 1) { + if (coltype == 2) + for (j = 0; j < h0; j++) + for (k = 0; k < w0; k++) memcpy(rest.rows[j + y0] + (k + x0) * 3, &tcolor, 3); + else + for (j = 0; j < h0; j++) memset(rest.rows[j + y0] + x0 * bpp, tcolor, w0 * bpp); + } + /* process apng dispose - end */ + + x0 = op[op_best].x; + y0 = op[op_best].y; + w0 = op[op_best].w; + h0 = op[op_best].h; + bop = op_best & 1; + + deflate_rect_fin(deflate_method, iter, zbuf, &zsize, bpp, dest, zbuf_size, op_best); + } + + if (img.size() > 1) { + png_save_uint_32(buf_fcTL, next_seq_num++); + png_save_uint_32(buf_fcTL + 4, w0); + png_save_uint_32(buf_fcTL + 8, h0); + png_save_uint_32(buf_fcTL + 12, x0); + png_save_uint_32(buf_fcTL + 16, y0); + png_save_uint_16(buf_fcTL + 20, img.back().delay_num); + png_save_uint_16(buf_fcTL + 22, img.back().delay_den); + buf_fcTL[24] = 0; + buf_fcTL[25] = bop; + write_chunk(f, "fcTL", buf_fcTL, 26); + } + + write_IDATs(f, (unsigned int)(img.size() - 1), zbuf, zsize, idat_size); + + write_chunk(f, "tEXt", png_Software, 28); + write_chunk(f, "IEND", 0, 0); + fclose(f); + + delete[] zbuf; + delete[] op_zbuf1; + delete[] op_zbuf2; + delete[] row_buf; + delete[] sub_row; + delete[] up_row; + delete[] avg_row; + delete[] paeth_row; + + deflateEnd(&op_zstream1); + deflateEnd(&op_zstream2); + + temp.free(); + over1.free(); + over2.free(); + over3.free(); + rest.free(); + delete[] dest; + + return 0; +} \ No newline at end of file diff --git a/viewer/src/utils/apng/APNGAssembler.h b/viewer/src/utils/apng/APNGAssembler.h new file mode 100644 index 0000000000..657a8cfe7d --- /dev/null +++ b/viewer/src/utils/apng/APNGAssembler.h @@ -0,0 +1,93 @@ +/* APNG Assembler 2.91 + * + * This program creates APNG animation from PNG/TGA image sequence. + * + * http://apngasm.sourceforge.net/ + * + * Copyright (c) 2009-2016 Max Stepin + * maxst at users.sourceforge.net + * + * zlib license + * ------------ + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + */ + +#pragma once + +#include <string> +#include "image.h" +#include "zlib.h" + +class APNGAssembler { + public: + typedef struct { + Image* image; + unsigned int size; + int x, y, w, h, valid, filters; + } OP; + APNGAssembler(); + ~APNGAssembler(); + + int exportFromPNGSequence(const std::string& outPath, const std::string& firstPNGPath, + int frameRate); + + private: + void write_chunk(FILE* f, const char* name, unsigned char* data, unsigned int length); + void write_IDATs(FILE* f, unsigned int frame, unsigned char* data, unsigned int length, + unsigned int idat_size); + void deflate_rect_op(Image* image, int x, int y, int w, int h, int bpp, int zbuf_size, int n); + void deflate_rect_fin(int deflate_method, int iter, unsigned char* zbuf, unsigned int* zsize, + int bpp, unsigned char* dest, int zbuf_size, int n); + void get_rect(unsigned int w, unsigned int h, Image* image1, Image* image2, Image* temp, + unsigned int bpp, int zbuf_size, unsigned int has_tcolor, unsigned int tcolor, + int n); + void process_rect(Image* image, int xbytes, int rowbytes, int y, int h, int bpp, + unsigned char* dest); + int load_image_sequence(char* szImage, unsigned int first, std::vector<Image>& img, int delay_num, + int delay_den, unsigned char* coltype); + int save_apng(const char* szOut, std::vector<Image>& img, unsigned int loops, unsigned int first, + int deflate_method, int iter); + + private: + unsigned char* op_zbuf1 = nullptr; + unsigned char* op_zbuf2 = nullptr; + unsigned char* row_buf = nullptr; + unsigned char* sub_row = nullptr; + unsigned char* up_row = nullptr; + unsigned char* avg_row = nullptr; + unsigned char* paeth_row = nullptr; + unsigned char coltype = 6; + unsigned char png_sign[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + unsigned char png_Software[28] = {83, 111, 102, 116, 119, 97, 114, 101, '\0', 65, 80, 78, 71, 32, + 65, 115, 115, 101, 109, 98, 108, 101, 114, 32, 50, 46, 57, 49}; + + unsigned int first = 0; + unsigned int loops = 0; + unsigned int next_seq_num = 0; + int iter = 15; + int delay_num = 1; + int delay_den = -1; + int keep_palette = 0; + int keep_coltype = 1; + int deflate_method = 0; + z_stream op_zstream1{}; + z_stream op_zstream2{}; + OP op[6]{}; + std::vector<Image> img; +}; diff --git a/viewer/src/utils/apng/image.cpp b/viewer/src/utils/apng/image.cpp new file mode 100644 index 0000000000..4395794bd6 --- /dev/null +++ b/viewer/src/utils/apng/image.cpp @@ -0,0 +1,1116 @@ +/* Image library + * + * Copyright (c) 2016 Max Stepin + * maxst at users.sourceforge.net + * + * zlib license + * ------------ + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + */ + +#include "image.h" +#include <stdio.h> +#include <stdlib.h> +#include "png.h" +#include "zlib.h" + +int load_png(char* szName, Image* image) { + FILE* f; + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (!png_ptr || !info_ptr) return 1; + + if ((f = fopen(szName, "rb")) == 0) { + printf("Error: can't open '%s'\n", szName); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 1; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + printf("Error: can't load '%s'\n", szName); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(f); + return 1; + } + + png_init_io(png_ptr, f); + png_read_info(png_ptr, info_ptr); + unsigned int depth = png_get_bit_depth(png_ptr, info_ptr); + if (depth < 8) { + if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) png_set_packing(png_ptr); + else + png_set_expand(png_ptr); + } else if (depth > 8) { + png_set_expand(png_ptr); + png_set_strip_16(png_ptr); + } + (void)png_set_interlace_handling(png_ptr); + png_read_update_info(png_ptr, info_ptr); + unsigned int w = png_get_image_width(png_ptr, info_ptr); + unsigned int h = png_get_image_height(png_ptr, info_ptr); + unsigned int bpp = png_get_channels(png_ptr, info_ptr); + unsigned int type = png_get_color_type(png_ptr, info_ptr); + image->init(w, h, bpp, type); + + png_colorp palette; + png_color_16p trans_color; + png_bytep trans_alpha; + + if (png_get_PLTE(png_ptr, info_ptr, &palette, &image->ps)) + memcpy(image->pl, palette, image->ps * 3); + + if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &image->ts, &trans_color) && image->ts > 0) { + if (type == PNG_COLOR_TYPE_GRAY) { + image->tr[0] = 0; + image->tr[1] = trans_color->gray & 0xFF; + image->ts = 2; + } else if (type == PNG_COLOR_TYPE_RGB) { + image->tr[0] = 0; + image->tr[1] = trans_color->red & 0xFF; + image->tr[2] = 0; + image->tr[3] = trans_color->green & 0xFF; + image->tr[4] = 0; + image->tr[5] = trans_color->blue & 0xFF; + image->ts = 6; + } else if (type == PNG_COLOR_TYPE_PALETTE) + memcpy(image->tr, trans_alpha, image->ts); + } + + png_read_image(png_ptr, image->rows); + png_read_end(png_ptr, info_ptr); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(f); + return 0; +} + +int load_tga(char* szName, Image* image) { + FILE* f; + unsigned int i, j, k, n; + unsigned int w, h; + unsigned int compressed, top_bottom; + unsigned char c; + unsigned char col[4]; + unsigned char header[18]; + + if ((f = fopen(szName, "rb")) == 0) { + printf("Error: can't open '%s'\n", szName); + return 1; + } + + if (fread(&header, 1, 18, f) != 18) goto fail; + + w = header[12] + header[13] * 256; + h = header[14] + header[15] * 256; + compressed = header[2] & 8; + top_bottom = header[17] & 0x20; + + if ((header[2] & 7) == 1 && header[16] == 8 && header[1] == 1 && header[7] == 24) + image->init(w, h, 1, 3); + else if ((header[2] & 7) == 3 && header[16] == 8) + image->init(w, h, 1, 0); + else if ((header[2] & 7) == 2 && header[16] == 24) + image->init(w, h, 3, 2); + else if ((header[2] & 7) == 2 && header[16] == 32) + image->init(w, h, 4, 6); + else + goto fail; + + if (header[0] != 0) fseek(f, header[0], SEEK_CUR); + + if (header[1] == 1) { + unsigned int start = header[3] + header[4] * 256; + unsigned int size = header[5] + header[6] * 256; + for (i = start; i < start + size && i < 256; i++) { + if (fread(&col, 1, 3, f) != 3) goto fail; + image->pl[i].r = col[2]; + image->pl[i].g = col[1]; + image->pl[i].b = col[0]; + } + image->ps = i; + if (start + size > 256) fseek(f, (start + size - 256) * 3, SEEK_CUR); + } + + for (j = 0; j < h; j++) { + unsigned char* row = image->rows[(top_bottom) ? j : h - 1 - j]; + if (compressed == 0) { + if (image->bpp >= 3) { + for (i = 0; i < w; i++) { + if (fread(&col, 1, image->bpp, f) != image->bpp) goto fail; + *row++ = col[2]; + *row++ = col[1]; + *row++ = col[0]; + if (image->bpp == 4) *row++ = col[3]; + } + } else { + if (fread(row, 1, w, f) != w) goto fail; + } + } else { + i = 0; + while (i < w) { + if (fread(&c, 1, 1, f) != 1) goto fail; + n = (c & 0x7F) + 1; + + if ((c & 0x80) != 0) { + if (image->bpp >= 3) { + if (fread(&col, 1, image->bpp, f) != image->bpp) goto fail; + for (k = 0; k < n; k++) { + *row++ = col[2]; + *row++ = col[1]; + *row++ = col[0]; + if (image->bpp == 4) *row++ = col[3]; + } + } else { + if (fread(&col, 1, 1, f) != 1) goto fail; + memset(row, col[0], n); + row += n; + } + } else { + if (image->bpp >= 3) { + for (k = 0; k < n; k++) { + if (fread(&col, 1, image->bpp, f) != image->bpp) goto fail; + *row++ = col[2]; + *row++ = col[1]; + *row++ = col[0]; + if (image->bpp == 4) *row++ = col[3]; + } + } else { + if (fread(row, 1, n, f) != n) goto fail; + row += n; + } + } + i += n; + } + } + } + fclose(f); + return 0; +fail: + printf("Error: can't load '%s'\n", szName); + fclose(f); + return 1; +} + +int load_image(char* szName, Image* image) { + FILE* f; + + if ((f = fopen(szName, "rb")) != 0) { + unsigned int sign; + size_t res = fread(&sign, sizeof(sign), 1, f); + fclose(f); + + if (res == 1) { + if (sign == 0x474E5089) return load_png(szName, image); + else + return load_tga(szName, image); + } + } + + printf("Error: can't load '%s'\n", szName); + return 1; +} + +unsigned char find_common_coltype(std::vector<Image>& img) { + unsigned char coltype = img[0].type; + + for (size_t i = 1; i < img.size(); i++) { + if (img[0].ps != img[i].ps || memcmp(img[0].pl, img[i].pl, img[0].ps * 3) != 0) coltype = 6; + else if (img[0].ts != img[i].ts || memcmp(img[0].tr, img[i].tr, img[0].ts) != 0) + coltype = 6; + else if (img[i].type != 3) { + if (coltype != 3) coltype |= img[i].type; + else + coltype = 6; + } else if (coltype != 3) + coltype = 6; + } + return coltype; +} + +void up0to6(Image* image) { + image->type = 6; + image->bpp = 4; + unsigned int x, y; + unsigned char g, a; + unsigned char *sp, *dp; + unsigned int rowbytes = image->w * image->bpp; + unsigned char* dst = new unsigned char[image->h * rowbytes]; + + for (y = 0; y < image->h; y++) { + sp = image->rows[y]; + image->rows[y] = (y == 0) ? dst : image->rows[y - 1] + rowbytes; + dp = image->rows[y]; + for (x = 0; x < image->w; x++) { + g = *sp++; + a = (image->ts > 0 && image->tr[1] == g) ? 0 : 255; + *dp++ = g; + *dp++ = g; + *dp++ = g; + *dp++ = a; + } + } + delete[] image->p; + image->p = dst; +} + +void up2to6(Image* image) { + image->type = 6; + image->bpp = 4; + unsigned int x, y; + unsigned char r, g, b, a; + unsigned char *sp, *dp; + unsigned int rowbytes = image->w * image->bpp; + unsigned char* dst = new unsigned char[image->h * rowbytes]; + + for (y = 0; y < image->h; y++) { + sp = image->rows[y]; + image->rows[y] = (y == 0) ? dst : image->rows[y - 1] + rowbytes; + dp = image->rows[y]; + for (x = 0; x < image->w; x++) { + r = *sp++; + g = *sp++; + b = *sp++; + a = (image->ts > 0 && image->tr[1] == r && image->tr[3] == g && image->tr[5] == b) ? 0 : 255; + *dp++ = r; + *dp++ = g; + *dp++ = b; + *dp++ = a; + } + } + delete[] image->p; + image->p = dst; +} + +void up3to6(Image* image) { + image->type = 6; + image->bpp = 4; + unsigned int x, y; + unsigned char *sp, *dp; + unsigned int rowbytes = image->w * image->bpp; + unsigned char* dst = new unsigned char[image->h * rowbytes]; + + for (y = 0; y < image->h; y++) { + sp = image->rows[y]; + image->rows[y] = (y == 0) ? dst : image->rows[y - 1] + rowbytes; + dp = image->rows[y]; + for (x = 0; x < image->w; x++) { + *dp++ = image->pl[*sp].r; + *dp++ = image->pl[*sp].g; + *dp++ = image->pl[*sp].b; + *dp++ = image->tr[*sp++]; + } + } + delete[] image->p; + image->p = dst; +} + +void up4to6(Image* image) { + image->type = 6; + image->bpp = 4; + unsigned int x, y; + unsigned char *sp, *dp; + unsigned int rowbytes = image->w * image->bpp; + unsigned char* dst = new unsigned char[image->h * rowbytes]; + + for (y = 0; y < image->h; y++) { + sp = image->rows[y]; + image->rows[y] = (y == 0) ? dst : image->rows[y - 1] + rowbytes; + dp = image->rows[y]; + for (x = 0; x < image->w; x++) { + *dp++ = *sp; + *dp++ = *sp; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + delete[] image->p; + image->p = dst; +} + +void up0to4(Image* image) { + image->type = 4; + image->bpp = 2; + unsigned int x, y; + unsigned char *sp, *dp; + unsigned int rowbytes = image->w * image->bpp; + unsigned char* dst = new unsigned char[image->h * rowbytes]; + + for (y = 0; y < image->h; y++) { + sp = image->rows[y]; + image->rows[y] = (y == 0) ? dst : image->rows[y - 1] + rowbytes; + dp = image->rows[y]; + for (x = 0; x < image->w; x++) { + *dp++ = *sp++; + *dp++ = 255; + } + } + delete[] image->p; + image->p = dst; +} + +void up0to2(Image* image) { + image->type = 2; + image->bpp = 3; + unsigned int x, y; + unsigned char *sp, *dp; + unsigned int rowbytes = image->w * image->bpp; + unsigned char* dst = new unsigned char[image->h * rowbytes]; + + for (y = 0; y < image->h; y++) { + sp = image->rows[y]; + image->rows[y] = (y == 0) ? dst : image->rows[y - 1] + rowbytes; + dp = image->rows[y]; + for (x = 0; x < image->w; x++) { + *dp++ = *sp; + *dp++ = *sp; + *dp++ = *sp++; + } + } + delete[] image->p; + image->p = dst; +} + +void optim_upconvert(Image* image, unsigned char coltype) { + if (image->type == 0 && coltype == 6) up0to6(image); + else if (image->type == 2 && coltype == 6) + up2to6(image); + else if (image->type == 3 && coltype == 6) + up3to6(image); + else if (image->type == 4 && coltype == 6) + up4to6(image); + else if (image->type == 0 && coltype == 4) + up0to4(image); + else if (image->type == 0 && coltype == 2) + up0to2(image); +} + +void optim_dirty_transp(Image* image) { + unsigned int x, y; + if (image->type == 6) { + for (y = 0; y < image->h; y++) { + unsigned char* sp = image->rows[y]; + for (x = 0; x < image->w; x++, sp += 4) { + if (sp[3] == 0) sp[0] = sp[1] = sp[2] = 0; + } + } + } else if (image->type == 4) { + for (y = 0; y < image->h; y++) { + unsigned char* sp = image->rows[y]; + for (x = 0; x < image->w; x++, sp += 2) { + if (sp[1] == 0) sp[0] = 0; + } + } + } +} + +int different(Image* image1, Image* image2) { + return memcmp(image1->p, image2->p, image1->w * image1->h * image1->bpp); +} + +void optim_duplicates(std::vector<Image>& img, unsigned int first) { + unsigned int i = first; + + while (++i < img.size()) { + if (different(&img[i - 1], &img[i])) continue; + + i--; + unsigned int num = img[i].delay_num; + unsigned int den = img[i].delay_den; + + img[i].free(); + img.erase(img.begin() + i); + + if (img[i].delay_den == den) img[i].delay_num += num; + else { + img[i].delay_num = num = num * img[i].delay_den + den * img[i].delay_num; + img[i].delay_den = den = den * img[i].delay_den; + while (num && den) { + if (num > den) num = num % den; + else + den = den % num; + } + num += den; + img[i].delay_num /= num; + img[i].delay_den /= num; + } + } +} + +typedef struct { + unsigned int num; + unsigned char r, g, b, a; +} COLORS; + +int cmp_colors(const void* arg1, const void* arg2) { + if (((COLORS*)arg1)->a != ((COLORS*)arg2)->a) + return (int)(((COLORS*)arg1)->a) - (int)(((COLORS*)arg2)->a); + + if (((COLORS*)arg1)->num != ((COLORS*)arg2)->num) + return (int)(((COLORS*)arg2)->num) - (int)(((COLORS*)arg1)->num); + + if (((COLORS*)arg1)->r != ((COLORS*)arg2)->r) + return (int)(((COLORS*)arg1)->r) - (int)(((COLORS*)arg2)->r); + + if (((COLORS*)arg1)->g != ((COLORS*)arg2)->g) + return (int)(((COLORS*)arg1)->g) - (int)(((COLORS*)arg2)->g); + + return (int)(((COLORS*)arg1)->b) - (int)(((COLORS*)arg2)->b); +} + +void down6(std::vector<Image>& img) { + unsigned int i, k, x, y; + unsigned char* sp; + unsigned char* dp; + unsigned char r, g, b, a; + int simple_transp = 1; + int full_transp = 0; + int grayscale = 1; + unsigned char cube[4096]; + unsigned char gray[256]; + COLORS col[256]; + unsigned int colors = 0; + Image* image = &img[0]; + + memset(&cube, 0, sizeof(cube)); + memset(&gray, 0, sizeof(gray)); + + for (i = 0; i < 256; i++) { + col[i].num = 0; + col[i].r = col[i].g = col[i].b = i; + col[i].a = image->tr[i] = 255; + } + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = img[i].rows[y]; + for (x = 0; x < image->w; x++) { + r = *sp++; + g = *sp++; + b = *sp++; + a = *sp++; + + if (a != 0) { + if (a != 255) simple_transp = 0; + else if (((r | g | b) & 15) == 0) + cube[(r << 4) + g + (b >> 4)] = 1; + + if (r != g || g != b) grayscale = 0; + else + gray[r] = 1; + } else + full_transp = 1; + + if (colors <= 256) { + int found = 0; + for (k = 0; k < colors; k++) + if (col[k].r == r && col[k].g == g && col[k].b == b && col[k].a == a) { + found = 1; + col[k].num++; + break; + } + if (found == 0) { + if (colors < 256) { + col[colors].num++; + col[colors].r = r; + col[colors].g = g; + col[colors].b = b; + col[colors].a = a; + } + colors++; + } + } + } + } + } + + if (colors <= 256) { + if (grayscale && simple_transp && colors > 128) /* 6 -> 0 */ + { + image->type = 0; + image->bpp = 1; + unsigned char t = 0; + + for (i = 0; i < 256; i++) + if (gray[i] == 0) { + image->tr[0] = 0; + image->tr[1] = t = i; + image->ts = 2; + break; + } + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = dp = img[i].rows[y]; + for (x = 0; x < image->w; x++, sp += 4) { + *dp++ = (sp[3] == 0) ? t : sp[0]; + } + } + } + } else /* 6 -> 3 */ + { + image->type = 3; + image->bpp = 1; + + if (full_transp == 0 && colors < 256) col[colors++].a = 0; + + qsort(&col[0], colors, sizeof(COLORS), cmp_colors); + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = dp = img[i].rows[y]; + for (x = 0; x < image->w; x++) { + r = *sp++; + g = *sp++; + b = *sp++; + a = *sp++; + for (k = 0; k < colors; k++) + if (col[k].r == r && col[k].g == g && col[k].b == b && col[k].a == a) break; + *dp++ = k; + } + } + } + + image->ps = colors; + for (i = 0; i < colors; i++) { + image->pl[i].r = col[i].r; + image->pl[i].g = col[i].g; + image->pl[i].b = col[i].b; + image->tr[i] = col[i].a; + if (image->tr[i] != 255) image->ts = i + 1; + } + } + } else if (grayscale) /* 6 -> 4 */ + { + image->type = 4; + image->bpp = 2; + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = dp = img[i].rows[y]; + for (x = 0; x < image->w; x++, sp += 4) { + *dp++ = sp[2]; + *dp++ = sp[3]; + } + } + } + } else if (simple_transp) /* 6 -> 2 */ + { + for (i = 0; i < 4096; i++) + if (cube[i] == 0) { + image->tr[0] = 0; + image->tr[1] = (i >> 4) & 0xF0; + image->tr[2] = 0; + image->tr[3] = i & 0xF0; + image->tr[4] = 0; + image->tr[5] = (i << 4) & 0xF0; + image->ts = 6; + break; + } + if (image->ts != 0) { + image->type = 2; + image->bpp = 3; + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = dp = img[i].rows[y]; + for (x = 0; x < image->w; x++) { + r = *sp++; + g = *sp++; + b = *sp++; + a = *sp++; + if (a == 0) { + *dp++ = image->tr[1]; + *dp++ = image->tr[3]; + *dp++ = image->tr[5]; + } else { + *dp++ = r; + *dp++ = g; + *dp++ = b; + } + } + } + } + } + } +} + +void down2(std::vector<Image>& img) { + unsigned int i, k, x, y; + unsigned char* sp; + unsigned char* dp; + unsigned char r, g, b, a; + int full_transp = 0; + int grayscale = 1; + unsigned char gray[256]; + COLORS col[256]; + unsigned int colors = 0; + Image* image = &img[0]; + + memset(&gray, 0, sizeof(gray)); + + for (i = 0; i < 256; i++) { + col[i].num = 0; + col[i].r = col[i].g = col[i].b = i; + col[i].a = 255; + } + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = img[i].rows[y]; + for (x = 0; x < image->w; x++) { + r = *sp++; + g = *sp++; + b = *sp++; + a = (image->ts > 0 && image->tr[1] == r && image->tr[3] == g && image->tr[5] == b) ? 0 + : 255; + + if (a != 0) { + if (r != g || g != b) grayscale = 0; + else + gray[r] = 1; + } else + full_transp = 1; + + if (colors <= 256) { + int found = 0; + for (k = 0; k < colors; k++) + if (col[k].r == r && col[k].g == g && col[k].b == b && col[k].a == a) { + found = 1; + col[k].num++; + break; + } + if (found == 0) { + if (colors < 256) { + col[colors].num++; + col[colors].r = r; + col[colors].g = g; + col[colors].b = b; + col[colors].a = a; + } + colors++; + } + } + } + } + } + + if (colors <= 256) { + if (grayscale && colors > 128) /* 2 -> 0 */ + { + image->type = 0; + image->bpp = 1; + unsigned char t = 0; + int ts = 0; + + for (i = 0; i < 256; i++) { + if (gray[i] == 0) { + t = i; + ts = 2; + break; + } + } + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = dp = img[i].rows[y]; + for (x = 0; x < image->w; x++, sp += 3) { + *dp++ = (image->ts > 0 && image->tr[1] == sp[0] && image->tr[3] == sp[1] && + image->tr[5] == sp[2]) + ? t + : sp[0]; + } + } + } + if (ts > 0) { + image->tr[0] = 0; + image->tr[1] = t; + image->ts = ts; + } + } else /* 2 -> 3 */ + { + image->type = 3; + image->bpp = 1; + + if (full_transp == 0 && colors < 256) col[colors++].a = 0; + + qsort(&col[0], colors, sizeof(COLORS), cmp_colors); + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = dp = img[i].rows[y]; + for (x = 0; x < image->w; x++) { + r = *sp++; + g = *sp++; + b = *sp++; + a = (image->ts > 0 && image->tr[1] == r && image->tr[3] == g && image->tr[5] == b) + ? 0 + : 255; + for (k = 0; k < colors; k++) + if (col[k].r == r && col[k].g == g && col[k].b == b && col[k].a == a) break; + *dp++ = k; + } + } + } + + image->ps = colors; + for (i = 0; i < colors; i++) { + image->pl[i].r = col[i].r; + image->pl[i].g = col[i].g; + image->pl[i].b = col[i].b; + image->tr[i] = col[i].a; + if (image->tr[i] != 255) image->ts = i + 1; + } + } + } +} + +void down4(std::vector<Image>& img) { + unsigned int i, k, x, y; + unsigned char* sp; + unsigned char* dp; + unsigned char g, a; + int simple_transp = 1; + int full_transp = 0; + unsigned char gray[256]; + COLORS col[256]; + unsigned int colors = 0; + Image* image = &img[0]; + + memset(&gray, 0, sizeof(gray)); + + for (i = 0; i < 256; i++) { + col[i].num = 0; + col[i].r = col[i].g = col[i].b = i; + col[i].a = image->tr[i] = 255; + } + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = img[i].rows[y]; + for (x = 0; x < image->w; x++) { + g = *sp++; + a = *sp++; + + if (a != 0) { + if (a != 255) simple_transp = 0; + else + gray[g] = 1; + } else + full_transp = 1; + + if (colors <= 256) { + int found = 0; + for (k = 0; k < colors; k++) + if (col[k].g == g && col[k].a == a) { + found = 1; + col[k].num++; + break; + } + if (found == 0) { + if (colors < 256) { + col[colors].num++; + col[colors].r = g; + col[colors].g = g; + col[colors].b = g; + col[colors].a = a; + } + colors++; + } + } + } + } + } + + if (simple_transp && colors <= 256) /* 4 -> 0 */ + { + image->type = 0; + image->bpp = 1; + unsigned char t = 0; + + for (i = 0; i < 256; i++) + if (gray[i] == 0) { + image->tr[0] = 0; + image->tr[1] = t = i; + image->ts = 2; + break; + } + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = dp = img[i].rows[y]; + for (x = 0; x < image->w; x++, sp += 2) { + *dp++ = (sp[1] == 0) ? t : sp[0]; + } + } + } + } else if (colors <= 256) /* 4 -> 3 */ + { + image->type = 3; + image->bpp = 1; + + if (full_transp == 0 && colors < 256) col[colors++].a = 0; + + qsort(&col[0], colors, sizeof(COLORS), cmp_colors); + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = dp = img[i].rows[y]; + for (x = 0; x < image->w; x++) { + g = *sp++; + a = *sp++; + for (k = 0; k < colors; k++) + if (col[k].g == g && col[k].a == a) break; + *dp++ = k; + } + } + } + + image->ps = colors; + for (i = 0; i < colors; i++) { + image->pl[i].r = col[i].r; + image->pl[i].g = col[i].g; + image->pl[i].b = col[i].b; + image->tr[i] = col[i].a; + if (image->tr[i] != 255) image->ts = i + 1; + } + } +} + +void down3(std::vector<Image>& img) { + unsigned int i, x, y; + unsigned char* sp; + unsigned char* dp; + int c; + int simple_transp = 1; + int grayscale = 1; + unsigned char gray[256]; + COLORS col[256]; + Image* image = &img[0]; + + memset(&gray, 0, sizeof(gray)); + + for (c = 0; c < 256; c++) { + col[c].num = 0; + if (c < image->ps) { + col[c].r = image->pl[c].r; + col[c].g = image->pl[c].g; + col[c].b = image->pl[c].b; + col[c].a = image->tr[c]; + } else { + col[c].r = col[c].g = col[c].b = c; + col[c].a = 255; + } + } + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = img[i].rows[y]; + for (x = 0; x < image->w; x++) col[*sp++].num++; + } + } + + for (i = 0; i < 256; i++) + if (col[i].num != 0) { + if (col[i].a != 0) { + if (col[i].a != 255) simple_transp = 0; + else if (col[i].r != col[i].g || col[i].g != col[i].b) + grayscale = 0; + else + gray[col[i].g] = 1; + } + } + + if (grayscale && simple_transp) /* 3 -> 0 */ + { + image->type = 0; + image->bpp = 1; + unsigned char t = 0; + int ts = 0; + + for (i = 0; i < 256; i++) { + if (gray[i] == 0) { + t = i; + ts = 2; + break; + } + } + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + dp = img[i].rows[y]; + for (x = 0; x < image->w; x++, dp++) { + *dp = (col[*dp].a == 0) ? t : image->pl[*dp].g; + } + } + } + image->ps = 0; + image->ts = 0; + if (ts > 0) { + image->tr[0] = 0; + image->tr[1] = t; + image->ts = ts; + } + } +} + +void optim_downconvert(std::vector<Image>& img) { + if (img[0].type == 6) down6(img); + else if (img[0].type == 2) + down2(img); + else if (img[0].type == 4) + down4(img); + else if (img[0].type == 3) + down3(img); +} + +void optim_palette(std::vector<Image>& img) { + unsigned int i, x, y; + unsigned char* sp; + unsigned char r, g, b, a; + int c; + int full_transp = 0; + COLORS col[256]; + Image* image = &img[0]; + + for (c = 0; c < 256; c++) { + col[c].num = 0; + if (c < image->ps) { + col[c].r = image->pl[c].r; + col[c].g = image->pl[c].g; + col[c].b = image->pl[c].b; + col[c].a = image->tr[c]; + } else { + col[c].r = col[c].g = col[c].b = c; + col[c].a = 255; + } + } + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = img[i].rows[y]; + for (x = 0; x < image->w; x++) col[*sp++].num++; + } + } + + for (i = 0; i < 256; i++) { + if (col[i].num != 0 && col[i].a == 0) { + full_transp = 1; + break; + } + } + + for (i = 0; i < 256; i++) { + if (col[i].num == 0) { + col[i].a = 255; + if (full_transp == 0) { + col[i].a = 0; + full_transp = 1; + } + } + } + + qsort(&col[0], 256, sizeof(COLORS), cmp_colors); + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = img[i].rows[y]; + for (x = 0; x < image->w; x++) { + r = image->pl[*sp].r; + g = image->pl[*sp].g; + b = image->pl[*sp].b; + a = image->tr[*sp]; + for (c = 0; c < image->ps; c++) + if (col[c].r == r && col[c].g == g && col[c].b == b && col[c].a == a) break; + *sp++ = c; + } + } + } + + for (i = 0; i < 256; i++) { + image->pl[i].r = col[i].r; + image->pl[i].g = col[i].g; + image->pl[i].b = col[i].b; + image->tr[i] = col[i].a; + if (col[i].num != 0) image->ps = i + 1; + if (image->tr[i] != 255) image->ts = i + 1; + } +} + +void add_transp2(std::vector<Image>& img) { + unsigned int i, x, y; + unsigned char* sp; + unsigned char r, g, b; + unsigned char cube[4096]; + Image* image = &img[0]; + + memset(&cube, 0, sizeof(cube)); + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = img[i].rows[y]; + for (x = 0; x < image->w; x++) { + r = *sp++; + g = *sp++; + b = *sp++; + if (((r | g | b) & 15) == 0) cube[(r << 4) + g + (b >> 4)] = 1; + } + } + } + + for (i = 0; i < 4096; i++) + if (cube[i] == 0) { + image->tr[0] = 0; + image->tr[1] = (i >> 4) & 0xF0; + image->tr[2] = 0; + image->tr[3] = i & 0xF0; + image->tr[4] = 0; + image->tr[5] = (i << 4) & 0xF0; + image->ts = 6; + break; + } +} + +void add_transp0(std::vector<Image>& img) { + unsigned int i, x, y; + unsigned char* sp; + unsigned char gray[256]; + Image* image = &img[0]; + + memset(&gray, 0, sizeof(gray)); + + for (i = 0; i < img.size(); i++) { + for (y = 0; y < image->h; y++) { + sp = img[i].rows[y]; + for (x = 0; x < image->w; x++) gray[*sp++] = 1; + } + } + + for (i = 0; i < 256; i++) + if (gray[i] == 0) { + image->tr[0] = 0; + image->tr[1] = i; + image->ts = 2; + break; + } +} + +void optim_add_transp(std::vector<Image>& img) { + if (img[0].ts == 0) { + if (img[0].type == 2) add_transp2(img); + else if (img[0].type == 0) + add_transp0(img); + } +} diff --git a/viewer/src/utils/apng/image.h b/viewer/src/utils/apng/image.h new file mode 100644 index 0000000000..6f107ea2f2 --- /dev/null +++ b/viewer/src/utils/apng/image.h @@ -0,0 +1,86 @@ +/* Image library + * + * Copyright (c) 2016 Max Stepin + * maxst at users.sourceforge.net + * + * zlib license + * ------------ + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + */ + +#ifndef IMAGE_H +#define IMAGE_H +#include <string.h> +#include <vector> + +struct rgb { + unsigned char r, g, b; +}; + +struct Image { + typedef unsigned char* ROW; + unsigned int w, h, bpp, type; + int ps, ts; + rgb pl[256]; + unsigned char tr[256]; + unsigned int delay_num, delay_den; + unsigned char* p; + ROW* rows; + Image() : w(0), h(0), bpp(0), type(0), ps(0), ts(0), delay_num(1), delay_den(10), p(0), rows(0) { + memset(pl, 255, sizeof(pl)); + memset(tr, 255, sizeof(tr)); + } + ~Image() { + } + void init(unsigned int w1, unsigned int h1, unsigned int bpp1, unsigned int type1) { + w = w1; + h = h1; + bpp = bpp1; + type = type1; + int rowbytes = w * bpp; + delete[] rows; + delete[] p; + rows = new ROW[h]; + rows[0] = p = new unsigned char[h * rowbytes]; + for (unsigned int j = 1; j < h; j++) rows[j] = rows[j - 1] + rowbytes; + } + void init(unsigned int w, unsigned int h, Image* image) { + init(w, h, image->bpp, image->type); + if ((ps = image->ps) != 0) memcpy(&pl[0], &image->pl[0], ps * 3); + if ((ts = image->ts) != 0) memcpy(&tr[0], &image->tr[0], ts); + } + void init(Image* image) { + init(image->w, image->h, image); + } + void free() { + delete[] rows; + delete[] p; + } +}; + +int load_image(char* szName, Image* image); +unsigned char find_common_coltype(std::vector<Image>& img); +void optim_upconvert(Image* image, unsigned char coltype); +void optim_duplicates(std::vector<Image>& img, unsigned int first); +void optim_dirty_transp(Image* image); +void optim_downconvert(std::vector<Image>& img); +void optim_palette(std::vector<Image>& img); +void optim_add_transp(std::vector<Image>& img); + +#endif /* IMAGE_H */ From 9000be2848d5e91d8c74019117da910e10d1a679 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 1 Apr 2025 15:57:16 +0800 Subject: [PATCH 27/36] Fixed viewer export function issues on Windows --- viewer/qml/Main.qml | 74 ++++++++++++-------- viewer/qml/Menu.qml | 1 + viewer/qml/utils/Utils.qml | 9 --- viewer/src/platform/win/PAGUtilsImpl.cpp | 42 +++++++---- viewer/src/task/PAGTask.cpp | 4 +- viewer/src/task/PAGTask.h | 1 + viewer/src/task/export/PAGExportAPNGTask.cpp | 1 + 7 files changed, 77 insertions(+), 55 deletions(-) diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 4c4d00f224..90bcb4f719 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -186,41 +186,55 @@ PAGWindow { width: 300 height: 64 + minimumWidth: width + maximumWidth: width + minimumHeight: height + maximumHeight: height hasMenu: false canResize: false titleBarHeight: windowTitleBarHeight visible: false - ProgressBar { - id: progressBar - width: parent.width - 24 - height: 30 - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - value: 0 - - contentItem: Item { - Rectangle { - width: parent.width - height: 15 - radius: 5 - color: "#DDDDDD" - anchors.verticalCenter: parent.verticalCenter - } + PAGRectangle { + id: rectangle + + color: "#2D2D37" + anchors.fill: parent + leftTopRadius: false + rightTopRadius: false + radius: 5 + + ProgressBar { + id: progressBar + width: parent.width - 24 + height: 30 + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + value: 0 + + contentItem: Item { + Rectangle { + width: parent.width + height: 15 + radius: 5 + color: "#DDDDDD" + anchors.verticalCenter: parent.verticalCenter + } - Rectangle { - width: progressBar.visualPosition * parent.width - height: 15 - radius: 5 - color: "#448EF9" - anchors.verticalCenter: parent.verticalCenter - } + Rectangle { + width: progressBar.visualPosition * parent.width + height: 15 + radius: 5 + color: "#448EF9" + anchors.verticalCenter: parent.verticalCenter + } - Text { - anchors.centerIn: parent - text: Math.round(progressBar.value * 100) + "%" - color: progressBar.value > 0.5 ? "white" : "black" // 根据进度自动调整文字颜色 - font.pixelSize: 12 + Text { + anchors.centerIn: parent + text: Math.round(progressBar.value * 100) + "%" + color: progressBar.value > 0.5 ? "white" : "black" // 根据进度自动调整文字颜色 + font.pixelSize: 12 + } } } } @@ -235,7 +249,6 @@ PAGWindow { Connections { id: taskConnections onProgressChanged: function (progress) { - console.log("progress: " + progress); progressWindow.progressBar.value = progress; } @@ -404,8 +417,7 @@ PAGWindow { openFolderDialog.accepted.disconnect(openFolderDialog.currentAcceptHandler); } openFolderDialog.title = qsTr("Select save path"); - openFolderDialog.currentFolder = Utils.getNativeFilePath(Utils.getFileDir(mainForm.pagView.filePath)); - console.log("openFolderDialog.folder: " + openFolderDialog.folder); + openFolderDialog.currentFolder = Utils.getFileDir(mainForm.pagView.filePath); openFolderDialog.currentAcceptHandler = function () { let filePath = openFolderDialog.folder; let task = taskFactory.createTask(PAGTaskFactory.PAGTaskType_ExportPNG, filePath, {}); diff --git a/viewer/qml/Menu.qml b/viewer/qml/Menu.qml index bf2d5252f5..a0589d63cf 100644 --- a/viewer/qml/Menu.qml +++ b/viewer/qml/Menu.qml @@ -49,6 +49,7 @@ Item { } } PAGMenu { + menuWidth: windowsMenuBar.menuWidth title: qsTr("Export") Action { text: qsTr("Export as PNG Sequence Frames") diff --git a/viewer/qml/utils/Utils.qml b/viewer/qml/utils/Utils.qml index bbaa755fdc..2456705cd9 100644 --- a/viewer/qml/utils/Utils.qml +++ b/viewer/qml/utils/Utils.qml @@ -32,13 +32,4 @@ QtObject { return str.substring(0, lastIndex) + newPara + str.substring(lastIndex + oldPara.length); } - - function getNativeFilePath(str) { - str = str.replace(/\\/ig, '/'); - let schema = "file://"; - if (Qt.platform.os === "windows") { - schema += "/"; - } - return schema + str; - } } diff --git a/viewer/src/platform/win/PAGUtilsImpl.cpp b/viewer/src/platform/win/PAGUtilsImpl.cpp index afbebb475e..580b14c267 100644 --- a/viewer/src/platform/win/PAGUtilsImpl.cpp +++ b/viewer/src/platform/win/PAGUtilsImpl.cpp @@ -1,6 +1,7 @@ #include "PAGUtilsImpl.h" #include <shlobj_core.h> #include <windows.h> +#include <QDir> namespace pag::Utils { @@ -10,27 +11,40 @@ auto openFileInFinder(QFileInfo& fileInfo) -> void { return; } - bool success = false; - PIDLIST_ABSOLUTE pidlFolder = ILCreateFromPathW(fileInfo.absolutePath().toStdWString().c_str()); - PIDLIST_ABSOLUTE pidlFile = ILCreateFromPathW(fileInfo.absoluteFilePath().toStdWString().c_str()); - if (pidlFolder && pidlFile) { - PCUITEMID_CHILD pidlChild = ILFindChild(pidlFolder, pidlFile); - if (pidlChild) { - PCUITEMID_CHILD_ARRAY pidls = const_cast<PCUITEMID_CHILD_ARRAY>(&pidlChild); - hr = SHOpenFolderAndSelectItems(pidlFolder, 1, pidls, 0); - success = SUCCEEDED(hr); + QString folderPath = QDir::toNativeSeparators(fileInfo.absolutePath()); + QString filePath = QDir::toNativeSeparators(fileInfo.absoluteFilePath()); + + std::wstring folderWPath = folderPath.toStdWString(); + std::wstring fileWPath = filePath.toStdWString(); + + PIDLIST_ABSOLUTE pidlFolder(ILCreateFromPathW(folderWPath.c_str())); + PIDLIST_ABSOLUTE pidlFile(ILCreateFromPathW(fileWPath.c_str())); + + if (!pidlFolder || !pidlFile) { + if (pidlFolder) { + ILFree(pidlFolder); } + if (pidlFile) { + ILFree(pidlFile); + } + CoUninitialize(); + return; } - if (pidlFolder) { + PCUITEMID_CHILD pidlChild = ILFindChild(pidlFolder, pidlFile); + if (!pidlChild) { ILFree(pidlFolder); - } - if (pidlFile) { ILFree(pidlFile); + CoUninitialize(); + return; } - CoUninitialize(); - return; + PCUITEMID_CHILD_ARRAY pidls = const_cast<PCUITEMID_CHILD_ARRAY>(&pidlChild); + SHOpenFolderAndSelectItems(pidlFolder, 1, pidls, 0); + + ILFree(pidlFolder); + ILFree(pidlFile); + CoUninitialize(); } } // namespace pag::Utils \ No newline at end of file diff --git a/viewer/src/task/PAGTask.cpp b/viewer/src/task/PAGTask.cpp index 4269e5876d..b7be5d51aa 100644 --- a/viewer/src/task/PAGTask.cpp +++ b/viewer/src/task/PAGTask.cpp @@ -135,7 +135,9 @@ auto PAGFileTask::startInternal() -> void { currentFrame = 0; releaseResource(); Q_EMIT taskFinished(result, filePath); - Utils::openInFinder(filePath, true); + if (openAfterExport) { + Utils::openInFinder(filePath, true); + } workerThread.exit(0); } diff --git a/viewer/src/task/PAGTask.h b/viewer/src/task/PAGTask.h index 60461682bc..658db631ae 100644 --- a/viewer/src/task/PAGTask.h +++ b/viewer/src/task/PAGTask.h @@ -80,6 +80,7 @@ class PAGFileTask : public PAGTask { auto initOpenGLEnvironment() -> void; protected: + bool openAfterExport = true; Frame currentFrame = 0; QString filePath; PAGPlayer* pagPlayer = nullptr; diff --git a/viewer/src/task/export/PAGExportAPNGTask.cpp b/viewer/src/task/export/PAGExportAPNGTask.cpp index bcbc16431d..551ed6dbf2 100644 --- a/viewer/src/task/export/PAGExportAPNGTask.cpp +++ b/viewer/src/task/export/PAGExportAPNGTask.cpp @@ -26,6 +26,7 @@ namespace pag { PAGExportAPNGTask::PAGExportAPNGTask(std::shared_ptr<PAGFile>& pagFile, const QString& apngFilePath, const QString& pngFilePath) : PAGExportPNGTask(pagFile, pngFilePath), apngFilePath(apngFilePath) { + openAfterExport = false; } auto PAGExportAPNGTask::onFrameFlush(double progress) -> void { From 3afabc557fc94aac3d4b87db3f8ede9286e06afe Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 1 Apr 2025 16:53:22 +0800 Subject: [PATCH 28/36] Release context when task is completed --- viewer/src/task/PAGTask.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/viewer/src/task/PAGTask.cpp b/viewer/src/task/PAGTask.cpp index b7be5d51aa..4089dd8f6c 100644 --- a/viewer/src/task/PAGTask.cpp +++ b/viewer/src/task/PAGTask.cpp @@ -104,9 +104,10 @@ auto PAGFileTask::releaseResource() -> void { frameBuffer->release(); delete frameBuffer; frameBuffer = nullptr; + context->doneCurrent(); } - context->doneCurrent(); - delete frameBuffer; + delete context; + context = nullptr; } if (offscreenSurface != nullptr) { delete offscreenSurface; From 363e54849e75fa96259468aa20e8d501f7b717ab Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 1 Apr 2025 16:57:14 +0800 Subject: [PATCH 29/36] Add license for viewer source files --- viewer/src/platform/mac/PAGUtilsImpl.h | 18 ++++++++++++++++++ viewer/src/platform/mac/PAGUtilsImpl.mm | 18 ++++++++++++++++++ viewer/src/platform/mac/PAGWindowHelper.h | 18 ++++++++++++++++++ viewer/src/platform/mac/PAGWindowHelper.mm | 18 ++++++++++++++++++ viewer/src/platform/win/PAGUtilsImpl.cpp | 18 ++++++++++++++++++ viewer/src/platform/win/PAGUtilsImpl.h | 18 ++++++++++++++++++ viewer/src/platform/win/PAGWindowHelper.cpp | 18 ++++++++++++++++++ viewer/src/platform/win/PAGWindowHelper.h | 18 ++++++++++++++++++ 8 files changed, 144 insertions(+) diff --git a/viewer/src/platform/mac/PAGUtilsImpl.h b/viewer/src/platform/mac/PAGUtilsImpl.h index 4e9eac2ce6..73ea82fce2 100644 --- a/viewer/src/platform/mac/PAGUtilsImpl.h +++ b/viewer/src/platform/mac/PAGUtilsImpl.h @@ -1,3 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + #pragma once #include <QFileInfo> diff --git a/viewer/src/platform/mac/PAGUtilsImpl.mm b/viewer/src/platform/mac/PAGUtilsImpl.mm index d5eb5821a8..6fbeea2dd8 100644 --- a/viewer/src/platform/mac/PAGUtilsImpl.mm +++ b/viewer/src/platform/mac/PAGUtilsImpl.mm @@ -1,3 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + #include "PAGUtilsImpl.h" #import <AppKit/AppKit.h> diff --git a/viewer/src/platform/mac/PAGWindowHelper.h b/viewer/src/platform/mac/PAGWindowHelper.h index 856445451a..04766d6ac0 100644 --- a/viewer/src/platform/mac/PAGWindowHelper.h +++ b/viewer/src/platform/mac/PAGWindowHelper.h @@ -1,3 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + #pragma once #include <QQuickWindow> diff --git a/viewer/src/platform/mac/PAGWindowHelper.mm b/viewer/src/platform/mac/PAGWindowHelper.mm index fdc68915fb..ddc761ec0d 100644 --- a/viewer/src/platform/mac/PAGWindowHelper.mm +++ b/viewer/src/platform/mac/PAGWindowHelper.mm @@ -1,3 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + #include "PAGWindowHelper.h" #include <Cocoa/Cocoa.h> diff --git a/viewer/src/platform/win/PAGUtilsImpl.cpp b/viewer/src/platform/win/PAGUtilsImpl.cpp index 580b14c267..4e17ecab42 100644 --- a/viewer/src/platform/win/PAGUtilsImpl.cpp +++ b/viewer/src/platform/win/PAGUtilsImpl.cpp @@ -1,3 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + #include "PAGUtilsImpl.h" #include <shlobj_core.h> #include <windows.h> diff --git a/viewer/src/platform/win/PAGUtilsImpl.h b/viewer/src/platform/win/PAGUtilsImpl.h index 4e9eac2ce6..73ea82fce2 100644 --- a/viewer/src/platform/win/PAGUtilsImpl.h +++ b/viewer/src/platform/win/PAGUtilsImpl.h @@ -1,3 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + #pragma once #include <QFileInfo> diff --git a/viewer/src/platform/win/PAGWindowHelper.cpp b/viewer/src/platform/win/PAGWindowHelper.cpp index d2443f123e..245227498e 100644 --- a/viewer/src/platform/win/PAGWindowHelper.cpp +++ b/viewer/src/platform/win/PAGWindowHelper.cpp @@ -1,3 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + #include "PAGWindowHelper.h" namespace pag { diff --git a/viewer/src/platform/win/PAGWindowHelper.h b/viewer/src/platform/win/PAGWindowHelper.h index 856445451a..04766d6ac0 100644 --- a/viewer/src/platform/win/PAGWindowHelper.h +++ b/viewer/src/platform/win/PAGWindowHelper.h @@ -1,3 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + #pragma once #include <QQuickWindow> From b913045d7e2a585bdcb59481037b96bdca2cda35 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 1 Apr 2025 17:15:11 +0800 Subject: [PATCH 30/36] Remove redundant comments in viewer --- viewer/qml/Main.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index 90bcb4f719..f8fb986dd2 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -232,7 +232,7 @@ PAGWindow { Text { anchors.centerIn: parent text: Math.round(progressBar.value * 100) + "%" - color: progressBar.value > 0.5 ? "white" : "black" // 根据进度自动调整文字颜色 + color: progressBar.value > 0.5 ? "white" : "black" font.pixelSize: 12 } } @@ -253,7 +253,6 @@ PAGWindow { } onVisibleChanged: function (visible) { - console.log("visible: " + visible); progressWindow.visible = visible; } From 41feb4c59fa347ae8cc520f2cca3b273cde14b9e Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Thu, 3 Apr 2025 15:37:29 +0800 Subject: [PATCH 31/36] Use smart pointers instead of raw pointers in viewer --- viewer/qml/Main.qml | 2 +- viewer/src/rendering/PAGView.cpp | 20 +++---- viewer/src/rendering/PAGView.h | 4 +- viewer/src/rendering/PAGWindow.cpp | 10 ++-- viewer/src/rendering/PAGWindow.h | 7 +-- viewer/src/task/PAGTask.cpp | 65 +++++++++------------ viewer/src/task/PAGTask.h | 16 ++--- viewer/src/task/PAGTaskFactory.h | 4 +- viewer/src/task/export/PAGExportPNGTask.cpp | 9 +-- viewer/src/task/export/PAGExportPNGTask.h | 2 +- viewer/src/utils/PAGFileUtils.cpp | 4 +- viewer/src/utils/PAGTimeUtils.cpp | 31 ---------- viewer/src/utils/PAGTimeUtils.h | 28 --------- 13 files changed, 67 insertions(+), 135 deletions(-) delete mode 100644 viewer/src/utils/PAGTimeUtils.cpp delete mode 100644 viewer/src/utils/PAGTimeUtils.h diff --git a/viewer/qml/Main.qml b/viewer/qml/Main.qml index f8fb986dd2..8771eebafb 100644 --- a/viewer/qml/Main.qml +++ b/viewer/qml/Main.qml @@ -439,7 +439,7 @@ PAGWindow { } openFileDialog.fileMode = FileDialog.SaveFile; openFileDialog.title = qsTr("Select save path"); - openFileDialog.nameFilters = ["PNG files(*.png)"]; + openFileDialog.nameFilters = ["APNG files(*.png)"]; openFileDialog.defaultSuffix = "png"; openFileDialog.currentFolder = Utils.getFileDir(mainForm.pagView.filePath); openFileDialog.currentAcceptHandler = function () { diff --git a/viewer/src/rendering/PAGView.cpp b/viewer/src/rendering/PAGView.cpp index 900759b1b6..8e73749cc3 100644 --- a/viewer/src/rendering/PAGView.cpp +++ b/viewer/src/rendering/PAGView.cpp @@ -27,19 +27,19 @@ namespace pag { PAGView::PAGView(QQuickItem* parent) : QQuickItem(parent) { setFlag(ItemHasContents, true); drawable = GPUDrawable::MakeFrom(this); - pagPlayer = new PAGPlayer(); + pagPlayer = std::make_unique<PAGPlayer>(); auto pagSurface = PAGSurface::MakeFrom(drawable); pagPlayer->setSurface(pagSurface); - renderThread = new PAGRenderThread(this); - renderThread->moveToThread(renderThread); - drawable->moveToThread(renderThread); + renderThread = std::make_unique<PAGRenderThread>(this); + renderThread->moveToThread(renderThread.get()); + drawable->moveToThread(renderThread.get()); } PAGView::~PAGView() { - QMetaObject::invokeMethod(renderThread, "shutDown", Qt::QueuedConnection); + QMetaObject::invokeMethod(renderThread.get(), "shutDown", Qt::QueuedConnection); renderThread->wait(); - delete renderThread; - delete pagPlayer; + renderThread.reset(); + pagPlayer.reset(); } auto PAGView::getPAGWidth() const -> int { @@ -149,7 +149,7 @@ auto PAGView::setIsPlaying(bool isPlaying) -> void { Q_EMIT isPlayingChanged(isPlaying); if (isPlaying) { lastPlayTime = tgfx::Clock::Now(); - QMetaObject::invokeMethod(renderThread, "flush", Qt::QueuedConnection); + QMetaObject::invokeMethod(renderThread.get(), "flush", Qt::QueuedConnection); } } @@ -167,7 +167,7 @@ auto PAGView::setProgress(double progress) -> void { pagPlayer->setProgress(progress); this->progress = progress; Q_EMIT progressChanged(progress); - QMetaObject::invokeMethod(renderThread, "flush", Qt::QueuedConnection); + QMetaObject::invokeMethod(renderThread.get(), "flush", Qt::QueuedConnection); } auto PAGView::setFile(const QString& filePath) -> bool { @@ -277,7 +277,7 @@ QSGNode* PAGView::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) { } setProgress(progress); } - QMetaObject::invokeMethod(renderThread, "flush", Qt::QueuedConnection); + QMetaObject::invokeMethod(renderThread.get(), "flush", Qt::QueuedConnection); } return node; } diff --git a/viewer/src/rendering/PAGView.h b/viewer/src/rendering/PAGView.h index 1d5f9b77df..a3a6817b8d 100644 --- a/viewer/src/rendering/PAGView.h +++ b/viewer/src/rendering/PAGView.h @@ -82,8 +82,8 @@ class PAGView : public QQuickItem { double progress = 0.0; double progressPerFrame = 0.0; QString filePath = ""; - PAGPlayer* pagPlayer = nullptr; - PAGRenderThread* renderThread = nullptr; + std::unique_ptr<PAGPlayer> pagPlayer = nullptr; + std::unique_ptr<PAGRenderThread> renderThread = nullptr; std::shared_ptr<PAGFile> pagFile = nullptr; std::shared_ptr<GPUDrawable> drawable = nullptr; diff --git a/viewer/src/rendering/PAGWindow.cpp b/viewer/src/rendering/PAGWindow.cpp index b71cd51009..cb4d8e24a9 100644 --- a/viewer/src/rendering/PAGWindow.cpp +++ b/viewer/src/rendering/PAGWindow.cpp @@ -30,8 +30,8 @@ PAGWindow::PAGWindow(QObject* parent) : QObject(parent) { } PAGWindow::~PAGWindow() { - delete engine; - delete windowHelper; + engine.reset(); + windowHelper.reset(); } auto PAGWindow::openFile(QString path) -> void { @@ -49,11 +49,11 @@ auto PAGWindow::onPAGViewerDestroyed() -> void { } auto PAGWindow::open() -> void { - engine = new QQmlApplicationEngine; - windowHelper = new PAGWindowHelper(); + engine = std::make_unique<QQmlApplicationEngine>(); + windowHelper = std::make_unique<PAGWindowHelper>(); auto context = engine->rootContext(); - context->setContextProperty("windowHelper", windowHelper); + context->setContextProperty("windowHelper", windowHelper.get()); engine->load(QUrl(QStringLiteral("qrc:/qml/Main.qml"))); diff --git a/viewer/src/rendering/PAGWindow.h b/viewer/src/rendering/PAGWindow.h index e1659a69cd..ec28d0d505 100644 --- a/viewer/src/rendering/PAGWindow.h +++ b/viewer/src/rendering/PAGWindow.h @@ -41,12 +41,11 @@ class PAGWindow : public QObject { static QList<PAGWindow*> AllWindows; private: - QString filePath; + QString filePath = ""; QQuickWindow* window = nullptr; PAGView* pagView = nullptr; - QThread* renderThread = nullptr; - PAGWindowHelper* windowHelper = nullptr; - QQmlApplicationEngine* engine = nullptr; + std::unique_ptr<PAGWindowHelper> windowHelper = nullptr; + std::unique_ptr<QQmlApplicationEngine> engine = nullptr; }; } // namespace pag diff --git a/viewer/src/task/PAGTask.cpp b/viewer/src/task/PAGTask.cpp index 4089dd8f6c..9d11ddf23a 100644 --- a/viewer/src/task/PAGTask.cpp +++ b/viewer/src/task/PAGTask.cpp @@ -18,8 +18,8 @@ #include "PAGTask.h" #include <QOpenGLContext> +#include "base/utils/TimeUtil.h" #include "utils/PAGFileUtils.h" -#include "utils/PAGTimeUtils.h" namespace pag { @@ -34,21 +34,21 @@ auto PAGTask::getProgress() const -> double { return progress; } -PAGFileTask::PAGFileTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath) +PAGPlayTask::PAGPlayTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath) : filePath(filePath), pagFile(pagFile) { - QObject::connect(&workerThread, &QThread::started, this, &PAGFileTask::startInternal, + QObject::connect(&workerThread, &QThread::started, this, &PAGPlayTask::startInternal, Qt::DirectConnection); } -PAGFileTask::~PAGFileTask() { +PAGPlayTask::~PAGPlayTask() { if (workerThread.isRunning()) { workerThread.quit(); } - QObject::disconnect(&workerThread, &QThread::started, this, &PAGFileTask::startInternal); + QObject::disconnect(&workerThread, &QThread::started, this, &PAGPlayTask::startInternal); releaseResource(); } -auto PAGFileTask::start() -> void { +auto PAGPlayTask::start() -> void { if (pagFile == nullptr) { return; } @@ -57,12 +57,12 @@ auto PAGFileTask::start() -> void { workerThread.start(); } -auto PAGFileTask::pause() -> void { +auto PAGPlayTask::pause() -> void { isWorking = false; currentFrame = 0; } -auto PAGFileTask::stop() -> void { +auto PAGPlayTask::stop() -> void { bool isWorking = this->isWorking; this->isWorking = false; if (isWorking) { @@ -71,56 +71,48 @@ auto PAGFileTask::stop() -> void { currentFrame = 0; } -auto PAGFileTask::onBegin() -> void { +auto PAGPlayTask::onBegin() -> void { initOpenGLEnvironment(); visible = true; Q_EMIT visibleChanged(visible); } -auto PAGFileTask::onFinish() -> int { +auto PAGPlayTask::onFinish() -> int { visible = false; Q_EMIT visibleChanged(visible); return 0; } -auto PAGFileTask::onFrameFlush(double progress) -> void { +auto PAGPlayTask::onFrameFlush(double progress) -> void { this->progress = progress; Q_EMIT progressChanged(progress); } -auto PAGFileTask::isNeedRenderCurrentFrame() -> bool { +auto PAGPlayTask::isNeedRenderCurrentFrame() -> bool { return currentFrame >= 0; } -auto PAGFileTask::releaseResource() -> void { - if (pagPlayer != nullptr) { - delete pagPlayer; - pagPlayer = nullptr; - } - surface = nullptr; +auto PAGPlayTask::releaseResource() -> void { + pagPlayer.reset(); + surface.reset(); if (context != nullptr) { if (frameBuffer != nullptr) { - context->makeCurrent(offscreenSurface); + context->makeCurrent(offscreenSurface.get()); frameBuffer->release(); - delete frameBuffer; - frameBuffer = nullptr; + frameBuffer.reset(); context->doneCurrent(); } - delete context; - context = nullptr; - } - if (offscreenSurface != nullptr) { - delete offscreenSurface; - offscreenSurface = nullptr; + context.reset(); } + offscreenSurface.reset(); } -auto PAGFileTask::startInternal() -> void { +auto PAGPlayTask::startInternal() -> void { float frameRate = pagFile->frameRate(); - pag::Frame totalFrame = Utils::usToFrame(pagFile->duration(), frameRate); + Frame totalFrame = TimeToFrame(pagFile->duration(), frameRate); while (currentFrame < totalFrame) { if (isNeedRenderCurrentFrame()) { - pagFile->setCurrentTime(Utils::frameToUs(currentFrame, frameRate)); + pagFile->setCurrentTime(FrameToTime(currentFrame, frameRate)); pagPlayer->flush(); onFrameFlush(pagPlayer->getProgress()); } @@ -142,19 +134,18 @@ auto PAGFileTask::startInternal() -> void { workerThread.exit(0); } -auto PAGFileTask::initOpenGLEnvironment() -> void { +auto PAGPlayTask::initOpenGLEnvironment() -> void { if (surface != nullptr) { return; } - pagPlayer = new pag::PAGPlayer(); - context = new QOpenGLContext(); + pagPlayer = std::make_unique<PAGPlayer>(); + context = std::make_unique<QOpenGLContext>(); context->create(); - offscreenSurface = new QOffscreenSurface(); + offscreenSurface = std::make_unique<QOffscreenSurface>(); offscreenSurface->setFormat(context->format()); offscreenSurface->create(); - context->makeCurrent(offscreenSurface); - frameBuffer = - new QOpenGLFramebufferObject(QSize(pagFile->width(), pagFile->height()), GL_TEXTURE_2D); + context->makeCurrent(offscreenSurface.get()); + frameBuffer = std::make_unique<QOpenGLFramebufferObject>(QSize(pagFile->width(), pagFile->height()), GL_TEXTURE_2D); GLFrameBufferInfo frameBufferInfo; frameBufferInfo.id = frameBuffer->handle(); BackendRenderTarget renderTarget = diff --git a/viewer/src/task/PAGTask.h b/viewer/src/task/PAGTask.h index 658db631ae..ba3a973f24 100644 --- a/viewer/src/task/PAGTask.h +++ b/viewer/src/task/PAGTask.h @@ -58,11 +58,11 @@ class PAGTask : public QObject { double progress = 0.0; }; -class PAGFileTask : public PAGTask { +class PAGPlayTask : public PAGTask { Q_OBJECT public: - explicit PAGFileTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath); - ~PAGFileTask() override; + explicit PAGPlayTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath); + ~PAGPlayTask() override; auto start() -> void override; auto pause() -> void override; @@ -82,13 +82,13 @@ class PAGFileTask : public PAGTask { protected: bool openAfterExport = true; Frame currentFrame = 0; - QString filePath; - PAGPlayer* pagPlayer = nullptr; - QOpenGLContext* context = nullptr; - QOffscreenSurface* offscreenSurface = nullptr; - QOpenGLFramebufferObject* frameBuffer = nullptr; + QString filePath = ""; std::shared_ptr<PAGFile> pagFile = nullptr; + std::unique_ptr<PAGPlayer> pagPlayer = nullptr; std::shared_ptr<PAGSurface> surface = nullptr; + std::unique_ptr<QOpenGLContext> context = nullptr; + std::unique_ptr<QOffscreenSurface> offscreenSurface = nullptr; + std::unique_ptr<QOpenGLFramebufferObject> frameBuffer = nullptr; private: QThread workerThread; diff --git a/viewer/src/task/PAGTaskFactory.h b/viewer/src/task/PAGTaskFactory.h index fcc6cb1c15..41f1ecfb32 100644 --- a/viewer/src/task/PAGTaskFactory.h +++ b/viewer/src/task/PAGTaskFactory.h @@ -38,8 +38,8 @@ class PAGTaskFactory : public QObject { private: PAGTask* task = nullptr; - std::string filePath; - std::shared_ptr<PAGFile> pagFile; + std::string filePath = ""; + std::shared_ptr<PAGFile> pagFile = nullptr; }; } // namespace pag diff --git a/viewer/src/task/export/PAGExportPNGTask.cpp b/viewer/src/task/export/PAGExportPNGTask.cpp index 4f8573afb4..9897db7cb4 100644 --- a/viewer/src/task/export/PAGExportPNGTask.cpp +++ b/viewer/src/task/export/PAGExportPNGTask.cpp @@ -25,14 +25,15 @@ namespace pag { PAGExportPNGTask::PAGExportPNGTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath, int exportFrame) - : PAGFileTask(pagFile, filePath), exportFrame(exportFrame) { - if (!filePath.endsWith(".png")) { + : PAGPlayTask(pagFile, filePath), exportFrame(exportFrame) { + QString lowerFilePath = filePath.toLower(); + if (!lowerFilePath.endsWith(".png")) { Utils::makeDir(filePath); } } auto PAGExportPNGTask::onFrameFlush(double progress) -> void { - PAGFileTask::onFrameFlush(progress); + PAGPlayTask::onFrameFlush(progress); QString outPath; if (exportFrame >= 0) { outPath = filePath; @@ -50,7 +51,7 @@ auto PAGExportPNGTask::isNeedRenderCurrentFrame() -> bool { } auto PAGExportPNGTask::exportCurrentFrameAsPNG(const QString& outPath) -> void { - context->makeCurrent(offscreenSurface); + context->makeCurrent(offscreenSurface.get()); auto image = frameBuffer->toImage(false); bool result = image.save(outPath, "PNG"); if (!result) { diff --git a/viewer/src/task/export/PAGExportPNGTask.h b/viewer/src/task/export/PAGExportPNGTask.h index f0bebc6706..096677cf8e 100644 --- a/viewer/src/task/export/PAGExportPNGTask.h +++ b/viewer/src/task/export/PAGExportPNGTask.h @@ -22,7 +22,7 @@ namespace pag { -class PAGExportPNGTask : public PAGFileTask { +class PAGExportPNGTask : public PAGPlayTask { Q_OBJECT public: explicit PAGExportPNGTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath, diff --git a/viewer/src/utils/PAGFileUtils.cpp b/viewer/src/utils/PAGFileUtils.cpp index 38f36e298c..2ae7061d80 100644 --- a/viewer/src/utils/PAGFileUtils.cpp +++ b/viewer/src/utils/PAGFileUtils.cpp @@ -26,7 +26,7 @@ namespace pag::Utils { auto openInFinder(const QString& path, bool select) -> void { QFileInfo fileInfo(path); - if (fileInfo.exists() == false) { + if (!fileInfo.exists()) { return; } @@ -51,7 +51,7 @@ auto deleteDir(const QString& path) -> bool { return true; } - dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden); QFileInfoList fileList = dir.entryInfoList(); for (auto& fileInfo : fileList) { if (fileInfo.isFile()) { diff --git a/viewer/src/utils/PAGTimeUtils.cpp b/viewer/src/utils/PAGTimeUtils.cpp deleted file mode 100644 index 214eba6eac..0000000000 --- a/viewer/src/utils/PAGTimeUtils.cpp +++ /dev/null @@ -1,31 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making libpag available. -// -// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file -// except in compliance with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "PAGTimeUtils.h" - -namespace pag::Utils { - -auto usToFrame(int64_t time, float frameRate) -> Frame { - return static_cast<Frame>(std::floor(time / 1000000 * frameRate)); -} - -auto frameToUs(Frame frame, float frameRate) -> int64_t { - return static_cast<int64_t>(std::ceil(frame * 1000000 / frameRate)); -} - -} // namespace pag::Utils diff --git a/viewer/src/utils/PAGTimeUtils.h b/viewer/src/utils/PAGTimeUtils.h deleted file mode 100644 index 825b208067..0000000000 --- a/viewer/src/utils/PAGTimeUtils.h +++ /dev/null @@ -1,28 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making libpag available. -// -// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file -// except in compliance with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "pag/pag.h" - -namespace pag::Utils { - -auto usToFrame(int64_t time, float frameRate) -> Frame; -auto frameToUs(Frame frame, float frameRate) -> int64_t; - -} // namespace pag::Utils From 42e68ea050723a0efe708e5a366d3ce10703b60f Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Thu, 3 Apr 2025 15:43:57 +0800 Subject: [PATCH 32/36] Codeformat the code of viewer --- viewer/src/task/PAGTask.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/viewer/src/task/PAGTask.cpp b/viewer/src/task/PAGTask.cpp index 9d11ddf23a..63d30e3650 100644 --- a/viewer/src/task/PAGTask.cpp +++ b/viewer/src/task/PAGTask.cpp @@ -145,7 +145,8 @@ auto PAGPlayTask::initOpenGLEnvironment() -> void { offscreenSurface->setFormat(context->format()); offscreenSurface->create(); context->makeCurrent(offscreenSurface.get()); - frameBuffer = std::make_unique<QOpenGLFramebufferObject>(QSize(pagFile->width(), pagFile->height()), GL_TEXTURE_2D); + frameBuffer = std::make_unique<QOpenGLFramebufferObject>( + QSize(pagFile->width(), pagFile->height()), GL_TEXTURE_2D); GLFrameBufferInfo frameBufferInfo; frameBufferInfo.id = frameBuffer->handle(); BackendRenderTarget renderTarget = From 2732f36c4a347e735c87f00fbe84435f458e1888 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Thu, 3 Apr 2025 16:03:44 +0800 Subject: [PATCH 33/36] Codeformat the code of viewer --- viewer/src/rendering/PAGRenderThread.cpp | 1 + viewer/src/rendering/PAGRenderThread.h | 3 ++- viewer/src/rendering/PAGView.cpp | 1 - viewer/src/rendering/PAGView.h | 3 +-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/viewer/src/rendering/PAGRenderThread.cpp b/viewer/src/rendering/PAGRenderThread.cpp index 880617bde4..15356ea586 100644 --- a/viewer/src/rendering/PAGRenderThread.cpp +++ b/viewer/src/rendering/PAGRenderThread.cpp @@ -18,6 +18,7 @@ #include "PAGRenderThread.h" #include <QGuiApplication> +#include "rendering/PAGView.h" namespace pag { diff --git a/viewer/src/rendering/PAGRenderThread.h b/viewer/src/rendering/PAGRenderThread.h index fbdbc6f66e..f0cf51e2b1 100644 --- a/viewer/src/rendering/PAGRenderThread.h +++ b/viewer/src/rendering/PAGRenderThread.h @@ -19,10 +19,11 @@ #pragma once #include <QThread> -#include "rendering/PAGView.h" namespace pag { +class PAGView; + class PAGRenderThread : public QThread { Q_OBJECT public: diff --git a/viewer/src/rendering/PAGView.cpp b/viewer/src/rendering/PAGView.cpp index 8e73749cc3..984d3ccc6d 100644 --- a/viewer/src/rendering/PAGView.cpp +++ b/viewer/src/rendering/PAGView.cpp @@ -19,7 +19,6 @@ #include "PAGView.h" #include <QSGImageNode> #include "pag/file.h" -#include "rendering/PAGRenderThread.h" #include "tgfx/core/Clock.h" namespace pag { diff --git a/viewer/src/rendering/PAGView.h b/viewer/src/rendering/PAGView.h index a3a6817b8d..a845700047 100644 --- a/viewer/src/rendering/PAGView.h +++ b/viewer/src/rendering/PAGView.h @@ -22,11 +22,10 @@ #include <QQuickItem> #include "pag/pag.h" #include "platform/qt/GPUDrawable.h" +#include "rendering/PAGRenderThread.h" namespace pag { -class PAGRenderThread; - class PAGView : public QQuickItem { Q_OBJECT public: From 907b13222a5f892220767efea0fad98873519f21 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Thu, 3 Apr 2025 16:21:21 +0800 Subject: [PATCH 34/36] Fix the compilation errors of viewer --- viewer/src/task/PAGTask.cpp | 1 - viewer/src/task/PAGTask.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/viewer/src/task/PAGTask.cpp b/viewer/src/task/PAGTask.cpp index 63d30e3650..2d932446f1 100644 --- a/viewer/src/task/PAGTask.cpp +++ b/viewer/src/task/PAGTask.cpp @@ -17,7 +17,6 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "PAGTask.h" -#include <QOpenGLContext> #include "base/utils/TimeUtil.h" #include "utils/PAGFileUtils.h" diff --git a/viewer/src/task/PAGTask.h b/viewer/src/task/PAGTask.h index ba3a973f24..a4566b1293 100644 --- a/viewer/src/task/PAGTask.h +++ b/viewer/src/task/PAGTask.h @@ -20,6 +20,7 @@ #include <QObject> #include <QOffscreenSurface> +#include <QOpenGLContext> #include <QOpenGLFramebufferObject> #include <QThread> #include <atomic> From c313ad5feccd574c291372981d00c5c2d07e8a69 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 8 Apr 2025 15:29:16 +0800 Subject: [PATCH 35/36] Fix some issues found in code review --- viewer/src/rendering/PAGView.h | 3 ++- viewer/src/rendering/PAGWindow.cpp | 5 +---- viewer/src/task/PAGTask.cpp | 9 --------- viewer/src/task/PAGTask.h | 2 -- viewer/src/task/PAGTaskFactory.cpp | 4 ++-- viewer/src/task/PAGTaskFactory.h | 2 +- viewer/src/task/export/PAGExportPNGTask.cpp | 6 ++++-- 7 files changed, 10 insertions(+), 21 deletions(-) diff --git a/viewer/src/rendering/PAGView.h b/viewer/src/rendering/PAGView.h index a845700047..91cc00b0b5 100644 --- a/viewer/src/rendering/PAGView.h +++ b/viewer/src/rendering/PAGView.h @@ -62,7 +62,8 @@ class PAGView : public QQuickItem { Q_SIGNAL void isPlayingChanged(bool isPlaying); Q_SIGNAL void progressChanged(double progress); - Q_SIGNAL void fileChanged(std::shared_ptr<pag::PAGFile> pagFile, std::string filePath); + Q_SIGNAL void fileChanged(const std::shared_ptr<pag::PAGFile>& pagFile, + const std::string& filePath); Q_INVOKABLE bool setFile(const QString& filePath); Q_INVOKABLE void firstFrame(); diff --git a/viewer/src/rendering/PAGWindow.cpp b/viewer/src/rendering/PAGWindow.cpp index cb4d8e24a9..d62c2cbd37 100644 --- a/viewer/src/rendering/PAGWindow.cpp +++ b/viewer/src/rendering/PAGWindow.cpp @@ -29,10 +29,7 @@ QList<PAGWindow*> PAGWindow::AllWindows; PAGWindow::PAGWindow(QObject* parent) : QObject(parent) { } -PAGWindow::~PAGWindow() { - engine.reset(); - windowHelper.reset(); -} +PAGWindow::~PAGWindow() = default; auto PAGWindow::openFile(QString path) -> void { bool result = pagView->setFile(path); diff --git a/viewer/src/task/PAGTask.cpp b/viewer/src/task/PAGTask.cpp index 2d932446f1..7b30fe2697 100644 --- a/viewer/src/task/PAGTask.cpp +++ b/viewer/src/task/PAGTask.cpp @@ -56,17 +56,8 @@ auto PAGPlayTask::start() -> void { workerThread.start(); } -auto PAGPlayTask::pause() -> void { - isWorking = false; - currentFrame = 0; -} - auto PAGPlayTask::stop() -> void { - bool isWorking = this->isWorking; this->isWorking = false; - if (isWorking) { - onFinish(); - } currentFrame = 0; } diff --git a/viewer/src/task/PAGTask.h b/viewer/src/task/PAGTask.h index a4566b1293..6b5f334c23 100644 --- a/viewer/src/task/PAGTask.h +++ b/viewer/src/task/PAGTask.h @@ -44,7 +44,6 @@ class PAGTask : public QObject { Q_SIGNAL void progressChanged(double progress); Q_INVOKABLE virtual void start() = 0; - Q_INVOKABLE virtual void pause() = 0; Q_INVOKABLE virtual void stop() = 0; protected: @@ -66,7 +65,6 @@ class PAGPlayTask : public PAGTask { ~PAGPlayTask() override; auto start() -> void override; - auto pause() -> void override; auto stop() -> void override; protected: diff --git a/viewer/src/task/PAGTaskFactory.cpp b/viewer/src/task/PAGTaskFactory.cpp index ce2f2b9da1..8a7c0650c7 100644 --- a/viewer/src/task/PAGTaskFactory.cpp +++ b/viewer/src/task/PAGTaskFactory.cpp @@ -61,8 +61,8 @@ auto PAGTaskFactory::createTask(PAGTaskType taskType, const QString& outPath, return task; } -auto PAGTaskFactory::resetFile([[maybe_unused]] std::shared_ptr<PAGFile> pagFile, - std::string filePath) -> void { +auto PAGTaskFactory::resetFile([[maybe_unused]] const std::shared_ptr<PAGFile>& pagFile, + const std::string& filePath) -> void { this->pagFile = PAGFile::Load(filePath); this->filePath = filePath; } diff --git a/viewer/src/task/PAGTaskFactory.h b/viewer/src/task/PAGTaskFactory.h index 41f1ecfb32..c6f5442b21 100644 --- a/viewer/src/task/PAGTaskFactory.h +++ b/viewer/src/task/PAGTaskFactory.h @@ -34,7 +34,7 @@ class PAGTaskFactory : public QObject { Q_INVOKABLE PAGTask* createTask(PAGTaskType taskType, const QString& outPath, const QVariantMap& extraParams); - auto resetFile(std::shared_ptr<PAGFile> pagFile, std::string filePath) -> void; + auto resetFile(const std::shared_ptr<PAGFile>& pagFile, const std::string& filePath) -> void; private: PAGTask* task = nullptr; diff --git a/viewer/src/task/export/PAGExportPNGTask.cpp b/viewer/src/task/export/PAGExportPNGTask.cpp index 9897db7cb4..d5544820fb 100644 --- a/viewer/src/task/export/PAGExportPNGTask.cpp +++ b/viewer/src/task/export/PAGExportPNGTask.cpp @@ -23,11 +23,13 @@ namespace pag { +const std::string ExportPNGFileSuffix = "png"; + PAGExportPNGTask::PAGExportPNGTask(std::shared_ptr<PAGFile>& pagFile, const QString& filePath, int exportFrame) : PAGPlayTask(pagFile, filePath), exportFrame(exportFrame) { QString lowerFilePath = filePath.toLower(); - if (!lowerFilePath.endsWith(".png")) { + if (!lowerFilePath.endsWith(QString(".%1").arg(ExportPNGFileSuffix.c_str()))) { Utils::makeDir(filePath); } } @@ -53,7 +55,7 @@ auto PAGExportPNGTask::isNeedRenderCurrentFrame() -> bool { auto PAGExportPNGTask::exportCurrentFrameAsPNG(const QString& outPath) -> void { context->makeCurrent(offscreenSurface.get()); auto image = frameBuffer->toImage(false); - bool result = image.save(outPath, "PNG"); + bool result = image.save(outPath, ExportPNGFileSuffix.c_str()); if (!result) { qDebug() << "Failed to save image to path: " << outPath; } From 64ba35146a637ccd30c794f0ba60b93da08fed34 Mon Sep 17 00:00:00 2001 From: markffan <markffan@tencent.com> Date: Tue, 8 Apr 2025 20:44:21 +0800 Subject: [PATCH 36/36] Fix some issues found in code review --- viewer/src/rendering/PAGView.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/viewer/src/rendering/PAGView.cpp b/viewer/src/rendering/PAGView.cpp index 984d3ccc6d..969adeadaa 100644 --- a/viewer/src/rendering/PAGView.cpp +++ b/viewer/src/rendering/PAGView.cpp @@ -37,8 +37,6 @@ PAGView::PAGView(QQuickItem* parent) : QQuickItem(parent) { PAGView::~PAGView() { QMetaObject::invokeMethod(renderThread.get(), "shutDown", Qt::QueuedConnection); renderThread->wait(); - renderThread.reset(); - pagPlayer.reset(); } auto PAGView::getPAGWidth() const -> int {